From: dan Date: Thu, 3 Dec 2009 17:36:22 +0000 (+0000) Subject: Fix an incorrect assert() in fts3.c. Add further fts3 tests. X-Git-Tag: version-3.7.2~759 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e2e51454412361eec00306a94658a134fbf17b07;p=thirdparty%2Fsqlite.git Fix an incorrect assert() in fts3.c. Add further fts3 tests. FossilOrigin-Name: 75863c2d55e0801add5b8dcf88d575c5c870af04 --- diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index ad3cf6f773..cca04a573b 100644 --- a/ext/fts3/fts3.c +++ b/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( idxColnColumn ){ - 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 diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index a4aeb4374a..d59478646b 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -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. diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 3a2ed6e166..d240149318 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -24,9 +24,6 @@ #include #include -#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,16 +276,18 @@ int sqlite3Fts3ReadBlock( if( rc!=SQLITE_OK ) return rc; sqlite3_reset(pStmt); - sqlite3_bind_int64(pStmt, 1, iBlock); - rc = sqlite3_step(pStmt); - if( rc!=SQLITE_ROW ){ - return SQLITE_CORRUPT; - } - - *pnBlock = sqlite3_column_bytes(pStmt, 0); - *pzBlock = (char *)sqlite3_column_blob(pStmt, 0); - if( !*pzBlock ){ - return SQLITE_NOMEM; + if( pzBlock ){ + sqlite3_bind_int64(pStmt, 1, iBlock); + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + return SQLITE_CORRUPT; + } + + *pnBlock = sqlite3_column_bytes(pStmt, 0); + *pzBlock = (char *)sqlite3_column_blob(pStmt, 0); + 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. */ diff --git a/manifest b/manifest index 57e4923111..f960c51a67 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index 225ad7c04a..c0742dfffb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -37495b55ffbdc2db4482367ac7d8e32d4d71d58e \ No newline at end of file +75863c2d55e0801add5b8dcf88d575c5c870af04 \ No newline at end of file diff --git a/test/fts3malloc.test b/test/fts3malloc.test index a1c0905136..920dc0c1d2 100644 --- a/test/fts3malloc.test +++ b/test/fts3malloc.test @@ -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] diff --git a/test/fts3rnd.test b/test/fts3rnd.test new file mode 100644 index 0000000000..ddea0cebb3 --- /dev/null +++ b/test/fts3rnd.test @@ -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 + diff --git a/test/quick.test b/test/quick.test index a56455a141..9006ef39bb 100644 --- a/test/quick.test +++ b/test/quick.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