xref: /sqlite-3.40.0/ext/fts5/fts5_storage.c (revision 13ca72d0)
1 /*
2 ** 2014 May 31
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 ******************************************************************************
12 **
13 */
14 
15 
16 
17 #include "fts5Int.h"
18 
19 struct Fts5Storage {
20   Fts5Config *pConfig;
21   Fts5Index *pIndex;
22   int bTotalsValid;               /* True if nTotalRow/aTotalSize[] are valid */
23   i64 nTotalRow;                  /* Total number of rows in FTS table */
24   i64 *aTotalSize;                /* Total sizes of each column */
25   sqlite3_stmt *aStmt[11];
26 };
27 
28 
29 #if FTS5_STMT_SCAN_ASC!=0
30 # error "FTS5_STMT_SCAN_ASC mismatch"
31 #endif
32 #if FTS5_STMT_SCAN_DESC!=1
33 # error "FTS5_STMT_SCAN_DESC mismatch"
34 #endif
35 #if FTS5_STMT_LOOKUP!=2
36 # error "FTS5_STMT_LOOKUP mismatch"
37 #endif
38 
39 #define FTS5_STMT_INSERT_CONTENT  3
40 #define FTS5_STMT_REPLACE_CONTENT 4
41 #define FTS5_STMT_DELETE_CONTENT  5
42 #define FTS5_STMT_REPLACE_DOCSIZE  6
43 #define FTS5_STMT_DELETE_DOCSIZE  7
44 #define FTS5_STMT_LOOKUP_DOCSIZE  8
45 #define FTS5_STMT_REPLACE_CONFIG 9
46 #define FTS5_STMT_SCAN 10
47 
48 /*
49 ** Prepare the two insert statements - Fts5Storage.pInsertContent and
50 ** Fts5Storage.pInsertDocsize - if they have not already been prepared.
51 ** Return SQLITE_OK if successful, or an SQLite error code if an error
52 ** occurs.
53 */
fts5StorageGetStmt(Fts5Storage * p,int eStmt,sqlite3_stmt ** ppStmt,char ** pzErrMsg)54 static int fts5StorageGetStmt(
55   Fts5Storage *p,                 /* Storage handle */
56   int eStmt,                      /* FTS5_STMT_XXX constant */
57   sqlite3_stmt **ppStmt,          /* OUT: Prepared statement handle */
58   char **pzErrMsg                 /* OUT: Error message (if any) */
59 ){
60   int rc = SQLITE_OK;
61 
62   /* If there is no %_docsize table, there should be no requests for
63   ** statements to operate on it.  */
64   assert( p->pConfig->bColumnsize || (
65         eStmt!=FTS5_STMT_REPLACE_DOCSIZE
66      && eStmt!=FTS5_STMT_DELETE_DOCSIZE
67      && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE
68   ));
69 
70   assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
71   if( p->aStmt[eStmt]==0 ){
72     const char *azStmt[] = {
73       "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
74       "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
75       "SELECT %s FROM %s T WHERE T.%Q=?",               /* LOOKUP  */
76 
77       "INSERT INTO %Q.'%q_content' VALUES(%s)",         /* INSERT_CONTENT  */
78       "REPLACE INTO %Q.'%q_content' VALUES(%s)",        /* REPLACE_CONTENT */
79       "DELETE FROM %Q.'%q_content' WHERE id=?",         /* DELETE_CONTENT  */
80       "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)",       /* REPLACE_DOCSIZE  */
81       "DELETE FROM %Q.'%q_docsize' WHERE id=?",         /* DELETE_DOCSIZE  */
82 
83       "SELECT sz FROM %Q.'%q_docsize' WHERE id=?",      /* LOOKUP_DOCSIZE  */
84 
85       "REPLACE INTO %Q.'%q_config' VALUES(?,?)",        /* REPLACE_CONFIG */
86       "SELECT %s FROM %s AS T",                         /* SCAN */
87     };
88     Fts5Config *pC = p->pConfig;
89     char *zSql = 0;
90 
91     switch( eStmt ){
92       case FTS5_STMT_SCAN:
93         zSql = sqlite3_mprintf(azStmt[eStmt],
94             pC->zContentExprlist, pC->zContent
95         );
96         break;
97 
98       case FTS5_STMT_SCAN_ASC:
99       case FTS5_STMT_SCAN_DESC:
100         zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist,
101             pC->zContent, pC->zContentRowid, pC->zContentRowid,
102             pC->zContentRowid
103         );
104         break;
105 
106       case FTS5_STMT_LOOKUP:
107         zSql = sqlite3_mprintf(azStmt[eStmt],
108             pC->zContentExprlist, pC->zContent, pC->zContentRowid
109         );
110         break;
111 
112       case FTS5_STMT_INSERT_CONTENT:
113       case FTS5_STMT_REPLACE_CONTENT: {
114         int nCol = pC->nCol + 1;
115         char *zBind;
116         int i;
117 
118         zBind = sqlite3_malloc64(1 + nCol*2);
119         if( zBind ){
120           for(i=0; i<nCol; i++){
121             zBind[i*2] = '?';
122             zBind[i*2 + 1] = ',';
123           }
124           zBind[i*2-1] = '\0';
125           zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
126           sqlite3_free(zBind);
127         }
128         break;
129       }
130 
131       default:
132         zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
133         break;
134     }
135 
136     if( zSql==0 ){
137       rc = SQLITE_NOMEM;
138     }else{
139       int f = SQLITE_PREPARE_PERSISTENT;
140       if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB;
141       p->pConfig->bLock++;
142       rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0);
143       p->pConfig->bLock--;
144       sqlite3_free(zSql);
145       if( rc!=SQLITE_OK && pzErrMsg ){
146         *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
147       }
148     }
149   }
150 
151   *ppStmt = p->aStmt[eStmt];
152   sqlite3_reset(*ppStmt);
153   return rc;
154 }
155 
156 
fts5ExecPrintf(sqlite3 * db,char ** pzErr,const char * zFormat,...)157 static int fts5ExecPrintf(
158   sqlite3 *db,
159   char **pzErr,
160   const char *zFormat,
161   ...
162 ){
163   int rc;
164   va_list ap;                     /* ... printf arguments */
165   char *zSql;
166 
167   va_start(ap, zFormat);
168   zSql = sqlite3_vmprintf(zFormat, ap);
169 
170   if( zSql==0 ){
171     rc = SQLITE_NOMEM;
172   }else{
173     rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
174     sqlite3_free(zSql);
175   }
176 
177   va_end(ap);
178   return rc;
179 }
180 
181 /*
182 ** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
183 ** code otherwise.
184 */
sqlite3Fts5DropAll(Fts5Config * pConfig)185 int sqlite3Fts5DropAll(Fts5Config *pConfig){
186   int rc = fts5ExecPrintf(pConfig->db, 0,
187       "DROP TABLE IF EXISTS %Q.'%q_data';"
188       "DROP TABLE IF EXISTS %Q.'%q_idx';"
189       "DROP TABLE IF EXISTS %Q.'%q_config';",
190       pConfig->zDb, pConfig->zName,
191       pConfig->zDb, pConfig->zName,
192       pConfig->zDb, pConfig->zName
193   );
194   if( rc==SQLITE_OK && pConfig->bColumnsize ){
195     rc = fts5ExecPrintf(pConfig->db, 0,
196         "DROP TABLE IF EXISTS %Q.'%q_docsize';",
197         pConfig->zDb, pConfig->zName
198     );
199   }
200   if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
201     rc = fts5ExecPrintf(pConfig->db, 0,
202         "DROP TABLE IF EXISTS %Q.'%q_content';",
203         pConfig->zDb, pConfig->zName
204     );
205   }
206   return rc;
207 }
208 
fts5StorageRenameOne(Fts5Config * pConfig,int * pRc,const char * zTail,const char * zName)209 static void fts5StorageRenameOne(
210   Fts5Config *pConfig,            /* Current FTS5 configuration */
211   int *pRc,                       /* IN/OUT: Error code */
212   const char *zTail,              /* Tail of table name e.g. "data", "config" */
213   const char *zName               /* New name of FTS5 table */
214 ){
215   if( *pRc==SQLITE_OK ){
216     *pRc = fts5ExecPrintf(pConfig->db, 0,
217         "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
218         pConfig->zDb, pConfig->zName, zTail, zName, zTail
219     );
220   }
221 }
222 
sqlite3Fts5StorageRename(Fts5Storage * pStorage,const char * zName)223 int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){
224   Fts5Config *pConfig = pStorage->pConfig;
225   int rc = sqlite3Fts5StorageSync(pStorage);
226 
227   fts5StorageRenameOne(pConfig, &rc, "data", zName);
228   fts5StorageRenameOne(pConfig, &rc, "idx", zName);
229   fts5StorageRenameOne(pConfig, &rc, "config", zName);
230   if( pConfig->bColumnsize ){
231     fts5StorageRenameOne(pConfig, &rc, "docsize", zName);
232   }
233   if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
234     fts5StorageRenameOne(pConfig, &rc, "content", zName);
235   }
236   return rc;
237 }
238 
239 /*
240 ** Create the shadow table named zPost, with definition zDefn. Return
241 ** SQLITE_OK if successful, or an SQLite error code otherwise.
242 */
sqlite3Fts5CreateTable(Fts5Config * pConfig,const char * zPost,const char * zDefn,int bWithout,char ** pzErr)243 int sqlite3Fts5CreateTable(
244   Fts5Config *pConfig,            /* FTS5 configuration */
245   const char *zPost,              /* Shadow table to create (e.g. "content") */
246   const char *zDefn,              /* Columns etc. for shadow table */
247   int bWithout,                   /* True for without rowid */
248   char **pzErr                    /* OUT: Error message */
249 ){
250   int rc;
251   char *zErr = 0;
252 
253   rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
254       pConfig->zDb, pConfig->zName, zPost, zDefn,
255 #ifndef SQLITE_FTS5_NO_WITHOUT_ROWID
256       bWithout?" WITHOUT ROWID":
257 #endif
258       ""
259   );
260   if( zErr ){
261     *pzErr = sqlite3_mprintf(
262         "fts5: error creating shadow table %q_%s: %s",
263         pConfig->zName, zPost, zErr
264     );
265     sqlite3_free(zErr);
266   }
267 
268   return rc;
269 }
270 
271 /*
272 ** Open a new Fts5Index handle. If the bCreate argument is true, create
273 ** and initialize the underlying tables
274 **
275 ** If successful, set *pp to point to the new object and return SQLITE_OK.
276 ** Otherwise, set *pp to NULL and return an SQLite error code.
277 */
sqlite3Fts5StorageOpen(Fts5Config * pConfig,Fts5Index * pIndex,int bCreate,Fts5Storage ** pp,char ** pzErr)278 int sqlite3Fts5StorageOpen(
279   Fts5Config *pConfig,
280   Fts5Index *pIndex,
281   int bCreate,
282   Fts5Storage **pp,
283   char **pzErr                    /* OUT: Error message */
284 ){
285   int rc = SQLITE_OK;
286   Fts5Storage *p;                 /* New object */
287   sqlite3_int64 nByte;            /* Bytes of space to allocate */
288 
289   nByte = sizeof(Fts5Storage)               /* Fts5Storage object */
290         + pConfig->nCol * sizeof(i64);      /* Fts5Storage.aTotalSize[] */
291   *pp = p = (Fts5Storage*)sqlite3_malloc64(nByte);
292   if( !p ) return SQLITE_NOMEM;
293 
294   memset(p, 0, (size_t)nByte);
295   p->aTotalSize = (i64*)&p[1];
296   p->pConfig = pConfig;
297   p->pIndex = pIndex;
298 
299   if( bCreate ){
300     if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
301       int nDefn = 32 + pConfig->nCol*10;
302       char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10);
303       if( zDefn==0 ){
304         rc = SQLITE_NOMEM;
305       }else{
306         int i;
307         int iOff;
308         sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
309         iOff = (int)strlen(zDefn);
310         for(i=0; i<pConfig->nCol; i++){
311           sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
312           iOff += (int)strlen(&zDefn[iOff]);
313         }
314         rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
315       }
316       sqlite3_free(zDefn);
317     }
318 
319     if( rc==SQLITE_OK && pConfig->bColumnsize ){
320       rc = sqlite3Fts5CreateTable(
321           pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
322       );
323     }
324     if( rc==SQLITE_OK ){
325       rc = sqlite3Fts5CreateTable(
326           pConfig, "config", "k PRIMARY KEY, v", 1, pzErr
327       );
328     }
329     if( rc==SQLITE_OK ){
330       rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
331     }
332   }
333 
334   if( rc ){
335     sqlite3Fts5StorageClose(p);
336     *pp = 0;
337   }
338   return rc;
339 }
340 
341 /*
342 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
343 */
sqlite3Fts5StorageClose(Fts5Storage * p)344 int sqlite3Fts5StorageClose(Fts5Storage *p){
345   int rc = SQLITE_OK;
346   if( p ){
347     int i;
348 
349     /* Finalize all SQL statements */
350     for(i=0; i<ArraySize(p->aStmt); i++){
351       sqlite3_finalize(p->aStmt[i]);
352     }
353 
354     sqlite3_free(p);
355   }
356   return rc;
357 }
358 
359 typedef struct Fts5InsertCtx Fts5InsertCtx;
360 struct Fts5InsertCtx {
361   Fts5Storage *pStorage;
362   int iCol;
363   int szCol;                      /* Size of column value in tokens */
364 };
365 
366 /*
367 ** Tokenization callback used when inserting tokens into the FTS index.
368 */
fts5StorageInsertCallback(void * pContext,int tflags,const char * pToken,int nToken,int iUnused1,int iUnused2)369 static int fts5StorageInsertCallback(
370   void *pContext,                 /* Pointer to Fts5InsertCtx object */
371   int tflags,
372   const char *pToken,             /* Buffer containing token */
373   int nToken,                     /* Size of token in bytes */
374   int iUnused1,                   /* Start offset of token */
375   int iUnused2                    /* End offset of token */
376 ){
377   Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext;
378   Fts5Index *pIdx = pCtx->pStorage->pIndex;
379   UNUSED_PARAM2(iUnused1, iUnused2);
380   if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
381   if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
382     pCtx->szCol++;
383   }
384   return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken);
385 }
386 
387 /*
388 ** If a row with rowid iDel is present in the %_content table, add the
389 ** delete-markers to the FTS index necessary to delete it. Do not actually
390 ** remove the %_content row at this time though.
391 */
fts5StorageDeleteFromIndex(Fts5Storage * p,i64 iDel,sqlite3_value ** apVal)392 static int fts5StorageDeleteFromIndex(
393   Fts5Storage *p,
394   i64 iDel,
395   sqlite3_value **apVal
396 ){
397   Fts5Config *pConfig = p->pConfig;
398   sqlite3_stmt *pSeek = 0;        /* SELECT to read row iDel from %_data */
399   int rc;                         /* Return code */
400   int rc2;                        /* sqlite3_reset() return code */
401   int iCol;
402   Fts5InsertCtx ctx;
403 
404   if( apVal==0 ){
405     rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
406     if( rc!=SQLITE_OK ) return rc;
407     sqlite3_bind_int64(pSeek, 1, iDel);
408     if( sqlite3_step(pSeek)!=SQLITE_ROW ){
409       return sqlite3_reset(pSeek);
410     }
411   }
412 
413   ctx.pStorage = p;
414   ctx.iCol = -1;
415   rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
416   for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
417     if( pConfig->abUnindexed[iCol-1]==0 ){
418       const char *zText;
419       int nText;
420       assert( pSeek==0 || apVal==0 );
421       assert( pSeek!=0 || apVal!=0 );
422       if( pSeek ){
423         zText = (const char*)sqlite3_column_text(pSeek, iCol);
424         nText = sqlite3_column_bytes(pSeek, iCol);
425       }else if( ALWAYS(apVal) ){
426         zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
427         nText = sqlite3_value_bytes(apVal[iCol-1]);
428       }else{
429         continue;
430       }
431       ctx.szCol = 0;
432       rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
433           zText, nText, (void*)&ctx, fts5StorageInsertCallback
434       );
435       p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
436       if( p->aTotalSize[iCol-1]<0 ){
437         rc = FTS5_CORRUPT;
438       }
439     }
440   }
441   if( rc==SQLITE_OK && p->nTotalRow<1 ){
442     rc = FTS5_CORRUPT;
443   }else{
444     p->nTotalRow--;
445   }
446 
447   rc2 = sqlite3_reset(pSeek);
448   if( rc==SQLITE_OK ) rc = rc2;
449   return rc;
450 }
451 
452 
453 /*
454 ** Insert a record into the %_docsize table. Specifically, do:
455 **
456 **   INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
457 **
458 ** If there is no %_docsize table (as happens if the columnsize=0 option
459 ** is specified when the FTS5 table is created), this function is a no-op.
460 */
fts5StorageInsertDocsize(Fts5Storage * p,i64 iRowid,Fts5Buffer * pBuf)461 static int fts5StorageInsertDocsize(
462   Fts5Storage *p,                 /* Storage module to write to */
463   i64 iRowid,                     /* id value */
464   Fts5Buffer *pBuf                /* sz value */
465 ){
466   int rc = SQLITE_OK;
467   if( p->pConfig->bColumnsize ){
468     sqlite3_stmt *pReplace = 0;
469     rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
470     if( rc==SQLITE_OK ){
471       sqlite3_bind_int64(pReplace, 1, iRowid);
472       sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
473       sqlite3_step(pReplace);
474       rc = sqlite3_reset(pReplace);
475       sqlite3_bind_null(pReplace, 2);
476     }
477   }
478   return rc;
479 }
480 
481 /*
482 ** Load the contents of the "averages" record from disk into the
483 ** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
484 ** argument bCache is true, set the p->bTotalsValid flag to indicate
485 ** that the contents of aTotalSize[] and nTotalRow are valid until
486 ** further notice.
487 **
488 ** Return SQLITE_OK if successful, or an SQLite error code if an error
489 ** occurs.
490 */
fts5StorageLoadTotals(Fts5Storage * p,int bCache)491 static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
492   int rc = SQLITE_OK;
493   if( p->bTotalsValid==0 ){
494     rc = sqlite3Fts5IndexGetAverages(p->pIndex, &p->nTotalRow, p->aTotalSize);
495     p->bTotalsValid = bCache;
496   }
497   return rc;
498 }
499 
500 /*
501 ** Store the current contents of the p->nTotalRow and p->aTotalSize[]
502 ** variables in the "averages" record on disk.
503 **
504 ** Return SQLITE_OK if successful, or an SQLite error code if an error
505 ** occurs.
506 */
fts5StorageSaveTotals(Fts5Storage * p)507 static int fts5StorageSaveTotals(Fts5Storage *p){
508   int nCol = p->pConfig->nCol;
509   int i;
510   Fts5Buffer buf;
511   int rc = SQLITE_OK;
512   memset(&buf, 0, sizeof(buf));
513 
514   sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow);
515   for(i=0; i<nCol; i++){
516     sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]);
517   }
518   if( rc==SQLITE_OK ){
519     rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n);
520   }
521   sqlite3_free(buf.p);
522 
523   return rc;
524 }
525 
526 /*
527 ** Remove a row from the FTS table.
528 */
sqlite3Fts5StorageDelete(Fts5Storage * p,i64 iDel,sqlite3_value ** apVal)529 int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
530   Fts5Config *pConfig = p->pConfig;
531   int rc;
532   sqlite3_stmt *pDel = 0;
533 
534   assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 );
535   rc = fts5StorageLoadTotals(p, 1);
536 
537   /* Delete the index records */
538   if( rc==SQLITE_OK ){
539     rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
540   }
541 
542   /* Delete the %_docsize record */
543   if( rc==SQLITE_OK && pConfig->bColumnsize ){
544     rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
545     if( rc==SQLITE_OK ){
546       sqlite3_bind_int64(pDel, 1, iDel);
547       sqlite3_step(pDel);
548       rc = sqlite3_reset(pDel);
549     }
550   }
551 
552   /* Delete the %_content record */
553   if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
554     if( rc==SQLITE_OK ){
555       rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0);
556     }
557     if( rc==SQLITE_OK ){
558       sqlite3_bind_int64(pDel, 1, iDel);
559       sqlite3_step(pDel);
560       rc = sqlite3_reset(pDel);
561     }
562   }
563 
564   return rc;
565 }
566 
567 /*
568 ** Delete all entries in the FTS5 index.
569 */
sqlite3Fts5StorageDeleteAll(Fts5Storage * p)570 int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
571   Fts5Config *pConfig = p->pConfig;
572   int rc;
573 
574   p->bTotalsValid = 0;
575 
576   /* Delete the contents of the %_data and %_docsize tables. */
577   rc = fts5ExecPrintf(pConfig->db, 0,
578       "DELETE FROM %Q.'%q_data';"
579       "DELETE FROM %Q.'%q_idx';",
580       pConfig->zDb, pConfig->zName,
581       pConfig->zDb, pConfig->zName
582   );
583   if( rc==SQLITE_OK && pConfig->bColumnsize ){
584     rc = fts5ExecPrintf(pConfig->db, 0,
585         "DELETE FROM %Q.'%q_docsize';",
586         pConfig->zDb, pConfig->zName
587     );
588   }
589 
590   /* Reinitialize the %_data table. This call creates the initial structure
591   ** and averages records.  */
592   if( rc==SQLITE_OK ){
593     rc = sqlite3Fts5IndexReinit(p->pIndex);
594   }
595   if( rc==SQLITE_OK ){
596     rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
597   }
598   return rc;
599 }
600 
sqlite3Fts5StorageRebuild(Fts5Storage * p)601 int sqlite3Fts5StorageRebuild(Fts5Storage *p){
602   Fts5Buffer buf = {0,0,0};
603   Fts5Config *pConfig = p->pConfig;
604   sqlite3_stmt *pScan = 0;
605   Fts5InsertCtx ctx;
606   int rc, rc2;
607 
608   memset(&ctx, 0, sizeof(Fts5InsertCtx));
609   ctx.pStorage = p;
610   rc = sqlite3Fts5StorageDeleteAll(p);
611   if( rc==SQLITE_OK ){
612     rc = fts5StorageLoadTotals(p, 1);
613   }
614 
615   if( rc==SQLITE_OK ){
616     rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
617   }
618 
619   while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
620     i64 iRowid = sqlite3_column_int64(pScan, 0);
621 
622     sqlite3Fts5BufferZero(&buf);
623     rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
624     for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
625       ctx.szCol = 0;
626       if( pConfig->abUnindexed[ctx.iCol]==0 ){
627         const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1);
628         int nText = sqlite3_column_bytes(pScan, ctx.iCol+1);
629         rc = sqlite3Fts5Tokenize(pConfig,
630             FTS5_TOKENIZE_DOCUMENT,
631             zText, nText,
632             (void*)&ctx,
633             fts5StorageInsertCallback
634         );
635       }
636       sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
637       p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
638     }
639     p->nTotalRow++;
640 
641     if( rc==SQLITE_OK ){
642       rc = fts5StorageInsertDocsize(p, iRowid, &buf);
643     }
644   }
645   sqlite3_free(buf.p);
646   rc2 = sqlite3_reset(pScan);
647   if( rc==SQLITE_OK ) rc = rc2;
648 
649   /* Write the averages record */
650   if( rc==SQLITE_OK ){
651     rc = fts5StorageSaveTotals(p);
652   }
653   return rc;
654 }
655 
sqlite3Fts5StorageOptimize(Fts5Storage * p)656 int sqlite3Fts5StorageOptimize(Fts5Storage *p){
657   return sqlite3Fts5IndexOptimize(p->pIndex);
658 }
659 
sqlite3Fts5StorageMerge(Fts5Storage * p,int nMerge)660 int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
661   return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
662 }
663 
sqlite3Fts5StorageReset(Fts5Storage * p)664 int sqlite3Fts5StorageReset(Fts5Storage *p){
665   return sqlite3Fts5IndexReset(p->pIndex);
666 }
667 
668 /*
669 ** Allocate a new rowid. This is used for "external content" tables when
670 ** a NULL value is inserted into the rowid column. The new rowid is allocated
671 ** by inserting a dummy row into the %_docsize table. The dummy will be
672 ** overwritten later.
673 **
674 ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
675 ** this case the user is required to provide a rowid explicitly.
676 */
fts5StorageNewRowid(Fts5Storage * p,i64 * piRowid)677 static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
678   int rc = SQLITE_MISMATCH;
679   if( p->pConfig->bColumnsize ){
680     sqlite3_stmt *pReplace = 0;
681     rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
682     if( rc==SQLITE_OK ){
683       sqlite3_bind_null(pReplace, 1);
684       sqlite3_bind_null(pReplace, 2);
685       sqlite3_step(pReplace);
686       rc = sqlite3_reset(pReplace);
687     }
688     if( rc==SQLITE_OK ){
689       *piRowid = sqlite3_last_insert_rowid(p->pConfig->db);
690     }
691   }
692   return rc;
693 }
694 
695 /*
696 ** Insert a new row into the FTS content table.
697 */
sqlite3Fts5StorageContentInsert(Fts5Storage * p,sqlite3_value ** apVal,i64 * piRowid)698 int sqlite3Fts5StorageContentInsert(
699   Fts5Storage *p,
700   sqlite3_value **apVal,
701   i64 *piRowid
702 ){
703   Fts5Config *pConfig = p->pConfig;
704   int rc = SQLITE_OK;
705 
706   /* Insert the new row into the %_content table. */
707   if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
708     if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
709       *piRowid = sqlite3_value_int64(apVal[1]);
710     }else{
711       rc = fts5StorageNewRowid(p, piRowid);
712     }
713   }else{
714     sqlite3_stmt *pInsert = 0;    /* Statement to write %_content table */
715     int i;                        /* Counter variable */
716     rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
717     for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
718       rc = sqlite3_bind_value(pInsert, i, apVal[i]);
719     }
720     if( rc==SQLITE_OK ){
721       sqlite3_step(pInsert);
722       rc = sqlite3_reset(pInsert);
723     }
724     *piRowid = sqlite3_last_insert_rowid(pConfig->db);
725   }
726 
727   return rc;
728 }
729 
730 /*
731 ** Insert new entries into the FTS index and %_docsize table.
732 */
sqlite3Fts5StorageIndexInsert(Fts5Storage * p,sqlite3_value ** apVal,i64 iRowid)733 int sqlite3Fts5StorageIndexInsert(
734   Fts5Storage *p,
735   sqlite3_value **apVal,
736   i64 iRowid
737 ){
738   Fts5Config *pConfig = p->pConfig;
739   int rc = SQLITE_OK;             /* Return code */
740   Fts5InsertCtx ctx;              /* Tokenization callback context object */
741   Fts5Buffer buf;                 /* Buffer used to build up %_docsize blob */
742 
743   memset(&buf, 0, sizeof(Fts5Buffer));
744   ctx.pStorage = p;
745   rc = fts5StorageLoadTotals(p, 1);
746 
747   if( rc==SQLITE_OK ){
748     rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
749   }
750   for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
751     ctx.szCol = 0;
752     if( pConfig->abUnindexed[ctx.iCol]==0 ){
753       const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]);
754       int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]);
755       rc = sqlite3Fts5Tokenize(pConfig,
756           FTS5_TOKENIZE_DOCUMENT,
757           zText, nText,
758           (void*)&ctx,
759           fts5StorageInsertCallback
760       );
761     }
762     sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
763     p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
764   }
765   p->nTotalRow++;
766 
767   /* Write the %_docsize record */
768   if( rc==SQLITE_OK ){
769     rc = fts5StorageInsertDocsize(p, iRowid, &buf);
770   }
771   sqlite3_free(buf.p);
772 
773   return rc;
774 }
775 
fts5StorageCount(Fts5Storage * p,const char * zSuffix,i64 * pnRow)776 static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
777   Fts5Config *pConfig = p->pConfig;
778   char *zSql;
779   int rc;
780 
781   zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
782       pConfig->zDb, pConfig->zName, zSuffix
783   );
784   if( zSql==0 ){
785     rc = SQLITE_NOMEM;
786   }else{
787     sqlite3_stmt *pCnt = 0;
788     rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pCnt, 0);
789     if( rc==SQLITE_OK ){
790       if( SQLITE_ROW==sqlite3_step(pCnt) ){
791         *pnRow = sqlite3_column_int64(pCnt, 0);
792       }
793       rc = sqlite3_finalize(pCnt);
794     }
795   }
796 
797   sqlite3_free(zSql);
798   return rc;
799 }
800 
801 /*
802 ** Context object used by sqlite3Fts5StorageIntegrity().
803 */
804 typedef struct Fts5IntegrityCtx Fts5IntegrityCtx;
805 struct Fts5IntegrityCtx {
806   i64 iRowid;
807   int iCol;
808   int szCol;
809   u64 cksum;
810   Fts5Termset *pTermset;
811   Fts5Config *pConfig;
812 };
813 
814 
815 /*
816 ** Tokenization callback used by integrity check.
817 */
fts5StorageIntegrityCallback(void * pContext,int tflags,const char * pToken,int nToken,int iUnused1,int iUnused2)818 static int fts5StorageIntegrityCallback(
819   void *pContext,                 /* Pointer to Fts5IntegrityCtx object */
820   int tflags,
821   const char *pToken,             /* Buffer containing token */
822   int nToken,                     /* Size of token in bytes */
823   int iUnused1,                   /* Start offset of token */
824   int iUnused2                    /* End offset of token */
825 ){
826   Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
827   Fts5Termset *pTermset = pCtx->pTermset;
828   int bPresent;
829   int ii;
830   int rc = SQLITE_OK;
831   int iPos;
832   int iCol;
833 
834   UNUSED_PARAM2(iUnused1, iUnused2);
835   if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
836 
837   if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
838     pCtx->szCol++;
839   }
840 
841   switch( pCtx->pConfig->eDetail ){
842     case FTS5_DETAIL_FULL:
843       iPos = pCtx->szCol-1;
844       iCol = pCtx->iCol;
845       break;
846 
847     case FTS5_DETAIL_COLUMNS:
848       iPos = pCtx->iCol;
849       iCol = 0;
850       break;
851 
852     default:
853       assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE );
854       iPos = 0;
855       iCol = 0;
856       break;
857   }
858 
859   rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent);
860   if( rc==SQLITE_OK && bPresent==0 ){
861     pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
862         pCtx->iRowid, iCol, iPos, 0, pToken, nToken
863     );
864   }
865 
866   for(ii=0; rc==SQLITE_OK && ii<pCtx->pConfig->nPrefix; ii++){
867     const int nChar = pCtx->pConfig->aPrefix[ii];
868     int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
869     if( nByte ){
870       rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent);
871       if( bPresent==0 ){
872         pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
873             pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte
874         );
875       }
876     }
877   }
878 
879   return rc;
880 }
881 
882 /*
883 ** Check that the contents of the FTS index match that of the %_content
884 ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
885 ** some other SQLite error code if an error occurs while attempting to
886 ** determine this.
887 */
sqlite3Fts5StorageIntegrity(Fts5Storage * p,int iArg)888 int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){
889   Fts5Config *pConfig = p->pConfig;
890   int rc = SQLITE_OK;             /* Return code */
891   int *aColSize;                  /* Array of size pConfig->nCol */
892   i64 *aTotalSize;                /* Array of size pConfig->nCol */
893   Fts5IntegrityCtx ctx;
894   sqlite3_stmt *pScan;
895   int bUseCksum;
896 
897   memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
898   ctx.pConfig = p->pConfig;
899   aTotalSize = (i64*)sqlite3_malloc64(pConfig->nCol*(sizeof(int)+sizeof(i64)));
900   if( !aTotalSize ) return SQLITE_NOMEM;
901   aColSize = (int*)&aTotalSize[pConfig->nCol];
902   memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
903 
904   bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL
905            || (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg)
906   );
907   if( bUseCksum ){
908     /* Generate the expected index checksum based on the contents of the
909     ** %_content table. This block stores the checksum in ctx.cksum. */
910     rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
911     if( rc==SQLITE_OK ){
912       int rc2;
913       while( SQLITE_ROW==sqlite3_step(pScan) ){
914         int i;
915         ctx.iRowid = sqlite3_column_int64(pScan, 0);
916         ctx.szCol = 0;
917         if( pConfig->bColumnsize ){
918           rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
919         }
920         if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
921           rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
922         }
923         for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
924           if( pConfig->abUnindexed[i] ) continue;
925           ctx.iCol = i;
926           ctx.szCol = 0;
927           if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
928             rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
929           }
930           if( rc==SQLITE_OK ){
931             const char *zText = (const char*)sqlite3_column_text(pScan, i+1);
932             int nText = sqlite3_column_bytes(pScan, i+1);
933             rc = sqlite3Fts5Tokenize(pConfig,
934                 FTS5_TOKENIZE_DOCUMENT,
935                 zText, nText,
936                 (void*)&ctx,
937                 fts5StorageIntegrityCallback
938             );
939           }
940           if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
941             rc = FTS5_CORRUPT;
942           }
943           aTotalSize[i] += ctx.szCol;
944           if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
945             sqlite3Fts5TermsetFree(ctx.pTermset);
946             ctx.pTermset = 0;
947           }
948         }
949         sqlite3Fts5TermsetFree(ctx.pTermset);
950         ctx.pTermset = 0;
951 
952         if( rc!=SQLITE_OK ) break;
953       }
954       rc2 = sqlite3_reset(pScan);
955       if( rc==SQLITE_OK ) rc = rc2;
956     }
957 
958     /* Test that the "totals" (sometimes called "averages") record looks Ok */
959     if( rc==SQLITE_OK ){
960       int i;
961       rc = fts5StorageLoadTotals(p, 0);
962       for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
963         if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
964       }
965     }
966 
967     /* Check that the %_docsize and %_content tables contain the expected
968     ** number of rows.  */
969     if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
970       i64 nRow = 0;
971       rc = fts5StorageCount(p, "content", &nRow);
972       if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
973     }
974     if( rc==SQLITE_OK && pConfig->bColumnsize ){
975       i64 nRow = 0;
976       rc = fts5StorageCount(p, "docsize", &nRow);
977       if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
978     }
979   }
980 
981   /* Pass the expected checksum down to the FTS index module. It will
982   ** verify, amongst other things, that it matches the checksum generated by
983   ** inspecting the index itself.  */
984   if( rc==SQLITE_OK ){
985     rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum);
986   }
987 
988   sqlite3_free(aTotalSize);
989   return rc;
990 }
991 
992 /*
993 ** Obtain an SQLite statement handle that may be used to read data from the
994 ** %_content table.
995 */
sqlite3Fts5StorageStmt(Fts5Storage * p,int eStmt,sqlite3_stmt ** pp,char ** pzErrMsg)996 int sqlite3Fts5StorageStmt(
997   Fts5Storage *p,
998   int eStmt,
999   sqlite3_stmt **pp,
1000   char **pzErrMsg
1001 ){
1002   int rc;
1003   assert( eStmt==FTS5_STMT_SCAN_ASC
1004        || eStmt==FTS5_STMT_SCAN_DESC
1005        || eStmt==FTS5_STMT_LOOKUP
1006   );
1007   rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
1008   if( rc==SQLITE_OK ){
1009     assert( p->aStmt[eStmt]==*pp );
1010     p->aStmt[eStmt] = 0;
1011   }
1012   return rc;
1013 }
1014 
1015 /*
1016 ** Release an SQLite statement handle obtained via an earlier call to
1017 ** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
1018 ** must match that passed to the sqlite3Fts5StorageStmt() call.
1019 */
sqlite3Fts5StorageStmtRelease(Fts5Storage * p,int eStmt,sqlite3_stmt * pStmt)1020 void sqlite3Fts5StorageStmtRelease(
1021   Fts5Storage *p,
1022   int eStmt,
1023   sqlite3_stmt *pStmt
1024 ){
1025   assert( eStmt==FTS5_STMT_SCAN_ASC
1026        || eStmt==FTS5_STMT_SCAN_DESC
1027        || eStmt==FTS5_STMT_LOOKUP
1028   );
1029   if( p->aStmt[eStmt]==0 ){
1030     sqlite3_reset(pStmt);
1031     p->aStmt[eStmt] = pStmt;
1032   }else{
1033     sqlite3_finalize(pStmt);
1034   }
1035 }
1036 
fts5StorageDecodeSizeArray(int * aCol,int nCol,const u8 * aBlob,int nBlob)1037 static int fts5StorageDecodeSizeArray(
1038   int *aCol, int nCol,            /* Array to populate */
1039   const u8 *aBlob, int nBlob      /* Record to read varints from */
1040 ){
1041   int i;
1042   int iOff = 0;
1043   for(i=0; i<nCol; i++){
1044     if( iOff>=nBlob ) return 1;
1045     iOff += fts5GetVarint32(&aBlob[iOff], aCol[i]);
1046   }
1047   return (iOff!=nBlob);
1048 }
1049 
1050 /*
1051 ** Argument aCol points to an array of integers containing one entry for
1052 ** each table column. This function reads the %_docsize record for the
1053 ** specified rowid and populates aCol[] with the results.
1054 **
1055 ** An SQLite error code is returned if an error occurs, or SQLITE_OK
1056 ** otherwise.
1057 */
sqlite3Fts5StorageDocsize(Fts5Storage * p,i64 iRowid,int * aCol)1058 int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
1059   int nCol = p->pConfig->nCol;    /* Number of user columns in table */
1060   sqlite3_stmt *pLookup = 0;      /* Statement to query %_docsize */
1061   int rc;                         /* Return Code */
1062 
1063   assert( p->pConfig->bColumnsize );
1064   rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
1065   if( pLookup ){
1066     int bCorrupt = 1;
1067     assert( rc==SQLITE_OK );
1068     sqlite3_bind_int64(pLookup, 1, iRowid);
1069     if( SQLITE_ROW==sqlite3_step(pLookup) ){
1070       const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
1071       int nBlob = sqlite3_column_bytes(pLookup, 0);
1072       if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){
1073         bCorrupt = 0;
1074       }
1075     }
1076     rc = sqlite3_reset(pLookup);
1077     if( bCorrupt && rc==SQLITE_OK ){
1078       rc = FTS5_CORRUPT;
1079     }
1080   }else{
1081     assert( rc!=SQLITE_OK );
1082   }
1083 
1084   return rc;
1085 }
1086 
sqlite3Fts5StorageSize(Fts5Storage * p,int iCol,i64 * pnToken)1087 int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
1088   int rc = fts5StorageLoadTotals(p, 0);
1089   if( rc==SQLITE_OK ){
1090     *pnToken = 0;
1091     if( iCol<0 ){
1092       int i;
1093       for(i=0; i<p->pConfig->nCol; i++){
1094         *pnToken += p->aTotalSize[i];
1095       }
1096     }else if( iCol<p->pConfig->nCol ){
1097       *pnToken = p->aTotalSize[iCol];
1098     }else{
1099       rc = SQLITE_RANGE;
1100     }
1101   }
1102   return rc;
1103 }
1104 
sqlite3Fts5StorageRowCount(Fts5Storage * p,i64 * pnRow)1105 int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){
1106   int rc = fts5StorageLoadTotals(p, 0);
1107   if( rc==SQLITE_OK ){
1108     /* nTotalRow being zero does not necessarily indicate a corrupt
1109     ** database - it might be that the FTS5 table really does contain zero
1110     ** rows. However this function is only called from the xRowCount() API,
1111     ** and there is no way for that API to be invoked if the table contains
1112     ** no rows. Hence the FTS5_CORRUPT return.  */
1113     *pnRow = p->nTotalRow;
1114     if( p->nTotalRow<=0 ) rc = FTS5_CORRUPT;
1115   }
1116   return rc;
1117 }
1118 
1119 /*
1120 ** Flush any data currently held in-memory to disk.
1121 */
sqlite3Fts5StorageSync(Fts5Storage * p)1122 int sqlite3Fts5StorageSync(Fts5Storage *p){
1123   int rc = SQLITE_OK;
1124   i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
1125   if( p->bTotalsValid ){
1126     rc = fts5StorageSaveTotals(p);
1127     p->bTotalsValid = 0;
1128   }
1129   if( rc==SQLITE_OK ){
1130     rc = sqlite3Fts5IndexSync(p->pIndex);
1131   }
1132   sqlite3_set_last_insert_rowid(p->pConfig->db, iLastRowid);
1133   return rc;
1134 }
1135 
sqlite3Fts5StorageRollback(Fts5Storage * p)1136 int sqlite3Fts5StorageRollback(Fts5Storage *p){
1137   p->bTotalsValid = 0;
1138   return sqlite3Fts5IndexRollback(p->pIndex);
1139 }
1140 
sqlite3Fts5StorageConfigValue(Fts5Storage * p,const char * z,sqlite3_value * pVal,int iVal)1141 int sqlite3Fts5StorageConfigValue(
1142   Fts5Storage *p,
1143   const char *z,
1144   sqlite3_value *pVal,
1145   int iVal
1146 ){
1147   sqlite3_stmt *pReplace = 0;
1148   int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0);
1149   if( rc==SQLITE_OK ){
1150     sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_STATIC);
1151     if( pVal ){
1152       sqlite3_bind_value(pReplace, 2, pVal);
1153     }else{
1154       sqlite3_bind_int(pReplace, 2, iVal);
1155     }
1156     sqlite3_step(pReplace);
1157     rc = sqlite3_reset(pReplace);
1158     sqlite3_bind_null(pReplace, 1);
1159   }
1160   if( rc==SQLITE_OK && pVal ){
1161     int iNew = p->pConfig->iCookie + 1;
1162     rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
1163     if( rc==SQLITE_OK ){
1164       p->pConfig->iCookie = iNew;
1165     }
1166   }
1167   return rc;
1168 }
1169