diff --git a/manifest b/manifest index 484e81c049..36e7f8c989 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplifications\sto\spager.c\sin\ssupport\sof\sstructural\scoverage\stesting.\s(CVS\s6927) -D 2009-07-24T16:32:01 +C Allow\svirtual\stables\sto\sbe\sused\sin\sshared-cache\smode.\s(CVS\s6928) +D 2009-07-24T17:58:53 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in df9359da7a726ccb67a45db905c5447d5c00c6ef F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -99,21 +99,21 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad -F src/alter.c 95f41d957f56407aac6224041ca5b954042318d1 +F src/alter.c 8b42cace4f8e312de596807ba2685179da64fec4 F src/analyze.c e239496cfb5394ac8867f1c112905ddab8d01cd9 F src/attach.c 13995348fc5a26cdd136a50806faf292aabc173f F src/auth.c 802a9439dfa0b8c208b10055cba400e82ef18025 F src/backup.c 6f1c2d9862c8a3feb7739dfcca02c1f5352e37f3 F src/bitvec.c cfbf6af5b0ababb4f06ed3e75c616dadaf47fcbd F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7 -F src/btree.c 64fbba1140bb06edbac2553cfd339145bbdc2665 +F src/btree.c c1d3d67007f2f49697b4204bf44f2a8f45f750df F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705 -F src/build.c 867028ee9f63f7bc8eb8d4a720bb98cf9b9a12b4 +F src/build.c a15de7c5d020a778b641fca0b2510126843f4b30 F src/callback.c cb68b21b0d4ae7d11ae0e487933bce3323784dcf F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3 -F src/delete.c fb05e577ab273cc8a63b44809aa5078f72f475c1 +F src/delete.c 6f0192ecf2ae97dbbf4cccfa32639b504da70d4a F src/expr.c f6f21604c1367354b28d03c983a83279071a2948 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/func.c 9856373f5315f6b8690d7f07f7191aa9f279ca87 @@ -121,7 +121,7 @@ F src/global.c 448419c44ce0701104c2121b0e06919b44514c0c F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7 F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1 F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb -F src/insert.c 94e51344e01aea6be43e95cd24e8baa014d8c3d6 +F src/insert.c a4bbd811a15f8b24a311753da947d61368685db1 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0 F src/legacy.c 54a11649f27ae55f4fcd6e6b5ba836f839cb8704 F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d @@ -154,16 +154,16 @@ F src/pcache.c 1dae135b70a029f81ed66f6e9b5d0db91480d5d0 F src/pcache.h 9b927ccc5a538e31b4c3bc7eec4f976db42a1324 F src/pcache1.c 6dc833c89feac405dd8b4858232c97e679f182ec F src/pragma.c 9eb44ac1d3dc1ac3ea4f444abe1a10ae8acaa16c -F src/prepare.c 12e556fc9f72a6563db7099697f657d75d4bb91c +F src/prepare.c 312ba96867ae9df6bd1a57cb84a9fd315e61cf8d F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/resolve.c 4a61d03e49b15440878096e6030863fc628828f0 F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f F src/select.c 71748b8e244112cf73df9446c4246c192276c30d F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb -F src/sqlite.h.in 92a8960ca178cfbd73ce71cca65829c6262137ac +F src/sqlite.h.in 5672d9a6e19f80c1c7f1276dbe10e7d51c8fd97b F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h c638fff6a05bb1c546f361e8527385fef0638917 +F src/sqliteInt.h 11d239cc658823c719a3077ea2c7691795002c34 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -200,20 +200,20 @@ F src/test_thread.c b8a1ab7ca1a632f18e8a361880d5d65eeea08eac F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241 F src/tokenize.c af8a56e6a50c5042fc305bfa796275e9bf26ff2b F src/trigger.c c07c5157c58fcdb704f65d5f5e4775276e45bb8b -F src/update.c a1bbe774bce495d62dce3df3f42a5f04c1de173a +F src/update.c 7570d90842f0d9338906eeea443193358869ca97 F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff F src/util.c 861d5b5c58be4921f0a254489ea94cb15f550ef8 F src/vacuum.c 3fe0eebea6d2311c1c2ab2962887d11f7a4dcfb0 -F src/vdbe.c 3f4b02f59287cc65583c0bba371e9c7d047abad2 -F src/vdbe.h 35a648bc3279a120da24f34d9a25213ec15daf8a +F src/vdbe.c 0ce57f8211899b59d1d6f1642f79e75fc212d6d0 +F src/vdbe.h 457b6c70f02885cec1f5225b5e6441d067b55d3f F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007 F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624 -F src/vdbeaux.c a1ed07f91a9d59d39e97b2844554a07c91c9b809 +F src/vdbeaux.c e3943dae17dd29d749a3e9bb42e4eb7b7eca43ed F src/vdbeblob.c a3f3e0e877fc64ea50165eec2855f5ada4477611 F src/vdbemem.c bfc25f9ef4fa914b473303566459552bdb2e008a -F src/vtab.c 00902f289521041712fb0293d0bf8688c7af8e48 +F src/vtab.c b19c4e96dcf2b89b5b2ba48e8ef624e654a59b2c F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04 -F src/where.c cf0d091748c2fa6f7df96e5b08d2db26fd2eb437 +F src/where.c bf56a85ab4f1e17f0c8060c97bd579cb8e0fa000 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 @@ -703,7 +703,7 @@ F test/vtabC.test 1cf7896ab6859bfe3074244b2b0e12de5cbdd766 F test/vtabD.test 74167b1578e5886fe4c886d6bef2fd1406444c42 F test/vtab_alter.test 3a299749fee97ca3d53bd55717f536e4a2284856 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 -F test/vtab_shared.test c19b2555b807ef2ee014c882cdda5bc8d84fcf48 +F test/vtab_shared.test 561305eff0bb574a983c29029ef294a4a895a14c F test/where.test de337a3fe0a459ec7c93db16a519657a90552330 F test/where2.test 45eacc126aabb37959a387aa83e59ce1f1f03820 F test/where3.test 97d3936e6a443b968f1a61cdcc0f673252000e94 @@ -741,7 +741,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746 -P 2d2f42ca0a24ed8b33f9ad560c76a6c1301c757b -R d83586425c23d3015fdf0b2326ebc67f -U drh -Z eeeb8d7f7200949823d967a49549fa65 +P 7222ad2667b95d6021d9ae47f548b76b224f46aa +R 477ef377a502774f707ff161355dd337 +U danielk1977 +Z b547740a8551902f628dba2106d37250 diff --git a/manifest.uuid b/manifest.uuid index 135cccaae8..d545e688d1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7222ad2667b95d6021d9ae47f548b76b224f46aa \ No newline at end of file +5d9e767a05e381235e064061043e30cc03a11a07 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 36033a5095..be4a2e0862 100644 --- a/src/alter.c +++ b/src/alter.c @@ -12,7 +12,7 @@ ** This file contains C code routines that used to generate VDBE code ** that implements the ALTER TABLE command. ** -** $Id: alter.c,v 1.61 2009/06/03 11:25:07 danielk1977 Exp $ +** $Id: alter.c,v 1.62 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -277,7 +277,7 @@ void sqlite3AlterRenameTable( #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif - int isVirtualRename = 0; /* True if this is a v-table with an xRename() */ + VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); @@ -332,8 +332,11 @@ void sqlite3AlterRenameTable( if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto exit_rename_table; } - if( IsVirtual(pTab) && pTab->pMod->pModule->xRename ){ - isVirtualRename = 1; + if( IsVirtual(pTab) ){ + pVTab = sqlite3GetVTable(db, pTab); + if( pVTab->pVtab->pModule->xRename==0 ){ + pVTab = 0; + } } #endif @@ -346,7 +349,7 @@ void sqlite3AlterRenameTable( if( v==0 ){ goto exit_rename_table; } - sqlite3BeginWriteOperation(pParse, isVirtualRename, iDb); + sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb); sqlite3ChangeCookie(pParse, iDb); /* If this is a virtual table, invoke the xRename() function if @@ -355,10 +358,10 @@ void sqlite3AlterRenameTable( ** SQLite tables) that are identified by the name of the virtual table. */ #ifndef SQLITE_OMIT_VIRTUALTABLE - if( isVirtualRename ){ + if( pVTab ){ int i = ++pParse->nMem; sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0); - sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); } #endif diff --git a/src/btree.c b/src/btree.c index a855798784..0e902b8fe6 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.701 2009/07/23 01:44:00 drh Exp $ +** $Id: btree.c,v 1.702 2009/07/24 17:58:53 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -1700,7 +1700,6 @@ int sqlite3BtreeOpen( char *zFullPathname = sqlite3Malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; - db->flags |= SQLITE_SharedCache; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; diff --git a/src/build.c b/src/build.c index 0696f1c82d..7136cbc1b3 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.556 2009/07/01 16:12:08 danielk1977 Exp $ +** $Id: build.c,v 1.557 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -162,7 +162,7 @@ void sqlite3FinishCoding(Parse *pParse){ { int i; for(i=0; inVtabLock; i++){ - char *vtab = (char *)pParse->apVtabLock[i]->pVtab; + char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); } pParse->nVtabLock = 0; @@ -424,6 +424,7 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ } assert( iDb==0 ); db->flags &= ~SQLITE_InternChanges; + sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); /* If one or more of the auxiliary database files has been closed, @@ -2002,7 +2003,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( IsVirtual(pTab) ){ code = SQLITE_DROP_VTABLE; - zArg2 = pTab->pMod->zName; + zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName; #endif }else{ if( !OMIT_TEMPDB && iDb==1 ){ diff --git a/src/delete.c b/src/delete.c index cbd8b375e7..a2c97bc15b 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.204 2009/06/23 20:28:54 drh Exp $ +** $Id: delete.c,v 1.205 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -43,16 +43,26 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ ** writable return 0; */ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ - if( ((pTab->tabFlags & TF_Readonly)!=0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && pParse->nested==0) -#ifndef SQLITE_OMIT_VIRTUALTABLE - || (pTab->pMod && pTab->pMod->pModule->xUpdate==0) -#endif + /* A table is not writable under the following circumstances: + ** + ** 1) It is a virtual table and no implementation of the xUpdate method + ** has been provided, or + ** 2) It is a system table (i.e. sqlite_master), this call is not + ** part of a nested parse and writable_schema pragma has not + ** been specified. + ** + ** In either case leave an error message in pParse and return non-zero. + */ + if( ( IsVirtual(pTab) + && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ) + || ( (pTab->tabFlags & TF_Readonly)!=0 + && (pParse->db->flags & SQLITE_WriteSchema)==0 + && pParse->nested==0 ) ){ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); return 1; } + #ifndef SQLITE_OMIT_VIEW if( !viewOk && pTab->pSelect ){ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); @@ -264,6 +274,12 @@ void sqlite3DeleteFrom( # define isView 0 #endif + /* If pTab is really a view, make sure it has been initialized. + */ + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto delete_from_cleanup; + } + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto delete_from_cleanup; } @@ -277,12 +293,6 @@ void sqlite3DeleteFrom( } assert(!isView || pTrigger); - /* If pTab is really a view, make sure it has been initialized. - */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto delete_from_cleanup; - } - /* Allocate a cursor used to store the old.* data for a trigger. */ if( pTrigger ){ @@ -443,9 +453,9 @@ void sqlite3DeleteFrom( /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ - const char *pVtab = (const char *)pTab->pVtab; + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); }else #endif { diff --git a/src/insert.c b/src/insert.c index f6d8b1c390..43fbe58515 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.269 2009/06/23 20:28:54 drh Exp $ +** $Id: insert.c,v 1.270 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -131,9 +131,14 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ ** a statement of the form "INSERT INTO SELECT ..." can ** run without using temporary table for the results of the SELECT. */ -static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){ +static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){ + Vdbe *v = sqlite3GetVdbe(p); int i; int iEnd = sqlite3VdbeCurrentAddr(v); +#ifndef SQLITE_OMIT_VIRTUALTABLE + VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0; +#endif + for(i=iStartAddr; iopcode==OP_VOpen && pOp->p4.pVtab==pTab->pVtab ){ + if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pVTab ){ assert( pOp->p4.pVtab!=0 ); assert( pOp->p4type==P4_VTAB ); return 1; @@ -504,15 +509,6 @@ void sqlite3Insert( #endif assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); - /* Ensure that: - * (a) the table is not read-only, - * (b) that if it is a view then ON INSERT triggers exist - */ - if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ - goto insert_cleanup; - } - assert( pTab!=0 ); - /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual ** module table). @@ -521,6 +517,14 @@ void sqlite3Insert( goto insert_cleanup; } + /* Ensure that: + * (a) the table is not read-only, + * (b) that if it is a view then ON INSERT triggers exist + */ + if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ + goto insert_cleanup; + } + /* Allocate a VDBE */ v = sqlite3GetVdbe(pParse); @@ -620,7 +624,7 @@ void sqlite3Insert( ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ - if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){ + if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){ useTempTable = 1; } @@ -976,9 +980,9 @@ void sqlite3Insert( */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, - (const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); }else #endif { diff --git a/src/prepare.c b/src/prepare.c index 4edbee0ac6..55208ea1b9 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -13,7 +13,7 @@ ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.128 2009/07/03 19:19:50 drh Exp $ +** $Id: prepare.c,v 1.129 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -581,6 +581,7 @@ static int sqlite3Prepare( } } + sqlite3VtabUnlockList(db); pParse->db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 29c14c7a2a..e62b4d5fb6 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.459 2009/07/15 11:26:44 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.460 2009/07/24 17:58:53 danielk1977 Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -4331,7 +4331,7 @@ SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* Used internally */ + int nRef; /* NO LONGER USED */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 5427fc7aa7..9368586a36 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.893 2009/07/13 15:52:38 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.894 2009/07/24 17:58:53 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -613,6 +613,7 @@ typedef struct TriggerStack TriggerStack; typedef struct TriggerStep TriggerStep; typedef struct Trigger Trigger; typedef struct UnpackedRecord UnpackedRecord; +typedef struct VTable VTable; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; @@ -839,8 +840,9 @@ struct sqlite3 { #ifndef SQLITE_OMIT_VIRTUALTABLE Hash aModule; /* populated by sqlite3_create_module() */ Table *pVTab; /* vtab with active Connect/Create method */ - sqlite3_vtab **aVTrans; /* Virtual tables with open transactions */ + VTable **aVTrans; /* Virtual tables with open transactions */ int nVTrans; /* Allocated size of aVTrans */ + VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ #endif FuncDefHash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ @@ -905,7 +907,6 @@ struct sqlite3 { #define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */ #define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ -#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ #define SQLITE_ReverseOrder 0x00100000 /* Reverse unordered SELECTs */ /* @@ -1114,6 +1115,56 @@ struct CollSeq { #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ +/* +** An object of this type is created for each virtual table present in +** the database schema. +** +** If the database schema is shared, then there is one instance of this +** structure for each database connection (sqlite3*) that uses the shared +** schema. This is because each database connection requires its own unique +** instance of the sqlite3_vtab* handle used to access the virtual table +** implementation. sqlite3_vtab* handles can not be shared between +** database connections, even when the rest of the in-memory database +** schema is shared, as the implementation often stores the database +** connection handle passed to it via the xConnect() or xCreate() method +** during initialization internally. This database connection handle may +** then used by the virtual table implementation to access real tables +** within the database. So that they appear as part of the callers +** transaction, these accesses need to be made via the same database +** connection as that used to execute SQL operations on the virtual table. +** +** All VTable objects that correspond to a single table in a shared +** database schema are initially stored in a linked-list pointed to by +** the Table.pVTable member variable of the corresponding Table object. +** When an sqlite3_prepare() operation is required to access the virtual +** table, it searches the list for the VTable that corresponds to the +** database connection doing the preparing so as to use the correct +** sqlite3_vtab* handle in the compiled query. +** +** When an in-memory Table object is deleted (for example when the +** schema is being reloaded for some reason), the VTable objects are not +** deleted and the sqlite3_vtab* handles are not xDisconnect()ed +** immediately. Instead, they are moved from the Table.pVTable list to +** another linked list headed by the sqlite3.pDisconnect member of the +** corresponding sqlite3 structure. They are then deleted/xDisconnected +** next time a statement is prepared using said sqlite3*. This is done +** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. +** Refer to comments above function sqlite3VtabUnlockList() for an +** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect +** list without holding the corresponding sqlite3.mutex mutex. +** +** The memory for objects of this type is always allocated by +** sqlite3DbMalloc(), using the connection handle stored in VTable.db as +** the first argument. +*/ +struct VTable { + sqlite3 *db; /* Database connection associated with this table */ + Module *pMod; /* Pointer to module implementation */ + sqlite3_vtab *pVtab; /* Pointer to vtab instance */ + int nRef; /* Number of pointers to this structure */ + VTable *pNext; /* Next in linked list (see above) */ +}; + /* ** Each SQL table is represented in memory by an instance of the ** following structure. @@ -1165,8 +1216,7 @@ struct Table { int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - Module *pMod; /* Pointer to the implementation of the module */ - sqlite3_vtab *pVtab; /* Pointer to the module instance */ + VTable *pVTable; /* List of VTable objects. */ int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* Text of all module args. [0] is module name */ #endif @@ -2812,21 +2862,25 @@ void sqlite3AutoLoadExtensions(sqlite3*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite3VtabClear(X) +# define sqlite3VtabClear(Y) # define sqlite3VtabSync(X,Y) SQLITE_OK # define sqlite3VtabRollback(X) # define sqlite3VtabCommit(X) # define sqlite3VtabInSync(db) 0 +# define sqlite3VtabLock(X) +# define sqlite3VtabUnlock(X) +# define sqlite3VtabUnlockList(X) #else void sqlite3VtabClear(Table*); int sqlite3VtabSync(sqlite3 *db, char **); int sqlite3VtabRollback(sqlite3 *db); int sqlite3VtabCommit(sqlite3 *db); + void sqlite3VtabLock(VTable *); + void sqlite3VtabUnlock(VTable *); + void sqlite3VtabUnlockList(sqlite3*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif void sqlite3VtabMakeWritable(Parse*,Table*); -void sqlite3VtabLock(sqlite3_vtab*); -void sqlite3VtabUnlock(sqlite3*, sqlite3_vtab*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*); void sqlite3VtabFinishParse(Parse*, Token*); void sqlite3VtabArgInit(Parse*); @@ -2834,7 +2888,7 @@ void sqlite3VtabArgExtend(Parse*, Token*); int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); int sqlite3VtabCallConnect(Parse*, Table*); int sqlite3VtabCallDestroy(sqlite3*, int, const char *); -int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *); +int sqlite3VtabBegin(sqlite3 *, VTable *); FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); @@ -2842,6 +2896,7 @@ int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); +VTable *sqlite3GetVTable(sqlite3*, Table*); diff --git a/src/update.c b/src/update.c index fb69799c56..6e33f0b3af 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.204 2009/06/27 11:17:35 drh Exp $ +** $Id: update.c,v 1.205 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -155,10 +155,10 @@ void sqlite3Update( # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto update_cleanup; } - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto update_cleanup; } aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); @@ -631,7 +631,7 @@ static void updateVirtualTable( int addr; /* Address of top of loop */ int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 *db = pParse->db; /* Database connection */ - const char *pVtab = (const char*)pTab->pVtab; + const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); SelectDest dest; /* Construct the SELECT statement that will find the new values for @@ -676,7 +676,7 @@ static void updateVirtualTable( sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); } sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); diff --git a/src/vdbe.c b/src/vdbe.c index 76641c4b6d..e27fbeb48e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.873 2009/07/22 00:35:24 drh Exp $ +** $Id: vdbe.c,v 1.874 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -5017,13 +5017,13 @@ case OP_TableLock: { ** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { - sqlite3_vtab *pVtab; - pVtab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, pVtab); - if( pVtab ){ + VTable *pVTab; + pVTab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, pVTab); + if( pVTab ){ sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = pVtab->zErrMsg; - pVtab->zErrMsg = 0; + p->zErrMsg = pVTab->pVtab->zErrMsg; + pVTab->pVtab->zErrMsg = 0; } break; } @@ -5070,7 +5070,7 @@ case OP_VOpen: { pCur = 0; pVtabCursor = 0; - pVtab = pOp->p4.pVtab; + pVtab = pOp->p4.pVtab->pVtab; pModule = (sqlite3_module *)pVtab->pModule; assert(pVtab && pModule); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; @@ -5153,14 +5153,12 @@ case OP_VFilter: { /* jump */ } if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(pVtab); p->inVtabMethod = 1; rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); p->inVtabMethod = 0; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( rc==SQLITE_OK ){ res = pModule->xEof(pVtabCursor); } @@ -5268,14 +5266,12 @@ case OP_VNext: { /* jump */ ** some other method is next invoked on the save virtual table cursor. */ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(pVtab); p->inVtabMethod = 1; rc = pModule->xNext(pCur->pVtabCursor); p->inVtabMethod = 0; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); } @@ -5300,18 +5296,16 @@ case OP_VRename: { sqlite3_vtab *pVtab; Mem *pName; - pVtab = pOp->p4.pVtab; + pVtab = pOp->p4.pVtab->pVtab; pName = &p->aMem[pOp->p1]; assert( pVtab->pModule->xRename ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(pVtab); rc = pVtab->pModule->xRename(pVtab, pName->z); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; break; @@ -5351,7 +5345,7 @@ case OP_VUpdate: { Mem **apArg; Mem *pX; - pVtab = pOp->p4.pVtab; + pVtab = pOp->p4.pVtab->pVtab; pModule = (sqlite3_module *)pVtab->pModule; nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); @@ -5364,12 +5358,10 @@ case OP_VUpdate: { pX++; } if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(pVtab); rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( rc==SQLITE_OK && pOp->p1 ){ assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); diff --git a/src/vdbe.h b/src/vdbe.h index 64c440e513..c72ebe1c8e 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.141 2009/04/10 00:56:29 drh Exp $ +** $Id: vdbe.h,v 1.142 2009/07/24 17:58:53 danielk1977 Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -58,7 +58,7 @@ struct VdbeOp { VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ - sqlite3_vtab *pVtab; /* Used when p4type is P4_VTAB */ + VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ } p4; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 118f4ee2b9..b09c741bf0 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -14,7 +14,7 @@ ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. ** -** $Id: vdbeaux.c,v 1.477 2009/07/22 00:35:24 drh Exp $ +** $Id: vdbeaux.c,v 1.478 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -478,6 +478,10 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ sqlite3ValueFree((sqlite3_value*)p4); break; } + case P4_VTAB : { + sqlite3VtabUnlock((VTable *)p4); + break; + } } } } @@ -531,7 +535,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ db = p->db; assert( p->magic==VDBE_MAGIC_INIT ); if( p->aOp==0 || db->mallocFailed ){ - if (n != P4_KEYINFO) { + if ( n!=P4_KEYINFO && n!=P4_VTAB ) { freeP4(db, n, (void*)*(char**)&zP4); } return; @@ -576,6 +580,11 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ }else if( n==P4_KEYINFO_HANDOFF ){ pOp->p4.p = (void*)zP4; pOp->p4type = P4_KEYINFO; + }else if( n==P4_VTAB ){ + pOp->p4.p = (void*)zP4; + pOp->p4type = P4_VTAB; + sqlite3VtabLock((VTable *)zP4); + assert( ((VTable *)zP4)->db==p->db ); }else if( n<0 ){ pOp->p4.p = (void*)zP4; pOp->p4type = (signed char)n; @@ -731,7 +740,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ } #ifndef SQLITE_OMIT_VIRTUALTABLE case P4_VTAB: { - sqlite3_vtab *pVtab = pOp->p4.pVtab; + sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule); break; } diff --git a/src/vtab.c b/src/vtab.c index e870ec043c..e09dcd53a3 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.92 2009/07/01 18:04:21 danielk1977 Exp $ +** $Id: vtab.c,v 1.93 2009/07/24 17:58:53 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -27,7 +27,7 @@ static int createModule( const sqlite3_module *pModule, /* The definition of the module */ void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ -) { +){ int rc, nName; Module *pMod; @@ -93,33 +93,128 @@ int sqlite3_create_module_v2( ** If a disconnect is attempted while a virtual table is locked, ** the disconnect is deferred until all locks have been removed. */ -void sqlite3VtabLock(sqlite3_vtab *pVtab){ - pVtab->nRef++; +void sqlite3VtabLock(VTable *pVTab){ + pVTab->nRef++; +} + + +/* +** pTab is a pointer to a Table structure representing a virtual-table. +** Return a pointer to the VTable object used by connection db to access +** this virtual-table, if one has been created, or NULL otherwise. +*/ +VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){ + VTable *pVtab; + assert( IsVirtual(pTab) ); + for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); + return pVtab; } /* -** Unlock a virtual table. When the last lock is removed, -** disconnect the virtual table. +** Decrement the ref-count on a virtual table object. When the ref-count +** reaches zero, call the xDisconnect() method to delete the object. */ -void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ -#ifndef SQLITE_DEBUG - UNUSED_PARAMETER(db); -#endif - assert( pVtab->nRef>0 ); - pVtab->nRef--; - assert(db); +void sqlite3VtabUnlock(VTable *pVTab){ + sqlite3 *db = pVTab->db; + + assert( db ); + assert( pVTab->nRef>0 ); assert( sqlite3SafetyCheckOk(db) ); - if( pVtab->nRef==0 ){ + + pVTab->nRef--; + if( pVTab->nRef==0 ){ + sqlite3_vtab *p = pVTab->pVtab; + if( p ){ #ifdef SQLITE_DEBUG - if( db->magic==SQLITE_MAGIC_BUSY ){ - (void)sqlite3SafetyOff(db); - pVtab->pModule->xDisconnect(pVtab); - (void)sqlite3SafetyOn(db); - } else + if( pVTab->db->magic==SQLITE_MAGIC_BUSY ){ + (void)sqlite3SafetyOff(db); + p->pModule->xDisconnect(p); + (void)sqlite3SafetyOn(db); + } else #endif - { - pVtab->pModule->xDisconnect(pVtab); + { + p->pModule->xDisconnect(p); + } } + sqlite3DbFree(db, pVTab); + } +} + +/* +** Table p is a virtual table. This function moves all elements in the +** p->pVTable list to the sqlite3.pDisconnect lists of their associated +** database connections to be disconnected at the next opportunity. +** Except, if argument db is not NULL, then the entry associated with +** connection db is left in the p->pVTable list. +*/ +static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ + VTable *pRet = 0; + VTable *pVTable = p->pVTable; + p->pVTable = 0; + + /* Assert that the mutex (if any) associated with the BtShared database + ** that contains table p is held by the caller. See header comments + ** above function sqlite3VtabUnlockList() for an explanation of why + ** this makes it safe to access the sqlite3.pDisconnect list of any + ** database connection that may have an entry in the p->pVTable list. */ + assert( db==0 || + sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt) + ); + + while( pVTable ){ + sqlite3 *db2 = pVTable->db; + VTable *pNext = pVTable->pNext; + assert( db2 ); + if( db2==db ){ + pRet = pVTable; + p->pVTable = pRet; + pRet->pNext = 0; + }else{ + pVTable->pNext = db2->pDisconnect; + db2->pDisconnect = pVTable; + } + pVTable = pNext; + } + + assert( !db || pRet ); + return pRet; +} + + +/* +** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. +** +** This function may only be called when the mutexes associated with all +** shared b-tree databases opened using connection db are held by the +** caller. This is done to protect the sqlite3.pDisconnect list. The +** sqlite3.pDisconnect list is accessed only as follows: +** +** 1) By this function. In this case, all BtShared mutexes and the mutex +** associated with the database handle itself must be held. +** +** 2) By function vtabDisconnectAll(), when it adds a VTable entry to +** the sqlite3.pDisconnect list. In this case either the BtShared mutex +** associated with the database the virtual table is stored in is held +** or, if the virtual table is stored in a non-sharable database, then +** the database handle mutex is held. +** +** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously +** by multiple threads. It is thread-safe. +*/ +void sqlite3VtabUnlockList(sqlite3 *db){ + VTable *p = db->pDisconnect; + db->pDisconnect = 0; + + assert( sqlite3BtreeHoldsAllMutexes(db) ); + assert( sqlite3_mutex_held(db->mutex) ); + + if( p ){ + sqlite3ExpirePreparedStatements(db); + do { + VTable *pNext = p->pNext; + sqlite3VtabUnlock(p); + p = pNext; + }while( p ); } } @@ -127,22 +222,24 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ ** Clear any and all virtual-table information from the Table record. ** This routine is called, for example, just before deleting the Table ** record. +** +** Since it is a virtual-table, the Table structure contains a pointer +** to the head of a linked list of VTable structures. Each VTable +** structure is associated with a single sqlite3* user of the schema. +** The reference count of the VTable structure associated with database +** connection db is decremented immediately (which may lead to the +** structure being xDisconnected and free). Any other VTable structures +** in the list are moved to the sqlite3.pDisconnect list of the associated +** database connection. */ void sqlite3VtabClear(Table *p){ - sqlite3_vtab *pVtab = p->pVtab; - Schema *pSchema = p->pSchema; - sqlite3 *db = pSchema ? pSchema->db : 0; - if( pVtab ){ - assert( p->pMod && p->pMod->pModule ); - sqlite3VtabUnlock(db, pVtab); - p->pVtab = 0; - } + vtabDisconnectAll(0, p); if( p->azModuleArg ){ int i; for(i=0; inModuleArg; i++){ - sqlite3DbFree(db, p->azModuleArg[i]); + sqlite3DbFree(p->dbMem, p->azModuleArg[i]); } - sqlite3DbFree(db, p->azModuleArg); + sqlite3DbFree(p->dbMem, p->azModuleArg); } } @@ -187,11 +284,6 @@ void sqlite3VtabBeginParse( Table *pTable; /* The new virtual table */ sqlite3 *db; /* Database connection */ - if( pParse->db->flags & SQLITE_SharedCache ){ - sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode"); - return; - } - sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0); pTable = pParse->pNewTable; if( pTable==0 ) return; @@ -240,23 +332,13 @@ static void addArgumentToVtab(Parse *pParse){ ** has been completely parsed. */ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ - Table *pTab; /* The table being constructed */ - sqlite3 *db; /* The database connection */ - char *zModule; /* The module name of the table: USING modulename */ - Module *pMod = 0; + Table *pTab = pParse->pNewTable; /* The table being constructed */ + sqlite3 *db = pParse->db; /* The database connection */ + if( pTab==0 ) return; addArgumentToVtab(pParse); pParse->sArg.z = 0; - - /* Lookup the module name. */ - pTab = pParse->pNewTable; - if( pTab==0 ) return; - db = pParse->db; if( pTab->nModuleArg<1 ) return; - zModule = pTab->azModuleArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zModule, - sqlite3Strlen30(zModule)); - pTab->pMod = pMod; /* If the CREATE VIRTUAL TABLE statement is being entered for the ** first time (in other words if the virtual table is actually being @@ -307,9 +389,10 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ } /* If we are rereading the sqlite_master table create the in-memory - ** record of the table. If the module has already been registered, - ** also call the xConnect method here. - */ + ** record of the table. The xConnect() method is not called until + ** the first time the virtual table is used in an SQL statement. This + ** allows a schema that contains virtual tables to be loaded before + ** the required virtual table implementations are registered. */ else { Table *pOld; Schema *pSchema = pTab->pSchema; @@ -363,9 +446,8 @@ static int vtabCallConstructor( int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), char **pzErr ){ + VTable *pVTable; int rc; - int rc2; - sqlite3_vtab *pVtab = 0; const char *const*azArg = (const char *const*)pTab->azModuleArg; int nArg = pTab->nModuleArg; char *zErr = 0; @@ -375,22 +457,23 @@ static int vtabCallConstructor( return SQLITE_NOMEM; } + pVTable = sqlite3DbMallocZero(db, sizeof(VTable)); + if( !pVTable ){ + sqlite3DbFree(db, zModuleName); + return SQLITE_NOMEM; + } + pVTable->db = db; + pVTable->pMod = pMod; + assert( !db->pVTab ); assert( xConstruct ); - db->pVTab = pTab; - rc = sqlite3SafetyOff(db); - assert( rc==SQLITE_OK ); - rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr); - rc2 = sqlite3SafetyOn(db); + + /* Invoke the virtual table constructor */ + (void)sqlite3SafetyOff(db); + rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + (void)sqlite3SafetyOn(db); if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - /* Justification of ALWAYS(): A correct vtab constructor must allocate - ** the sqlite3_vtab object if successful. */ - if( rc==SQLITE_OK && ALWAYS(pVtab) ){ - pVtab->pModule = pMod->pModule; - pVtab->nRef = 1; - pTab->pVtab = pVtab; - } if( SQLITE_OK!=rc ){ if( zErr==0 ){ @@ -399,54 +482,61 @@ static int vtabCallConstructor( *pzErr = sqlite3MPrintf(db, "%s", zErr); sqlite3DbFree(db, zErr); } - }else if( db->pVTab ){ - const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); - rc = SQLITE_ERROR; - } - if( rc==SQLITE_OK ){ - rc = rc2; - } - db->pVTab = 0; - sqlite3DbFree(db, zModuleName); + sqlite3DbFree(db, pVTable); + }else if( ALWAYS(pVTable->pVtab) ){ + /* Justification of ALWAYS(): A correct vtab constructor must allocate + ** the sqlite3_vtab object if successful. */ + pVTable->pVtab->pModule = pMod->pModule; + pVTable->nRef = 1; + if( db->pVTab ){ + const char *zFormat = "vtable constructor did not declare schema: %s"; + *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); + sqlite3VtabUnlock(pVTable); + rc = SQLITE_ERROR; + }else{ + int iCol; + /* If everything went according to plan, link the new VTable structure + ** into the linked list headed by pTab->pVTable. Then loop through the + ** columns of the table to see if any of them contain the token "hidden". + ** If so, set the Column.isHidden flag and remove the token from + ** the type string. */ + pVTable->pNext = pTab->pVTable; + pTab->pVTable = pVTable; - /* If everything went according to plan, loop through the columns - ** of the table to see if any of them contain the token "hidden". - ** If so, set the Column.isHidden flag and remove the token from - ** the type string. - */ - if( rc==SQLITE_OK ){ - int iCol; - for(iCol=0; iColnCol; iCol++){ - char *zType = pTab->aCol[iCol].zType; - int nType; - int i = 0; - if( !zType ) continue; - nType = sqlite3Strlen30(zType); - if( sqlite3StrNICmp("hidden", zType, 6) || (zType[6] && zType[6]!=' ') ){ - for(i=0; inCol; iCol++){ + char *zType = pTab->aCol[iCol].zType; + int nType; + int i = 0; + if( !zType ) continue; + nType = sqlite3Strlen30(zType); + if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){ + for(i=0; i0 ){ + assert(zType[i-1]==' '); + zType[i-1] = '\0'; + } + pTab->aCol[iCol].isHidden = 1; } - if( zType[i]=='\0' && i>0 ){ - assert(zType[i-1]==' '); - zType[i-1] = '\0'; - } - pTab->aCol[iCol].isHidden = 1; } } } + + sqlite3DbFree(db, zModuleName); + db->pVTab = 0; return rc; } @@ -458,20 +548,25 @@ static int vtabCallConstructor( ** This call is a no-op if table pTab is not a virtual table. */ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ + sqlite3 *db = pParse->db; + const char *zMod; Module *pMod; - int rc = SQLITE_OK; + int rc; assert( pTab ); - if( (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){ + if( (pTab->tabFlags & TF_Virtual)==0 || sqlite3GetVTable(db, pTab) ){ return SQLITE_OK; } - pMod = pTab->pMod; + /* Locate the required virtual table module */ + zMod = pTab->azModuleArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); + if( !pMod ){ const char *zModule = pTab->azModuleArg[0]; sqlite3ErrorMsg(pParse, "no such module: %s", zModule); rc = SQLITE_ERROR; - } else { + }else{ char *zErr = 0; sqlite3 *db = pParse->db; rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr); @@ -485,14 +580,14 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ } /* -** Add the virtual table pVtab to the array sqlite3.aVTrans[]. +** Add the virtual table pVTab to the array sqlite3.aVTrans[]. */ -static int addToVTrans(sqlite3 *db, sqlite3_vtab *pVtab){ +static int addToVTrans(sqlite3 *db, VTable *pVTab){ const int ARRAY_INCR = 5; /* Grow the sqlite3.aVTrans array if required */ if( (db->nVTrans%ARRAY_INCR)==0 ){ - sqlite3_vtab **aVTrans; + VTable **aVTrans; int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ @@ -503,8 +598,8 @@ static int addToVTrans(sqlite3 *db, sqlite3_vtab *pVtab){ } /* Add pVtab to the end of sqlite3.aVTrans */ - db->aVTrans[db->nVTrans++] = pVtab; - sqlite3VtabLock(pVtab); + db->aVTrans[db->nVTrans++] = pVTab; + sqlite3VtabLock(pVTab); return SQLITE_OK; } @@ -520,19 +615,21 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ int rc = SQLITE_OK; Table *pTab; Module *pMod; - const char *zModule; + const char *zMod; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - assert(pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVtab); - pMod = pTab->pMod; - zModule = pTab->azModuleArg[0]; + assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable ); + + /* Locate the required virtual table module */ + zMod = pTab->azModuleArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); /* If the module has been registered and includes a Create method, ** invoke it now. If the module has not been registered, return an ** error. Otherwise, do nothing. */ if( !pMod ){ - *pzErr = sqlite3MPrintf(db, "no such module: %s", zModule); + *pzErr = sqlite3MPrintf(db, "no such module: %s", zMod); rc = SQLITE_ERROR; }else{ rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr); @@ -540,8 +637,8 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ /* Justification of ALWAYS(): The xConstructor method is required to ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ - if( rc==SQLITE_OK && ALWAYS(pTab->pVtab) ){ - rc = addToVTrans(db, pTab->pVtab); + if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){ + rc = addToVTrans(db, sqlite3GetVTable(db, pTab)); } return rc; @@ -566,7 +663,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE; } - assert((pTab->tabFlags & TF_Virtual)!=0 && pTab->nCol==0 && pTab->aCol==0); + assert( (pTab->tabFlags & TF_Virtual)!=0 ); pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ @@ -581,10 +678,12 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ !pParse->pNewTable->pSelect && (pParse->pNewTable->tabFlags & TF_Virtual)==0 ){ - pTab->aCol = pParse->pNewTable->aCol; - pTab->nCol = pParse->pNewTable->nCol; - pParse->pNewTable->nCol = 0; - pParse->pNewTable->aCol = 0; + if( !pTab->aCol ){ + pTab->aCol = pParse->pNewTable->aCol; + pTab->nCol = pParse->pNewTable->nCol; + pParse->pNewTable->nCol = 0; + pParse->pNewTable->aCol = 0; + } db->pVTab = 0; } else { sqlite3Error(db, SQLITE_ERROR, zErr); @@ -618,21 +717,20 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - if( ALWAYS(pTab!=0 && pTab->pVtab!=0) ){ - int (*xDestroy)(sqlite3_vtab *pVTab) = pTab->pMod->pModule->xDestroy; + if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ + VTable *p = vtabDisconnectAll(db, pTab); + rc = sqlite3SafetyOff(db); assert( rc==SQLITE_OK ); - rc = xDestroy(pTab->pVtab); + rc = p->pMod->pModule->xDestroy(p->pVtab); (void)sqlite3SafetyOn(db); + + /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ - int i; - for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pTab->pVtab ){ - db->aVTrans[i] = db->aVTrans[--db->nVTrans]; - break; - } - } - pTab->pVtab = 0; + assert( pTab->pVTable==p && p->pNext==0 ); + p->pVtab = 0; + pTab->pVTable = 0; + sqlite3VtabUnlock(p); } } @@ -651,13 +749,14 @@ static void callFinaliser(sqlite3 *db, int offset){ int i; if( db->aVTrans ){ for(i=0; inVTrans; i++){ - sqlite3_vtab *pVtab = db->aVTrans[i]; - int (*x)(sqlite3_vtab *); - - assert( pVtab!=0 ); - x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset); - if( x ) x(pVtab); - sqlite3VtabUnlock(db, pVtab); + VTable *pVTab = db->aVTrans[i]; + sqlite3_vtab *p = pVTab->pVtab; + if( p ){ + int (*x)(sqlite3_vtab *); + x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); + if( x ) x(p); + } + sqlite3VtabUnlock(pVTab); } sqlite3DbFree(db, db->aVTrans); db->nVTrans = 0; @@ -677,16 +776,14 @@ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ int i; int rc = SQLITE_OK; int rcsafety; - sqlite3_vtab **aVTrans = db->aVTrans; + VTable **aVTrans = db->aVTrans; rc = sqlite3SafetyOff(db); db->aVTrans = 0; for(i=0; rc==SQLITE_OK && inVTrans; i++){ - sqlite3_vtab *pVtab = aVTrans[i]; int (*x)(sqlite3_vtab *); - assert( pVtab!=0 ); - x = pVtab->pModule->xSync; - if( x ){ + sqlite3_vtab *pVtab = aVTrans[i]->pVtab; + if( pVtab && (x = pVtab->pModule->xSync)!=0 ){ rc = x(pVtab); sqlite3DbFree(db, *pzErrmsg); *pzErrmsg = pVtab->zErrMsg; @@ -728,7 +825,7 @@ int sqlite3VtabCommit(sqlite3 *db){ ** If the xBegin call is successful, place the sqlite3_vtab pointer ** in the sqlite3.aVTrans array. */ -int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ +int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ int rc = SQLITE_OK; const sqlite3_module *pModule; @@ -740,10 +837,10 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ if( sqlite3VtabInSync(db) ){ return SQLITE_LOCKED; } - if( !pVtab ){ + if( !pVTab ){ return SQLITE_OK; } - pModule = pVtab->pModule; + pModule = pVTab->pVtab->pModule; if( pModule->xBegin ){ int i; @@ -751,15 +848,15 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ /* If pVtab is already in the aVTrans array, return early */ for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pVtab ){ + if( db->aVTrans[i]==pVTab ){ return SQLITE_OK; } } /* Invoke the xBegin method */ - rc = pModule->xBegin(pVtab); + rc = pModule->xBegin(pVTab->pVtab); if( rc==SQLITE_OK ){ - rc = addToVTrans(db, pVtab); + rc = addToVTrans(db, pVTab); } } return rc; @@ -801,7 +898,7 @@ FuncDef *sqlite3VtabOverloadFunction( pTab = pExpr->pTab; if( NEVER(pTab==0) ) return pDef; if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef; - pVtab = pTab->pVtab; + pVtab = sqlite3GetVTable(db, pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); pMod = (sqlite3_module *)pVtab->pModule; diff --git a/src/where.c b/src/where.c index efadfc1b8b..4416b8d81b 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.408 2009/06/16 14:15:22 shane Exp $ +** $Id: where.c,v 1.409 2009/07/24 17:58:53 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -1726,7 +1726,7 @@ static sqlite3_index_info *allocateIndexInfo( ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3_vtab *pVtab = pTab->pVtab; + sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int i; int rc; @@ -1823,7 +1823,7 @@ static void bestVirtualIndex( ** sqlite3ViewGetColumnNames() would have picked up the error. */ assert( pTab->azModuleArg && pTab->azModuleArg[0] ); - assert( pTab->pVtab ); + assert( sqlite3GetVTable(pParse->db, pTab) ); /* Set the aConstraint[].usable fields and initialize all ** output variables to zero. @@ -3404,13 +3404,13 @@ WhereInfo *sqlite3WhereBegin( #endif /* SQLITE_OMIT_EXPLAIN */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue; #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); int iCur = pTabItem->iCursor; - sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, - (const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); }else #endif if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 diff --git a/test/vtab_shared.test b/test/vtab_shared.test index cfc48d9a6f..e195ef232b 100644 --- a/test/vtab_shared.test +++ b/test/vtab_shared.test @@ -11,7 +11,7 @@ # This file tests interactions between the virtual table and # shared-schema functionality. # -# $Id: vtab_shared.test,v 1.2 2008/03/19 13:03:34 drh Exp $ +# $Id: vtab_shared.test,v 1.3 2009/07/24 17:58:53 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -23,39 +23,207 @@ ifcapable !vtab||!shared_cache { db close sqlite3_enable_shared_cache 1 -sqlite3 db test.db - -do_test vtab_shared-1.0 { - register_echo_module [sqlite3_connection_pointer db] - catchsql { - CREATE TABLE t0(a, b, c); - CREATE VIRTUAL TABLE t1 USING echo(t0); - } -} {1 {Cannot use virtual tables in shared-cache mode}} - -db close -sqlite3_enable_shared_cache 0 -sqlite3 db test.db +sqlite3 db test.db +sqlite3 db2 test.db do_test vtab_shared-1.1 { register_echo_module [sqlite3_connection_pointer db] - catchsql { + execsql { + CREATE TABLE t0(a, b, c); + INSERT INTO t0 VALUES(1, 2, 3); CREATE VIRTUAL TABLE t1 USING echo(t0); } -} {0 {}} - -db close -sqlite3_enable_shared_cache 1 -sqlite3 db test.db +} {} do_test vtab_shared-1.2 { - register_echo_module [sqlite3_connection_pointer db] - catchsql { + execsql { SELECT * FROM t1 } db +} {1 2 3} + +# Fails because the 'echo' module has not been registered with connection db2 +do_test vtab_shared-1.3 { + catchsql { SELECT * FROM t1 } db2 +} {1 {no such module: echo}} + +do_test vtab_shared-1.4 { + execsql { SELECT * FROM t0 } db2 +} {1 2 3} + +do_test vtab_shared-1.5 { + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t1 } db +} {1 2 3} + +# Works after the module is registered with db2 +do_test vtab_shared-1.6 { + execsql { SELECT * FROM t1 } db2 +} {1 2 3} + +# Set a write-lock on table t0 using connection [db]. Then try to read from +# virtual table t1 using [db2]. That this returns an SQLITE_LOCKED error +# shows that the correct sqlite3_vtab is being used. +# +do_test vtab_shared-1.8.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(4, 5, 6); SELECT * FROM t1; } -} [list 1 \ - {malformed database schema (t1) - Cannot use virtual tables in shared-cache mode}] +} {1 2 3 4 5 6} +do_test vtab_shared-1.8.2 { + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked}} +do_test vtab_shared-1.8.3 { + catchsql { SELECT * FROM t0 } db2 +} {1 {database table is locked: t0}} +do_test vtab_shared-1.8.4 { + execsql { SELECT * FROM t0 } db +} {1 2 3 4 5 6} +do_test vtab_shared-1.8.5 { + execsql { COMMIT } db + execsql { SELECT * FROM t1 } db2 +} {1 2 3 4 5 6} + +# While a SELECT is active on virtual table t1 via connection [db], close +# [db2]. This causes the schema to be reset internally. Verify that this +# does not cause a problem. +# +foreach {iTest dbSelect dbClose} { + 1 db db2 + 2 db db2 + 3 db2 db +} { + do_test vtab_shared-1.9.$iTest { + set res [list] + $dbSelect eval { SELECT * FROM t1 } { + if {$a == 1} {$dbClose close} + lappend res $a $b $c + } + sqlite3 $dbClose test.db + register_echo_module [sqlite3_connection_pointer $dbClose] + set res + } {1 2 3 4 5 6} +} + +# Ensure that it is not possible for one connection to DROP a virtual +# table while a second connection is reading from the database. +# +do_test vtab_shared-1.10 { + db eval { SELECT * FROM t1 } { + set error [catchsql { DROP TABLE t1 } db2] + break + } + set error +} {1 {database table is locked: sqlite_master}} + +do_test vtab_shared-1.11 { +breakpoint + execsql { + CREATE VIRTUAL TABLE t2 USING echo(t0); + CREATE VIRTUAL TABLE t3 USING echo(t0); + } + execsql { SELECT * FROM t3 } db2 +} {1 2 3 4 5 6} + +do_test vtab_shared-1.12.1 { + db close + execsql { + SELECT * FROM t1 UNION ALL + SELECT * FROM t2 UNION ALL + SELECT * FROM t3 + } db2 +} {1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6} +do_test vtab_shared-1.12.2 { + sqlite3 db test.db + register_echo_module [sqlite3_connection_pointer db] + execsql { + SELECT * FROM t1 UNION ALL + SELECT * FROM t2 UNION ALL + SELECT * FROM t3 + } db +} {1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6} + +# Try a rename or two. +# +do_test vtab_shared-1.13.1 { + execsql { ALTER TABLE t1 RENAME TO t4 } + execsql { SELECT * FROM t4 } db +} {1 2 3 4 5 6} +do_test vtab_shared-1.13.2 { + execsql { SELECT * FROM t4 } db2 +} {1 2 3 4 5 6} +do_test vtab_shared-1.13.3 { + execsql { ALTER TABLE t2 RENAME TO t5 } + execsql { SELECT * FROM t4 } db2 +} {1 2 3 4 5 6} + +# Try an UPDATE/INSERT/DELETE on a shared vtab as the first statement after a +# schema is loaded. +do_test vtab_shared_1.14.1 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t3 } +} {1 2 3 4 5 6} +do_test vtab_shared_1.14.2 { + execsql { + UPDATE t3 SET c = 'six' WHERE c = 6; + SELECT * FROM t3; + } db2 +} {1 2 3 4 5 six} +do_test vtab_shared_1.14.3 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t3 } +} {1 2 3 4 5 six} +do_test vtab_shared_1.14.4 { + execsql { + DELETE FROM t3 WHERE c = 'six'; + SELECT * FROM t3; + } db2 +} {1 2 3} +do_test vtab_shared_1.14.5 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t3 } +} {1 2 3} +do_test vtab_shared_1.14.6 { + execsql { + INSERT INTO t3 VALUES(4, 5, 6); + SELECT * FROM t3; + } db2 +} {1 2 3 4 5 6} + +do_test vtab_shared_1.15.1 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { + UPDATE t3 SET c = 'six' WHERE c = 6; + SELECT * FROM t3; + } db2 +} {1 2 3 4 5 six} +do_test vtab_shared_1.15.2 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { + DELETE FROM t3 WHERE c = 'six'; + SELECT * FROM t3; + } db2 +} {1 2 3} +do_test vtab_shared_1.15.3 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { + INSERT INTO t3 VALUES(4, 5, 6); + SELECT * FROM t3; + } +} {1 2 3 4 5 6} db close +db2 close sqlite3_enable_shared_cache 0 finish_test