Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: third_party/sqlite/sqlite-src-3170000/ext/fts5/fts5_storage.c

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

Powered by Google App Engine
This is Rietveld 408576698