Fix an incorrect assert() in fts3.c. Add further fts3 tests.
FossilOrigin-Name: 75863c2d55e0801add5b8dcf88d575c5c870af04
This commit is contained in:
parent
e585b8f05c
commit
e2e5145441
107
ext/fts3/fts3.c
107
ext/fts3/fts3.c
@ -473,11 +473,6 @@ static void fts3GetDeltaVarint2(char **pp, char *pEnd, sqlite3_int64 *pVal){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Fts3Table *cursor_vtab(Fts3Cursor *c){
|
||||
return (Fts3Table *) c->base.pVtab;
|
||||
}
|
||||
|
||||
/*
|
||||
** The xDisconnect() virtual table method.
|
||||
*/
|
||||
@ -650,6 +645,14 @@ int fts3InitVtab(
|
||||
int nDb;
|
||||
int nName;
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
char *zTestParam = 0;
|
||||
if( strncmp(argv[argc-1], "test:", 5)==0 ){
|
||||
zTestParam = argv[argc-1];
|
||||
argc--;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *zTokenizer = 0; /* Name of tokenizer to use */
|
||||
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */
|
||||
|
||||
@ -696,6 +699,7 @@ int fts3InitVtab(
|
||||
p->nPendingData = 0;
|
||||
p->azColumn = (char **)&p[1];
|
||||
p->pTokenizer = pTokenizer;
|
||||
p->nNodeSize = 1000;
|
||||
zCsr = (char *)&p->azColumn[nCol];
|
||||
|
||||
fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1);
|
||||
@ -739,6 +743,11 @@ int fts3InitVtab(
|
||||
rc = fts3DeclareVtab(p);
|
||||
if( rc!=SQLITE_OK ) goto fts3_init_out;
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
if( zTestParam ){
|
||||
p->nNodeSize = atoi(&zTestParam[5]);
|
||||
}
|
||||
#endif
|
||||
*ppVTab = &p->base;
|
||||
|
||||
fts3_init_out:
|
||||
@ -1010,12 +1019,16 @@ static int fts3SelectLeaf(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to create delta-encoded serialized lists of FTS3
|
||||
** varints. Each call to this function appends a single varint to a list.
|
||||
*/
|
||||
static void fts3PutDeltaVarint(
|
||||
char **pp,
|
||||
sqlite3_int64 *piPrev,
|
||||
sqlite3_int64 iVal
|
||||
char **pp, /* IN/OUT: Output pointer */
|
||||
sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */
|
||||
sqlite3_int64 iVal /* Write this value to the list */
|
||||
){
|
||||
assert( iVal-*piPrev > 0 );
|
||||
assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
|
||||
*pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
|
||||
*piPrev = iVal;
|
||||
}
|
||||
@ -1550,6 +1563,7 @@ static int fts3TermSelect(
|
||||
sqlite3_int64 i2 = sqlite3_column_int64(pStmt, 2);
|
||||
rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew);
|
||||
}
|
||||
sqlite3Fts3ReadBlock(p, 0, 0, 0);
|
||||
}
|
||||
iAge++;
|
||||
|
||||
@ -1699,6 +1713,9 @@ static int evalFts3Expr(
|
||||
if( SQLITE_OK==(rc = evalFts3Expr(p, pExpr->pRight, &aRight, &nRight))
|
||||
&& SQLITE_OK==(rc = evalFts3Expr(p, pExpr->pLeft, &aLeft, &nLeft))
|
||||
){
|
||||
assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR
|
||||
|| pExpr->eType==FTSQUERY_AND || pExpr->eType==FTSQUERY_NOT
|
||||
);
|
||||
switch( pExpr->eType ){
|
||||
case FTSQUERY_NEAR: {
|
||||
Fts3Expr *pLeft;
|
||||
@ -1749,8 +1766,7 @@ static int evalFts3Expr(
|
||||
break;
|
||||
}
|
||||
|
||||
case FTSQUERY_AND:
|
||||
case FTSQUERY_NOT: {
|
||||
default: {
|
||||
assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND );
|
||||
fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut,
|
||||
aLeft, nLeft, aRight, nRight
|
||||
@ -1865,38 +1881,6 @@ static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
|
||||
return ((Fts3Cursor *)pCursor)->isEof;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xColumn method of the virtual table. The SQLite
|
||||
** core calls this method during a query when it needs the value
|
||||
** of a column from the virtual table. This method needs to use
|
||||
** one of the sqlite3_result_*() routines to store the requested
|
||||
** value back in the pContext.
|
||||
*/
|
||||
static int fts3ColumnMethod(sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *pContext, int idxCol){
|
||||
Fts3Cursor *c = (Fts3Cursor *) pCursor;
|
||||
Fts3Table *v = cursor_vtab(c);
|
||||
int rc = fts3CursorSeek(c);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( idxCol<v->nColumn ){
|
||||
sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
|
||||
sqlite3_result_value(pContext, pVal);
|
||||
}else if( idxCol==v->nColumn ){
|
||||
/* The extra column whose name is the same as the table.
|
||||
** Return a blob which is a pointer to the cursor
|
||||
*/
|
||||
sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
|
||||
}else if( idxCol==v->nColumn+1 ){
|
||||
/* The docid column, which is an alias for rowid. */
|
||||
sqlite3_value *pVal = sqlite3_column_value(c->pStmt, 0);
|
||||
sqlite3_result_value(pContext, pVal);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xRowid method. The SQLite core calls this routine to
|
||||
** retrieve the rowid for the current row of the result set. fts3
|
||||
@ -1913,6 +1897,43 @@ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xColumn method, called by SQLite to request a value from
|
||||
** the row that the supplied cursor currently points to.
|
||||
*/
|
||||
static int fts3ColumnMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
int rc; /* Return Code */
|
||||
Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
|
||||
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
|
||||
|
||||
/* The column value supplied by SQLite must be in range. */
|
||||
assert( iCol>=0 && iCol<=p->nColumn+1 );
|
||||
|
||||
rc = fts3CursorSeek(pCsr);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iCol==p->nColumn+1 ){
|
||||
/* This call is a request for the "docid" column. Since "docid" is an
|
||||
** alias for "rowid", use the xRowid() method to obtain the value.
|
||||
*/
|
||||
sqlite3_int64 iRowid;
|
||||
rc = fts3RowidMethod(pCursor, &iRowid);
|
||||
sqlite3_result_int64(pContext, iRowid);
|
||||
}else if( iCol==p->nColumn ){
|
||||
/* The extra column whose name is the same as the table.
|
||||
** Return a blob which is a pointer to the cursor.
|
||||
*/
|
||||
sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the implementation of the xUpdate callback used by
|
||||
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
|
||||
|
@ -115,6 +115,8 @@ struct Fts3Table {
|
||||
int nLeavesAlloc; /* Allocated size of aLeavesStmt */
|
||||
sqlite3_stmt **aLeavesStmt; /* Array of prepared zSelectLeaves stmts */
|
||||
|
||||
int nNodeSize; /* Soft limit for node size */
|
||||
|
||||
/* The following hash table is used to buffer pending index updates during
|
||||
** transactions. Variable nPendingData estimates the memory size of the
|
||||
** pending data, including hash table overhead, but not malloc overhead.
|
||||
|
@ -24,9 +24,6 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INTERIOR_MAX 2048 /* Soft limit for segment node size */
|
||||
#define LEAF_MAX 2048 /* Soft limit for segment leaf size */
|
||||
|
||||
typedef struct PendingList PendingList;
|
||||
typedef struct SegmentNode SegmentNode;
|
||||
typedef struct SegmentWriter SegmentWriter;
|
||||
@ -279,6 +276,7 @@ int sqlite3Fts3ReadBlock(
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_reset(pStmt);
|
||||
|
||||
if( pzBlock ){
|
||||
sqlite3_bind_int64(pStmt, 1, iBlock);
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
@ -290,6 +288,7 @@ int sqlite3Fts3ReadBlock(
|
||||
if( !*pzBlock ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -1193,13 +1192,13 @@ static int fts3NodeAddTerm(
|
||||
nSuffix = nTerm-nPrefix;
|
||||
|
||||
nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix;
|
||||
if( nReq<=INTERIOR_MAX || !pTree->zTerm ){
|
||||
if( nReq<=p->nNodeSize || !pTree->zTerm ){
|
||||
|
||||
if( nReq>INTERIOR_MAX ){
|
||||
if( nReq>p->nNodeSize ){
|
||||
/* An unusual case: this is the first term to be added to the node
|
||||
** and the static node buffer (INTERIOR_MAX bytes) is not large
|
||||
** and the static node buffer (p->nNodeSize bytes) is not large
|
||||
** enough. Use a separately malloced buffer instead This wastes
|
||||
** INTERIOR_MAX bytes, but since this scenario only comes about when
|
||||
** p->nNodeSize bytes, but since this scenario only comes about when
|
||||
** the database contain two terms that share a prefix of almost 2KB,
|
||||
** this is not expected to be a serious problem.
|
||||
*/
|
||||
@ -1248,7 +1247,7 @@ static int fts3NodeAddTerm(
|
||||
** now. Instead, the term is inserted into the parent of pTree. If pTree
|
||||
** has no parent, one is created here.
|
||||
*/
|
||||
pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + INTERIOR_MAX);
|
||||
pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize);
|
||||
if( !pNew ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@ -1401,9 +1400,9 @@ static int fts3SegWriterAdd(
|
||||
*ppWriter = pWriter;
|
||||
|
||||
/* Allocate a buffer in which to accumulate data */
|
||||
pWriter->aData = (char *)sqlite3_malloc(LEAF_MAX);
|
||||
pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize);
|
||||
if( !pWriter->aData ) return SQLITE_NOMEM;
|
||||
pWriter->nSize = LEAF_MAX;
|
||||
pWriter->nSize = p->nNodeSize;
|
||||
|
||||
/* Find the next free blockid in the %_segments table */
|
||||
rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pStmt, 0);
|
||||
@ -1427,7 +1426,7 @@ static int fts3SegWriterAdd(
|
||||
sqlite3Fts3VarintLen(nDoclist) + /* Size of doclist */
|
||||
nDoclist; /* Doclist data */
|
||||
|
||||
if( nData>0 && nData+nReq>LEAF_MAX ){
|
||||
if( nData>0 && nData+nReq>p->nNodeSize ){
|
||||
int rc;
|
||||
|
||||
/* The current leaf node is full. Write it out to the database. */
|
||||
|
23
manifest
23
manifest
@ -1,5 +1,5 @@
|
||||
C Updates\sto\sFTS3\sto\scorrect\scompiler\swarnings\sunder\sMSVC.
|
||||
D 2009-12-03T06:26:46
|
||||
C Fix\san\sincorrect\sassert()\sin\sfts3.c.\sAdd\sfurther\sfts3\stests.
|
||||
D 2009-12-03T17:36:22
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in c5827ead754ab32b9585487177c93bb00b9497b3
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@ -56,9 +56,9 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
|
||||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 8352dc3506c3b30fde126ea5da9c431d3c243522
|
||||
F ext/fts3/fts3.c b15d44a46f76b08806b75d70c44c62254269d619
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 515132f0ae6b35eccbeef72a2bafb16d7e251953
|
||||
F ext/fts3/fts3Int.h cc716c74afa7da8e0f8ef39404f33ea62a823eb3
|
||||
F ext/fts3/fts3_expr.c c18794a62c257d3456d3314c5a18e348ae0d84bd
|
||||
F ext/fts3/fts3_hash.c 18feef38fca216992725e9eae775a0c7735e6724
|
||||
F ext/fts3/fts3_hash.h d410ff2c93c81a56b927fcf07b2099ccbfa7a479
|
||||
@ -68,7 +68,7 @@ F ext/fts3/fts3_snippet.c 6c2eb6d872d66b2a9aa5663f2662e993f18a6496
|
||||
F ext/fts3/fts3_tokenizer.c 73a4e0e068720153901622f215298b73e7c976c7
|
||||
F ext/fts3/fts3_tokenizer.h 7ff73caa3327589bf6550f60d93ebdd1f6a0fb5c
|
||||
F ext/fts3/fts3_tokenizer1.c 11a604a53cff5e8c28882727bf794e5252e5227b
|
||||
F ext/fts3/fts3_write.c 6c59b1d6eed759815151298c132d79301c205fce
|
||||
F ext/fts3/fts3_write.c ec51fb6886f910e78ae32158ec0301aa675f52d8
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33
|
||||
F ext/icu/icu.c 12e763d288d23b5a49de37caa30737b971a2f1e2
|
||||
@ -400,8 +400,9 @@ F test/fts3d.test 95fb3c862cbc4297c93fceb9a635543744e9ef52
|
||||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
F test/fts3expr.test 05dab77387801e4900009917bb18f556037d82da
|
||||
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
|
||||
F test/fts3malloc.test efbd316eafe54471b7f68604c050418b31d1914e
|
||||
F test/fts3malloc.test d02ee86b21edd2b43044e0d6dfdcd26cb6efddcb
|
||||
F test/fts3near.test dc196dd17b4606f440c580d45b3d23aa975fd077
|
||||
F test/fts3rnd.test bbb85c6b2b55f15a8ecaaf1585a94845501f7369
|
||||
F test/func.test af106ed834001738246d276659406823e35cde7b
|
||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||
F test/fuzz.test a4174c3009a3e2c2e14b31b364ebf7ddb49de2c9
|
||||
@ -522,7 +523,7 @@ F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
|
||||
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
|
||||
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/quick.test 12fdc7656b4d20a537a686fb223eb99b5fe54483
|
||||
F test/quick.test 31d0c41d1602af5a2b402682770943b77d42d309
|
||||
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
|
||||
@ -775,7 +776,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P e3aa0870fce0666bf8c67ad6ec23e135d03b604a
|
||||
R 8d9ad89e2fac8a1d1d94a1d048213789
|
||||
U shaneh
|
||||
Z 7667ec8a886e15725b2ab6f5b341cdfd
|
||||
P 37495b55ffbdc2db4482367ac7d8e32d4d71d58e
|
||||
R cfd8087616225b6f440fa1c8b43bd349
|
||||
U dan
|
||||
Z bf00a60fa922c646641246a97c22e493
|
||||
|
@ -1 +1 @@
|
||||
37495b55ffbdc2db4482367ac7d8e32d4d71d58e
|
||||
75863c2d55e0801add5b8dcf88d575c5c870af04
|
@ -36,126 +36,6 @@ set DO_MALLOC_TEST 1
|
||||
#
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This proc is used to test a single SELECT statement. Parameter $name is
|
||||
# passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter
|
||||
# $sql is passed the text of the SELECT statement. Parameter $result is
|
||||
# set to the expected output if the SELECT statement is successfully
|
||||
# executed using [db eval].
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2}
|
||||
#
|
||||
# If global variable DO_MALLOC_TEST is set to a non-zero value, or if
|
||||
# it is not defined at all, then OOM testing is performed on the SELECT
|
||||
# statement. Each OOM test case is said to pass if either (a) executing
|
||||
# the SELECT statement succeeds and the results match those specified
|
||||
# by parameter $result, or (b) TCL throws an "out of memory" error.
|
||||
#
|
||||
# If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement
|
||||
# is executed just once. In this case the test case passes if the results
|
||||
# match the expected results passed via parameter $result.
|
||||
#
|
||||
proc do_select_test {name sql result} {
|
||||
doPassiveTest $name $sql [list 0 $result]
|
||||
}
|
||||
|
||||
proc do_error_test {name sql error} {
|
||||
doPassiveTest $name $sql [list 1 $error]
|
||||
}
|
||||
|
||||
proc doPassiveTest {name sql catchres} {
|
||||
if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
|
||||
|
||||
if {$::DO_MALLOC_TEST} {
|
||||
set answers [list {1 {out of memory}} $catchres]
|
||||
set modes [list 100000 transient 1 persistent]
|
||||
} else {
|
||||
set answers [list $catchres]
|
||||
set modes [list 0 nofail]
|
||||
}
|
||||
set str [join $answers " OR "]
|
||||
|
||||
foreach {nRepeat zName} $modes {
|
||||
for {set iFail 1} 1 {incr iFail} {
|
||||
if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat}
|
||||
|
||||
set res [catchsql $sql]
|
||||
if {[lsearch $answers $res]>=0} {
|
||||
set res $str
|
||||
}
|
||||
do_test $name.$zName.$iFail [list set {} $res] $str
|
||||
set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
|
||||
if {$nFail==0} break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test a single write to the database. In this case a "write" is a
|
||||
# DELETE, UPDATE or INSERT statement.
|
||||
#
|
||||
# If OOM testing is performed, there are several acceptable outcomes:
|
||||
#
|
||||
# 1) The write succeeds. No error is returned.
|
||||
#
|
||||
# 2) An "out of memory" exception is thrown and:
|
||||
#
|
||||
# a) The statement has no effect, OR
|
||||
# b) The current transaction is rolled back, OR
|
||||
# c) The statement succeeds. This can only happen if the connection
|
||||
# is in auto-commit mode (after the statement is executed, so this
|
||||
# includes COMMIT statements).
|
||||
#
|
||||
# If the write operation eventually succeeds, zero is returned. If a
|
||||
# transaction is rolled back, non-zero is returned.
|
||||
#
|
||||
# Parameter $name is the name to use for the test case (or test cases).
|
||||
# The second parameter, $tbl, should be the name of the database table
|
||||
# being modified. Parameter $sql contains the SQL statement to test.
|
||||
#
|
||||
proc do_write_test {name tbl sql} {
|
||||
if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
|
||||
|
||||
# Figure out an statement to get a checksum for table $tbl.
|
||||
db eval "SELECT * FROM $tbl" V break
|
||||
set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl"
|
||||
|
||||
# Calculate the initial table checksum.
|
||||
set cksum1 [db one $cksumsql]
|
||||
|
||||
|
||||
if {$::DO_MALLOC_TEST } {
|
||||
set answers [list {1 {out of memory}} {0 {}}]
|
||||
set modes [list 100000 transient 1 persistent]
|
||||
} else {
|
||||
set answers [list {0 {}}]
|
||||
set modes [list 0 nofail]
|
||||
}
|
||||
set str [join $answers " OR "]
|
||||
|
||||
foreach {nRepeat zName} $modes {
|
||||
for {set iFail 1} 1 {incr iFail} {
|
||||
if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat}
|
||||
|
||||
set res [catchsql $sql]
|
||||
set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
|
||||
if {$nFail==0} {
|
||||
do_test $name.$zName.$iFail [list set {} $res] {0 {}}
|
||||
return
|
||||
} else {
|
||||
if {[lsearch $answers $res]>=0} {
|
||||
set res $str
|
||||
}
|
||||
do_test $name.$zName.$iFail [list set {} $res] $str
|
||||
set cksum2 [db one $cksumsql]
|
||||
if {$cksum1 != $cksum2} return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc normal_list {l} {
|
||||
set ret [list]
|
||||
|
148
test/fts3rnd.test
Normal file
148
test/fts3rnd.test
Normal file
@ -0,0 +1,148 @@
|
||||
# 2009 December 03
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Brute force (random data) tests for FTS3.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If this build does not include FTS3, skip the tests in this file.
|
||||
#
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
source $testdir/fts3_common.tcl
|
||||
|
||||
set nVocab 100
|
||||
set lVocab [list]
|
||||
|
||||
# Generate a vocabulary of nVocab words. Each word is 3 characters long.
|
||||
#
|
||||
set lChar {a b c d e f g h i j k l m n o p q r s t u v w x y z}
|
||||
for {set i 0} {$i < $nVocab} {incr i} {
|
||||
set word [lindex $lChar [expr int(rand()*26)]]
|
||||
append word [lindex $lChar [expr int(rand()*26)]]
|
||||
append word [lindex $lChar [expr int(rand()*26)]]
|
||||
lappend lVocab $word
|
||||
}
|
||||
|
||||
proc random_term {} {
|
||||
lindex $::lVocab [expr {int(rand()*$::nVocab)}]
|
||||
}
|
||||
|
||||
# Return a document consisting of $nWord arbitrarily selected terms
|
||||
# from the $::lVocab list.
|
||||
#
|
||||
proc generate_doc {nWord} {
|
||||
set doc [list]
|
||||
for {set i 0} {$i < $nWord} {incr i} {
|
||||
lappend doc [random_term]
|
||||
}
|
||||
return $doc
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Primitives to update the table.
|
||||
#
|
||||
proc insert_row {rowid} {
|
||||
set a [generate_doc [expr int((rand()*100))]]
|
||||
set b [generate_doc [expr int((rand()*100))]]
|
||||
set c [generate_doc [expr int((rand()*100))]]
|
||||
execsql { INSERT INTO t1(docid, a, b, c) VALUES($rowid, $a, $b, $c) }
|
||||
set ::t1($rowid) [list $a $b $c]
|
||||
}
|
||||
proc delete_row {rowid} {
|
||||
execsql { DELETE FROM t1 WHERE rowid = $rowid }
|
||||
catch {unset ::t1($rowid)}
|
||||
}
|
||||
proc update_row {rowid} {
|
||||
set cols {a b c}
|
||||
set iCol [expr int(rand()*3)]
|
||||
set doc [generate_doc [expr int((rand()*100))]]
|
||||
lset ::t1($rowid) $iCol $doc
|
||||
execsql "UPDATE t1 SET [lindex $cols $iCol] = \$doc WHERE rowid = \$rowid"
|
||||
}
|
||||
|
||||
# Primitives to query the in-memory table.
|
||||
#
|
||||
proc simple_term {zTerm} {
|
||||
set ret [list]
|
||||
foreach {key value} [array get ::t1] {
|
||||
if {[string first $zTerm $value]>=0} { lappend ret $key }
|
||||
}
|
||||
lsort -integer $ret
|
||||
}
|
||||
|
||||
foreach nodesize {50 500 1000 2000} {
|
||||
catch { array unset ::t1 }
|
||||
|
||||
# Create the FTS3 table. Populate it (and the Tcl array) with 100 rows.
|
||||
#
|
||||
db transaction {
|
||||
catchsql { DROP TABLE t1 }
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING fts3(a, b, c, test:$nodesize)"
|
||||
for {set i 0} {$i < 100} {incr i} { insert_row $i }
|
||||
}
|
||||
|
||||
for {set iTest 1} {$iTest <= 100} {incr iTest} {
|
||||
|
||||
# Delete one row, update one row and insert one row.
|
||||
#
|
||||
set rows [array names ::t1]
|
||||
set nRow [llength $rows]
|
||||
set iUpdate [lindex $rows [expr {int(rand()*$nRow)}]]
|
||||
set iDelete $iUpdate
|
||||
while {$iDelete == $iUpdate} {
|
||||
set iDelete [lindex $rows [expr {int(rand()*$nRow)}]]
|
||||
}
|
||||
set iInsert $iUpdate
|
||||
while {[info exists ::t1($iInsert)]} {
|
||||
set iInsert [expr {int(rand()*1000000)}]
|
||||
}
|
||||
db transaction {
|
||||
insert_row $iInsert
|
||||
update_row $iUpdate
|
||||
delete_row $iDelete
|
||||
}
|
||||
|
||||
# Pick 10 terms from the vocabulary. Check that the results of querying
|
||||
# the database for the set of documents containing each of these terms
|
||||
# is the same as the result obtained by scanning the contents of the Tcl
|
||||
# array for each term.
|
||||
#
|
||||
set n [expr {$iTest % ([llength $::lVocab]-10)}]
|
||||
foreach term [lrange $::lVocab $n [expr $n+10]] {
|
||||
do_test fts3rnd-1.$nodesize.$iTest.$term {
|
||||
execsql { SELECT docid FROM t1 WHERE t1 MATCH $term }
|
||||
} [simple_term $term]
|
||||
}
|
||||
|
||||
# Similar to the above, except for phrase queries.
|
||||
#
|
||||
for {set i 0} {$i < 10} {incr i} {
|
||||
set term [list [random_term] [random_term]]
|
||||
set match "\"$term\""
|
||||
do_test fts3rnd-1.$nodesize.$iTest.$match {
|
||||
execsql { SELECT docid FROM t1 WHERE t1 MATCH $match }
|
||||
} [simple_term $term]
|
||||
}
|
||||
|
||||
# Three word phrases.
|
||||
#
|
||||
for {set i 0} {$i < 10} {incr i} {
|
||||
set term [list [random_term] [random_term] [random_term]]
|
||||
set match "\"$term\""
|
||||
do_test fts3rnd-1.$nodesize.$iTest.$match {
|
||||
execsql { SELECT docid FROM t1 WHERE t1 MATCH $match }
|
||||
} [simple_term $term]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -57,7 +57,9 @@ set EXCLUDE {
|
||||
crash6.test
|
||||
crash7.test
|
||||
delete3.test
|
||||
e_fts3.test
|
||||
fts3.test
|
||||
fts3fuzz.test
|
||||
fkey_malloc.test
|
||||
fuzz.test
|
||||
fuzz3.test
|
||||
|
Loading…
Reference in New Issue
Block a user