aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/alter.c32
-rw-r--r--src/analyze.c17
-rw-r--r--src/attach.c21
-rw-r--r--src/auth.c10
-rw-r--r--src/btree.c453
-rw-r--r--src/btree.h3
-rw-r--r--src/build.c188
-rw-r--r--src/delete.c22
-rw-r--r--src/expr.c11
-rw-r--r--src/insert.c32
-rw-r--r--src/main.c60
-rw-r--r--src/pragma.c12
-rw-r--r--src/prepare.c34
-rw-r--r--src/select.c11
-rw-r--r--src/sqliteInt.h51
-rw-r--r--src/trigger.c40
-rw-r--r--src/update.c14
-rw-r--r--src/vdbe.c10
-rw-r--r--src/where.c15
19 files changed, 743 insertions, 293 deletions
diff --git a/src/alter.c b/src/alter.c
index 77109c783..d99001161 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -12,7 +12,7 @@
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
**
-** $Id: alter.c,v 1.13 2005/12/21 14:43:12 drh Exp $
+** $Id: alter.c,v 1.14 2006/01/05 11:34:33 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -177,9 +177,16 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
Trigger *pTrig;
char *zWhere = 0;
char *tmp = 0;
- if( pTab->iDb!=1 ){
+ const DbSchema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */
+
+ /* If the table is not located in the temp-db (in which case NULL is
+ ** returned, loop through the tables list of triggers. For each trigger
+ ** that is not part of the temp-db schema, add a clause to the WHERE
+ ** expression being built up in zWhere.
+ */
+ if( pTab->pSchema!=pTempSchema ){
for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
- if( pTrig->iDb==1 ){
+ if( pTrig->pSchema==pTempSchema ){
if( !zWhere ){
zWhere = sqlite3MPrintf("name=%Q", pTrig->name);
}else{
@@ -204,20 +211,22 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
Vdbe *v;
char *zWhere;
- int iDb;
+ int iDb; /* Index of database containing pTab */
#ifndef SQLITE_OMIT_TRIGGER
Trigger *pTrig;
#endif
v = sqlite3GetVdbe(pParse);
if( !v ) return;
- iDb = pTab->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ assert( iDb>=0 );
#ifndef SQLITE_OMIT_TRIGGER
/* Drop any table triggers from the internal schema. */
for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
- assert( pTrig->iDb==iDb || pTrig->iDb==1 );
- sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0);
+ int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
+ assert( iTrigDb==iDb || iTrigDb==1 );
+ sqlite3VdbeOp3(v, OP_DropTrigger, iTrigDb, 0, pTrig->name, 0);
}
#endif
@@ -263,7 +272,7 @@ void sqlite3AlterRenameTable(
pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
if( !pTab ) goto exit_rename_table;
- iDb = pTab->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zName;
/* Get a NULL terminated version of the new table name. */
@@ -390,7 +399,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
pNew = pParse->pNewTable;
assert( pNew );
- iDb = pNew->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pNew->pSchema);
zDb = pParse->db->aDb[iDb].zName;
zTab = pNew->zName;
pCol = &pNew->aCol[pNew->nCol-1];
@@ -490,7 +499,6 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
int i;
int nAlloc;
-
/* Look up the table being altered. */
assert( pParse->pNewTable==0 );
if( sqlite3Tsd()->mallocFailed ) goto exit_begin_add_column;
@@ -504,7 +512,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
}
assert( pTab->addColOffset>0 );
- iDb = pTab->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
/* Put a copy of the Table struct in Parse.pNewTable for the
** sqlite3AddColumn() function and friends to modify.
@@ -529,7 +537,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
pCol->zType = 0;
pCol->pDflt = 0;
}
- pNew->iDb = iDb;
+ pNew->pSchema = pParse->db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
pNew->nRef = 1;
diff --git a/src/analyze.c b/src/analyze.c
index 41fecb13b..3bcc8add4 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code associated with the ANALYZE command.
**
-** @(#) $Id: analyze.c,v 1.11 2005/11/14 22:29:05 drh Exp $
+** @(#) $Id: analyze.c,v 1.12 2006/01/05 11:34:33 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h"
@@ -86,6 +86,7 @@ static void analyzeOneTable(
int topOfLoop; /* The top of the loop */
int endOfLoop; /* The end of the loop */
int addr; /* The address of an instruction */
+ int iDb; /* Index of database containing pTab */
v = sqlite3GetVdbe(pParse);
if( pTab==0 || pTab->pIndex==0 ){
@@ -93,9 +94,11 @@ static void analyzeOneTable(
return;
}
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ assert( iDb>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
- pParse->db->aDb[pTab->iDb].zName ) ){
+ pParse->db->aDb[iDb].zName ) ){
return;
}
#endif
@@ -104,7 +107,8 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
/* Open a cursor to the index to be analyzed
*/
- sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
VdbeComment((v, "# %s", pIdx->zName));
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO);
@@ -215,6 +219,7 @@ static void loadAnalysis(Parse *pParse, int iDb){
*/
static void analyzeDatabase(Parse *pParse, int iDb){
sqlite3 *db = pParse->db;
+ DbSchema *pSchema = db->aDb[iDb].pSchema; /* Schema of database iDb */
HashElem *k;
int iStatCur;
int iMem;
@@ -223,7 +228,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
iStatCur = pParse->nTab++;
openStatTable(pParse, iDb, iStatCur, 0);
iMem = pParse->nMem;
- for(k=sqliteHashFirst(&db->aDb[iDb].tblHash); k; k=sqliteHashNext(k)){
+ for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, iStatCur, iMem);
}
@@ -239,7 +244,7 @@ static void analyzeTable(Parse *pParse, Table *pTab){
int iStatCur;
assert( pTab!=0 );
- iDb = pTab->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab++;
openStatTable(pParse, iDb, iStatCur, pTab->zName);
@@ -361,7 +366,7 @@ void sqlite3AnalysisLoad(sqlite3 *db, int iDb){
char *zSql;
/* Clear any prior statistics */
- for(i=sqliteHashFirst(&db->aDb[iDb].idxHash); i; i=sqliteHashNext(i)){
+ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
}
diff --git a/src/attach.c b/src/attach.c
index 35af84521..ef6f281d7 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
-** $Id: attach.c,v 1.38 2005/12/29 23:04:02 drh Exp $
+** $Id: attach.c,v 1.39 2006/01/05 11:34:33 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -116,15 +116,20 @@ static void attachFunc(
db->aDb = aNew;
aNew = &db->aDb[db->nDb++];
memset(aNew, 0, sizeof(*aNew));
- sqlite3HashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0);
- sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0);
- sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0);
- sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1);
+
+ /* Open the database file. If the btree is successfully opened, use
+ ** it to obtain the database schema. At this point the schema may
+ ** or may not be initialised.
+ */
+ rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
+ if( rc==SQLITE_OK ){
+ aNew->pSchema = sqlite3SchemaGet(aNew->pBt);
+ if( !aNew->pSchema ){
+ rc = SQLITE_NOMEM;
+ }
+ }
aNew->zName = sqliteStrDup(zName);
aNew->safety_level = 3;
-
- /* Open the database file */
- rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
#if SQLITE_HAS_CODEC
{
diff --git a/src/auth.c b/src/auth.c
index 7627ee140..b24976e57 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -14,7 +14,7 @@
** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
**
-** $Id: auth.c,v 1.22 2005/07/29 15:36:15 drh Exp $
+** $Id: auth.c,v 1.23 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -112,10 +112,12 @@ void sqlite3AuthRead(
int iSrc; /* Index in pTabList->a[] of table being read */
const char *zDBase; /* Name of database being accessed */
TriggerStack *pStack; /* The stack of current triggers */
+ int iDb; /* The index of the database the expression refers to */
if( db->xAuth==0 ) return;
if( pExpr->op==TK_AS ) return;
assert( pExpr->op==TK_COLUMN );
+ iDb = sqlite3SchemaToIndex(pParse->db, pExpr->pSchema);
for(iSrc=0; pTabList && iSrc<pTabList->nSrc; iSrc++){
if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
}
@@ -140,14 +142,14 @@ void sqlite3AuthRead(
}else{
zCol = "ROWID";
}
- assert( pExpr->iDb<db->nDb );
- zDBase = db->aDb[pExpr->iDb].zName;
+ assert( iDb<db->nDb );
+ zDBase = db->aDb[iDb].zName;
rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase,
pParse->zAuthContext);
if( rc==SQLITE_IGNORE ){
pExpr->op = TK_NULL;
}else if( rc==SQLITE_DENY ){
- if( db->nDb>2 || pExpr->iDb!=0 ){
+ if( db->nDb>2 || iDb!=0 ){
sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",
zDBase, pTab->zName, zCol);
}else{
diff --git a/src/btree.c b/src/btree.c
index 7e5314b58..636bee7f7 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.278 2005/12/30 16:31:54 danielk1977 Exp $
+** $Id: btree.c,v 1.279 2006/01/05 11:34:34 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -349,6 +349,8 @@ struct BtShared {
int nRef; /* Number of references to this structure */
int nTransaction; /* Number of open transactions (read + write) */
BtLock *pLock; /* List of locks held on this shared-btree struct */
+ void *pSchema;
+ void (*xFreeSchema)(void*);
};
/*
@@ -382,10 +384,39 @@ struct BtCursor {
int idx; /* Index of the entry in pPage->aCell[] */
CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */
- u8 isValid; /* TRUE if points to a valid entry */
+ u8 eState; /* One of the CURSOR_XXX constants (see below) */
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ void *pKey;
+ i64 nKey;
+ int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
+#endif
};
/*
+** Potential values for BtCursor.eState. The first two values (VALID and
+** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur
+** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined.
+**
+** CURSOR_VALID:
+** Cursor points to a valid entry. getPayload() etc. may be called.
+**
+** CURSOR_INVALID:
+** Cursor does not point to a valid entry. This can happen (for example)
+** because the table is empty or because BtreeCursorFirst() has not been
+** called.
+**
+** CURSOR_REQUIRESEEK:
+** The table that this cursor was opened on still exists, but has been
+** modified since the cursor was last used. The cursor position is saved
+** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
+** this state, restoreCursorPosition() can be called to attempt to seek
+** the cursor to the saved position.
+*/
+#define CURSOR_INVALID 0
+#define CURSOR_VALID 1
+#define CURSOR_REQUIRESEEK 2
+
+/*
** The TRACE macro will print high-level status information about the
** btree operation when the global variable sqlite3_btree_trace is
** enabled.
@@ -462,15 +493,111 @@ struct BtLock {
** manipulate entries in the BtShared.pLock linked list used to store
** shared-cache table level locks. If the library is compiled with the
** shared-cache feature disabled, then there is only ever one user
- ** of each BtShared structure and so this locking is not required.
- ** So define the three interface functions as no-ops.
+ ** of each BtShared structure and so this locking is not necessary.
+ ** So define the lock related functions as no-ops.
*/
#define queryTableLock(a,b,c) SQLITE_OK
#define lockTable(a,b,c) SQLITE_OK
- #define unlockAllTables(a,b,c)
+ #define unlockAllTables(a)
+ #define restoreCursorPosition(a,b) SQLITE_OK
+ #define saveAllCursors(a,b,c) SQLITE_OK
+
#else
/*
+** Save the current cursor position in the variables BtCursor.nKey
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+ int rc = SQLITE_OK;
+
+ assert( CURSOR_VALID==pCur->eState|| CURSOR_INVALID==pCur->eState );
+ assert( 0==pCur->pKey );
+
+ if( pCur->eState==CURSOR_VALID ){
+ rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
+
+ /* If this is an intKey table, then the above call to BtreeKeySize()
+ ** stores the integer key in pCur->nKey. In this case this value is
+ ** all that is required. Otherwise, if pCur is not open on an intKey
+ ** table, then malloc space for and store the pCur->nKey bytes of key
+ ** data.
+ */
+ if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
+ void *pKey = sqliteMalloc(pCur->nKey);
+ if( pKey ){
+ rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
+ if( pKey ){
+ pCur->pKey = pKey;
+ }else{
+ sqliteFree(pKey);
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ assert( !pCur->pPage->intKey || !pCur->pKey );
+
+ /* Todo: Should we drop the reference to pCur->pPage here? */
+
+ if( rc==SQLITE_OK ){
+ pCur->eState = CURSOR_REQUIRESEEK;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Save the positions of all cursors except pExcept open on the table
+** with root-page iRoot. Usually, this is called just before cursor
+** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+*/
+static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
+ BtCursor *p;
+ if( sqlite3Tsd()->useSharedData ){
+ for(p=pBt->pCursor; p; p=p->pNext){
+ if( p!=pExcept && p->pgnoRoot==iRoot && p->eState==CURSOR_VALID ){
+ int rc = saveCursorPosition(p);
+ if( SQLITE_OK!=rc ){
+ return rc;
+ }
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Restore the cursor to the position it was in (or as close to as possible)
+** when saveCursorPosition() was called. Note that this call deletes the
+** saved position info stored by saveCursorPosition(), so there can be
+** at most one effective restoreCursorPosition() call after each
+** saveCursorPosition().
+**
+** If the second argument argument - doSeek - is false, then instead of
+** returning the cursor to it's saved position, any saved position is deleted
+** and the cursor state set to CURSOR_INVALID.
+*/
+static int restoreCursorPosition(BtCursor *pCur, int doSeek){
+ int rc = SQLITE_OK;
+ if( pCur->eState==CURSOR_REQUIRESEEK ){
+ assert( sqlite3Tsd()->useSharedData );
+ if( doSeek ){
+ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
+ }else{
+ pCur->eState = CURSOR_INVALID;
+ }
+ if( rc==SQLITE_OK ){
+ sqliteFree(pCur->pKey);
+ pCur->pKey = 0;
+ assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
+ }
+ }
+ return rc;
+}
+
+/*
** Query to see if btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
** SQLITE_OK if the lock may be obtained (by calling lockTable()), or
@@ -480,10 +607,36 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
BtShared *pBt = p->pBt;
BtLock *pIter;
- for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
- if( pIter->pBtree!=p && pIter->iTable==iTab &&
- (pIter->eLock!=READ_LOCK || eLock!=READ_LOCK) ){
- return SQLITE_BUSY;
+ /* This is a no-op if the shared-cache is not enabled */
+ if( 0==sqlite3Tsd()->useSharedData ){
+ return SQLITE_OK;
+ }
+
+ /* This (along with lockTable()) is where the ReadUncommitted flag is
+ ** dealt with. If the caller is querying for a read-lock and the flag is
+ ** set, it is unconditionally granted - even if there are write-locks
+ ** on the table. If a write-lock is requested, the ReadUncommitted flag
+ ** is not considered.
+ **
+ ** In function lockTable(), if a read-lock is demanded and the
+ ** ReadUncommitted flag is set, no entry is added to the locks list
+ ** (BtShared.pLock).
+ **
+ ** To summarize: If the ReadUncommitted flag is set, then read cursors do
+ ** not create or respect table locks. The locking procedure for a
+ ** write-cursor does not change.
+ */
+ if(
+ !p->pSqlite ||
+ 0==(p->pSqlite->flags&SQLITE_ReadUncommitted) ||
+ eLock==WRITE_LOCK ||
+ iTab==1
+ ){
+ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
+ if( pIter->pBtree!=p && pIter->iTable==iTab &&
+ (pIter->eLock!=eLock || eLock!=READ_LOCK) ){
+ return SQLITE_BUSY;
+ }
}
}
return SQLITE_OK;
@@ -502,8 +655,27 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
+ /* This is a no-op if the shared-cache is not enabled */
+ if( 0==sqlite3Tsd()->useSharedData ){
+ return SQLITE_OK;
+ }
+
assert( SQLITE_OK==queryTableLock(p, iTable, eLock) );
+ /* If the read-uncommitted flag is set and a read-lock is requested,
+ ** return early without adding an entry to the BtShared.pLock list. See
+ ** comment in function queryTableLock() for more info on handling
+ ** the ReadUncommitted flag.
+ */
+ if(
+ (p->pSqlite) &&
+ (p->pSqlite->flags&SQLITE_ReadUncommitted) &&
+ (eLock==READ_LOCK) &&
+ iTable!=1
+ ){
+ return SQLITE_OK;
+ }
+
/* First search the list for an existing lock on this table. */
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->iTable==iTable && pIter->pBtree==p ){
@@ -544,6 +716,13 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
*/
static void unlockAllTables(Btree *p){
BtLock **ppIter = &p->pBt->pLock;
+
+ /* If the shared-cache extension is not enabled, there should be no
+ ** locks in the BtShared.pLock list, making this procedure a no-op. Assert
+ ** that this is the case.
+ */
+ assert( sqlite3Tsd()->useSharedData || 0==*ppIter );
+
while( *ppIter ){
BtLock *pLock = *ppIter;
if( pLock->pBtree==p ){
@@ -681,27 +860,6 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
#endif /* SQLITE_OMIT_AUTOVACUUM */
/*
-** Return a pointer to the Btree structure associated with btree pBt
-** and connection handle pSqlite.
-*/
-#if 0
-static Btree *btree_findref(BtShared *pBt, sqlite3 *pSqlite){
-#ifndef SQLITE_OMIT_SHARED_DATA
- if( pBt->aRef ){
- int i;
- for(i=0; i<pBt->nRef; i++){
- if( pBt->aRef[i].pSqlite==pSqlite ){
- return &pBt->aRef[i];
- }
- }
- assert(0);
- }
-#endif
- return &pBt->ref;
-}
-#endif
-
-/*
** Given a btree page and a cell index (0 means the first cell on
** the page, 1 means the second cell, and so forth) return a pointer
** to the cell content.
@@ -1386,7 +1544,9 @@ int sqlite3BtreeOpen(
int rc;
int nReserve;
unsigned char zDbHeader[100];
+#ifndef SQLITE_OMIT_SHARED_CACHE
SqliteTsd *pTsd = sqlite3Tsd();
+#endif
/* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if
@@ -1501,8 +1661,8 @@ int sqlite3BtreeOpen(
pBt->pNext = pTsd->pBtree;
pTsd->pBtree = pBt;
}
- pBt->nRef = 1;
#endif
+ pBt->nRef = 1;
*ppBtree = p;
return SQLITE_OK;
}
@@ -1511,10 +1671,13 @@ int sqlite3BtreeOpen(
** Close an open database and invalidate all cursors.
*/
int sqlite3BtreeClose(Btree *p){
- SqliteTsd *pTsd = sqlite3Tsd();
BtShared *pBt = p->pBt;
BtCursor *pCur;
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ SqliteTsd *pTsd = sqlite3Tsd();
+#endif
+
/* Drop any table-locks */
unlockAllTables(p);
@@ -1556,6 +1719,9 @@ int sqlite3BtreeClose(Btree *p){
/* Close the pager and free the shared-btree structure */
assert( !pBt->pCursor );
sqlite3pager_close(pBt->pPager);
+ if( pBt->xFreeSchema && pBt->pSchema ){
+ pBt->xFreeSchema(pBt->pSchema);
+ }
sqliteFree(pBt);
return SQLITE_OK;
}
@@ -2323,7 +2489,7 @@ void sqlite3BtreeCursorList(Btree *p){
sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
pCur, pCur->pgnoRoot, zMode,
pPage ? pPage->pgno : 0, pCur->idx,
- pCur->isValid ? "" : " eof"
+ (pCur->eState==CURSOR_VALID) ? "" : " eof"
);
}
}
@@ -2532,7 +2698,7 @@ int sqlite3BtreeCursor(
return rc;
}
}
- pCur = sqliteMallocRaw( sizeof(*pCur) );
+ pCur = sqliteMalloc( sizeof(*pCur) );
if( pCur==0 ){
rc = SQLITE_NOMEM;
goto create_cursor_exception;
@@ -2571,7 +2737,7 @@ int sqlite3BtreeCursor(
}
pCur->pPrev = 0;
pBt->pCursor = pCur;
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
*ppCur = pCur;
return SQLITE_OK;
@@ -2604,6 +2770,7 @@ void sqlite3BtreeSetCompare(
*/
int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBtree->pBt;
+ restoreCursorPosition(pCur, 0);
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
}else{
@@ -2670,13 +2837,17 @@ static void getCellInfo(BtCursor *pCur){
** itself, not the number of bytes in the key.
*/
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
- if( !pCur->isValid ){
- *pSize = 0;
- }else{
- getCellInfo(pCur);
- *pSize = pCur->info.nKey;
+ int rc = restoreCursorPosition(pCur, 1);
+ if( rc==SQLITE_OK ){
+ assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
+ if( pCur->eState==CURSOR_INVALID ){
+ *pSize = 0;
+ }else{
+ getCellInfo(pCur);
+ *pSize = pCur->info.nKey;
+ }
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -2687,14 +2858,18 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
** the database is empty) then *pSize is set to 0.
*/
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
- if( !pCur->isValid ){
- /* Not pointing at a valid entry - set *pSize to 0. */
- *pSize = 0;
- }else{
- getCellInfo(pCur);
- *pSize = pCur->info.nData;
+ int rc = restoreCursorPosition(pCur, 1);
+ if( rc==SQLITE_OK ){
+ assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
+ if( pCur->eState==CURSOR_INVALID ){
+ /* Not pointing at a valid entry - set *pSize to 0. */
+ *pSize = 0;
+ }else{
+ getCellInfo(pCur);
+ *pSize = pCur->info.nData;
+ }
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -2722,7 +2897,7 @@ static int getPayload(
u32 nKey;
assert( pCur!=0 && pCur->pPage!=0 );
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
pBt = pCur->pBtree->pBt;
pPage = pCur->pPage;
pageIntegrity(pPage);
@@ -2798,14 +2973,18 @@ static int getPayload(
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- assert( pCur->isValid );
- assert( pCur->pPage!=0 );
- if( pCur->pPage->intKey ){
- return SQLITE_CORRUPT_BKPT;
+ int rc = restoreCursorPosition(pCur, 1);
+ if( rc==SQLITE_OK ){
+ assert( pCur->eState==CURSOR_VALID );
+ assert( pCur->pPage!=0 );
+ if( pCur->pPage->intKey ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ assert( pCur->pPage->intKey==0 );
+ assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+ rc = getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
}
- assert( pCur->pPage->intKey==0 );
- assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
- return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
+ return rc;
}
/*
@@ -2818,10 +2997,14 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- assert( pCur->isValid );
- assert( pCur->pPage!=0 );
- assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
- return getPayload(pCur, offset, amt, pBuf, 1);
+ int rc = restoreCursorPosition(pCur, 1);
+ if( rc==SQLITE_OK ){
+ assert( pCur->eState==CURSOR_VALID );
+ assert( pCur->pPage!=0 );
+ assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+ rc = getPayload(pCur, offset, amt, pBuf, 1);
+ }
+ return rc;
}
/*
@@ -2854,7 +3037,7 @@ static const unsigned char *fetchPayload(
int nLocal;
assert( pCur!=0 && pCur->pPage!=0 );
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
@@ -2892,10 +3075,16 @@ static const unsigned char *fetchPayload(
** in the common case where no overflow pages are used.
*/
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
- return (const void*)fetchPayload(pCur, pAmt, 0);
+ if( pCur->eState==CURSOR_VALID ){
+ return (const void*)fetchPayload(pCur, pAmt, 0);
+ }
+ return 0;
}
const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
- return (const void*)fetchPayload(pCur, pAmt, 1);
+ if( pCur->eState==CURSOR_VALID ){
+ return (const void*)fetchPayload(pCur, pAmt, 1);
+ }
+ return 0;
}
@@ -2909,7 +3098,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
MemPage *pOldPage;
BtShared *pBt = pCur->pBtree->pBt;
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc;
pageIntegrity(pNewPage);
@@ -2956,7 +3145,7 @@ static void moveToParent(BtCursor *pCur){
MemPage *pPage;
int idxParent;
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
assert( pPage!=0 );
assert( !isRootPage(pPage) );
@@ -2981,9 +3170,10 @@ static int moveToRoot(BtCursor *pCur){
int rc;
BtShared *pBt = pCur->pBtree->pBt;
+ restoreCursorPosition(pCur, 0);
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0);
if( rc ){
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
return rc;
}
releasePage(pCur->pPage);
@@ -2996,10 +3186,10 @@ static int moveToRoot(BtCursor *pCur){
assert( pRoot->pgno==1 );
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
assert( subpage>0 );
- pCur->isValid = 1;
+ pCur->eState = CURSOR_VALID;
rc = moveToChild(pCur, subpage);
}
- pCur->isValid = pCur->pPage->nCell>0;
+ pCur->eState = ((pCur->pPage->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
return rc;
}
@@ -3012,7 +3202,7 @@ static int moveToLeftmost(BtCursor *pCur){
int rc;
MemPage *pPage;
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->pPage)->leaf ){
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
pgno = get4byte(findCell(pPage, pCur->idx));
@@ -3034,7 +3224,7 @@ static int moveToRightmost(BtCursor *pCur){
int rc;
MemPage *pPage;
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->pPage)->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->idx = pPage->nCell;
@@ -3054,7 +3244,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc;
rc = moveToRoot(pCur);
if( rc ) return rc;
- if( pCur->isValid==0 ){
+ if( pCur->eState==CURSOR_INVALID ){
assert( pCur->pPage->nCell==0 );
*pRes = 1;
return SQLITE_OK;
@@ -3073,12 +3263,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
rc = moveToRoot(pCur);
if( rc ) return rc;
- if( pCur->isValid==0 ){
+ if( CURSOR_INVALID==pCur->eState ){
assert( pCur->pPage->nCell==0 );
*pRes = 1;
return SQLITE_OK;
}
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
return rc;
@@ -3117,7 +3307,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
if( rc ) return rc;
assert( pCur->pPage );
assert( pCur->pPage->isInit );
- if( pCur->isValid==0 ){
+ if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
assert( pCur->pPage->nCell==0 );
return SQLITE_OK;
@@ -3209,7 +3399,11 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
** the first entry. TRUE is also returned if the table is empty.
*/
int sqlite3BtreeEof(BtCursor *pCur){
- return pCur->isValid==0;
+ /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries
+ ** have been deleted? This API will need to change to return an error code
+ ** as well as the boolean result value.
+ */
+ return (CURSOR_VALID!=pCur->eState);
}
/*
@@ -3222,8 +3416,21 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage = pCur->pPage;
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ rc = restoreCursorPosition(pCur, 1);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( pCur->skip>0 ){
+ pCur->skip = 0;
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ pCur->skip = 0;
+#endif
+
assert( pRes!=0 );
- if( pCur->isValid==0 ){
+ if( CURSOR_INVALID==pCur->eState ){
*pRes = 1;
return SQLITE_OK;
}
@@ -3243,7 +3450,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
do{
if( isRootPage(pPage) ){
*pRes = 1;
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}
moveToParent(pCur);
@@ -3275,7 +3482,21 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
int rc;
Pgno pgno;
MemPage *pPage;
- if( pCur->isValid==0 ){
+
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ rc = restoreCursorPosition(pCur, 1);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( pCur->skip<0 ){
+ pCur->skip = 0;
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ pCur->skip = 0;
+#endif
+
+ if( CURSOR_INVALID==pCur->eState ){
*pRes = 1;
return SQLITE_OK;
}
@@ -3291,7 +3512,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
}else{
while( pCur->idx==0 ){
if( isRootPage(pPage) ){
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
*pRes = 1;
return SQLITE_OK;
}
@@ -4887,7 +5108,9 @@ static int balance(MemPage *pPage, int insert){
static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
BtCursor *p;
for(p=pBt->pCursor; p; p=p->pNext){
+ u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0);
if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
+ if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue;
if( p->wrFlag==0 ) return SQLITE_LOCKED;
if( p->pPage->pgno!=p->pgnoRoot ){
moveToRoot(p);
@@ -4929,6 +5152,14 @@ int sqlite3BtreeInsert(
if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
+
+ /* Save the positions of any other cursors open on this table */
+ restoreCursorPosition(pCur, 0);
+ rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ if( rc ){
+ return rc;
+ }
+
rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc);
if( rc ) return rc;
pPage = pCur->pPage;
@@ -4946,7 +5177,7 @@ int sqlite3BtreeInsert(
if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) );
assert( szNew<=MX_CELL_SIZE(pBt) );
- if( loc==0 && pCur->isValid ){
+ if( loc==0 && CURSOR_VALID==pCur->eState ){
int szOld;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
oldCell = findCell(pPage, pCur->idx);
@@ -5003,8 +5234,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
- rc = sqlite3pager_write(pPage->aData);
- if( rc ) return rc;
+
+ /* Restore the current cursor position (a no-op if the cursor is not in
+ ** CURSOR_REQUIRESEEK state) and save the positions of any other cursors
+ ** open on the same table. Then call sqlite3pager_write() on the page
+ ** that the entry will be deleted from.
+ */
+ if(
+ (rc = restoreCursorPosition(pCur, 1)) ||
+ (rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
+ (rc = sqlite3pager_write(pPage->aData))
+ ){
+ return rc;
+ }
/* Locate the cell within it's page and leave pCell pointing to the
** data. The clearCell() call frees any overflow pages associated with the
@@ -5427,6 +5669,16 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
unsigned char *pP1;
BtShared *pBt = p->pBt;
+ /* Reading a meta-data value requires a read-lock on page 1 (and hence
+ ** the sqlite_master table. We grab this lock regardless of whether or
+ ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
+ ** 1 is treated as a special case by queryTableLock() and lockTable()).
+ */
+ rc = queryTableLock(p, 1, READ_LOCK);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
assert( idx>=0 && idx<=15 );
rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1);
if( rc ) return rc;
@@ -5440,7 +5692,9 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
#endif
- return SQLITE_OK;
+ /* Grab the read-lock on page 1. */
+ rc = lockTable(p, 1, READ_LOCK);
+ return rc;
}
/*
@@ -5468,6 +5722,9 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
** is currently pointing to.
*/
int sqlite3BtreeFlags(BtCursor *pCur){
+ /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
+ ** restoreCursorPosition() here.
+ */
MemPage *pPage = pCur->pPage;
return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}
@@ -5601,6 +5858,11 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
MemPage *pPage = pCur->pPage;
BtCursor tmpCur;
+ int rc = restoreCursorPosition(pCur, 1);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
pageIntegrity(pPage);
assert( pPage->isInit );
getTempCursor(pCur, &tmpCur);
@@ -6196,6 +6458,31 @@ int sqlite3BtreeSync(Btree *p, const char *zMaster){
return SQLITE_OK;
}
+/*
+** This function returns a pointer to a blob of memory associated with
+** a single shared-btree. The memory is used by client code for it's own
+** purposes (for example, to store a high-level schema associated with
+** the shared-btree). The btree layer manages reference counting issues.
+**
+** The first time this is called on a shared-btree, nBytes bytes of memory
+** are allocated, zeroed, and returned to the caller. For each subsequent
+** call the nBytes parameter is ignored and a pointer to the same blob
+** of memory returned.
+**
+** Just before the shared-btree is closed, the function passed as the
+** xFree argument when the memory allocation was made is invoked on the
+** blob of allocated memory. This function should not call sqliteFree()
+** on the memory, the btree layer does that.
+*/
+void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
+ BtShared *pBt = p->pBt;
+ if( !pBt->pSchema ){
+ pBt->pSchema = sqliteMalloc(nBytes);
+ pBt->xFreeSchema = xFree;
+ }
+ return pBt->pSchema;
+}
+
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Enable the shared pager and schema features.
diff --git a/src/btree.h b/src/btree.h
index 75bb991a5..69eb34fbf 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -13,7 +13,7 @@
** subsystem. See comments in the source code for a detailed description
** of what each interface routine does.
**
-** @(#) $Id: btree.h,v 1.66 2005/12/30 16:28:02 danielk1977 Exp $
+** @(#) $Id: btree.h,v 1.67 2006/01/05 11:34:34 danielk1977 Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_
@@ -76,6 +76,7 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeSync(Btree*, const char *zMaster);
+void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
const char *sqlite3BtreeGetFilename(Btree *);
const char *sqlite3BtreeGetDirname(Btree *);
diff --git a/src/build.c b/src/build.c
index 8907fdf2f..3170bcf2d 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
** COMMIT
** ROLLBACK
**
-** $Id: build.c,v 1.366 2006/01/04 21:40:07 drh Exp $
+** $Id: build.c,v 1.367 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -172,7 +172,7 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
- p = sqlite3HashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1);
+ p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, strlen(zName)+1);
if( p ) break;
}
return p;
@@ -227,8 +227,12 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
assert( (db->flags & SQLITE_Initialized) || db->init.busy );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
+ DbSchema *pSchema = db->aDb[j].pSchema;
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
- p = sqlite3HashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1);
+ assert( pSchema || (j==1 && !db->aDb[1].pBt) );
+ if( pSchema ){
+ p = sqlite3HashFind(&pSchema->idxHash, zName, strlen(zName)+1);
+ }
if( p ) break;
}
return p;
@@ -252,10 +256,10 @@ static void freeIndex(Index *p){
*/
static void sqliteDeleteIndex(sqlite3 *db, Index *p){
Index *pOld;
+ const char *zName = p->zName;
- assert( db!=0 && p->zName!=0 );
- pOld = sqlite3HashInsert(&db->aDb[p->iDb].idxHash, p->zName,
- strlen(p->zName)+1, 0);
+ assert( db!=0 && zName!=0 );
+ pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen( zName)+1, 0);
assert( pOld==0 || pOld==p );
freeIndex(p);
}
@@ -269,9 +273,10 @@ static void sqliteDeleteIndex(sqlite3 *db, Index *p){
void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
Index *pIndex;
int len;
+ Hash *pHash = &db->aDb[iDb].pSchema->idxHash;
len = strlen(zIdxName);
- pIndex = sqlite3HashInsert(&db->aDb[iDb].idxHash, zIdxName, len+1, 0);
+ pIndex = sqlite3HashInsert(pHash, zIdxName, len+1, 0);
if( pIndex ){
if( pIndex->pTable->pIndex==pIndex ){
pIndex->pTable->pIndex = pIndex->pNext;
@@ -308,23 +313,25 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
db->flags &= ~SQLITE_Initialized;
for(i=iDb; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
- temp1 = pDb->tblHash;
- temp2 = pDb->trigHash;
- sqlite3HashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0);
- sqlite3HashClear(&pDb->aFKey);
- sqlite3HashClear(&pDb->idxHash);
- for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
- sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
- }
- sqlite3HashClear(&temp2);
- sqlite3HashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0);
- for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTab = sqliteHashData(pElem);
- sqlite3DeleteTable(db, pTab);
- }
- sqlite3HashClear(&temp1);
- pDb->pSeqTab = 0;
- DbClearProperty(db, i, DB_SchemaLoaded);
+ if( pDb->pSchema ){
+ temp1 = pDb->pSchema->tblHash;
+ temp2 = pDb->pSchema->trigHash;
+ sqlite3HashInit(&pDb->pSchema->trigHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashClear(&pDb->pSchema->aFKey);
+ sqlite3HashClear(&pDb->pSchema->idxHash);
+ for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
+ sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
+ }
+ sqlite3HashClear(&temp2);
+ sqlite3HashInit(&pDb->pSchema->tblHash, SQLITE_HASH_STRING, 0);
+ for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
+ Table *pTab = sqliteHashData(pElem);
+ sqlite3DeleteTable(db, pTab);
+ }
+ sqlite3HashClear(&temp1);
+ pDb->pSchema->pSeqTab = 0;
+ DbClearProperty(db, i, DB_SchemaLoaded);
+ }
if( iDb>0 ) return;
}
assert( iDb==0 );
@@ -433,7 +440,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
*/
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
pNext = pIndex->pNext;
- assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) );
+ assert( pIndex->pSchema==pTable->pSchema );
sqliteDeleteIndex(db, pIndex);
}
@@ -443,8 +450,8 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
*/
for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
pNextFKey = pFKey->pNextFrom;
- assert( pTable->iDb<db->nDb );
- assert( sqlite3HashFind(&db->aDb[pTable->iDb].aFKey,
+ assert( sqlite3SchemaToIndex(db, pTable->pSchema)<db->nDb );
+ assert( sqlite3HashFind(&pTable->pSchema->aFKey,
pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
sqliteFree(pFKey);
}
@@ -475,14 +482,14 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
assert( iDb>=0 && iDb<db->nDb );
assert( zTabName && zTabName[0] );
pDb = &db->aDb[iDb];
- p = sqlite3HashInsert(&pDb->tblHash, zTabName, strlen(zTabName)+1, 0);
+ p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, strlen(zTabName)+1,0);
if( p ){
#ifndef SQLITE_OMIT_FOREIGN_KEY
for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
int nTo = strlen(pF1->zTo) + 1;
- pF2 = sqlite3HashFind(&pDb->aFKey, pF1->zTo, nTo);
+ pF2 = sqlite3HashFind(&pDb->pSchema->aFKey, pF1->zTo, nTo);
if( pF2==pF1 ){
- sqlite3HashInsert(&pDb->aFKey, pF1->zTo, nTo, pF1->pNextTo);
+ sqlite3HashInsert(&pDb->pSchema->aFKey, pF1->zTo, nTo, pF1->pNextTo);
}else{
while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
if( pF2 ){
@@ -734,7 +741,7 @@ void sqlite3StartTable(
pTable->aCol = 0;
pTable->iPKey = -1;
pTable->pIndex = 0;
- pTable->iDb = iDb;
+ pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable);
pParse->pNewTable = pTable;
@@ -745,7 +752,7 @@ void sqlite3StartTable(
*/
#ifndef SQLITE_OMIT_AUTOINCREMENT
if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){
- db->aDb[iDb].pSeqTab = pTable;
+ pTable->pSchema->pSeqTab = pTable;
}
#endif
@@ -1179,7 +1186,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
** 1 chance in 2^32. So we're safe enough.
*/
void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){
- sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].schema_cookie+1, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, 0);
sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0);
}
@@ -1227,7 +1234,7 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
** table. Memory to hold the text of the statement is obtained
** from sqliteMalloc() and must be freed by the calling function.
*/
-static char *createTableStmt(Table *p){
+static char *createTableStmt(Table *p, int isTemp){
int i, k, n;
char *zStmt;
char *zSep, *zSep2, *zEnd, *z;
@@ -1253,7 +1260,7 @@ static char *createTableStmt(Table *p){
n += 35 + 6*p->nCol;
zStmt = sqliteMallocRaw( n );
if( zStmt==0 ) return 0;
- strcpy(zStmt, !OMIT_TEMPDB&&p->iDb==1 ? "CREATE TEMP TABLE ":"CREATE TABLE ");
+ strcpy(zStmt, !OMIT_TEMPDB&&isTemp ? "CREATE TEMP TABLE ":"CREATE TABLE ");
k = strlen(zStmt);
identPut(zStmt, &k, p->zName);
zStmt[k++] = '(';
@@ -1300,6 +1307,7 @@ void sqlite3EndTable(
){
Table *p;
sqlite3 *db = pParse->db;
+ int iDb;
if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3Tsd()->mallocFailed ) {
return;
@@ -1309,6 +1317,8 @@ void sqlite3EndTable(
assert( !db->init.busy || !pSelect );
+ iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+
#ifndef SQLITE_OMIT_CHECK
/* Resolve names in all CHECK constraint expressions.
*/
@@ -1387,7 +1397,7 @@ void sqlite3EndTable(
if( pSelect ){
Table *pSelTab;
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3VdbeAddOp(v, OP_Integer, p->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0);
pParse->nTab = 2;
sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0);
@@ -1406,7 +1416,7 @@ void sqlite3EndTable(
/* Compute the complete text of the CREATE statement */
if( pSelect ){
- zStmt = createTableStmt(p);
+ zStmt = createTableStmt(p, p->pSchema==pParse->db->aDb[1].pSchema);
}else{
n = pEnd->z - pParse->sNameToken.z + 1;
zStmt = sqlite3MPrintf("CREATE %s %.*s", zType2, n, pParse->sNameToken.z);
@@ -1422,22 +1432,22 @@ void sqlite3EndTable(
"UPDATE %Q.%s "
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q "
"WHERE rowid=#1",
- db->aDb[p->iDb].zName, SCHEMA_TABLE(p->iDb),
+ db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
zType,
p->zName,
p->zName,
zStmt
);
sqliteFree(zStmt);
- sqlite3ChangeCookie(db, v, p->iDb);
+ sqlite3ChangeCookie(db, v, iDb);
#ifndef SQLITE_OMIT_AUTOINCREMENT
/* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
if( p->autoInc ){
- Db *pDb = &db->aDb[p->iDb];
- if( pDb->pSeqTab==0 ){
+ Db *pDb = &db->aDb[iDb];
+ if( pDb->pSchema->pSeqTab==0 ){
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
pDb->zName
@@ -1447,7 +1457,7 @@ void sqlite3EndTable(
#endif
/* Reparse everything to update our internal data structures */
- sqlite3VdbeOp3(v, OP_ParseSchema, p->iDb, 0,
+ sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC);
}
@@ -1457,8 +1467,8 @@ void sqlite3EndTable(
if( db->init.busy && pParse->nErr==0 ){
Table *pOld;
FKey *pFKey;
- Db *pDb = &db->aDb[p->iDb];
- pOld = sqlite3HashInsert(&pDb->tblHash, p->zName, strlen(p->zName)+1, p);
+ DbSchema *pSchema = p->pSchema;
+ pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, strlen(p->zName)+1,p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
return;
@@ -1466,8 +1476,8 @@ void sqlite3EndTable(
#ifndef SQLITE_OMIT_FOREIGN_KEY
for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
int nTo = strlen(pFKey->zTo) + 1;
- pFKey->pNextTo = sqlite3HashFind(&pDb->aFKey, pFKey->zTo, nTo);
- sqlite3HashInsert(&pDb->aFKey, pFKey->zTo, nTo, pFKey);
+ pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo);
+ sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
}
#endif
pParse->pNewTable = 0;
@@ -1502,6 +1512,7 @@ void sqlite3CreateView(
Token sEnd;
DbFixer sFix;
Token *pName;
+ int iDb;
if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
@@ -1515,7 +1526,8 @@ void sqlite3CreateView(
return;
}
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
- if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName)
+ iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+ if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
&& sqlite3FixSelect(&sFix, pSelect)
){
sqlite3SelectDelete(pSelect);
@@ -1615,7 +1627,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
pSelTab->nCol = 0;
pSelTab->aCol = 0;
sqlite3DeleteTable(0, pSelTab);
- DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews);
+ pTable->pSchema->flags |= DB_UnresetViews;
}else{
pTable->nCol = 0;
nErr++;
@@ -1635,7 +1647,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
static void sqliteViewResetAll(sqlite3 *db, int idx){
HashElem *i;
if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
- for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
+ for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){
sqliteResetColumnNames(pTab);
@@ -1656,15 +1668,18 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
#ifndef SQLITE_OMIT_AUTOVACUUM
void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){
HashElem *pElem;
-
- for(pElem=sqliteHashFirst(&pDb->tblHash); pElem; pElem=sqliteHashNext(pElem)){
+ Hash *pHash;
+
+ pHash = &pDb->pSchema->tblHash;
+ for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){
Table *pTab = sqliteHashData(pElem);
if( pTab->tnum==iFrom ){
pTab->tnum = iTo;
return;
}
}
- for(pElem=sqliteHashFirst(&pDb->idxHash); pElem; pElem=sqliteHashNext(pElem)){
+ pHash = &pDb->pSchema->idxHash;
+ for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){
Index *pIdx = sqliteHashData(pElem);
if( pIdx->tnum==iFrom ){
pIdx->tnum = iTo;
@@ -1741,14 +1756,18 @@ static void destroyTable(Parse *pParse, Table *pTab){
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int iIdx = pIdx->tnum;
- assert( pIdx->iDb==pTab->iDb );
+ assert( pIdx->pSchema==pTab->pSchema );
if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
iLargest = iIdx;
}
}
- if( iLargest==0 ) return;
- destroyRootPage(pParse, iLargest, pTab->iDb);
- iDestroyed = iLargest;
+ if( iLargest==0 ){
+ return;
+ }else{
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ destroyRootPage(pParse, iLargest, iDb);
+ iDestroyed = iLargest;
+ }
}
#endif
}
@@ -1773,13 +1792,13 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
}
goto exit_drop_table;
}
- iDb = pTab->iDb;
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 && iDb<db->nDb );
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code;
- const char *zTab = SCHEMA_TABLE(pTab->iDb);
- const char *zDb = db->aDb[pTab->iDb].zName;
+ const char *zTab = SCHEMA_TABLE(iDb);
+ const char *zDb = db->aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
}
@@ -1804,7 +1823,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
}
}
#endif
- if( pTab->readOnly || pTab==db->aDb[iDb].pSeqTab ){
+ if( pTab->readOnly || pTab==db->aDb[iDb].pSchema->pSeqTab ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -1829,7 +1848,6 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
v = sqlite3GetVdbe(pParse);
if( v ){
Trigger *pTrigger;
- int iDb = pTab->iDb;
Db *pDb = &db->aDb[iDb];
sqlite3BeginWriteOperation(pParse, 0, iDb);
@@ -1839,7 +1857,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
*/
pTrigger = pTab->pTrigger;
while( pTrigger ){
- assert( pTrigger->iDb==iDb || pTrigger->iDb==1 );
+ assert( pTrigger->pSchema==pTab->pSchema ||
+ pTrigger->pSchema==db->aDb[1].pSchema );
sqlite3DropTriggerPtr(pParse, pTrigger, 1);
pTrigger = pTrigger->pNext;
}
@@ -2035,10 +2054,11 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int addr1; /* Address of top of loop */
int tnum; /* Root page of index */
Vdbe *v; /* Generate code into this virtual machine */
+ int iDb = sqlite3SchemaToIndex(pParse->db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
- pParse->db->aDb[pIndex->iDb].zName ) ){
+ pParse->db->aDb[iDb].zName ) ){
return;
}
#endif
@@ -2058,12 +2078,12 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
tnum = 0;
}else{
tnum = pIndex->tnum;
- sqlite3VdbeAddOp(v, OP_Clear, tnum, pIndex->iDb);
+ sqlite3VdbeAddOp(v, OP_Clear, tnum, iDb);
}
- sqlite3VdbeAddOp(v, OP_Integer, pIndex->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeOp3(v, OP_OpenWrite, iIdx, tnum,
(char*)&pIndex->keyInfo, P3_KEYINFO);
- sqlite3OpenTableForReading(v, iTab, pTab);
+ sqlite3OpenTableForReading(v, iTab, iDb, pTab);
addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
sqlite3GenerateIndexKey(v, pIndex, iTab);
if( pIndex->onError!=OE_None ){
@@ -2142,7 +2162,7 @@ void sqlite3CreateIndex(
** is a temp table. If so, set the database to 1.
*/
pTab = sqlite3SrcListLookup(pParse, pTblName);
- if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){
+ if( pName2 && pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){
iDb = 1;
}
#endif
@@ -2157,12 +2177,12 @@ void sqlite3CreateIndex(
pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName,
pTblName->a[0].zDatabase);
if( !pTab ) goto exit_create_index;
- assert( iDb==pTab->iDb );
+ assert( db->aDb[iDb].pSchema==pTab->pSchema );
}else{
assert( pName==0 );
- pTab = pParse->pNewTable;
+ pTab = pParse->pNewTable;
if( !pTab ) goto exit_create_index;
- iDb = pTab->iDb;
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
}
pDb = &db->aDb[iDb];
@@ -2266,11 +2286,11 @@ void sqlite3CreateIndex(
pIndex->nColumn = pList->nExpr;
pIndex->onError = onError;
pIndex->autoIndex = pName==0;
- pIndex->iDb = iDb;
+ pIndex->pSchema = db->aDb[iDb].pSchema;
/* Check to see if we should honor DESC requests on index columns
*/
- if( pDb->file_format>=4 ){
+ if( pDb->pSchema->file_format>=4 ){
sortOrderMask = -1; /* Honor DESC */
}else{
sortOrderMask = 0; /* Ignore DESC */
@@ -2365,7 +2385,7 @@ void sqlite3CreateIndex(
*/
if( db->init.busy ){
Index *p;
- p = sqlite3HashInsert(&db->aDb[pIndex->iDb].idxHash,
+ p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
pIndex->zName, strlen(pIndex->zName)+1, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
@@ -2533,6 +2553,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
Index *pIndex;
Vdbe *v;
sqlite3 *db = pParse->db;
+ int iDb;
if( pParse->nErr || sqlite3Tsd()->mallocFailed ){
goto exit_drop_index;
@@ -2554,16 +2575,17 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
"or PRIMARY KEY constraint cannot be dropped", 0);
goto exit_drop_index;
}
+ iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable;
- const char *zDb = db->aDb[pIndex->iDb].zName;
- const char *zTab = SCHEMA_TABLE(pIndex->iDb);
+ const char *zDb = db->aDb[iDb].zName;
+ const char *zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
}
- if( !OMIT_TEMPDB && pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX;
+ if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
goto exit_drop_index;
}
@@ -2573,7 +2595,6 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
/* Generate code to remove the index and from the master table */
v = sqlite3GetVdbe(pParse);
if( v ){
- int iDb = pIndex->iDb;
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
@@ -2853,6 +2874,12 @@ static int sqlite3OpenTempDatabase(Parse *pParse){
pParse->rc = rc;
return 1;
}
+/*
+ db->aDb[1].pSchema = sqlite3SchemaGet(db->aDb[1].pBt);
+ if( !db->aDb[1].pSchema ){
+ return SQLITE_NOMEM;
+ }
+*/
if( db->flags & !db->autoCommit ){
rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1);
if( rc!=SQLITE_OK ){
@@ -2906,7 +2933,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
mask = 1<<iDb;
if( (pParse->cookieMask & mask)==0 ){
pParse->cookieMask |= mask;
- pParse->cookieValue[iDb] = db->aDb[iDb].schema_cookie;
+ pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
if( !OMIT_TEMPDB && iDb==1 ){
sqlite3OpenTempDatabase(pParse);
}
@@ -2971,7 +2998,8 @@ static void reindexTable(Parse *pParse, Table *pTab, CollSeq *pColl){
for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
if( pColl==0 || collationMatch(pColl,pIndex) ){
- sqlite3BeginWriteOperation(pParse, 0, pTab->iDb);
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
}
}
@@ -2993,7 +3021,7 @@ static void reindexDatabases(Parse *pParse, CollSeq *pColl){
for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
if( pDb==0 ) continue;
- for(k=sqliteHashFirst(&pDb->tblHash); k; k=sqliteHashNext(k)){
+ for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
pTab = (Table*)sqliteHashData(k);
reindexTable(pParse, pTab, pColl);
}
diff --git a/src/delete.c b/src/delete.c
index 259e0f5a8..9114304ed 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
-** $Id: delete.c,v 1.113 2005/12/15 15:22:09 danielk1977 Exp $
+** $Id: delete.c,v 1.114 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -62,9 +62,10 @@ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
void sqlite3OpenTableForReading(
Vdbe *v, /* Generate code into this VDBE */
int iCur, /* The cursor number of the table */
+ int iDb, /* The database index in sqlite3.aDb[] */
Table *pTab /* The table to be opened */
){
- sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
VdbeComment((v, "# %s", pTab->zName));
sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
@@ -95,6 +96,7 @@ void sqlite3DeleteFrom(
AuthContext sContext; /* Authorization context */
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
NameContext sNC; /* Name context to resolve expressions in */
+ int iDb;
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */
@@ -134,8 +136,9 @@ void sqlite3DeleteFrom(
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
goto delete_from_cleanup;
}
- assert( pTab->iDb<db->nDb );
- zDb = db->aDb[pTab->iDb].zName;
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( iDb<db->nDb );
+ zDb = db->aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
goto delete_from_cleanup;
}
@@ -176,7 +179,7 @@ void sqlite3DeleteFrom(
goto delete_from_cleanup;
}
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
- sqlite3BeginWriteOperation(pParse, triggers_exist, pTab->iDb);
+ sqlite3BeginWriteOperation(pParse, triggers_exist, iDb);
/* If we are trying to delete from a view, realize that view into
** a ephemeral table.
@@ -205,7 +208,7 @@ void sqlite3DeleteFrom(
int endOfLoop = sqlite3VdbeMakeLabel(v);
int addr;
if( !isView ){
- sqlite3OpenTableForReading(v, iCur, pTab);
+ sqlite3OpenTableForReading(v, iCur, iDb, pTab);
}
sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
@@ -214,12 +217,13 @@ void sqlite3DeleteFrom(
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
}
if( !isView ){
- sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
+ sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb);
if( !pParse->nested ){
sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
+ assert( pIdx->pSchema==pTab->pSchema );
+ sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, iDb);
}
}
}
@@ -272,7 +276,7 @@ void sqlite3DeleteFrom(
addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
if( !isView ){
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
- sqlite3OpenTableForReading(v, iCur, pTab);
+ sqlite3OpenTableForReading(v, iCur, iDb, pTab);
}
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
diff --git a/src/expr.c b/src/expr.c
index d56b81943..2846017db 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.243 2006/01/03 15:16:26 drh Exp $
+** $Id: expr.c,v 1.244 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -843,6 +843,7 @@ static int lookupName(
if( pSrcList ){
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
Table *pTab = pItem->pTab;
+ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
Column *pCol;
if( pTab==0 ) continue;
@@ -854,14 +855,14 @@ static int lookupName(
}else{
char *zTabName = pTab->zName;
if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
- if( zDb!=0 && sqlite3StrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){
+ if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
continue;
}
}
}
if( 0==(cntTab++) ){
pExpr->iTable = pItem->iCursor;
- pExpr->iDb = pTab->iDb;
+ pExpr->pSchema = pTab->pSchema;
pMatch = pItem;
}
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
@@ -870,7 +871,7 @@ static int lookupName(
cnt++;
pExpr->iTable = pItem->iCursor;
pMatch = pItem;
- pExpr->iDb = pTab->iDb;
+ pExpr->pSchema = pTab->pSchema;
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
pExpr->affinity = pTab->aCol[j].affinity;
@@ -921,7 +922,7 @@ static int lookupName(
int j;
Column *pCol = pTab->aCol;
- pExpr->iDb = pTab->iDb;
+ pExpr->pSchema = pTab->pSchema;
cntTab++;
for(j=0; j < pTab->nCol; j++, pCol++) {
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
diff --git a/src/insert.c b/src/insert.c
index bf7c594d3..64bff8d5c 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
-** $Id: insert.c,v 1.151 2005/12/15 15:22:09 danielk1977 Exp $
+** $Id: insert.c,v 1.152 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -104,15 +104,15 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
**
** No checking is done for sub-selects that are part of expressions.
*/
-static int selectReadsTable(Select *p, int iDb, int iTab){
+static int selectReadsTable(Select *p, DbSchema *pSchema, int iTab){
int i;
struct SrcList_item *pItem;
if( p->pSrc==0 ) return 0;
for(i=0, pItem=p->pSrc->a; i<p->pSrc->nSrc; i++, pItem++){
if( pItem->pSelect ){
- if( selectReadsTable(pItem->pSelect, iDb, iTab) ) return 1;
+ if( selectReadsTable(pItem->pSelect, pSchema, iTab) ) return 1;
}else{
- if( pItem->pTab->iDb==iDb && pItem->pTab->tnum==iTab ) return 1;
+ if( pItem->pTab->pSchema==pSchema && pItem->pTab->tnum==iTab ) return 1;
}
}
return 0;
@@ -214,6 +214,7 @@ void sqlite3Insert(
int newIdx = -1; /* Cursor for the NEW table */
Db *pDb; /* The database containing table being inserted into */
int counterMem = 0; /* Memory cell holding AUTOINCREMENT counter */
+ int iDb;
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */
@@ -236,8 +237,9 @@ void sqlite3Insert(
if( pTab==0 ){
goto insert_cleanup;
}
- assert( pTab->iDb<db->nDb );
- pDb = &db->aDb[pTab->iDb];
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( iDb<db->nDb );
+ pDb = &db->aDb[iDb];
zDb = pDb->zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
goto insert_cleanup;
@@ -285,7 +287,7 @@ void sqlite3Insert(
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto insert_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
- sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, pTab->iDb);
+ sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb);
/* if there are row triggers, allocate a temp table for new.* references. */
if( triggers_exist ){
@@ -303,8 +305,8 @@ void sqlite3Insert(
int base = sqlite3VdbeCurrentAddr(v);
counterRowid = pParse->nMem++;
counterMem = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
- sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pDb->pSeqTab->tnum);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pDb->pSchema->pSeqTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2);
sqlite3VdbeAddOp(v, OP_Rewind, iCur, base+13);
sqlite3VdbeAddOp(v, OP_Column, iCur, 0);
@@ -353,7 +355,7 @@ void sqlite3Insert(
** of the tables being read by the SELECT statement. Also use a
** temp table in the case of row triggers.
*/
- if( triggers_exist || selectReadsTable(pSelect, pTab->iDb, pTab->tnum) ){
+ if( triggers_exist || selectReadsTable(pSelect,pTab->pSchema,pTab->tnum) ){
useTempTable = 1;
}
@@ -684,8 +686,8 @@ void sqlite3Insert(
if( pTab->autoInc ){
int iCur = pParse->nTab;
int base = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
- sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pDb->pSeqTab->tnum);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pDb->pSchema->pSeqTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2);
sqlite3VdbeAddOp(v, OP_MemLoad, counterRowid, 0);
sqlite3VdbeAddOp(v, OP_NotNull, -1, base+7);
@@ -1104,15 +1106,17 @@ void sqlite3OpenTableAndIndices(
int op /* OP_OpenRead or OP_OpenWrite */
){
int i;
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
Index *pIdx;
Vdbe *v = sqlite3GetVdbe(pParse);
assert( v!=0 );
- sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
VdbeComment((v, "# %s", pTab->zName));
sqlite3VdbeAddOp(v, op, base, pTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, base, pTab->nCol);
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ assert( pIdx->pSchema==pTab->pSchema );
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
VdbeComment((v, "# %s", pIdx->zName));
sqlite3VdbeOp3(v, op, i+base, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO);
diff --git a/src/main.c b/src/main.c
index cb0350380..9858e6efc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.313 2005/12/30 16:28:02 danielk1977 Exp $
+** $Id: main.c,v 1.314 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -96,6 +96,53 @@ int sqlite3_total_changes(sqlite3 *db){
}
/*
+** Free a schema structure.
+*/
+void sqlite3SchemaFree(void *p){
+ sqliteFree(p);
+}
+
+DbSchema *sqlite3SchemaGet(Btree *pBt){
+ DbSchema * p;
+ if( pBt ){
+ p = (DbSchema *)sqlite3BtreeSchema(pBt,sizeof(DbSchema),sqlite3SchemaFree);
+ }else{
+ p = (DbSchema *)sqliteMalloc(sizeof(DbSchema));
+ }
+ if( p ){
+ sqlite3HashInit(&p->tblHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&p->idxHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&p->trigHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&p->aFKey, SQLITE_HASH_STRING, 1);
+ }
+ return p;
+}
+
+int sqlite3SchemaToIndex(sqlite3 *db, DbSchema *pSchema){
+ int i = -1000000;
+
+ /* If pSchema is NULL, then return -1000000. This happens when code in
+ ** expr.c is trying to resolve a reference to a transient table (i.e. one
+ ** created by a sub-select). In this case the return value of this
+ ** function should never be used.
+ **
+ ** We return -1000000 instead of the more usual -1 simply because using
+ ** -1000000 as incorrectly using -1000000 index into db->aDb[] is much
+ ** more likely to cause a segfault than -1 (of course there are assert()
+ ** statements too, but it never hurts to play the odds).
+ */
+ if( pSchema ){
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pSchema==pSchema ){
+ break;
+ }
+ }
+ assert( i>=0 &&i>=0 && i<db->nDb );
+ }
+ return i;
+}
+
+/*
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){
@@ -185,6 +232,7 @@ int sqlite3_close(sqlite3 *db){
#endif
db->magic = SQLITE_MAGIC_ERROR;
+ sqliteFree(db->aDb[1].pSchema);
sqliteFree(db);
sqlite3MallocAllow();
return SQLITE_OK;
@@ -639,7 +687,7 @@ int sqlite3BtreeFactory(
#endif /* SQLITE_OMIT_MEMORYDB */
}
- rc = sqlite3BtreeOpen(zFilename, db, ppBtree, btree_flags);
+ rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btree_flags);
if( rc==SQLITE_OK ){
sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler);
sqlite3BtreeSetCacheSize(*ppBtree, nCache);
@@ -732,7 +780,7 @@ static int openDatabase(
sqlite3 **ppDb /* OUT: Returned database handle */
){
sqlite3 *db;
- int rc, i;
+ int rc;
CollSeq *pColl;
assert( !sqlite3Tsd()->mallocFailed );
@@ -749,12 +797,15 @@ static int openDatabase(
db->flags |= SQLITE_ShortColNames;
sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
+
+#if 0
for(i=0; i<db->nDb; i++){
sqlite3HashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aDb[i].idxHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1);
}
+#endif
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
** and UTF-16, so add a version for each to avoid any unnecessary
@@ -789,6 +840,8 @@ static int openDatabase(
db->magic = SQLITE_MAGIC_CLOSED;
goto opendb_out;
}
+ db->aDb[0].pSchema = sqlite3SchemaGet(db->aDb[0].pBt);
+ db->aDb[1].pSchema = sqlite3SchemaGet(0);
/* The default safety_level for the main database is 'full'; for the temp
** database it is 'NONE'. This matches the pager layer defaults.
@@ -800,7 +853,6 @@ static int openDatabase(
db->aDb[1].safety_level = 1;
#endif
-
/* Register all built-in functions, but do not attempt to read the
** database schema yet. This is delayed until the first time the database
** is accessed.
diff --git a/src/pragma.c b/src/pragma.c
index dd1485a88..413ac8b23 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
-** $Id: pragma.c,v 1.107 2005/12/09 20:02:05 drh Exp $
+** $Id: pragma.c,v 1.108 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -157,6 +157,10 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
/* The following is VERY experimental */
{ "writable_schema", SQLITE_WriteSchema },
{ "omit_readlock", SQLITE_NoReadlock },
+
+ /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
+ ** flag if there are any active statements. */
+ { "read_uncommitted", SQLITE_ReadUncommitted },
};
int i;
const struct sPragmaType *p;
@@ -650,6 +654,7 @@ void sqlite3Pragma(
/* Do an integrity check on each database file */
for(i=0; i<db->nDb; i++){
HashElem *x;
+ Hash *pTbls;
int cnt = 0;
if( OMIT_TEMPDB && i==1 ) continue;
@@ -658,7 +663,8 @@ void sqlite3Pragma(
/* Do an integrity check of the B-Tree
*/
- for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
+ pTbls = &db->aDb[i].pSchema->tblHash;
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0);
@@ -685,7 +691,7 @@ void sqlite3Pragma(
/* Make sure all the indices are constructed correctly.
*/
sqlite3CodeVerifySchema(pParse, i);
- for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
int loopTop;
diff --git a/src/prepare.c b/src/prepare.c
index 1510b344e..6a75aa2ff 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -13,7 +13,7 @@
** interface, and routines that contribute to loading the database schema
** from disk.
**
-** $Id: prepare.c,v 1.12 2006/01/04 15:54:36 drh Exp $
+** $Id: prepare.c,v 1.13 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -49,6 +49,10 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
sqlite3 *db = pData->db;
int iDb;
+ if( sqlite3Tsd()->mallocFailed ){
+ return SQLITE_NOMEM;
+ }
+
assert( argc==4 );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[1]==0 || argv[3]==0 ){
@@ -151,6 +155,22 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
assert( iDb>=0 && iDb<db->nDb );
+ if( 0==db->aDb[iDb].pSchema ){
+ DbSchema *pS = sqlite3BtreeSchema(db->aDb[iDb].pBt, sizeof(DbSchema),
+ sqlite3SchemaFree);
+ db->aDb[iDb].pSchema = pS;
+ if( !pS ){
+ return SQLITE_NOMEM;
+ }else if( pS->file_format!=0 ){
+ return SQLITE_OK;
+ }else{
+ sqlite3HashInit(&pS->tblHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&pS->idxHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&pS->trigHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&pS->aFKey, SQLITE_HASH_STRING, 1);
+ }
+ }
+
/* zMasterSchema and zInitScript are set to point at the master schema
** and initialisation script appropriate for the database being
** initialised. zMasterName is the name of the master table.
@@ -226,7 +246,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}else{
memset(meta, 0, sizeof(meta));
}
- pDb->schema_cookie = meta[0];
+ pDb->pSchema->schema_cookie = meta[0];
/* If opening a non-empty database, check the text encoding. For the
** main database, set sqlite3.enc to the encoding of the main database.
@@ -260,11 +280,11 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
** file_format==3 Version 3.1.4. // ditto but with non-NULL defaults
** file_format==4 Version 3.3.0. // DESC indices. Boolean constants
*/
- pDb->file_format = meta[1];
- if( pDb->file_format==0 ){
- pDb->file_format = 1;
+ pDb->pSchema->file_format = meta[1];
+ if( pDb->pSchema->file_format==0 ){
+ pDb->pSchema->file_format = 1;
}
- if( pDb->file_format>SQLITE_MAX_FILE_FORMAT ){
+ if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
sqlite3BtreeCloseCursor(curMain);
sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
return SQLITE_ERROR;
@@ -394,7 +414,7 @@ static int schemaIsValid(sqlite3 *db){
rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
- if( rc==SQLITE_OK && cookie!=db->aDb[iDb].schema_cookie ){
+ if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
allOk = 0;
}
sqlite3BtreeCloseCursor(curTemp);
diff --git a/src/select.c b/src/select.c
index 298d0f839..20f07d4db 100644
--- a/src/select.c
+++ b/src/select.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.283 2006/01/03 15:16:26 drh Exp $
+** $Id: select.c,v 1.284 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -2202,6 +2202,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
struct ExprList_item eListItem;
SrcList *pSrc;
int brk;
+ int iDb;
/* Check to see if this query is a simple min() or max() query. Return
** zero if it is not.
@@ -2263,12 +2264,13 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
** or last entry in the main table.
*/
- sqlite3CodeVerifySchema(pParse, pTab->iDb);
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ sqlite3CodeVerifySchema(pParse, iDb);
base = pSrc->a[0].iCursor;
brk = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, brk);
if( pSrc->a[0].pSelect==0 ){
- sqlite3OpenTableForReading(v, base, pTab);
+ sqlite3OpenTableForReading(v, base, iDb, pTab);
}
if( pIdx==0 ){
sqlite3VdbeAddOp(v, seekOp, base, 0);
@@ -2281,7 +2283,8 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
*/
int iIdx;
iIdx = pParse->nTab++;
- sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ assert( pIdx->pSchema==pTab->pSchema );
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeOp3(v, OP_OpenRead, iIdx, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO);
if( seekOp==OP_Rewind ){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7c30a5eec..3cc6d6fdf 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.447 2006/01/04 15:54:36 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.448 2006/01/05 11:34:34 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -336,6 +336,7 @@ typedef struct AuthContext AuthContext;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
typedef struct Db Db;
+typedef struct DbSchema DbSchema;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
typedef struct FKey FKey;
@@ -367,29 +368,36 @@ typedef struct WhereLevel WhereLevel;
struct Db {
char *zName; /* Name of this database */
Btree *pBt; /* The B*Tree structure for this database file */
+ u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
+ u8 safety_level; /* How aggressive at synching data to disk */
+ int cache_size; /* Number of pages to use in the cache */
+ void *pAux; /* Auxiliary data. Usually NULL */
+ void (*xFreeAux)(void*); /* Routine to free pAux */
+ DbSchema *pSchema; /* Pointer to database schema (possibly shared) */
+};
+
+/*
+** An instance of the following structure stores a database schema.
+*/
+struct DbSchema {
int schema_cookie; /* Database schema version number for this file */
Hash tblHash; /* All tables indexed by name */
Hash idxHash; /* All (named) indices indexed by name */
Hash trigHash; /* All triggers indexed by name */
Hash aFKey; /* Foreign keys indexed by to-table */
- u16 flags; /* Flags associated with this database */
- u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
- u8 safety_level; /* How aggressive at synching data to disk */
- u8 file_format; /* Schema format version for this file */
- int cache_size; /* Number of pages to use in the cache */
Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */
- void *pAux; /* Auxiliary data. Usually NULL */
- void (*xFreeAux)(void*); /* Routine to free pAux */
+ u8 file_format; /* Schema format version for this file */
+ u16 flags; /* Flags associated with this schema */
};
/*
** These macros can be used to test, set, or clear bits in the
** Db.flags field.
*/
-#define DbHasProperty(D,I,P) (((D)->aDb[I].flags&(P))==(P))
-#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].flags&(P))!=0)
-#define DbSetProperty(D,I,P) (D)->aDb[I].flags|=(P)
-#define DbClearProperty(D,I,P) (D)->aDb[I].flags&=~(P)
+#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P))
+#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0)
+#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P)
+#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P)
/*
** Allowed values for the DB.flags field.
@@ -518,6 +526,7 @@ struct sqlite3 {
#define SQLITE_NoReadlock 0x00001000 /* Readlocks are omitted when
** accessing read-only databases */
#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */
+#define SQLITE_ReadUncommitted 0x00004000 /* For shared-cache mode */
/*
** Possible values for the sqlite.magic field.
@@ -672,7 +681,7 @@ struct Table {
int tnum; /* Root BTree node for this table (see note above) */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
u8 readOnly; /* True if this table should not be written by the user */
- u8 iDb; /* Index into sqlite.aDb[] of the backend for this table */
+// u8 iDb; /* Index into sqlite.aDb[] of the backend for this table */
u8 isTransient; /* True if automatically deleted when VDBE finishes */
u8 hasPrimKey; /* True if there exists a primary key */
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
@@ -687,6 +696,7 @@ struct Table {
#ifndef SQLITE_OMIT_ALTERTABLE
int addColOffset; /* Offset in CREATE TABLE statement to add a new column */
#endif
+ DbSchema *pSchema;
};
/*
@@ -822,9 +832,10 @@ struct Index {
int tnum; /* Page containing root of this index in database file */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
- u8 iDb; /* Index in sqlite.aDb[] of where this index is stored */
+ // u8 iDb; /* Index in sqlite.aDb[] of where this index is stored */
char *zColAff; /* String defining the affinity of each column */
Index *pNext; /* The next index associated with the same table */
+ DbSchema *pSchema;
KeyInfo keyInfo; /* Info on how to order keys. MUST BE LAST */
};
@@ -934,7 +945,7 @@ struct AggInfo {
struct Expr {
u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */
- u8 iDb; /* Database referenced by this expression */
+//u8 iDb; /* Database referenced by this expression */
u8 flags; /* Various flags. See below */
CollSeq *pColl; /* The collation type of the column or 0 */
Expr *pLeft, *pRight; /* Left and right subnodes */
@@ -950,6 +961,7 @@ struct Expr {
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
Table *pTab; /* Table for OP_Column expressions. */
+ DbSchema *pSchema;
};
/*
@@ -1277,7 +1289,7 @@ struct AuthContext {
struct Trigger {
char *name; /* The name of the trigger */
char *table; /* The table or view to which the trigger applies */
- u8 iDb; /* Database containing this trigger */
+//u8 iDb; /* Database containing this trigger */
u8 iTabDb; /* Database containing Trigger.table */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@@ -1286,7 +1298,7 @@ struct Trigger {
the <column-list> is stored here */
int foreach; /* One of TK_ROW or TK_STATEMENT */
Token nameToken; /* Token containing zName. Use during parsing only */
-
+ DbSchema *pSchema;
TriggerStep *step_list; /* Link list of trigger program steps */
Trigger *pNext; /* Next trigger associated with the table */
};
@@ -1527,7 +1539,7 @@ void sqlite3SelectDelete(Select*);
void sqlite3SelectUnbind(Select*);
Table *sqlite3SrcListLookup(Parse*, SrcList*);
int sqlite3IsReadOnly(Parse*, Table*, int);
-void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*);
+void sqlite3OpenTableForReading(Vdbe*, int iCur, int iDb, Table*);
void sqlite3OpenTable(Vdbe*, int iCur, Table*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
@@ -1700,6 +1712,9 @@ int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
SqliteTsd *sqlite3Tsd();
void sqlite3AttachFunctions(sqlite3 *);
void sqlite3MinimumFileFormat(Parse*, int, int);
+void sqlite3SchemaFree(void *);
+DbSchema *sqlite3SchemaGet(Btree *);
+int sqlite3SchemaToIndex(sqlite3 *db, DbSchema *);
void sqlite3MallocClearFailed();
#ifdef NDEBUG
diff --git a/src/trigger.c b/src/trigger.c
index afd88df55..cd68f4497 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -58,6 +58,7 @@ void sqlite3BeginTrigger(
int iDb; /* The database to store the trigger in */
Token *pName; /* The unqualified db name */
DbFixer sFix;
+ int iTabDb;
if( isTemp ){
/* If TEMP was specified, then the trigger name may not be qualified. */
@@ -82,7 +83,7 @@ void sqlite3BeginTrigger(
*/
if( !pTableName || sqlite3Tsd()->mallocFailed ) goto trigger_cleanup;
pTab = sqlite3SrcListLookup(pParse, pTableName);
- if( pName2->n==0 && pTab && pTab->iDb==1 ){
+ if( pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){
iDb = 1;
}
@@ -105,7 +106,7 @@ void sqlite3BeginTrigger(
if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto trigger_cleanup;
}
- if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
+ if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), zName,pName->n+1) ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
goto trigger_cleanup;
}
@@ -130,17 +131,18 @@ void sqlite3BeginTrigger(
" trigger on table: %S", pTableName, 0);
goto trigger_cleanup;
}
+ iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_CREATE_TRIGGER;
- const char *zDb = db->aDb[pTab->iDb].zName;
+ const char *zDb = db->aDb[iTabDb].zName;
const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
- if( pTab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
+ if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
goto trigger_cleanup;
}
- if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->iDb),0,zDb)){
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb),0,zDb)){
goto trigger_cleanup;
}
}
@@ -161,8 +163,8 @@ void sqlite3BeginTrigger(
pTrigger->name = zName;
zName = 0;
pTrigger->table = sqliteStrDup(pTableName->a[0].zName);
- pTrigger->iDb = iDb;
- pTrigger->iTabDb = pTab->iDb;
+ pTrigger->pSchema = db->aDb[iDb].pSchema;
+ pTrigger->iTabDb = iTabDb;
pTrigger->op = op;
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
pTrigger->pWhen = sqlite3ExprDup(pWhen);
@@ -196,16 +198,18 @@ void sqlite3FinishTrigger(
Trigger *pTrig = 0; /* The trigger whose construction is finishing up */
sqlite3 *db = pParse->db; /* The database */
DbFixer sFix;
+ int iDb; /* Database containing the trigger */
pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0;
if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
pTrig->step_list = pStepList;
while( pStepList ){
pStepList->pTrig = pTrig;
pStepList = pStepList->pNext;
}
- if( sqlite3FixInit(&sFix, pParse, pTrig->iDb, "trigger", &pTrig->nameToken)
+ if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &pTrig->nameToken)
&& sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
goto triggerfinish_cleanup;
}
@@ -232,22 +236,22 @@ void sqlite3FinishTrigger(
/* Make an entry in the sqlite_master table */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto triggerfinish_cleanup;
- sqlite3BeginWriteOperation(pParse, 0, pTrig->iDb);
- sqlite3OpenMasterTable(v, pTrig->iDb);
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ sqlite3OpenMasterTable(v, iDb);
addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
sqlite3VdbeChangeP3(v, addr+2, pTrig->name, 0);
sqlite3VdbeChangeP3(v, addr+3, pTrig->table, 0);
sqlite3VdbeChangeP3(v, addr+6, (char*)pAll->z, pAll->n);
- sqlite3ChangeCookie(db, v, pTrig->iDb);
+ sqlite3ChangeCookie(db, v, iDb);
sqlite3VdbeAddOp(v, OP_Close, 0, 0);
- sqlite3VdbeOp3(v, OP_ParseSchema, pTrig->iDb, 0,
+ sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
sqlite3MPrintf("type='trigger' AND name='%q'", pTrig->name), P3_DYNAMIC);
}
if( db->init.busy ){
Table *pTab;
Trigger *pDel;
- pDel = sqlite3HashInsert(&db->aDb[pTrig->iDb].trigHash,
+ pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash,
pTrig->name, strlen(pTrig->name)+1, pTrig);
if( pDel ){
assert( sqlite3Tsd()->mallocFailed && pDel==pTrig );
@@ -445,7 +449,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName){
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
- pTrigger = sqlite3HashFind(&(db->aDb[j].trigHash), zName, nName+1);
+ pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName+1);
if( pTrigger ) break;
}
if( !pTrigger ){
@@ -478,11 +482,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
sqlite3 *db = pParse->db;
int iDb;
- iDb = pTrigger->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
assert( iDb>=0 && iDb<db->nDb );
pTable = tableOfTrigger(db, pTrigger);
assert(pTable);
- assert( pTable->iDb==iDb || iDb==1 );
+ assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_TRIGGER;
@@ -528,7 +532,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
Trigger *pTrigger;
int nName = strlen(zName);
- pTrigger = sqlite3HashInsert(&(db->aDb[iDb].trigHash), zName, nName+1, 0);
+ pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash), zName, nName+1, 0);
if( pTrigger ){
Table *pTable = tableOfTrigger(db, pTrigger);
assert( pTable!=0 );
@@ -620,7 +624,7 @@ static SrcList *targetSrcList(
int iDb; /* Index of the database to use */
SrcList *pSrc; /* SrcList to be returned */
- iDb = pStep->pTrig->iDb;
+ iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema);
if( iDb==0 || iDb>=2 ){
assert( iDb<pParse->db->nDb );
sDb.z = (u8*)pParse->db->aDb[iDb].zName;
diff --git a/src/update.c b/src/update.c
index 5b5c2e954..fd6edc60f 100644
--- a/src/update.c
+++ b/src/update.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.114 2005/12/06 12:53:01 danielk1977 Exp $
+** $Id: update.c,v 1.115 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -89,6 +89,7 @@ void sqlite3Update(
int openAll = 0; /* True if all indices need to be opened */
AuthContext sContext; /* The authorization context */
NameContext sNC; /* The name-context to resolve expressions in */
+ int iDb; /* Database containing the table being updated */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* Trying to update a view */
@@ -107,6 +108,7 @@ void sqlite3Update(
*/
pTab = sqlite3SrcListLookup(pParse, pTabList);
if( pTab==0 ) goto update_cleanup;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
/* Figure out if we have any triggers and if the table being
** updated is a view
@@ -192,7 +194,7 @@ void sqlite3Update(
{
int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
- pTab->aCol[j].zName, db->aDb[pTab->iDb].zName);
+ pTab->aCol[j].zName, db->aDb[iDb].zName);
if( rc==SQLITE_DENY ){
goto update_cleanup;
}else if( rc==SQLITE_IGNORE ){
@@ -257,7 +259,7 @@ void sqlite3Update(
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
- sqlite3BeginWriteOperation(pParse, 1, pTab->iDb);
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
/* If we are trying to update a view, realize that view into
** a ephemeral table.
@@ -307,7 +309,7 @@ void sqlite3Update(
/* Open a cursor and make it point to the record that is
** being updated.
*/
- sqlite3OpenTableForReading(v, iCur, pTab);
+ sqlite3OpenTableForReading(v, iCur, iDb, pTab);
}
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
@@ -362,7 +364,7 @@ void sqlite3Update(
** action, then we need to open all indices because we might need
** to be deleting some records.
*/
- sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
if( onError==OE_Replace ){
@@ -378,7 +380,7 @@ void sqlite3Update(
}
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
if( openAll || aIdxUsed[i] ){
- sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO);
assert( pParse->nTab>iCur+i+1 );
diff --git a/src/vdbe.c b/src/vdbe.c
index 4dff6b21f..a42964d17 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.510 2006/01/03 15:16:26 drh Exp $
+** $Id: vdbe.c,v 1.511 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -2422,11 +2422,11 @@ case OP_SetCookie: { /* no-push */
rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->i);
if( pOp->p2==0 ){
/* When the schema cookie changes, record the new cookie internally */
- pDb->schema_cookie = pTos->i;
+ pDb->pSchema->schema_cookie = pTos->i;
db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==1 ){
/* Record changes in the file format */
- pDb->file_format = pTos->i;
+ pDb->pSchema->file_format = pTos->i;
}
assert( (pTos->flags & MEM_Dyn)==0 );
pTos--;
@@ -2530,8 +2530,8 @@ case OP_OpenWrite: { /* no-push */
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
wrFlag = 1;
- if( pDb->file_format < p->minWriteFileFormat ){
- p->minWriteFileFormat = pDb->file_format;
+ if( pDb->pSchema->file_format < p->minWriteFileFormat ){
+ p->minWriteFileFormat = pDb->pSchema->file_format;
}
}else{
wrFlag = 0;
diff --git a/src/where.c b/src/where.c
index e3dc7aca6..86d3ac3bc 100644
--- a/src/where.c
+++ b/src/where.c
@@ -16,7 +16,7 @@
** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer".
**
-** $Id: where.c,v 1.189 2005/12/21 18:36:46 drh Exp $
+** $Id: where.c,v 1.190 2006/01/05 11:34:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -1554,8 +1554,9 @@ WhereInfo *sqlite3WhereBegin(
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
pLevel = pWInfo->a;
for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
- Table *pTab;
- Index *pIx;
+ Table *pTab; /* Table to open */
+ Index *pIx; /* Index used to access pTab (if any) */
+ int iDb; /* Index of database containing table/index */
int iIdxCur = pLevel->iIdxCur;
#ifndef SQLITE_OMIT_EXPLAIN
@@ -1576,13 +1577,15 @@ WhereInfo *sqlite3WhereBegin(
#endif /* SQLITE_OMIT_EXPLAIN */
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
+ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
if( pTab->isTransient || pTab->pSelect ) continue;
if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
- sqlite3OpenTableForReading(v, pTabItem->iCursor, pTab);
+ sqlite3OpenTableForReading(v, pTabItem->iCursor, iDb, pTab);
}
pLevel->iTabCur = pTabItem->iCursor;
if( (pIx = pLevel->pIdx)!=0 ){
- sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
+ assert( pIx->pSchema==pTab->pSchema );
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
VdbeComment((v, "# %s", pIx->zName));
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum,
(char*)&pIx->keyInfo, P3_KEYINFO);
@@ -1590,7 +1593,7 @@ WhereInfo *sqlite3WhereBegin(
if( (pLevel->flags & WHERE_IDX_ONLY)!=0 ){
sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
}
- sqlite3CodeVerifySchema(pParse, pTab->iDb);
+ sqlite3CodeVerifySchema(pParse, iDb);
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);