--- /dev/null
- }
- if( rc==SQLITE_OK ){
- sqlite3_bind_int64(pDel, 1, iDel);
- sqlite3_step(pDel);
- rc = sqlite3_reset(pDel);
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+*/
+
+#ifdef SQLITE_ENABLE_FTS5
+
+
+#include "fts5Int.h"
+
+struct Fts5Storage {
+ Fts5Config *pConfig;
+ Fts5Index *pIndex;
+ int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */
+ i64 nTotalRow; /* Total number of rows in FTS table */
+ i64 *aTotalSize; /* Total sizes of each column */
+ sqlite3_stmt *aStmt[11];
+};
+
+
+#if FTS5_STMT_SCAN_ASC!=0
+# error "FTS5_STMT_SCAN_ASC mismatch"
+#endif
+#if FTS5_STMT_SCAN_DESC!=1
+# error "FTS5_STMT_SCAN_DESC mismatch"
+#endif
+#if FTS5_STMT_LOOKUP!=2
+# error "FTS5_STMT_LOOKUP mismatch"
+#endif
+
+#define FTS5_STMT_INSERT_CONTENT 3
+#define FTS5_STMT_REPLACE_CONTENT 4
+#define FTS5_STMT_DELETE_CONTENT 5
+#define FTS5_STMT_REPLACE_DOCSIZE 6
+#define FTS5_STMT_DELETE_DOCSIZE 7
+#define FTS5_STMT_LOOKUP_DOCSIZE 8
+#define FTS5_STMT_REPLACE_CONFIG 9
+#define FTS5_STMT_SCAN 10
+
+/*
+** Prepare the two insert statements - Fts5Storage.pInsertContent and
+** Fts5Storage.pInsertDocsize - if they have not already been prepared.
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageGetStmt(
+ Fts5Storage *p, /* Storage handle */
+ int eStmt, /* FTS5_STMT_XXX constant */
+ sqlite3_stmt **ppStmt, /* OUT: Prepared statement handle */
+ char **pzErrMsg /* OUT: Error message (if any) */
+){
+ int rc = SQLITE_OK;
+
+ /* If there is no %_docsize table, there should be no requests for
+ ** statements to operate on it. */
+ assert( p->pConfig->bColumnsize || (
+ eStmt!=FTS5_STMT_REPLACE_DOCSIZE
+ && eStmt!=FTS5_STMT_DELETE_DOCSIZE
+ && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE
+ ));
+
+ assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
+ if( p->aStmt[eStmt]==0 ){
+ const char *azStmt[] = {
+ "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
+ "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
+ "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */
+
+ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
+ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
+ "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
+ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
+ "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
+
+ "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
+
+ "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
+ "SELECT %s FROM %s AS T", /* SCAN */
+ };
+ Fts5Config *pC = p->pConfig;
+ char *zSql = 0;
+
+ switch( eStmt ){
+ case FTS5_STMT_SCAN:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ pC->zContentExprlist, pC->zContent
+ );
+ break;
+
+ case FTS5_STMT_SCAN_ASC:
+ case FTS5_STMT_SCAN_DESC:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist,
+ pC->zContent, pC->zContentRowid, pC->zContentRowid,
+ pC->zContentRowid
+ );
+ break;
+
+ case FTS5_STMT_LOOKUP:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ pC->zContentExprlist, pC->zContent, pC->zContentRowid
+ );
+ break;
+
+ case FTS5_STMT_INSERT_CONTENT:
+ case FTS5_STMT_REPLACE_CONTENT: {
+ int nCol = pC->nCol + 1;
+ char *zBind;
+ int i;
+
+ zBind = sqlite3_malloc(1 + nCol*2);
+ if( zBind ){
+ for(i=0; i<nCol; i++){
+ zBind[i*2] = '?';
+ zBind[i*2 + 1] = ',';
+ }
+ zBind[i*2-1] = '\0';
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
+ sqlite3_free(zBind);
+ }
+ break;
+ }
+
+ default:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
+ break;
+ }
+
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK && pzErrMsg ){
+ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
+ }
+ }
+ }
+
+ *ppStmt = p->aStmt[eStmt];
+ return rc;
+}
+
+
+static int fts5ExecPrintf(
+ sqlite3 *db,
+ char **pzErr,
+ const char *zFormat,
+ ...
+){
+ int rc;
+ va_list ap; /* ... printf arguments */
+ va_start(ap, zFormat);
+ char *zSql = sqlite3_vmprintf(zFormat, ap);
+
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
+ sqlite3_free(zSql);
+ }
+
+ va_end(ap);
+ return rc;
+}
+
+/*
+** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
+** code otherwise.
+*/
+int sqlite3Fts5DropAll(Fts5Config *pConfig){
+ int rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_data';"
+ "DROP TABLE IF EXISTS %Q.'%q_config';",
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName
+ );
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_docsize';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_content';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+ return rc;
+}
+
+static void fts5StorageRenameOne(
+ Fts5Config *pConfig, /* Current FTS5 configuration */
+ int *pRc, /* IN/OUT: Error code */
+ const char *zTail, /* Tail of table name e.g. "data", "config" */
+ const char *zName /* New name of FTS5 table */
+){
+ if( *pRc==SQLITE_OK ){
+ *pRc = fts5ExecPrintf(pConfig->db, 0,
+ "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
+ pConfig->zDb, pConfig->zName, zTail, zName, zTail
+ );
+ }
+}
+
+int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){
+ Fts5Config *pConfig = pStorage->pConfig;
+ int rc = sqlite3Fts5StorageSync(pStorage, 1);
+
+ fts5StorageRenameOne(pConfig, &rc, "data", zName);
+ fts5StorageRenameOne(pConfig, &rc, "config", zName);
+ if( pConfig->bColumnsize ){
+ fts5StorageRenameOne(pConfig, &rc, "docsize", zName);
+ }
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ fts5StorageRenameOne(pConfig, &rc, "content", zName);
+ }
+ return rc;
+}
+
+/*
+** Create the shadow table named zPost, with definition zDefn. Return
+** SQLITE_OK if successful, or an SQLite error code otherwise.
+*/
+int sqlite3Fts5CreateTable(
+ Fts5Config *pConfig, /* FTS5 configuration */
+ const char *zPost, /* Shadow table to create (e.g. "content") */
+ const char *zDefn, /* Columns etc. for shadow table */
+ int bWithout, /* True for without rowid */
+ char **pzErr /* OUT: Error message */
+){
+ int rc;
+ char *zErr = 0;
+
+ rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
+ pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":""
+ );
+ if( zErr ){
+ *pzErr = sqlite3_mprintf(
+ "fts5: error creating shadow table %q_%s: %s",
+ pConfig->zName, zPost, zErr
+ );
+ sqlite3_free(zErr);
+ }
+
+ return rc;
+}
+
+/*
+** Open a new Fts5Index handle. If the bCreate argument is true, create
+** and initialize the underlying tables
+**
+** If successful, set *pp to point to the new object and return SQLITE_OK.
+** Otherwise, set *pp to NULL and return an SQLite error code.
+*/
+int sqlite3Fts5StorageOpen(
+ Fts5Config *pConfig,
+ Fts5Index *pIndex,
+ int bCreate,
+ Fts5Storage **pp,
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK;
+ Fts5Storage *p; /* New object */
+ int nByte; /* Bytes of space to allocate */
+
+ nByte = sizeof(Fts5Storage) /* Fts5Storage object */
+ + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */
+ *pp = p = (Fts5Storage*)sqlite3_malloc(nByte);
+ if( !p ) return SQLITE_NOMEM;
+
+ memset(p, 0, nByte);
+ p->aTotalSize = (i64*)&p[1];
+ p->pConfig = pConfig;
+ p->pIndex = pIndex;
+
+ if( bCreate ){
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ int nDefn = 32 + pConfig->nCol*10;
+ char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
+ if( zDefn==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ int iOff;
+ sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
+ iOff = strlen(zDefn);
+ for(i=0; i<pConfig->nCol; i++){
+ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
+ iOff += strlen(&zDefn[iOff]);
+ }
+ rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
+ }
+ sqlite3_free(zDefn);
+ }
+
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "config", "k PRIMARY KEY, v", 1, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
+ }
+ }
+
+ if( rc ){
+ sqlite3Fts5StorageClose(p);
+ *pp = 0;
+ }
+ return rc;
+}
+
+/*
+** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
+*/
+int sqlite3Fts5StorageClose(Fts5Storage *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ int i;
+
+ /* Finalize all SQL statements */
+ for(i=0; i<ArraySize(p->aStmt); i++){
+ sqlite3_finalize(p->aStmt[i]);
+ }
+
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+typedef struct Fts5InsertCtx Fts5InsertCtx;
+struct Fts5InsertCtx {
+ Fts5Storage *pStorage;
+ int iCol;
+ int szCol; /* Size of column value in tokens */
+};
+
+/*
+** Tokenization callback used when inserting tokens into the FTS index.
+*/
+static int fts5StorageInsertCallback(
+ void *pContext, /* Pointer to Fts5InsertCtx object */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Start offset of token */
+ int iEnd /* End offset of token */
+){
+ Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext;
+ Fts5Index *pIdx = pCtx->pStorage->pIndex;
+ int iPos = pCtx->szCol++;
+ return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, iPos, pToken, nToken);
+}
+
+/*
+** If a row with rowid iDel is present in the %_content table, add the
+** delete-markers to the FTS index necessary to delete it. Do not actually
+** remove the %_content row at this time though.
+*/
+static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pSeek; /* SELECT to read row iDel from %_data */
+ int rc; /* Return code */
+
+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ sqlite3_bind_int64(pSeek, 1, iDel);
+ if( sqlite3_step(pSeek)==SQLITE_ROW ){
+ int iCol;
+ Fts5InsertCtx ctx;
+ ctx.pStorage = p;
+ ctx.iCol = -1;
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
+ for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
+ if( pConfig->abUnindexed[iCol-1] ) continue;
+ ctx.szCol = 0;
+ rc = sqlite3Fts5Tokenize(pConfig,
+ (const char*)sqlite3_column_text(pSeek, iCol),
+ sqlite3_column_bytes(pSeek, iCol),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
+ }
+ p->nTotalRow--;
+ }
+ rc2 = sqlite3_reset(pSeek);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ return rc;
+}
+
+
+/*
+** Insert a record into the %_docsize table. Specifically, do:
+**
+** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
+**
+** If there is no %_docsize table (as happens if the columnsize=0 option
+** is specified when the FTS5 table is created), this function is a no-op.
+*/
+static int fts5StorageInsertDocsize(
+ Fts5Storage *p, /* Storage module to write to */
+ i64 iRowid, /* id value */
+ Fts5Buffer *pBuf /* sz value */
+){
+ int rc = SQLITE_OK;
+ if( p->pConfig->bColumnsize ){
+ sqlite3_stmt *pReplace = 0;
+ rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pReplace, 1, iRowid);
+ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ }
+ return rc;
+}
+
+/*
+** Load the contents of the "averages" record from disk into the
+** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
+** argument bCache is true, set the p->bTotalsValid flag to indicate
+** that the contents of aTotalSize[] and nTotalRow are valid until
+** further notice.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
+ int rc = SQLITE_OK;
+ if( p->bTotalsValid==0 ){
+ int nCol = p->pConfig->nCol;
+ Fts5Buffer buf;
+ memset(&buf, 0, sizeof(buf));
+
+ memset(p->aTotalSize, 0, sizeof(i64) * nCol);
+ p->nTotalRow = 0;
+ rc = sqlite3Fts5IndexGetAverages(p->pIndex, &buf);
+ if( rc==SQLITE_OK && buf.n ){
+ int i = 0;
+ int iCol;
+ i += fts5GetVarint(&buf.p[i], (u64*)&p->nTotalRow);
+ for(iCol=0; i<buf.n && iCol<nCol; iCol++){
+ i += fts5GetVarint(&buf.p[i], (u64*)&p->aTotalSize[iCol]);
+ }
+ }
+ sqlite3_free(buf.p);
+ p->bTotalsValid = bCache;
+ }
+ return rc;
+}
+
+/*
+** Store the current contents of the p->nTotalRow and p->aTotalSize[]
+** variables in the "averages" record on disk.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageSaveTotals(Fts5Storage *p){
+ int nCol = p->pConfig->nCol;
+ int i;
+ Fts5Buffer buf;
+ int rc = SQLITE_OK;
+ memset(&buf, 0, sizeof(buf));
+
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow);
+ for(i=0; i<nCol; i++){
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n);
+ }
+ sqlite3_free(buf.p);
+
+ return rc;
+}
+
+/*
+** Remove a row from the FTS table.
+*/
+int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+ sqlite3_stmt *pDel;
+
+ rc = fts5StorageLoadTotals(p, 1);
+
+ /* Delete the index records */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageDeleteFromIndex(p, iDel);
+ }
+
+ /* Delete the %_docsize record */
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
++ if( rc==SQLITE_OK ){
++ sqlite3_bind_int64(pDel, 1, iDel);
++ sqlite3_step(pDel);
++ rc = sqlite3_reset(pDel);
++ }
+ }
+
+ /* Delete the %_content record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iDel);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+int sqlite3Fts5StorageSpecialDelete(
+ Fts5Storage *p,
+ i64 iDel,
+ sqlite3_value **apVal
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+ sqlite3_stmt *pDel;
+
+ assert( pConfig->eContent!=FTS5_CONTENT_NORMAL );
+ rc = fts5StorageLoadTotals(p, 1);
+
+ /* Delete the index records */
+ if( rc==SQLITE_OK ){
+ int iCol;
+ Fts5InsertCtx ctx;
+ ctx.pStorage = p;
+ ctx.iCol = -1;
+
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
+ for(iCol=0; rc==SQLITE_OK && iCol<pConfig->nCol; iCol++){
+ if( pConfig->abUnindexed[iCol] ) continue;
+ ctx.szCol = 0;
+ rc = sqlite3Fts5Tokenize(pConfig,
+ (const char*)sqlite3_value_text(apVal[iCol]),
+ sqlite3_value_bytes(apVal[iCol]),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ p->aTotalSize[iCol] -= (i64)ctx.szCol;
+ }
+ p->nTotalRow--;
+ }
+
+ /* Delete the %_docsize record */
+ if( pConfig->bColumnsize ){
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iDel);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+ }
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+/*
+** Delete all entries in the FTS5 index.
+*/
+int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+
+ /* Delete the contents of the %_data and %_docsize tables. */
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DELETE FROM %Q.'%q_data';",
+ pConfig->zDb, pConfig->zName
+ );
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DELETE FROM %Q.'%q_docsize';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+
+ /* Reinitialize the %_data table. This call creates the initial structure
+ ** and averages records. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexReinit(p->pIndex);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
+ }
+ return rc;
+}
+
+int sqlite3Fts5StorageRebuild(Fts5Storage *p){
+ Fts5Buffer buf = {0,0,0};
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pScan = 0;
+ Fts5InsertCtx ctx;
+ int rc;
+
+ memset(&ctx, 0, sizeof(Fts5InsertCtx));
+ ctx.pStorage = p;
+ rc = sqlite3Fts5StorageDeleteAll(p);
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageLoadTotals(p, 1);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
+ i64 iRowid = sqlite3_column_int64(pScan, 0);
+
+ sqlite3Fts5BufferZero(&buf);
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iRowid);
+ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
+ ctx.szCol = 0;
+ if( pConfig->abUnindexed[ctx.iCol]==0 ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ (const char*)sqlite3_column_text(pScan, ctx.iCol+1),
+ sqlite3_column_bytes(pScan, ctx.iCol+1),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ }
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
+ p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
+ }
+ p->nTotalRow++;
+
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageInsertDocsize(p, iRowid, &buf);
+ }
+ }
+ sqlite3_free(buf.p);
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+ return rc;
+}
+
+int sqlite3Fts5StorageOptimize(Fts5Storage *p){
+ return sqlite3Fts5IndexOptimize(p->pIndex);
+}
+
+int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
+ return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
+}
+
+/*
+** Allocate a new rowid. This is used for "external content" tables when
+** a NULL value is inserted into the rowid column. The new rowid is allocated
+** by inserting a dummy row into the %_docsize table. The dummy will be
+** overwritten later.
+**
+** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
+** this case the user is required to provide a rowid explicitly.
+*/
+static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
+ int rc = SQLITE_MISMATCH;
+ if( p->pConfig->bColumnsize ){
+ sqlite3_stmt *pReplace = 0;
+ rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_null(pReplace, 1);
+ sqlite3_bind_null(pReplace, 2);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ if( rc==SQLITE_OK ){
+ *piRowid = sqlite3_last_insert_rowid(p->pConfig->db);
+ }
+ }
+ return rc;
+}
+
+/*
+** Insert a new row into the FTS table.
+*/
+int sqlite3Fts5StorageInsert(
+ Fts5Storage *p, /* Storage module to write to */
+ sqlite3_value **apVal, /* Array of values passed to xUpdate() */
+ int eConflict, /* on conflict clause */
+ i64 *piRowid /* OUT: rowid of new record */
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc = SQLITE_OK; /* Return code */
+ sqlite3_stmt *pInsert; /* Statement used to write %_content table */
+ int eStmt = 0; /* Type of statement used on %_content */
+ int i; /* Counter variable */
+ Fts5InsertCtx ctx; /* Tokenization callback context object */
+ Fts5Buffer buf; /* Buffer used to build up %_docsize blob */
+
+ memset(&buf, 0, sizeof(Fts5Buffer));
+ rc = fts5StorageLoadTotals(p, 1);
+
+ /* Insert the new row into the %_content table. */
+ if( rc==SQLITE_OK ){
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
+ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
+ *piRowid = sqlite3_value_int64(apVal[1]);
+ }else{
+ rc = fts5StorageNewRowid(p, piRowid);
+ }
+ }else{
+ if( eConflict==SQLITE_REPLACE ){
+ eStmt = FTS5_STMT_REPLACE_CONTENT;
+ rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1]));
+ }else{
+ eStmt = FTS5_STMT_INSERT_CONTENT;
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, eStmt, &pInsert, 0);
+ }
+ for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
+ rc = sqlite3_bind_value(pInsert, i, apVal[i]);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pInsert);
+ rc = sqlite3_reset(pInsert);
+ }
+ *piRowid = sqlite3_last_insert_rowid(pConfig->db);
+ }
+ }
+
+ /* Add new entries to the FTS index */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, *piRowid);
+ ctx.pStorage = p;
+ }
+ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
+ ctx.szCol = 0;
+ if( pConfig->abUnindexed[ctx.iCol]==0 ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ (const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
+ sqlite3_value_bytes(apVal[ctx.iCol+2]),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ }
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
+ p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
+ }
+ p->nTotalRow++;
+
+ /* Write the %_docsize record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageInsertDocsize(p, *piRowid, &buf);
+ }
+ sqlite3_free(buf.p);
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
+ Fts5Config *pConfig = p->pConfig;
+ char *zSql;
+ int rc;
+
+ zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
+ pConfig->zDb, pConfig->zName, zSuffix
+ );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pCnt = 0;
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pCnt, 0);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pCnt) ){
+ *pnRow = sqlite3_column_int64(pCnt, 0);
+ }
+ rc = sqlite3_finalize(pCnt);
+ }
+ }
+
+ sqlite3_free(zSql);
+ return rc;
+}
+
+/*
+** Context object used by sqlite3Fts5StorageIntegrity().
+*/
+typedef struct Fts5IntegrityCtx Fts5IntegrityCtx;
+struct Fts5IntegrityCtx {
+ i64 iRowid;
+ int iCol;
+ int szCol;
+ u64 cksum;
+ Fts5Config *pConfig;
+};
+
+/*
+** Tokenization callback used by integrity check.
+*/
+static int fts5StorageIntegrityCallback(
+ void *pContext, /* Pointer to Fts5InsertCtx object */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Start offset of token */
+ int iEnd /* End offset of token */
+){
+ Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
+ int iPos = pCtx->szCol++;
+ pCtx->cksum ^= sqlite3Fts5IndexCksum(
+ pCtx->pConfig, pCtx->iRowid, pCtx->iCol, iPos, pToken, nToken
+ );
+ return SQLITE_OK;
+}
+
+/*
+** Check that the contents of the FTS index match that of the %_content
+** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
+** some other SQLite error code if an error occurs while attempting to
+** determine this.
+*/
+int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
+ Fts5Config *pConfig = p->pConfig;
+ int rc; /* Return code */
+ int *aColSize; /* Array of size pConfig->nCol */
+ i64 *aTotalSize; /* Array of size pConfig->nCol */
+ Fts5IntegrityCtx ctx;
+ sqlite3_stmt *pScan;
+
+ memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
+ ctx.pConfig = p->pConfig;
+ aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64)));
+ if( !aTotalSize ) return SQLITE_NOMEM;
+ aColSize = (int*)&aTotalSize[pConfig->nCol];
+ memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
+
+ /* Generate the expected index checksum based on the contents of the
+ ** %_content table. This block stores the checksum in ctx.cksum. */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ while( SQLITE_ROW==sqlite3_step(pScan) ){
+ int i;
+ ctx.iRowid = sqlite3_column_int64(pScan, 0);
+ ctx.szCol = 0;
+ rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i] ) continue;
+ ctx.iCol = i;
+ ctx.szCol = 0;
+ rc = sqlite3Fts5Tokenize(
+ pConfig,
+ (const char*)sqlite3_column_text(pScan, i+1),
+ sqlite3_column_bytes(pScan, i+1),
+ (void*)&ctx,
+ fts5StorageIntegrityCallback
+ );
+ if( ctx.szCol!=aColSize[i] ) rc = FTS5_CORRUPT;
+ aTotalSize[i] += ctx.szCol;
+ }
+ if( rc!=SQLITE_OK ) break;
+ }
+ rc2 = sqlite3_reset(pScan);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ /* Test that the "totals" (sometimes called "averages") record looks Ok */
+ if( rc==SQLITE_OK ){
+ int i;
+ rc = fts5StorageLoadTotals(p, 0);
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
+ }
+ }
+
+ /* Check that the %_docsize and %_content tables contain the expected
+ ** number of rows. */
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ i64 nRow;
+ rc = fts5StorageCount(p, "content", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+ if( rc==SQLITE_OK ){
+ i64 nRow;
+ rc = fts5StorageCount(p, "docsize", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+
+ /* Pass the expected checksum down to the FTS index module. It will
+ ** verify, amongst other things, that it matches the checksum generated by
+ ** inspecting the index itself. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
+ }
+
+ sqlite3_free(aTotalSize);
+ return rc;
+}
+
+/*
+** Obtain an SQLite statement handle that may be used to read data from the
+** %_content table.
+*/
+int sqlite3Fts5StorageStmt(
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt **pp,
+ char **pzErrMsg
+){
+ int rc;
+ assert( eStmt==FTS5_STMT_SCAN_ASC
+ || eStmt==FTS5_STMT_SCAN_DESC
+ || eStmt==FTS5_STMT_LOOKUP
+ );
+ rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
+ if( rc==SQLITE_OK ){
+ assert( p->aStmt[eStmt]==*pp );
+ p->aStmt[eStmt] = 0;
+ }
+ return rc;
+}
+
+/*
+** Release an SQLite statement handle obtained via an earlier call to
+** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
+** must match that passed to the sqlite3Fts5StorageStmt() call.
+*/
+void sqlite3Fts5StorageStmtRelease(
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt *pStmt
+){
+ assert( eStmt==FTS5_STMT_SCAN_ASC
+ || eStmt==FTS5_STMT_SCAN_DESC
+ || eStmt==FTS5_STMT_LOOKUP
+ );
+ if( p->aStmt[eStmt]==0 ){
+ sqlite3_reset(pStmt);
+ p->aStmt[eStmt] = pStmt;
+ }else{
+ sqlite3_finalize(pStmt);
+ }
+}
+
+static int fts5StorageDecodeSizeArray(
+ int *aCol, int nCol, /* Array to populate */
+ const u8 *aBlob, int nBlob /* Record to read varints from */
+){
+ int i;
+ int iOff = 0;
+ for(i=0; i<nCol; i++){
+ if( iOff>=nBlob ) return 1;
+ iOff += fts5GetVarint32(&aBlob[iOff], aCol[i]);
+ }
+ return (iOff!=nBlob);
+}
+
+/*
+** Argument aCol points to an array of integers containing one entry for
+** each table column. This function reads the %_docsize record for the
+** specified rowid and populates aCol[] with the results.
+**
+** An SQLite error code is returned if an error occurs, or SQLITE_OK
+** otherwise.
+*/
+int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
+ int nCol = p->pConfig->nCol;
+ sqlite3_stmt *pLookup = 0;
+ int rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
+ if( rc==SQLITE_OK ){
+ int bCorrupt = 1;
+ sqlite3_bind_int64(pLookup, 1, iRowid);
+ if( SQLITE_ROW==sqlite3_step(pLookup) ){
+ const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
+ int nBlob = sqlite3_column_bytes(pLookup, 0);
+ if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){
+ bCorrupt = 0;
+ }
+ }
+ rc = sqlite3_reset(pLookup);
+ if( bCorrupt && rc==SQLITE_OK ){
+ rc = FTS5_CORRUPT;
+ }
+ }
+
+ return rc;
+}
+
+int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
+ int rc = fts5StorageLoadTotals(p, 0);
+ if( rc==SQLITE_OK ){
+ *pnToken = 0;
+ if( iCol<0 ){
+ int i;
+ for(i=0; i<p->pConfig->nCol; i++){
+ *pnToken += p->aTotalSize[i];
+ }
+ }else if( iCol<p->pConfig->nCol ){
+ *pnToken = p->aTotalSize[iCol];
+ }else{
+ rc = SQLITE_RANGE;
+ }
+ }
+ return rc;
+}
+
+int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){
+ int rc = fts5StorageLoadTotals(p, 0);
+ if( rc==SQLITE_OK ){
+ *pnRow = p->nTotalRow;
+ }
+ return rc;
+}
+
+/*
+** Flush any data currently held in-memory to disk.
+*/
+int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit){
+ if( bCommit && p->bTotalsValid ){
+ int rc = fts5StorageSaveTotals(p);
+ p->bTotalsValid = 0;
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ return sqlite3Fts5IndexSync(p->pIndex, bCommit);
+}
+
+int sqlite3Fts5StorageRollback(Fts5Storage *p){
+ p->bTotalsValid = 0;
+ return sqlite3Fts5IndexRollback(p->pIndex);
+}
+
+int sqlite3Fts5StorageConfigValue(
+ Fts5Storage *p,
+ 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);
+ 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 && pVal ){
+ int iNew = p->pConfig->iCookie + 1;
+ rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
+ if( rc==SQLITE_OK ){
+ p->pConfig->iCookie = iNew;
+ }
+ }
+ return rc;
+}
+
+
+#endif /* SQLITE_ENABLE_FTS5 */
--- /dev/null
+# 2014 June 17
+#
+# 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 is focused on OOM errors.
+#
+
+source [file join [file dirname [info script]] fts5_common.tcl]
+source $testdir/malloc_common.tcl
+set testprefix fts5fault4
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+#-------------------------------------------------------------------------
+# An OOM while dropping an fts5 table.
+#
+db func rnddoc fts5_rnddoc
+do_test 1.0 {
+ execsql { CREATE VIRTUAL TABLE xx USING fts5(x) }
+} {}
+faultsim_save_and_close
+
+do_faultsim_test 1 -faults oom-* -prep {
+ faultsim_restore_and_reopen
+ execsql { SELECT * FROM xx }
+} -body {
+ execsql { DROP TABLE xx }
+} -test {
+ faultsim_test_result [list 0 {}]
+}
+
+#-------------------------------------------------------------------------
+# An OOM within an "ORDER BY rank" query.
+#
+db func rnddoc fts5_rnddoc
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE xx USING fts5(x);
+ INSERT INTO xx VALUES ('abc ' || rnddoc(10));
+ INSERT INTO xx VALUES ('abc abc' || rnddoc(9));
+ INSERT INTO xx VALUES ('abc abc abc' || rnddoc(8));
+} {}
+faultsim_save_and_close
+
+do_faultsim_test 2 -faults oom-* -prep {
+ faultsim_restore_and_reopen
+ execsql { SELECT * FROM xx }
+} -body {
+ execsql { SELECT rowid FROM xx WHERE xx MATCH 'abc' ORDER BY rank }
+} -test {
+ faultsim_test_result [list 0 {3 2 1}]
+}
+
+#-------------------------------------------------------------------------
+# An OOM while "reseeking" an FTS cursor.
+#
+do_execsql_test 3.0 {
+ CREATE VIRTUAL TABLE jj USING fts5(j);
+ INSERT INTO jj(rowid, j) VALUES(101, 'm t w t f s s');
+ INSERT INTO jj(rowid, j) VALUES(202, 't w t f s');
+ INSERT INTO jj(rowid, j) VALUES(303, 'w t f');
+ INSERT INTO jj(rowid, j) VALUES(404, 't');
+}
+faultsim_save_and_close
+
+do_faultsim_test 3 -faults oom-* -prep {
+ faultsim_restore_and_reopen
+ execsql { SELECT * FROM jj }
+} -body {
+ set res [list]
+ db eval { SELECT rowid FROM jj WHERE jj MATCH 't' } {
+ lappend res $rowid
+ if {$rowid==303} {
+ execsql { DELETE FROM jj WHERE rowid=404 }
+ }
+ }
+ set res
+} -test {
+ faultsim_test_result [list 0 {101 202 303}]
+}
+
+#-------------------------------------------------------------------------
+# An OOM within a special "*reads" query.
+#
+reset_db
+db func rnddoc fts5_rnddoc
+do_execsql_test 4.0 {
+ CREATE VIRTUAL TABLE x1 USING fts5(x);
+ INSERT INTO x1(x1, rank) VALUES('pgsz', 32);
+
+ WITH ii(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10 )
+ INSERT INTO x1 SELECT rnddoc(5) FROM ii;
+}
+
+set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
+
+do_faultsim_test 4 -faults oom-* -body {
+ db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
+} -test {
+ faultsim_test_result {0 {0 {} 3}}
+}
+
+#-------------------------------------------------------------------------
+# An OOM within a query that uses a custom rank function.
+#
+reset_db
+do_execsql_test 5.0 {
+ PRAGMA encoding='utf16';
+ CREATE VIRTUAL TABLE x2 USING fts5(x);
+ INSERT INTO x2(rowid, x) VALUES(10, 'a b c'); -- 3
+ INSERT INTO x2(rowid, x) VALUES(20, 'a b c'); -- 6
+ INSERT INTO x2(rowid, x) VALUES(30, 'a b c'); -- 2
+ INSERT INTO x2(rowid, x) VALUES(40, 'a b c'); -- 5
+ INSERT INTO x2(rowid, x) VALUES(50, 'a b c'); -- 1
+}
+
+proc rowidmod {cmd mod} {
+ set row [$cmd xRowid]
+ expr {$row % $mod}
+}
+sqlite3_fts5_create_function db rowidmod rowidmod
+
+do_faultsim_test 5.1 -faults oom-* -body {
+ db eval {
+ SELECT rowid || '-' || rank FROM x2 WHERE x2 MATCH 'b' AND
+ rank MATCH "rowidmod('7')" ORDER BY rank
+ }
+} -test {
+ faultsim_test_result {0 {50-1 30-2 10-3 40-5 20-6}}
+}
+
+proc rowidprefix {cmd prefix} {
+ set row [$cmd xRowid]
+ set {} "${row}-${prefix}"
+}
+sqlite3_fts5_create_function db rowidprefix rowidprefix
+
+set str [string repeat abcdefghijklmnopqrstuvwxyz 10]
+do_faultsim_test 5.2 -faults oom-* -body {
+ db eval "
+ SELECT rank, x FROM x2 WHERE x2 MATCH 'b' AND
+ rank MATCH 'rowidprefix(''$::str'')'
+ LIMIT 1
+ "
+} -test {
+ faultsim_test_result "0 {10-$::str {a b c}}"
+}
+
+
+#-------------------------------------------------------------------------
+# OOM errors within auxiliary functions.
+#
+reset_db
+do_execsql_test 6.0 {
+ CREATE VIRTUAL TABLE x3 USING fts5(xxx);
+ INSERT INTO x3 VALUES('a b c d c b a');
+ INSERT INTO x3 VALUES('a a a a a a a');
+ INSERT INTO x3 VALUES('a a a a a a a');
+}
+
+do_faultsim_test 6.1 -faults oom-t* -body {
+ db eval { SELECT highlight(x3, 0, '*', '*') FROM x3 WHERE x3 MATCH 'c' }
+} -test {
+ faultsim_test_result {0 {{a b *c* d *c* b a}}}
+}
+
+proc firstinst {cmd} {
+ foreach {p c o} [$cmd xInst 0] {}
+ expr $c*100 + $o
+}
+sqlite3_fts5_create_function db firstinst firstinst
+
+do_faultsim_test 6.2 -faults oom-t* -body {
+ db eval { SELECT firstinst(x3) FROM x3 WHERE x3 MATCH 'c' }
+} -test {
+ faultsim_test_result {0 2} {1 SQLITE_NOMEM}
+}
+
+proc previc {cmd} {
+ set res [$cmd xGetAuxdataInt 0]
+ $cmd xSetAuxdataInt [$cmd xInstCount]
+ return $res
+}
+sqlite3_fts5_create_function db previc previc
+
+do_faultsim_test 6.2 -faults oom-t* -body {
+ db eval { SELECT previc(x3) FROM x3 WHERE x3 MATCH 'a' }
+} -test {
+ faultsim_test_result {0 {0 2 7}} {1 SQLITE_NOMEM}
+}
+
+#-------------------------------------------------------------------------
+# OOM error when querying for a phrase with many tokens.
+#
+reset_db
+do_execsql_test 7.0 {
+ CREATE VIRTUAL TABLE tt USING fts5(x, y);
+ INSERT INTO tt VALUES('f b g b c b', 'f a d c c b'); -- 1
+ INSERT INTO tt VALUES('d a e f e d', 'f b b d e e'); -- 2
+ INSERT INTO tt VALUES('f b g a d c', 'e f c f a d'); -- 3
+ INSERT INTO tt VALUES('f f c d g f', 'f a e b g b'); -- 4
+ INSERT INTO tt VALUES('a g b d a g', 'e g a e a c'); -- 5
+ INSERT INTO tt VALUES('c d b d e f', 'f g e g e e'); -- 6
+ INSERT INTO tt VALUES('e g f f b c', 'f c e f g f'); -- 7
+ INSERT INTO tt VALUES('e g c f c e', 'f e e a f g'); -- 8
+ INSERT INTO tt VALUES('e a e b e e', 'd c c f f f'); -- 9
+ INSERT INTO tt VALUES('f a g g c c', 'e g d g c e'); -- 10
+ INSERT INTO tt VALUES('c d b a e f', 'f g e h e e'); -- 11
+
+ CREATE VIRTUAL TABLE tt2 USING fts5(o);
+ INSERT INTO tt2(rowid, o) SELECT rowid, x||' '||y FROM tt;
+ INSERT INTO tt2(rowid, o) VALUES(12, 'a b c d e f g h i j k l');
+}
+
+do_faultsim_test 7.2 -faults oom-* -body {
+ db eval { SELECT rowid FROM tt WHERE tt MATCH 'f+g+e+g+e+e' }
+} -test {
+ faultsim_test_result {0 6} {1 SQLITE_NOMEM}
+}
+
+do_faultsim_test 7.3 -faults oom-* -body {
+ db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d e f)' }
+} -test {
+ faultsim_test_result {0 11} {1 SQLITE_NOMEM}
+}
+
+do_faultsim_test 7.4 -faults oom-t* -body {
+ db eval { SELECT rowid FROM tt2 WHERE tt2 MATCH '"g c f c e f e e a f"' }
+} -test {
+ faultsim_test_result {0 8} {1 SQLITE_NOMEM}
+}
+
+do_faultsim_test 7.5 -faults oom-* -body {
+ db eval {SELECT rowid FROM tt2 WHERE tt2 MATCH 'NEAR(a b c d e f g h i j k)'}
+} -test {
+ faultsim_test_result {0 12} {1 SQLITE_NOMEM}
+}
+
+do_faultsim_test 7.6 -faults oom-* -body {
+ db eval {SELECT rowid FROM tt WHERE tt MATCH 'y: "c c"'}
+} -test {
+ faultsim_test_result {0 {1 9}} {1 SQLITE_NOMEM}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 8.0 {
+ CREATE VIRTUAL TABLE tt USING fts5(x);
+ INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
+ BEGIN;
+ INSERT INTO tt(rowid, x) VALUES(1, 'a b c d x x');
+ WITH ii(i) AS (SELECT 2 UNION ALL SELECT i+1 FROM ii WHERE i<99)
+ INSERT INTO tt(rowid, x) SELECT i, 'a b c x x d' FROM ii;
+ INSERT INTO tt(rowid, x) VALUES(100, 'a b c d x x');
+ COMMIT;
+}
+
+do_faultsim_test 8.1 -faults oom-t* -body {
+ db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d, 2)' }
+} -test {
+ faultsim_test_result {0 {1 100}} {1 SQLITE_NOMEM}
+}
+
+do_faultsim_test 8.2 -faults oom-t* -body {
+ db eval { SELECT count(*) FROM tt WHERE tt MATCH 'a OR d' }
+} -test {
+ faultsim_test_result {0 100} {1 SQLITE_NOMEM}
+}
+
+
+#-------------------------------------------------------------------------
+# Fault in NOT query.
+#
+reset_db
+do_execsql_test 9.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 9.1 -faults oom-* -body {
+ db eval { SELECT rowid FROM tt WHERE tt MATCH 'a NOT x' }
+} -test {
+ faultsim_test_result {0 {50 100 150 200}} {1 SQLITE_NOMEM}
+}
+
+#-------------------------------------------------------------------------
+# OOM in fts5_expr() SQL function.
+#
+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 {AND [ns -col 0 -- {a b c}] [ns -- {b}] [ns -near 10 -- {a} {b}]}
+ faultsim_test_result [list 0 $res]
+}
+
+do_faultsim_test 10.3 -faults oom-t* -body {
+ db one { SELECT fts5_expr('x:a', 'x') }
+} -test {
+ faultsim_test_result {0 {x : "a"}}
+}
+
+#-------------------------------------------------------------------------
+# OOM while configuring 'rank' option.
+#
+reset_db
+do_execsql_test 11.0 {
+ CREATE VIRTUAL TABLE ft USING fts5(x);
+}
+do_faultsim_test 11.1 -faults oom-t* -body {
+ db eval { INSERT INTO ft(ft, rank) VALUES('rank', 'bm25(10.0, 5.0)') }
+} -test {
+ faultsim_test_result {0 {}} {1 {disk I/O error}}
+}
+
+#-------------------------------------------------------------------------
+# OOM while creating an fts5vocab table.
+#
+reset_db
+do_execsql_test 12.0 {
+ CREATE VIRTUAL TABLE ft USING fts5(x);
+}
+faultsim_save_and_close
+do_faultsim_test 12.1 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+ db eval { SELECT * FROM sqlite_master }
+} -body {
+ db eval { CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row') }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+#-------------------------------------------------------------------------
+# OOM while querying an fts5vocab table.
+#
+reset_db
+do_execsql_test 13.0 {
+ CREATE VIRTUAL TABLE ft USING fts5(x);
+ INSERT INTO ft VALUES('a b');
+ CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row');
+}
+faultsim_save_and_close
+do_faultsim_test 13.1 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+ db eval { SELECT * FROM vv }
+} -body {
+ db eval { SELECT * FROM vv }
+} -test {
+ faultsim_test_result {0 {a 1 1 b 1 1}}
+}
+
+#-------------------------------------------------------------------------
+# OOM in multi-column token query.
+#
+reset_db
+do_execsql_test 13.0 {
+ CREATE VIRTUAL TABLE ft USING fts5(x, y, z);
+ INSERT INTO ft(ft, rank) VALUES('pgsz', 32);
+ INSERT INTO ft VALUES(
+ 'x x x x x x x x x x x x x x x x',
+ 'y y y y y y y y y y y y y y y y',
+ 'z z z z z z z z x x x x x x x x'
+ );
+ INSERT INTO ft SELECT * FROM ft;
+ INSERT INTO ft SELECT * FROM ft;
+ INSERT INTO ft SELECT * FROM ft;
+ INSERT INTO ft SELECT * FROM ft;
+}
+faultsim_save_and_close
+do_faultsim_test 13.1 -faults oom-t* -prep {
+ faultsim_restore_and_reopen
+ db eval { SELECT * FROM ft }
+} -body {
+ db eval { SELECT rowid FROM ft WHERE ft MATCH '{x z}: x' }
+} -test {
+ faultsim_test_result {0 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}}
+}
+
++#-------------------------------------------------------------------------
++# OOM in an "ALTER TABLE RENAME TO"
++#
++reset_db
++do_execsql_test 14.0 {
++ CREATE VIRTUAL TABLE "tbl one" USING fts5(x, y, z);
++}
++faultsim_save_and_close
++do_faultsim_test 14.1 -faults oom-t* -prep {
++ faultsim_restore_and_reopen
++ db eval { SELECT * FROM "tbl one" }
++} -body {
++ db eval { ALTER TABLE "tbl one" RENAME TO "tbl two" }
++} -test {
++ faultsim_test_result {0 {}}
++}
+
+finish_test
+
- C Fix\sthe\sfts5\sxRename()\smethod.
- D 2015-06-10T10:45:34.820
-C Test\sthat\sthe\sleft\sand\sright\ssides\sof\sa\scompound\sSELECT\soperator\shave\sthe\ssame\s\snumber\sof\sexpressions\sin\sthe\sexpanded\sexpression\slist\sbefore\sbeginning\sto\sgenerate\scode.
-D 2015-06-23T12:19:55.597
++C Merge\slatest\strunk\schanges\swith\sthis\sbranch.\sAdd\stests\sfor\scolumnsize=0.
++D 2015-06-23T15:06:13.029
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
- F Makefile.in d272f8755b464f20e02dd7799bfe16794c9574c4
-F Makefile.in 1063c58075b7400d93326b0eb332b48a54f53025
++F Makefile.in 6fa5a3c6f1f558bb443429e33806e2e494823e44
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
- F Makefile.msc d37d2c2323df3acae6e24c71a478889421c17264
+ F Makefile.msc b7db9ccbbad1c495b98e5326a06cac03aa206127
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION ce0ae95abd7121c534f6917c1c8f2b70d9acd4db
F ext/fts3/tool/fts3view.c 8e53d0190a7b3443764bbd32ad47be2bd852026d
F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
-F ext/fts3/unicode/mkunicode.tcl a2567f9d6ad6779879a2e394c120ad8718557e65
+F ext/fts3/unicode/mkunicode.tcl ed0534dd51efce39878bce33944c6073d37a1e20
+F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
+F ext/fts5/extract_api_docs.tcl 55a6d648d516f35d9a1e580ac00de27154e1904a
+F ext/fts5/fts5.c 4ce5d0990c61a41155fc014b0066ae6d25a388d3
+F ext/fts5/fts5.h 81d1a92fc2b4bd477af7e4e0b38b456f3e199fba
+F ext/fts5/fts5Int.h 21eb91e02ad119e1d92ff100f366a976e12190de
+F ext/fts5/fts5_aux.c d53f00f31ad615ca4f139dd8751f9041afa00971
+F ext/fts5/fts5_buffer.c be0dc80a9406151b350be27c7ec2956722578771
+F ext/fts5/fts5_config.c 6ae691e36f90185896f4db0a819ae2394f880ca1
+F ext/fts5/fts5_expr.c 549bda1f7edcf10365fbfbc002bdea1be3c287bb
+F ext/fts5/fts5_hash.c c1cfdb2cae0fad00b06fae38a40eaf9261563ccc
+F ext/fts5/fts5_index.c 7cea402924cd3d8cd5943a7f9514c9153696571b
- F ext/fts5/fts5_storage.c 7e77d1b2da424283d1d58a77e9a98067dc96f2c7
++F ext/fts5/fts5_storage.c b2fa301fce865d582d367a5e1bb438fe60c03cb5
+F ext/fts5/fts5_tcl.c 7ea165878e4ae3598e89acd470a0ee1b5a00e33c
+F ext/fts5/fts5_tokenize.c 97251d68d7a6a9415bde1203f9382864dfc1f989
+F ext/fts5/fts5_unicode2.c da3cf712f05cd8347c8c5bc00964cc0361c88da9
+F ext/fts5/fts5_varint.c 366452037bf9a000c351374b489badc1b3541796
+F ext/fts5/fts5_vocab.c 1f8543b2c1ae4427f127a911bc8e60873fcd7bf9
+F ext/fts5/fts5parse.y 833db1101b78c0c47686ab1b84918e38c36e9452
+F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
+F ext/fts5/test/fts5_common.tcl 0b465b1127adcd1c8131f3454ab4264a6964674c
+F ext/fts5/test/fts5aa.test 0be21c89fd66b588db355a6398911fd875bdcc6c
+F ext/fts5/test/fts5ab.test 6fe3a56731d15978afbb74ae51b355fc9310f2ad
+F ext/fts5/test/fts5ac.test 0990ae7497ebaea2ab5f7fd5caedd93a71a905fc
+F ext/fts5/test/fts5ad.test 312f3c8ed9592533499c5b94d2059ae6382913a0
+F ext/fts5/test/fts5ae.test ddc558e3e3b52db0101f7541b2e3849b77052c92
+F ext/fts5/test/fts5af.test c2501ec2b61d6b179c305f5d2b8782ab3d4f832a
+F ext/fts5/test/fts5ag.test ec3e119b728196620a31507ef503c455a7a73505
+F ext/fts5/test/fts5ah.test b9e78fa986a7bd564ebadfb244de02c84d7ac3ae
+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 fc60ebeac9d8e366e71309d4c31fa72199d711d7
+F ext/fts5/test/fts5alter.test 3342e7fd58556d2a7e5299a7d9dec62e358028ed
+F ext/fts5/test/fts5auto.test caa5bcf917db11944655a2a9bd38c67c520376ca
+F ext/fts5/test/fts5aux.test e5631607bbc05ac1c38cf7d691000509aca71ef3
+F ext/fts5/test/fts5auxdata.test c69b86092bf1a157172de5f9169731af3403179b
+F ext/fts5/test/fts5bigpl.test b1cfd00561350ab04994ba7dd9d48468e5e0ec3b
- F ext/fts5/test/fts5columnsize.test c7333cf079022c1ad25d04538b8f279fad4c2f8d
++F ext/fts5/test/fts5columnsize.test bd07a42a80a6805e84afa7daf54ecd4563f752d0
+F ext/fts5/test/fts5config.test c9cc535f3b36cde1e5a32bf579f3f5962a9e82b2
+F ext/fts5/test/fts5content.test e46904decd896e38c848ad4f38fa4e80251a028b
+F ext/fts5/test/fts5corrupt.test 35bfdbbb3cdcea46ae7385f6432e9b5c574e70a1
+F ext/fts5/test/fts5corrupt2.test c231f532162de381fa83ec477b51cd8633fd9da7
+F ext/fts5/test/fts5corrupt3.test da4e2adb2308d8587c2eff31b5aa47447b8a2edb
+F ext/fts5/test/fts5dlidx.test 070531bd45685e545e3e6021deb543f730a4011b
+F ext/fts5/test/fts5doclist.test 635b80ac785627841a59c583bac702b55d49fdc5
+F ext/fts5/test/fts5ea.test 451bb37310ee6df8ef72e4354fda5621b3b51448
+F ext/fts5/test/fts5eb.test 728a1f23f263548f5c29b29dfb851b5f2dbe723e
+F ext/fts5/test/fts5fault1.test b42d3296be8a75f557cf2cbce0d8b483fc9db45b
+F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341
+F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
- F ext/fts5/test/fts5fault4.test 8671f534136aa1c80a102e8fd25b4921885e6667
++F ext/fts5/test/fts5fault4.test 762991d526ee67c2b374351a17248097ea38bee7
+F ext/fts5/test/fts5fault5.test 54da9fd4c3434a1d4f6abdcb6469299d91cf5875
+F ext/fts5/test/fts5fault6.test 234dc6355f8d3f8b5be2763f30699d770247c215
+F ext/fts5/test/fts5full.test 0924bdca5416a242103239ace79c6f5aa34bab8d
+F ext/fts5/test/fts5hash.test bdba7b591d503005d5a81871ba00a359daa1e969
+F ext/fts5/test/fts5integrity.test b45f633381a85dc000e41d68c96ab510985ca35e
+F ext/fts5/test/fts5merge.test 8077454f2975a63f35761f4b8a718b3a808b7c9c
+F ext/fts5/test/fts5near.test d2e3343e62d438f2efd96ebcd83a0d30a16ea6dc
+F ext/fts5/test/fts5optimize.test 0028c90a7817d3e576d1148fc8dff17d89054e54
+F ext/fts5/test/fts5plan.test 7f38179220c9385f88e1470aae6cba134a308b40
+F ext/fts5/test/fts5porter.test 50322599823cb8080a99f0ec0c39f7d0c12bcb5e
+F ext/fts5/test/fts5porter2.test c534385e88e685b354c2b2020acc0c4920042c8e
+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 ca9d91ccb3a4590fc561b2d7a884361bb21e8df5
+F ext/fts5/test/fts5tokenizer.test 668747fcb41de6fc7daebc478920b705164fccc1
+F ext/fts5/test/fts5unicode.test 79b3e34eb29ce4929628aa514a40cb467fdabe4d
+F ext/fts5/test/fts5unicode2.test ad38982b03dc9213445facb16e99f668a74cc4ba
+F ext/fts5/test/fts5unicode3.test 273f9086ad33935566bbc0d0c94d0d9687ef686b
+F ext/fts5/test/fts5unindexed.test f388605341a476b6ab622b4c267cd168f59a5944
+F ext/fts5/test/fts5version.test dc34a735af6625a1a7a4a916a38d122071343887
+F ext/fts5/test/fts5vocab.test 389e5fe4928eae5fddcf26bcc5a6890b0791aa75
+F ext/fts5/tool/loadfts5.tcl 7ef3e62131f0434a78e4f5c5b056b09d221710a8
+F ext/fts5/tool/showfts5.tcl 921f33b30c3189deefd2b2cc81f951638544aaf1
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
- F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb
+ F ext/icu/icu.c b2732aef0b076e4276d9b39b5a33cec7a05e1413
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/misc/amatch.c 27b9b601fb1453084e18a3432ea0240d7af8decb
F ext/misc/closure.c 636024302cde41b2bf0c542f81c40c624cfb7012
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
- F main.mk dc2b1bf580712422d5a8bb774d8d940940563fb8
-F main.mk 68f86c21505d6b66765a13c193f00a53dde6a212
++F main.mk 080c85fad8bf6532b7aeb782452b78e4440de346
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
- F src/loadext.c 29255bbe1cfb2ce9bbff2526a5ecfddcb49b9271
- F src/main.c 33562894d96cb65f4926cd5317725427cdb22770
+ F src/loadext.c e722f4b832f923744788365df5fb8515c0bc8a47
-F src/main.c c0061a4f8ba86f957534be93b7026dab324f12c2
++F src/main.c fb40edfcda10062e4d1adab7f41635002e9b7e9d
F src/malloc.c 908c780fdddd472163c2d1b1820ae4081f01ad20
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c a6431c92803b975b7322724a7b433e538d243539
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
- F src/vdbe.c 0a6a1df5c31415a0e974e74e7bd412616889453d
- F src/vdbe.h 7e538ecf47dccb307ea2d087c3ddc2dd8d70e79d
- F src/vdbeInt.h f0ccddac48583d5f762dc554a9f79e85ea8807e0
+ F src/vdbe.c c9b8985dfc5df9bd512342ea2e56af4be30cb31a
+ F src/vdbe.h 90048aea1910f9df93e6044592bd4a466dc9c5e7
+ F src/vdbeInt.h 20295e482121d13437f69985f77db211cdc8bac1
F src/vdbeapi.c 6a0d7757987018ff6b1b81bc5293219cd26bb299
- F src/vdbeaux.c 46f9bc4b32866082eb87a36b461e487a0bbdbe8e
+ F src/vdbeaux.c 4c82d6f686f72ea7d266d26d528a171b728626f7
F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
- F src/vdbemem.c 67b302dc6df64b4d6785881c5d22bd4f9b17739d
+ F src/vdbemem.c 4e947cd322bb531e3f7f6f58f0f536d182b38ef8
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
-F src/vtab.c c535e80259ebe616467181a83a4263555b97c694
+F src/vtab.c 082b35a25a26e3d36f365ca8cd73c1922532f05e
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
- F test/permutations.test a54a4c5e66dc158cb2c05579bbb4f7d1a4fdb6c1
-F test/permutations.test 6b0f339a4d5f00041555a986dde8fbe8f54c25bc
++F test/permutations.test 6a88fd9ca15b804e9c20990773262ca67494058f
F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2
F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
F tool/mkpragmatab.tcl 40c287d3f929ece67da6e9e7c49885789960accf
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e
- F tool/mksqlite3c.tcl ccee8fe53dabbeb00d55fe0a4a24005f69eccec9
-F tool/mksqlite3c.tcl d79e450048b0bbf04d53677e01c249a012981182
++F tool/mksqlite3c.tcl b601b174d783094edd926d913a8f545709e89f8a
F tool/mksqlite3h.tcl 44730d586c9031638cdd2eb443b801c0d2dbd9f8
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl 3b58b9398f91c7dbf18d49eb87cefeee9efdbce1
F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
- F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
+ F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
- P aa12f9d9b79c2f523fd6b00e47bcb66dba09ce0c
- R 92d332093f5da6f2c10bbd12aba6b7b5
-P b97f9cf73e503c7285ba3a801e1f932f222d96b2
-R 5d4925e11483f968b1575f6656e62b8a
++P 0f7fd51325875fbf0f1eaca3bbbd170ef99c4208 4df852ce26c95d5d23c83dbe9c59d2c3435acddf
++R 3ac475f744e56c1ad80818d65ed40944
U dan
- Z 4c307463292fc937d885d935d5d6ac74
-Z 6c0c66a18bcc0b19ae618b4e0a65b57c
++Z b74a37a10556c5365b257dc3f6c234f4