diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alter.c | 32 | ||||
-rw-r--r-- | src/analyze.c | 17 | ||||
-rw-r--r-- | src/attach.c | 21 | ||||
-rw-r--r-- | src/auth.c | 10 | ||||
-rw-r--r-- | src/btree.c | 453 | ||||
-rw-r--r-- | src/btree.h | 3 | ||||
-rw-r--r-- | src/build.c | 188 | ||||
-rw-r--r-- | src/delete.c | 22 | ||||
-rw-r--r-- | src/expr.c | 11 | ||||
-rw-r--r-- | src/insert.c | 32 | ||||
-rw-r--r-- | src/main.c | 60 | ||||
-rw-r--r-- | src/pragma.c | 12 | ||||
-rw-r--r-- | src/prepare.c | 34 | ||||
-rw-r--r-- | src/select.c | 11 | ||||
-rw-r--r-- | src/sqliteInt.h | 51 | ||||
-rw-r--r-- | src/trigger.c | 40 | ||||
-rw-r--r-- | src/update.c | 14 | ||||
-rw-r--r-- | src/vdbe.c | 10 | ||||
-rw-r--r-- | src/where.c | 15 |
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); |