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(?,?)", /* 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
;
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_malloc(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
);
132 zSql
= sqlite3_mprintf(azStmt
[eStmt
], pC
->zDb
, pC
->zName
);
139 rc
= sqlite3_prepare_v3(pC
->db
, zSql
, -1,
140 SQLITE_PREPARE_PERSISTENT
, &p
->aStmt
[eStmt
], 0);
142 if( rc
!=SQLITE_OK
&& pzErrMsg
){
143 *pzErrMsg
= sqlite3_mprintf("%s", sqlite3_errmsg(pC
->db
));
148 *ppStmt
= p
->aStmt
[eStmt
];
149 sqlite3_reset(*ppStmt
);
154 static int fts5ExecPrintf(
161 va_list ap
; /* ... printf arguments */
164 va_start(ap
, zFormat
);
165 zSql
= sqlite3_vmprintf(zFormat
, ap
);
170 rc
= sqlite3_exec(db
, zSql
, 0, 0, pzErr
);
179 ** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
182 int sqlite3Fts5DropAll(Fts5Config
*pConfig
){
183 int rc
= fts5ExecPrintf(pConfig
->db
, 0,
184 "DROP TABLE IF EXISTS %Q.'%q_data';"
185 "DROP TABLE IF EXISTS %Q.'%q_idx';"
186 "DROP TABLE IF EXISTS %Q.'%q_config';",
187 pConfig
->zDb
, pConfig
->zName
,
188 pConfig
->zDb
, pConfig
->zName
,
189 pConfig
->zDb
, pConfig
->zName
191 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
192 rc
= fts5ExecPrintf(pConfig
->db
, 0,
193 "DROP TABLE IF EXISTS %Q.'%q_docsize';",
194 pConfig
->zDb
, pConfig
->zName
197 if( rc
==SQLITE_OK
&& pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
198 rc
= fts5ExecPrintf(pConfig
->db
, 0,
199 "DROP TABLE IF EXISTS %Q.'%q_content';",
200 pConfig
->zDb
, pConfig
->zName
206 static void fts5StorageRenameOne(
207 Fts5Config
*pConfig
, /* Current FTS5 configuration */
208 int *pRc
, /* IN/OUT: Error code */
209 const char *zTail
, /* Tail of table name e.g. "data", "config" */
210 const char *zName
/* New name of FTS5 table */
212 if( *pRc
==SQLITE_OK
){
213 *pRc
= fts5ExecPrintf(pConfig
->db
, 0,
214 "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
215 pConfig
->zDb
, pConfig
->zName
, zTail
, zName
, zTail
220 int sqlite3Fts5StorageRename(Fts5Storage
*pStorage
, const char *zName
){
221 Fts5Config
*pConfig
= pStorage
->pConfig
;
222 int rc
= sqlite3Fts5StorageSync(pStorage
);
224 fts5StorageRenameOne(pConfig
, &rc
, "data", zName
);
225 fts5StorageRenameOne(pConfig
, &rc
, "idx", zName
);
226 fts5StorageRenameOne(pConfig
, &rc
, "config", zName
);
227 if( pConfig
->bColumnsize
){
228 fts5StorageRenameOne(pConfig
, &rc
, "docsize", zName
);
230 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
231 fts5StorageRenameOne(pConfig
, &rc
, "content", zName
);
237 ** Create the shadow table named zPost, with definition zDefn. Return
238 ** SQLITE_OK if successful, or an SQLite error code otherwise.
240 int sqlite3Fts5CreateTable(
241 Fts5Config
*pConfig
, /* FTS5 configuration */
242 const char *zPost
, /* Shadow table to create (e.g. "content") */
243 const char *zDefn
, /* Columns etc. for shadow table */
244 int bWithout
, /* True for without rowid */
245 char **pzErr
/* OUT: Error message */
250 rc
= fts5ExecPrintf(pConfig
->db
, &zErr
, "CREATE TABLE %Q.'%q_%q'(%s)%s",
251 pConfig
->zDb
, pConfig
->zName
, zPost
, zDefn
,
252 #ifndef SQLITE_FTS5_NO_WITHOUT_ROWID
253 bWithout
?" WITHOUT ROWID":
258 *pzErr
= sqlite3_mprintf(
259 "fts5: error creating shadow table %q_%s: %s",
260 pConfig
->zName
, zPost
, zErr
269 ** Open a new Fts5Index handle. If the bCreate argument is true, create
270 ** and initialize the underlying tables
272 ** If successful, set *pp to point to the new object and return SQLITE_OK.
273 ** Otherwise, set *pp to NULL and return an SQLite error code.
275 int sqlite3Fts5StorageOpen(
280 char **pzErr
/* OUT: Error message */
283 Fts5Storage
*p
; /* New object */
284 int nByte
; /* Bytes of space to allocate */
286 nByte
= sizeof(Fts5Storage
) /* Fts5Storage object */
287 + pConfig
->nCol
* sizeof(i64
); /* Fts5Storage.aTotalSize[] */
288 *pp
= p
= (Fts5Storage
*)sqlite3_malloc(nByte
);
289 if( !p
) return SQLITE_NOMEM
;
292 p
->aTotalSize
= (i64
*)&p
[1];
293 p
->pConfig
= pConfig
;
297 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
298 int nDefn
= 32 + pConfig
->nCol
*10;
299 char *zDefn
= sqlite3_malloc(32 + pConfig
->nCol
* 10);
305 sqlite3_snprintf(nDefn
, zDefn
, "id INTEGER PRIMARY KEY");
306 iOff
= (int)strlen(zDefn
);
307 for(i
=0; i
<pConfig
->nCol
; i
++){
308 sqlite3_snprintf(nDefn
-iOff
, &zDefn
[iOff
], ", c%d", i
);
309 iOff
+= (int)strlen(&zDefn
[iOff
]);
311 rc
= sqlite3Fts5CreateTable(pConfig
, "content", zDefn
, 0, pzErr
);
316 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
317 rc
= sqlite3Fts5CreateTable(
318 pConfig
, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
322 rc
= sqlite3Fts5CreateTable(
323 pConfig
, "config", "k PRIMARY KEY, v", 1, pzErr
327 rc
= sqlite3Fts5StorageConfigValue(p
, "version", 0, FTS5_CURRENT_VERSION
);
332 sqlite3Fts5StorageClose(p
);
339 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
341 int sqlite3Fts5StorageClose(Fts5Storage
*p
){
346 /* Finalize all SQL statements */
347 for(i
=0; i
<ArraySize(p
->aStmt
); i
++){
348 sqlite3_finalize(p
->aStmt
[i
]);
356 typedef struct Fts5InsertCtx Fts5InsertCtx
;
357 struct Fts5InsertCtx
{
358 Fts5Storage
*pStorage
;
360 int szCol
; /* Size of column value in tokens */
364 ** Tokenization callback used when inserting tokens into the FTS index.
366 static int fts5StorageInsertCallback(
367 void *pContext
, /* Pointer to Fts5InsertCtx object */
369 const char *pToken
, /* Buffer containing token */
370 int nToken
, /* Size of token in bytes */
371 int iUnused1
, /* Start offset of token */
372 int iUnused2
/* End offset of token */
374 Fts5InsertCtx
*pCtx
= (Fts5InsertCtx
*)pContext
;
375 Fts5Index
*pIdx
= pCtx
->pStorage
->pIndex
;
376 UNUSED_PARAM2(iUnused1
, iUnused2
);
377 if( nToken
>FTS5_MAX_TOKEN_SIZE
) nToken
= FTS5_MAX_TOKEN_SIZE
;
378 if( (tflags
& FTS5_TOKEN_COLOCATED
)==0 || pCtx
->szCol
==0 ){
381 return sqlite3Fts5IndexWrite(pIdx
, pCtx
->iCol
, pCtx
->szCol
-1, pToken
, nToken
);
385 ** If a row with rowid iDel is present in the %_content table, add the
386 ** delete-markers to the FTS index necessary to delete it. Do not actually
387 ** remove the %_content row at this time though.
389 static int fts5StorageDeleteFromIndex(
392 sqlite3_value
**apVal
394 Fts5Config
*pConfig
= p
->pConfig
;
395 sqlite3_stmt
*pSeek
= 0; /* SELECT to read row iDel from %_data */
396 int rc
; /* Return code */
397 int rc2
; /* sqlite3_reset() return code */
402 rc
= fts5StorageGetStmt(p
, FTS5_STMT_LOOKUP
, &pSeek
, 0);
403 if( rc
!=SQLITE_OK
) return rc
;
404 sqlite3_bind_int64(pSeek
, 1, iDel
);
405 if( sqlite3_step(pSeek
)!=SQLITE_ROW
){
406 return sqlite3_reset(pSeek
);
412 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 1, iDel
);
413 for(iCol
=1; rc
==SQLITE_OK
&& iCol
<=pConfig
->nCol
; iCol
++){
414 if( pConfig
->abUnindexed
[iCol
-1]==0 ){
418 zText
= (const char*)sqlite3_column_text(pSeek
, iCol
);
419 nText
= sqlite3_column_bytes(pSeek
, iCol
);
421 zText
= (const char*)sqlite3_value_text(apVal
[iCol
-1]);
422 nText
= sqlite3_value_bytes(apVal
[iCol
-1]);
425 rc
= sqlite3Fts5Tokenize(pConfig
, FTS5_TOKENIZE_DOCUMENT
,
426 zText
, nText
, (void*)&ctx
, fts5StorageInsertCallback
428 p
->aTotalSize
[iCol
-1] -= (i64
)ctx
.szCol
;
433 rc2
= sqlite3_reset(pSeek
);
434 if( rc
==SQLITE_OK
) rc
= rc2
;
440 ** Insert a record into the %_docsize table. Specifically, do:
442 ** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
444 ** If there is no %_docsize table (as happens if the columnsize=0 option
445 ** is specified when the FTS5 table is created), this function is a no-op.
447 static int fts5StorageInsertDocsize(
448 Fts5Storage
*p
, /* Storage module to write to */
449 i64 iRowid
, /* id value */
450 Fts5Buffer
*pBuf
/* sz value */
453 if( p
->pConfig
->bColumnsize
){
454 sqlite3_stmt
*pReplace
= 0;
455 rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_DOCSIZE
, &pReplace
, 0);
457 sqlite3_bind_int64(pReplace
, 1, iRowid
);
458 sqlite3_bind_blob(pReplace
, 2, pBuf
->p
, pBuf
->n
, SQLITE_STATIC
);
459 sqlite3_step(pReplace
);
460 rc
= sqlite3_reset(pReplace
);
467 ** Load the contents of the "averages" record from disk into the
468 ** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
469 ** argument bCache is true, set the p->bTotalsValid flag to indicate
470 ** that the contents of aTotalSize[] and nTotalRow are valid until
473 ** Return SQLITE_OK if successful, or an SQLite error code if an error
476 static int fts5StorageLoadTotals(Fts5Storage
*p
, int bCache
){
478 if( p
->bTotalsValid
==0 ){
479 rc
= sqlite3Fts5IndexGetAverages(p
->pIndex
, &p
->nTotalRow
, p
->aTotalSize
);
480 p
->bTotalsValid
= bCache
;
486 ** Store the current contents of the p->nTotalRow and p->aTotalSize[]
487 ** variables in the "averages" record on disk.
489 ** Return SQLITE_OK if successful, or an SQLite error code if an error
492 static int fts5StorageSaveTotals(Fts5Storage
*p
){
493 int nCol
= p
->pConfig
->nCol
;
497 memset(&buf
, 0, sizeof(buf
));
499 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, p
->nTotalRow
);
500 for(i
=0; i
<nCol
; i
++){
501 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, p
->aTotalSize
[i
]);
504 rc
= sqlite3Fts5IndexSetAverages(p
->pIndex
, buf
.p
, buf
.n
);
512 ** Remove a row from the FTS table.
514 int sqlite3Fts5StorageDelete(Fts5Storage
*p
, i64 iDel
, sqlite3_value
**apVal
){
515 Fts5Config
*pConfig
= p
->pConfig
;
517 sqlite3_stmt
*pDel
= 0;
519 assert( pConfig
->eContent
!=FTS5_CONTENT_NORMAL
|| apVal
==0 );
520 rc
= fts5StorageLoadTotals(p
, 1);
522 /* Delete the index records */
524 rc
= fts5StorageDeleteFromIndex(p
, iDel
, apVal
);
527 /* Delete the %_docsize record */
528 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
529 rc
= fts5StorageGetStmt(p
, FTS5_STMT_DELETE_DOCSIZE
, &pDel
, 0);
531 sqlite3_bind_int64(pDel
, 1, iDel
);
533 rc
= sqlite3_reset(pDel
);
537 /* Delete the %_content record */
538 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
540 rc
= fts5StorageGetStmt(p
, FTS5_STMT_DELETE_CONTENT
, &pDel
, 0);
543 sqlite3_bind_int64(pDel
, 1, iDel
);
545 rc
= sqlite3_reset(pDel
);
553 ** Delete all entries in the FTS5 index.
555 int sqlite3Fts5StorageDeleteAll(Fts5Storage
*p
){
556 Fts5Config
*pConfig
= p
->pConfig
;
559 /* Delete the contents of the %_data and %_docsize tables. */
560 rc
= fts5ExecPrintf(pConfig
->db
, 0,
561 "DELETE FROM %Q.'%q_data';"
562 "DELETE FROM %Q.'%q_idx';",
563 pConfig
->zDb
, pConfig
->zName
,
564 pConfig
->zDb
, pConfig
->zName
566 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
567 rc
= fts5ExecPrintf(pConfig
->db
, 0,
568 "DELETE FROM %Q.'%q_docsize';",
569 pConfig
->zDb
, pConfig
->zName
573 /* Reinitialize the %_data table. This call creates the initial structure
574 ** and averages records. */
576 rc
= sqlite3Fts5IndexReinit(p
->pIndex
);
579 rc
= sqlite3Fts5StorageConfigValue(p
, "version", 0, FTS5_CURRENT_VERSION
);
584 int sqlite3Fts5StorageRebuild(Fts5Storage
*p
){
585 Fts5Buffer buf
= {0,0,0};
586 Fts5Config
*pConfig
= p
->pConfig
;
587 sqlite3_stmt
*pScan
= 0;
591 memset(&ctx
, 0, sizeof(Fts5InsertCtx
));
593 rc
= sqlite3Fts5StorageDeleteAll(p
);
595 rc
= fts5StorageLoadTotals(p
, 1);
599 rc
= fts5StorageGetStmt(p
, FTS5_STMT_SCAN
, &pScan
, 0);
602 while( rc
==SQLITE_OK
&& SQLITE_ROW
==sqlite3_step(pScan
) ){
603 i64 iRowid
= sqlite3_column_int64(pScan
, 0);
605 sqlite3Fts5BufferZero(&buf
);
606 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 0, iRowid
);
607 for(ctx
.iCol
=0; rc
==SQLITE_OK
&& ctx
.iCol
<pConfig
->nCol
; ctx
.iCol
++){
609 if( pConfig
->abUnindexed
[ctx
.iCol
]==0 ){
610 rc
= sqlite3Fts5Tokenize(pConfig
,
611 FTS5_TOKENIZE_DOCUMENT
,
612 (const char*)sqlite3_column_text(pScan
, ctx
.iCol
+1),
613 sqlite3_column_bytes(pScan
, ctx
.iCol
+1),
615 fts5StorageInsertCallback
618 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, ctx
.szCol
);
619 p
->aTotalSize
[ctx
.iCol
] += (i64
)ctx
.szCol
;
624 rc
= fts5StorageInsertDocsize(p
, iRowid
, &buf
);
629 /* Write the averages record */
631 rc
= fts5StorageSaveTotals(p
);
636 int sqlite3Fts5StorageOptimize(Fts5Storage
*p
){
637 return sqlite3Fts5IndexOptimize(p
->pIndex
);
640 int sqlite3Fts5StorageMerge(Fts5Storage
*p
, int nMerge
){
641 return sqlite3Fts5IndexMerge(p
->pIndex
, nMerge
);
644 int sqlite3Fts5StorageReset(Fts5Storage
*p
){
645 return sqlite3Fts5IndexReset(p
->pIndex
);
649 ** Allocate a new rowid. This is used for "external content" tables when
650 ** a NULL value is inserted into the rowid column. The new rowid is allocated
651 ** by inserting a dummy row into the %_docsize table. The dummy will be
652 ** overwritten later.
654 ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
655 ** this case the user is required to provide a rowid explicitly.
657 static int fts5StorageNewRowid(Fts5Storage
*p
, i64
*piRowid
){
658 int rc
= SQLITE_MISMATCH
;
659 if( p
->pConfig
->bColumnsize
){
660 sqlite3_stmt
*pReplace
= 0;
661 rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_DOCSIZE
, &pReplace
, 0);
663 sqlite3_bind_null(pReplace
, 1);
664 sqlite3_bind_null(pReplace
, 2);
665 sqlite3_step(pReplace
);
666 rc
= sqlite3_reset(pReplace
);
669 *piRowid
= sqlite3_last_insert_rowid(p
->pConfig
->db
);
676 ** Insert a new row into the FTS content table.
678 int sqlite3Fts5StorageContentInsert(
680 sqlite3_value
**apVal
,
683 Fts5Config
*pConfig
= p
->pConfig
;
686 /* Insert the new row into the %_content table. */
687 if( pConfig
->eContent
!=FTS5_CONTENT_NORMAL
){
688 if( sqlite3_value_type(apVal
[1])==SQLITE_INTEGER
){
689 *piRowid
= sqlite3_value_int64(apVal
[1]);
691 rc
= fts5StorageNewRowid(p
, piRowid
);
694 sqlite3_stmt
*pInsert
= 0; /* Statement to write %_content table */
695 int i
; /* Counter variable */
696 rc
= fts5StorageGetStmt(p
, FTS5_STMT_INSERT_CONTENT
, &pInsert
, 0);
697 for(i
=1; rc
==SQLITE_OK
&& i
<=pConfig
->nCol
+1; i
++){
698 rc
= sqlite3_bind_value(pInsert
, i
, apVal
[i
]);
701 sqlite3_step(pInsert
);
702 rc
= sqlite3_reset(pInsert
);
704 *piRowid
= sqlite3_last_insert_rowid(pConfig
->db
);
711 ** Insert new entries into the FTS index and %_docsize table.
713 int sqlite3Fts5StorageIndexInsert(
715 sqlite3_value
**apVal
,
718 Fts5Config
*pConfig
= p
->pConfig
;
719 int rc
= SQLITE_OK
; /* Return code */
720 Fts5InsertCtx ctx
; /* Tokenization callback context object */
721 Fts5Buffer buf
; /* Buffer used to build up %_docsize blob */
723 memset(&buf
, 0, sizeof(Fts5Buffer
));
725 rc
= fts5StorageLoadTotals(p
, 1);
728 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 0, iRowid
);
730 for(ctx
.iCol
=0; rc
==SQLITE_OK
&& ctx
.iCol
<pConfig
->nCol
; ctx
.iCol
++){
732 if( pConfig
->abUnindexed
[ctx
.iCol
]==0 ){
733 rc
= sqlite3Fts5Tokenize(pConfig
,
734 FTS5_TOKENIZE_DOCUMENT
,
735 (const char*)sqlite3_value_text(apVal
[ctx
.iCol
+2]),
736 sqlite3_value_bytes(apVal
[ctx
.iCol
+2]),
738 fts5StorageInsertCallback
741 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, ctx
.szCol
);
742 p
->aTotalSize
[ctx
.iCol
] += (i64
)ctx
.szCol
;
746 /* Write the %_docsize record */
748 rc
= fts5StorageInsertDocsize(p
, iRowid
, &buf
);
755 static int fts5StorageCount(Fts5Storage
*p
, const char *zSuffix
, i64
*pnRow
){
756 Fts5Config
*pConfig
= p
->pConfig
;
760 zSql
= sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
761 pConfig
->zDb
, pConfig
->zName
, zSuffix
766 sqlite3_stmt
*pCnt
= 0;
767 rc
= sqlite3_prepare_v2(pConfig
->db
, zSql
, -1, &pCnt
, 0);
769 if( SQLITE_ROW
==sqlite3_step(pCnt
) ){
770 *pnRow
= sqlite3_column_int64(pCnt
, 0);
772 rc
= sqlite3_finalize(pCnt
);
781 ** Context object used by sqlite3Fts5StorageIntegrity().
783 typedef struct Fts5IntegrityCtx Fts5IntegrityCtx
;
784 struct Fts5IntegrityCtx
{
789 Fts5Termset
*pTermset
;
795 ** Tokenization callback used by integrity check.
797 static int fts5StorageIntegrityCallback(
798 void *pContext
, /* Pointer to Fts5IntegrityCtx object */
800 const char *pToken
, /* Buffer containing token */
801 int nToken
, /* Size of token in bytes */
802 int iUnused1
, /* Start offset of token */
803 int iUnused2
/* End offset of token */
805 Fts5IntegrityCtx
*pCtx
= (Fts5IntegrityCtx
*)pContext
;
806 Fts5Termset
*pTermset
= pCtx
->pTermset
;
813 UNUSED_PARAM2(iUnused1
, iUnused2
);
814 if( nToken
>FTS5_MAX_TOKEN_SIZE
) nToken
= FTS5_MAX_TOKEN_SIZE
;
816 if( (tflags
& FTS5_TOKEN_COLOCATED
)==0 || pCtx
->szCol
==0 ){
820 switch( pCtx
->pConfig
->eDetail
){
821 case FTS5_DETAIL_FULL
:
822 iPos
= pCtx
->szCol
-1;
826 case FTS5_DETAIL_COLUMNS
:
832 assert( pCtx
->pConfig
->eDetail
==FTS5_DETAIL_NONE
);
838 rc
= sqlite3Fts5TermsetAdd(pTermset
, 0, pToken
, nToken
, &bPresent
);
839 if( rc
==SQLITE_OK
&& bPresent
==0 ){
840 pCtx
->cksum
^= sqlite3Fts5IndexEntryCksum(
841 pCtx
->iRowid
, iCol
, iPos
, 0, pToken
, nToken
845 for(ii
=0; rc
==SQLITE_OK
&& ii
<pCtx
->pConfig
->nPrefix
; ii
++){
846 const int nChar
= pCtx
->pConfig
->aPrefix
[ii
];
847 int nByte
= sqlite3Fts5IndexCharlenToBytelen(pToken
, nToken
, nChar
);
849 rc
= sqlite3Fts5TermsetAdd(pTermset
, ii
+1, pToken
, nByte
, &bPresent
);
851 pCtx
->cksum
^= sqlite3Fts5IndexEntryCksum(
852 pCtx
->iRowid
, iCol
, iPos
, ii
+1, pToken
, nByte
862 ** Check that the contents of the FTS index match that of the %_content
863 ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
864 ** some other SQLite error code if an error occurs while attempting to
867 int sqlite3Fts5StorageIntegrity(Fts5Storage
*p
){
868 Fts5Config
*pConfig
= p
->pConfig
;
869 int rc
; /* Return code */
870 int *aColSize
; /* Array of size pConfig->nCol */
871 i64
*aTotalSize
; /* Array of size pConfig->nCol */
872 Fts5IntegrityCtx ctx
;
875 memset(&ctx
, 0, sizeof(Fts5IntegrityCtx
));
876 ctx
.pConfig
= p
->pConfig
;
877 aTotalSize
= (i64
*)sqlite3_malloc(pConfig
->nCol
* (sizeof(int)+sizeof(i64
)));
878 if( !aTotalSize
) return SQLITE_NOMEM
;
879 aColSize
= (int*)&aTotalSize
[pConfig
->nCol
];
880 memset(aTotalSize
, 0, sizeof(i64
) * pConfig
->nCol
);
882 /* Generate the expected index checksum based on the contents of the
883 ** %_content table. This block stores the checksum in ctx.cksum. */
884 rc
= fts5StorageGetStmt(p
, FTS5_STMT_SCAN
, &pScan
, 0);
887 while( SQLITE_ROW
==sqlite3_step(pScan
) ){
889 ctx
.iRowid
= sqlite3_column_int64(pScan
, 0);
891 if( pConfig
->bColumnsize
){
892 rc
= sqlite3Fts5StorageDocsize(p
, ctx
.iRowid
, aColSize
);
894 if( rc
==SQLITE_OK
&& pConfig
->eDetail
==FTS5_DETAIL_NONE
){
895 rc
= sqlite3Fts5TermsetNew(&ctx
.pTermset
);
897 for(i
=0; rc
==SQLITE_OK
&& i
<pConfig
->nCol
; i
++){
898 if( pConfig
->abUnindexed
[i
] ) continue;
901 if( pConfig
->eDetail
==FTS5_DETAIL_COLUMNS
){
902 rc
= sqlite3Fts5TermsetNew(&ctx
.pTermset
);
905 rc
= sqlite3Fts5Tokenize(pConfig
,
906 FTS5_TOKENIZE_DOCUMENT
,
907 (const char*)sqlite3_column_text(pScan
, i
+1),
908 sqlite3_column_bytes(pScan
, i
+1),
910 fts5StorageIntegrityCallback
913 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
&& ctx
.szCol
!=aColSize
[i
] ){
916 aTotalSize
[i
] += ctx
.szCol
;
917 if( pConfig
->eDetail
==FTS5_DETAIL_COLUMNS
){
918 sqlite3Fts5TermsetFree(ctx
.pTermset
);
922 sqlite3Fts5TermsetFree(ctx
.pTermset
);
925 if( rc
!=SQLITE_OK
) break;
927 rc2
= sqlite3_reset(pScan
);
928 if( rc
==SQLITE_OK
) rc
= rc2
;
931 /* Test that the "totals" (sometimes called "averages") record looks Ok */
934 rc
= fts5StorageLoadTotals(p
, 0);
935 for(i
=0; rc
==SQLITE_OK
&& i
<pConfig
->nCol
; i
++){
936 if( p
->aTotalSize
[i
]!=aTotalSize
[i
] ) rc
= FTS5_CORRUPT
;
940 /* Check that the %_docsize and %_content tables contain the expected
941 ** number of rows. */
942 if( rc
==SQLITE_OK
&& pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
944 rc
= fts5StorageCount(p
, "content", &nRow
);
945 if( rc
==SQLITE_OK
&& nRow
!=p
->nTotalRow
) rc
= FTS5_CORRUPT
;
947 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
949 rc
= fts5StorageCount(p
, "docsize", &nRow
);
950 if( rc
==SQLITE_OK
&& nRow
!=p
->nTotalRow
) rc
= FTS5_CORRUPT
;
953 /* Pass the expected checksum down to the FTS index module. It will
954 ** verify, amongst other things, that it matches the checksum generated by
955 ** inspecting the index itself. */
957 rc
= sqlite3Fts5IndexIntegrityCheck(p
->pIndex
, ctx
.cksum
);
960 sqlite3_free(aTotalSize
);
965 ** Obtain an SQLite statement handle that may be used to read data from the
968 int sqlite3Fts5StorageStmt(
975 assert( eStmt
==FTS5_STMT_SCAN_ASC
976 || eStmt
==FTS5_STMT_SCAN_DESC
977 || eStmt
==FTS5_STMT_LOOKUP
979 rc
= fts5StorageGetStmt(p
, eStmt
, pp
, pzErrMsg
);
981 assert( p
->aStmt
[eStmt
]==*pp
);
988 ** Release an SQLite statement handle obtained via an earlier call to
989 ** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
990 ** must match that passed to the sqlite3Fts5StorageStmt() call.
992 void sqlite3Fts5StorageStmtRelease(
997 assert( eStmt
==FTS5_STMT_SCAN_ASC
998 || eStmt
==FTS5_STMT_SCAN_DESC
999 || eStmt
==FTS5_STMT_LOOKUP
1001 if( p
->aStmt
[eStmt
]==0 ){
1002 sqlite3_reset(pStmt
);
1003 p
->aStmt
[eStmt
] = pStmt
;
1005 sqlite3_finalize(pStmt
);
1009 static int fts5StorageDecodeSizeArray(
1010 int *aCol
, int nCol
, /* Array to populate */
1011 const u8
*aBlob
, int nBlob
/* Record to read varints from */
1015 for(i
=0; i
<nCol
; i
++){
1016 if( iOff
>=nBlob
) return 1;
1017 iOff
+= fts5GetVarint32(&aBlob
[iOff
], aCol
[i
]);
1019 return (iOff
!=nBlob
);
1023 ** Argument aCol points to an array of integers containing one entry for
1024 ** each table column. This function reads the %_docsize record for the
1025 ** specified rowid and populates aCol[] with the results.
1027 ** An SQLite error code is returned if an error occurs, or SQLITE_OK
1030 int sqlite3Fts5StorageDocsize(Fts5Storage
*p
, i64 iRowid
, int *aCol
){
1031 int nCol
= p
->pConfig
->nCol
; /* Number of user columns in table */
1032 sqlite3_stmt
*pLookup
= 0; /* Statement to query %_docsize */
1033 int rc
; /* Return Code */
1035 assert( p
->pConfig
->bColumnsize
);
1036 rc
= fts5StorageGetStmt(p
, FTS5_STMT_LOOKUP_DOCSIZE
, &pLookup
, 0);
1037 if( rc
==SQLITE_OK
){
1039 sqlite3_bind_int64(pLookup
, 1, iRowid
);
1040 if( SQLITE_ROW
==sqlite3_step(pLookup
) ){
1041 const u8
*aBlob
= sqlite3_column_blob(pLookup
, 0);
1042 int nBlob
= sqlite3_column_bytes(pLookup
, 0);
1043 if( 0==fts5StorageDecodeSizeArray(aCol
, nCol
, aBlob
, nBlob
) ){
1047 rc
= sqlite3_reset(pLookup
);
1048 if( bCorrupt
&& rc
==SQLITE_OK
){
1056 int sqlite3Fts5StorageSize(Fts5Storage
*p
, int iCol
, i64
*pnToken
){
1057 int rc
= fts5StorageLoadTotals(p
, 0);
1058 if( rc
==SQLITE_OK
){
1062 for(i
=0; i
<p
->pConfig
->nCol
; i
++){
1063 *pnToken
+= p
->aTotalSize
[i
];
1065 }else if( iCol
<p
->pConfig
->nCol
){
1066 *pnToken
= p
->aTotalSize
[iCol
];
1074 int sqlite3Fts5StorageRowCount(Fts5Storage
*p
, i64
*pnRow
){
1075 int rc
= fts5StorageLoadTotals(p
, 0);
1076 if( rc
==SQLITE_OK
){
1077 *pnRow
= p
->nTotalRow
;
1083 ** Flush any data currently held in-memory to disk.
1085 int sqlite3Fts5StorageSync(Fts5Storage
*p
){
1087 i64 iLastRowid
= sqlite3_last_insert_rowid(p
->pConfig
->db
);
1088 if( p
->bTotalsValid
){
1089 rc
= fts5StorageSaveTotals(p
);
1090 p
->bTotalsValid
= 0;
1092 if( rc
==SQLITE_OK
){
1093 rc
= sqlite3Fts5IndexSync(p
->pIndex
);
1095 sqlite3_set_last_insert_rowid(p
->pConfig
->db
, iLastRowid
);
1099 int sqlite3Fts5StorageRollback(Fts5Storage
*p
){
1100 p
->bTotalsValid
= 0;
1101 return sqlite3Fts5IndexRollback(p
->pIndex
);
1104 int sqlite3Fts5StorageConfigValue(
1107 sqlite3_value
*pVal
,
1110 sqlite3_stmt
*pReplace
= 0;
1111 int rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_CONFIG
, &pReplace
, 0);
1112 if( rc
==SQLITE_OK
){
1113 sqlite3_bind_text(pReplace
, 1, z
, -1, SQLITE_STATIC
);
1115 sqlite3_bind_value(pReplace
, 2, pVal
);
1117 sqlite3_bind_int(pReplace
, 2, iVal
);
1119 sqlite3_step(pReplace
);
1120 rc
= sqlite3_reset(pReplace
);
1122 if( rc
==SQLITE_OK
&& pVal
){
1123 int iNew
= p
->pConfig
->iCookie
+ 1;
1124 rc
= sqlite3Fts5IndexSetCookie(p
->pIndex
, iNew
);
1125 if( rc
==SQLITE_OK
){
1126 p
->pConfig
->iCookie
= iNew
;