Snapshot of upstream SQLite 3.38.5
[sqlcipher.git] / ext / fts5 / fts5_storage.c
blob02b98d9e44e9eb365ddab93d9b77804eb9c4b582
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.
11 ******************************************************************************
17 #include "fts5Int.h"
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];
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
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
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.
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) */
60 int rc = SQLITE_OK;
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 ));
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 */
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 */
83 "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
85 "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
86 "SELECT %s FROM %s AS T", /* SCAN */
88 Fts5Config *pC = p->pConfig;
89 char *zSql = 0;
91 switch( eStmt ){
92 case FTS5_STMT_SCAN:
93 zSql = sqlite3_mprintf(azStmt[eStmt],
94 pC->zContentExprlist, pC->zContent
96 break;
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
104 break;
106 case FTS5_STMT_LOOKUP:
107 zSql = sqlite3_mprintf(azStmt[eStmt],
108 pC->zContentExprlist, pC->zContent, pC->zContentRowid
110 break;
112 case FTS5_STMT_INSERT_CONTENT:
113 case FTS5_STMT_REPLACE_CONTENT: {
114 int nCol = pC->nCol + 1;
115 char *zBind;
116 int i;
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] = ',';
124 zBind[i*2-1] = '\0';
125 zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
126 sqlite3_free(zBind);
128 break;
131 default:
132 zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
133 break;
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));
151 *ppStmt = p->aStmt[eStmt];
152 sqlite3_reset(*ppStmt);
153 return rc;
157 static int fts5ExecPrintf(
158 sqlite3 *db,
159 char **pzErr,
160 const char *zFormat,
163 int rc;
164 va_list ap; /* ... printf arguments */
165 char *zSql;
167 va_start(ap, zFormat);
168 zSql = sqlite3_vmprintf(zFormat, ap);
170 if( zSql==0 ){
171 rc = SQLITE_NOMEM;
172 }else{
173 rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
174 sqlite3_free(zSql);
177 va_end(ap);
178 return rc;
182 ** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
183 ** code otherwise.
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
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
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
206 return rc;
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 */
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
223 int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){
224 Fts5Config *pConfig = pStorage->pConfig;
225 int rc = sqlite3Fts5StorageSync(pStorage);
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);
233 if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
234 fts5StorageRenameOne(pConfig, &rc, "content", zName);
236 return rc;
240 ** Create the shadow table named zPost, with definition zDefn. Return
241 ** SQLITE_OK if successful, or an SQLite error code otherwise.
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 */
250 int rc;
251 char *zErr = 0;
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
260 if( zErr ){
261 *pzErr = sqlite3_mprintf(
262 "fts5: error creating shadow table %q_%s: %s",
263 pConfig->zName, zPost, zErr
265 sqlite3_free(zErr);
268 return rc;
272 ** Open a new Fts5Index handle. If the bCreate argument is true, create
273 ** and initialize the underlying tables
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.
278 int sqlite3Fts5StorageOpen(
279 Fts5Config *pConfig,
280 Fts5Index *pIndex,
281 int bCreate,
282 Fts5Storage **pp,
283 char **pzErr /* OUT: Error message */
285 int rc = SQLITE_OK;
286 Fts5Storage *p; /* New object */
287 sqlite3_int64 nByte; /* Bytes of space to allocate */
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;
294 memset(p, 0, (size_t)nByte);
295 p->aTotalSize = (i64*)&p[1];
296 p->pConfig = pConfig;
297 p->pIndex = pIndex;
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]);
314 rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
316 sqlite3_free(zDefn);
319 if( rc==SQLITE_OK && pConfig->bColumnsize ){
320 rc = sqlite3Fts5CreateTable(
321 pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
324 if( rc==SQLITE_OK ){
325 rc = sqlite3Fts5CreateTable(
326 pConfig, "config", "k PRIMARY KEY, v", 1, pzErr
329 if( rc==SQLITE_OK ){
330 rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
334 if( rc ){
335 sqlite3Fts5StorageClose(p);
336 *pp = 0;
338 return rc;
342 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
344 int sqlite3Fts5StorageClose(Fts5Storage *p){
345 int rc = SQLITE_OK;
346 if( p ){
347 int i;
349 /* Finalize all SQL statements */
350 for(i=0; i<ArraySize(p->aStmt); i++){
351 sqlite3_finalize(p->aStmt[i]);
354 sqlite3_free(p);
356 return rc;
359 typedef struct Fts5InsertCtx Fts5InsertCtx;
360 struct Fts5InsertCtx {
361 Fts5Storage *pStorage;
362 int iCol;
363 int szCol; /* Size of column value in tokens */
367 ** Tokenization callback used when inserting tokens into the FTS index.
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 */
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++;
384 return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken);
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.
392 static int fts5StorageDeleteFromIndex(
393 Fts5Storage *p,
394 i64 iDel,
395 sqlite3_value **apVal
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;
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);
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;
431 ctx.szCol = 0;
432 rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
433 zText, nText, (void*)&ctx, fts5StorageInsertCallback
435 p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
436 if( p->aTotalSize[iCol-1]<0 ){
437 rc = FTS5_CORRUPT;
441 if( rc==SQLITE_OK && p->nTotalRow<1 ){
442 rc = FTS5_CORRUPT;
443 }else{
444 p->nTotalRow--;
447 rc2 = sqlite3_reset(pSeek);
448 if( rc==SQLITE_OK ) rc = rc2;
449 return rc;
454 ** Insert a record into the %_docsize table. Specifically, do:
456 ** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
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.
461 static int fts5StorageInsertDocsize(
462 Fts5Storage *p, /* Storage module to write to */
463 i64 iRowid, /* id value */
464 Fts5Buffer *pBuf /* sz value */
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);
478 return rc;
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.
488 ** Return SQLITE_OK if successful, or an SQLite error code if an error
489 ** occurs.
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;
497 return rc;
501 ** Store the current contents of the p->nTotalRow and p->aTotalSize[]
502 ** variables in the "averages" record on disk.
504 ** Return SQLITE_OK if successful, or an SQLite error code if an error
505 ** occurs.
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));
514 sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow);
515 for(i=0; i<nCol; i++){
516 sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]);
518 if( rc==SQLITE_OK ){
519 rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n);
521 sqlite3_free(buf.p);
523 return rc;
527 ** Remove a row from the FTS table.
529 int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
530 Fts5Config *pConfig = p->pConfig;
531 int rc;
532 sqlite3_stmt *pDel = 0;
534 assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 );
535 rc = fts5StorageLoadTotals(p, 1);
537 /* Delete the index records */
538 if( rc==SQLITE_OK ){
539 rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
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);
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);
557 if( rc==SQLITE_OK ){
558 sqlite3_bind_int64(pDel, 1, iDel);
559 sqlite3_step(pDel);
560 rc = sqlite3_reset(pDel);
564 return rc;
568 ** Delete all entries in the FTS5 index.
570 int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
571 Fts5Config *pConfig = p->pConfig;
572 int rc;
574 p->bTotalsValid = 0;
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
583 if( rc==SQLITE_OK && pConfig->bColumnsize ){
584 rc = fts5ExecPrintf(pConfig->db, 0,
585 "DELETE FROM %Q.'%q_docsize';",
586 pConfig->zDb, pConfig->zName
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);
595 if( rc==SQLITE_OK ){
596 rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
598 return rc;
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;
608 memset(&ctx, 0, sizeof(Fts5InsertCtx));
609 ctx.pStorage = p;
610 rc = sqlite3Fts5StorageDeleteAll(p);
611 if( rc==SQLITE_OK ){
612 rc = fts5StorageLoadTotals(p, 1);
615 if( rc==SQLITE_OK ){
616 rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
619 while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
620 i64 iRowid = sqlite3_column_int64(pScan, 0);
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
636 sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
637 p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
639 p->nTotalRow++;
641 if( rc==SQLITE_OK ){
642 rc = fts5StorageInsertDocsize(p, iRowid, &buf);
645 sqlite3_free(buf.p);
646 rc2 = sqlite3_reset(pScan);
647 if( rc==SQLITE_OK ) rc = rc2;
649 /* Write the averages record */
650 if( rc==SQLITE_OK ){
651 rc = fts5StorageSaveTotals(p);
653 return rc;
656 int sqlite3Fts5StorageOptimize(Fts5Storage *p){
657 return sqlite3Fts5IndexOptimize(p->pIndex);
660 int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
661 return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
664 int sqlite3Fts5StorageReset(Fts5Storage *p){
665 return sqlite3Fts5IndexReset(p->pIndex);
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.
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.
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);
688 if( rc==SQLITE_OK ){
689 *piRowid = sqlite3_last_insert_rowid(p->pConfig->db);
692 return rc;
696 ** Insert a new row into the FTS content table.
698 int sqlite3Fts5StorageContentInsert(
699 Fts5Storage *p,
700 sqlite3_value **apVal,
701 i64 *piRowid
703 Fts5Config *pConfig = p->pConfig;
704 int rc = SQLITE_OK;
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);
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]);
720 if( rc==SQLITE_OK ){
721 sqlite3_step(pInsert);
722 rc = sqlite3_reset(pInsert);
724 *piRowid = sqlite3_last_insert_rowid(pConfig->db);
727 return rc;
731 ** Insert new entries into the FTS index and %_docsize table.
733 int sqlite3Fts5StorageIndexInsert(
734 Fts5Storage *p,
735 sqlite3_value **apVal,
736 i64 iRowid
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 */
743 memset(&buf, 0, sizeof(Fts5Buffer));
744 ctx.pStorage = p;
745 rc = fts5StorageLoadTotals(p, 1);
747 if( rc==SQLITE_OK ){
748 rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
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
762 sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
763 p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
765 p->nTotalRow++;
767 /* Write the %_docsize record */
768 if( rc==SQLITE_OK ){
769 rc = fts5StorageInsertDocsize(p, iRowid, &buf);
771 sqlite3_free(buf.p);
773 return rc;
776 static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
777 Fts5Config *pConfig = p->pConfig;
778 char *zSql;
779 int rc;
781 zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
782 pConfig->zDb, pConfig->zName, zSuffix
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);
793 rc = sqlite3_finalize(pCnt);
797 sqlite3_free(zSql);
798 return rc;
802 ** Context object used by sqlite3Fts5StorageIntegrity().
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;
816 ** Tokenization callback used by integrity check.
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 */
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;
834 UNUSED_PARAM2(iUnused1, iUnused2);
835 if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
837 if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
838 pCtx->szCol++;
841 switch( pCtx->pConfig->eDetail ){
842 case FTS5_DETAIL_FULL:
843 iPos = pCtx->szCol-1;
844 iCol = pCtx->iCol;
845 break;
847 case FTS5_DETAIL_COLUMNS:
848 iPos = pCtx->iCol;
849 iCol = 0;
850 break;
852 default:
853 assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE );
854 iPos = 0;
855 iCol = 0;
856 break;
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
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
879 return rc;
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.
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;
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);
904 bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL
905 || (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg)
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);
920 if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
921 rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
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);
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
940 if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
941 rc = FTS5_CORRUPT;
943 aTotalSize[i] += ctx.szCol;
944 if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
945 sqlite3Fts5TermsetFree(ctx.pTermset);
946 ctx.pTermset = 0;
949 sqlite3Fts5TermsetFree(ctx.pTermset);
950 ctx.pTermset = 0;
952 if( rc!=SQLITE_OK ) break;
954 rc2 = sqlite3_reset(pScan);
955 if( rc==SQLITE_OK ) rc = rc2;
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;
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;
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;
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);
988 sqlite3_free(aTotalSize);
989 return rc;
993 ** Obtain an SQLite statement handle that may be used to read data from the
994 ** %_content table.
996 int sqlite3Fts5StorageStmt(
997 Fts5Storage *p,
998 int eStmt,
999 sqlite3_stmt **pp,
1000 char **pzErrMsg
1002 int rc;
1003 assert( eStmt==FTS5_STMT_SCAN_ASC
1004 || eStmt==FTS5_STMT_SCAN_DESC
1005 || eStmt==FTS5_STMT_LOOKUP
1007 rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
1008 if( rc==SQLITE_OK ){
1009 assert( p->aStmt[eStmt]==*pp );
1010 p->aStmt[eStmt] = 0;
1012 return rc;
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.
1020 void sqlite3Fts5StorageStmtRelease(
1021 Fts5Storage *p,
1022 int eStmt,
1023 sqlite3_stmt *pStmt
1025 assert( eStmt==FTS5_STMT_SCAN_ASC
1026 || eStmt==FTS5_STMT_SCAN_DESC
1027 || eStmt==FTS5_STMT_LOOKUP
1029 if( p->aStmt[eStmt]==0 ){
1030 sqlite3_reset(pStmt);
1031 p->aStmt[eStmt] = pStmt;
1032 }else{
1033 sqlite3_finalize(pStmt);
1037 static int fts5StorageDecodeSizeArray(
1038 int *aCol, int nCol, /* Array to populate */
1039 const u8 *aBlob, int nBlob /* Record to read varints from */
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]);
1047 return (iOff!=nBlob);
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.
1055 ** An SQLite error code is returned if an error occurs, or SQLITE_OK
1056 ** otherwise.
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 */
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;
1076 rc = sqlite3_reset(pLookup);
1077 if( bCorrupt && rc==SQLITE_OK ){
1078 rc = FTS5_CORRUPT;
1080 }else{
1081 assert( rc!=SQLITE_OK );
1084 return rc;
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];
1096 }else if( iCol<p->pConfig->nCol ){
1097 *pnToken = p->aTotalSize[iCol];
1098 }else{
1099 rc = SQLITE_RANGE;
1102 return rc;
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;
1116 return rc;
1120 ** Flush any data currently held in-memory to disk.
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;
1129 if( rc==SQLITE_OK ){
1130 rc = sqlite3Fts5IndexSync(p->pIndex);
1132 sqlite3_set_last_insert_rowid(p->pConfig->db, iLastRowid);
1133 return rc;
1136 int sqlite3Fts5StorageRollback(Fts5Storage *p){
1137 p->bTotalsValid = 0;
1138 return sqlite3Fts5IndexRollback(p->pIndex);
1141 int sqlite3Fts5StorageConfigValue(
1142 Fts5Storage *p,
1143 const char *z,
1144 sqlite3_value *pVal,
1145 int iVal
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);
1156 sqlite3_step(pReplace);
1157 rc = sqlite3_reset(pReplace);
1158 sqlite3_bind_null(pReplace, 1);
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;
1167 return rc;