Overview
| SHA1 Hash: | e38fb02d5ea5daa6992df4dfbbeec92bf7b525f6 |
|---|---|
| Date: | 2012-11-27 15:56:38 |
| User: | dan |
| Edited Comment: | Fix a problem causing the "number-of-documents" field maintained by FTS4 to be set incorrectly by REPLACE queries. |
| Original Comment: | Fix a problem causing the "number-of-documents" field maintained by FTS4 to be set incorrectly. |
Tags And Properties
- branch=trunk inherited from [704b122e53]
- comment=Fix a problem causing the "number-of-documents" field maintained by FTS4 to be set incorrectly by REPLACE queries. added by [7b74306f2d] on 2012-11-27 15:57:19
- sym-trunk inherited from [704b122e53]
Changes
Changes to ext/fts3/fts3_write.c
772 ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. 772 ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. 773 */ 773 */ 774 static int fts3PendingTermsAdd( 774 static int fts3PendingTermsAdd( 775 Fts3Table *p, /* Table into which text will be inserted */ 775 Fts3Table *p, /* Table into which text will be inserted */ 776 int iLangid, /* Language id to use */ 776 int iLangid, /* Language id to use */ 777 const char *zText, /* Text of document to be inserted */ 777 const char *zText, /* Text of document to be inserted */ 778 int iCol, /* Column into which text is being inserted */ 778 int iCol, /* Column into which text is being inserted */ 779 u32 *pnWord /* OUT: Number of tokens inserted */ | 779 u32 *pnWord /* IN/OUT: Incr. by number tokens inserted */ 780 ){ 780 ){ 781 int rc; 781 int rc; 782 int iStart = 0; 782 int iStart = 0; 783 int iEnd = 0; 783 int iEnd = 0; 784 int iPos = 0; 784 int iPos = 0; 785 int nWord = 0; 785 int nWord = 0; 786 786 ................................................................................................................................................................................ 836 rc = fts3PendingTermsAddOne( 836 rc = fts3PendingTermsAddOne( 837 p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix 837 p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix 838 ); 838 ); 839 } 839 } 840 } 840 } 841 841 842 pModule->xClose(pCsr); 842 pModule->xClose(pCsr); 843 *pnWord = nWord; | 843 *pnWord += nWord; 844 return (rc==SQLITE_DONE ? SQLITE_OK : rc); 844 return (rc==SQLITE_DONE ? SQLITE_OK : rc); 845 } 845 } 846 846 847 /* 847 /* 848 ** Calling this function indicates that subsequent calls to 848 ** Calling this function indicates that subsequent calls to 849 ** fts3PendingTermsAdd() are to add term/position-list pairs for the 849 ** fts3PendingTermsAdd() are to add term/position-list pairs for the 850 ** contents of the document with docid iDocid. 850 ** contents of the document with docid iDocid. ................................................................................................................................................................................ 1040 ** (an integer) of a row about to be deleted. Remove all terms from the 1040 ** (an integer) of a row about to be deleted. Remove all terms from the 1041 ** full-text index. 1041 ** full-text index. 1042 */ 1042 */ 1043 static void fts3DeleteTerms( 1043 static void fts3DeleteTerms( 1044 int *pRC, /* Result code */ 1044 int *pRC, /* Result code */ 1045 Fts3Table *p, /* The FTS table to delete from */ 1045 Fts3Table *p, /* The FTS table to delete from */ 1046 sqlite3_value *pRowid, /* The docid to be deleted */ 1046 sqlite3_value *pRowid, /* The docid to be deleted */ 1047 u32 *aSz /* Sizes of deleted document written here */ | 1047 u32 *aSz, /* Sizes of deleted document written here */ > 1048 int *pbFound /* OUT: Set to true if row really does exist */ 1048 ){ 1049 ){ 1049 int rc; 1050 int rc; 1050 sqlite3_stmt *pSelect; 1051 sqlite3_stmt *pSelect; 1051 1052 > 1053 assert( *pbFound==0 ); 1052 if( *pRC ) return; 1054 if( *pRC ) return; 1053 rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid); 1055 rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid); 1054 if( rc==SQLITE_OK ){ 1056 if( rc==SQLITE_OK ){ 1055 if( SQLITE_ROW==sqlite3_step(pSelect) ){ 1057 if( SQLITE_ROW==sqlite3_step(pSelect) ){ 1056 int i; 1058 int i; 1057 int iLangid = langidFromSelect(p, pSelect); 1059 int iLangid = langidFromSelect(p, pSelect); 1058 rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0)); 1060 rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0)); ................................................................................................................................................................................ 1062 aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i); 1064 aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i); 1063 } 1065 } 1064 if( rc!=SQLITE_OK ){ 1066 if( rc!=SQLITE_OK ){ 1065 sqlite3_reset(pSelect); 1067 sqlite3_reset(pSelect); 1066 *pRC = rc; 1068 *pRC = rc; 1067 return; 1069 return; 1068 } 1070 } > 1071 *pbFound = 1; 1069 } 1072 } 1070 rc = sqlite3_reset(pSelect); 1073 rc = sqlite3_reset(pSelect); 1071 }else{ 1074 }else{ 1072 sqlite3_reset(pSelect); 1075 sqlite3_reset(pSelect); 1073 } 1076 } 1074 *pRC = rc; 1077 *pRC = rc; 1075 } 1078 } ................................................................................................................................................................................ 3286 } 3289 } 3287 } 3290 } 3288 3291 3289 while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ 3292 while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ 3290 int iCol; 3293 int iCol; 3291 int iLangid = langidFromSelect(p, pStmt); 3294 int iLangid = langidFromSelect(p, pStmt); 3292 rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0)); 3295 rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0)); 3293 aSz[p->nColumn] = 0; | 3296 memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1)); 3294 for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){ 3297 for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){ 3295 const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1); 3298 const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1); 3296 rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]); 3299 rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]); 3297 aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1); 3300 aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1); 3298 } 3301 } 3299 if( p->bHasDocsize ){ 3302 if( p->bHasDocsize ){ 3300 fts3InsertDocsize(&rc, p, aSz); 3303 fts3InsertDocsize(&rc, p, aSz); ................................................................................................................................................................................ 5190 ** SQLite value pRowid contains the rowid of a row that may or may not be 5193 ** SQLite value pRowid contains the rowid of a row that may or may not be 5191 ** present in the FTS3 table. If it is, delete it and adjust the contents 5194 ** present in the FTS3 table. If it is, delete it and adjust the contents 5192 ** of subsiduary data structures accordingly. 5195 ** of subsiduary data structures accordingly. 5193 */ 5196 */ 5194 static int fts3DeleteByRowid( 5197 static int fts3DeleteByRowid( 5195 Fts3Table *p, 5198 Fts3Table *p, 5196 sqlite3_value *pRowid, 5199 sqlite3_value *pRowid, 5197 int *pnDoc, | 5200 int *pnChng, /* IN/OUT: Decrement if row is deleted */ 5198 u32 *aSzDel 5201 u32 *aSzDel 5199 ){ 5202 ){ 5200 int isEmpty = 0; | 5203 int rc = SQLITE_OK; /* Return code */ > 5204 int bFound = 0; /* True if *pRowid really is in the table */ > 5205 > 5206 fts3DeleteTerms(&rc, p, pRowid, aSzDel, &bFound); > 5207 if( bFound && rc==SQLITE_OK ){ > 5208 int isEmpty = 0; /* Deleting *pRowid leaves the table empty */ 5201 int rc = fts3IsEmpty(p, pRowid, &isEmpty); | 5209 rc = fts3IsEmpty(p, pRowid, &isEmpty); 5202 if( rc==SQLITE_OK ){ | 5210 if( rc==SQLITE_OK ){ 5203 if( isEmpty ){ | 5211 if( isEmpty ){ 5204 /* Deleting this row means the whole table is empty. In this case | 5212 /* Deleting this row means the whole table is empty. In this case 5205 ** delete the contents of all three tables and throw away any | 5213 ** delete the contents of all three tables and throw away any 5206 ** data in the pendingTerms hash table. */ | 5214 ** data in the pendingTerms hash table. */ 5207 rc = fts3DeleteAll(p, 1); | 5215 rc = fts3DeleteAll(p, 1); 5208 *pnDoc = *pnDoc - 1; < > 5216 *pnChng = 0; > 5217 memset(aSzDel, 0, sizeof(u32) * (p->nColumn+1) * 2); 5209 }else{ | 5218 }else{ 5210 fts3DeleteTerms(&rc, p, pRowid, aSzDel); < > 5219 *pnChng = *pnChng - 1; 5211 if( p->zContentTbl==0 ){ | 5220 if( p->zContentTbl==0 ){ 5212 fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid); | 5221 fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid); 5213 if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1; < 5214 }else{ < 5215 *pnDoc = *pnDoc - 1; < 5216 } | 5222 } 5217 if( p->bHasDocsize ){ | 5223 if( p->bHasDocsize ){ 5218 fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid); | 5224 fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid); > 5225 } 5219 } 5226 } 5220 } 5227 } 5221 } 5228 } 5222 5229 5223 return rc; 5230 return rc; 5224 } 5231 } 5225 5232 ................................................................................................................................................................................ 5270 5277 5271 if( nArg>1 && sqlite3_value_int(apVal[2 + p->nColumn + 2])<0 ){ 5278 if( nArg>1 && sqlite3_value_int(apVal[2 + p->nColumn + 2])<0 ){ 5272 rc = SQLITE_CONSTRAINT; 5279 rc = SQLITE_CONSTRAINT; 5273 goto update_out; 5280 goto update_out; 5274 } 5281 } 5275 5282 5276 /* Allocate space to hold the change in document sizes */ 5283 /* Allocate space to hold the change in document sizes */ 5277 aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 ); | 5284 aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 ); 5278 if( aSzIns==0 ){ | 5285 if( aSzDel==0 ){ 5279 rc = SQLITE_NOMEM; 5286 rc = SQLITE_NOMEM; 5280 goto update_out; 5287 goto update_out; 5281 } 5288 } 5282 aSzDel = &aSzIns[p->nColumn+1]; | 5289 aSzIns = &aSzDel[p->nColumn+1]; 5283 memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2); | 5290 memset(aSzDel, 0, sizeof(aSzDel[0])*(p->nColumn+1)*2); 5284 5291 5285 /* If this is an INSERT operation, or an UPDATE that modifies the rowid 5292 /* If this is an INSERT operation, or an UPDATE that modifies the rowid 5286 ** value, then this operation requires constraint handling. 5293 ** value, then this operation requires constraint handling. 5287 ** 5294 ** 5288 ** If the on-conflict mode is REPLACE, this means that the existing row 5295 ** If the on-conflict mode is REPLACE, this means that the existing row 5289 ** should be deleted from the database before inserting the new row. Or, 5296 ** should be deleted from the database before inserting the new row. Or, 5290 ** if the on-conflict mode is other than REPLACE, then this method must 5297 ** if the on-conflict mode is other than REPLACE, then this method must ................................................................................................................................................................................ 5361 } 5368 } 5362 5369 5363 if( p->bFts4 ){ 5370 if( p->bFts4 ){ 5364 fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng); 5371 fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng); 5365 } 5372 } 5366 5373 5367 update_out: 5374 update_out: 5368 sqlite3_free(aSzIns); | 5375 sqlite3_free(aSzDel); 5369 sqlite3Fts3SegmentsClose(p); 5376 sqlite3Fts3SegmentsClose(p); 5370 return rc; 5377 return rc; 5371 } 5378 } 5372 5379 5373 /* 5380 /* 5374 ** Flush any data in the pending-terms hash table to disk. If successful, 5381 ** Flush any data in the pending-terms hash table to disk. If successful, 5375 ** merge all segments in the database (including the new segment, if 5382 ** merge all segments in the database (including the new segment, if
Changes to test/fts3conf.test
132 INSERT INTO t1(docid, x) VALUES(1, 'a b c'); 132 INSERT INTO t1(docid, x) VALUES(1, 'a b c'); 133 REPLACE INTO t1(docid, x) VALUES('zero', 'd e f'); 133 REPLACE INTO t1(docid, x) VALUES('zero', 'd e f'); 134 } {1 {datatype mismatch}} 134 } {1 {datatype mismatch}} 135 do_execsql_test 2.2.2 { COMMIT } 135 do_execsql_test 2.2.2 { COMMIT } 136 do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}} 136 do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}} 137 fts3_integrity 2.2.4 db t1 137 fts3_integrity 2.2.4 db t1 138 138 > 139 do_execsql_test 3.1 { > 140 CREATE VIRTUAL TABLE t3 USING fts4; > 141 REPLACE INTO t3(docid, content) VALUES (1, 'one two'); > 142 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' > 143 } {X'0100000002000000'} > 144 > 145 do_execsql_test 3.2 { > 146 REPLACE INTO t3(docid, content) VALUES (2, 'one two three four'); > 147 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'four' > 148 } {X'0200000003000000'} > 149 > 150 do_execsql_test 3.3 { > 151 REPLACE INTO t3(docid, content) VALUES (1, 'one two three four five six'); > 152 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' > 153 } {X'0200000005000000'} > 154 > 155 do_execsql_test 3.4 { > 156 UPDATE OR REPLACE t3 SET docid = 2 WHERE docid=1; > 157 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' > 158 } {X'0100000006000000'} > 159 > 160 do_execsql_test 3.5 { > 161 UPDATE OR REPLACE t3 SET docid = 3 WHERE docid=2; > 162 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'six' > 163 } {X'0100000006000000'} > 164 > 165 do_execsql_test 3.6 { > 166 REPLACE INTO t3(docid, content) VALUES (3, 'one two'); > 167 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' > 168 } {X'0100000002000000'} > 169 > 170 do_execsql_test 3.7 { > 171 REPLACE INTO t3(docid, content) VALUES (NULL, 'one two three four'); > 172 REPLACE INTO t3(docid, content) VALUES (NULL, 'one two three four five six'); > 173 SELECT docid FROM t3; > 174 } {3 4 5} > 175 > 176 do_execsql_test 3.8 { > 177 UPDATE OR REPLACE t3 SET docid = 5, content='three four' WHERE docid = 4; > 178 SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' > 179 } {X'0200000002000000'} > 180 139 finish_test 181 finish_test