1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Allow virtual tables to be used in shared-cache mode. (CVS 6928)

FossilOrigin-Name: 5d9e767a05e381235e064061043e30cc03a11a07
This commit is contained in:
danielk1977
2009-07-24 17:58:53 +00:00
parent ad7516c45f
commit 595a523a10
17 changed files with 631 additions and 292 deletions

View File

@@ -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

View File

@@ -1 +1 @@
7222ad2667b95d6021d9ae47f548b76b224f46aa
5d9e767a05e381235e064061043e30cc03a11a07

View File

@@ -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

View File

@@ -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;

View File

@@ -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; i<pParse->nVtabLock; 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 ){

View File

@@ -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
{

View File

@@ -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 <iDb, pTab> 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; i<iEnd; i++){
VdbeOp *pOp = sqlite3VdbeGetOp(v, i);
assert( pOp!=0 );
@@ -150,7 +155,7 @@ static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){
}
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pOp->opcode==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
{

View File

@@ -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) ){

View File

@@ -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 */
};

View File

@@ -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*);

View File

@@ -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);

View File

@@ -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) );

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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; i<p->nModuleArg; 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; iCol<pTab->nCol; 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; i<nType; i++){
if( (0==sqlite3StrNICmp(" hidden", &zType[i], 7))
&& (zType[i+7]=='\0' || zType[i+7]==' ')
){
i++;
break;
for(iCol=0; iCol<pTab->nCol; 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; i<nType; i++){
if( (0==sqlite3StrNICmp(" hidden", &zType[i], 7))
&& (zType[i+7]=='\0' || zType[i+7]==' ')
){
i++;
break;
}
}
}
}
if( i<nType ){
int j;
int nDel = 6 + (zType[i+6] ? 1 : 0);
for(j=i; (j+nDel)<=nType; j++){
zType[j] = zType[j+nDel];
if( i<nType ){
int j;
int nDel = 6 + (zType[i+6] ? 1 : 0);
for(j=i; (j+nDel)<=nType; j++){
zType[j] = zType[j+nDel];
}
if( zType[i]=='\0' && i>0 ){
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; i<db->nVTrans; 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; i<db->nVTrans; 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 && i<db->nVTrans; 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; i<db->nVTrans; 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;

View File

@@ -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

View File

@@ -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