4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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 ******************************************************************************
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"
32 #if FTS5_STMT_SCAN_DESC!=1
33 # error "FTS5_STMT_SCAN_DESC mismatch"
35 #if FTS5_STMT_LOOKUP!=2
36 # error "FTS5_STMT_LOOKUP mismatch"
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
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) */
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
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(?,?%s)", /* REPLACE_DOCSIZE */
81 "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
83 "SELECT sz%s 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
;
93 zSql
= sqlite3_mprintf(azStmt
[eStmt
],
94 pC
->zContentExprlist
, pC
->zContent
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
,
106 case FTS5_STMT_LOOKUP
:
107 zSql
= sqlite3_mprintf(azStmt
[eStmt
],
108 pC
->zContentExprlist
, pC
->zContent
, pC
->zContentRowid
112 case FTS5_STMT_INSERT_CONTENT
:
113 case FTS5_STMT_REPLACE_CONTENT
: {
114 int nCol
= pC
->nCol
+ 1;
118 zBind
= sqlite3_malloc64(1 + nCol
*2);
120 for(i
=0; i
<nCol
; i
++){
122 zBind
[i
*2 + 1] = ',';
125 zSql
= sqlite3_mprintf(azStmt
[eStmt
], pC
->zDb
, pC
->zName
, zBind
);
131 case FTS5_STMT_REPLACE_DOCSIZE
:
132 zSql
= sqlite3_mprintf(azStmt
[eStmt
], pC
->zDb
, pC
->zName
,
133 (pC
->bContentlessDelete
? ",?" : "")
137 case FTS5_STMT_LOOKUP_DOCSIZE
:
138 zSql
= sqlite3_mprintf(azStmt
[eStmt
],
139 (pC
->bContentlessDelete
? ",origin" : ""),
145 zSql
= sqlite3_mprintf(azStmt
[eStmt
], pC
->zDb
, pC
->zName
);
152 int f
= SQLITE_PREPARE_PERSISTENT
;
153 if( eStmt
>FTS5_STMT_LOOKUP
) f
|= SQLITE_PREPARE_NO_VTAB
;
155 rc
= sqlite3_prepare_v3(pC
->db
, zSql
, -1, f
, &p
->aStmt
[eStmt
], 0);
158 if( rc
!=SQLITE_OK
&& pzErrMsg
){
159 *pzErrMsg
= sqlite3_mprintf("%s", sqlite3_errmsg(pC
->db
));
164 *ppStmt
= p
->aStmt
[eStmt
];
165 sqlite3_reset(*ppStmt
);
170 static int fts5ExecPrintf(
177 va_list ap
; /* ... printf arguments */
180 va_start(ap
, zFormat
);
181 zSql
= sqlite3_vmprintf(zFormat
, ap
);
186 rc
= sqlite3_exec(db
, zSql
, 0, 0, pzErr
);
195 ** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
198 int sqlite3Fts5DropAll(Fts5Config
*pConfig
){
199 int rc
= fts5ExecPrintf(pConfig
->db
, 0,
200 "DROP TABLE IF EXISTS %Q.'%q_data';"
201 "DROP TABLE IF EXISTS %Q.'%q_idx';"
202 "DROP TABLE IF EXISTS %Q.'%q_config';",
203 pConfig
->zDb
, pConfig
->zName
,
204 pConfig
->zDb
, pConfig
->zName
,
205 pConfig
->zDb
, pConfig
->zName
207 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
208 rc
= fts5ExecPrintf(pConfig
->db
, 0,
209 "DROP TABLE IF EXISTS %Q.'%q_docsize';",
210 pConfig
->zDb
, pConfig
->zName
213 if( rc
==SQLITE_OK
&& pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
214 rc
= fts5ExecPrintf(pConfig
->db
, 0,
215 "DROP TABLE IF EXISTS %Q.'%q_content';",
216 pConfig
->zDb
, pConfig
->zName
222 static void fts5StorageRenameOne(
223 Fts5Config
*pConfig
, /* Current FTS5 configuration */
224 int *pRc
, /* IN/OUT: Error code */
225 const char *zTail
, /* Tail of table name e.g. "data", "config" */
226 const char *zName
/* New name of FTS5 table */
228 if( *pRc
==SQLITE_OK
){
229 *pRc
= fts5ExecPrintf(pConfig
->db
, 0,
230 "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
231 pConfig
->zDb
, pConfig
->zName
, zTail
, zName
, zTail
236 int sqlite3Fts5StorageRename(Fts5Storage
*pStorage
, const char *zName
){
237 Fts5Config
*pConfig
= pStorage
->pConfig
;
238 int rc
= sqlite3Fts5StorageSync(pStorage
);
240 fts5StorageRenameOne(pConfig
, &rc
, "data", zName
);
241 fts5StorageRenameOne(pConfig
, &rc
, "idx", zName
);
242 fts5StorageRenameOne(pConfig
, &rc
, "config", zName
);
243 if( pConfig
->bColumnsize
){
244 fts5StorageRenameOne(pConfig
, &rc
, "docsize", zName
);
246 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
247 fts5StorageRenameOne(pConfig
, &rc
, "content", zName
);
253 ** Create the shadow table named zPost, with definition zDefn. Return
254 ** SQLITE_OK if successful, or an SQLite error code otherwise.
256 int sqlite3Fts5CreateTable(
257 Fts5Config
*pConfig
, /* FTS5 configuration */
258 const char *zPost
, /* Shadow table to create (e.g. "content") */
259 const char *zDefn
, /* Columns etc. for shadow table */
260 int bWithout
, /* True for without rowid */
261 char **pzErr
/* OUT: Error message */
266 rc
= fts5ExecPrintf(pConfig
->db
, &zErr
, "CREATE TABLE %Q.'%q_%q'(%s)%s",
267 pConfig
->zDb
, pConfig
->zName
, zPost
, zDefn
,
268 #ifndef SQLITE_FTS5_NO_WITHOUT_ROWID
269 bWithout
?" WITHOUT ROWID":
274 *pzErr
= sqlite3_mprintf(
275 "fts5: error creating shadow table %q_%s: %s",
276 pConfig
->zName
, zPost
, zErr
285 ** Open a new Fts5Index handle. If the bCreate argument is true, create
286 ** and initialize the underlying tables
288 ** If successful, set *pp to point to the new object and return SQLITE_OK.
289 ** Otherwise, set *pp to NULL and return an SQLite error code.
291 int sqlite3Fts5StorageOpen(
296 char **pzErr
/* OUT: Error message */
299 Fts5Storage
*p
; /* New object */
300 sqlite3_int64 nByte
; /* Bytes of space to allocate */
302 nByte
= sizeof(Fts5Storage
) /* Fts5Storage object */
303 + pConfig
->nCol
* sizeof(i64
); /* Fts5Storage.aTotalSize[] */
304 *pp
= p
= (Fts5Storage
*)sqlite3_malloc64(nByte
);
305 if( !p
) return SQLITE_NOMEM
;
307 memset(p
, 0, (size_t)nByte
);
308 p
->aTotalSize
= (i64
*)&p
[1];
309 p
->pConfig
= pConfig
;
313 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
314 int nDefn
= 32 + pConfig
->nCol
*10;
315 char *zDefn
= sqlite3_malloc64(32 + (sqlite3_int64
)pConfig
->nCol
* 10);
321 sqlite3_snprintf(nDefn
, zDefn
, "id INTEGER PRIMARY KEY");
322 iOff
= (int)strlen(zDefn
);
323 for(i
=0; i
<pConfig
->nCol
; i
++){
324 sqlite3_snprintf(nDefn
-iOff
, &zDefn
[iOff
], ", c%d", i
);
325 iOff
+= (int)strlen(&zDefn
[iOff
]);
327 rc
= sqlite3Fts5CreateTable(pConfig
, "content", zDefn
, 0, pzErr
);
332 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
333 const char *zCols
= "id INTEGER PRIMARY KEY, sz BLOB";
334 if( pConfig
->bContentlessDelete
){
335 zCols
= "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER";
337 rc
= sqlite3Fts5CreateTable(pConfig
, "docsize", zCols
, 0, pzErr
);
340 rc
= sqlite3Fts5CreateTable(
341 pConfig
, "config", "k PRIMARY KEY, v", 1, pzErr
345 rc
= sqlite3Fts5StorageConfigValue(p
, "version", 0, FTS5_CURRENT_VERSION
);
350 sqlite3Fts5StorageClose(p
);
357 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
359 int sqlite3Fts5StorageClose(Fts5Storage
*p
){
364 /* Finalize all SQL statements */
365 for(i
=0; i
<ArraySize(p
->aStmt
); i
++){
366 sqlite3_finalize(p
->aStmt
[i
]);
374 typedef struct Fts5InsertCtx Fts5InsertCtx
;
375 struct Fts5InsertCtx
{
376 Fts5Storage
*pStorage
;
378 int szCol
; /* Size of column value in tokens */
382 ** Tokenization callback used when inserting tokens into the FTS index.
384 static int fts5StorageInsertCallback(
385 void *pContext
, /* Pointer to Fts5InsertCtx object */
387 const char *pToken
, /* Buffer containing token */
388 int nToken
, /* Size of token in bytes */
389 int iUnused1
, /* Start offset of token */
390 int iUnused2
/* End offset of token */
392 Fts5InsertCtx
*pCtx
= (Fts5InsertCtx
*)pContext
;
393 Fts5Index
*pIdx
= pCtx
->pStorage
->pIndex
;
394 UNUSED_PARAM2(iUnused1
, iUnused2
);
395 if( nToken
>FTS5_MAX_TOKEN_SIZE
) nToken
= FTS5_MAX_TOKEN_SIZE
;
396 if( (tflags
& FTS5_TOKEN_COLOCATED
)==0 || pCtx
->szCol
==0 ){
399 return sqlite3Fts5IndexWrite(pIdx
, pCtx
->iCol
, pCtx
->szCol
-1, pToken
, nToken
);
403 ** If a row with rowid iDel is present in the %_content table, add the
404 ** delete-markers to the FTS index necessary to delete it. Do not actually
405 ** remove the %_content row at this time though.
407 static int fts5StorageDeleteFromIndex(
410 sqlite3_value
**apVal
412 Fts5Config
*pConfig
= p
->pConfig
;
413 sqlite3_stmt
*pSeek
= 0; /* SELECT to read row iDel from %_data */
414 int rc
= SQLITE_OK
; /* Return code */
415 int rc2
; /* sqlite3_reset() return code */
420 rc
= fts5StorageGetStmt(p
, FTS5_STMT_LOOKUP
, &pSeek
, 0);
421 if( rc
!=SQLITE_OK
) return rc
;
422 sqlite3_bind_int64(pSeek
, 1, iDel
);
423 if( sqlite3_step(pSeek
)!=SQLITE_ROW
){
424 return sqlite3_reset(pSeek
);
430 for(iCol
=1; rc
==SQLITE_OK
&& iCol
<=pConfig
->nCol
; iCol
++){
431 if( pConfig
->abUnindexed
[iCol
-1]==0 ){
434 assert( pSeek
==0 || apVal
==0 );
435 assert( pSeek
!=0 || apVal
!=0 );
437 zText
= (const char*)sqlite3_column_text(pSeek
, iCol
);
438 nText
= sqlite3_column_bytes(pSeek
, iCol
);
439 }else if( ALWAYS(apVal
) ){
440 zText
= (const char*)sqlite3_value_text(apVal
[iCol
-1]);
441 nText
= sqlite3_value_bytes(apVal
[iCol
-1]);
446 rc
= sqlite3Fts5Tokenize(pConfig
, FTS5_TOKENIZE_DOCUMENT
,
447 zText
, nText
, (void*)&ctx
, fts5StorageInsertCallback
449 p
->aTotalSize
[iCol
-1] -= (i64
)ctx
.szCol
;
450 if( p
->aTotalSize
[iCol
-1]<0 ){
455 if( rc
==SQLITE_OK
&& p
->nTotalRow
<1 ){
461 rc2
= sqlite3_reset(pSeek
);
462 if( rc
==SQLITE_OK
) rc
= rc2
;
467 ** This function is called to process a DELETE on a contentless_delete=1
468 ** table. It adds the tombstone required to delete the entry with rowid
469 ** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs,
470 ** an SQLite error code.
472 static int fts5StorageContentlessDelete(Fts5Storage
*p
, i64 iDel
){
474 sqlite3_stmt
*pLookup
= 0;
477 assert( p
->pConfig
->bContentlessDelete
);
478 assert( p
->pConfig
->eContent
==FTS5_CONTENT_NONE
);
480 /* Look up the origin of the document in the %_docsize table. Store
481 ** this in stack variable iOrigin. */
482 rc
= fts5StorageGetStmt(p
, FTS5_STMT_LOOKUP_DOCSIZE
, &pLookup
, 0);
484 sqlite3_bind_int64(pLookup
, 1, iDel
);
485 if( SQLITE_ROW
==sqlite3_step(pLookup
) ){
486 iOrigin
= sqlite3_column_int64(pLookup
, 1);
488 rc
= sqlite3_reset(pLookup
);
491 if( rc
==SQLITE_OK
&& iOrigin
!=0 ){
492 rc
= sqlite3Fts5IndexContentlessDelete(p
->pIndex
, iOrigin
, iDel
);
499 ** Insert a record into the %_docsize table. Specifically, do:
501 ** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
503 ** If there is no %_docsize table (as happens if the columnsize=0 option
504 ** is specified when the FTS5 table is created), this function is a no-op.
506 static int fts5StorageInsertDocsize(
507 Fts5Storage
*p
, /* Storage module to write to */
508 i64 iRowid
, /* id value */
509 Fts5Buffer
*pBuf
/* sz value */
512 if( p
->pConfig
->bColumnsize
){
513 sqlite3_stmt
*pReplace
= 0;
514 rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_DOCSIZE
, &pReplace
, 0);
516 sqlite3_bind_int64(pReplace
, 1, iRowid
);
517 if( p
->pConfig
->bContentlessDelete
){
519 rc
= sqlite3Fts5IndexGetOrigin(p
->pIndex
, &iOrigin
);
520 sqlite3_bind_int64(pReplace
, 3, iOrigin
);
523 sqlite3_bind_blob(pReplace
, 2, pBuf
->p
, pBuf
->n
, SQLITE_STATIC
);
524 sqlite3_step(pReplace
);
525 rc
= sqlite3_reset(pReplace
);
526 sqlite3_bind_null(pReplace
, 2);
534 ** Load the contents of the "averages" record from disk into the
535 ** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
536 ** argument bCache is true, set the p->bTotalsValid flag to indicate
537 ** that the contents of aTotalSize[] and nTotalRow are valid until
540 ** Return SQLITE_OK if successful, or an SQLite error code if an error
543 static int fts5StorageLoadTotals(Fts5Storage
*p
, int bCache
){
545 if( p
->bTotalsValid
==0 ){
546 rc
= sqlite3Fts5IndexGetAverages(p
->pIndex
, &p
->nTotalRow
, p
->aTotalSize
);
547 p
->bTotalsValid
= bCache
;
553 ** Store the current contents of the p->nTotalRow and p->aTotalSize[]
554 ** variables in the "averages" record on disk.
556 ** Return SQLITE_OK if successful, or an SQLite error code if an error
559 static int fts5StorageSaveTotals(Fts5Storage
*p
){
560 int nCol
= p
->pConfig
->nCol
;
564 memset(&buf
, 0, sizeof(buf
));
566 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, p
->nTotalRow
);
567 for(i
=0; i
<nCol
; i
++){
568 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, p
->aTotalSize
[i
]);
571 rc
= sqlite3Fts5IndexSetAverages(p
->pIndex
, buf
.p
, buf
.n
);
579 ** Remove a row from the FTS table.
581 int sqlite3Fts5StorageDelete(Fts5Storage
*p
, i64 iDel
, sqlite3_value
**apVal
){
582 Fts5Config
*pConfig
= p
->pConfig
;
584 sqlite3_stmt
*pDel
= 0;
586 assert( pConfig
->eContent
!=FTS5_CONTENT_NORMAL
|| apVal
==0 );
587 rc
= fts5StorageLoadTotals(p
, 1);
589 /* Delete the index records */
591 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 1, iDel
);
595 if( p
->pConfig
->bContentlessDelete
){
596 rc
= fts5StorageContentlessDelete(p
, iDel
);
598 rc
= fts5StorageDeleteFromIndex(p
, iDel
, apVal
);
602 /* Delete the %_docsize record */
603 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
604 rc
= fts5StorageGetStmt(p
, FTS5_STMT_DELETE_DOCSIZE
, &pDel
, 0);
606 sqlite3_bind_int64(pDel
, 1, iDel
);
608 rc
= sqlite3_reset(pDel
);
612 /* Delete the %_content record */
613 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
615 rc
= fts5StorageGetStmt(p
, FTS5_STMT_DELETE_CONTENT
, &pDel
, 0);
618 sqlite3_bind_int64(pDel
, 1, iDel
);
620 rc
= sqlite3_reset(pDel
);
628 ** Delete all entries in the FTS5 index.
630 int sqlite3Fts5StorageDeleteAll(Fts5Storage
*p
){
631 Fts5Config
*pConfig
= p
->pConfig
;
636 /* Delete the contents of the %_data and %_docsize tables. */
637 rc
= fts5ExecPrintf(pConfig
->db
, 0,
638 "DELETE FROM %Q.'%q_data';"
639 "DELETE FROM %Q.'%q_idx';",
640 pConfig
->zDb
, pConfig
->zName
,
641 pConfig
->zDb
, pConfig
->zName
643 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
644 rc
= fts5ExecPrintf(pConfig
->db
, 0,
645 "DELETE FROM %Q.'%q_docsize';",
646 pConfig
->zDb
, pConfig
->zName
650 /* Reinitialize the %_data table. This call creates the initial structure
651 ** and averages records. */
653 rc
= sqlite3Fts5IndexReinit(p
->pIndex
);
656 rc
= sqlite3Fts5StorageConfigValue(p
, "version", 0, FTS5_CURRENT_VERSION
);
661 int sqlite3Fts5StorageRebuild(Fts5Storage
*p
){
662 Fts5Buffer buf
= {0,0,0};
663 Fts5Config
*pConfig
= p
->pConfig
;
664 sqlite3_stmt
*pScan
= 0;
668 memset(&ctx
, 0, sizeof(Fts5InsertCtx
));
670 rc
= sqlite3Fts5StorageDeleteAll(p
);
672 rc
= fts5StorageLoadTotals(p
, 1);
676 rc
= fts5StorageGetStmt(p
, FTS5_STMT_SCAN
, &pScan
, pConfig
->pzErrmsg
);
679 while( rc
==SQLITE_OK
&& SQLITE_ROW
==sqlite3_step(pScan
) ){
680 i64 iRowid
= sqlite3_column_int64(pScan
, 0);
682 sqlite3Fts5BufferZero(&buf
);
683 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 0, iRowid
);
684 for(ctx
.iCol
=0; rc
==SQLITE_OK
&& ctx
.iCol
<pConfig
->nCol
; ctx
.iCol
++){
686 if( pConfig
->abUnindexed
[ctx
.iCol
]==0 ){
687 const char *zText
= (const char*)sqlite3_column_text(pScan
, ctx
.iCol
+1);
688 int nText
= sqlite3_column_bytes(pScan
, ctx
.iCol
+1);
689 rc
= sqlite3Fts5Tokenize(pConfig
,
690 FTS5_TOKENIZE_DOCUMENT
,
693 fts5StorageInsertCallback
696 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, ctx
.szCol
);
697 p
->aTotalSize
[ctx
.iCol
] += (i64
)ctx
.szCol
;
702 rc
= fts5StorageInsertDocsize(p
, iRowid
, &buf
);
706 rc2
= sqlite3_reset(pScan
);
707 if( rc
==SQLITE_OK
) rc
= rc2
;
709 /* Write the averages record */
711 rc
= fts5StorageSaveTotals(p
);
716 int sqlite3Fts5StorageOptimize(Fts5Storage
*p
){
717 return sqlite3Fts5IndexOptimize(p
->pIndex
);
720 int sqlite3Fts5StorageMerge(Fts5Storage
*p
, int nMerge
){
721 return sqlite3Fts5IndexMerge(p
->pIndex
, nMerge
);
724 int sqlite3Fts5StorageReset(Fts5Storage
*p
){
725 return sqlite3Fts5IndexReset(p
->pIndex
);
729 ** Allocate a new rowid. This is used for "external content" tables when
730 ** a NULL value is inserted into the rowid column. The new rowid is allocated
731 ** by inserting a dummy row into the %_docsize table. The dummy will be
732 ** overwritten later.
734 ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
735 ** this case the user is required to provide a rowid explicitly.
737 static int fts5StorageNewRowid(Fts5Storage
*p
, i64
*piRowid
){
738 int rc
= SQLITE_MISMATCH
;
739 if( p
->pConfig
->bColumnsize
){
740 sqlite3_stmt
*pReplace
= 0;
741 rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_DOCSIZE
, &pReplace
, 0);
743 sqlite3_bind_null(pReplace
, 1);
744 sqlite3_bind_null(pReplace
, 2);
745 sqlite3_step(pReplace
);
746 rc
= sqlite3_reset(pReplace
);
749 *piRowid
= sqlite3_last_insert_rowid(p
->pConfig
->db
);
756 ** Insert a new row into the FTS content table.
758 int sqlite3Fts5StorageContentInsert(
760 sqlite3_value
**apVal
,
763 Fts5Config
*pConfig
= p
->pConfig
;
766 /* Insert the new row into the %_content table. */
767 if( pConfig
->eContent
!=FTS5_CONTENT_NORMAL
){
768 if( sqlite3_value_type(apVal
[1])==SQLITE_INTEGER
){
769 *piRowid
= sqlite3_value_int64(apVal
[1]);
771 rc
= fts5StorageNewRowid(p
, piRowid
);
774 sqlite3_stmt
*pInsert
= 0; /* Statement to write %_content table */
775 int i
; /* Counter variable */
776 rc
= fts5StorageGetStmt(p
, FTS5_STMT_INSERT_CONTENT
, &pInsert
, 0);
777 for(i
=1; rc
==SQLITE_OK
&& i
<=pConfig
->nCol
+1; i
++){
778 rc
= sqlite3_bind_value(pInsert
, i
, apVal
[i
]);
781 sqlite3_step(pInsert
);
782 rc
= sqlite3_reset(pInsert
);
784 *piRowid
= sqlite3_last_insert_rowid(pConfig
->db
);
791 ** Insert new entries into the FTS index and %_docsize table.
793 int sqlite3Fts5StorageIndexInsert(
795 sqlite3_value
**apVal
,
798 Fts5Config
*pConfig
= p
->pConfig
;
799 int rc
= SQLITE_OK
; /* Return code */
800 Fts5InsertCtx ctx
; /* Tokenization callback context object */
801 Fts5Buffer buf
; /* Buffer used to build up %_docsize blob */
803 memset(&buf
, 0, sizeof(Fts5Buffer
));
805 rc
= fts5StorageLoadTotals(p
, 1);
808 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 0, iRowid
);
810 for(ctx
.iCol
=0; rc
==SQLITE_OK
&& ctx
.iCol
<pConfig
->nCol
; ctx
.iCol
++){
812 if( pConfig
->abUnindexed
[ctx
.iCol
]==0 ){
813 const char *zText
= (const char*)sqlite3_value_text(apVal
[ctx
.iCol
+2]);
814 int nText
= sqlite3_value_bytes(apVal
[ctx
.iCol
+2]);
815 rc
= sqlite3Fts5Tokenize(pConfig
,
816 FTS5_TOKENIZE_DOCUMENT
,
819 fts5StorageInsertCallback
822 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, ctx
.szCol
);
823 p
->aTotalSize
[ctx
.iCol
] += (i64
)ctx
.szCol
;
827 /* Write the %_docsize record */
829 rc
= fts5StorageInsertDocsize(p
, iRowid
, &buf
);
836 static int fts5StorageCount(Fts5Storage
*p
, const char *zSuffix
, i64
*pnRow
){
837 Fts5Config
*pConfig
= p
->pConfig
;
841 zSql
= sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
842 pConfig
->zDb
, pConfig
->zName
, zSuffix
847 sqlite3_stmt
*pCnt
= 0;
848 rc
= sqlite3_prepare_v2(pConfig
->db
, zSql
, -1, &pCnt
, 0);
850 if( SQLITE_ROW
==sqlite3_step(pCnt
) ){
851 *pnRow
= sqlite3_column_int64(pCnt
, 0);
853 rc
= sqlite3_finalize(pCnt
);
862 ** Context object used by sqlite3Fts5StorageIntegrity().
864 typedef struct Fts5IntegrityCtx Fts5IntegrityCtx
;
865 struct Fts5IntegrityCtx
{
870 Fts5Termset
*pTermset
;
876 ** Tokenization callback used by integrity check.
878 static int fts5StorageIntegrityCallback(
879 void *pContext
, /* Pointer to Fts5IntegrityCtx object */
881 const char *pToken
, /* Buffer containing token */
882 int nToken
, /* Size of token in bytes */
883 int iUnused1
, /* Start offset of token */
884 int iUnused2
/* End offset of token */
886 Fts5IntegrityCtx
*pCtx
= (Fts5IntegrityCtx
*)pContext
;
887 Fts5Termset
*pTermset
= pCtx
->pTermset
;
894 UNUSED_PARAM2(iUnused1
, iUnused2
);
895 if( nToken
>FTS5_MAX_TOKEN_SIZE
) nToken
= FTS5_MAX_TOKEN_SIZE
;
897 if( (tflags
& FTS5_TOKEN_COLOCATED
)==0 || pCtx
->szCol
==0 ){
901 switch( pCtx
->pConfig
->eDetail
){
902 case FTS5_DETAIL_FULL
:
903 iPos
= pCtx
->szCol
-1;
907 case FTS5_DETAIL_COLUMNS
:
913 assert( pCtx
->pConfig
->eDetail
==FTS5_DETAIL_NONE
);
919 rc
= sqlite3Fts5TermsetAdd(pTermset
, 0, pToken
, nToken
, &bPresent
);
920 if( rc
==SQLITE_OK
&& bPresent
==0 ){
921 pCtx
->cksum
^= sqlite3Fts5IndexEntryCksum(
922 pCtx
->iRowid
, iCol
, iPos
, 0, pToken
, nToken
926 for(ii
=0; rc
==SQLITE_OK
&& ii
<pCtx
->pConfig
->nPrefix
; ii
++){
927 const int nChar
= pCtx
->pConfig
->aPrefix
[ii
];
928 int nByte
= sqlite3Fts5IndexCharlenToBytelen(pToken
, nToken
, nChar
);
930 rc
= sqlite3Fts5TermsetAdd(pTermset
, ii
+1, pToken
, nByte
, &bPresent
);
932 pCtx
->cksum
^= sqlite3Fts5IndexEntryCksum(
933 pCtx
->iRowid
, iCol
, iPos
, ii
+1, pToken
, nByte
943 ** Check that the contents of the FTS index match that of the %_content
944 ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
945 ** some other SQLite error code if an error occurs while attempting to
948 int sqlite3Fts5StorageIntegrity(Fts5Storage
*p
, int iArg
){
949 Fts5Config
*pConfig
= p
->pConfig
;
950 int rc
= SQLITE_OK
; /* Return code */
951 int *aColSize
; /* Array of size pConfig->nCol */
952 i64
*aTotalSize
; /* Array of size pConfig->nCol */
953 Fts5IntegrityCtx ctx
;
957 memset(&ctx
, 0, sizeof(Fts5IntegrityCtx
));
958 ctx
.pConfig
= p
->pConfig
;
959 aTotalSize
= (i64
*)sqlite3_malloc64(pConfig
->nCol
*(sizeof(int)+sizeof(i64
)));
960 if( !aTotalSize
) return SQLITE_NOMEM
;
961 aColSize
= (int*)&aTotalSize
[pConfig
->nCol
];
962 memset(aTotalSize
, 0, sizeof(i64
) * pConfig
->nCol
);
964 bUseCksum
= (pConfig
->eContent
==FTS5_CONTENT_NORMAL
965 || (pConfig
->eContent
==FTS5_CONTENT_EXTERNAL
&& iArg
)
968 /* Generate the expected index checksum based on the contents of the
969 ** %_content table. This block stores the checksum in ctx.cksum. */
970 rc
= fts5StorageGetStmt(p
, FTS5_STMT_SCAN
, &pScan
, 0);
973 while( SQLITE_ROW
==sqlite3_step(pScan
) ){
975 ctx
.iRowid
= sqlite3_column_int64(pScan
, 0);
977 if( pConfig
->bColumnsize
){
978 rc
= sqlite3Fts5StorageDocsize(p
, ctx
.iRowid
, aColSize
);
980 if( rc
==SQLITE_OK
&& pConfig
->eDetail
==FTS5_DETAIL_NONE
){
981 rc
= sqlite3Fts5TermsetNew(&ctx
.pTermset
);
983 for(i
=0; rc
==SQLITE_OK
&& i
<pConfig
->nCol
; i
++){
984 if( pConfig
->abUnindexed
[i
] ) continue;
987 if( pConfig
->eDetail
==FTS5_DETAIL_COLUMNS
){
988 rc
= sqlite3Fts5TermsetNew(&ctx
.pTermset
);
991 const char *zText
= (const char*)sqlite3_column_text(pScan
, i
+1);
992 int nText
= sqlite3_column_bytes(pScan
, i
+1);
993 rc
= sqlite3Fts5Tokenize(pConfig
,
994 FTS5_TOKENIZE_DOCUMENT
,
997 fts5StorageIntegrityCallback
1000 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
&& ctx
.szCol
!=aColSize
[i
] ){
1003 aTotalSize
[i
] += ctx
.szCol
;
1004 if( pConfig
->eDetail
==FTS5_DETAIL_COLUMNS
){
1005 sqlite3Fts5TermsetFree(ctx
.pTermset
);
1009 sqlite3Fts5TermsetFree(ctx
.pTermset
);
1012 if( rc
!=SQLITE_OK
) break;
1014 rc2
= sqlite3_reset(pScan
);
1015 if( rc
==SQLITE_OK
) rc
= rc2
;
1018 /* Test that the "totals" (sometimes called "averages") record looks Ok */
1019 if( rc
==SQLITE_OK
){
1021 rc
= fts5StorageLoadTotals(p
, 0);
1022 for(i
=0; rc
==SQLITE_OK
&& i
<pConfig
->nCol
; i
++){
1023 if( p
->aTotalSize
[i
]!=aTotalSize
[i
] ) rc
= FTS5_CORRUPT
;
1027 /* Check that the %_docsize and %_content tables contain the expected
1028 ** number of rows. */
1029 if( rc
==SQLITE_OK
&& pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
1031 rc
= fts5StorageCount(p
, "content", &nRow
);
1032 if( rc
==SQLITE_OK
&& nRow
!=p
->nTotalRow
) rc
= FTS5_CORRUPT
;
1034 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
1036 rc
= fts5StorageCount(p
, "docsize", &nRow
);
1037 if( rc
==SQLITE_OK
&& nRow
!=p
->nTotalRow
) rc
= FTS5_CORRUPT
;
1041 /* Pass the expected checksum down to the FTS index module. It will
1042 ** verify, amongst other things, that it matches the checksum generated by
1043 ** inspecting the index itself. */
1044 if( rc
==SQLITE_OK
){
1045 rc
= sqlite3Fts5IndexIntegrityCheck(p
->pIndex
, ctx
.cksum
, bUseCksum
);
1048 sqlite3_free(aTotalSize
);
1053 ** Obtain an SQLite statement handle that may be used to read data from the
1056 int sqlite3Fts5StorageStmt(
1063 assert( eStmt
==FTS5_STMT_SCAN_ASC
1064 || eStmt
==FTS5_STMT_SCAN_DESC
1065 || eStmt
==FTS5_STMT_LOOKUP
1067 rc
= fts5StorageGetStmt(p
, eStmt
, pp
, pzErrMsg
);
1068 if( rc
==SQLITE_OK
){
1069 assert( p
->aStmt
[eStmt
]==*pp
);
1070 p
->aStmt
[eStmt
] = 0;
1076 ** Release an SQLite statement handle obtained via an earlier call to
1077 ** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
1078 ** must match that passed to the sqlite3Fts5StorageStmt() call.
1080 void sqlite3Fts5StorageStmtRelease(
1085 assert( eStmt
==FTS5_STMT_SCAN_ASC
1086 || eStmt
==FTS5_STMT_SCAN_DESC
1087 || eStmt
==FTS5_STMT_LOOKUP
1089 if( p
->aStmt
[eStmt
]==0 ){
1090 sqlite3_reset(pStmt
);
1091 p
->aStmt
[eStmt
] = pStmt
;
1093 sqlite3_finalize(pStmt
);
1097 static int fts5StorageDecodeSizeArray(
1098 int *aCol
, int nCol
, /* Array to populate */
1099 const u8
*aBlob
, int nBlob
/* Record to read varints from */
1103 for(i
=0; i
<nCol
; i
++){
1104 if( iOff
>=nBlob
) return 1;
1105 iOff
+= fts5GetVarint32(&aBlob
[iOff
], aCol
[i
]);
1107 return (iOff
!=nBlob
);
1111 ** Argument aCol points to an array of integers containing one entry for
1112 ** each table column. This function reads the %_docsize record for the
1113 ** specified rowid and populates aCol[] with the results.
1115 ** An SQLite error code is returned if an error occurs, or SQLITE_OK
1118 int sqlite3Fts5StorageDocsize(Fts5Storage
*p
, i64 iRowid
, int *aCol
){
1119 int nCol
= p
->pConfig
->nCol
; /* Number of user columns in table */
1120 sqlite3_stmt
*pLookup
= 0; /* Statement to query %_docsize */
1121 int rc
; /* Return Code */
1123 assert( p
->pConfig
->bColumnsize
);
1124 rc
= fts5StorageGetStmt(p
, FTS5_STMT_LOOKUP_DOCSIZE
, &pLookup
, 0);
1127 assert( rc
==SQLITE_OK
);
1128 sqlite3_bind_int64(pLookup
, 1, iRowid
);
1129 if( SQLITE_ROW
==sqlite3_step(pLookup
) ){
1130 const u8
*aBlob
= sqlite3_column_blob(pLookup
, 0);
1131 int nBlob
= sqlite3_column_bytes(pLookup
, 0);
1132 if( 0==fts5StorageDecodeSizeArray(aCol
, nCol
, aBlob
, nBlob
) ){
1136 rc
= sqlite3_reset(pLookup
);
1137 if( bCorrupt
&& rc
==SQLITE_OK
){
1141 assert( rc
!=SQLITE_OK
);
1147 int sqlite3Fts5StorageSize(Fts5Storage
*p
, int iCol
, i64
*pnToken
){
1148 int rc
= fts5StorageLoadTotals(p
, 0);
1149 if( rc
==SQLITE_OK
){
1153 for(i
=0; i
<p
->pConfig
->nCol
; i
++){
1154 *pnToken
+= p
->aTotalSize
[i
];
1156 }else if( iCol
<p
->pConfig
->nCol
){
1157 *pnToken
= p
->aTotalSize
[iCol
];
1165 int sqlite3Fts5StorageRowCount(Fts5Storage
*p
, i64
*pnRow
){
1166 int rc
= fts5StorageLoadTotals(p
, 0);
1167 if( rc
==SQLITE_OK
){
1168 /* nTotalRow being zero does not necessarily indicate a corrupt
1169 ** database - it might be that the FTS5 table really does contain zero
1170 ** rows. However this function is only called from the xRowCount() API,
1171 ** and there is no way for that API to be invoked if the table contains
1172 ** no rows. Hence the FTS5_CORRUPT return. */
1173 *pnRow
= p
->nTotalRow
;
1174 if( p
->nTotalRow
<=0 ) rc
= FTS5_CORRUPT
;
1180 ** Flush any data currently held in-memory to disk.
1182 int sqlite3Fts5StorageSync(Fts5Storage
*p
){
1184 i64 iLastRowid
= sqlite3_last_insert_rowid(p
->pConfig
->db
);
1185 if( p
->bTotalsValid
){
1186 rc
= fts5StorageSaveTotals(p
);
1187 if( rc
==SQLITE_OK
){
1188 p
->bTotalsValid
= 0;
1191 if( rc
==SQLITE_OK
){
1192 rc
= sqlite3Fts5IndexSync(p
->pIndex
);
1194 sqlite3_set_last_insert_rowid(p
->pConfig
->db
, iLastRowid
);
1198 int sqlite3Fts5StorageRollback(Fts5Storage
*p
){
1199 p
->bTotalsValid
= 0;
1200 return sqlite3Fts5IndexRollback(p
->pIndex
);
1203 int sqlite3Fts5StorageConfigValue(
1206 sqlite3_value
*pVal
,
1209 sqlite3_stmt
*pReplace
= 0;
1210 int rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_CONFIG
, &pReplace
, 0);
1211 if( rc
==SQLITE_OK
){
1212 sqlite3_bind_text(pReplace
, 1, z
, -1, SQLITE_STATIC
);
1214 sqlite3_bind_value(pReplace
, 2, pVal
);
1216 sqlite3_bind_int(pReplace
, 2, iVal
);
1218 sqlite3_step(pReplace
);
1219 rc
= sqlite3_reset(pReplace
);
1220 sqlite3_bind_null(pReplace
, 1);
1222 if( rc
==SQLITE_OK
&& pVal
){
1223 int iNew
= p
->pConfig
->iCookie
+ 1;
1224 rc
= sqlite3Fts5IndexSetCookie(p
->pIndex
, iNew
);
1225 if( rc
==SQLITE_OK
){
1226 p
->pConfig
->iCookie
= iNew
;