From: dan Date: Thu, 7 May 2015 19:29:46 +0000 (+0000) Subject: Change to storing all keys in a single merge-tree structure instead of one main struc... X-Git-Tag: version-3.8.11~114^2~49 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4591334dd4152852332836670c222c274949713b;p=thirdparty%2Fsqlite.git Change to storing all keys in a single merge-tree structure instead of one main structure and a separate one for each prefix index. This is a file-format change. Also introduce a mechanism for managing file-format changes. FossilOrigin-Name: a684b5e2d9d52cf4700e7e5f9dd547a2ba54e8e9 --- diff --git a/ext/fts5/fts5.c b/ext/fts5/fts5.c index a753d671da..df1646786a 100644 --- a/ext/fts5/fts5.c +++ b/ext/fts5/fts5.c @@ -1164,7 +1164,7 @@ static int fts5SpecialInsert( if( bError ){ rc = SQLITE_ERROR; }else{ - rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal); + rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal, 0); } } } diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 07c3a767b2..d09029710b 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -122,12 +122,16 @@ struct Fts5Config { char *zRankArgs; /* Arguments to rank function */ }; +/* Current expected value of %_config table 'version' field */ +#define FTS5_CURRENT_VERSION 1 + #define FTS5_CONTENT_NORMAL 0 #define FTS5_CONTENT_NONE 1 #define FTS5_CONTENT_EXTERNAL 2 + int sqlite3Fts5ConfigParse( Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** ); @@ -394,6 +398,7 @@ int sqlite3Fts5HashWrite( i64 iRowid, /* Rowid for this entry */ int iCol, /* Column token appears in (-ve -> delete) */ int iPos, /* Position of token within column */ + char bByte, const char *pToken, int nToken /* Token to add or remove to or from index */ ); @@ -458,7 +463,9 @@ int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow); int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit); int sqlite3Fts5StorageRollback(Fts5Storage *p); -int sqlite3Fts5StorageConfigValue(Fts5Storage *p, const char*, sqlite3_value*); +int sqlite3Fts5StorageConfigValue( + Fts5Storage *p, const char*, sqlite3_value*, int +); int sqlite3Fts5StorageSpecialDelete(Fts5Storage *p, i64 iDel, sqlite3_value**); diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 1b29351ec2..0846eec8f6 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -203,33 +203,6 @@ void sqlite3Fts5Dequote(char *z){ } } -/* -** Argument z points to a nul-terminated string containing an SQL identifier. -** This function returns a copy of the identifier enclosed in backtick -** quotes. -*/ -static char *fts5EscapeName(int *pRc, const char *z){ - char *pRet = 0; - if( *pRc==SQLITE_OK ){ - int n = strlen(z); - pRet = (char*)sqlite3_malloc(2 + 2*n + 1); - if( pRet==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - int i; - char *p = pRet; - *p++ = '`'; - for(i=0; izContentRowid); if( p->eContent!=FTS5_CONTENT_NONE ){ @@ -849,6 +821,7 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ char *zSql; sqlite3_stmt *p = 0; int rc; + int iVersion = 0; /* Set default values */ pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE; @@ -868,9 +841,17 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ while( SQLITE_ROW==sqlite3_step(p) ){ const char *zK = (const char*)sqlite3_column_text(p, 0); sqlite3_value *pVal = sqlite3_column_value(p, 1); - sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, 0); + if( 0==sqlite3_stricmp(zK, "version") ){ + iVersion = sqlite3_value_int(pVal); + }else{ + sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, 0); + } } - rc = sqlite3_finalize(p); + if( rc==SQLITE_OK ) rc = sqlite3_finalize(p); + } + + if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){ + rc = sqlite3Fts5Corrupt(); } if( rc==SQLITE_OK ){ diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 570331bfb1..b7018e4768 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -1371,7 +1371,7 @@ static char *fts5PrintfAppend(char *zApp, const char *zFmt, ...){ va_start(ap, zFmt); zNew = sqlite3_vmprintf(zFmt, ap); va_end(ap); - if( zApp ){ + if( zApp && zNew ){ char *zNew2 = sqlite3_mprintf("%s%s", zApp, zNew); sqlite3_free(zNew); zNew = zNew2; @@ -1548,12 +1548,14 @@ static void fts5ExprFunction( const char *zNearsetCmd = "nearset"; int nConfig; /* Size of azConfig[] */ Fts5Config *pConfig = 0; + int iArg = 1; if( bTcl && nArg>1 ){ zNearsetCmd = (const char*)sqlite3_value_text(apVal[1]); + iArg = 2; } - nConfig = nArg + 2 - bTcl; + nConfig = 3 + (nArg-iArg); azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig); if( azConfig==0 ){ sqlite3_result_error_nomem(pCtx); @@ -1562,9 +1564,10 @@ static void fts5ExprFunction( azConfig[0] = 0; azConfig[1] = "main"; azConfig[2] = "tbl"; - for(i=1+bTcl; ipRoot); } - if( rc==SQLITE_OK ){ + if( zText==0 ){ + rc = SQLITE_NOMEM; + }else{ sqlite3_result_text(pCtx, zText, -1, SQLITE_TRANSIENT); sqlite3_free(zText); } diff --git a/ext/fts5/fts5_hash.c b/ext/fts5/fts5_hash.c index 8bafbd6c82..39821d04a2 100644 --- a/ext/fts5/fts5_hash.c +++ b/ext/fts5/fts5_hash.c @@ -136,6 +136,16 @@ static unsigned int fts5HashKey(int nSlot, const char *p, int n){ return (h % nSlot); } +static unsigned int fts5HashKey2(int nSlot, char b, const char *p, int n){ + int i; + unsigned int h = 13; + for(i=n-1; i>=0; i--){ + h = (h << 3) ^ h ^ p[i]; + } + h = (h << 3) ^ h ^ b; + return (h % nSlot); +} + /* ** Resize the hash table by doubling the number of slots. */ @@ -191,36 +201,44 @@ int sqlite3Fts5HashWrite( i64 iRowid, /* Rowid for this entry */ int iCol, /* Column token appears in (-ve -> delete) */ int iPos, /* Position of token within column */ + char bByte, /* First byte of token */ const char *pToken, int nToken /* Token to add or remove to or from index */ ){ - unsigned int iHash = fts5HashKey(pHash->nSlot, pToken, nToken); + unsigned int iHash = fts5HashKey2(pHash->nSlot, bByte, pToken, nToken); Fts5HashEntry *p; u8 *pPtr; int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */ /* Attempt to locate an existing hash entry */ for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ - if( memcmp(p->zKey, pToken, nToken)==0 && p->zKey[nToken]==0 ) break; + if( p->zKey[0]==bByte + && memcmp(&p->zKey[1], pToken, nToken)==0 + && p->zKey[nToken+1]==0 + ){ + break; + } } /* If an existing hash entry cannot be found, create a new one. */ if( p==0 ){ - int nByte = sizeof(Fts5HashEntry) + nToken + 1 + 64; + int nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; if( nByte<128 ) nByte = 128; if( (pHash->nEntry*2)>=pHash->nSlot ){ int rc = fts5HashResize(pHash); if( rc!=SQLITE_OK ) return rc; - iHash = fts5HashKey(pHash->nSlot, pToken, nToken); + iHash = fts5HashKey2(pHash->nSlot, bByte, pToken, nToken); } p = (Fts5HashEntry*)sqlite3_malloc(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); p->nAlloc = nByte; - memcpy(p->zKey, pToken, nToken); - p->zKey[nToken] = '\0'; - p->nData = nToken + 1 + sizeof(Fts5HashEntry); + p->zKey[0] = bByte; + memcpy(&p->zKey[1], pToken, nToken); + assert( iHash==fts5HashKey(pHash->nSlot, p->zKey, nToken+1) ); + p->zKey[nToken+1] = '\0'; + p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry); p->nData += sqlite3PutVarint(&((u8*)p)[p->nData], iRowid); p->iSzPoslist = p->nData; p->nData += 1; diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 8701ff4059..a680c20c28 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -49,6 +49,12 @@ #define FTS5_MIN_DLIDX_SIZE 4 /* Add dlidx if this many empty pages */ +#define FTS5_MAIN_PREFIX '0' + +#if FTS5_MAX_PREFIX_INDEXES > 31 +# error "FTS5_MAX_PREFIX_INDEXES is too large" +#endif + /* ** Details: ** @@ -211,7 +217,7 @@ ** Rowids for the averages and structure records in the %_data table. */ #define FTS5_AVERAGES_ROWID 1 /* Rowid used for the averages record */ -#define FTS5_STRUCTURE_ROWID(iIdx) (10 + (iIdx)) /* For structure records */ +#define FTS5_STRUCTURE_ROWID 10 /* The structure record */ /* ** Macros determining the rowids used by segment nodes. All nodes in all @@ -233,22 +239,16 @@ ** to encode the three FTS5_SEGMENT_ROWID() arguments. This module returns ** SQLITE_FULL and fails the current operation if they ever prove too small. */ -#define FTS5_DATA_IDX_B 5 /* Max of 31 prefix indexes */ #define FTS5_DATA_ID_B 16 /* Max seg id number 65535 */ #define FTS5_DATA_HEIGHT_B 5 /* Max b-tree height of 32 */ #define FTS5_DATA_PAGE_B 31 /* Max page number of 2147483648 */ -#define FTS5_SEGMENT_ROWID(idx, segid, height, pgno) ( \ - ((i64)(idx) << (FTS5_DATA_ID_B + FTS5_DATA_PAGE_B + FTS5_DATA_HEIGHT_B)) + \ +#define FTS5_SEGMENT_ROWID(segid, height, pgno) ( \ ((i64)(segid) << (FTS5_DATA_PAGE_B + FTS5_DATA_HEIGHT_B)) + \ ((i64)(height) << (FTS5_DATA_PAGE_B)) + \ ((i64)(pgno)) \ ) -#if FTS5_MAX_PREFIX_INDEXES > ((1<pConfig; Fts5Structure *pRet = 0; /* Object to return */ Fts5Data *pData; /* %_data entry containing structure record */ int iCookie; /* Configuration cookie */ - assert( iIdx<=pConfig->nPrefix ); - pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID(iIdx)); + pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID); if( !pData ) return 0; p->rc = fts5StructureDecode(pData->p, pData->n, &iCookie, &pRet); @@ -1198,12 +1195,12 @@ static int fts5StructureCountSegments(Fts5Structure *pStruct){ #endif /* -** Serialize and store the "structure" record for index iIdx. +** Serialize and store the "structure" record. ** ** If an error occurs, leave an error code in the Fts5Index object. If an ** error has already occurred, this function is a no-op. */ -static void fts5StructureWrite(Fts5Index *p, int iIdx, Fts5Structure *pStruct){ +static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){ if( p->rc==SQLITE_OK ){ Fts5Buffer buf; /* Buffer to serialize record into */ int iLvl; /* Used to iterate through levels */ @@ -1236,7 +1233,7 @@ static void fts5StructureWrite(Fts5Index *p, int iIdx, Fts5Structure *pStruct){ } } - fts5DataWrite(p, FTS5_STRUCTURE_ROWID(iIdx), buf.p, buf.n); + fts5DataWrite(p, FTS5_STRUCTURE_ROWID, buf.p, buf.n); fts5BufferFree(&buf); } } @@ -1532,7 +1529,7 @@ static int fts5DlidxIterPrev(Fts5DlidxIter *pIter){ static Fts5DlidxIter *fts5DlidxIterInit( Fts5Index *p, /* Fts5 Backend to iterate within */ int bRev, /* True for ORDER BY ASC */ - int iIdx, int iSegid, /* Segment iSegid within index iIdx */ + int iSegid, /* Segment id */ int iLeafPg /* Leaf page number to load dlidx for */ ){ Fts5DlidxIter *pIter; @@ -1540,7 +1537,7 @@ static Fts5DlidxIter *fts5DlidxIterInit( pIter = (Fts5DlidxIter*)fts5IdxMalloc(p, sizeof(Fts5DlidxIter)); if( pIter==0 ) return 0; - pIter->pData = fts5DataRead(p, FTS5_DOCLIST_IDX_ROWID(iIdx, iSegid, iLeafPg)); + pIter->pData = fts5DataRead(p, FTS5_DOCLIST_IDX_ROWID(iSegid, iLeafPg)); if( pIter->pData==0 ){ sqlite3_free(pIter); pIter = 0; @@ -1583,7 +1580,7 @@ static void fts5SegIterNextPage( pIter->iLeafPgno++; if( pIter->iLeafPgno<=pSeg->pgnoLast ){ pIter->pLeaf = fts5DataRead(p, - FTS5_SEGMENT_ROWID(pIter->iIdx, pSeg->iSegid, 0, pIter->iLeafPgno) + FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, pIter->iLeafPgno) ); }else{ pIter->pLeaf = 0; @@ -1669,15 +1666,14 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ /* ** Initialize the iterator object pIter to iterate through the entries in -** segment pSeg within index iIdx. The iterator is left pointing to the -** first entry when this function returns. +** segment pSeg. The iterator is left pointing to the first entry when +** this function returns. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If ** an error has already occurred when this function is called, it is a no-op. */ static void fts5SegIterInit( - Fts5Index *p, - int iIdx, /* Config.aHash[] index of FTS index */ + Fts5Index *p, /* FTS index object */ Fts5StructureSegment *pSeg, /* Description of segment */ Fts5SegIter *pIter /* Object to populate */ ){ @@ -1694,7 +1690,6 @@ static void fts5SegIterInit( if( p->rc==SQLITE_OK ){ memset(pIter, 0, sizeof(*pIter)); pIter->pSeg = pSeg; - pIter->iIdx = iIdx; pIter->iLeafPgno = pSeg->pgnoFirst-1; fts5SegIterNextPage(p, pIter); } @@ -1771,7 +1766,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ Fts5Data *pNew; pIter->iLeafPgno--; pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID( - pIter->iIdx, pIter->pSeg->iSegid, 0, pIter->iLeafPgno + pIter->pSeg->iSegid, 0, pIter->iLeafPgno )); if( pNew ){ if( pIter->iLeafPgno==pIter->iTermLeafPgno ){ @@ -1879,8 +1874,8 @@ static void fts5SegIterNext( const char *zTerm; int nList; if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ - sqlite3Fts5HashScanNext(p->apHash[0]); - sqlite3Fts5HashScanEntry(p->apHash[0], &zTerm, &pList, &nList); + sqlite3Fts5HashScanNext(p->pHash); + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); } if( pList==0 ){ fts5DataRelease(pIter->pLeaf); @@ -1935,7 +1930,7 @@ static void fts5SegIterNext( ** function sets the iterator up so that iterates in reverse order through ** the doclist. */ -static void fts5SegIterReverse(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ +static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ Fts5DlidxIter *pDlidx = pIter->pDlidx; Fts5Data *pLast = 0; int pgnoLast = 0; @@ -1946,7 +1941,7 @@ static void fts5SegIterReverse(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ if( fts5DlidxIterEof(p, pDlidx)==0 ){ int iSegid = pIter->pSeg->iSegid; pgnoLast = pDlidx->iLeafPgno; - pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, pgnoLast)); + pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, 0, pgnoLast)); }else{ pIter->iLeafOffset -= sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel); } @@ -1989,7 +1984,7 @@ static void fts5SegIterReverse(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ /* The last rowid in the doclist may not be on the current page. Search ** forward to find the page containing the last rowid. */ for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){ - i64 iAbs = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, pgno); + i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, pgno); Fts5Data *pNew = fts5DataRead(p, iAbs); if( pNew ){ int iRowid, iTerm; @@ -2029,13 +2024,12 @@ static void fts5SegIterReverse(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ } /* -** Iterator pIter currently points to the first rowid of a doclist within -** index iIdx. There is a doclist-index associated with the final term on -** the current page. If the current term is the last term on the page, -** load the doclist-index from disk and initialize an iterator at -** (pIter->pDlidx). +** Iterator pIter currently points to the first rowid of a doclist. +** There is a doclist-index associated with the final term on the current +** page. If the current term is the last term on the page, load the +** doclist-index from disk and initialize an iterator at (pIter->pDlidx). */ -static void fts5SegIterLoadDlidx(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ +static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){ int iSeg = pIter->pSeg->iSegid; int bRev = (pIter->flags & FTS5_SEGITER_REVERSE); Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ @@ -2062,20 +2056,18 @@ static void fts5SegIterLoadDlidx(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ } } - pIter->pDlidx = fts5DlidxIterInit(p, bRev, iIdx, iSeg, pIter->iTermLeafPgno); + pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno); } /* ** Initialize the object pIter to point to term pTerm/nTerm within segment -** pSeg, index iIdx. If there is no such term in the index, the iterator -** is set to EOF. +** pSeg. If there is no such term in the index, the iterator is set to EOF. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If ** an error has already occurred when this function is called, it is a no-op. */ static void fts5SegIterSeekInit( Fts5Index *p, /* FTS5 backend */ - int iIdx, /* Config.aHash[] index of FTS index */ const u8 *pTerm, int nTerm, /* Term to seek to */ int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5StructureSegment *pSeg, /* Description of segment */ @@ -2083,20 +2075,19 @@ static void fts5SegIterSeekInit( ){ int iPg = 1; int h; - int bGe = ((flags & FTS5INDEX_QUERY_PREFIX) && iIdx==0); + int bGe = (flags & FTS5INDEX_QUERY_PREFIX); int bDlidx = 0; /* True if there is a doclist-index */ assert( bGe==0 || (flags & FTS5INDEX_QUERY_DESC)==0 ); assert( pTerm && nTerm ); memset(pIter, 0, sizeof(*pIter)); pIter->pSeg = pSeg; - pIter->iIdx = iIdx; /* This block sets stack variable iPg to the leaf page number that may ** contain term (pTerm/nTerm), if it is present in the segment. */ for(h=pSeg->nHeight-1; h>0; h--){ Fts5NodeIter node; /* For iterating through internal nodes */ - i64 iRowid = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, h, iPg); + i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, h, iPg); Fts5Data *pNode = fts5DataRead(p, iRowid); if( pNode==0 ) break; @@ -2149,10 +2140,10 @@ static void fts5SegIterSeekInit( pIter->flags |= FTS5_SEGITER_REVERSE; } if( bDlidx ){ - fts5SegIterLoadDlidx(p, iIdx, pIter); + fts5SegIterLoadDlidx(p, pIter); } if( flags & FTS5INDEX_QUERY_DESC ){ - fts5SegIterReverse(p, iIdx, pIter); + fts5SegIterReverse(p, pIter); } } } @@ -2160,7 +2151,7 @@ static void fts5SegIterSeekInit( /* ** Initialize the object pIter to point to term pTerm/nTerm within the -** in-memory hash table iIdx. If there is no such term in the table, the +** in-memory hash table. If there is no such term in the hash-table, the ** iterator is set to EOF. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If @@ -2168,27 +2159,25 @@ static void fts5SegIterSeekInit( */ static void fts5SegIterHashInit( Fts5Index *p, /* FTS5 backend */ - int iIdx, /* Config.aHash[] index of FTS index */ const u8 *pTerm, int nTerm, /* Term to seek to */ int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5SegIter *pIter /* Object to populate */ ){ - Fts5Hash *pHash = p->apHash[iIdx]; const u8 *pList = 0; int nList = 0; const u8 *z = 0; int n = 0; - assert( pHash ); + assert( p->pHash ); assert( p->rc==SQLITE_OK ); - if( pTerm==0 || (iIdx==0 && (flags & FTS5INDEX_QUERY_PREFIX)) ){ - p->rc = sqlite3Fts5HashScanInit(pHash, (const char*)pTerm, nTerm); - sqlite3Fts5HashScanEntry(pHash, (const char**)&z, &pList, &nList); + if( pTerm==0 || (flags & FTS5INDEX_QUERY_PREFIX) ){ + p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); + sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); n = (z ? strlen((const char*)z) : 0); }else{ pIter->flags |= FTS5_SEGITER_ONETERM; - sqlite3Fts5HashQuery(pHash, (const char*)pTerm, nTerm, &pList, &nList); + sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList); z = pTerm; n = nTerm; } @@ -2552,7 +2541,6 @@ static void fts5MultiIterNext( static void fts5MultiIterNew( Fts5Index *p, /* FTS5 backend to iterate within */ Fts5Structure *pStruct, /* Structure of specific index */ - int iIdx, /* Config.aHash[] index of FTS index */ int bSkipEmpty, /* True to ignore delete-keys */ int flags, /* FTS5INDEX_QUERY_XXX flags */ const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */ @@ -2574,7 +2562,7 @@ static void fts5MultiIterNew( if( iLevel<0 ){ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); nSeg = pStruct->nSegment; - nSeg += (p->apHash ? 1 : 0); + nSeg += (p->pHash ? 1 : 0); }else{ nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); } @@ -2596,26 +2584,26 @@ static void fts5MultiIterNew( /* Initialize each of the component segment iterators. */ if( iLevel<0 ){ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; - if( p->apHash ){ + if( p->pHash ){ /* Add a segment iterator for the current contents of the hash table. */ Fts5SegIter *pIter = &pNew->aSeg[iIter++]; - fts5SegIterHashInit(p, iIdx, pTerm, nTerm, flags, pIter); + fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); } for(pLvl=&pStruct->aLevel[0]; pLvlnSeg-1; iSeg>=0; iSeg--){ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; Fts5SegIter *pIter = &pNew->aSeg[iIter++]; if( pTerm==0 ){ - fts5SegIterInit(p, iIdx, pSeg, pIter); + fts5SegIterInit(p, pSeg, pIter); }else{ - fts5SegIterSeekInit(p, iIdx, pTerm, nTerm, flags, pSeg, pIter); + fts5SegIterSeekInit(p, pTerm, nTerm, flags, pSeg, pIter); } } } }else{ pLvl = &pStruct->aLevel[iLevel]; for(iSeg=nSeg-1; iSeg>=0; iSeg--){ - fts5SegIterInit(p, iIdx, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); + fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); } } assert( iIter==nSeg ); @@ -2734,8 +2722,7 @@ static void fts5ChunkIterInit( ** currently stored in a hash table. In this case there is no leaf-rowid ** to calculate. */ if( pSeg->pSeg ){ - int iId = pSeg->pSeg->iSegid; - i64 rowid = FTS5_SEGMENT_ROWID(pSeg->iIdx, iId, 0, pSeg->iLeafPgno); + i64 rowid = FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, 0, pSeg->iLeafPgno); pIter->iLeafRowid = rowid; } @@ -2794,13 +2781,9 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ ** Discard all data currently cached in the hash-tables. */ static void fts5IndexDiscardData(Fts5Index *p){ - assert( p->apHash || p->nPendingData==0 ); - if( p->apHash ){ - Fts5Config *pConfig = p->pConfig; - int i; - for(i=0; i<=pConfig->nPrefix; i++){ - sqlite3Fts5HashClear(p->apHash[i]); - } + assert( p->pHash || p->nPendingData==0 ); + if( p->pHash ){ + sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; } } @@ -2832,8 +2815,7 @@ static void fts5WriteBtreeNEmpty(Fts5Index *p, Fts5SegWriter *pWriter){ pPg = &pWriter->aWriter[1]; if( pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){ i64 iKey = FTS5_DOCLIST_IDX_ROWID( - pWriter->iIdx, pWriter->iSegid, - pWriter->aWriter[0].pgno - 1 - pWriter->nEmpty + pWriter->iSegid, pWriter->aWriter[0].pgno - 1 - pWriter->nEmpty ); assert( pWriter->cdlidx.n>0 ); fts5DataWrite(p, iKey, pWriter->cdlidx.p, pWriter->cdlidx.n); @@ -2901,9 +2883,7 @@ static void fts5WriteBtreeTerm( if( pPage->buf.n>=p->pConfig->pgsz ){ /* pPage will be written to disk. The term will be written into the ** parent of pPage. */ - i64 iRowid = FTS5_SEGMENT_ROWID( - pWriter->iIdx, pWriter->iSegid, iHeight, pPage->pgno - ); + i64 iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, iHeight, pPage->pgno); fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n); fts5BufferZero(&pPage->buf); fts5BufferZero(&pPage->term); @@ -2971,7 +2951,7 @@ static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){ } /* Write the current page to the db. */ - iRowid = FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, 0, pPage->pgno); + iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, 0, pPage->pgno); fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n); /* Initialize the next page. */ @@ -3179,7 +3159,7 @@ static void fts5WriteFinish( for(i=1; inWriter; i++){ Fts5PageWriter *pPg = &pWriter->aWriter[i]; fts5DataWrite(p, - FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, i, pPg->pgno), + FTS5_SEGMENT_ROWID(pWriter->iSegid, i, pPg->pgno), pPg->buf.p, pPg->buf.n ); } @@ -3197,10 +3177,9 @@ static void fts5WriteFinish( static void fts5WriteInit( Fts5Index *p, Fts5SegWriter *pWriter, - int iIdx, int iSegid + int iSegid ){ memset(pWriter, 0, sizeof(Fts5SegWriter)); - pWriter->iIdx = iIdx; pWriter->iSegid = iSegid; pWriter->aWriter = (Fts5PageWriter*)fts5IdxMalloc(p,sizeof(Fts5PageWriter)); @@ -3213,12 +3192,10 @@ static void fts5WriteInit( static void fts5WriteInitForAppend( Fts5Index *p, /* FTS5 backend object */ Fts5SegWriter *pWriter, /* Writer to initialize */ - int iIdx, /* Index segment is a part of */ Fts5StructureSegment *pSeg /* Segment object to append to */ ){ int nByte = pSeg->nHeight * sizeof(Fts5PageWriter); memset(pWriter, 0, sizeof(Fts5SegWriter)); - pWriter->iIdx = iIdx; pWriter->iSegid = pSeg->iSegid; pWriter->aWriter = (Fts5PageWriter*)fts5IdxMalloc(p, nByte); @@ -3228,7 +3205,7 @@ static void fts5WriteInitForAppend( pWriter->nWriter = pSeg->nHeight; pWriter->aWriter[0].pgno = pSeg->pgnoLast+1; for(i=pSeg->nHeight-1; i>0; i--){ - i64 iRowid = FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, i, pgno); + i64 iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, i, pgno); Fts5PageWriter *pPg = &pWriter->aWriter[i]; pPg->pgno = pgno; fts5DataBuffer(p, &pPg->buf, iRowid); @@ -3276,7 +3253,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5MultiSegIter *pIter){ int iId = pSeg->pSeg->iSegid; u8 aHdr[4] = {0x00, 0x00, 0x00, 0x04}; - iLeafRowid = FTS5_SEGMENT_ROWID(pSeg->iIdx, iId, 0, pSeg->iTermLeafPgno); + iLeafRowid = FTS5_SEGMENT_ROWID(iId, 0, pSeg->iTermLeafPgno); pData = fts5DataRead(p, iLeafRowid); if( pData ){ fts5BufferZero(&buf); @@ -3286,7 +3263,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5MultiSegIter *pIter){ fts5BufferAppendBlob(&p->rc, &buf, pData->n - iOff, &pData->p[iOff]); fts5DataRelease(pData); pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; - fts5DataDelete(p, FTS5_SEGMENT_ROWID(pSeg->iIdx, iId, 0, 1),iLeafRowid); + fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 0, 1),iLeafRowid); fts5DataWrite(p, iLeafRowid, buf.p, buf.n); } } @@ -3299,8 +3276,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5MultiSegIter *pIter){ */ static void fts5IndexMergeLevel( Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ - Fts5Structure **ppStruct, /* IN/OUT: Stucture of index iIdx */ + Fts5Structure **ppStruct, /* IN/OUT: Stucture of index */ int iLvl, /* Level to read input from */ int *pnRem /* Write up to this many output leaves */ ){ @@ -3321,12 +3297,11 @@ static void fts5IndexMergeLevel( memset(&writer, 0, sizeof(Fts5SegWriter)); memset(&term, 0, sizeof(Fts5Buffer)); - writer.iIdx = iIdx; if( pLvl->nMerge ){ pLvlOut = &pStruct->aLevel[iLvl+1]; assert( pLvlOut->nSeg>0 ); nInput = pLvl->nMerge; - fts5WriteInitForAppend(p, &writer, iIdx, &pLvlOut->aSeg[pLvlOut->nSeg-1]); + fts5WriteInitForAppend(p, &writer, &pLvlOut->aSeg[pLvlOut->nSeg-1]); pSeg = &pLvlOut->aSeg[pLvlOut->nSeg-1]; }else{ int iSegid = fts5AllocateSegid(p, pStruct); @@ -3342,7 +3317,7 @@ static void fts5IndexMergeLevel( pLvl = &pStruct->aLevel[iLvl]; pLvlOut = &pStruct->aLevel[iLvl+1]; - fts5WriteInit(p, &writer, iIdx, iSegid); + fts5WriteInit(p, &writer, iSegid); /* Add the new segment to the output level */ pSeg = &pLvlOut->aSeg[pLvlOut->nSeg]; @@ -3362,7 +3337,7 @@ fflush(stdout); #endif assert( iLvl>=0 ); - for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, 0, iLvl, nInput, &pIter); + for(fts5MultiIterNew(p, pStruct, 0, 0, 0, 0, iLvl, nInput, &pIter); fts5MultiIterEof(p, pIter)==0; fts5MultiIterNext(p, pIter, 0, 0) ){ @@ -3414,7 +3389,7 @@ fflush(stdout); /* Remove the redundant segments from the %_data table */ for(i=0; iaSeg[i].iSegid); + fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid); } /* Remove the redundant segments from the input level */ @@ -3441,11 +3416,10 @@ fflush(stdout); } /* -** Do up to nPg pages of automerge work on index iIdx. +** Do up to nPg pages of automerge work on the index. */ static void fts5IndexMerge( Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ int nPg /* Pages of work to do */ ){ @@ -3485,7 +3459,7 @@ static void fts5IndexMerge( ){ break; } - fts5IndexMergeLevel(p, iIdx, &pStruct, iBestLvl, &nRem); + fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem); if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){ fts5StructurePromote(p, iBestLvl+1, pStruct); } @@ -3495,16 +3469,14 @@ static void fts5IndexMerge( /* ** A total of nLeaf leaf pages of data has just been flushed to a level-0 -** segments in index iIdx with structure pStruct. This function updates the -** write-counter accordingly and, if necessary, performs incremental merge -** work. +** segment. This function updates the write-counter accordingly and, if +** necessary, performs incremental merge work. ** ** If an error occurs, set the Fts5Index.rc error code. If an error has ** already occurred, this function is a no-op. */ static void fts5IndexAutomerge( Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ int nLeaf /* Number of output leaves just written */ ){ @@ -3520,13 +3492,12 @@ static void fts5IndexAutomerge( pStruct->nWriteCounter += nLeaf; nRem = p->nWorkUnit * nWork * pStruct->nLevel; - fts5IndexMerge(p, iIdx, ppStruct, nRem); + fts5IndexMerge(p, ppStruct, nRem); } } static void fts5IndexCrisismerge( Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ Fts5Structure **ppStruct /* IN/OUT: Current structure of index */ ){ const int nCrisis = p->pConfig->nCrisisMerge; @@ -3535,7 +3506,7 @@ static void fts5IndexCrisismerge( assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 ); while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ - fts5IndexMergeLevel(p, iIdx, &pStruct, iLvl, 0); + fts5IndexMergeLevel(p, &pStruct, iLvl, 0); fts5StructurePromote(p, iLvl+1, pStruct); iLvl++; } @@ -3584,15 +3555,15 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){ ** If an error occurs, set the Fts5Index.rc error code. If an error has ** already occurred, this function is a no-op. */ -static void fts5FlushOneHash(Fts5Index *p, int iHash, int *pnLeaf){ - Fts5Hash *pHash = p->apHash[iHash]; +static void fts5FlushOneHash(Fts5Index *p){ + Fts5Hash *pHash = p->pHash; Fts5Structure *pStruct; int iSegid; int pgnoLast = 0; /* Last leaf page number in segment */ /* Obtain a reference to the index structure and allocate a new segment-id ** for the new level-0 segment. */ - pStruct = fts5StructureRead(p, iHash); + pStruct = fts5StructureRead(p); iSegid = fts5AllocateSegid(p, pStruct); if( iSegid ){ @@ -3604,7 +3575,7 @@ static void fts5FlushOneHash(Fts5Index *p, int iHash, int *pnLeaf){ const u8 *zPrev = 0; Fts5SegWriter writer; - fts5WriteInit(p, &writer, iHash, iSegid); + fts5WriteInit(p, &writer, iSegid); /* Pre-allocate the buffer used to assemble leaf pages to the target ** page size. */ @@ -3749,9 +3720,9 @@ static void fts5FlushOneHash(Fts5Index *p, int iHash, int *pnLeaf){ } - fts5IndexAutomerge(p, iHash, &pStruct, pgnoLast); - fts5IndexCrisismerge(p, iHash, &pStruct); - fts5StructureWrite(p, iHash, pStruct); + fts5IndexAutomerge(p, &pStruct, pgnoLast); + fts5IndexCrisismerge(p, &pStruct); + fts5StructureWrite(p, pStruct); fts5StructureRelease(pStruct); } @@ -3759,87 +3730,77 @@ static void fts5FlushOneHash(Fts5Index *p, int iHash, int *pnLeaf){ ** Flush any data stored in the in-memory hash tables to the database. */ static void fts5IndexFlush(Fts5Index *p){ - Fts5Config *pConfig = p->pConfig; - int i; /* Used to iterate through indexes */ - int nLeaf = 0; /* Number of leaves written */ - - /* If an error has already occured this call is a no-op. */ - if( p->nPendingData==0 ) return; - assert( p->apHash ); - - /* Flush the terms and each prefix index to disk */ - for(i=0; i<=pConfig->nPrefix; i++){ - fts5FlushOneHash(p, i, &nLeaf); + /* Unless it is empty, flush the hash table to disk */ + if( p->rc==SQLITE_OK && p->nPendingData ){ + assert( p->pHash ); + p->nPendingData = 0; + fts5FlushOneHash(p); } - p->nPendingData = 0; } int sqlite3Fts5IndexOptimize(Fts5Index *p){ - Fts5Config *pConfig = p->pConfig; - int i; + Fts5Structure *pStruct; + Fts5Structure *pNew = 0; + int nSeg = 0; assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); - for(i=0; i<=pConfig->nPrefix; i++){ - Fts5Structure *pStruct = fts5StructureRead(p, i); - Fts5Structure *pNew = 0; - int nSeg = 0; - if( pStruct ){ - assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); - nSeg = pStruct->nSegment; - if( nSeg>1 ){ - int nByte = sizeof(Fts5Structure); - nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel); - pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); - } + pStruct = fts5StructureRead(p); + + if( pStruct ){ + assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); + nSeg = pStruct->nSegment; + if( nSeg>1 ){ + int nByte = sizeof(Fts5Structure); + nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel); + pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); } - if( pNew ){ - Fts5StructureLevel *pLvl; - int nByte = nSeg * sizeof(Fts5StructureSegment); - pNew->nLevel = pStruct->nLevel+1; - pNew->nWriteCounter = pStruct->nWriteCounter; - pLvl = &pNew->aLevel[pStruct->nLevel]; - pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); - if( pLvl->aSeg ){ - int iLvl, iSeg; - int iSegOut = 0; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg]; - iSegOut++; - } + } + if( pNew ){ + Fts5StructureLevel *pLvl; + int nByte = nSeg * sizeof(Fts5StructureSegment); + pNew->nLevel = pStruct->nLevel+1; + pNew->nWriteCounter = pStruct->nWriteCounter; + pLvl = &pNew->aLevel[pStruct->nLevel]; + pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); + if( pLvl->aSeg ){ + int iLvl, iSeg; + int iSegOut = 0; + for(iLvl=0; iLvlnLevel; iLvl++){ + for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ + pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg]; + iSegOut++; } - pNew->nSegment = pLvl->nSeg = nSeg; - }else{ - sqlite3_free(pNew); - pNew = 0; } + pNew->nSegment = pLvl->nSeg = nSeg; + }else{ + sqlite3_free(pNew); + pNew = 0; } + } - if( pNew ){ - int iLvl = pNew->nLevel-1; - while( p->rc==SQLITE_OK && pNew->aLevel[iLvl].nSeg>0 ){ - int nRem = FTS5_OPT_WORK_UNIT; - fts5IndexMergeLevel(p, i, &pNew, iLvl, &nRem); - } - - fts5StructureWrite(p, i, pNew); - fts5StructureRelease(pNew); + if( pNew ){ + int iLvl = pNew->nLevel-1; + while( p->rc==SQLITE_OK && pNew->aLevel[iLvl].nSeg>0 ){ + int nRem = FTS5_OPT_WORK_UNIT; + fts5IndexMergeLevel(p, &pNew, iLvl, &nRem); } - fts5StructureRelease(pStruct); + fts5StructureWrite(p, pNew); + fts5StructureRelease(pNew); } + fts5StructureRelease(pStruct); return fts5IndexReturn(p); } int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ Fts5Structure *pStruct; - pStruct = fts5StructureRead(p, 0); - fts5IndexMerge(p, 0, &pStruct, nMerge); - fts5StructureWrite(p, 0, pStruct); + pStruct = fts5StructureRead(p); + fts5IndexMerge(p, &pStruct, nMerge); + fts5StructureWrite(p, pStruct); fts5StructureRelease(pStruct); return fts5IndexReturn(p); @@ -4040,7 +4001,7 @@ static void fts5SetupPrefixIter( const int nBuf = 32; aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); - pStruct = fts5StructureRead(p, 0); + pStruct = fts5StructureRead(p); if( aBuf && pStruct ){ Fts5DoclistIter *pDoclist; @@ -4050,7 +4011,7 @@ static void fts5SetupPrefixIter( Fts5Buffer doclist; memset(&doclist, 0, sizeof(doclist)); - for(fts5MultiIterNew(p, pStruct, 0, 1, 1, pToken, nToken, -1, 0, &p1); + for(fts5MultiIterNew(p, pStruct, 1, 1, pToken, nToken, -1, 0, &p1); fts5MultiIterEof(p, p1)==0; fts5MultiIterNext(p, p1, 0, 0) ){ @@ -4113,32 +4074,12 @@ static void fts5SetupPrefixIter( int sqlite3Fts5IndexBeginWrite(Fts5Index *p, i64 iRowid){ assert( p->rc==SQLITE_OK ); - /* Allocate hash tables if they have not already been allocated */ - if( p->apHash==0 ){ - int i; - int rc = SQLITE_OK; - int nHash = p->pConfig->nPrefix + 1; - Fts5Hash **apNew; - - apNew = (Fts5Hash**)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Hash*)*nHash); - for(i=0; rc==SQLITE_OK && inPendingData); - } - if( rc==SQLITE_OK ){ - p->apHash = apNew; - }else{ - if( apNew ){ - for(i=0; ipHash==0 ){ + p->rc = sqlite3Fts5HashNew(&p->pHash, &p->nPendingData); } if( iRowid<=p->iWriteRowid || (p->nPendingData > p->nMaxPendingData) ){ - assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); } p->iWriteRowid = iRowid; @@ -4174,16 +4115,13 @@ int sqlite3Fts5IndexRollback(Fts5Index *p){ ** and the initial version of the "averages" record (a zero-byte blob). */ int sqlite3Fts5IndexReinit(Fts5Index *p){ - int i; Fts5Structure s; + assert( p->rc==SQLITE_OK ); + p->rc = sqlite3Fts5IndexSetAverages(p, (const u8*)"", 0); + memset(&s, 0, sizeof(Fts5Structure)); - for(i=0; ipConfig->nPrefix+1; i++){ - fts5StructureWrite(p, i, &s); - } - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3Fts5IndexSetAverages(p, (const u8*)"", 0); - } + fts5StructureWrite(p, &s); return fts5IndexReturn(p); } @@ -4239,13 +4177,8 @@ int sqlite3Fts5IndexClose(Fts5Index *p){ assert( p->pReader==0 ); sqlite3_finalize(p->pWriter); sqlite3_finalize(p->pDeleter); - if( p->apHash ){ - int i; - for(i=0; i<=p->pConfig->nPrefix; i++){ - sqlite3Fts5HashFree(p->apHash[i]); - } - sqlite3_free(p->apHash); - } + sqlite3Fts5HashFree(p->pHash); + sqlite3Fts5BufferFree(&p->scratch); sqlite3_free(p->zDataTbl); sqlite3_free(p); } @@ -4302,21 +4235,21 @@ int sqlite3Fts5IndexWrite( const char *pToken, int nToken /* Token to add or remove to or from index */ ){ int i; /* Used to iterate through indexes */ - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ Fts5Config *pConfig = p->pConfig; assert( p->rc==SQLITE_OK ); - /* Add the new token to the main terms hash table. And to each of the - ** prefix hash tables that it is large enough for. */ + /* Add the entry to the main terms index. */ rc = sqlite3Fts5HashWrite( - p->apHash[0], p->iWriteRowid, iCol, iPos, pToken, nToken + p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX, pToken, nToken ); + for(i=0; inPrefix && rc==SQLITE_OK; i++){ int nByte = fts5IndexCharlenToBytelen(pToken, nToken, pConfig->aPrefix[i]); if( nByte ){ - rc = sqlite3Fts5HashWrite( - p->apHash[i+1], p->iWriteRowid, iCol, iPos, pToken, nByte + rc = sqlite3Fts5HashWrite(p->pHash, + p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX+i+1, pToken, nByte ); } } @@ -4337,6 +4270,11 @@ int sqlite3Fts5IndexQuery( Fts5Config *pConfig = p->pConfig; Fts5IndexIter *pRet; int iIdx = 0; + Fts5Buffer buf = {0, 0, 0}; + + if( sqlite3Fts5BufferGrow(&p->rc, &buf, nToken+1)==0 ){ + memcpy(&buf.p[1], pToken, nToken); + } if( flags & FTS5INDEX_QUERY_PREFIX ){ if( flags & FTS5INDEX_QUERY_TEST_NOIDX ){ @@ -4355,15 +4293,18 @@ int sqlite3Fts5IndexQuery( pRet->pIndex = p; if( iIdx<=pConfig->nPrefix ){ - pRet->pStruct = fts5StructureRead(p, iIdx); + buf.p[0] = FTS5_MAIN_PREFIX + iIdx; + pRet->pStruct = fts5StructureRead(p); if( pRet->pStruct ){ - fts5MultiIterNew(p, pRet->pStruct, - iIdx, 1, flags, (const u8*)pToken, nToken, -1, 0, &pRet->pMulti + int f = (flags & ~FTS5INDEX_QUERY_PREFIX); + fts5MultiIterNew( + p, pRet->pStruct, 1, f, buf.p, nToken+1, -1, 0, &pRet->pMulti ); } }else{ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; - fts5SetupPrefixIter(p, bDesc, (const u8*)pToken, nToken, pRet); + buf.p[0] = FTS5_MAIN_PREFIX; + fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pRet); } } @@ -4372,6 +4313,7 @@ int sqlite3Fts5IndexQuery( pRet = 0; } *ppIter = pRet; + sqlite3Fts5BufferFree(&buf); return fts5IndexReturn(p); } @@ -4520,23 +4462,20 @@ int sqlite3Fts5IndexReads(Fts5Index *p){ ** occurs. */ int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){ - int rc = SQLITE_OK; - Fts5Config *pConfig = p->pConfig; - u8 aCookie[4]; - int i; + int rc; /* Return code */ + Fts5Config *pConfig = p->pConfig; /* Configuration object */ + u8 aCookie[4]; /* Binary representation of iNew */ assert( p->rc==SQLITE_OK ); + sqlite3Fts5Put32(aCookie, iNew); - for(i=0; rc==SQLITE_OK && i<=pConfig->nPrefix; i++){ - sqlite3_blob *pBlob = 0; - i64 iRowid = FTS5_STRUCTURE_ROWID(i); - rc = sqlite3_blob_open( - pConfig->db, pConfig->zDb, p->zDataTbl, "block", iRowid, 1, &pBlob - ); - if( rc==SQLITE_OK ){ - sqlite3_blob_write(pBlob, aCookie, 4, 0); - rc = sqlite3_blob_close(pBlob); - } + sqlite3_blob *pBlob = 0; + rc = sqlite3_blob_open(pConfig->db, pConfig->zDb, p->zDataTbl, + "block", FTS5_STRUCTURE_ROWID, 1, &pBlob + ); + if( rc==SQLITE_OK ){ + sqlite3_blob_write(pBlob, aCookie, 4, 0); + rc = sqlite3_blob_close(pBlob); } return rc; @@ -4544,7 +4483,7 @@ int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){ int sqlite3Fts5IndexLoadConfig(Fts5Index *p){ Fts5Structure *pStruct; - pStruct = fts5StructureRead(p, 0); + pStruct = fts5StructureRead(p); fts5StructureRelease(pStruct); return fts5IndexReturn(p); } @@ -4563,20 +4502,21 @@ static u64 fts5IndexEntryCksum( i64 iRowid, int iCol, int iPos, - const char *pTerm, + int iIdx, + const char *pTerm, int nTerm ){ int i; u64 ret = iRowid; ret += (ret<<3) + iCol; ret += (ret<<3) + iPos; + if( iIdx>=0 ) ret += (ret<<3) + (FTS5_MAIN_PREFIX + iIdx); for(i=0; irc==SQLITE_OK ){ pIter->nLvl = pSeg->nHeight-1; - pIter->iIdx = iIdx; pIter->p = p; pIter->pSeg = pSeg; } for(i=0; p->rc==SQLITE_OK && inLvl; i++){ - i64 iRowid = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, i+1, 1); + i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, i+1, 1); Fts5Data *pData; pIter->aLvl[i].pData = pData = fts5DataRead(p, iRowid); if( pData ){ @@ -4635,7 +4574,7 @@ static void fts5BtreeIterNext(Fts5BtreeIter *pIter){ int iSegid = pIter->pSeg->iSegid; for(i--; i>=0; i--){ Fts5BtreeIterLevel *pLvl = &pIter->aLvl[i]; - i64 iRowid = FTS5_SEGMENT_ROWID(pIter->iIdx,iSegid,i+1,pLvl[1].s.iChild); + i64 iRowid = FTS5_SEGMENT_ROWID(iSegid, i+1, pLvl[1].s.iChild); pLvl->pData = fts5DataRead(p, iRowid); if( pLvl->pData ){ fts5NodeIterInit(pLvl->pData->p, pLvl->pData->n, &pLvl->s); @@ -4668,12 +4607,11 @@ static void fts5BtreeIterFree(Fts5BtreeIter *pIter){ ** ** Instead, it tests that the same set of pgno/rowid combinations are ** visited regardless of whether the doclist-index identified by parameters -** iIdx/iSegid/iLeaf is iterated in forwards or reverse order. +** iSegid/iLeaf is iterated in forwards or reverse order. */ #ifdef SQLITE_DEBUG static void fts5DlidxIterTestReverse( Fts5Index *p, - int iIdx, /* Index to load doclist-index from */ int iSegid, /* Segment id to load from */ int iLeaf /* Load doclist-index for this leaf */ ){ @@ -4681,7 +4619,7 @@ static void fts5DlidxIterTestReverse( i64 cksum1 = 13; i64 cksum2 = 13; - for(pDlidx=fts5DlidxIterInit(p, 0, iIdx, iSegid, iLeaf); + for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iLeaf); fts5DlidxIterEof(p, pDlidx)==0; fts5DlidxIterNext(pDlidx) ){ @@ -4692,7 +4630,7 @@ static void fts5DlidxIterTestReverse( fts5DlidxIterFree(pDlidx); pDlidx = 0; - for(pDlidx=fts5DlidxIterInit(p, 1, iIdx, iSegid, iLeaf); + for(pDlidx=fts5DlidxIterInit(p, 1, iSegid, iLeaf); fts5DlidxIterEof(p, pDlidx)==0; fts5DlidxIterPrev(pDlidx) ){ @@ -4706,12 +4644,11 @@ static void fts5DlidxIterTestReverse( if( p->rc==SQLITE_OK && cksum1!=cksum2 ) p->rc = FTS5_CORRUPT; } #else -# define fts5DlidxIterTestReverse(w,x,y,z) +# define fts5DlidxIterTestReverse(x,y,z) #endif static void fts5IndexIntegrityCheckSegment( Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index that pSeg is a part of */ Fts5StructureSegment *pSeg /* Segment to check internal consistency */ ){ Fts5BtreeIter iter; /* Used to iterate through b-tree hierarchy */ @@ -4719,7 +4656,7 @@ static void fts5IndexIntegrityCheckSegment( if( pSeg->pgnoFirst==0 ) return; /* Iterate through the b-tree hierarchy. */ - for(fts5BtreeIterInit(p, iIdx, pSeg, &iter); + for(fts5BtreeIterInit(p, pSeg, &iter); p->rc==SQLITE_OK && iter.bEof==0; fts5BtreeIterNext(&iter) ){ @@ -4731,7 +4668,7 @@ static void fts5IndexIntegrityCheckSegment( /* If the leaf in question has already been trimmed from the segment, ** ignore this b-tree entry. Otherwise, load it into memory. */ if( iter.iLeafpgnoFirst ) continue; - iRow = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, iter.iLeaf); + iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, iter.iLeaf); pLeaf = fts5DataRead(p, iRow); if( pLeaf==0 ) break; @@ -4771,14 +4708,14 @@ static void fts5IndexIntegrityCheckSegment( int iPg; i64 iKey; - for(pDlidx=fts5DlidxIterInit(p, 0, iIdx, iSegid, iter.iLeaf); + for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iter.iLeaf); fts5DlidxIterEof(p, pDlidx)==0; fts5DlidxIterNext(pDlidx) ){ /* Check any rowid-less pages that occur before the current leaf. */ for(iPg=iPrevLeaf+1; iPgiLeafPgno; iPg++){ - iKey = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, iPg); + iKey = FTS5_SEGMENT_ROWID(iSegid, 0, iPg); pLeaf = fts5DataRead(p, iKey); if( pLeaf ){ if( fts5GetU16(&pLeaf->p[0])!=0 ) p->rc = FTS5_CORRUPT; @@ -4789,7 +4726,7 @@ static void fts5IndexIntegrityCheckSegment( /* Check that the leaf page indicated by the iterator really does ** contain the rowid suggested by the same. */ - iKey = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, pDlidx->iLeafPgno); + iKey = FTS5_SEGMENT_ROWID(iSegid, 0, pDlidx->iLeafPgno); pLeaf = fts5DataRead(p, iKey); if( pLeaf ){ i64 iRowid; @@ -4802,7 +4739,7 @@ static void fts5IndexIntegrityCheckSegment( } for(iPg=iPrevLeaf+1; iPg<=(iter.iLeaf + iter.nEmpty); iPg++){ - iKey = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, iPg); + iKey = FTS5_SEGMENT_ROWID(iSegid, 0, iPg); pLeaf = fts5DataRead(p, iKey); if( pLeaf ){ if( fts5GetU16(&pLeaf->p[0])!=0 ) p->rc = FTS5_CORRUPT; @@ -4811,7 +4748,7 @@ static void fts5IndexIntegrityCheckSegment( } fts5DlidxIterFree(pDlidx); - fts5DlidxIterTestReverse(p, iIdx, iSegid, iter.iLeaf); + fts5DlidxIterTestReverse(p, iSegid, iter.iLeaf); } } @@ -4830,11 +4767,12 @@ static void fts5IndexIntegrityCheckSegment( static int fts5QueryCksum( - Fts5Index *p, - const char *z, - int n, - int flags, - u64 *pCksum + Fts5Index *p, /* Fts5 index object */ + int iIdx, + const char *z, /* Index key to query for */ + int n, /* Size of index key in bytes */ + int flags, /* Flags for Fts5IndexQuery */ + u64 *pCksum /* IN/OUT: Checksum value */ ){ u64 cksum = *pCksum; Fts5IndexIter *pIdxIter = 0; @@ -4853,7 +4791,7 @@ static int fts5QueryCksum( ){ int iCol = FTS5_POS2COLUMN(sReader.iPos); int iOff = FTS5_POS2OFFSET(sReader.iPos); - cksum ^= fts5IndexEntryCksum(rowid, iCol, iOff, z, n); + cksum ^= fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n); } rc = sqlite3Fts5IterNext(pIdxIter); } @@ -4875,26 +4813,25 @@ static int fts5QueryCksum( ** occurs. */ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - Fts5Config *pConfig = p->pConfig; - int iIdx; /* Used to iterate through indexes */ u64 cksum2 = 0; /* Checksum based on contents of indexes */ u64 cksum3 = 0; /* Checksum based on contents of indexes */ Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */ Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ + Fts5MultiSegIter *pIter; /* Used to iterate through entire index */ + Fts5Structure *pStruct; /* Index structure */ + + /* Load the FTS index structure */ + pStruct = fts5StructureRead(p); /* Check that the internal nodes of each segment match the leaves */ - for(iIdx=0; p->rc==SQLITE_OK && iIdx<=pConfig->nPrefix; iIdx++){ - Fts5Structure *pStruct = fts5StructureRead(p, iIdx); - if( pStruct ){ - int iLvl, iSeg; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; - fts5IndexIntegrityCheckSegment(p, iIdx, pSeg); - } + if( pStruct ){ + int iLvl, iSeg; + for(iLvl=0; iLvlnLevel; iLvl++){ + for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ + Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; + fts5IndexIntegrityCheckSegment(p, pSeg); } } - fts5StructureRelease(pStruct); } /* The cksum argument passed to this function is a checksum calculated @@ -4910,68 +4847,69 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ ** same term is performed. cksum3 is calculated based on the entries ** extracted by these queries. */ - for(iIdx=0; iIdx<=pConfig->nPrefix; iIdx++){ - Fts5MultiSegIter *pIter; - Fts5Structure *pStruct = fts5StructureRead(p, iIdx); - for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, 0, -1, 0, &pIter); - fts5MultiIterEof(p, pIter)==0; - fts5MultiIterNext(p, pIter, 0, 0) - ){ - int n; /* Size of term in bytes */ - i64 iPos = 0; /* Position read from poslist */ - int iOff = 0; /* Offset within poslist */ - i64 iRowid = fts5MultiIterRowid(pIter); - char *z = (char*)fts5MultiIterTerm(pIter, &n); - - poslist.n = 0; - fts5MultiIterPoslist(p, pIter, 0, &poslist); - while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ - int iCol = FTS5_POS2COLUMN(iPos); - int iTokOff = FTS5_POS2OFFSET(iPos); - cksum2 ^= fts5IndexEntryCksum(iRowid, iCol, iTokOff, z, n); - } + for(fts5MultiIterNew(p, pStruct, 0, 0, 0, 0, -1, 0, &pIter); + fts5MultiIterEof(p, pIter)==0; + fts5MultiIterNext(p, pIter, 0, 0) + ){ + int n; /* Size of term in bytes */ + i64 iPos = 0; /* Position read from poslist */ + int iOff = 0; /* Offset within poslist */ + i64 iRowid = fts5MultiIterRowid(pIter); + char *z = (char*)fts5MultiIterTerm(pIter, &n); + + poslist.n = 0; + fts5MultiIterPoslist(p, pIter, 0, &poslist); + while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ + int iCol = FTS5_POS2COLUMN(iPos); + int iTokOff = FTS5_POS2OFFSET(iPos); + cksum2 ^= fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); + } - /* If this is a new term, query for it. Update cksum3 with the results. */ - if( p->rc==SQLITE_OK && (term.n!=n || memcmp(term.p, z, n)) ){ - int rc; - int flags = (iIdx==0 ? 0 : FTS5INDEX_QUERY_PREFIX); - u64 ck1 = 0; - u64 ck2 = 0; - - /* Check that the results returned for ASC and DESC queries are - ** the same. If not, call this corruption. */ - rc = fts5QueryCksum(p, z, n, flags, &ck1); - if( rc==SQLITE_OK ){ - rc = fts5QueryCksum(p, z, n, flags|FTS5INDEX_QUERY_DESC, &ck2); - } + /* If this is a new term, query for it. Update cksum3 with the results. */ + if( p->rc==SQLITE_OK && (term.n!=n || memcmp(term.p, z, n)) ){ + const char *zTerm = &z[1]; /* The term without the prefix-byte */ + int nTerm = n-1; /* Size of zTerm in bytes */ + int iIdx = (z[0] - FTS5_MAIN_PREFIX); + int flags = (iIdx==0 ? 0 : FTS5INDEX_QUERY_PREFIX); + int rc; + u64 ck1 = 0; + u64 ck2 = 0; + + /* Check that the results returned for ASC and DESC queries are + ** the same. If not, call this corruption. */ + rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, flags, &ck1); + if( rc==SQLITE_OK ){ + int f = flags|FTS5INDEX_QUERY_DESC; + rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2); + } + if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT; + + /* If this is a prefix query, check that the results returned if the + ** the index is disabled are the same. In both ASC and DESC order. */ + if( iIdx>0 && rc==SQLITE_OK ){ + int f = flags|FTS5INDEX_QUERY_TEST_NOIDX; + ck2 = 0; + rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2); + if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT; + } + if( iIdx>0 && rc==SQLITE_OK ){ + int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC; + ck2 = 0; + rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2); if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT; - - /* If this is a prefix query, check that the results returned if the - ** the index is disabled are the same. In both ASC and DESC order. */ - if( iIdx>0 && rc==SQLITE_OK ){ - int f = flags|FTS5INDEX_QUERY_TEST_NOIDX; - ck2 = 0; - rc = fts5QueryCksum(p, z, n, f, &ck2); - if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT; - } - if( iIdx>0 && rc==SQLITE_OK ){ - int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC; - ck2 = 0; - rc = fts5QueryCksum(p, z, n, f, &ck2); - if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT; - } - - cksum3 ^= ck1; - fts5BufferSet(&rc, &term, n, (const u8*)z); - p->rc = rc; } + + cksum3 ^= ck1; + fts5BufferSet(&rc, &term, n, (const u8*)z); + p->rc = rc; } - fts5MultiIterFree(p, pIter); - fts5StructureRelease(pStruct); } + fts5MultiIterFree(p, pIter); + if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT; if( p->rc==SQLITE_OK && cksum!=cksum3 ) p->rc = FTS5_CORRUPT; + fts5StructureRelease(pStruct); fts5BufferFree(&term); fts5BufferFree(&poslist); return fts5IndexReturn(p); @@ -4993,12 +4931,12 @@ u64 sqlite3Fts5IndexCksum( u64 ret = 0; /* Return value */ int iIdx; /* For iterating through indexes */ - ret = fts5IndexEntryCksum(iRowid, iCol, iPos, pTerm, nTerm); + ret = fts5IndexEntryCksum(iRowid, iCol, iPos, 0, pTerm, nTerm); for(iIdx=0; iIdxnPrefix; iIdx++){ int nByte = fts5IndexCharlenToBytelen(pTerm, nTerm, pConfig->aPrefix[iIdx]); if( nByte ){ - ret ^= fts5IndexEntryCksum(iRowid, iCol, iPos, pTerm, nByte); + ret ^= fts5IndexEntryCksum(iRowid, iCol, iPos, iIdx+1, pTerm, nByte); } } @@ -5017,7 +4955,6 @@ u64 sqlite3Fts5IndexCksum( */ static void fts5DecodeRowid( i64 iRowid, /* Rowid from %_data table */ - int *piIdx, /* OUT: Index */ int *piSegid, /* OUT: Segment id */ int *piHeight, /* OUT: Height */ int *piPgno /* OUT: Page number */ @@ -5029,14 +4966,11 @@ static void fts5DecodeRowid( iRowid >>= FTS5_DATA_HEIGHT_B; *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); - iRowid >>= FTS5_DATA_ID_B; - - *piIdx = (int)(iRowid & (((i64)1 << FTS5_DATA_IDX_B) - 1)); } static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ - int iIdx,iSegid,iHeight,iPgno; /* Rowid compenents */ - fts5DecodeRowid(iKey, &iIdx, &iSegid, &iHeight, &iPgno); + int iSegid, iHeight, iPgno; /* Rowid compenents */ + fts5DecodeRowid(iKey, &iSegid, &iHeight, &iPgno); if( iSegid==0 ){ if( iKey==FTS5_AVERAGES_ROWID ){ @@ -5048,12 +4982,12 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ } } else if( iHeight==FTS5_SEGMENT_MAX_HEIGHT ){ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(dlidx idx=%d segid=%d pgno=%d)", - iIdx, iSegid, iPgno + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(dlidx segid=%d pgno=%d)", + iSegid, iPgno ); }else{ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(idx=%d segid=%d h=%d pgno=%d)", - iIdx, iSegid, iHeight, iPgno + sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(segid=%d h=%d pgno=%d)", + iSegid, iHeight, iPgno ); } } @@ -5163,7 +5097,7 @@ static void fts5DecodeFunction( sqlite3_value **apVal /* Function arguments */ ){ i64 iRowid; /* Rowid for record being decoded */ - int iIdx,iSegid,iHeight,iPgno; /* Rowid components */ + int iSegid,iHeight,iPgno; /* Rowid components */ const u8 *aBlob; int n; /* Record to decode */ u8 *a = 0; Fts5Buffer s; /* Build up text to return here */ @@ -5180,7 +5114,7 @@ static void fts5DecodeFunction( a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace); if( a==0 ) goto decode_out; memcpy(a, aBlob, n); - fts5DecodeRowid(iRowid, &iIdx, &iSegid, &iHeight, &iPgno); + fts5DecodeRowid(iRowid, &iSegid, &iHeight, &iPgno); fts5DebugRowid(&rc, &s, iRowid); if( iHeight==FTS5_SEGMENT_MAX_HEIGHT ){ @@ -5301,19 +5235,19 @@ static void fts5RowidFunction( zArg = (const char*)sqlite3_value_text(apVal[0]); if( 0==sqlite3_stricmp(zArg, "segment") ){ i64 iRowid; - int idx, segid, height, pgno; - if( nArg!=5 ){ + int segid, height, pgno; + if( nArg!=4 ){ sqlite3_result_error(pCtx, - "should be: fts5_rowid('segment', idx, segid, height, pgno))", -1 + "should be: fts5_rowid('segment', segid, height, pgno))", -1 ); }else{ - idx = sqlite3_value_int(apVal[1]); - segid = sqlite3_value_int(apVal[2]); - height = sqlite3_value_int(apVal[3]); - pgno = sqlite3_value_int(apVal[4]); - iRowid = FTS5_SEGMENT_ROWID(idx, segid, height, pgno); + segid = sqlite3_value_int(apVal[1]); + height = sqlite3_value_int(apVal[2]); + pgno = sqlite3_value_int(apVal[3]); + iRowid = FTS5_SEGMENT_ROWID(segid, height, pgno); sqlite3_result_int64(pCtx, iRowid); } +#if 0 }else if( 0==sqlite3_stricmp(zArg, "start-of-index") ){ i64 iRowid; int idx; @@ -5326,6 +5260,7 @@ static void fts5RowidFunction( iRowid = FTS5_SEGMENT_ROWID(idx, 1, 0, 0); sqlite3_result_int64(pCtx, iRowid); } +#endif }else { sqlite3_result_error(pCtx, "first arg to fts5_rowid() must be 'segment' " diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index e7f5027d86..0ea99c25a2 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -257,6 +257,9 @@ int sqlite3Fts5StorageOpen( pConfig, "config", "k PRIMARY KEY, v", 1, pzErr ); } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION); + } } if( rc ){ @@ -543,6 +546,9 @@ int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexReinit(p->pIndex); } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION); + } return rc; } @@ -983,18 +989,23 @@ int sqlite3Fts5StorageRollback(Fts5Storage *p){ int sqlite3Fts5StorageConfigValue( Fts5Storage *p, - const char *z, - sqlite3_value *pVal + const char *z, + sqlite3_value *pVal, + int iVal ){ sqlite3_stmt *pReplace = 0; int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0); if( rc==SQLITE_OK ){ sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_STATIC); - sqlite3_bind_value(pReplace, 2, pVal); + if( pVal ){ + sqlite3_bind_value(pReplace, 2, pVal); + }else{ + sqlite3_bind_int(pReplace, 2, iVal); + } sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); } - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && pVal ){ int iNew = p->pConfig->iCookie + 1; rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew); if( rc==SQLITE_OK ){ diff --git a/ext/fts5/test/fts5al.test b/ext/fts5/test/fts5al.test index 522f44ce23..9f712ffc86 100644 --- a/ext/fts5/test/fts5al.test +++ b/ext/fts5/test/fts5al.test @@ -26,17 +26,17 @@ ifcapable !fts5 { do_execsql_test 1.1 { CREATE VIRTUAL TABLE ft1 USING fts5(x); SELECT * FROM ft1_config; -} {} +} {version 1} do_execsql_test 1.2 { INSERT INTO ft1(ft1, rank) VALUES('pgsz', 32); SELECT * FROM ft1_config; -} {pgsz 32} +} {pgsz 32 version 1} do_execsql_test 1.3 { INSERT INTO ft1(ft1, rank) VALUES('pgsz', 64); SELECT * FROM ft1_config; -} {pgsz 64} +} {pgsz 64 version 1} #-------------------------------------------------------------------------- # Test the logic for parsing the rank() function definition. diff --git a/ext/fts5/test/fts5corrupt.test b/ext/fts5/test/fts5corrupt.test index 7d0ea9d2bc..efbe3f5d84 100644 --- a/ext/fts5/test/fts5corrupt.test +++ b/ext/fts5/test/fts5corrupt.test @@ -37,7 +37,7 @@ set segid [lindex [fts5_level_segids t1] 0] do_test 1.3 { execsql { - DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', 0, $segid, 0, 4); + DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 0, 4); } catchsql { INSERT INTO t1(t1) VALUES('integrity-check') } } {1 {database disk image is malformed}} @@ -46,7 +46,7 @@ do_test 1.4 { db_restore_and_reopen execsql { UPDATE t1_data set block = X'00000000' || substr(block, 5) WHERE - rowid = fts5_rowid('segment', 0, $segid, 0, 4); + rowid = fts5_rowid('segment', $segid, 0, 4); } catchsql { INSERT INTO t1(t1) VALUES('integrity-check') } } {1 {database disk image is malformed}} diff --git a/ext/fts5/test/fts5corrupt2.test b/ext/fts5/test/fts5corrupt2.test index 74591cda78..7cbd7b00e8 100644 --- a/ext/fts5/test/fts5corrupt2.test +++ b/ext/fts5/test/fts5corrupt2.test @@ -126,8 +126,8 @@ do_execsql_test 3.0 { } foreach {tn hdr} { - 1 "\00\00\00\00" - 2 "\FF\FF\FF\FF" + 1 "\x00\x00\x00\x00" + 2 "\xFF\xFF\xFF\xFF" } { set tn2 0 set nCorrupt 0 diff --git a/ext/fts5/test/fts5ea.test b/ext/fts5/test/fts5ea.test index 1248edea0b..b80e767b63 100644 --- a/ext/fts5/test/fts5ea.test +++ b/ext/fts5/test/fts5ea.test @@ -79,6 +79,13 @@ foreach {tn expr err} { do_catchsql_test 3.$tn {SELECT fts5_expr($expr, 'name', 'addr')} [list 1 $err] } +#------------------------------------------------------------------------- +# Experiment with a tokenizer that considers " to be a token character. +# +do_execsql_test 4.0 { + SELECT fts5_expr('a AND """"', 'x', 'tokenize="unicode61 tokenchars ''""''"'); +} {{"a" AND """"}} + diff --git a/ext/fts5/test/fts5fault1.test b/ext/fts5/test/fts5fault1.test index ff6e2483e9..56f73c3ab7 100644 --- a/ext/fts5/test/fts5fault1.test +++ b/ext/fts5/test/fts5fault1.test @@ -32,12 +32,12 @@ ifcapable !fts5 { # faultsim_save_and_close -do_faultsim_test 1 -prep { +do_faultsim_test 1 -faults ioerr-t* -prep { faultsim_restore_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3') } } -test { - faultsim_test_result {0 {}} + faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}} } reset_db diff --git a/ext/fts5/test/fts5fault4.test b/ext/fts5/test/fts5fault4.test index 943d331db8..df2112c63f 100644 --- a/ext/fts5/test/fts5fault4.test +++ b/ext/fts5/test/fts5fault4.test @@ -201,8 +201,6 @@ do_faultsim_test 6.2 -faults oom-t* -body { faultsim_test_result {0 {0 2 7}} {1 SQLITE_NOMEM} } -} - #------------------------------------------------------------------------- # OOM error when querying for a phrase with many tokens. # @@ -304,6 +302,36 @@ do_faultsim_test 9.1 -faults oom-* -body { faultsim_test_result {0 {50 100 150 200}} {1 SQLITE_NOMEM} } +#------------------------------------------------------------------------- +# OOM in fts5_expr() SQL function. +# +reset_db +do_execsql_test 10.0 { + CREATE VIRTUAL TABLE tt USING fts5(x); + INSERT INTO tt(tt, rank) VALUES('pgsz', 32); + BEGIN; + WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<200) + INSERT INTO tt(rowid, x) + SELECT i, CASE WHEN (i%50)==0 THEN 'a a a a a a' ELSE 'a x a x a x' END + FROM ii; + COMMIT; +} + +} + +do_faultsim_test 10.1 -faults oom-t* -body { + db one { SELECT fts5_expr('a AND b NEAR(a b)') } +} -test { + faultsim_test_result {0 {"a" AND ("b" AND NEAR("a" "b", 10))}} +} + +#do_faultsim_test 10.2 -faults oom-t* -body { +# db one { SELECT fts5_expr_tcl('x:"a b c" AND b NEAR(a b)', 'ns', 'x') } +#} -test { +# set res {[ns -col 0 -- {a b c}] && ([ns -- {b}] && [ns -near 10 -- {a} {b}]} +# faultsim_test_result [list 0 $res] +#} + finish_test diff --git a/ext/fts5/test/fts5integrity.test b/ext/fts5/test/fts5integrity.test new file mode 100644 index 0000000000..a6dc34a90e --- /dev/null +++ b/ext/fts5/test/fts5integrity.test @@ -0,0 +1,35 @@ +# 2015 Jan 13 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# 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. +# +#*********************************************************************** +# +# This file containst tests focused on the integrity-check procedure. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5integrity + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE xx USING fts5(x); + INSERT INTO xx VALUES('term'); +} +do_execsql_test 1.1 { + INSERT INTO xx(xx) VALUES('integrity-check'); +} + +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE yy USING fts5(x, prefix=1); + INSERT INTO yy VALUES('term'); +} +do_execsql_test 2.1 { + INSERT INTO yy(yy) VALUES('integrity-check'); +} + +finish_test + diff --git a/ext/fts5/test/fts5prefix.test b/ext/fts5/test/fts5prefix.test index 7c5a1a39a9..c555080a27 100644 --- a/ext/fts5/test/fts5prefix.test +++ b/ext/fts5/test/fts5prefix.test @@ -15,17 +15,28 @@ source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5prefix +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE xx USING fts5(x, prefix=1); + INSERT INTO xx VALUES('one two three'); + INSERT INTO xx VALUES('four five six'); + INSERT INTO xx VALUES('seven eight nine ten'); +} + +do_execsql_test 1.1 { + SELECT rowid FROM xx WHERE xx MATCH 't*' +} {1 3} + #------------------------------------------------------------------------- # Check that prefix indexes really do index n-character prefixes, not # n-byte prefixes. Use the ascii tokenizer so as not to be confused by # diacritic removal. # -do_execsql_test 1.0 { +do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = ascii, prefix = 2) } -do_test 1.2 { +do_test 2.1 { foreach {rowid string} { 1 "\xCA\xCB\xCC\xCD" 2 "\u1234\u5678\u4321\u8765" @@ -34,26 +45,15 @@ do_test 1.2 { } } {} -do_execsql_test 1.1.2 { +do_execsql_test 2.2 { INSERT INTO t1(t1) VALUES('integrity-check'); } -#db eval { select fts5_decode(id, block) AS d FROM t1_data; } { puts $d } - -foreach o {1 2} { - if {$o==2} breakpoint - foreach {tn q res} { - 1 "SELECT rowid FROM t1 WHERE t1 MATCH '\xCA\xCB*'" 1 - 2 "SELECT rowid FROM t1 WHERE t1 MATCH '\u1234\u5678*'" 2 - } { - do_execsql_test 1.$o.$tn $q $res - } - - execsql { - DELETE FROM t1_data WHERE - rowid>=fts5_rowid('start-of-index', 0) AND - rowid=$nOpt } usage + set O(prefix) [lindex $argv $i] + } + default { usage } @@ -98,8 +105,10 @@ sqlite3 db $dbfile db func loadfile loadfile db transaction { + set pref "" + if {$O(prefix)!=""} { set pref ", prefix='$O(prefix)'" } catch { - db eval "CREATE VIRTUAL TABLE t1 USING $O(vtab) (path, content$O(tok))" + db eval "CREATE VIRTUAL TABLE t1 USING $O(vtab) (path, content$O(tok)$pref)" } if {$O(automerge)>=0} { if {$O(vtab) == "fts5"} { diff --git a/manifest b/manifest index 53ddbec52d..c9a1dfd035 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reorganize\ssome\sof\sthe\sfts5\sexpression\sparsing\scode.\sImprove\stest\scoverage\sof\sthe\ssame. -D 2015-05-02T20:35:24.467 +C Change\sto\sstoring\sall\skeys\sin\sa\ssingle\smerge-tree\sstructure\sinstead\sof\sone\smain\sstructure\sand\sa\sseparate\sone\sfor\seach\sprefix\sindex.\sThis\sis\sa\sfile-format\schange.\sAlso\sintroduce\sa\smechanism\sfor\smanaging\sfile-format\schanges. +D 2015-05-07T19:29:46.763 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 31b38b9da2e4b36f54a013bd71a5c3f6e45ca78f F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -104,16 +104,16 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl 159c1194da0bc72f51b3c2eb71022568006dc5ad F ext/fts5/extract_api_docs.tcl 55a6d648d516f35d9a1e580ac00de27154e1904a -F ext/fts5/fts5.c 3a0a73bcfbcb7e65ccda099cfb8fd268d2480c7e +F ext/fts5/fts5.c 62b2657320aac309d7bcf2bfb855f8d4c216ae15 F ext/fts5/fts5.h 24a2cc35b5e76eec57b37ba48c12d9d2cb522b3a -F ext/fts5/fts5Int.h 05e97ffb2911e8c8cfcb8bdb009e17347c24eb2d +F ext/fts5/fts5Int.h 94b1800ea50e52ce19365744174c65e6fc8b87e0 F ext/fts5/fts5_aux.c d53f00f31ad615ca4f139dd8751f9041afa00971 F ext/fts5/fts5_buffer.c 70b971e13503566f1e257941c60817ba0920a16b -F ext/fts5/fts5_config.c 4e0de8bea4746a7560740b9dcf8be4dced68ef4f -F ext/fts5/fts5_expr.c f49d68411dc72cb66f2b55cc109dbf3dce368eef -F ext/fts5/fts5_hash.c 29d8b0668727863cc1f1efa65efe4dd78635b016 -F ext/fts5/fts5_index.c de588982b0237b1605d6c37afd115b34c95c3da1 -F ext/fts5/fts5_storage.c ef60fc9dcc4e274f9589165e26833173c273ae18 +F ext/fts5/fts5_config.c 7a8b4665239a4f3001a4ecbc77573c42d2694161 +F ext/fts5/fts5_expr.c 3fe1170453d6a322d2de8a3fd0aed3edff7b8b09 +F ext/fts5/fts5_hash.c 54dd25348a46ea62ea96322c572e08cd1fb37304 +F ext/fts5/fts5_index.c aa8d73d043417740c07861beb78c86103a6a9d90 +F ext/fts5/fts5_storage.c cb8b585bfb7870a36101f1a8fa0b0777f4d1b68d F ext/fts5/fts5_tcl.c aa3b102bb01f366174718be7ce8e9311b9abb482 F ext/fts5/fts5_tokenize.c 830eae0d35a5a5a90af34df65da3427f46d942fc F ext/fts5/fts5_unicode2.c f74f53316377068812a1fa5a37819e6b8124631d @@ -131,38 +131,39 @@ F ext/fts5/test/fts5ah.test d74cf8b7de5b8424f732acef69fe12122a12f2bf F ext/fts5/test/fts5ai.test f20e53bbf0c55bc596f1fd47f2740dae028b8f37 F ext/fts5/test/fts5aj.test 05b569f5c16ea3098fb1984eec5cf50dbdaae5d8 F ext/fts5/test/fts5ak.test 7b8c5df96df599293f920b7e5521ebc79f647592 -F ext/fts5/test/fts5al.test e6bddd2c11c0d1e3ae189ee51081899d2f4ea570 +F ext/fts5/test/fts5al.test 8cde0a064ffe452281b7c90a759d220f796bbb20 F ext/fts5/test/fts5aux.test d9c724351d8e4dc46cad1308c0b4b8ac94d07660 F ext/fts5/test/fts5auxdata.test c69b86092bf1a157172de5f9169731af3403179b F ext/fts5/test/fts5bigpl.test b1cfd00561350ab04994ba7dd9d48468e5e0ec3b F ext/fts5/test/fts5content.test 532e15b541254410adc7bfb51f94631cfe82de8f -F ext/fts5/test/fts5corrupt.test 138aecc75c36c3dac9259c7f57c5bc3d009255f8 -F ext/fts5/test/fts5corrupt2.test 494111fd4f2dab36499cf97718eaba1f7c11e9d0 +F ext/fts5/test/fts5corrupt.test 35bfdbbb3cdcea46ae7385f6432e9b5c574e70a1 +F ext/fts5/test/fts5corrupt2.test c65a6619a1f712b87be0ccb3ef1a2120bf1f6430 F ext/fts5/test/fts5dlidx.test 748a84ceb74a4154725096a26dfa854260b0182f F ext/fts5/test/fts5doclist.test 635b80ac785627841a59c583bac702b55d49fdc5 -F ext/fts5/test/fts5ea.test f4d35cd2776dab9358206f7d88a67ea187fdec22 +F ext/fts5/test/fts5ea.test ed163ed820fd503354bd7dcf9d3b0e3801ade962 F ext/fts5/test/fts5eb.test 728a1f23f263548f5c29b29dfb851b5f2dbe723e -F ext/fts5/test/fts5fault1.test ed71717a479bef32d05f02d9c48691011d160d4d +F ext/fts5/test/fts5fault1.test b42d3296be8a75f557cf2cbce0d8b483fc9db45b F ext/fts5/test/fts5fault2.test 26c3d70648f691e2cc9391e14bbc11a973656383 F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3 -F ext/fts5/test/fts5fault4.test 09728cadb4897c97cea092edb9c431d9ec25b88b +F ext/fts5/test/fts5fault4.test 420f2e23775b458eeb9a325bcdfe84650c2e9d39 F ext/fts5/test/fts5full.test 0924bdca5416a242103239ace79c6f5aa34bab8d F ext/fts5/test/fts5hash.test adb7b0442cc1c77c507f07e16d11490486e75dfa +F ext/fts5/test/fts5integrity.test 39deee579b84df2786d9c8298e9196b339cfc872 F ext/fts5/test/fts5merge.test 453a0717881aa7784885217b2040f3f275caff03 F ext/fts5/test/fts5near.test 3f9f64e16cac82725d03d4e04c661090f0b3b947 F ext/fts5/test/fts5optimize.test 0028c90a7817d3e576d1148fc8dff17d89054e54 F ext/fts5/test/fts5plan.test 89783f70dab89ff936ed6f21d88959b49c853a47 F ext/fts5/test/fts5porter.test 50322599823cb8080a99f0ec0c39f7d0c12bcb5e -F ext/fts5/test/fts5prefix.test 1287803c3df0e43f536196256fb9e0e6baccb4f1 +F ext/fts5/test/fts5prefix.test 7eba86fc270b110ba2b83ba286a1fd4b3b17955e F ext/fts5/test/fts5rank.test f59a6b20ec8e08cb130d833dcece59cf9cd92890 F ext/fts5/test/fts5rebuild.test 77c6613aa048f38b4a12ddfacb2e6e1342e1b066 F ext/fts5/test/fts5restart.test cd58a5fb552ac10db549482698e503f82693bcd0 -F ext/fts5/test/fts5rowid.test a1b2a6d76648c734c1aab11ee1a619067e8d90e6 +F ext/fts5/test/fts5rowid.test 0dd51524739ebe5f1251a25f3d3ece9840fdc1a8 F ext/fts5/test/fts5tokenizer.test bbcde2a7473dcaa9a1fc6809aa8965acb7b846ff F ext/fts5/test/fts5unicode.test 79b3e34eb29ce4929628aa514a40cb467fdabe4d F ext/fts5/test/fts5unicode2.test 64a5267fd6082fcb46439892ebd0cbaa5c38acee F ext/fts5/test/fts5unindexed.test f388605341a476b6ab622b4c267cd168f59a5944 -F ext/fts5/tool/loadfts5.tcl 1e126891d14ab85dcdb0fac7755a4cd5ba52e8b8 +F ext/fts5/tool/loadfts5.tcl 8a8f10d7d2d0d77f622e0a84cc0824c158c34a52 F ext/fts5/tool/showfts5.tcl 921f33b30c3189deefd2b2cc81f951638544aaf1 F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb @@ -1315,7 +1316,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P d4331943dff259380c4025bb740d8aba6972d351 -R 5651f6663bbd8efe3d630621e3f4b900 +P c4456dc5f5f8f45f04e3bbae53b6bcc209fc27d5 +R d535b9198036ecff3827ebadb9a9b6f4 U dan -Z 822e7611ba479003f18b45bbb7ca820a +Z 02d7c47a9ec4004e437813912cd05f33 diff --git a/manifest.uuid b/manifest.uuid index ddc1ccf8f3..d0b2967a0c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4456dc5f5f8f45f04e3bbae53b6bcc209fc27d5 \ No newline at end of file +a684b5e2d9d52cf4700e7e5f9dd547a2ba54e8e9 \ No newline at end of file