From: dan Date: Tue, 26 Apr 2011 19:21:34 +0000 (+0000) Subject: Extra tests for fts3. And fixes for conflict-handling related problems in fts3. X-Git-Tag: version-3.7.7~138^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a311b803925df71cb57d6741e87992009eb4e0e3;p=thirdparty%2Fsqlite.git Extra tests for fts3. And fixes for conflict-handling related problems in fts3. FossilOrigin-Name: fb4a355871d9482ccb28b6ba03b842b3cc87b696 --- diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index c7e06d4bb6..52b853a9a2 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -3544,8 +3544,19 @@ static int fts3RenameMethod( return rc; } +static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ + return sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab); +} +static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ + return SQLITE_OK; +} +static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ + sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab); + return SQLITE_OK; +} + static const sqlite3_module fts3Module = { - /* iVersion */ 0, + /* iVersion */ 1, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, @@ -3565,6 +3576,9 @@ static const sqlite3_module fts3Module = { /* xRollback */ fts3RollbackMethod, /* xFindFunction */ fts3FindFunctionMethod, /* xRename */ fts3RenameMethod, + /* xSavepoint */ fts3SavepointMethod, + /* xRelease */ fts3ReleaseMethod, + /* xRollbackTo */ fts3RollbackToMethod, }; /* @@ -3611,6 +3625,11 @@ int sqlite3Fts3Init(sqlite3 *db){ sqlite3Fts3IcuTokenizerModule(&pIcu); #endif +#ifdef SQLITE_TEST + rc = sqlite3Fts3InitTerm(db); + if( rc!=SQLITE_OK ) return rc; +#endif + rc = sqlite3Fts3InitAux(db); if( rc!=SQLITE_OK ) return rc; diff --git a/ext/fts3/fts3_term.c b/ext/fts3/fts3_term.c new file mode 100644 index 0000000000..ce581633e1 --- /dev/null +++ b/ext/fts3/fts3_term.c @@ -0,0 +1,346 @@ +/* +** 2011 Jan 27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +*/ + +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) +#ifdef SQLITE_TEST + +#include "fts3Int.h" +#include +#include + +typedef struct Fts3termTable Fts3termTable; +typedef struct Fts3termCursor Fts3termCursor; + +struct Fts3termTable { + sqlite3_vtab base; /* Base class used by SQLite core */ + Fts3Table *pFts3Tab; +}; + +struct Fts3termCursor { + sqlite3_vtab_cursor base; /* Base class used by SQLite core */ + Fts3SegReaderCursor csr; /* Must be right after "base" */ + Fts3SegFilter filter; + + int isEof; /* True if cursor is at EOF */ + char *pNext; + + sqlite3_int64 iRowid; /* Current 'rowid' value */ + sqlite3_int64 iDocid; /* Current 'docid' value */ + int iCol; /* Current 'col' value */ + int iPos; /* Current 'pos' value */ +}; + +/* +** Schema of the terms table. +*/ +#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, docid, col, pos)" + +/* +** This function does all the work for both the xConnect and xCreate methods. +** These tables have no persistent representation of their own, so xConnect +** and xCreate are identical operations. +*/ +static int fts3termConnectMethod( + sqlite3 *db, /* Database connection */ + void *pUnused, /* Unused */ + int argc, /* Number of elements in argv array */ + const char * const *argv, /* xCreate/xConnect argument array */ + sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ + char **pzErr /* OUT: sqlite3_malloc'd error message */ +){ + char const *zDb; /* Name of database (e.g. "main") */ + char const *zFts3; /* Name of fts3 table */ + int nDb; /* Result of strlen(zDb) */ + int nFts3; /* Result of strlen(zFts3) */ + int nByte; /* Bytes of space to allocate here */ + int rc; /* value returned by declare_vtab() */ + Fts3termTable *p; /* Virtual table object to return */ + + UNUSED_PARAMETER(pUnused); + + /* The user should specify a single argument - the name of an fts3 table. */ + if( argc!=4 ){ + *pzErr = sqlite3_mprintf( + "wrong number of arguments to fts4term constructor" + ); + return SQLITE_ERROR; + } + + zDb = argv[1]; + nDb = strlen(zDb); + zFts3 = argv[3]; + nFts3 = strlen(zFts3); + + rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); + if( rc!=SQLITE_OK ) return rc; + + nByte = sizeof(Fts3termTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; + p = (Fts3termTable *)sqlite3_malloc(nByte); + if( !p ) return SQLITE_NOMEM; + memset(p, 0, nByte); + + p->pFts3Tab = (Fts3Table *)&p[1]; + p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; + p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; + p->pFts3Tab->db = db; + + memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); + memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); + sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); + + *ppVtab = (sqlite3_vtab *)p; + return SQLITE_OK; +} + +/* +** This function does the work for both the xDisconnect and xDestroy methods. +** These tables have no persistent representation of their own, so xDisconnect +** and xDestroy are identical operations. +*/ +static int fts3termDisconnectMethod(sqlite3_vtab *pVtab){ + Fts3termTable *p = (Fts3termTable *)pVtab; + Fts3Table *pFts3 = p->pFts3Tab; + int i; + + /* Free any prepared statements held */ + for(i=0; iaStmt); i++){ + sqlite3_finalize(pFts3->aStmt[i]); + } + sqlite3_free(pFts3->zSegmentsTbl); + sqlite3_free(p); + return SQLITE_OK; +} + +#define FTS4AUX_EQ_CONSTRAINT 1 +#define FTS4AUX_GE_CONSTRAINT 2 +#define FTS4AUX_LE_CONSTRAINT 4 + +/* +** xBestIndex - Analyze a WHERE and ORDER BY clause. +*/ +static int fts3termBestIndexMethod( + sqlite3_vtab *pVTab, + sqlite3_index_info *pInfo +){ + UNUSED_PARAMETER(pVTab); + UNUSED_PARAMETER(pInfo); + return SQLITE_OK; +} + +/* +** xOpen - Open a cursor. +*/ +static int fts3termOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ + Fts3termCursor *pCsr; /* Pointer to cursor object to return */ + + UNUSED_PARAMETER(pVTab); + + pCsr = (Fts3termCursor *)sqlite3_malloc(sizeof(Fts3termCursor)); + if( !pCsr ) return SQLITE_NOMEM; + memset(pCsr, 0, sizeof(Fts3termCursor)); + + *ppCsr = (sqlite3_vtab_cursor *)pCsr; + return SQLITE_OK; +} + +/* +** xClose - Close a cursor. +*/ +static int fts3termCloseMethod(sqlite3_vtab_cursor *pCursor){ + Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab; + Fts3termCursor *pCsr = (Fts3termCursor *)pCursor; + + sqlite3Fts3SegmentsClose(pFts3); + sqlite3Fts3SegReaderFinish(&pCsr->csr); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +/* +** xNext - Advance the cursor to the next row, if any. +*/ +static int fts3termNextMethod(sqlite3_vtab_cursor *pCursor){ + Fts3termCursor *pCsr = (Fts3termCursor *)pCursor; + Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab; + int rc; + sqlite3_int64 v; + + /* Increment our pretend rowid value. */ + pCsr->iRowid++; + + /* Advance to the next term in the full-text index. */ + if( pCsr->csr.aDoclist==0 + || pCsr->pNext>=&pCsr->csr.aDoclist[pCsr->csr.nDoclist-1] + ){ + rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr); + if( rc!=SQLITE_ROW ){ + pCsr->isEof = 1; + return rc; + } + + pCsr->iCol = 0; + pCsr->iPos = 0; + pCsr->iDocid = 0; + pCsr->pNext = pCsr->csr.aDoclist; + + /* Read docid */ + pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &pCsr->iDocid); + } + + pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); + if( v==0 ){ + pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); + pCsr->iDocid += v; + pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); + pCsr->iCol = 0; + pCsr->iPos = 0; + } + + if( v==1 ){ + pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); + pCsr->iCol += v; + pCsr->iPos = 0; + pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); + } + + pCsr->iPos += (v - 2); + + return SQLITE_OK; +} + +/* +** xFilter - Initialize a cursor to point at the start of its data. +*/ +static int fts3termFilterMethod( + sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ + int idxNum, /* Strategy index */ + const char *idxStr, /* Unused */ + int nVal, /* Number of elements in apVal */ + sqlite3_value **apVal /* Arguments for the indexing scheme */ +){ + Fts3termCursor *pCsr = (Fts3termCursor *)pCursor; + Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab; + int rc; + + UNUSED_PARAMETER(nVal); + UNUSED_PARAMETER(idxNum); + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(apVal); + + assert( idxStr==0 && idxNum==0 ); + + /* In case this cursor is being reused, close and zero it. */ + testcase(pCsr->filter.zTerm); + sqlite3Fts3SegReaderFinish(&pCsr->csr); + memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); + + pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; + pCsr->filter.flags |= FTS3_SEGMENT_SCAN; + + rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL, + pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr + ); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter); + } + if( rc==SQLITE_OK ){ + rc = fts3termNextMethod(pCursor); + } + return rc; +} + +/* +** xEof - Return true if the cursor is at EOF, or false otherwise. +*/ +static int fts3termEofMethod(sqlite3_vtab_cursor *pCursor){ + Fts3termCursor *pCsr = (Fts3termCursor *)pCursor; + return pCsr->isEof; +} + +/* +** xColumn - Return a column value. +*/ +static int fts3termColumnMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ + int iCol /* Index of column to read value from */ +){ + Fts3termCursor *p = (Fts3termCursor *)pCursor; + + assert( iCol>=0 && iCol<=3 ); + switch( iCol ){ + case 0: + sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT); + break; + case 1: + sqlite3_result_int64(pCtx, p->iDocid); + break; + case 2: + sqlite3_result_int64(pCtx, p->iCol); + break; + default: + sqlite3_result_int64(pCtx, p->iPos); + break; + } + + return SQLITE_OK; +} + +/* +** xRowid - Return the current rowid for the cursor. +*/ +static int fts3termRowidMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite_int64 *pRowid /* OUT: Rowid value */ +){ + Fts3termCursor *pCsr = (Fts3termCursor *)pCursor; + *pRowid = pCsr->iRowid; + return SQLITE_OK; +} + +/* +** Register the fts3term module with database connection db. Return SQLITE_OK +** if successful or an error code if sqlite3_create_module() fails. +*/ +int sqlite3Fts3InitTerm(sqlite3 *db){ + static const sqlite3_module fts3term_module = { + 0, /* iVersion */ + fts3termConnectMethod, /* xCreate */ + fts3termConnectMethod, /* xConnect */ + fts3termBestIndexMethod, /* xBestIndex */ + fts3termDisconnectMethod, /* xDisconnect */ + fts3termDisconnectMethod, /* xDestroy */ + fts3termOpenMethod, /* xOpen */ + fts3termCloseMethod, /* xClose */ + fts3termFilterMethod, /* xFilter */ + fts3termNextMethod, /* xNext */ + fts3termEofMethod, /* xEof */ + fts3termColumnMethod, /* xColumn */ + fts3termRowidMethod, /* xRowid */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindFunction */ + 0 /* xRename */ + }; + int rc; /* Return code */ + + rc = sqlite3_create_module(db, "fts4term", &fts3term_module, 0); + return rc; +} + +#endif +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 9d2bfaae8b..425010a3d0 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -2711,15 +2711,31 @@ int sqlite3Fts3UpdateMethod( ** modify the database file. */ if( nArg>1 ){ - sqlite3_int64 iNewRowid; + /* Find the value object that holds the new rowid value. */ sqlite3_value *pNewRowid = apVal[3+p->nColumn]; if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){ pNewRowid = apVal[1]; } + if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && ( sqlite3_value_type(apVal[0])==SQLITE_NULL || sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid) )){ + /* The new rowid is not NULL (in this case the rowid will be + ** automatically assigned and there is no chance of a conflict), and + ** the statement is either an INSERT or an UPDATE that modifies the + ** rowid column. So if the conflict mode is REPLACE, then delete any + ** existing row with rowid=pNewRowid. + ** + ** Or, if the conflict mode is not REPLACE, insert the new record into + ** the %_content table. If we hit the duplicate rowid constraint (or any + ** other error) while doing so, return immediately. + ** + ** This branch may also run if pNewRowid contains a value that cannot + ** be losslessly converted to an integer. In this case, the eventual + ** call to fts3InsertData() (either just below or further on in this + ** function) will return SQLITE_MISMATCH. + */ if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){ rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel); }else{ diff --git a/main.mk b/main.mk index e1df7a77f1..3df069a71d 100644 --- a/main.mk +++ b/main.mk @@ -299,6 +299,7 @@ TESTSRC2 = \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ + $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c diff --git a/manifest b/manifest index 36dc26d0ca..e810ed1767 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\son\sconflict\sclauses\sto\sfts3/fts4. -D 2011-04-25T18:49:57.773 +C Extra\stests\sfor\sfts3.\sAnd\sfixes\sfor\sconflict-handling\srelated\sproblems\sin\sfts3. +D 2011-04-26T19:21:34.191 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -61,7 +61,7 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 94fa15fc9d6290e2ba042c24fc83e272c86a40c6 +F ext/fts3/fts3.c ce37973c86f15711a020fa629d8f95cfd642ebc3 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf @@ -71,10 +71,11 @@ F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295 F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2 F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62 +F ext/fts3/fts3_term.c c1dbc904ab1c2d687b97643c671795456228ab22 F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3 F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d -F ext/fts3/fts3_write.c 388a7c7119f322d8fd4a5c19c9bd5793da47ccce +F ext/fts3/fts3_write.c b4dfd76d61adb183b87c56573a1bdd0e1d1501da F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 @@ -101,7 +102,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 -F main.mk bd4e376deea4704b2bd9c77a4e6f0fa3de25c495 +F main.mk 496cec8b7890e39127532294e28e5e1d1b1beae1 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac @@ -178,9 +179,9 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 -F src/sqlite.h.in 92f2daa48c1926d79db79229fb583cdb22d2d4c5 +F src/sqlite.h.in 3dc514ef85adfdb6377abee4fb780b420fc43f5e F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 -F src/sqliteInt.h 5facb244a286e5c9ecd2f59758019f24a9245c8e +F src/sqliteInt.h 1ec9fa7b728c486e526ec012f73fdfb244238dfc F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -235,15 +236,15 @@ F src/update.c 5bcb56e5c7380a2eecb0e71891dbd4ad7437748f F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60 F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e -F src/vdbe.c ac7aab1148964422b0a91ae5d50d31724fbd82ec +F src/vdbe.c b6396cb75bead0d163577b834cbcf4dcd0cea231 F src/vdbe.h 8a675fefdf7119441fe817c800a9a52440c2e797 F src/vdbeInt.h fe8f58d305e629fff02f61f655aca1d299f1f6ae F src/vdbeapi.c e0e2672e0a96ae3f8575c8ecd02912a3e8a554a1 -F src/vdbeaux.c 9ae5074b19bdff2d8806a278533956fb281510d5 +F src/vdbeaux.c 5c4cd4be10b8247061f97b77fa2b0a23728d43ed F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 -F src/vtab.c 0e89db3e7416ccdab5138883d69ed8006a7e992c +F src/vtab.c bcfd5a8b0a4951a60658cdd887a929f6c3816fdf F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794 F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f @@ -455,7 +456,7 @@ F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c -F test/fts3conf.test 2dc3bce3fe20d1e9b0ecd27d4040d07a2b79d16b +F test/fts3conf.test 03e5baecc3a1c82fc50fc75789bc1e13861f47fe F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 @@ -931,10 +932,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P ddeea5ab5f6c0c4a86cdfbbb9f24d9d54bf8d301 -R ca5f1cf4bf0f7df53478a4e115a67980 -T *branch * vtab-conflict -T *sym-vtab-conflict * -T -sym-trunk * +P 6d2633a6d0a9bb88fb1a6adac0827dc51df2d4d2 +R 3d729f606d16bb9d30fc86b95bfd6047 U dan -Z dd80c4f8ae24b83ff1c8f809d0d54000 +Z dd1f59cf11e82349f934a1bcc8d1254a diff --git a/manifest.uuid b/manifest.uuid index 942ac84499..923ce0ed96 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6d2633a6d0a9bb88fb1a6adac0827dc51df2d4d2 \ No newline at end of file +fb4a355871d9482ccb28b6ba03b842b3cc87b696 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 2c3b1a5031..0f18c07b40 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4607,6 +4607,11 @@ struct sqlite3_module { void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); + /* The methods above are in version 0 of the sqlite_module object. Those + ** below are for version 1 and greater. */ + int (*xSavepoint)(sqlite3_vtab *pVTab, int); + int (*xRelease)(sqlite3_vtab *pVTab, int); + int (*xRollbackTo)(sqlite3_vtab *pVTab, int); }; /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f000a228ca..71cb368cc6 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3049,6 +3049,7 @@ void sqlite3AutoLoadExtensions(sqlite3*); # define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabUnlockList(X) +# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK #else void sqlite3VtabClear(sqlite3 *db, Table*); int sqlite3VtabSync(sqlite3 *db, char **); @@ -3057,6 +3058,7 @@ void sqlite3AutoLoadExtensions(sqlite3*); void sqlite3VtabLock(VTable *); void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); + int sqlite3VtabSavepoint(sqlite3 *, int, int); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif void sqlite3VtabMakeWritable(Parse*,Table*); diff --git a/src/vdbe.c b/src/vdbe.c index 71756f8fca..b348a397f4 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2821,7 +2821,11 @@ case OP_Transaction: { db->nStatement++; p->iStatement = db->nSavepoint + db->nStatement; } - rc = sqlite3BtreeBeginStmt(pBt, p->iStatement); + + rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeBeginStmt(pBt, p->iStatement); + } /* Store the current value of the database handles deferred constraint ** counter. If the statement transaction needs to be rolled back, diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 4d4bb224f9..55e10eb70c 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2013,6 +2013,15 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ db->nStatement--; p->iStatement = 0; + if( rc==SQLITE_OK ){ + if( eOp==SAVEPOINT_ROLLBACK ){ + rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); + } + if( rc==SQLITE_OK ){ + rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); + } + } + /* If the statement transaction is being rolled back, also restore the ** database handles deferred constraint counter to the value it had when ** the statement transaction was opened. */ diff --git a/src/vtab.c b/src/vtab.c index 51826e91f9..51ff9fd910 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -836,7 +836,6 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ if( pModule->xBegin ){ int i; - /* If pVtab is already in the aVTrans array, return early */ for(i=0; inVTrans; i++){ if( db->aVTrans[i]==pVTab ){ @@ -853,6 +852,32 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ return rc; } +int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ + int i; + int rc = SQLITE_OK; + + assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN ); + + for(i=0; rc==SQLITE_OK && inVTrans; i++){ + sqlite3_vtab *pVtab = db->aVTrans[i]->pVtab; + sqlite3_module *pMod = db->aVTrans[i]->pMod->pModule; + if( pMod->iVersion>=1 ){ + switch( op ){ + case SAVEPOINT_BEGIN: + rc = pMod->xSavepoint(pVtab, iSavepoint); + break; + case SAVEPOINT_ROLLBACK: + rc = pMod->xRollbackTo(pVtab, iSavepoint); + break; + default: + rc = pMod->xRelease(pVtab, iSavepoint); + break; + } + } + } + return rc; +} + /* ** The first parameter (pDef) is a function implementation. The ** second parameter (pExpr) is the first argument to this function. diff --git a/test/fts3conf.test b/test/fts3conf.test index c3f36d7ff9..7bcf66ef53 100644 --- a/test/fts3conf.test +++ b/test/fts3conf.test @@ -22,7 +22,43 @@ ifcapable !fts3 { return } -do_execsql_test 1.0 { + +proc fts3_integrity {tn db tbl} { + + if {[sqlite3_get_autocommit $db]==0} { + error "fts3_integrity does not work with an open transaction" + } + + set sql [db one {SELECT sql FROM sqlite_master WHERE name = $tbl}] + regexp -nocase {[^(]* using (.*)} $sql -> tail + set cols [list] + $db eval "PRAGMA table_info($tbl)" { + lappend cols $name + } + set cols [join [concat docid $cols] ,] + + $db eval [subst { + CREATE VIRTUAL TABLE fts3check USING fts4term($tbl); + CREATE VIRTUAL TABLE temp.fts3check2 USING $tail; + INSERT INTO temp.fts3check2($cols) SELECT docid, * FROM $tbl; + CREATE VIRTUAL TABLE temp.fts3check3 USING fts4term(fts3check2); + }] + + set m1 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check}] + set m2 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check3}] + + $db eval { + DROP TABLE fts3check; + DROP TABLE temp.fts3check2; + DROP TABLE temp.fts3check3; + } + + uplevel [list do_test $tn [list set {} $m1] $m2] +} + + + +do_execsql_test 1.0.1 { CREATE VIRTUAL TABLE t1 USING fts3(x); INSERT INTO t1(rowid, x) VALUES(1, 'a b c d'); INSERT INTO t1(rowid, x) VALUES(2, 'e f g h'); @@ -36,6 +72,9 @@ db_save_and_close set T1 "INTO t1(rowid, x) VALUES(1, 'x')" set T2 "INTO t1(rowid, x) SELECT * FROM source" +set T3 "t1 SET docid = 2 WHERE docid = 1" +set T4 "t1 SET docid = CASE WHEN docid = 1 THEN 4 ELSE 3 END WHERE docid <=2" + foreach {tn sql constraint data} [subst { 1 "INSERT OR ROLLBACK $T1" 1 {{a b c d} {e f g h}} 2 "INSERT OR ABORT $T1" 1 {{a b c d} {e f g h} {i j k l}} @@ -48,6 +87,18 @@ foreach {tn sql constraint data} [subst { 8 "INSERT OR FAIL $T2" 1 {{a b c d} {e f g h} {i j k l} z} 9 "INSERT OR IGNORE $T2" 0 {{a b c d} {e f g h} {i j k l} z} 10 "INSERT OR REPLACE $T2" 0 {{a b c d} y {i j k l} z} + + 11 "UPDATE OR ROLLBACK $T3" 1 {{a b c d} {e f g h}} + 12 "UPDATE OR ABORT $T3" 1 {{a b c d} {e f g h} {i j k l}} + 13 "UPDATE OR FAIL $T3" 1 {{a b c d} {e f g h} {i j k l}} + 14 "UPDATE OR IGNORE $T3" 0 {{a b c d} {e f g h} {i j k l}} + 15 "UPDATE OR REPLACE $T3" 0 {{a b c d} {i j k l}} + + 16 "UPDATE OR ROLLBACK $T4" 1 {{a b c d} {e f g h}} + 17 "UPDATE OR ABORT $T4" 1 {{a b c d} {e f g h} {i j k l}} + 18 "UPDATE OR FAIL $T4" 1 {{e f g h} {i j k l} {a b c d}} + 19 "UPDATE OR IGNORE $T4" 0 {{e f g h} {i j k l} {a b c d}} + 20 "UPDATE OR REPLACE $T4" 0 {{e f g h} {a b c d}} }] { db_restore_and_reopen execsql { @@ -58,6 +109,10 @@ foreach {tn sql constraint data} [subst { set R(1) {1 {constraint failed}} do_catchsql_test 1.$tn.1 $sql $R($constraint) do_catchsql_test 1.$tn.2 { SELECT * FROM t1 } [list 0 $data] + catchsql COMMIT + + fts3_integrity 1.$tn.3 db t1 } + finish_test