Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Clarify the role of Fts5Storage.pSavedRow in the new feature on this branch. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | fts5-locale |
Files: | files | file ages | folders |
SHA3-256: |
e8a61d5c48073fdd4d99d0b6fc70469b |
User & Date: | dan 2024-08-01 17:15:17 |
Context
2024-08-02
| ||
21:06 | Change things so that locale=1 is required to write fts5_locale() values to an fts5 table, and so that blobs may not be stored in indexed (i.e. not UNINDEXED) columns of these tables. (check-in: c98ccc12 user: dan tags: fts5-locale) | |
2024-08-01
| ||
17:15 | Clarify the role of Fts5Storage.pSavedRow in the new feature on this branch. (check-in: e8a61d5c user: dan tags: fts5-locale) | |
2024-07-31
| ||
20:49 | Fix various problems with the code on this branch. (check-in: 8bd4ae7e user: dan tags: fts5-locale) | |
Changes
Changes to ext/fts5/fts5_storage.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ** */ #include "fts5Int.h" struct Fts5Storage { Fts5Config *pConfig; Fts5Index *pIndex; int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ i64 nTotalRow; /* Total number of rows in FTS table */ i64 *aTotalSize; /* Total sizes of each column */ sqlite3_stmt *pSavedRow; | > > > > > > > > > > > > > > > > > > > > > > > > > > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | ** */ #include "fts5Int.h" /* ** pSavedRow: ** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it ** does a by-rowid lookup to retrieve a single row from the %_content ** table or equivalent external-content table/view. ** ** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original ** values for a row being UPDATEd. In that case, the SQL statement is ** not reset and pSavedRow is set to point at it. This is so that the ** insert operation that follows the delete may access the original ** row values for any new values for which sqlite3_value_nochange() returns ** true. i.e. if the user executes: ** ** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1); ** ... ** UPDATE fts SET a=?, b=? WHERE rowid=?; ** ** then the value passed to the xUpdate() method of this table as the ** new.c value is an sqlite3_value_nochange() value. So in this case it ** must be read from the saved row stored in Fts5Storage.pSavedRow. ** ** This is necessary - using sqlite3_value_nochange() instead of just having ** SQLite pass the original value back via xUpdate() - so as not to discard ** any locale information associated with such values. ** */ struct Fts5Storage { Fts5Config *pConfig; Fts5Index *pIndex; int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ i64 nTotalRow; /* Total number of rows in FTS table */ i64 *aTotalSize; /* Total sizes of each column */ sqlite3_stmt *pSavedRow; |
︙ | ︙ | |||
33 34 35 36 37 38 39 | #if FTS5_STMT_SCAN_DESC!=1 # error "FTS5_STMT_SCAN_DESC mismatch" #endif #if FTS5_STMT_LOOKUP!=2 # error "FTS5_STMT_LOOKUP mismatch" #endif | | | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | #if FTS5_STMT_SCAN_DESC!=1 # error "FTS5_STMT_SCAN_DESC mismatch" #endif #if FTS5_STMT_LOOKUP!=2 # error "FTS5_STMT_LOOKUP mismatch" #endif #define FTS5_STMT_LOOKUP2 3 #define FTS5_STMT_INSERT_CONTENT 4 #define FTS5_STMT_REPLACE_CONTENT 5 #define FTS5_STMT_DELETE_CONTENT 6 #define FTS5_STMT_REPLACE_DOCSIZE 7 #define FTS5_STMT_DELETE_DOCSIZE 8 #define FTS5_STMT_LOOKUP_DOCSIZE 9 #define FTS5_STMT_REPLACE_CONFIG 10 #define FTS5_STMT_SCAN 11 /* ** Prepare the two insert statements - Fts5Storage.pInsertContent and ** Fts5Storage.pInsertDocsize - if they have not already been prepared. ** Return SQLITE_OK if successful, or an SQLite error code if an error ** occurs. */ |
︙ | ︙ | |||
401 402 403 404 405 406 407 408 409 410 411 412 413 414 | if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ pCtx->szCol++; } return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); } int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ int rc = SQLITE_OK; sqlite3_stmt *pSeek = 0; assert( p->pSavedRow==0 ); rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0); if( rc==SQLITE_OK ){ | > > > > > > > > > > | 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 | if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ pCtx->szCol++; } return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); } /* ** This function is used as part of an UPDATE statement that modifies the ** rowid of a row. In that case, this function is called first to set ** Fts5Storage.pSavedRow to point to a statement that may be used to ** access the original values of the row being deleted - iDel. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** It is not considered an error if row iDel does not exist. In this case ** pSavedRow is not set and SQLITE_OK returned. */ int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ int rc = SQLITE_OK; sqlite3_stmt *pSeek = 0; assert( p->pSavedRow==0 ); rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0); if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
423 424 425 426 427 428 429 430 431 432 433 434 | return rc; } /* ** If a row with rowid iDel is present in the %_content table, add the ** delete-markers to the FTS index necessary to delete it. Do not actually ** remove the %_content row at this time though. */ static int fts5StorageDeleteFromIndex( Fts5Storage *p, i64 iDel, sqlite3_value **apVal, | > > > > > | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | return rc; } /* ** If a row with rowid iDel is present in the %_content table, add the ** delete-markers to the FTS index necessary to delete it. Do not actually ** remove the %_content row at this time though. ** ** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left ** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access ** the original values of the row being deleted. This is used by UPDATE ** statements. */ static int fts5StorageDeleteFromIndex( Fts5Storage *p, i64 iDel, sqlite3_value **apVal, int bSaveRow /* True to set pSavedRow */ ){ Fts5Config *pConfig = p->pConfig; sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ int rc = SQLITE_OK; /* Return code */ int rc2; /* sqlite3_reset() return code */ int iCol; Fts5InsertCtx ctx; |
︙ | ︙ | |||
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | }else{ rc2 = sqlite3_reset(pSeek); if( rc==SQLITE_OK ) rc = rc2; } return rc; } void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ sqlite3_reset(pStorage->pSavedRow); pStorage->pSavedRow = 0; } /* ** This function is called to process a DELETE on a contentless_delete=1 ** table. It adds the tombstone required to delete the entry with rowid | > > > > > > > > | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 | }else{ rc2 = sqlite3_reset(pSeek); if( rc==SQLITE_OK ) rc = rc2; } return rc; } /* ** Reset any saved statement pSavedRow. Zero pSavedRow as well. This ** should be called by the xUpdate() method of the fts5 table before ** returning from any operation that may have set Fts5Storage.pSavedRow. */ void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ assert( pStorage->pSavedRow==0 || pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2] ); sqlite3_reset(pStorage->pSavedRow); pStorage->pSavedRow = 0; } /* ** This function is called to process a DELETE on a contentless_delete=1 ** table. It adds the tombstone required to delete the entry with rowid |
︙ | ︙ | |||
626 627 628 629 630 631 632 | /* ** Remove a row from the FTS table. */ int sqlite3Fts5StorageDelete( Fts5Storage *p, /* Storage object */ i64 iDel, /* Rowid to delete from table */ sqlite3_value **apVal, /* Optional - values to remove from index */ | | | 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 | /* ** Remove a row from the FTS table. */ int sqlite3Fts5StorageDelete( Fts5Storage *p, /* Storage object */ i64 iDel, /* Rowid to delete from table */ sqlite3_value **apVal, /* Optional - values to remove from index */ int bSaveRow /* If true, set pSavedRow for deleted row */ ){ Fts5Config *pConfig = p->pConfig; int rc; sqlite3_stmt *pDel = 0; assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 ); rc = fts5StorageLoadTotals(p, 1); |
︙ | ︙ | |||
733 734 735 736 737 738 739 | i64 iRowid = sqlite3_column_int64(pScan, 0); sqlite3Fts5BufferZero(&buf); rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ | | | | < | < > | 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 | i64 iRowid = sqlite3_column_int64(pScan, 0); sqlite3Fts5BufferZero(&buf); rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &pText, &nText); if( rc==SQLITE_OK ){ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); |
︙ | ︙ | |||
834 835 836 837 838 839 840 841 842 843 844 845 846 847 | }else{ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ int i; /* Counter variable */ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ sqlite3_value *pVal = apVal[i]; if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ pVal = sqlite3_column_value(p->pSavedRow, i-1); }else if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE ){ if( pConfig->bLocale==0 ){ sqlite3Fts5ConfigErrmsg(pConfig, "fts5_locale() may not be used without locale=1" ); rc = SQLITE_ERROR; | > > | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | }else{ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ int i; /* Counter variable */ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ sqlite3_value *pVal = apVal[i]; if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ /* This is an UPDATE statement, and column (i-2) was not modified. ** Retrieve the value from Fts5Storage.pSavedRow instead. */ pVal = sqlite3_column_value(p->pSavedRow, i-1); }else if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE ){ if( pConfig->bLocale==0 ){ sqlite3Fts5ConfigErrmsg(pConfig, "fts5_locale() may not be used without locale=1" ); rc = SQLITE_ERROR; |
︙ | ︙ | |||
855 856 857 858 859 860 861 | sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); continue; } }else if( pConfig->bLocale && sqlite3_value_type(pVal)==SQLITE_BLOB && i>=2 && pConfig->abUnindexed[i-2]==0 ){ | | > | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 | sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); continue; } }else if( pConfig->bLocale && sqlite3_value_type(pVal)==SQLITE_BLOB && i>=2 && pConfig->abUnindexed[i-2]==0 ){ /* Inserting a blob into a normal content table with locale=1. ** Add the 4 0x00 byte header. */ int n = sqlite3_value_bytes(pVal); u8 *pBlob = sqlite3Fts5MallocZero(&rc, n+4); if( pBlob ){ memcpy(&pBlob[4], sqlite3_value_blob(pVal), n); rc = sqlite3_bind_blob(pInsert, i, pBlob, n+4, SQLITE_TRANSIENT); sqlite3_free(pBlob); } |
︙ | ︙ | |||
901 902 903 904 905 906 907 | if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); } for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ | | | | | | < < < < < | | | | < | 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 | if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); } for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ sqlite3_value *pVal = apVal[ctx.iCol+2]; int bDisk = 0; if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); bDisk = 1; } rc = sqlite3Fts5ExtractText(pConfig, pVal, bDisk, &bReset, &pText,&nText); if( rc==SQLITE_OK ){ assert( bReset==0 || pConfig->bLocale ); rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); if( bReset ) sqlite3Fts5ClearLocale(pConfig); } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; } p->nTotalRow++; |
︙ | ︙ | |||
1095 1096 1097 1098 1099 1100 1101 | if( pConfig->abUnindexed[i] ) continue; ctx.iCol = i; ctx.szCol = 0; if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); } if( rc==SQLITE_OK ){ | | | | < | 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 | if( pConfig->abUnindexed[i] ) continue; ctx.iCol = i; ctx.szCol = 0; if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); } if( rc==SQLITE_OK ){ int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ rc = sqlite3Fts5ExtractText(pConfig, sqlite3_column_value(pScan, i+1), 1, &bReset, &pText, &nText ); if( rc==SQLITE_OK ){ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageIntegrityCallback ); |
︙ | ︙ |