diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 0b8851d227..b15521f163 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -636,18 +636,13 @@ Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); int sqlite3Fts5FlushToDisk(Fts5Table*); -int sqlite3Fts5ExtractText( - Fts5Config *pConfig, - sqlite3_value *pVal, /* Value to extract text from */ - int bContent, /* Loaded from content table */ - int *pbResetTokenizer, /* OUT: True if ClearLocale() required */ - const char **ppText, /* OUT: Pointer to text buffer */ - int *pnText /* OUT: Size of (*ppText) in bytes */ -); - void sqlite3Fts5ClearLocale(Fts5Config *pConfig); +void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc); int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal); +int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal, + const char **ppText, int *pnText, const char **ppLoc, int *pnLoc +); /* ** End of interface to code in fts5.c. diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 3cb1bd3bea..1fade5f7a8 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -516,6 +516,15 @@ static int fts5ConfigMakeExprlist(Fts5Config *p){ } } } + if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){ + for(i=0; inCol; i++){ + if( p->abUnindexed[i]==0 ){ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i); + }else{ + sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL"); + } + } + } assert( p->zContentExprlist==0 ); p->zContentExprlist = (char*)buf.p; diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 6ccca8a3ee..db413b572a 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -1259,7 +1259,7 @@ static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ ** valid until after the final call to sqlite3Fts5Tokenize() that will use ** the locale. */ -static void fts5SetLocale( +static void sqlite3Fts5SetLocale( Fts5Config *pConfig, const char *zLocale, int nLocale @@ -1270,11 +1270,10 @@ static void fts5SetLocale( } /* -** Clear any locale configured by an earlier call to fts5SetLocale() or -** sqlite3Fts5ExtractText(). +** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale(). */ void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ - fts5SetLocale(pConfig, 0, 0); + sqlite3Fts5SetLocale(pConfig, 0, 0); } /* @@ -1302,118 +1301,43 @@ int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){ } /* -** This function is used to extract utf-8 text from an sqlite3_value. This -** is usually done in order to tokenize it. For example, when: +** Value pVal is guaranteed to be an fts5_locale() value, according to +** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale +** from the value and returns them separately. ** -** * a value is written to an fts5 table, -** * a value is deleted from an FTS5_CONTENT_NORMAL table, -** * a value containing a query expression is passed to xFilter() +** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set +** to point to buffers containing the text and locale, as utf-8, +** respectively. In this case output parameters (*pnText) and (*pnLoc) are +** set to the sizes in bytes of these two buffers. ** -** and so on. -** -** This function handles 2 cases: -** -** 1) Ordinary values. The text can be extracted from these using -** sqlite3_value_text(). -** -** 2) Combination text/locale blobs created by fts5_locale(). There -** are several cases for these: -** -** * Blobs that have the 16-byte header, and -** * Blobs read from the content table of a locale=1 regular -** content table. -** -** The first case above has the 16 byte FTS5_LOCALE_HDR(pConfig) -** header. It is an error if a blob read from the content table of -** an external content table does not have the required header. A blob -** read from the content table of a regular locale=1 table does not -** have the header. This is to save space. -** -** If successful, SQLITE_OK is returned and output parameters (*ppText) -** and (*pnText) are set to point to a buffer containing the extracted utf-8 -** text and its length in bytes, respectively. The buffer is not -** nul-terminated. It has the same lifetime as the sqlite3_value object -** from which it is extracted. -** -** Parameter bContent must be true if the value was read from an indexed -** column (i.e. not UNINDEXED) of the on disk content. -** -** If pbResetTokenizer is not NULL and if case (2) is used, then -** fts5SetLocale() is called to ensure subsequent sqlite3Fts5Tokenize() calls -** use the locale. In this case (*pbResetTokenizer) is set to true before -** returning, to indicate that the caller must call sqlite3Fts5ClearLocale() -** to clear the locale after tokenizing the text. +** Or, if an error occurs, then an SQLite error code is returned. The final +** value of the four output parameters is undefined in this case. */ -int sqlite3Fts5ExtractText( - Fts5Config *pConfig, - sqlite3_value *pVal, /* Value to extract text from */ - int bContent, /* True if indexed table content */ - int *pbResetTokenizer, /* OUT: True if xSetLocale(NULL) required */ - const char **ppText, /* OUT: Pointer to text buffer */ - int *pnText /* OUT: Size of (*ppText) in bytes */ +int sqlite3Fts5DecodeLocaleValue( + sqlite3_value *pVal, + const char **ppText, + int *pnText, + const char **ppLoc, + int *pnLoc ){ - const char *pText = 0; - int nText = 0; - int rc = SQLITE_OK; + const char *p = sqlite3_value_blob(pVal); + int n = sqlite3_value_bytes(pVal); + int nLoc = 0; - /* 0: Do not decode blob - ** 1: Decode blob, expect fts5_locale() header - ** 2: Decode blob, expect no fts5_locale() header - */ - int eDecodeBlob = 0; + assert( sqlite3_value_type(pVal)==SQLITE_BLOB ); + assert( n>FTS5_LOCALE_HDR_SIZE ); - assert( pbResetTokenizer==0 || *pbResetTokenizer==0 ); - assert( bContent==0 || pConfig->eContent!=FTS5_CONTENT_NONE ); - - if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ - if( bContent - && pConfig->bLocale - && pConfig->eContent==FTS5_CONTENT_NORMAL - ){ - eDecodeBlob = 2; - }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ - eDecodeBlob = 1; - }else if( bContent && pConfig->bLocale ){ - return SQLITE_ERROR; + for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){ + if( nLoc==(n-1) ){ + return SQLITE_MISMATCH; } } + *ppLoc = &p[FTS5_LOCALE_HDR_SIZE]; + *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE; - if( eDecodeBlob ){ - const u8 *pBlob = sqlite3_value_blob(pVal); - int nBlob = sqlite3_value_bytes(pVal); - int nLocale = 0; - - /* Unless this blob was read from the %_content table of an - ** FTS5_CONTENT_NORMAL table, it should have the 4 byte fts5_locale() - ** header. Check for this. If it is not found, return an error. */ - if( eDecodeBlob==1 ){ - pBlob += FTS5_LOCALE_HDR_SIZE; - nBlob -= FTS5_LOCALE_HDR_SIZE; - } - - for(nLocale=0; nLocalenCol; ii++){ - sqlite3_value *pVal = apVal[ii+2]; - if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ - int isLocale = sqlite3Fts5IsLocaleValue(pConfig, pVal); - if( pConfig->bLocale ){ - if( isLocale==0 && pConfig->abUnindexed[ii]==0 ){ - rc = SQLITE_MISMATCH; - goto update_out; - } - }else{ - if( isLocale ){ - fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); - rc = SQLITE_MISMATCH; - goto update_out; - } + /* It is an error to write an fts5_locale() value to a table without + ** the locale=1 option. */ + if( pConfig->bLocale==0 ){ + int ii; + for(ii=0; iinCol; ii++){ + sqlite3_value *pVal = apVal[ii+2]; + if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); + rc = SQLITE_MISMATCH; + goto update_out; } } } @@ -2166,11 +2082,11 @@ static int fts5ApiTokenize_v2( Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); int rc = SQLITE_OK; - fts5SetLocale(pTab->pConfig, pLoc, nLoc); + sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc); rc = sqlite3Fts5Tokenize(pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken ); - fts5SetLocale(pTab->pConfig, 0, 0); + sqlite3Fts5SetLocale(pTab->pConfig, 0, 0); return rc; } @@ -2198,6 +2114,49 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); } +/* +** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This +** function extracts the text value of column iCol of the current row. +** Additionally, if there is an associated locale, it invokes +** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller +** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point +** after this function returns. +** +** If successful, (*ppText) is set to point to a buffer containing the text +** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that +** buffer in bytes. It is not guaranteed to be nul-terminated. If an error +** occurs, an SQLite error code is returned. The final values of the two +** output parameters are undefined in this case. +*/ +static int fts5TextFromStmt( + Fts5Config *pConfig, + sqlite3_stmt *pStmt, + int iCol, + const char **ppText, + int *pnText +){ + sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1); + const char *pLoc = 0; + int nLoc = 0; + int rc = SQLITE_OK; + + if( pConfig->bLocale + && pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc); + }else{ + *ppText = (const char*)sqlite3_value_text(pVal); + *pnText = sqlite3_value_bytes(pVal); + if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){ + pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol); + nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol); + } + } + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); + return rc; +} + static int fts5ApiColumnText( Fts5Context *pCtx, int iCol, @@ -2217,10 +2176,8 @@ static int fts5ApiColumnText( }else{ rc = fts5SeekCursor(pCsr, 0); if( rc==SQLITE_OK ){ - Fts5Config *pConfig = pTab->pConfig; - int bContent = (pConfig->abUnindexed[iCol]==0); - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); - sqlite3Fts5ExtractText(pConfig, pVal, bContent, 0, pz, pn); + rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn); + sqlite3Fts5ClearLocale(pTab->pConfig); } } return rc; @@ -2262,17 +2219,15 @@ static int fts5CsrPoslist( rc = fts5SeekCursor(pCsr, 0); } for(i=0; inCol && rc==SQLITE_OK; i++){ - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); const char *z = 0; int n = 0; - int bReset = 0; - rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ExprPopulatePoslists( pConfig, pCsr->pExpr, aPopulator, i, z, n ); } - if( bReset ) sqlite3Fts5ClearLocale(pConfig); + sqlite3Fts5ClearLocale(pConfig); } sqlite3_free(aPopulator); @@ -2458,17 +2413,14 @@ static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ if( pConfig->abUnindexed[i]==0 ){ const char *z = 0; int n = 0; - int bReset = 0; - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); - pCsr->aColumnSize[i] = 0; - rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n); if( rc==SQLITE_OK ){ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb ); - if( bReset ) sqlite3Fts5ClearLocale(pConfig); } + sqlite3Fts5ClearLocale(pConfig); } } } @@ -2740,37 +2692,14 @@ static int fts5ApiColumnLocale( ){ rc = fts5SeekCursor(pCsr, 0); if( rc==SQLITE_OK ){ - /* Load the value into pVal. pVal is a locale/text pair iff: - ** - ** 1) It is an SQLITE_BLOB, and - ** 2) Either the FTS5_LOCALE_HDR header is present, or else the - ** value was loaded from an FTS5_CONTENT_NORMAL table. - ** - ** If condition (1) is met but condition (2) is not, it is an error. - */ - sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); - if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ - const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); - int nBlob = sqlite3_value_bytes(pVal); - if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ - if( sqlite3Fts5IsLocaleValue(pConfig, pVal)==0 ){ - rc = SQLITE_ERROR; - } - pBlob += FTS5_LOCALE_HDR_SIZE; - nBlob -= FTS5_LOCALE_HDR_SIZE; - } - if( rc==SQLITE_OK ){ - int nLocale = 0; - for(nLocale=0; nLocalepStmt, iCol, &zDummy, &nDummy); + if( rc==SQLITE_OK ){ + *pzLocale = pConfig->t.pLocale; + *pnLocale = pConfig->t.nLocale; } + sqlite3Fts5ClearLocale(pConfig); } } @@ -2991,58 +2920,6 @@ static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){ return rc; } -/* -** Value pVal was read from column iCol of the FTS5 table. This function -** returns it to the owner of pCtx via a call to an sqlite3_result_xxx() -** function. This function deals with the same cases as -** sqlite3Fts5ExtractText(): -** -** 1) Ordinary values. These can be returned using sqlite3_result_value(). -** -** 2) Blobs from fts5_locale(). The text is extracted from these and -** returned via sqlite3_result_text(). The locale is discarded. -*/ -static void fts5ExtractValueFromColumn( - sqlite3_context *pCtx, - Fts5Config *pConfig, - int iCol, - sqlite3_value *pVal -){ - assert( pConfig->eContent!=FTS5_CONTENT_NONE ); - - if( pConfig->bLocale - && sqlite3_value_type(pVal)==SQLITE_BLOB - && pConfig->abUnindexed[iCol]==0 - ){ - const u8 *pBlob = sqlite3_value_blob(pVal); - int nBlob = sqlite3_value_bytes(pVal); - int ii; - - if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ - if( nBlobpStmt, iCol+1); - fts5ExtractValueFromColumn(pCtx, pConfig, iCol, pVal); + if( pConfig->bLocale + && pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + const char *z = 0; + int n = 0; + rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n); + if( rc==SQLITE_OK ){ + sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT); + } + sqlite3Fts5ClearLocale(pConfig); + }else{ + sqlite3_result_value(pCtx, pVal); + } } + pConfig->pzErrmsg = 0; } } diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index 3bf20b3390..e8649c703f 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -73,6 +73,30 @@ struct Fts5Storage { #define FTS5_STMT_REPLACE_CONFIG 10 #define FTS5_STMT_SCAN 11 +/* +** Return a pointer to a buffer obtained from sqlite3_malloc() that contains +** nBind comma-separated question marks. e.g. if nBind is passed 5, this +** function returns "?,?,?,?,?". +** +** If *pRc is not SQLITE_OK when this function is called, it is a no-op and +** NULL is returned immediately. Or, if the attempt to malloc a buffer +** fails, then *pRc is set to SQLITE_NOMEM and NULL is returned. Otherwise, +** if it is SQLITE_OK when this function is called and the malloc() succeeds, +** *pRc is left unchanged. +*/ +static char *fts5BindingsList(int *pRc, int nBind){ + char *zBind = sqlite3Fts5MallocZero(pRc, 1 + nBind*2); + if( zBind ){ + int ii; + for(ii=0; iinCol + 1; + case FTS5_STMT_INSERT_CONTENT: { + int nCol = 0; char *zBind; int i; - zBind = sqlite3_malloc64(1 + nCol*2); - if( zBind ){ - for(i=0; inCol; + if( pC->bLocale ){ + for(i=0; inCol; i++){ + if( pC->abUnindexed[i]==0 ) nCol++; } - zBind[i*2-1] = '\0'; + } + + zBind = fts5BindingsList(&rc, nCol); + if( zBind ){ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind); sqlite3_free(zBind); } @@ -344,7 +369,7 @@ int sqlite3Fts5StorageOpen( if( bCreate ){ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ int nDefn = 32 + pConfig->nCol*10; - char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); + char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); if( zDefn==0 ){ rc = SQLITE_NOMEM; }else{ @@ -356,6 +381,14 @@ int sqlite3Fts5StorageOpen( sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); iOff += (int)strlen(&zDefn[iOff]); } + if( pConfig->bLocale ){ + for(i=0; inCol; i++){ + if( pConfig->abUnindexed[i]==0 ){ + sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); + iOff += (int)strlen(&zDefn[iOff]); + } + } + } rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); } sqlite3_free(zDefn); @@ -507,7 +540,8 @@ static int fts5StorageDeleteFromIndex( sqlite3_value *pVal = 0; const char *pText = 0; int nText = 0; - int bReset = 0; + const char *pLoc = 0; + int nLoc = 0; assert( pSeek==0 || apVal==0 ); assert( pSeek!=0 || apVal!=0 ); @@ -517,10 +551,19 @@ static int fts5StorageDeleteFromIndex( pVal = apVal[iCol-1]; } - rc = sqlite3Fts5ExtractText( - pConfig, pVal, pSeek!=0, &bReset, &pText, &nText - ); + if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + }else{ + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + if( pConfig->bLocale && pSeek ){ + pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol); + nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol); + } + } + if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); ctx.szCol = 0; rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback @@ -529,7 +572,7 @@ static int fts5StorageDeleteFromIndex( if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ rc = FTS5_CORRUPT; } - if( bReset ) sqlite3Fts5ClearLocale(pConfig); + sqlite3Fts5ClearLocale(pConfig); } } } @@ -788,20 +831,35 @@ int sqlite3Fts5StorageRebuild(Fts5Storage *p){ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ - int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ - sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); + int nLoc = 0; /* Size of pLoc in bytes */ + const char *pLoc = 0; /* Pointer to buffer containing text value */ + + sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); + if( pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + }else{ + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + if( pConfig->bLocale ){ + int iCol = ctx.iCol + 1 + pConfig->nCol; + pLoc = (const char*)sqlite3_column_text(pScan, iCol); + nLoc = sqlite3_column_bytes(pScan, iCol); + } + } - rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &pText, &nText); if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); - if( bReset ) sqlite3Fts5ClearLocale(pConfig); + sqlite3Fts5ClearLocale(pConfig); } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); @@ -884,33 +942,46 @@ int sqlite3Fts5StorageContentInsert( }else{ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ int i; /* Counter variable */ + int nIndexed = 0; /* Number indexed columns seen */ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); - for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ + if( pInsert ) sqlite3_clear_bindings(pInsert); + + /* Bind the rowid value */ + sqlite3_bind_value(pInsert, 1, apVal[1]); + + /* Loop through values for user-defined columns. i=2 is the leftmost + ** user-defined column. As is column 1 of pSavedRow. */ + for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ + int bUnindexed = pConfig->abUnindexed[i-2]; sqlite3_value *pVal = apVal[i]; + + nIndexed += !bUnindexed; if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ /* This is an UPDATE statement, and column (i-2) was not modified. ** Retrieve the value from Fts5Storage.pSavedRow instead. */ pVal = sqlite3_column_value(p->pSavedRow, i-1); - }else if( sqlite3_value_type(pVal)==SQLITE_BLOB && pConfig->bLocale ){ - assert( pConfig->bLocale ); - assert( i>1 ); - if( pConfig->abUnindexed[i-2] ){ - if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ - /* At attempt to insert an fts5_locale() value into an UNINDEXED - ** column. Strip the locale away and just bind the text. */ - const char *pText = 0; - int nText = 0; - rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, 0, &pText, &nText); - sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); - continue; - } - }else{ - const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); - int nBlob = sqlite3_value_bytes(pVal); - assert( nBlob>4 ); - sqlite3_bind_blob(pInsert, i, pBlob+16, nBlob-16, SQLITE_TRANSIENT); - continue; + if( pConfig->bLocale && bUnindexed==0 ){ + sqlite3_bind_value(pInsert, pConfig->nCol + 1 + nIndexed, + sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1) + ); } + }else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + const char *pText = 0; + const char *pLoc = 0; + int nText = 0; + int nLoc = 0; + assert( pConfig->bLocale ); + + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + if( rc==SQLITE_OK ){ + sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); + if( bUnindexed==0 ){ + int iLoc = pConfig->nCol + 1 + nIndexed; + sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT); + } + } + + continue; } rc = sqlite3_bind_value(pInsert, i, pVal); @@ -948,23 +1019,37 @@ int sqlite3Fts5StorageIndexInsert( for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ - int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ + int nLoc = 0; /* Size of pText in bytes */ + const char *pLoc = 0; /* Pointer to buffer containing text value */ + sqlite3_value *pVal = apVal[ctx.iCol+2]; - int bDisk = 0; if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); - bDisk = 1; + if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ + int iCol = ctx.iCol + 1 + pConfig->nCol; + pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol); + nLoc = sqlite3_column_bytes(p->pSavedRow, iCol); + } + }else{ + pVal = apVal[ctx.iCol+2]; } - rc = sqlite3Fts5ExtractText(pConfig, pVal, bDisk, &bReset, &pText,&nText); + + if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){ + rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc); + }else{ + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + } + if( rc==SQLITE_OK ){ - assert( bReset==0 || pConfig->bLocale ); + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); - if( bReset ) sqlite3Fts5ClearLocale(pConfig); + sqlite3Fts5ClearLocale(pConfig); } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); @@ -1129,37 +1214,61 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); } for(i=0; rc==SQLITE_OK && inCol; i++){ - if( pConfig->abUnindexed[i] ) continue; - ctx.iCol = i; - ctx.szCol = 0; - if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ - rc = sqlite3Fts5TermsetNew(&ctx.pTermset); - } - if( rc==SQLITE_OK ){ - int bReset = 0; /* True if tokenizer locale must be reset */ - int nText = 0; /* Size of pText in bytes */ - const char *pText = 0; /* Pointer to buffer containing text value */ + if( pConfig->abUnindexed[i]==0 ){ + const char *pText = 0; + int nText = 0; + const char *pLoc = 0; + int nLoc = 0; + sqlite3_value *pVal = sqlite3_column_value(pScan, i+1); + + if( pConfig->eContent==FTS5_CONTENT_EXTERNAL + && sqlite3Fts5IsLocaleValue(pConfig, pVal) + ){ + rc = sqlite3Fts5DecodeLocaleValue( + pVal, &pText, &nText, &pLoc, &nLoc + ); + }else{ + if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){ + int iCol = i + 1 + pConfig->nCol; + pLoc = (const char*)sqlite3_column_text(pScan, iCol); + nLoc = sqlite3_column_bytes(pScan, iCol); + } + pText = (const char*)sqlite3_value_text(pVal); + nText = sqlite3_value_bytes(pVal); + } + + ctx.iCol = i; + ctx.szCol = 0; + + if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ + rc = sqlite3Fts5TermsetNew(&ctx.pTermset); + } - rc = sqlite3Fts5ExtractText(pConfig, - sqlite3_column_value(pScan, i+1), 1, &bReset, &pText, &nText - ); if( rc==SQLITE_OK ){ + sqlite3Fts5SetLocale(pConfig, pLoc, nLoc); rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageIntegrityCallback ); - if( bReset ) sqlite3Fts5ClearLocale(pConfig); + sqlite3Fts5ClearLocale(pConfig); + } + + /* If this is not a columnsize=0 database, check that the number + ** of tokens in the value matches the aColSize[] value read from + ** the %_docsize table. */ + if( rc==SQLITE_OK + && pConfig->bColumnsize + && ctx.szCol!=aColSize[i] + ){ + rc = FTS5_CORRUPT; + } + aTotalSize[i] += ctx.szCol; + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ + sqlite3Fts5TermsetFree(ctx.pTermset); + ctx.pTermset = 0; } - } - if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ - rc = FTS5_CORRUPT; - } - aTotalSize[i] += ctx.szCol; - if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ - sqlite3Fts5TermsetFree(ctx.pTermset); - ctx.pTermset = 0; } } sqlite3Fts5TermsetFree(ctx.pTermset); diff --git a/ext/fts5/test/fts5blob.test b/ext/fts5/test/fts5blob.test index 4233719fbc..9348554104 100644 --- a/ext/fts5/test/fts5blob.test +++ b/ext/fts5/test/fts5blob.test @@ -103,13 +103,13 @@ do_execsql_test 3.0 { do_catchsql_test 3.1 { INSERT INTO x1(rowid, a, b) VALUES(113, 'hello world', X'123456'); -} {1 {datatype mismatch}} +} {0 {}} do_catchsql_test 3.2 { INSERT INTO x2(rowid, a, b) VALUES(113, 'hello world', X'123456'); -} {1 {datatype mismatch}} +} {0 {}} do_catchsql_test 3.3 { INSERT INTO x3(rowid, a, b) VALUES(113, 'hello world', X'123456'); -} {1 {datatype mismatch}} +} {0 {}} #-------------------------------------------------------------------------- diff --git a/ext/fts5/test/fts5faultI.test b/ext/fts5/test/fts5faultI.test index fe7c0aad50..72f25caee1 100644 --- a/ext/fts5/test/fts5faultI.test +++ b/ext/fts5/test/fts5faultI.test @@ -256,15 +256,16 @@ do_faultsim_test 10.1 -faults oom* -prep { faultsim_test_result {0 hello} } +breakpoint faultsim_save_and_close -do_faultsim_test 10.2 -faults oom* -prep { +do_faultsim_test 10.2 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { INSERT INTO ft VALUES(zeroblob(10000)); } } -test { - faultsim_test_result {1 {datatype mismatch}} + faultsim_test_result {0 {}} } #------------------------------------------------------------------------- diff --git a/ext/fts5/test/fts5locale.test b/ext/fts5/test/fts5locale.test index f0df4969dc..e5799fb7fd 100644 --- a/ext/fts5/test/fts5locale.test +++ b/ext/fts5/test/fts5locale.test @@ -73,6 +73,7 @@ do_execsql_test 1.2 { SELECT rowid, a FROM t1( fts5_locale('reverse', 'abc') ); } {2 cba} + #------------------------------------------------------------------------- # Test that the locale= option exists and seems to accept values. And # that fts5_locale() values may only be inserted into an internal-content @@ -99,8 +100,11 @@ do_catchsql_test 2.3 { INSERT INTO b1(b1, rank) VALUES('locale', 0); } {1 {SQL logic error}} -do_execsql_test 2.4 { +do_execsql_test 2.4.1 { INSERT INTO b1 VALUES('abc', 'one two three'); +} + +do_execsql_test 2.4.2 { INSERT INTO b1 VALUES('def', fts5_locale('reverse', 'four five six')); } @@ -131,6 +135,7 @@ do_execsql_test 2.12 { SELECT quote(y) FROM b1('ruof') } { do_execsql_test 2.13 { INSERT INTO b1(b1) VALUES('integrity-check'); } + do_execsql_test 2.14 { INSERT INTO b1(b1) VALUES('rebuild'); } @@ -149,7 +154,6 @@ do_execsql_test 2.18 { INSERT INTO b1(rowid, x, y) VALUES( test_setsubtype(45, 76), 'abc def', 'def abc' ); - INSERT INTO b1(b1) VALUES('integrity-check'); } #------------------------------------------------------------------------- @@ -278,9 +282,9 @@ do_execsql_test 5.2 { } do_execsql_test 5.3 { - SELECT typeof(c0), typeof(c1) FROM t1_content + SELECT typeof(c0), typeof(c1), typeof(l0) FROM t1_content } { - blob text + text text text } #------------------------------------------------------------------------- @@ -305,37 +309,37 @@ foreach {tn opt} { fts5_aux_test_functions db - do_execsql_test 5.$tn.3 { + do_execsql_test 6.$tn.3 { SELECT fts5_test_columnsize(y1) FROM y1 } { 2 3 2 4 } - do_execsql_test 5.$tn.4 { + do_execsql_test 6.$tn.4 { SELECT rowid, fts5_test_columnsize(y1) FROM y1('shall'); } { 2 3 } - do_execsql_test 5.$tn.5 { + do_execsql_test 6.$tn.5 { SELECT rowid, fts5_test_columnsize(y1) FROM y1('shall'); } { 2 3 } - do_execsql_test 5.$tn.6 { + do_execsql_test 6.$tn.6 { SELECT rowid, fts5_test_columnsize(y1) FROM y1('have'); } { 4 4 } - do_execsql_test 5.$tn.7 { + do_execsql_test 6.$tn.7 { SELECT rowid, highlight(y1, 0, '[', ']') FROM y1('have'); } { 4 {which it hath been used to [have]} } - do_execsql_test 5.$tn.8 { + do_execsql_test 6.$tn.8 { SELECT rowid, highlight(y1, 0, '[', ']'), snippet(y1, 0, '[', ']', '...', 10) @@ -473,7 +477,7 @@ foreach_detail_mode $::testprefix { } foreach {tn v} { - 1 X'001122' + 1 X'001152' 2 X'0011223344' 3 X'00E0B2EB68656c6c6f' 4 X'00E0B2EB0068656c6c6f' @@ -484,7 +488,7 @@ foreach_detail_mode $::testprefix { do_catchsql_test 10.2.$tn.3 { INSERT INTO ft(ft) VALUES('rebuild'); - } {1 {SQL logic error}} + } {0 {}} do_catchsql_test 10.2.$tn.4 " SELECT * FROM ft( test_setsubtype($v, 76) ); @@ -494,23 +498,23 @@ foreach_detail_mode $::testprefix { INSERT INTO ft(rowid, x) VALUES(1, 'hello world'); } - if {"%DETAIL%"!="full"} { - do_catchsql_test 10.2.$tn.6 { + if {"%DETAIL%"=="full"} { + do_execsql_test 10.2.$tn.6 { SELECT fts5_test_poslist(ft) FROM ft('world'); - } {1 SQLITE_ERROR} + } {0.0.1} - do_catchsql_test 10.2.$tn.7 { + do_execsql_test 10.2.$tn.7.1 { SELECT fts5_test_columnsize(ft) FROM ft('world'); - } {1 SQLITE_ERROR} + } {1} - do_catchsql_test 10.2.$tn.7 { + do_execsql_test 10.2.$tn.7.2 { SELECT fts5_test_columnlocale(ft) FROM ft('world'); - } {1 SQLITE_ERROR} + } {{{}}} } do_catchsql_test 10.2.$tn.8 { - SELECT * FROM ft('hello') - } {1 {SQL logic error}} + SELECT count(*) FROM ft('hello') + } {0 1} do_catchsql_test 10.2.$tn.9 { PRAGMA integrity_check; @@ -527,7 +531,7 @@ foreach_detail_mode $::testprefix { do_catchsql_test 10.2.$tn.12 " INSERT INTO ft(rowid, x) VALUES(2, test_setsubtype($v,76) ) - " {1 {datatype mismatch}} + " {0 {}} do_execsql_test 10.2.$tn.13 { INSERT INTO ft2(rowid, x) VALUES(1, 'hello world'); @@ -536,7 +540,7 @@ foreach_detail_mode $::testprefix { do_catchsql_test 10.2.$tn.15 { PRAGMA integrity_check; - } {1 {SQL logic error}} + } {0 {{malformed inverted index for FTS5 table main.ft2}}} do_execsql_test 10.2.$tn.16 { DELETE FROM ft2_content; @@ -679,13 +683,66 @@ do_execsql_test 14.3 { UPDATE ft SET b = fts5_locale('en_AU', 'world'); } -do_catchsql_test 14.4 { - INSERT INTO ft VALUES(X'abcd', X'1234'); -} {1 {datatype mismatch}} - do_execsql_test 14.4 { - SELECT * FROM ft -} {hello world} + INSERT INTO ft VALUES(X'abcd', X'1234'); +} {} + +do_execsql_test 14.5 { + SELECT quote(a), quote(b) FROM ft +} {'hello' 'world' X'ABCD' X'1234'} + +do_execsql_test 14.6 { + DELETE FROM ft; + INSERT INTO ft VALUES(NULL, 'null'); + INSERT INTO ft VALUES(123, 'int'); + INSERT INTO ft VALUES(345.0, 'real'); + INSERT INTO ft VALUES('abc', 'text'); + INSERT INTO ft VALUES(fts5_locale('abc', 'def'), 'text'); + + SELECT a, typeof(a), b FROM ft +} { + {} null null + 123 integer int + 345.0 real real + abc text text + def text text +} + +do_execsql_test 14.7 { + SELECT quote(c0), typeof(c0) FROM ft_content +} { + NULL null + 123 integer + 345.0 real + 'abc' text + 'def' text +} + +#------------------------------------------------------------------------- +# Check that inserting UNINDEXED columns between indexed columns of a +# locale=1 table does not cause a problem. +# +reset_db +sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create +fts5_aux_test_functions db + +do_execsql_test 15.1 { + CREATE VIRTUAL TABLE ft USING fts5(a, b UNINDEXED, c, locale=1, tokenize=tcl); +} + +do_execsql_test 15.2 { + INSERT INTO ft VALUES('one', 'two', 'three'); + INSERT INTO ft VALUES('one', 'two', fts5_locale('loc', 'three')); +} + +do_execsql_test 15.3 { + SELECT c2, l2 FROM ft_content +} {three {} three loc} + +do_execsql_test 15.4 { + SELECT c, fts5_columnlocale(ft, 2) FROM ft +} {three {} three loc} + finish_test diff --git a/manifest b/manifest index cfcc58cbf1..b71c9002b3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\stest\sin\sfts3corrupt4.test\sthat\swould\sfail\sif\sSQLITE_ENABLE_FTS5\swas\snot\sdefined. -D 2024-09-10T16:40:08.960 +C Alternative\simplementation\sof\sfts5\slocale=1\sfeature\sthat\sallows\sblobs\sto\sbe\sstored\sin\sindexed\scolumns\sof\sfts5\slocale=1\stables. +D 2024-09-10T20:32:36.789 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -93,15 +93,15 @@ F ext/fts3/unicode/mkunicode.tcl 63db9624ccf70d4887836c320eda93ab552f21008f3be7e F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl 009cf59c77afa86d137b0cca3e3b1a5efbe2264faa2df233f9a7aa8563926d15 F ext/fts5/fts5.h efaaac0df3d3bc740383044c144b582f47921aafa21d7b10eb98f42c24c740b0 -F ext/fts5/fts5Int.h 7ab1d838adc4f22fdad5e1ba19182d6899ebded1d3ecadbe995322b0f0de7b9f +F ext/fts5/fts5Int.h 93aba03ca417f403b07b2ab6f50aa0e0c1b8b031917a9026b81520e7047a168e F ext/fts5/fts5_aux.c 65a0468dd177d6093aa9ae1622e6d86b0136b8d267c62c0ad6493ad1e9a3d759 F ext/fts5/fts5_buffer.c 0eec58bff585f1a44ea9147eae5da2447292080ea435957f7488c70673cb6f09 -F ext/fts5/fts5_config.c 353d2a0d12678cae6ab5b9ce54aed8dac0825667b69248b5a4ed81cbefc109ea +F ext/fts5/fts5_config.c da21548ddbc1a457cb42545f527065221ede8ada6a734891b8c34317a7a9506b F ext/fts5/fts5_expr.c 9a56f53700d1860f0ee2f373c2b9074eaf2a7aa0637d0e27a6476de26a3fee33 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 F ext/fts5/fts5_index.c 571483823193f09439356741669aa8c81da838ae6f5e1bfa7517f7ee2fb3addd -F ext/fts5/fts5_main.c 9124eba418eb0c608c1454c4ad08a5f1ac21a4748c36a44828a0a7a1b32ef896 -F ext/fts5/fts5_storage.c 42cde97eb7d8506a8d2c7ea80b292fc3017b1f5469e1acb0035a69c345e6cf71 +F ext/fts5/fts5_main.c 4503498d3453e29a3cd89dacaba029011e89cb8c481a6241611d106e7a369bd4 +F ext/fts5/fts5_storage.c 3332497823c3d171cf56379f2bd8c971ce15a19aadacff961106462022c92470 F ext/fts5/fts5_tcl.c 4db9258a7882c5eac0da4433042132aaf15b87dd1e1636c7a6ca203abd2c8bfe F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b @@ -132,7 +132,7 @@ F ext/fts5/test/fts5auxdata.test 372549088ff792655f73e62b9dfaf4863ce74f5e604c06c F ext/fts5/test/fts5bigid.test 2860854c2561a57594192b00c33a29f91cb85e25f3d6c03b5c2b8f62708f39dd F ext/fts5/test/fts5bigpl.test 8f09858aab866c33593560e6480b2b6975ae7ff29ca32ad7b77e2da61402f8ef F ext/fts5/test/fts5bigtok.test 541119e616c637caea925a8c028c37c2c29e94383e00aa2f9198d530724b6e36 -F ext/fts5/test/fts5blob.test caa33369e93e99ff494cd1103506ae34c5afbc0bcc369ed5e58e135144e33689 +F ext/fts5/test/fts5blob.test 9644a5f917306690e08c5f89a470a3f2489376eaa52026eeca3209d149d6af74 F ext/fts5/test/fts5cat.test bf67dd335f964482ee658287521b81e2b88697b45eb7f73933e15f198ed447cb F ext/fts5/test/fts5circref.test f880dfd0d99f6fb73b88ccacb0927d18e833672fd906cc47d6b4e529419eaa62 F ext/fts5/test/fts5colset.test 544f4998cdbfe06a3123887fc0221612e8aa8192cdaff152872f1aadb10e6897 @@ -178,7 +178,7 @@ F ext/fts5/test/fts5faultE.test 844586ce71dab4be85bb86880e87b624d089f851654cd22e F ext/fts5/test/fts5faultF.test 4abef99f86e99d9f0c6460dd68c586a766b6b9f1f660ada55bf2e8266bd1bbc1 F ext/fts5/test/fts5faultG.test 0544411ffcb3e19b42866f757a8a5e0fb8fef3a62c06f61d14deebc571bb7ea9 F ext/fts5/test/fts5faultH.test 2b2b5b8cb1b3fd7679f488c06e22af44107fbc6137eaf45b3e771dc7b149312d -F ext/fts5/test/fts5faultI.test a1496d6d72b864102f95f9a616a0f583320310a6fb7a463a37c88dfb40d68ae5 +F ext/fts5/test/fts5faultI.test 0706b307b208638554c9e65b4091e1c0dd8c92941535089a301df454ff2c56f4 F ext/fts5/test/fts5first.test bfd685b96905bf541d99d8644e0a7219d1d833455a08ab64e344071a613b6ba9 F ext/fts5/test/fts5full.test 97d263c1072f4a560929cca31e70f65d2ae232610e17e6affcf7e979df59547b F ext/fts5/test/fts5fuzz1.test 238d8c45f3b81342aa384de3e581ff2fa330bf922a7b69e484bbc06051a1080e @@ -189,7 +189,7 @@ F ext/fts5/test/fts5interrupt.test 20d04204d3e341b104c0c24a41596b6393a3a81eba104 F ext/fts5/test/fts5lastrowid.test f36298a1fb9f988bde060a274a7ce638faa9c38a31400f8d2d27ea9373e0c4a1 F ext/fts5/test/fts5leftjoin.test c0b4cafb9661379e576dc4405c0891d8fcc2782680740513c4d1fc114b43d4ad F ext/fts5/test/fts5limits.test 8ab67cf5d311c124b6ceb0062d0297767176df4572d955fce79fa43004dff01c -F ext/fts5/test/fts5locale.test 58ce0515c4f49cbb9905e3711168050d58fc184daf885c9ef7483e20aab63e5a +F ext/fts5/test/fts5locale.test 83ba7ee12628b540d3098f39c39c1de0c0440eddff8f7512c8c698d0c4a3ae3c F ext/fts5/test/fts5matchinfo.test 877520582feb86bbfd95ab780099bcba4526f18ac75ee34979144cf86ba3a5a3 F ext/fts5/test/fts5merge.test 2654df0bcdb2d117c2d38b6aeb0168061be01c643f9e9194b36c43a2970e8082 F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2 @@ -2212,8 +2212,9 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d8103684f660ff9b3186d0f89afb113ca580bd16f0bf413ed8a9434236b54426 -R aef238013a1ce5e62ad259c3eba70261 +P 437849c80851da842b5c4fd37d5c147f821abc541e9b4d6f9000c12983548844 7d87a27a01311153ddee122cedecedc3bcc331618dc2ab1da397a3b257dc21cf +R cbbfe15fbc887a71ab881832205b2d5a +T +closed 7d87a27a01311153ddee122cedecedc3bcc331618dc2ab1da397a3b257dc21cf U dan -Z ba098087a8d44fd7d814378cea692b59 +Z f39f2a1cdc64ffa8dc528a76af023d0e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a7511290dc..e0567f9ee8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -437849c80851da842b5c4fd37d5c147f821abc541e9b4d6f9000c12983548844 +198305de92ebba7045d8ec7d2de98511f3b00924f808a3811f061dca47b01ec7