| OLD | NEW |
| 1 /* | 1 /* |
| 2 ** 2014 May 31 | 2 ** 2014 May 31 |
| 3 ** | 3 ** |
| 4 ** The author disclaims copyright to this source code. In place of | 4 ** The author disclaims copyright to this source code. In place of |
| 5 ** a legal notice, here is a blessing: | 5 ** a legal notice, here is a blessing: |
| 6 ** | 6 ** |
| 7 ** May you do good and not evil. | 7 ** May you do good and not evil. |
| 8 ** May you find forgiveness for yourself and forgive others. | 8 ** May you find forgiveness for yourself and forgive others. |
| 9 ** May you share freely, never taking more than you give. | 9 ** May you share freely, never taking more than you give. |
| 10 ** | 10 ** |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 }else{ | 138 }else{ |
| 139 rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0); | 139 rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0); |
| 140 sqlite3_free(zSql); | 140 sqlite3_free(zSql); |
| 141 if( rc!=SQLITE_OK && pzErrMsg ){ | 141 if( rc!=SQLITE_OK && pzErrMsg ){ |
| 142 *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); | 142 *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); |
| 143 } | 143 } |
| 144 } | 144 } |
| 145 } | 145 } |
| 146 | 146 |
| 147 *ppStmt = p->aStmt[eStmt]; | 147 *ppStmt = p->aStmt[eStmt]; |
| 148 sqlite3_reset(*ppStmt); |
| 148 return rc; | 149 return rc; |
| 149 } | 150 } |
| 150 | 151 |
| 151 | 152 |
| 152 static int fts5ExecPrintf( | 153 static int fts5ExecPrintf( |
| 153 sqlite3 *db, | 154 sqlite3 *db, |
| 154 char **pzErr, | 155 char **pzErr, |
| 155 const char *zFormat, | 156 const char *zFormat, |
| 156 ... | 157 ... |
| 157 ){ | 158 ){ |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 Fts5Config *pConfig, /* FTS5 configuration */ | 240 Fts5Config *pConfig, /* FTS5 configuration */ |
| 240 const char *zPost, /* Shadow table to create (e.g. "content") */ | 241 const char *zPost, /* Shadow table to create (e.g. "content") */ |
| 241 const char *zDefn, /* Columns etc. for shadow table */ | 242 const char *zDefn, /* Columns etc. for shadow table */ |
| 242 int bWithout, /* True for without rowid */ | 243 int bWithout, /* True for without rowid */ |
| 243 char **pzErr /* OUT: Error message */ | 244 char **pzErr /* OUT: Error message */ |
| 244 ){ | 245 ){ |
| 245 int rc; | 246 int rc; |
| 246 char *zErr = 0; | 247 char *zErr = 0; |
| 247 | 248 |
| 248 rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s", | 249 rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s", |
| 249 pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":"" | 250 pConfig->zDb, pConfig->zName, zPost, zDefn, |
| 251 #ifndef SQLITE_FTS5_NO_WITHOUT_ROWID |
| 252 bWithout?" WITHOUT ROWID": |
| 253 #endif |
| 254 "" |
| 250 ); | 255 ); |
| 251 if( zErr ){ | 256 if( zErr ){ |
| 252 *pzErr = sqlite3_mprintf( | 257 *pzErr = sqlite3_mprintf( |
| 253 "fts5: error creating shadow table %q_%s: %s", | 258 "fts5: error creating shadow table %q_%s: %s", |
| 254 pConfig->zName, zPost, zErr | 259 pConfig->zName, zPost, zErr |
| 255 ); | 260 ); |
| 256 sqlite3_free(zErr); | 261 sqlite3_free(zErr); |
| 257 } | 262 } |
| 258 | 263 |
| 259 return rc; | 264 return rc; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 | 336 |
| 332 /* | 337 /* |
| 333 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen(). | 338 ** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen(). |
| 334 */ | 339 */ |
| 335 int sqlite3Fts5StorageClose(Fts5Storage *p){ | 340 int sqlite3Fts5StorageClose(Fts5Storage *p){ |
| 336 int rc = SQLITE_OK; | 341 int rc = SQLITE_OK; |
| 337 if( p ){ | 342 if( p ){ |
| 338 int i; | 343 int i; |
| 339 | 344 |
| 340 /* Finalize all SQL statements */ | 345 /* Finalize all SQL statements */ |
| 341 for(i=0; i<(int)ArraySize(p->aStmt); i++){ | 346 for(i=0; i<ArraySize(p->aStmt); i++){ |
| 342 sqlite3_finalize(p->aStmt[i]); | 347 sqlite3_finalize(p->aStmt[i]); |
| 343 } | 348 } |
| 344 | 349 |
| 345 sqlite3_free(p); | 350 sqlite3_free(p); |
| 346 } | 351 } |
| 347 return rc; | 352 return rc; |
| 348 } | 353 } |
| 349 | 354 |
| 350 typedef struct Fts5InsertCtx Fts5InsertCtx; | 355 typedef struct Fts5InsertCtx Fts5InsertCtx; |
| 351 struct Fts5InsertCtx { | 356 struct Fts5InsertCtx { |
| 352 Fts5Storage *pStorage; | 357 Fts5Storage *pStorage; |
| 353 int iCol; | 358 int iCol; |
| 354 int szCol; /* Size of column value in tokens */ | 359 int szCol; /* Size of column value in tokens */ |
| 355 }; | 360 }; |
| 356 | 361 |
| 357 /* | 362 /* |
| 358 ** Tokenization callback used when inserting tokens into the FTS index. | 363 ** Tokenization callback used when inserting tokens into the FTS index. |
| 359 */ | 364 */ |
| 360 static int fts5StorageInsertCallback( | 365 static int fts5StorageInsertCallback( |
| 361 void *pContext, /* Pointer to Fts5InsertCtx object */ | 366 void *pContext, /* Pointer to Fts5InsertCtx object */ |
| 362 int tflags, | 367 int tflags, |
| 363 const char *pToken, /* Buffer containing token */ | 368 const char *pToken, /* Buffer containing token */ |
| 364 int nToken, /* Size of token in bytes */ | 369 int nToken, /* Size of token in bytes */ |
| 365 int iStart, /* Start offset of token */ | 370 int iUnused1, /* Start offset of token */ |
| 366 int iEnd /* End offset of token */ | 371 int iUnused2 /* End offset of token */ |
| 367 ){ | 372 ){ |
| 368 Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext; | 373 Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext; |
| 369 Fts5Index *pIdx = pCtx->pStorage->pIndex; | 374 Fts5Index *pIdx = pCtx->pStorage->pIndex; |
| 375 UNUSED_PARAM2(iUnused1, iUnused2); |
| 376 if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; |
| 370 if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ | 377 if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ |
| 371 pCtx->szCol++; | 378 pCtx->szCol++; |
| 372 } | 379 } |
| 373 return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); | 380 return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); |
| 374 } | 381 } |
| 375 | 382 |
| 376 /* | 383 /* |
| 377 ** If a row with rowid iDel is present in the %_content table, add the | 384 ** If a row with rowid iDel is present in the %_content table, add the |
| 378 ** delete-markers to the FTS index necessary to delete it. Do not actually | 385 ** delete-markers to the FTS index necessary to delete it. Do not actually |
| 379 ** remove the %_content row at this time though. | 386 ** remove the %_content row at this time though. |
| 380 */ | 387 */ |
| 381 static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){ | 388 static int fts5StorageDeleteFromIndex( |
| 389 Fts5Storage *p, |
| 390 i64 iDel, |
| 391 sqlite3_value **apVal |
| 392 ){ |
| 382 Fts5Config *pConfig = p->pConfig; | 393 Fts5Config *pConfig = p->pConfig; |
| 383 sqlite3_stmt *pSeek; /* SELECT to read row iDel from %_data */ | 394 sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ |
| 384 int rc; /* Return code */ | 395 int rc; /* Return code */ |
| 396 int rc2; /* sqlite3_reset() return code */ |
| 397 int iCol; |
| 398 Fts5InsertCtx ctx; |
| 385 | 399 |
| 386 rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); | 400 if( apVal==0 ){ |
| 387 if( rc==SQLITE_OK ){ | 401 rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); |
| 388 int rc2; | 402 if( rc!=SQLITE_OK ) return rc; |
| 389 sqlite3_bind_int64(pSeek, 1, iDel); | 403 sqlite3_bind_int64(pSeek, 1, iDel); |
| 390 if( sqlite3_step(pSeek)==SQLITE_ROW ){ | 404 if( sqlite3_step(pSeek)!=SQLITE_ROW ){ |
| 391 int iCol; | 405 return sqlite3_reset(pSeek); |
| 392 Fts5InsertCtx ctx; | |
| 393 ctx.pStorage = p; | |
| 394 ctx.iCol = -1; | |
| 395 rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); | |
| 396 for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ | |
| 397 if( pConfig->abUnindexed[iCol-1] ) continue; | |
| 398 ctx.szCol = 0; | |
| 399 rc = sqlite3Fts5Tokenize(pConfig, | |
| 400 FTS5_TOKENIZE_DOCUMENT, | |
| 401 (const char*)sqlite3_column_text(pSeek, iCol), | |
| 402 sqlite3_column_bytes(pSeek, iCol), | |
| 403 (void*)&ctx, | |
| 404 fts5StorageInsertCallback | |
| 405 ); | |
| 406 p->aTotalSize[iCol-1] -= (i64)ctx.szCol; | |
| 407 } | |
| 408 p->nTotalRow--; | |
| 409 } | 406 } |
| 410 rc2 = sqlite3_reset(pSeek); | |
| 411 if( rc==SQLITE_OK ) rc = rc2; | |
| 412 } | 407 } |
| 413 | 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; |
| 414 return rc; | 434 return rc; |
| 415 } | 435 } |
| 416 | 436 |
| 417 | 437 |
| 418 /* | 438 /* |
| 419 ** Insert a record into the %_docsize table. Specifically, do: | 439 ** Insert a record into the %_docsize table. Specifically, do: |
| 420 ** | 440 ** |
| 421 ** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf); | 441 ** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf); |
| 422 ** | 442 ** |
| 423 ** If there is no %_docsize table (as happens if the columnsize=0 option | 443 ** If there is no %_docsize table (as happens if the columnsize=0 option |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 483 rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n); | 503 rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n); |
| 484 } | 504 } |
| 485 sqlite3_free(buf.p); | 505 sqlite3_free(buf.p); |
| 486 | 506 |
| 487 return rc; | 507 return rc; |
| 488 } | 508 } |
| 489 | 509 |
| 490 /* | 510 /* |
| 491 ** Remove a row from the FTS table. | 511 ** Remove a row from the FTS table. |
| 492 */ | 512 */ |
| 493 int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){ | 513 int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){ |
| 494 Fts5Config *pConfig = p->pConfig; | 514 Fts5Config *pConfig = p->pConfig; |
| 495 int rc; | 515 int rc; |
| 496 sqlite3_stmt *pDel = 0; | 516 sqlite3_stmt *pDel = 0; |
| 497 | 517 |
| 518 assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 ); |
| 498 rc = fts5StorageLoadTotals(p, 1); | 519 rc = fts5StorageLoadTotals(p, 1); |
| 499 | 520 |
| 500 /* Delete the index records */ | 521 /* Delete the index records */ |
| 501 if( rc==SQLITE_OK ){ | 522 if( rc==SQLITE_OK ){ |
| 502 rc = fts5StorageDeleteFromIndex(p, iDel); | 523 rc = fts5StorageDeleteFromIndex(p, iDel, apVal); |
| 503 } | 524 } |
| 504 | 525 |
| 505 /* Delete the %_docsize record */ | 526 /* Delete the %_docsize record */ |
| 506 if( rc==SQLITE_OK && pConfig->bColumnsize ){ | 527 if( rc==SQLITE_OK && pConfig->bColumnsize ){ |
| 507 rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); | 528 rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); |
| 508 if( rc==SQLITE_OK ){ | 529 if( rc==SQLITE_OK ){ |
| 509 sqlite3_bind_int64(pDel, 1, iDel); | 530 sqlite3_bind_int64(pDel, 1, iDel); |
| 510 sqlite3_step(pDel); | 531 sqlite3_step(pDel); |
| 511 rc = sqlite3_reset(pDel); | 532 rc = sqlite3_reset(pDel); |
| 512 } | 533 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 525 } | 546 } |
| 526 | 547 |
| 527 /* Write the averages record */ | 548 /* Write the averages record */ |
| 528 if( rc==SQLITE_OK ){ | 549 if( rc==SQLITE_OK ){ |
| 529 rc = fts5StorageSaveTotals(p); | 550 rc = fts5StorageSaveTotals(p); |
| 530 } | 551 } |
| 531 | 552 |
| 532 return rc; | 553 return rc; |
| 533 } | 554 } |
| 534 | 555 |
| 535 int sqlite3Fts5StorageSpecialDelete( | |
| 536 Fts5Storage *p, | |
| 537 i64 iDel, | |
| 538 sqlite3_value **apVal | |
| 539 ){ | |
| 540 Fts5Config *pConfig = p->pConfig; | |
| 541 int rc; | |
| 542 sqlite3_stmt *pDel = 0; | |
| 543 | |
| 544 assert( pConfig->eContent!=FTS5_CONTENT_NORMAL ); | |
| 545 rc = fts5StorageLoadTotals(p, 1); | |
| 546 | |
| 547 /* Delete the index records */ | |
| 548 if( rc==SQLITE_OK ){ | |
| 549 int iCol; | |
| 550 Fts5InsertCtx ctx; | |
| 551 ctx.pStorage = p; | |
| 552 ctx.iCol = -1; | |
| 553 | |
| 554 rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); | |
| 555 for(iCol=0; rc==SQLITE_OK && iCol<pConfig->nCol; iCol++){ | |
| 556 if( pConfig->abUnindexed[iCol] ) continue; | |
| 557 ctx.szCol = 0; | |
| 558 rc = sqlite3Fts5Tokenize(pConfig, | |
| 559 FTS5_TOKENIZE_DOCUMENT, | |
| 560 (const char*)sqlite3_value_text(apVal[iCol]), | |
| 561 sqlite3_value_bytes(apVal[iCol]), | |
| 562 (void*)&ctx, | |
| 563 fts5StorageInsertCallback | |
| 564 ); | |
| 565 p->aTotalSize[iCol] -= (i64)ctx.szCol; | |
| 566 } | |
| 567 p->nTotalRow--; | |
| 568 } | |
| 569 | |
| 570 /* Delete the %_docsize record */ | |
| 571 if( pConfig->bColumnsize ){ | |
| 572 if( rc==SQLITE_OK ){ | |
| 573 rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); | |
| 574 } | |
| 575 if( rc==SQLITE_OK ){ | |
| 576 sqlite3_bind_int64(pDel, 1, iDel); | |
| 577 sqlite3_step(pDel); | |
| 578 rc = sqlite3_reset(pDel); | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 /* Write the averages record */ | |
| 583 if( rc==SQLITE_OK ){ | |
| 584 rc = fts5StorageSaveTotals(p); | |
| 585 } | |
| 586 | |
| 587 return rc; | |
| 588 } | |
| 589 | |
| 590 /* | 556 /* |
| 591 ** Delete all entries in the FTS5 index. | 557 ** Delete all entries in the FTS5 index. |
| 592 */ | 558 */ |
| 593 int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ | 559 int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ |
| 594 Fts5Config *pConfig = p->pConfig; | 560 Fts5Config *pConfig = p->pConfig; |
| 595 int rc; | 561 int rc; |
| 596 | 562 |
| 597 /* Delete the contents of the %_data and %_docsize tables. */ | 563 /* Delete the contents of the %_data and %_docsize tables. */ |
| 598 rc = fts5ExecPrintf(pConfig->db, 0, | 564 rc = fts5ExecPrintf(pConfig->db, 0, |
| 599 "DELETE FROM %Q.'%q_data';" | 565 "DELETE FROM %Q.'%q_data';" |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 672 } | 638 } |
| 673 | 639 |
| 674 int sqlite3Fts5StorageOptimize(Fts5Storage *p){ | 640 int sqlite3Fts5StorageOptimize(Fts5Storage *p){ |
| 675 return sqlite3Fts5IndexOptimize(p->pIndex); | 641 return sqlite3Fts5IndexOptimize(p->pIndex); |
| 676 } | 642 } |
| 677 | 643 |
| 678 int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){ | 644 int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){ |
| 679 return sqlite3Fts5IndexMerge(p->pIndex, nMerge); | 645 return sqlite3Fts5IndexMerge(p->pIndex, nMerge); |
| 680 } | 646 } |
| 681 | 647 |
| 648 int sqlite3Fts5StorageReset(Fts5Storage *p){ |
| 649 return sqlite3Fts5IndexReset(p->pIndex); |
| 650 } |
| 651 |
| 682 /* | 652 /* |
| 683 ** Allocate a new rowid. This is used for "external content" tables when | 653 ** Allocate a new rowid. This is used for "external content" tables when |
| 684 ** a NULL value is inserted into the rowid column. The new rowid is allocated | 654 ** a NULL value is inserted into the rowid column. The new rowid is allocated |
| 685 ** by inserting a dummy row into the %_docsize table. The dummy will be | 655 ** by inserting a dummy row into the %_docsize table. The dummy will be |
| 686 ** overwritten later. | 656 ** overwritten later. |
| 687 ** | 657 ** |
| 688 ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In | 658 ** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In |
| 689 ** this case the user is required to provide a rowid explicitly. | 659 ** this case the user is required to provide a rowid explicitly. |
| 690 */ | 660 */ |
| 691 static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ | 661 static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 818 | 788 |
| 819 /* | 789 /* |
| 820 ** Context object used by sqlite3Fts5StorageIntegrity(). | 790 ** Context object used by sqlite3Fts5StorageIntegrity(). |
| 821 */ | 791 */ |
| 822 typedef struct Fts5IntegrityCtx Fts5IntegrityCtx; | 792 typedef struct Fts5IntegrityCtx Fts5IntegrityCtx; |
| 823 struct Fts5IntegrityCtx { | 793 struct Fts5IntegrityCtx { |
| 824 i64 iRowid; | 794 i64 iRowid; |
| 825 int iCol; | 795 int iCol; |
| 826 int szCol; | 796 int szCol; |
| 827 u64 cksum; | 797 u64 cksum; |
| 798 Fts5Termset *pTermset; |
| 828 Fts5Config *pConfig; | 799 Fts5Config *pConfig; |
| 829 }; | 800 }; |
| 830 | 801 |
| 802 |
| 831 /* | 803 /* |
| 832 ** Tokenization callback used by integrity check. | 804 ** Tokenization callback used by integrity check. |
| 833 */ | 805 */ |
| 834 static int fts5StorageIntegrityCallback( | 806 static int fts5StorageIntegrityCallback( |
| 835 void *pContext, /* Pointer to Fts5InsertCtx object */ | 807 void *pContext, /* Pointer to Fts5IntegrityCtx object */ |
| 836 int tflags, | 808 int tflags, |
| 837 const char *pToken, /* Buffer containing token */ | 809 const char *pToken, /* Buffer containing token */ |
| 838 int nToken, /* Size of token in bytes */ | 810 int nToken, /* Size of token in bytes */ |
| 839 int iStart, /* Start offset of token */ | 811 int iUnused1, /* Start offset of token */ |
| 840 int iEnd /* End offset of token */ | 812 int iUnused2 /* End offset of token */ |
| 841 ){ | 813 ){ |
| 842 Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; | 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 |
| 843 if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ | 825 if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ |
| 844 pCtx->szCol++; | 826 pCtx->szCol++; |
| 845 } | 827 } |
| 846 pCtx->cksum ^= sqlite3Fts5IndexCksum( | 828 |
| 847 pCtx->pConfig, pCtx->iRowid, pCtx->iCol, pCtx->szCol-1, pToken, nToken | 829 switch( pCtx->pConfig->eDetail ){ |
| 848 ); | 830 case FTS5_DETAIL_FULL: |
| 849 return SQLITE_OK; | 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; |
| 850 } | 868 } |
| 851 | 869 |
| 852 /* | 870 /* |
| 853 ** Check that the contents of the FTS index match that of the %_content | 871 ** Check that the contents of the FTS index match that of the %_content |
| 854 ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return | 872 ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return |
| 855 ** some other SQLite error code if an error occurs while attempting to | 873 ** some other SQLite error code if an error occurs while attempting to |
| 856 ** determine this. | 874 ** determine this. |
| 857 */ | 875 */ |
| 858 int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ | 876 int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ |
| 859 Fts5Config *pConfig = p->pConfig; | 877 Fts5Config *pConfig = p->pConfig; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 875 rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); | 893 rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); |
| 876 if( rc==SQLITE_OK ){ | 894 if( rc==SQLITE_OK ){ |
| 877 int rc2; | 895 int rc2; |
| 878 while( SQLITE_ROW==sqlite3_step(pScan) ){ | 896 while( SQLITE_ROW==sqlite3_step(pScan) ){ |
| 879 int i; | 897 int i; |
| 880 ctx.iRowid = sqlite3_column_int64(pScan, 0); | 898 ctx.iRowid = sqlite3_column_int64(pScan, 0); |
| 881 ctx.szCol = 0; | 899 ctx.szCol = 0; |
| 882 if( pConfig->bColumnsize ){ | 900 if( pConfig->bColumnsize ){ |
| 883 rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); | 901 rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); |
| 884 } | 902 } |
| 903 if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 904 rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 905 } |
| 885 for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ | 906 for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 886 if( pConfig->abUnindexed[i] ) continue; | 907 if( pConfig->abUnindexed[i] ) continue; |
| 887 ctx.iCol = i; | 908 ctx.iCol = i; |
| 888 ctx.szCol = 0; | 909 ctx.szCol = 0; |
| 889 rc = sqlite3Fts5Tokenize(pConfig, | 910 if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 890 FTS5_TOKENIZE_DOCUMENT, | 911 rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 891 (const char*)sqlite3_column_text(pScan, i+1), | 912 } |
| 892 sqlite3_column_bytes(pScan, i+1), | 913 if( rc==SQLITE_OK ){ |
| 893 (void*)&ctx, | 914 rc = sqlite3Fts5Tokenize(pConfig, |
| 894 fts5StorageIntegrityCallback | 915 FTS5_TOKENIZE_DOCUMENT, |
| 895 ); | 916 (const char*)sqlite3_column_text(pScan, i+1), |
| 896 if( pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ | 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] ){ |
| 897 rc = FTS5_CORRUPT; | 923 rc = FTS5_CORRUPT; |
| 898 } | 924 } |
| 899 aTotalSize[i] += ctx.szCol; | 925 aTotalSize[i] += ctx.szCol; |
| 926 if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 927 sqlite3Fts5TermsetFree(ctx.pTermset); |
| 928 ctx.pTermset = 0; |
| 929 } |
| 900 } | 930 } |
| 931 sqlite3Fts5TermsetFree(ctx.pTermset); |
| 932 ctx.pTermset = 0; |
| 933 |
| 901 if( rc!=SQLITE_OK ) break; | 934 if( rc!=SQLITE_OK ) break; |
| 902 } | 935 } |
| 903 rc2 = sqlite3_reset(pScan); | 936 rc2 = sqlite3_reset(pScan); |
| 904 if( rc==SQLITE_OK ) rc = rc2; | 937 if( rc==SQLITE_OK ) rc = rc2; |
| 905 } | 938 } |
| 906 | 939 |
| 907 /* Test that the "totals" (sometimes called "averages") record looks Ok */ | 940 /* Test that the "totals" (sometimes called "averages") record looks Ok */ |
| 908 if( rc==SQLITE_OK ){ | 941 if( rc==SQLITE_OK ){ |
| 909 int i; | 942 int i; |
| 910 rc = fts5StorageLoadTotals(p, 0); | 943 rc = fts5StorageLoadTotals(p, 0); |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1092 } | 1125 } |
| 1093 if( rc==SQLITE_OK && pVal ){ | 1126 if( rc==SQLITE_OK && pVal ){ |
| 1094 int iNew = p->pConfig->iCookie + 1; | 1127 int iNew = p->pConfig->iCookie + 1; |
| 1095 rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew); | 1128 rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew); |
| 1096 if( rc==SQLITE_OK ){ | 1129 if( rc==SQLITE_OK ){ |
| 1097 p->pConfig->iCookie = iNew; | 1130 p->pConfig->iCookie = iNew; |
| 1098 } | 1131 } |
| 1099 } | 1132 } |
| 1100 return rc; | 1133 return rc; |
| 1101 } | 1134 } |
| 1102 | |
| 1103 | |
| OLD | NEW |