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_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
);
132 zSql
= sqlite3_mprintf(azStmt
[eStmt
], pC
->zDb
, pC
->zName
);
139 int f
= SQLITE_PREPARE_PERSISTENT
;
140 if( eStmt
>FTS5_STMT_LOOKUP
) f
|= SQLITE_PREPARE_NO_VTAB
;
142 rc
= sqlite3_prepare_v3(pC
->db
, zSql
, -1, f
, &p
->aStmt
[eStmt
], 0);
145 if( rc
!=SQLITE_OK
&& pzErrMsg
){
146 *pzErrMsg
= sqlite3_mprintf("%s", sqlite3_errmsg(pC
->db
));
151 *ppStmt
= p
->aStmt
[eStmt
];
152 sqlite3_reset(*ppStmt
);
157 static int fts5ExecPrintf(
164 va_list ap
; /* ... printf arguments */
167 va_start(ap
, zFormat
);
168 zSql
= sqlite3_vmprintf(zFormat
, ap
);
173 rc
= sqlite3_exec(db
, zSql
, 0, 0, pzErr
);
182 ** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
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
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
);
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 */
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":
261 *pzErr
= sqlite3_mprintf(
262 "fts5: error creating shadow table %q_%s: %s",
263 pConfig
->zName
, zPost
, zErr
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(
283 char **pzErr
/* OUT: Error message */
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
;
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);
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
);
319 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
320 rc
= sqlite3Fts5CreateTable(
321 pConfig
, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
325 rc
= sqlite3Fts5CreateTable(
326 pConfig
, "config", "k PRIMARY KEY, v", 1, pzErr
330 rc
= sqlite3Fts5StorageConfigValue(p
, "version", 0, FTS5_CURRENT_VERSION
);
335 sqlite3Fts5StorageClose(p
);
342 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
344 int sqlite3Fts5StorageClose(Fts5Storage
*p
){
349 /* Finalize all SQL statements */
350 for(i
=0; i
<ArraySize(p
->aStmt
); i
++){
351 sqlite3_finalize(p
->aStmt
[i
]);
359 typedef struct Fts5InsertCtx Fts5InsertCtx
;
360 struct Fts5InsertCtx
{
361 Fts5Storage
*pStorage
;
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 */
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 ){
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(
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 */
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
);
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 ){
420 assert( pSeek
==0 || apVal
==0 );
421 assert( pSeek
!=0 || apVal
!=0 );
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]);
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 ){
441 if( rc
==SQLITE_OK
&& p
->nTotalRow
<1 ){
447 rc2
= sqlite3_reset(pSeek
);
448 if( rc
==SQLITE_OK
) rc
= rc2
;
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 */
467 if( p
->pConfig
->bColumnsize
){
468 sqlite3_stmt
*pReplace
= 0;
469 rc
= fts5StorageGetStmt(p
, FTS5_STMT_REPLACE_DOCSIZE
, &pReplace
, 0);
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);
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
488 ** Return SQLITE_OK if successful, or an SQLite error code if an error
491 static int fts5StorageLoadTotals(Fts5Storage
*p
, int bCache
){
493 if( p
->bTotalsValid
==0 ){
494 rc
= sqlite3Fts5IndexGetAverages(p
->pIndex
, &p
->nTotalRow
, p
->aTotalSize
);
495 p
->bTotalsValid
= bCache
;
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
507 static int fts5StorageSaveTotals(Fts5Storage
*p
){
508 int nCol
= p
->pConfig
->nCol
;
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
]);
519 rc
= sqlite3Fts5IndexSetAverages(p
->pIndex
, buf
.p
, buf
.n
);
527 ** Remove a row from the FTS table.
529 int sqlite3Fts5StorageDelete(Fts5Storage
*p
, i64 iDel
, sqlite3_value
**apVal
){
530 Fts5Config
*pConfig
= p
->pConfig
;
532 sqlite3_stmt
*pDel
= 0;
534 assert( pConfig
->eContent
!=FTS5_CONTENT_NORMAL
|| apVal
==0 );
535 rc
= fts5StorageLoadTotals(p
, 1);
537 /* Delete the index records */
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);
546 sqlite3_bind_int64(pDel
, 1, iDel
);
548 rc
= sqlite3_reset(pDel
);
552 /* Delete the %_content record */
553 if( pConfig
->eContent
==FTS5_CONTENT_NORMAL
){
555 rc
= fts5StorageGetStmt(p
, FTS5_STMT_DELETE_CONTENT
, &pDel
, 0);
558 sqlite3_bind_int64(pDel
, 1, iDel
);
560 rc
= sqlite3_reset(pDel
);
568 ** Delete all entries in the FTS5 index.
570 int sqlite3Fts5StorageDeleteAll(Fts5Storage
*p
){
571 Fts5Config
*pConfig
= p
->pConfig
;
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. */
593 rc
= sqlite3Fts5IndexReinit(p
->pIndex
);
596 rc
= sqlite3Fts5StorageConfigValue(p
, "version", 0, FTS5_CURRENT_VERSION
);
601 int sqlite3Fts5StorageRebuild(Fts5Storage
*p
){
602 Fts5Buffer buf
= {0,0,0};
603 Fts5Config
*pConfig
= p
->pConfig
;
604 sqlite3_stmt
*pScan
= 0;
608 memset(&ctx
, 0, sizeof(Fts5InsertCtx
));
610 rc
= sqlite3Fts5StorageDeleteAll(p
);
612 rc
= fts5StorageLoadTotals(p
, 1);
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
++){
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
,
633 fts5StorageInsertCallback
636 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, ctx
.szCol
);
637 p
->aTotalSize
[ctx
.iCol
] += (i64
)ctx
.szCol
;
642 rc
= fts5StorageInsertDocsize(p
, iRowid
, &buf
);
646 rc2
= sqlite3_reset(pScan
);
647 if( rc
==SQLITE_OK
) rc
= rc2
;
649 /* Write the averages record */
651 rc
= fts5StorageSaveTotals(p
);
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);
683 sqlite3_bind_null(pReplace
, 1);
684 sqlite3_bind_null(pReplace
, 2);
685 sqlite3_step(pReplace
);
686 rc
= sqlite3_reset(pReplace
);
689 *piRowid
= sqlite3_last_insert_rowid(p
->pConfig
->db
);
696 ** Insert a new row into the FTS content table.
698 int sqlite3Fts5StorageContentInsert(
700 sqlite3_value
**apVal
,
703 Fts5Config
*pConfig
= p
->pConfig
;
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]);
711 rc
= fts5StorageNewRowid(p
, piRowid
);
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
]);
721 sqlite3_step(pInsert
);
722 rc
= sqlite3_reset(pInsert
);
724 *piRowid
= sqlite3_last_insert_rowid(pConfig
->db
);
731 ** Insert new entries into the FTS index and %_docsize table.
733 int sqlite3Fts5StorageIndexInsert(
735 sqlite3_value
**apVal
,
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
));
745 rc
= fts5StorageLoadTotals(p
, 1);
748 rc
= sqlite3Fts5IndexBeginWrite(p
->pIndex
, 0, iRowid
);
750 for(ctx
.iCol
=0; rc
==SQLITE_OK
&& ctx
.iCol
<pConfig
->nCol
; ctx
.iCol
++){
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
,
759 fts5StorageInsertCallback
762 sqlite3Fts5BufferAppendVarint(&rc
, &buf
, ctx
.szCol
);
763 p
->aTotalSize
[ctx
.iCol
] += (i64
)ctx
.szCol
;
767 /* Write the %_docsize record */
769 rc
= fts5StorageInsertDocsize(p
, iRowid
, &buf
);
776 static int fts5StorageCount(Fts5Storage
*p
, const char *zSuffix
, i64
*pnRow
){
777 Fts5Config
*pConfig
= p
->pConfig
;
781 zSql
= sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
782 pConfig
->zDb
, pConfig
->zName
, zSuffix
787 sqlite3_stmt
*pCnt
= 0;
788 rc
= sqlite3_prepare_v2(pConfig
->db
, zSql
, -1, &pCnt
, 0);
790 if( SQLITE_ROW
==sqlite3_step(pCnt
) ){
791 *pnRow
= sqlite3_column_int64(pCnt
, 0);
793 rc
= sqlite3_finalize(pCnt
);
802 ** Context object used by sqlite3Fts5StorageIntegrity().
804 typedef struct Fts5IntegrityCtx Fts5IntegrityCtx
;
805 struct Fts5IntegrityCtx
{
810 Fts5Termset
*pTermset
;
816 ** Tokenization callback used by integrity check.
818 static int fts5StorageIntegrityCallback(
819 void *pContext
, /* Pointer to Fts5IntegrityCtx object */
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
;
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 ){
841 switch( pCtx
->pConfig
->eDetail
){
842 case FTS5_DETAIL_FULL
:
843 iPos
= pCtx
->szCol
-1;
847 case FTS5_DETAIL_COLUMNS
:
853 assert( pCtx
->pConfig
->eDetail
==FTS5_DETAIL_NONE
);
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
);
870 rc
= sqlite3Fts5TermsetAdd(pTermset
, ii
+1, pToken
, nByte
, &bPresent
);
872 pCtx
->cksum
^= sqlite3Fts5IndexEntryCksum(
873 pCtx
->iRowid
, iCol
, iPos
, ii
+1, pToken
, nByte
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
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
;
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
)
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);
913 while( SQLITE_ROW
==sqlite3_step(pScan
) ){
915 ctx
.iRowid
= sqlite3_column_int64(pScan
, 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;
927 if( pConfig
->eDetail
==FTS5_DETAIL_COLUMNS
){
928 rc
= sqlite3Fts5TermsetNew(&ctx
.pTermset
);
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
,
937 fts5StorageIntegrityCallback
940 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
&& ctx
.szCol
!=aColSize
[i
] ){
943 aTotalSize
[i
] += ctx
.szCol
;
944 if( pConfig
->eDetail
==FTS5_DETAIL_COLUMNS
){
945 sqlite3Fts5TermsetFree(ctx
.pTermset
);
949 sqlite3Fts5TermsetFree(ctx
.pTermset
);
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 */
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
){
971 rc
= fts5StorageCount(p
, "content", &nRow
);
972 if( rc
==SQLITE_OK
&& nRow
!=p
->nTotalRow
) rc
= FTS5_CORRUPT
;
974 if( rc
==SQLITE_OK
&& pConfig
->bColumnsize
){
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. */
985 rc
= sqlite3Fts5IndexIntegrityCheck(p
->pIndex
, ctx
.cksum
, bUseCksum
);
988 sqlite3_free(aTotalSize
);
993 ** Obtain an SQLite statement handle that may be used to read data from the
996 int sqlite3Fts5StorageStmt(
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;
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(
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
;
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 */
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
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);
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
) ){
1076 rc
= sqlite3_reset(pLookup
);
1077 if( bCorrupt
&& rc
==SQLITE_OK
){
1081 assert( rc
!=SQLITE_OK
);
1087 int sqlite3Fts5StorageSize(Fts5Storage
*p
, int iCol
, i64
*pnToken
){
1088 int rc
= fts5StorageLoadTotals(p
, 0);
1089 if( rc
==SQLITE_OK
){
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
];
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
;
1120 ** Flush any data currently held in-memory to disk.
1122 int sqlite3Fts5StorageSync(Fts5Storage
*p
){
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
);
1136 int sqlite3Fts5StorageRollback(Fts5Storage
*p
){
1137 p
->bTotalsValid
= 0;
1138 return sqlite3Fts5IndexRollback(p
->pIndex
);
1141 int sqlite3Fts5StorageConfigValue(
1144 sqlite3_value
*pVal
,
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
);
1152 sqlite3_bind_value(pReplace
, 2, pVal
);
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
;