From: dan Date: Wed, 25 May 2011 18:34:53 +0000 (+0000) Subject: Change fts4 so that the prefix= parameter is passes a comma-separated list of integer... X-Git-Tag: version-3.7.7~62^2~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9d1f874ab30b038d7e0bda416dfeee6ecf1be790;p=thirdparty%2Fsqlite.git Change fts4 so that the prefix= parameter is passes a comma-separated list of integers. For each integer N, a separate index of all prefixes of length N bytes is created. FossilOrigin-Name: be59bf49402d2e2f4b95fb6668849f3745cb7bf2 --- diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index a127773449..6a5c2ae4ef 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -832,6 +832,58 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ return zRet; } +static int fts3GobbleInt(const char **pp, int *pnOut){ + const char *p = *pp; + int nInt = 0; + for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ + nInt = nInt * 10 + (p[0] - '0'); + } + if( p==*pp ) return SQLITE_ERROR; + *pnOut = nInt; + *pp = p; + return SQLITE_OK; +} + + +static int fts3PrefixParameter( + const char *zParam, /* ABC in prefix=ABC parameter to parse */ + int *pnIndex, /* OUT: size of *apIndex[] array */ + struct Fts3Index **apIndex, /* OUT: Array of indexes for this table */ + struct Fts3Index **apFree /* OUT: Free this with sqlite3_free() */ +){ + struct Fts3Index *aIndex; + int nIndex = 1; + + if( zParam && zParam[0] ){ + const char *p; + nIndex++; + for(p=zParam; *p; p++){ + if( *p==',' ) nIndex++; + } + } + + aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); + *apIndex = *apFree = aIndex; + *pnIndex = nIndex; + if( !aIndex ){ + return SQLITE_NOMEM; + } + + memset(aIndex, 0, sizeof(struct Fts3Index) * nIndex); + if( zParam ){ + const char *p = zParam; + int i; + for(i=1; ibHasStat = isFts4; TESTONLY( p->inTransaction = -1 ); TESTONLY( p->mxSavepoint = -1 ); - p->bPrefix = bPrefix; - fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1); - fts3HashInit(&p->pendingPrefixes, FTS3_HASH_STRING, 1); + + p->aIndex = (struct Fts3Index *)&p->azColumn[nCol]; + memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex); + p->nIndex = nIndex; + for(i=0; iaIndex[i].hPending, FTS3_HASH_STRING, 1); + } /* Fill in the zName and zDb fields of the vtab structure. */ - zCsr = (char *)&p->azColumn[nCol]; + zCsr = (char *)&p->aIndex[nIndex]; p->zName = zCsr; memcpy(zCsr, argv[2], nName); zCsr += nName; @@ -1034,6 +1104,8 @@ static int fts3InitVtab( fts3DeclareVtab(&rc, p); fts3_init_out: + sqlite3_free(zPrefix); + sqlite3_free(aFree); sqlite3_free(zCompress); sqlite3_free(zUncompress); sqlite3_free((void *)aCol); @@ -2144,6 +2216,9 @@ static int fts3DeferredTermSelect( return SQLITE_OK; } +/* +** Append SegReader object pNew to the end of the pCsr->apSegment[] array. +*/ static int fts3SegReaderCursorAppend( Fts3SegReaderCursor *pCsr, Fts3SegReader *pNew @@ -2163,11 +2238,12 @@ static int fts3SegReaderCursorAppend( } /* -** Set up a cursor object for iterating through the full-text index or -** a single level therein. +** Set up a cursor object for iterating through a full-text index or a +** single level therein. */ int sqlite3Fts3SegReaderCursor( Fts3Table *p, /* FTS3 table handle */ + int iIndex, /* Index to search (from 0 to p->nIndex-1) */ int iLevel, /* Level of segments to scan */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ @@ -2180,49 +2256,41 @@ int sqlite3Fts3SegReaderCursor( int iAge = 0; sqlite3_stmt *pStmt = 0; - assert( iLevel==FTS3_SEGCURSOR_ALL_TERM + assert( iIndex>=0 && iIndexnIndex ); + assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel==FTS3_SEGCURSOR_PENDING - || iLevel==FTS3_SEGCURSOR_PENDING_PREFIX - || iLevel==FTS3_SEGCURSOR_ALL_PREFIX || iLevel>=0 ); - assert( 0>FTS3_SEGCURSOR_ALL_TERM - && 0>FTS3_SEGCURSOR_PENDING - && 0>FTS3_SEGCURSOR_PENDING_PREFIX - && 0>FTS3_SEGCURSOR_ALL_PREFIX - ); - assert( iLevel==FTS3_SEGCURSOR_ALL_TERM - || iLevel==FTS3_SEGCURSOR_ALL_PREFIX - || (zTerm==0 && isPrefix==1) - ); + assert( iLevelaIndex==0 ); - /* "isScan" is only set to true by the ft4aux module, not an ordinary - ** full-text table. The pendingTerms and pendingPrefixes tables must be - ** empty in this case. */ - assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 ); - assert( isScan==0 || fts3HashCount(&p->pendingPrefixes)==0 ); - - /* If iLevel is less than 0, include a seg-reader for the pending-terms. */ - if( iLevel<0 && isScan==0 ){ - int bPrefix = ( - iLevel==FTS3_SEGCURSOR_PENDING_PREFIX - || iLevel==FTS3_SEGCURSOR_ALL_PREFIX - ); - Fts3SegReader *pPending = 0; + memset(pCsr, 0, sizeof(Fts3SegReaderCursor)); - rc = sqlite3Fts3SegReaderPending(p,zTerm,nTerm,isPrefix,bPrefix,&pPending); - if( rc==SQLITE_OK && pPending ){ - rc = fts3SegReaderCursorAppend(pCsr, pPending); + /* If iLevel is less than 0 and this is not a scan, include a seg-reader + ** for the pending-terms. If this is a scan, then this call must be being + ** made by an fts4aux module, not an FTS table. In this case calling + ** Fts3SegReaderPending might segfault, as the data structures used by + ** fts4aux are not completely populated. So it's easiest to filter these + ** calls out here. */ + if( iLevel<0 && p->aIndex ){ + Fts3SegReader *pSeg = 0; + rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg); + if( rc==SQLITE_OK && pSeg ){ + rc = fts3SegReaderCursorAppend(pCsr, pSeg); } } - if( iLevel!=FTS3_SEGCURSOR_PENDING && iLevel!=FTS3_SEGCURSOR_PENDING_PREFIX ){ + if( iLevel!=FTS3_SEGCURSOR_PENDING ){ if( rc==SQLITE_OK ){ - rc = sqlite3Fts3AllSegdirs(p, iLevel, &pStmt); + rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt); } + while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ Fts3SegReader *pSeg = 0; @@ -2273,17 +2341,25 @@ static int fts3TermSegReaderCursor( if( pSegcsr ){ int i; int nCost = 0; + int bFound = 0; /* True once an index has been found */ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - if( isPrefix && p->bPrefix && nTerm<=FTS3_MAX_PREFIX ){ - rc = sqlite3Fts3SegReaderCursor( - p, FTS3_SEGCURSOR_ALL_PREFIX, zTerm, nTerm, 0, 0, pSegcsr); + if( isPrefix ){ + for(i=1; inIndex; i++){ + if( p->aIndex[i].nPrefix==nTerm ){ + bFound = 1; + rc = sqlite3Fts3SegReaderCursor( + p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr); + break; + } + } + } - }else{ + if( bFound==0 ){ rc = sqlite3Fts3SegReaderCursor( - p, FTS3_SEGCURSOR_ALL_TERM, zTerm, nTerm, isPrefix, 0, pSegcsr); + p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr + ); } - for(i=0; rc==SQLITE_OK && inSegment; i++){ rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost); } @@ -3340,7 +3416,6 @@ static int fts3RollbackMethod(sqlite3_vtab *pVtab){ assert( p->inTransaction!=0 ); TESTONLY( p->inTransaction = 0 ); TESTONLY( p->mxSavepoint = -1; ); - sqlite3Fts3PendingPrefixesClear((Fts3Table *)pVtab); return SQLITE_OK; } @@ -3715,7 +3790,6 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ assert( p->mxSavepoint >= iSavepoint ); TESTONLY( p->mxSavepoint = iSavepoint ); sqlite3Fts3PendingTermsClear(p); - sqlite3Fts3PendingPrefixesClear((Fts3Table *)pVtab); return SQLITE_OK; } diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 785a6aa655..00e1c37d2b 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -23,8 +23,6 @@ #include "fts3_tokenizer.h" #include "fts3_hash.h" -#define FTS3_MAX_PREFIX 8 - /* ** This constant controls how often segments are merged. Once there are ** FTS3_MERGE_COUNT segments of level N, they are merged into a single @@ -56,18 +54,22 @@ #define FTS3_VARINT_MAX 10 /* -** FTS4 virtual tables may maintain two separate indexes. One that indexes -** all document terms (the same index FTS3 tables maintain) and another used -** for prefixes. B+-trees that are part of the prefix index have values for -** the %_segdir.level column that are equal to or greater than the following -** value. +** FTS4 virtual tables may maintain multiple indexes - one index of all terms +** in the document set and zero or more prefix indexes. All indexes are stored +** as one or more b+-trees in the %_segments and %_segdir tables. +** +** It is possible to determine which index a b+-tree belongs to based on the +** value stored in the "%_segdir.level" column. Given this value L, the index +** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with +** level values between 0 and 1023 (inclusive) belong to index 0, all levels +** between 1024 and 2047 to index 1, and so on. ** -** It is considered impossible for the regular index to use levels this large. -** In theory it could, but that would require that at least 2^1024 separate -** write operations to be made within the lifetime of the database. +** It is considered impossible for an index to use more than 1024 levels. In +** theory though this may happen, but only after at least +** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables. */ -#define FTS3_SEGDIR_PREFIXLEVEL 1024 -#define FTS3_SEGDIR_PREFIXLEVEL_STR "1024" +#define FTS3_SEGDIR_MAXLEVEL 1024 +#define FTS3_SEGDIR_MAXLEVEL_STR "1024" /* ** The testcase() macro is only used by the amalgamation. If undefined, @@ -172,23 +174,32 @@ struct Fts3Table { int nNodeSize; /* Soft limit for node size */ u8 bHasStat; /* True if %_stat table exists */ u8 bHasDocsize; /* True if %_docsize table exists */ - u8 bPrefix; /* True if there is a prefix index */ int nPgsz; /* Page size for host database */ char *zSegmentsTbl; /* Name of %_segments table */ sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ - /* The following hash table is used to buffer pending index updates during + /* TODO: Fix the first paragraph of this comment. + ** + ** 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. ** When nPendingData exceeds nMaxPendingData, the buffer is flushed ** automatically. Variable iPrevDocid is the docid of the most recently ** inserted record. + ** + ** A single FTS4 table may have multiple full-text indexes. For each index + ** there is an entry in the aIndex[] array. Index 0 is an index of all the + ** terms that appear in the document set. Each subsequent index in aIndex[] + ** is an index of prefixes of a specific length. */ - int nMaxPendingData; - int nPendingData; - sqlite_int64 iPrevDocid; - Fts3Hash pendingTerms; - Fts3Hash pendingPrefixes; + int nIndex; /* Size of aIndex[] */ + struct Fts3Index { + int nPrefix; /* Prefix length (0 for main terms index) */ + Fts3Hash hPending; /* Pending terms table for this index */ + } *aIndex; + int nMaxPendingData; /* Max pending data before flush to disk */ + int nPendingData; /* Current bytes of pending data */ + sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ #if defined(SQLITE_DEBUG) /* State variables used for validating that the transaction control @@ -336,10 +347,10 @@ int sqlite3Fts3Optimize(Fts3Table *); int sqlite3Fts3SegReaderNew(int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); int sqlite3Fts3SegReaderPending( - Fts3Table*,const char*,int,int,int,Fts3SegReader**); + Fts3Table*,int,const char*,int,int,Fts3SegReader**); void sqlite3Fts3SegReaderFree(Fts3SegReader *); int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *); -int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **); +int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **); int sqlite3Fts3ReadLock(Fts3Table *); int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*); @@ -355,15 +366,13 @@ void sqlite3Fts3SegmentsClose(Fts3Table *); /* Special values interpreted by sqlite3SegReaderCursor() */ #define FTS3_SEGCURSOR_PENDING -1 -#define FTS3_SEGCURSOR_PENDING_PREFIX -2 -#define FTS3_SEGCURSOR_ALL_PREFIX -3 -#define FTS3_SEGCURSOR_ALL_TERM -4 +#define FTS3_SEGCURSOR_ALL -2 int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*); int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *); void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *); int sqlite3Fts3SegReaderCursor( - Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *); + Fts3Table *, int, int, const char *, int, int, int, Fts3SegReaderCursor *); /* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 diff --git a/ext/fts3/fts3_aux.c b/ext/fts3/fts3_aux.c index cb91cda347..5c7c7b0b0f 100644 --- a/ext/fts3/fts3_aux.c +++ b/ext/fts3/fts3_aux.c @@ -96,6 +96,7 @@ static int fts3auxConnectMethod( p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; p->pFts3Tab->db = db; + p->pFts3Tab->nIndex = 1; memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); @@ -375,7 +376,7 @@ static int fts3auxFilterMethod( if( pCsr->zStop==0 ) return SQLITE_NOMEM; } - rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL_TERM, + rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL, pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr ); if( rc==SQLITE_OK ){ diff --git a/ext/fts3/fts3_term.c b/ext/fts3/fts3_term.c index ca46269689..d7aa66855a 100644 --- a/ext/fts3/fts3_term.c +++ b/ext/fts3/fts3_term.c @@ -27,7 +27,7 @@ typedef struct Fts3termCursor Fts3termCursor; struct Fts3termTable { sqlite3_vtab base; /* Base class used by SQLite core */ - int bPrefix; /* True for an fts4prefix table */ + int iIndex; /* Index for Fts3Table.aIndex[] */ Fts3Table *pFts3Tab; }; @@ -70,6 +70,12 @@ static int fts3termConnectMethod( int nByte; /* Bytes of space to allocate here */ int rc; /* value returned by declare_vtab() */ Fts3termTable *p; /* Virtual table object to return */ + int iIndex = 0; + + if( argc==5 ){ + iIndex = atoi(argv[4]); + argc--; + } /* The user should specify a single argument - the name of an fts3 table. */ if( argc!=4 ){ @@ -96,7 +102,8 @@ static int fts3termConnectMethod( p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; p->pFts3Tab->db = db; - p->bPrefix = (int)pCtx; + p->pFts3Tab->nIndex = iIndex+1; + p->iIndex = iIndex; memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); @@ -263,8 +270,7 @@ static int fts3termFilterMethod( pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; pCsr->filter.flags |= FTS3_SEGMENT_SCAN; - rc = sqlite3Fts3SegReaderCursor(pFts3, - p->bPrefix ? FTS3_SEGCURSOR_ALL_PREFIX : FTS3_SEGCURSOR_ALL_TERM, + rc = sqlite3Fts3SegReaderCursor(pFts3, p->iIndex, FTS3_SEGCURSOR_ALL, pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr ); if( rc==SQLITE_OK ){ @@ -355,9 +361,6 @@ int sqlite3Fts3InitTerm(sqlite3 *db){ int rc; /* Return code */ rc = sqlite3_create_module(db, "fts4term", &fts3term_module, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module(db, "fts4prefix", &fts3term_module, (void*)1); - } return rc; } diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 5ff7e0b1aa..9a136c3a65 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -183,10 +183,10 @@ struct SegmentNode { #define SQL_NEXT_SEGMENTS_ID 10 #define SQL_INSERT_SEGDIR 11 #define SQL_SELECT_LEVEL 12 -#define SQL_SELECT_ALL_LEVEL 13 +#define SQL_SELECT_LEVEL_RANGE 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_MAX_LEVEL 15 -#define SQL_DELETE_SEGDIR_BY_LEVEL 16 +#define SQL_DELETE_SEGDIR_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 #define SQL_DELETE_DOCSIZE 19 @@ -194,10 +194,11 @@ struct SegmentNode { #define SQL_SELECT_DOCSIZE 21 #define SQL_SELECT_DOCTOTAL 22 #define SQL_REPLACE_DOCTOTAL 23 -#define SQL_SELECT_ALL_PREFIX_LEVEL 24 +#define SQL_SELECT_ALL_PREFIX_LEVEL 24 #define SQL_DELETE_ALL_TERMS_SEGDIR 25 -#define SQL_DELETE_ALL_PREFIX_SEGDIR 26 + +#define SQL_DELETE_SEGDIR_RANGE 26 /* ** This function is used to obtain an SQLite prepared statement handle @@ -234,12 +235,11 @@ static int fts3SqlStmt( /* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root " "FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC", /* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root " - "FROM %Q.'%q_segdir' WHERE level < " FTS3_SEGDIR_PREFIXLEVEL_STR - " ORDER BY level DESC, idx ASC", + "FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?" + "ORDER BY level DESC, idx ASC", /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", -/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level < (?+1)*" - FTS3_SEGDIR_PREFIXLEVEL_STR, +/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", @@ -249,11 +249,11 @@ static int fts3SqlStmt( /* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", /* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0", /* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)", -/* 24 */ "SELECT idx, start_block, leaves_end_block, end_block, root " - "FROM %Q.'%q_segdir' WHERE level >= " FTS3_SEGDIR_PREFIXLEVEL_STR - " ORDER BY level DESC, idx ASC", -/* 25 */ "DELETE FROM %Q.'%q_segdir' WHERE level<" FTS3_SEGDIR_PREFIXLEVEL_STR, -/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level>=" FTS3_SEGDIR_PREFIXLEVEL_STR, +/* 24 */ "", +/* 25 */ "", + +/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", + }; int rc = SQLITE_OK; sqlite3_stmt *pStmt; @@ -409,17 +409,32 @@ int sqlite3Fts3ReadLock(Fts3Table *p){ ** 3: end_block ** 4: root */ -int sqlite3Fts3AllSegdirs(Fts3Table *p, int iLevel, sqlite3_stmt **ppStmt){ +int sqlite3Fts3AllSegdirs( + Fts3Table *p, /* FTS3 table */ + int iIndex, /* Index for p->aIndex[] */ + int iLevel, /* Level to select */ + sqlite3_stmt **ppStmt /* OUT: Compiled statement */ +){ int rc; sqlite3_stmt *pStmt = 0; - if( iLevel==FTS3_SEGCURSOR_ALL_PREFIX ){ - rc = fts3SqlStmt(p, SQL_SELECT_ALL_PREFIX_LEVEL, &pStmt, 0); - }else if( iLevel==FTS3_SEGCURSOR_ALL_TERM ){ - rc = fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, &pStmt, 0); + + assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel>=0 ); + assert( iLevel=0 && iIndexnIndex ); + + if( iLevel==FTS3_SEGCURSOR_ALL ){ + /* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */ + rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL); + sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL-1); + } }else{ - assert( iLevel>=0 ); + /* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); - if( rc==SQLITE_OK ) sqlite3_bind_int(pStmt, 1, iLevel); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pStmt, 1, iLevel+iIndex*FTS3_SEGDIR_MAXLEVEL); + } } *ppStmt = pStmt; return rc; @@ -565,9 +580,6 @@ static int fts3PendingTermsAddOne( return rc; } - - - /* ** Tokenize the nul-terminated string zText and add all tokens to the ** pending-terms hash-table. The docid used is that currently stored in @@ -616,6 +628,7 @@ static int fts3PendingTermsAdd( while( SQLITE_OK==rc && SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos)) ){ + int i; if( iPos>=nWord ) nWord = iPos+1; /* Positions cannot be negative; we use -1 as a terminator internally. @@ -626,12 +639,19 @@ static int fts3PendingTermsAdd( break; } - rc = fts3PendingTermsAddOne(p,iCol,iPos,&p->pendingTerms,zToken,nToken); - if( p->bPrefix ){ - int n = (nToken > FTS3_MAX_PREFIX ? FTS3_MAX_PREFIX : nToken); - for(; n>0 && rc==SQLITE_OK; n--){ - rc = fts3PendingTermsAddOne(p,iCol,iPos,&p->pendingPrefixes,zToken,n); - } + /* Add the term to the terms index */ + rc = fts3PendingTermsAddOne( + p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken + ); + + /* Add the term to each of the prefix indexes that it is not too + ** short for. */ + for(i=1; rc==SQLITE_OK && inIndex; i++){ + struct Fts3Index *pIndex = &p->aIndex[i]; + if( nTokennPrefix ) continue; + rc = fts3PendingTermsAddOne( + p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix + ); } } @@ -661,28 +681,21 @@ static int fts3PendingTermsDocid(Fts3Table *p, sqlite_int64 iDocid){ } /* -** Discard the contents of the pending-terms hash table. +** Discard the contents of the pending-terms hash tables. */ void sqlite3Fts3PendingTermsClear(Fts3Table *p){ - Fts3HashElem *pElem; - for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ - sqlite3_free(fts3HashData(pElem)); + int i; + for(i=0; inIndex; i++){ + Fts3HashElem *pElem; + Fts3Hash *pHash = &p->aIndex[i].hPending; + for(pElem=fts3HashFirst(pHash); pElem; pElem=fts3HashNext(pElem)){ + sqlite3_free(fts3HashData(pElem)); + } + fts3HashClear(pHash); } - fts3HashClear(&p->pendingTerms); p->nPendingData = 0; } -/* -** Discard the contents of the pending-prefixes hash table. -*/ -void sqlite3Fts3PendingPrefixesClear(Fts3Table *p){ - Fts3HashElem *pElem; - for(pElem=fts3HashFirst(&p->pendingPrefixes); pElem; pElem=fts3HashNext(pElem)){ - sqlite3_free(fts3HashData(pElem)); - } - fts3HashClear(&p->pendingPrefixes); -} - /* ** This function is called by the xUpdate() method as part of an INSERT ** operation. It adds entries for each term in the new record to the @@ -780,7 +793,6 @@ static int fts3DeleteAll(Fts3Table *p){ /* Discard the contents of the pending-terms hash table. */ sqlite3Fts3PendingTermsClear(p); - sqlite3Fts3PendingPrefixesClear(p); /* Delete everything from the %_content, %_segments and %_segdir tables. */ fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); @@ -836,7 +848,7 @@ static void fts3DeleteTerms( ** Forward declaration to account for the circular dependency between ** functions fts3SegmentMerge() and fts3AllocateSegdirIdx(). */ -static int fts3SegmentMerge(Fts3Table *, int); +static int fts3SegmentMerge(Fts3Table *, int, int); /* ** This function allocates a new level iLevel index in the segdir table. @@ -853,7 +865,12 @@ static int fts3SegmentMerge(Fts3Table *, int); ** If successful, *piIdx is set to the allocated index slot and SQLITE_OK ** returned. Otherwise, an SQLite error code is returned. */ -static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ +static int fts3AllocateSegdirIdx( + Fts3Table *p, + int iIndex, /* Index for p->aIndex */ + int iLevel, + int *piIdx +){ int rc; /* Return Code */ sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */ int iNext = 0; /* Result of query pNextIdx */ @@ -861,7 +878,7 @@ static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ /* Set variable iNext to the next available segdir index at level iLevel. */ rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pNextIdx, 1, iLevel); + sqlite3_bind_int(pNextIdx, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel); if( SQLITE_ROW==sqlite3_step(pNextIdx) ){ iNext = sqlite3_column_int(pNextIdx, 0); } @@ -875,7 +892,7 @@ static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ ** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext. */ if( iNext>=FTS3_MERGE_COUNT ){ - rc = fts3SegmentMerge(p, iLevel); + rc = fts3SegmentMerge(p, iIndex, iLevel); *piIdx = 0; }else{ *piIdx = iNext; @@ -1309,23 +1326,22 @@ static int fts3CompareElemByTerm(const void *lhs, const void *rhs){ */ int sqlite3Fts3SegReaderPending( Fts3Table *p, /* Virtual table handle */ + int iIndex, /* Index for p->aIndex */ const char *zTerm, /* Term to search for */ int nTerm, /* Size of buffer zTerm */ - int isMultiTerm, /* True to visit multiple terms */ - int isPrefixIter, /* 0->pendingTerms, 1->pendingPrefixes */ + int bPrefix, /* True for a prefix iterator */ Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */ ){ Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */ Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */ int nElem = 0; /* Size of array at aElem */ int rc = SQLITE_OK; /* Return Code */ + Fts3Hash *pHash; - if( isMultiTerm ){ + pHash = &p->aIndex[iIndex].hPending; + if( bPrefix ){ int nAlloc = 0; /* Size of allocated array at aElem */ Fts3HashElem *pE = 0; /* Iterator variable */ - Fts3Hash *pHash; - - pHash = (isPrefixIter ? &p->pendingPrefixes : &p->pendingTerms); for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){ char *zKey = (char *)fts3HashKey(pE); @@ -1360,7 +1376,7 @@ int sqlite3Fts3SegReaderPending( }else{ /* The query is a simple term lookup that matches at most one term in ** the index. All that is required is a straight hash-lookup. */ - Fts3HashElem *pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm); + Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm); if( pE ){ aElem = &pE; nElem = 1; @@ -1380,7 +1396,7 @@ int sqlite3Fts3SegReaderPending( } } - if( isMultiTerm ){ + if( bPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; @@ -1992,28 +2008,28 @@ static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){ } /* -** Set *pnMax to the largest segment level in the database for either the -** terms index (if parameter bPrefixIndex is 0) or the prefixes index (if -** parameter bPrefixIndex is 1). +** Set *pnMax to the largest segment level in the database for the index +** iIndex. ** ** Segment levels are stored in the 'level' column of the %_segdir table. ** ** Return SQLITE_OK if successful, or an SQLite error code if not. */ -static int fts3SegmentMaxLevel(Fts3Table *p, int bPrefixIndex, int *pnMax){ +static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){ sqlite3_stmt *pStmt; int rc; - assert( bPrefixIndex==0 || bPrefixIndex==1 ); + assert( iIndex>=0 && iIndexnIndex ); /* Set pStmt to the compiled version of: ** - ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level < (?+1) * 1024 + ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? ** ** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR). */ rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0); if( rc!=SQLITE_OK ) return rc; - sqlite3_bind_int(pStmt, 1, bPrefixIndex); + sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL); + sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL - 1); if( SQLITE_ROW==sqlite3_step(pStmt) ){ *pnMax = sqlite3_column_int(pStmt, 0); } @@ -2036,6 +2052,7 @@ static int fts3SegmentMaxLevel(Fts3Table *p, int bPrefixIndex, int *pnMax){ */ static int fts3DeleteSegdir( Fts3Table *p, /* Virtual table handle */ + int iIndex, /* Index for p->aIndex */ int iLevel, /* Level of %_segdir entries to delete */ Fts3SegReader **apSegment, /* Array of SegReader objects */ int nReader /* Size of array apSegment */ @@ -2058,27 +2075,23 @@ static int fts3DeleteSegdir( return rc; } - assert( iLevel>=0 - || iLevel==FTS3_SEGCURSOR_ALL_TERM - || iLevel==FTS3_SEGCURSOR_ALL_PREFIX - || iLevel==FTS3_SEGCURSOR_PENDING - || iLevel==FTS3_SEGCURSOR_PENDING_PREFIX - ); - if( iLevel==FTS3_SEGCURSOR_ALL_TERM ){ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_TERMS_SEGDIR, 0); - }else if( iLevel==FTS3_SEGCURSOR_ALL_PREFIX ){ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_PREFIX_SEGDIR, 0); - }else if( iLevel==FTS3_SEGCURSOR_PENDING_PREFIX ){ - sqlite3Fts3PendingPrefixesClear(p); - }else if( iLevel==FTS3_SEGCURSOR_PENDING ){ - sqlite3Fts3PendingTermsClear(p); - }else if( iLevel>=0 ){ - rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0); + assert( iLevel>=0 || iLevel==FTS3_SEGCURSOR_ALL ); + if( iLevel==FTS3_SEGCURSOR_ALL ){ + rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pDelete, 1, iLevel); - sqlite3_step(pDelete); - rc = sqlite3_reset(pDelete); + sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL); + sqlite3_bind_int(pDelete, 2, (iIndex+1) * FTS3_SEGDIR_MAXLEVEL - 1); } + }else{ + rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel); + } + } + + if( rc==SQLITE_OK ){ + sqlite3_step(pDelete); + rc = sqlite3_reset(pDelete); } return rc; @@ -2323,46 +2336,52 @@ void sqlite3Fts3SegReaderFinish( ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ -static int fts3SegmentMerge(Fts3Table *p, int iLevel){ +static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){ int rc; /* Return code */ int iIdx = 0; /* Index of new segment */ - int iNewLevel = 0; /* Level to create new segment at */ + int iNewLevel = 0; /* Level/index to create new segment at */ SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */ Fts3SegFilter filter; /* Segment term filter condition */ Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */ int bIgnoreEmpty = 0; /* True to ignore empty segments */ - rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr); + assert( iLevel==FTS3_SEGCURSOR_ALL + || iLevel==FTS3_SEGCURSOR_PENDING + || iLevel>=0 + ); + assert( iLevel=0 && iIndexnIndex ); + + rc = sqlite3Fts3SegReaderCursor(p, iIndex, iLevel, 0, 0, 1, 0, &csr); if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished; - if( iLevel==FTS3_SEGCURSOR_ALL_TERM || iLevel==FTS3_SEGCURSOR_ALL_PREFIX ){ + if( iLevel==FTS3_SEGCURSOR_ALL ){ /* This call is to merge all segments in the database to a single ** segment. The level of the new segment is equal to the the numerically - ** greatest segment level currently present in the database. The index - ** of the new segment is always 0. */ + ** greatest segment level currently present in the database for this + ** index. The idx of the new segment is always 0. */ if( csr.nSegment==1 ){ rc = SQLITE_DONE; goto finished; } - rc = fts3SegmentMaxLevel(p, iLevel==FTS3_SEGCURSOR_ALL_PREFIX, &iNewLevel); + rc = fts3SegmentMaxLevel(p, iIndex, &iNewLevel); bIgnoreEmpty = 1; + + }else if( iLevel==FTS3_SEGCURSOR_PENDING ){ + iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL; + rc = fts3AllocateSegdirIdx(p, iIndex, 0, &iIdx); }else{ /* This call is to merge all segments at level iLevel. find the next ** available segment index at level iLevel+1. The call to ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to ** a single iLevel+2 segment if necessary. */ - if( iLevel==FTS3_SEGCURSOR_PENDING ){ - iNewLevel = 0; - }else if( iLevel==FTS3_SEGCURSOR_PENDING_PREFIX ){ - iNewLevel = FTS3_SEGDIR_PREFIXLEVEL; - }else{ - iNewLevel = iLevel+1; - } - rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx); + rc = fts3AllocateSegdirIdx(p, iIndex, iLevel+1, &iIdx); + iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL + iLevel+1; } if( rc!=SQLITE_OK ) goto finished; assert( csr.nSegment>0 ); - assert( iNewLevel>=0 ); + assert( iNewLevel>=(iIndex*FTS3_SEGDIR_MAXLEVEL) ); + assert( iNewLevel<((iIndex+1)*FTS3_SEGDIR_MAXLEVEL) ); memset(&filter, 0, sizeof(Fts3SegFilter)); filter.flags = FTS3_SEGMENT_REQUIRE_POS; @@ -2378,8 +2397,10 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ if( rc!=SQLITE_OK ) goto finished; assert( pWriter ); - rc = fts3DeleteSegdir(p, iLevel, csr.apSegment, csr.nSegment); - if( rc!=SQLITE_OK ) goto finished; + if( iLevel!=FTS3_SEGCURSOR_PENDING ){ + rc = fts3DeleteSegdir(p, iIndex, iLevel, csr.apSegment, csr.nSegment); + if( rc!=SQLITE_OK ) goto finished; + } rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); finished: @@ -2390,19 +2411,16 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ /* -** Flush the contents of pendingTerms to a level 0 segment. +** Flush the contents of pendingTerms to level 0 segments. */ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ int rc = SQLITE_OK; - if( p->bPrefix ){ - rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING_PREFIX); - } - if( rc==SQLITE_OK || rc==SQLITE_DONE ){ - rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING); - } - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; + int i; + for(i=0; rc==SQLITE_OK && inIndex; i++){ + rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_PENDING); + if( rc==SQLITE_DONE ) rc = SQLITE_OK; } + sqlite3Fts3PendingTermsClear(p); return rc; } @@ -2554,6 +2572,23 @@ static void fts3UpdateDocTotals( sqlite3_free(a); } +static int fts3DoOptimize(Fts3Table *p, int bReturnDone){ + int i; + int bSeenDone = 0; + int rc = SQLITE_OK; + for(i=0; rc==SQLITE_OK && inIndex; i++){ + rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_ALL); + if( rc==SQLITE_DONE ){ + bSeenDone = 1; + rc = SQLITE_OK; + } + } + sqlite3Fts3SegmentsClose(p); + sqlite3Fts3PendingTermsClear(p); + + return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc; +} + /* ** Handle a 'special' INSERT of the form: ** @@ -2570,10 +2605,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ if( !zVal ){ return SQLITE_NOMEM; }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ - rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_PREFIX); - if( rc==SQLITE_OK ){ - rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_TERM); - } + rc = fts3DoOptimize(p, 0); #ifdef SQLITE_TEST }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ p->nNodeSize = atoi(&zVal[9]); @@ -2586,9 +2618,6 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ rc = SQLITE_ERROR; } - sqlite3Fts3SegmentsClose(p); - sqlite3Fts3PendingTermsClear(p); - sqlite3Fts3PendingPrefixesClear(p); return rc; } @@ -2905,30 +2934,19 @@ int sqlite3Fts3UpdateMethod( */ int sqlite3Fts3Optimize(Fts3Table *p){ int rc; - int bReturnDone = 0; rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0); if( rc==SQLITE_OK ){ - rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_PREFIX); - if( rc==SQLITE_OK ){ - rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_TERM); - } - if( rc==SQLITE_DONE ){ - bReturnDone = 1; - rc = SQLITE_OK; - } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); - if( rc==SQLITE_OK ){ - sqlite3Fts3PendingTermsClear(p); - sqlite3Fts3PendingPrefixesClear(p); - } + rc = fts3DoOptimize(p, 1); + if( rc==SQLITE_OK || rc==SQLITE_DONE ){ + int rc2 = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); + if( rc2!=SQLITE_OK ) rc = rc2; }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); } } sqlite3Fts3SegmentsClose(p); - return ((rc==SQLITE_OK && bReturnDone) ? SQLITE_DONE : rc); + return rc; } #endif diff --git a/manifest b/manifest index bf93a84328..aff0cdf712 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C If\sthe\sfts4\soption\sprefix=1\sis\sspecified,\shave\sthe\sfts4\smodule\smaintain\san\sindex\sof\sprefixes\sas\swell\sas\sterms. -D 2011-05-24T18:49:45.786 +C Change\sfts4\sso\sthat\sthe\sprefix=\sparameter\sis\spasses\sa\scomma-separated\slist\sof\sintegers.\sFor\seach\sinteger\sN,\sa\sseparate\sindex\sof\sall\sprefixes\sof\slength\sN\sbytes\sis\screated. +D 2011-05-25T18:34:53.807 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 11dcc00a8d0e5202def00e81732784fb0cc4fe1d F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -61,21 +61,21 @@ 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 4a48bfef342badba0a71bdeb5354edaa3ad83382 +F ext/fts3/fts3.c 9ebda4cd406e5aa234977caa97c2045d36048753 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h 02699211c0b6cf5aa713cc68c527c9a6e9159fbe -F ext/fts3/fts3_aux.c d68d8e4d39e0342302d2c834618755af7c8058ea +F ext/fts3/fts3Int.h deeeac21d17da06683a79b40ae119b93cf86f90a +F ext/fts3/fts3_aux.c 2817a1ec9ffbad673cb1a1527ad807811bc7645b F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93 F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295 F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2 F ext/fts3/fts3_snippet.c 92b40397b28422c35c4127492d7ac6da34d1966a -F ext/fts3/fts3_term.c cd226a311940b8ef414d5c1f7c74971a47cacedb +F ext/fts3/fts3_term.c 0ade1812c0e97f394b58299810dfd5d2fb7ba501 F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3 F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d -F ext/fts3/fts3_write.c 0fd6a55c774731852f889007fc6edb1b99819ee5 +F ext/fts3/fts3_write.c 416b367f56f3100314fa76615535178718c645e1 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 @@ -473,7 +473,7 @@ F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844 -F test/fts3prefix.test 5b4e08c63d5d4a79e54754dc6b2209b03c885200 +F test/fts3prefix.test c51b04f38ce232cd0c9edd3cc60dc981db5e2726 F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2 F test/fts3rnd.test 2b1a579be557ab8ac54a51b39caa4aa8043cc4ad F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2 @@ -939,10 +939,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 651ef24249d8c22c4f13e4c0bb98a60099cfd23a -R c499b030bb5d74c4afca730dec7a05bc -T *branch * fts3-prefix-search -T *sym-fts3-prefix-search * -T -sym-trunk * +P b5bdc639898ee22eebedeb560810e94e74de8aa4 +R e9f427c5bc714eae400763155f710174 U dan -Z 0c60a0f2885eb6df2e24e22407faffbe +Z 6f0f673a9e9eaf0f72d887565c9f2227 diff --git a/manifest.uuid b/manifest.uuid index 747bc2e8b4..3ae4c6e5e2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b5bdc639898ee22eebedeb560810e94e74de8aa4 \ No newline at end of file +be59bf49402d2e2f4b95fb6668849f3745cb7bf2 \ No newline at end of file diff --git a/test/fts3prefix.test b/test/fts3prefix.test index 95a5c89629..2e0c7a8ede 100644 --- a/test/fts3prefix.test +++ b/test/fts3prefix.test @@ -16,56 +16,75 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix fts3prefix +ifcapable !fts3 { + finish_test + return +} + # This proc tests that the prefixes index appears to represent the same content # as the terms index. # -proc fts3_terms_and_prefixes {db tbl} { - $db eval "CREATE VIRTUAL TABLE fts3check1 USING fts4term($tbl);" - $db eval "CREATE VIRTUAL TABLE fts3check2 USING fts4prefix($tbl);" - - $db eval { - CREATE TEMP TABLE terms AS SELECT * FROM fts3check1; - CREATE TEMP TABLE prefixes AS SELECT * FROM fts3check2; - CREATE INDEX temp.idx ON prefixes(term); - DROP TABLE fts3check1; - DROP TABLE fts3check2; - } +proc fts3_terms_and_prefixes {db tbl prefixlengths} { + + set iIndex 0 + foreach len $prefixlengths { + incr iIndex + $db eval { + DROP TABLE IF EXISTS fts3check1; + DROP TABLE IF EXISTS fts3check2; + } + $db eval "CREATE VIRTUAL TABLE fts3check1 USING fts4term($tbl, 0);" + $db eval "CREATE VIRTUAL TABLE fts3check2 USING fts4term($tbl, $iIndex);" + + $db eval { + DROP TABLE IF EXISTS temp.terms; + DROP TABLE IF EXISTS temp.prefixes; + CREATE TEMP TABLE terms AS SELECT * FROM fts3check1; + CREATE TEMP TABLE prefixes AS SELECT * FROM fts3check2; + CREATE INDEX temp.idx ON prefixes(term); + DROP TABLE fts3check1; + DROP TABLE fts3check2; + } - $db eval { SELECT term, docid, col, pos FROM temp.terms } a { - set nMax [expr [string length $a(term)] - 1] - if {$nMax>8} {set nMax 8} - for {set n 0} {$n < $nMax} {incr n} { - set t [string range $a(term) 0 $n] + set nExpect 0 + $db eval { SELECT term, docid, col, pos FROM temp.terms } a { + if {[string length $a(term)]<$len} continue + incr nExpect + set prefix [string range $a(term) 0 [expr $len-1]] set r [$db one { SELECT count(*) FROM temp.prefixes WHERE - term = $t AND docid = $a(docid) AND col = $a(col) AND pos = $a(pos) + term = $prefix AND docid = $a(docid) AND col = $a(col) AND pos = $a(pos) }] if {$r != 1} { error "$t, $a(docid), $a(col), $a(pos)" } } - } - execsql { DROP TABLE temp.prefixes } - execsql { DROP TABLE temp.terms } - - set terms_layout [$db eval " - SELECT level, idx FROM ${tbl}_segdir WHERE level < 1024 ORDER by 1, 2 - "] - set prefixes_layout [$db eval " - SELECT level-1024, idx FROM ${tbl}_segdir WHERE level >= 1024 ORDER by 1, 2 - "] + set nCount [$db one {SELECT count(*) FROM temp.prefixes}] + if {$nCount != $nExpect} { + error "prefixes.count(*) is $nCount expected $nExpect" + } + + execsql { DROP TABLE temp.prefixes } + execsql { DROP TABLE temp.terms } + + set list [list] + $db eval " + SELECT sum( 1 << (16*(level%1024)) ) AS total, (level/1024) AS tree + FROM ${tbl}_segdir GROUP BY tree + " { + lappend list [list $total $tree] + } - if {$terms_layout != $prefixes_layout} { - puts "TERMS LAYOUT: $terms_layout" - puts "PREFIX LAYOUT: $prefixes_layout" - error "Terms and prefixes are comprised of different b-trees" + if { [lsort -integer -index 0 $list] != [lsort -integer -index 1 $list] } { + error "inconsistent tree structures: $list" + } } return "" } -proc fts3_tap_test {tn db tbl} { - uplevel [list do_test $tn [list fts3_terms_and_prefixes $db $tbl] ""] +proc fts3_tap_test {tn db tbl lens} { + uplevel [list do_test $tn [list fts3_terms_and_prefixes $db $tbl $lens] ""] } #------------------------------------------------------------------------- @@ -73,21 +92,24 @@ proc fts3_tap_test {tn db tbl} { # being constructed correctly for the simplest possible case. # do_execsql_test 1.1 { - CREATE VIRTUAL TABLE t1 USING fts4(prefix=1); - CREATE VIRTUAL TABLE prefixes USING fts4prefix(t1); + CREATE VIRTUAL TABLE t1 USING fts4(prefix='1,3,6'); + + CREATE VIRTUAL TABLE p1 USING fts4term(t1, 1); + CREATE VIRTUAL TABLE p2 USING fts4term(t1, 2); + CREATE VIRTUAL TABLE p3 USING fts4term(t1, 3); CREATE VIRTUAL TABLE terms USING fts4term(t1); } do_execsql_test 1.2 { INSERT INTO t1 VALUES('sqlite mysql firebird'); } -do_execsql_test 1.3 { - SELECT term FROM prefixes; -} {f fi fir fire fireb firebi firebir firebird m my mys mysq mysql s sq sql sqli sqlit sqlite} +do_execsql_test 1.3.1 { SELECT term FROM p1 } {f m s} +do_execsql_test 1.3.2 { SELECT term FROM p2 } {fir mys sql} +do_execsql_test 1.3.3 { SELECT term FROM p3 } {firebi sqlite} do_execsql_test 1.4 { SELECT term FROM terms; } {firebird mysql sqlite} -fts3_tap_test 1.5 db t1 +fts3_tap_test 1.5 db t1 {1 3 6} #------------------------------------------------------------------------- # A slightly more complicated dataset. This test also verifies that DELETE @@ -115,15 +137,15 @@ do_execsql_test 2.1 { INSERT INTO t1 VALUES('Google. It is now developed and maintained as part'); INSERT INTO t1 VALUES('of SQLite. '); } -fts3_tap_test 2.2 db t1 +fts3_tap_test 2.2 db t1 {1 3 6} do_execsql_test 2.3 { DELETE FROM t1 WHERE docid%2; } -fts3_tap_test 2.4 db t1 +fts3_tap_test 2.4 db t1 {1 3 6} do_execsql_test 2.5 { INSERT INTO t1(t1) VALUES('optimize') } -fts3_tap_test 2.6 db t1 +fts3_tap_test 2.6 db t1 {1 3 6} do_execsql_test 3.1 { - CREATE VIRTUAL TABLE t2 USING fts4(prefix=1); + CREATE VIRTUAL TABLE t2 USING fts4(prefix='1,2,3'); INSERT INTO t2 VALUES('On 12 September the wind direction turned and'); INSERT INTO t2 VALUES('William''s fleet sailed. A storm blew up and the'); INSERT INTO t2 VALUES('fleet was forced to take shelter at'); @@ -148,16 +170,16 @@ do_execsql_test 3.1 { INSERT INTO t2 VALUES('and waited for Harold''s return from the north.'); } -fts3_tap_test 3.2 db t2 +fts3_tap_test 3.2 db t2 {1 2 3} do_execsql_test 3.3 { SELECT optimize(t2) FROM t2 LIMIT 1 } {{Index optimized}} -fts3_tap_test 3.4 db t2 +fts3_tap_test 3.4 db t2 {1 2 3} #------------------------------------------------------------------------- # Simple tests for reading the prefix-index. # do_execsql_test 4.1 { - CREATE VIRTUAL TABLE t3 USING fts4(prefix=1); + CREATE VIRTUAL TABLE t3 USING fts4(prefix="1,4"); INSERT INTO t3 VALUES('one two three'); INSERT INTO t3 VALUES('four five six'); INSERT INTO t3 VALUES('seven eight nine');