-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
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
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
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
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
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
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
-7222ad2667b95d6021d9ae47f548b76b224f46aa
\ No newline at end of file
+5d9e767a05e381235e064061043e30cc03a11a07
\ No newline at end of file
** 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"
#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 );
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
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
** 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
** 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.
char *zFullPathname = sqlite3Malloc(nFullPathname);
sqlite3_mutex *mutexShared;
p->sharable = 1;
- db->flags |= SQLITE_SharedCache;
if( !zFullPathname ){
sqlite3_free(p);
return SQLITE_NOMEM;
** 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"
{
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;
}
assert( iDb==0 );
db->flags &= ~SQLITE_InternChanges;
+ sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
/* If one or more of the auxiliary database files has been closed,
#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 ){
** 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"
** 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);
# 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;
}
}
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 ){
/* 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
{
** 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"
** 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 );
}
}
#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;
#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).
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);
** 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;
}
*/
#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
{
** 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"
}
}
+ sqlite3VtabUnlockList(db);
pParse->db = db;
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
** 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_
*/
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 */
};
*************************************************************************
** 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_
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;
#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 */
#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 */
/*
#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.
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
#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*);
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 *);
void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
int sqlite3TempInMemory(const sqlite3*);
+VTable *sqlite3GetVTable(sqlite3*, Table*);
** 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"
# 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 );
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
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);
** 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"
** 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;
}
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;
}
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);
}
** 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);
}
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;
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 );
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) );
** 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_
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;
** 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"
sqlite3ValueFree((sqlite3_value*)p4);
break;
}
+ case P4_VTAB : {
+ sqlite3VtabUnlock((VTable *)p4);
+ break;
+ }
}
}
}
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;
}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;
}
#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;
}
*************************************************************************
** 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"
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;
** 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++;
}
+
/*
-** Unlock a virtual table. When the last lock is removed,
-** disconnect the virtual table.
+** 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.
*/
-void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
-#ifndef SQLITE_DEBUG
- UNUSED_PARAMETER(db);
-#endif
- assert( pVtab->nRef>0 );
- pVtab->nRef--;
- assert(db);
+VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
+ VTable *pVtab;
+ assert( IsVirtual(pTab) );
+ for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
+ return pVtab;
+}
+
+/*
+** 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(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 );
}
}
** 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);
}
}
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;
** 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
}
/* 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;
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;
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 ){
*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);
-
- /* 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;
+ 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;
+
+ 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( zType[i]=='\0' && i>0 ){
- assert(zType[i-1]==' ');
- zType[i-1] = '\0';
+ 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;
}
- pTab->aCol[iCol].isHidden = 1;
}
}
}
+
+ sqlite3DbFree(db, zModuleName);
+ db->pVTab = 0;
return rc;
}
** 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);
}
/*
-** 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 ){
}
/* 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;
}
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);
/* 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;
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 ){
!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);
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);
}
}
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;
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;
** 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;
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;
/* 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;
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;
** 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"
** 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;
** 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.
#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
# 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
db close
sqlite3_enable_shared_cache 1
-sqlite3 db test.db
+sqlite3 db test.db
+sqlite3 db2 test.db
-do_test vtab_shared-1.0 {
+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);
}
-} {1 {Cannot use virtual tables in shared-cache mode}}
+} {}
-db close
-sqlite3_enable_shared_cache 0
-sqlite3 db test.db
+do_test vtab_shared-1.2 {
+ execsql { SELECT * FROM t1 } db
+} {1 2 3}
-do_test vtab_shared-1.1 {
- register_echo_module [sqlite3_connection_pointer db]
- catchsql {
- CREATE VIRTUAL TABLE t1 USING echo(t0);
+# 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;
}
-} {0 {}}
+} {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}
-db close
-sqlite3_enable_shared_cache 1
-sqlite3 db test.db
+# 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}
+}
-do_test vtab_shared-1.2 {
+# 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]
- catchsql {
- SELECT * FROM t1;
+ 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;
}
-} [list 1 \
- {malformed database schema (t1) - Cannot use virtual tables in shared-cache mode}]
+} {1 2 3 4 5 6}
db close
+db2 close
sqlite3_enable_shared_cache 0
finish_test