Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix an fts5 bug in handling writes while there are active cursors. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | fts5 |
Files: | files | file ages | folders |
SHA1: |
07f70955392697556ca2951c9b6c3a52 |
User & Date: | dan 2015-04-28 20:24:50 |
Context
2015-04-29
| ||
20:54 | Improve fts5 tests. check-in: c1f07a3a user: dan tags: fts5 | |
2015-04-28
| ||
20:24 | Fix an fts5 bug in handling writes while there are active cursors. check-in: 07f70955 user: dan tags: fts5 | |
18:35 | Improve coverage of fts5 tests. check-in: 8e8136f2 user: dan tags: fts5 | |
Changes
Changes to ext/fts5/fts5.c.
182 182 /* 183 183 ** Values for Fts5Cursor.csrflags 184 184 */ 185 185 #define FTS5CSR_REQUIRE_CONTENT 0x01 186 186 #define FTS5CSR_REQUIRE_DOCSIZE 0x02 187 187 #define FTS5CSR_EOF 0x04 188 188 #define FTS5CSR_FREE_ZRANK 0x08 189 +#define FTS5CSR_REQUIRE_RESEEK 0x10 189 190 190 191 /* 191 192 ** Macros to Set(), Clear() and Test() cursor flags. 192 193 */ 193 194 #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) 194 195 #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag)) 195 196 #define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag)) ................................................................................ 265 266 return pTab->pConfig->eContent==FTS5_CONTENT_NONE; 266 267 } 267 268 268 269 /* 269 270 ** Delete a virtual table handle allocated by fts5InitVtab(). 270 271 */ 271 272 static void fts5FreeVtab(Fts5Table *pTab){ 272 - int rc = SQLITE_OK; 273 273 if( pTab ){ 274 274 sqlite3Fts5IndexClose(pTab->pIndex); 275 275 sqlite3Fts5StorageClose(pTab->pStorage); 276 276 sqlite3Fts5ConfigFree(pTab->pConfig); 277 277 sqlite3_free(pTab); 278 278 } 279 - return rc; 280 279 } 281 280 282 281 /* 283 282 ** The xDisconnect() virtual table method. 284 283 */ 285 284 static int fts5DisconnectMethod(sqlite3_vtab *pVtab){ 286 285 fts5FreeVtab((Fts5Table*)pVtab); ................................................................................ 603 602 604 603 pSorter->aPoslist = a; 605 604 fts5CsrNewrow(pCsr); 606 605 } 607 606 608 607 return rc; 609 608 } 609 + 610 + 611 +/* 612 +** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors 613 +** open on table pTab. 614 +*/ 615 +static void fts5TripCursors(Fts5Table *pTab){ 616 + Fts5Cursor *pCsr; 617 + for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ 618 + if( FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_MATCH 619 + && pCsr->base.pVtab==(sqlite3_vtab*)pTab 620 + ){ 621 + CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK); 622 + } 623 + } 624 +} 625 + 626 +/* 627 +** If the REQUIRE_RESEEK flag is set on the cursor passed as the first 628 +** argument, close and reopen all Fts5IndexIter iterators that the cursor 629 +** is using. Then attempt to move the cursor to a rowid equal to or laster 630 +** (in the cursors sort order - ASC or DESC) than the current rowid. 631 +** 632 +** If the new rowid is not equal to the old, set output parameter *pbSkip 633 +** to 1 before returning. Otherwise, leave it unchanged. 634 +** 635 +** Return SQLITE_OK if successful or if no reseek was required, or an 636 +** error code if an error occurred. 637 +*/ 638 +static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ 639 + int rc = SQLITE_OK; 640 + assert( *pbSkip==0 ); 641 + if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){ 642 + Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); 643 + int bDesc = ((pCsr->idxNum & FTS5_ORDER_DESC) ? 1 : 0); 644 + i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr); 645 + 646 + rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, bDesc); 647 + while( rc==SQLITE_OK && sqlite3Fts5ExprEof(pCsr->pExpr)==0 ){ 648 + i64 ii = sqlite3Fts5ExprRowid(pCsr->pExpr); 649 + if( ii==iRowid ) break; 650 + if( (bDesc && ii<iRowid) || (bDesc==0 && ii>iRowid) ){ 651 + *pbSkip = 1; 652 + break; 653 + } 654 + rc = sqlite3Fts5ExprNext(pCsr->pExpr); 655 + } 656 + 657 + CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK); 658 + fts5CsrNewrow(pCsr); 659 + if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ 660 + CsrFlagSet(pCsr, FTS5CSR_EOF); 661 + } 662 + } 663 + return rc; 664 +} 665 + 610 666 611 667 /* 612 668 ** Advance the cursor to the next row in the table that matches the 613 669 ** search criteria. 614 670 ** 615 671 ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned 616 672 ** even if we reach end-of-file. The fts5EofMethod() will be called 617 673 ** subsequently to determine whether or not an EOF was hit. 618 674 */ 619 675 static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ 620 676 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; 621 677 int ePlan = FTS5_PLAN(pCsr->idxNum); 622 - int rc = SQLITE_OK; 678 + int bSkip = 0; 679 + int rc; 680 + 681 + if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; 623 682 624 683 switch( ePlan ){ 625 684 case FTS5_PLAN_MATCH: 626 685 case FTS5_PLAN_SOURCE: 627 686 rc = sqlite3Fts5ExprNext(pCsr->pExpr); 628 687 if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ 629 688 CsrFlagSet(pCsr, FTS5CSR_EOF); ................................................................................ 1152 1211 1153 1212 eType0 = sqlite3_value_type(apVal[0]); 1154 1213 eConflict = sqlite3_vtab_on_conflict(pConfig->db); 1155 1214 1156 1215 assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); 1157 1216 assert( pVtab->zErrMsg==0 ); 1158 1217 1218 + fts5TripCursors(pTab); 1159 1219 if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){ 1160 1220 if( fts5IsContentless(pTab) ){ 1161 1221 pTab->base.zErrMsg = sqlite3_mprintf( 1162 1222 "cannot %s contentless fts5 table: %s", 1163 1223 (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName 1164 1224 ); 1165 1225 rc = SQLITE_ERROR; ................................................................................ 1192 1252 /* 1193 1253 ** Implementation of xSync() method. 1194 1254 */ 1195 1255 static int fts5SyncMethod(sqlite3_vtab *pVtab){ 1196 1256 int rc; 1197 1257 Fts5Table *pTab = (Fts5Table*)pVtab; 1198 1258 fts5CheckTransactionState(pTab, FTS5_SYNC, 0); 1259 + fts5TripCursors(pTab); 1199 1260 rc = sqlite3Fts5StorageSync(pTab->pStorage, 1); 1200 1261 return rc; 1201 1262 } 1202 1263 1203 1264 /* 1204 1265 ** Implementation of xBegin() method. 1205 1266 */ ................................................................................ 1713 1774 ** The xSavepoint() method. 1714 1775 ** 1715 1776 ** Flush the contents of the pending-terms table to disk. 1716 1777 */ 1717 1778 static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ 1718 1779 Fts5Table *pTab = (Fts5Table*)pVtab; 1719 1780 fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); 1781 + fts5TripCursors(pTab); 1720 1782 return sqlite3Fts5StorageSync(pTab->pStorage, 0); 1721 1783 } 1722 1784 1723 1785 /* 1724 1786 ** The xRelease() method. 1725 1787 ** 1726 1788 ** This is a no-op. 1727 1789 */ 1728 1790 static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ 1729 1791 Fts5Table *pTab = (Fts5Table*)pVtab; 1730 1792 fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); 1793 + fts5TripCursors(pTab); 1731 1794 return sqlite3Fts5StorageSync(pTab->pStorage, 0); 1732 1795 } 1733 1796 1734 1797 /* 1735 1798 ** The xRollbackTo() method. 1736 1799 ** 1737 1800 ** Discard the contents of the pending terms table. 1738 1801 */ 1739 1802 static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ 1740 1803 Fts5Table *pTab = (Fts5Table*)pVtab; 1741 1804 fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); 1805 + fts5TripCursors(pTab); 1742 1806 return sqlite3Fts5StorageRollback(pTab->pStorage); 1743 1807 } 1744 1808 1745 1809 /* 1746 1810 ** Register a new auxiliary function with global context pGlobal. 1747 1811 */ 1748 1812 static int fts5CreateAux(
Changes to ext/fts5/fts5_expr.c.
772 772 int i, j; 773 773 int rc = SQLITE_OK; 774 774 775 775 for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){ 776 776 pPhrase = pNear->apPhrase[i]; 777 777 for(j=0; j<pPhrase->nTerm; j++){ 778 778 pTerm = &pPhrase->aTerm[j]; 779 + if( pTerm->pIter ){ 780 + sqlite3Fts5IterClose(pTerm->pIter); 781 + pTerm->pIter = 0; 782 + } 779 783 rc = sqlite3Fts5IndexQuery( 780 784 pExpr->pIndex, pTerm->zTerm, strlen(pTerm->zTerm), 781 785 (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | 782 786 (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), 783 787 &pTerm->pIter 784 788 ); 785 789 assert( rc==SQLITE_OK || pTerm->pIter==0 );
Added ext/fts5/test/fts5restart.test.
1 +# 2015 April 28 2 +# 3 +# The author disclaims copyright to this source code. In place of 4 +# a legal notice, here is a blessing: 5 +# 6 +# May you do good and not evil. 7 +# May you find forgiveness for yourself and forgive others. 8 +# May you share freely, never taking more than you give. 9 +# 10 +#*********************************************************************** 11 +# 12 +# This file focuses on testing the planner (xBestIndex function). 13 +# 14 + 15 +source [file join [file dirname [info script]] fts5_common.tcl] 16 +set testprefix fts5restart 17 + 18 +do_execsql_test 1.0 { 19 + CREATE VIRTUAL TABLE f1 USING fts5(ff); 20 +} 21 + 22 +do_test 1.1 { 23 + for {set i 1} {$i < 1000} {incr i} { 24 + execsql { INSERT INTO f1 VALUES('a b c d e') } 25 + lappend lRowid $i 26 + } 27 +} {} 28 + 29 +do_execsql_test 1.2 { 30 + SELECT rowid FROM f1 WHERE f1 MATCH 'c'; 31 +} $lRowid 32 + 33 +breakpoint 34 +do_test 1.3 { 35 + set res [list] 36 + db eval { SELECT rowid FROM f1 WHERE f1 MATCH 'c' } { 37 + if {$rowid == 100} { 38 + execsql { INSERT INTO f1(f1) VALUES('optimize') } 39 + } 40 + lappend res $rowid 41 + } 42 + set res 43 +} $lRowid 44 + 45 + 46 + 47 +finish_test 48 +