diff options
author | drh <drh@noemail.net> | 2003-03-19 03:14:00 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2003-03-19 03:14:00 +0000 |
commit | 001bbcbb8fa5a55e9950445c6287a1fc0496e83e (patch) | |
tree | 0c8a0383e24add990c29dda08c96ad1f8585ccba /src | |
parent | 9468c7f489259174d8ec41b9db3adbdd87527b27 (diff) | |
download | sqlite-001bbcbb8fa5a55e9950445c6287a1fc0496e83e.tar.gz sqlite-001bbcbb8fa5a55e9950445c6287a1fc0496e83e.zip |
Modifications to the VDBE to support more than one database file. (CVS 878)
FossilOrigin-Name: 875da9eed981bfa27b98e95025f9fdbed74b4098
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 101 | ||||
-rw-r--r-- | src/btree.h | 3 | ||||
-rw-r--r-- | src/build.c | 109 | ||||
-rw-r--r-- | src/delete.c | 19 | ||||
-rw-r--r-- | src/insert.c | 19 | ||||
-rw-r--r-- | src/main.c | 66 | ||||
-rw-r--r-- | src/os.c | 24 | ||||
-rw-r--r-- | src/os.h | 1 | ||||
-rw-r--r-- | src/pager.c | 21 | ||||
-rw-r--r-- | src/pager.h | 3 | ||||
-rw-r--r-- | src/printf.c | 1 | ||||
-rw-r--r-- | src/select.c | 13 | ||||
-rw-r--r-- | src/sqliteInt.h | 30 | ||||
-rw-r--r-- | src/update.c | 13 | ||||
-rw-r--r-- | src/vdbe.c | 230 | ||||
-rw-r--r-- | src/where.c | 14 |
16 files changed, 426 insertions, 241 deletions
diff --git a/src/btree.c b/src/btree.c index 9745f6d90..a8fd80e4d 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.83 2003/02/12 14:09:43 drh Exp $ +** $Id: btree.c,v 1.84 2003/03/19 03:14:01 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -2874,6 +2874,105 @@ int sqliteBtreeDropTable(Btree *pBt, int iTable){ return rc; } +#if 0 /* UNTESTED */ +/* +** Copy all cell data from one database file into another. +** pages back the freelist. +*/ +static int copyCell(Btree *pBtFrom, BTree *pBtTo, Cell *pCell){ + Pager *pFromPager = pBtFrom->pPager; + OverflowPage *pOvfl; + Pgno ovfl, nextOvfl; + Pgno *pPrev; + int rc = SQLITE_OK; + MemPage *pNew, *pPrevPg; + Pgno new; + + if( NKEY(pBtTo, pCell->h) + NDATA(pBtTo, pCell->h) <= MX_LOCAL_PAYLOAD ){ + return SQLITE_OK; + } + pPrev = &pCell->ovfl; + pPrevPg = 0; + ovfl = SWAB32(pBtTo, pCell->ovfl); + while( ovfl && rc==SQLITE_OK ){ + rc = sqlitepager_get(pFromPager, ovfl, (void**)&pOvfl); + if( rc ) return rc; + nextOvfl = SWAB32(pBtFrom, pOvfl->iNext); + rc = allocatePage(pBtTo, &pNew, &new, 0); + if( rc==SQLITE_OK ){ + rc = sqlitepager_write(pNew); + if( rc==SQLITE_OK ){ + memcpy(pNew, pOvfl, SQLITE_PAGE_SIZE); + *pPrev = SWAB32(pBtTo, new); + if( pPrevPg ){ + sqlitepager_unref(pPrevPg); + } + pPrev = &pOvfl->iNext; + pPrevPg = pNew; + } + } + sqlitepager_unref(pOvfl); + ovfl = nextOvfl; + } + if( pPrevPg ){ + sqlitepager_unref(pPrevPg); + } + return rc; +} +#endif + + +#if 0 /* UNTESTED */ +/* +** Copy a page of data from one database over to another. +*/ +static int copyDatabasePage( + Btree *pBtFrom, + Pgno pgnoFrom, + Btree *pBtTo, + Pgno *pTo +){ + MemPage *pPageFrom, *pPage; + Pgno to; + int rc; + Cell *pCell; + int idx; + + rc = sqlitepager_get(pBtFrom->pPager, pgno, (void**)&pPageFrom); + if( rc ) return rc; + rc = allocatePage(pBt, &pPage, pTo, 0); + if( rc==SQLITE_OK ){ + rc = sqlitepager_write(pPage); + } + if( rc==SQLITE_OK ){ + memcpy(pPage, pPageFrom, SQLITE_PAGE_SIZE); + idx = SWAB16(pBt, pPage->u.hdr.firstCell); + while( idx>0 ){ + pCell = (Cell*)&pPage->u.aDisk[idx]; + idx = SWAB16(pBt, pCell->h.iNext); + if( pCell->h.leftChild ){ + Pgno newChld; + rc = copyDatabasePage(pBtFrom, SWAB32(pBtFrom, pCell->h.leftChild), + pBtTo, &newChld); + if( rc ) return rc; + pCell->h.leftChild = SWAB32(pBtFrom, newChld); + } + rc = copyCell(pBtFrom, pBtTo, pCell); + if( rc ) return rc; + } + if( pPage->u.hdr.rightChild ){ + Pgno newChld; + rc = copyDatabasePage(pBtFrom, SWAB32(pBtFrom, pPage->u.hdr.rightChild), + pBtTo, &newChld); + if( rc ) return rc; + pPage->u.hdr.rightChild = SWAB32(pBtTo, newChild); + } + } + sqlitepager_unref(pPage); + return rc; +} +#endif + /* ** Read the meta-information out of a database file. */ diff --git a/src/btree.h b/src/btree.h index b92dce6e2..ef6f78127 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.27 2003/02/12 14:09:44 drh Exp $ +** @(#) $Id: btree.h,v 1.28 2003/03/19 03:14:01 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -37,6 +37,7 @@ int sqliteBtreeCreateTable(Btree*, int*); int sqliteBtreeCreateIndex(Btree*, int*); int sqliteBtreeDropTable(Btree*, int); int sqliteBtreeClearTable(Btree*, int); +int sqliteBtreeCopyTable(Btree *pFrom, int iFrom, Btree *pTo, int iTo); int sqliteBtreeCursor(Btree*, int iTable, int wrFlag, BtCursor **ppCur); int sqliteBtreeMoveto(BtCursor*, const void *pKey, int nKey, int *pRes); diff --git a/src/build.c b/src/build.c index 45b82b0b5..0c763d07a 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.131 2003/03/01 19:45:34 drh Exp $ +** $Id: build.c,v 1.132 2003/03/19 03:14:01 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -85,7 +85,7 @@ void sqliteExec(Parse *pParse){ if( pParse->useCallback ){ if( pParse->explain ){ rc = sqliteVdbeList(v); - db->next_cookie = db->schema_cookie; + db->next_cookie = db->aDb[0].schema_cookie; }else{ sqliteVdbeExec(v); } @@ -211,7 +211,7 @@ void sqliteRollbackInternalChanges(sqlite *db){ ** This routine is called when a commit occurs. */ void sqliteCommitInternalChanges(sqlite *db){ - db->schema_cookie = db->next_cookie; + db->aDb[0].schema_cookie = db->next_cookie; db->flags &= ~SQLITE_InternChanges; } @@ -310,13 +310,8 @@ char *sqliteTableNameFromToken(Token *pName){ ** on cursor 0. */ void sqliteOpenMasterTable(Vdbe *v, int isTemp){ - if( isTemp ){ - sqliteVdbeAddOp(v, OP_OpenWrAux, 0, 2); - sqliteVdbeChangeP3(v, -1, TEMP_MASTER_NAME, P3_STATIC); - }else{ - sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); - sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC); - } + sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); } /* @@ -383,8 +378,8 @@ void sqliteStartTable( /* Before trying to create a temporary table, make sure the Btree for ** holding temporary tables is open. */ - if( isTemp && db->pBeTemp==0 ){ - int rc = sqliteBtreeOpen(0, 0, MAX_PAGES, &db->pBeTemp); + if( isTemp && db->aDb[1].pBt==0 ){ + int rc = sqliteBtreeOpen(0, 0, MAX_PAGES, &db->aDb[1].pBt); if( rc!=SQLITE_OK ){ sqliteSetString(&pParse->zErrMsg, "unable to open a temporary database " "file for storing temporary tables", 0); @@ -392,7 +387,7 @@ void sqliteStartTable( return; } if( db->flags & SQLITE_InTrans ){ - rc = sqliteBtreeBeginTrans(db->pBeTemp); + rc = sqliteBtreeBeginTrans(db->aDb[1].pBt); if( rc!=SQLITE_OK ){ sqliteSetNString(&pParse->zErrMsg, "unable to get a write lock on " "the temporary database file", 0); @@ -713,8 +708,8 @@ void sqliteAddCollateType(Parse *pParse, int collType){ ** 1 chance in 2^32. So we're safe enough. */ void sqliteChangeCookie(sqlite *db, Vdbe *v){ - if( db->next_cookie==db->schema_cookie ){ - db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1; + if( db->next_cookie==db->aDb[0].schema_cookie ){ + db->next_cookie = db->aDb[0].schema_cookie + sqliteRandomByte() + 1; db->flags |= SQLITE_InternChanges; sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0); sqliteVdbeAddOp(v, OP_SetCookie, 0, 0); @@ -901,8 +896,8 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ } sqliteVdbeAddOp(v, OP_Close, 0, 0); if( pSelect ){ - int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, op, 1, 0); + sqliteVdbeAddOp(v, OP_Integer, p->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); pParse->nTab = 2; sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0); } @@ -1647,11 +1642,8 @@ void sqliteCreateIndex( pIndex->tnum = 0; if( pTable ){ sqliteVdbeAddOp(v, OP_Dup, 0, 0); - if( isTemp ){ - sqliteVdbeAddOp(v, OP_OpenWrAux, 1, 0); - }else{ - sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); - } + sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); } addr = sqliteVdbeAddOp(v, OP_String, 0, 0); if( pStart && pEnd ){ @@ -1661,7 +1653,8 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); if( pTable ){ - sqliteVdbeAddOp(v, isTemp ? OP_OpenAux : OP_Open, 2, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, 2, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); lbl2 = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, 2, lbl2); @@ -1940,16 +1933,16 @@ void sqliteCopy( } v = sqliteGetVdbe(pParse); if( v ){ - int openOp; sqliteBeginWriteOperation(pParse, 1, pTab->isTemp); addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); - openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, openOp, 0, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqliteVdbeAddOp(v, openOp, i, pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, i, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } if( db->flags & SQLITE_CountRows ){ @@ -2019,7 +2012,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ void sqliteBeginTransaction(Parse *pParse, int onError){ sqlite *db; - if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0) ) return; if( db->flags & SQLITE_InTrans ){ @@ -2039,7 +2032,7 @@ void sqliteBeginTransaction(Parse *pParse, int onError){ void sqliteCommitTransaction(Parse *pParse){ sqlite *db; - if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0) ) return; if( (db->flags & SQLITE_InTrans)==0 ){ @@ -2060,7 +2053,7 @@ void sqliteRollbackTransaction(Parse *pParse){ sqlite *db; Vdbe *v; - if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0) ) return; if( (db->flags & SQLITE_InTrans)==0 ){ @@ -2078,6 +2071,21 @@ void sqliteRollbackTransaction(Parse *pParse){ } /* +** Generate VDBE code that will verify the schema cookie for all +** named database files. +*/ +void sqliteCodeVerifySchema(Parse *pParse){ + int i; + sqlite *db = pParse->db; + Vdbe *v = sqliteGetVdbe(pParse); + for(i=0; i<db->nDb; i++){ + if( db->aDb[i].zName==0 || db->aDb[i].pBt==0 ) continue; + sqliteVdbeAddOp(v, OP_VerifyCookie, 0, db->aDb[i].schema_cookie); + } + pParse->schemaVerified = 1; +} + +/* ** Generate VDBE code that prepares for doing an operation that ** might change the database. ** @@ -2101,13 +2109,14 @@ void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){ if( v==0 ) return; if( pParse->trigStack ) return; /* if this is in a trigger */ if( (pParse->db->flags & SQLITE_InTrans)==0 ){ - sqliteVdbeAddOp(v, OP_Transaction, tempOnly, 0); + sqliteVdbeAddOp(v, OP_Transaction, 1, 0); if( !tempOnly ){ - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); - pParse->schemaVerified = 1; + sqliteVdbeAddOp(v, OP_Transaction, 0, 0); + sqliteCodeVerifySchema(pParse); } }else if( setCheckpoint ){ sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); + sqliteVdbeAddOp(v, OP_Checkpoint, 1, 0); } } @@ -2254,7 +2263,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ sqliteVdbeAddOp(v, OP_SetCookie, 0, 2); sqliteEndWriteOperation(pParse); db->cache_size = db->cache_size<0 ? -size : size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); } }else @@ -2287,7 +2296,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ if( size<0 ) size = -size; if( db->cache_size<0 ) size = -size; db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); } }else @@ -2349,8 +2358,8 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ sqliteVdbeAddOp(v, OP_SetCookie, 0, 3); sqliteEndWriteOperation(pParse); db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); - sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level); + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); + sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); } }else @@ -2377,8 +2386,8 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ db->safety_level = getSafetyLevel(zRight)+1; if( db->safety_level==1 ) size = -size; db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); - sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level); + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); + sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); } }else @@ -2535,21 +2544,23 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ if( sqliteStrICmp(zLeft, "integrity_check")==0 ){ static VdbeOp checkDb[] = { { OP_SetInsert, 0, 0, "2"}, - { OP_Open, 0, 2, 0}, - { OP_Rewind, 0, 6, 0}, - { OP_Column, 0, 3, 0}, /* 3 */ + { OP_Integer, 0, 0, 0}, + { OP_OpenRead, 0, 2, 0}, + { OP_Rewind, 0, 7, 0}, + { OP_Column, 0, 3, 0}, /* 4 */ { OP_SetInsert, 0, 0, 0}, - { OP_Next, 0, 3, 0}, - { OP_IntegrityCk, 0, 0, 0}, /* 6 */ + { OP_Next, 0, 4, 0}, + { OP_IntegrityCk, 0, 0, 0}, /* 7 */ { OP_ColumnName, 0, 0, "integrity_check"}, { OP_Callback, 1, 0, 0}, { OP_SetInsert, 1, 0, "2"}, - { OP_OpenAux, 1, 2, 0}, - { OP_Rewind, 1, 15, 0}, - { OP_Column, 1, 3, 0}, /* 12 */ + { OP_Integer, 1, 0, 0}, + { OP_OpenRead, 1, 2, 0}, + { OP_Rewind, 1, 17, 0}, + { OP_Column, 1, 3, 0}, /* 14 */ { OP_SetInsert, 1, 0, 0}, - { OP_Next, 1, 12, 0}, - { OP_IntegrityCk, 1, 1, 0}, /* 15 */ + { OP_Next, 1, 14, 0}, + { OP_IntegrityCk, 1, 1, 0}, /* 17 */ { OP_Callback, 1, 0, 0}, }; sqliteVdbeAddOpList(v, ArraySize(checkDb), checkDb); diff --git a/src/delete.c b/src/delete.c index 65e5075b4..62bcf9ca1 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 ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.45 2003/01/13 23:27:33 drh Exp $ +** $Id: delete.c,v 1.46 2003/03/19 03:14:01 drh Exp $ */ #include "sqliteInt.h" @@ -83,7 +83,6 @@ void sqliteDeleteFrom( Index *pIdx; /* For looping over indices of the table */ int base; /* Index of the first available table cursor */ sqlite *db; /* Main database structure */ - int openOp; /* Opcode used to open a cursor to the table */ int row_triggers_exist = 0; int oldIdx = -1; @@ -173,8 +172,8 @@ void sqliteDeleteFrom( ** entries in the table. */ int endOfLoop = sqliteVdbeMakeLabel(v); int addr; - openOp = pTab->isTemp ? OP_OpenAux : OP_Open; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2); addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0); sqliteVdbeAddOp(v, OP_Next, base, addr); @@ -220,9 +219,8 @@ void sqliteDeleteFrom( if( row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); sqliteVdbeAddOp(v, OP_Dup, 0, 0); - - openOp = pTab->isTemp ? OP_OpenAux : OP_Open; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0); @@ -251,10 +249,11 @@ void sqliteDeleteFrom( ** cursors are opened only once on the outside the loop. */ pParse->nTab = base + 1; - openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, openOp, pParse->nTab++, pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum); } /* This is the beginning of the delete loop when there are no diff --git a/src/insert.c b/src/insert.c index f90e2ab0d..5b96f48a1 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.72 2003/01/29 18:46:52 drh Exp $ +** $Id: insert.c,v 1.73 2003/03/19 03:14:01 drh Exp $ */ #include "sqliteInt.h" @@ -100,7 +100,6 @@ void sqliteInsert( int base; /* First available cursor */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ sqlite *db; /* The main database structure */ - int openOp; /* Opcode used to open cursors */ int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop; /* Label for the end of the insertion loop */ int useTempTable; /* Store SELECT results in intermediate table */ @@ -198,7 +197,7 @@ void sqliteInsert( ** should be written into a temporary table. Set to FALSE if each ** row of the SELECT can be written directly into the result table. */ - opCode = pTab->isTemp ? OP_OpenTemp : OP_Open; + opCode = pTab->isTemp ? OP_OpenTemp : OP_OpenRead; useTempTable = row_triggers_exist || sqliteVdbeFindOp(v,opCode,pTab->tnum); if( useTempTable ){ @@ -329,11 +328,12 @@ void sqliteInsert( /* Open tables and indices if there are no row triggers */ if( !row_triggers_exist ){ base = pParse->nTab; - openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } pParse->nTab += idx; @@ -390,11 +390,12 @@ void sqliteInsert( /* Open the tables and indices for the INSERT */ if( !pTab->pSelect ){ base = pParse->nTab; - openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } pParse->nTab += idx; diff --git a/src/main.c b/src/main.c index 8fa81d83c..cf1481418 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.114 2003/02/16 22:21:32 drh Exp $ +** $Id: main.c,v 1.115 2003/03/19 03:14:02 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -249,8 +249,8 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ /* Create a cursor to hold the database open */ - if( db->pBe==0 ) return SQLITE_OK; - rc = sqliteBtreeCursor(db->pBe, 2, 0, &curMain); + if( db->aDb[0].pBt==0 ) return SQLITE_OK; + rc = sqliteBtreeCursor(db->aDb[0].pBt, 2, 0, &curMain); if( rc ){ sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0); sqliteResetInternalSchema(db); @@ -259,23 +259,22 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ /* Get the database meta information */ - rc = sqliteBtreeGetMeta(db->pBe, meta); + rc = sqliteBtreeGetMeta(db->aDb[0].pBt, meta); if( rc ){ sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0); sqliteResetInternalSchema(db); sqliteBtreeCloseCursor(curMain); return rc; } - db->schema_cookie = meta[1]; - db->next_cookie = db->schema_cookie; + db->next_cookie = db->aDb[0].schema_cookie = meta[1]; db->file_format = meta[2]; size = meta[3]; if( size==0 ){ size = MAX_PAGES; } db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, size); + sqliteBtreeSetCacheSize(db->aDb[0].pBt, size); db->safety_level = meta[4]; if( db->safety_level==0 ) db->safety_level = 2; - sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level); + sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); /* ** file_format==1 Version 2.1.0. @@ -297,7 +296,6 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ */ memset(&sParse, 0, sizeof(sParse)); sParse.db = db; - sParse.pBe = db->pBe; sParse.xCallback = sqliteInitCallback; sParse.pArg = (void*)&initData; sParse.initFlag = 1; @@ -308,7 +306,7 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ if( sqlite_malloc_failed ){ sqliteSetString(pzErrMsg, "out of memory", 0); sParse.rc = SQLITE_NOMEM; - sqliteBtreeRollback(db->pBe); + sqliteBtreeRollback(db->aDb[0].pBt); sqliteResetInternalSchema(db); } if( sParse.rc==SQLITE_OK ){ @@ -363,9 +361,11 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ db->onError = OE_Default; db->priorNewRowid = 0; db->magic = SQLITE_MAGIC_BUSY; + db->nDb = 2; + db->aDb = db->aDbStatic; /* Open the backend database driver */ - rc = sqliteBtreeOpen(zFilename, 0, MAX_PAGES, &db->pBe); + rc = sqliteBtreeOpen(zFilename, 0, MAX_PAGES, &db->aDb[0].pBt); if( rc!=SQLITE_OK ){ switch( rc ){ default: { @@ -376,6 +376,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ sqliteStrRealloc(pzErrMsg); return 0; } + db->aDb[0].zName = "main"; /* Attempt to read the schema */ sqliteRegisterBuiltinFunctions(db); @@ -412,9 +413,9 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ &initData, &zErr); if( rc==SQLITE_OK ){ - sqliteBtreeGetMeta(db->pBe, meta); + sqliteBtreeGetMeta(db->aDb[0].pBt, meta); meta[2] = 4; - sqliteBtreeUpdateMeta(db->pBe, meta); + sqliteBtreeUpdateMeta(db->aDb[0].pBt, meta); sqlite_exec(db, "COMMIT", 0, 0, 0); } if( rc!=SQLITE_OK ){ @@ -457,17 +458,22 @@ int sqlite_changes(sqlite *db){ */ void sqlite_close(sqlite *db){ HashElem *i; + int j; db->want_to_close = 1; if( sqliteSafetyCheck(db) || sqliteSafetyOn(db) ){ /* printf("DID NOT CLOSE\n"); fflush(stdout); */ return; } db->magic = SQLITE_MAGIC_CLOSED; - sqliteBtreeClose(db->pBe); - sqliteResetInternalSchema(db); - if( db->pBeTemp ){ - sqliteBtreeClose(db->pBeTemp); + for(j=0; j<db->nDb; j++){ + if( db->aDb[j].pBt ){ + sqliteBtreeClose(db->aDb[j].pBt); + } } + if( db->aDb!=db->aDbStatic ){ + sqliteFree(db->aDb); + } + sqliteResetInternalSchema(db); for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){ FuncDef *pFunc, *pNext; for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){ @@ -593,6 +599,20 @@ int sqlite_complete(const char *zSql){ } /* +** Rollback all database files. +*/ +void sqliteRollbackAll(sqlite *db){ + int i; + for(i=0; i<db->nDb; i++){ + if( db->aDb[i].pBt ){ + sqliteBtreeRollback(db->aDb[i].pBt); + db->aDb[i].inTrans = 0; + } + } + sqliteRollbackInternalChanges(db); +} + +/* ** This routine does the work of either sqlite_exec() or sqlite_compile(). ** It works like sqlite_exec() if pVm==NULL and it works like sqlite_compile() ** otherwise. @@ -632,7 +652,6 @@ static int sqliteMain( if( db->pVdbe==0 ){ db->nChange = 0; } memset(&sParse, 0, sizeof(sParse)); sParse.db = db; - sParse.pBe = db->pBe; sParse.xCallback = xCallback; sParse.pArg = pArg; sParse.useCallback = ppVm==0; @@ -643,10 +662,9 @@ static int sqliteMain( if( sqlite_malloc_failed ){ sqliteSetString(pzErrMsg, "out of memory", 0); sParse.rc = SQLITE_NOMEM; - sqliteBtreeRollback(db->pBe); - if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); - db->flags &= ~SQLITE_InTrans; + sqliteRollbackAll(db); sqliteResetInternalSchema(db); + db->flags &= ~SQLITE_InTrans; } if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK; if( sParse.rc!=SQLITE_OK && pzErrMsg && *pzErrMsg==0 ){ @@ -965,10 +983,10 @@ int sqlite_open_aux_file(sqlite *db, const char *zName, char **pzErrMsg){ if( zName && zName[0]==0 ) zName = 0; if( sqliteSafetyOn(db) ) goto openaux_misuse; sqliteResetInternalSchema(db); - if( db->pBeTemp!=0 ){ - sqliteBtreeClose(db->pBeTemp); + if( db->aDb[1].pBt!=0 ){ + sqliteBtreeClose(db->aDb[1].pBt); } - rc = sqliteBtreeOpen(zName, 0, MAX_PAGES, &db->pBeTemp); + rc = sqliteBtreeOpen(zName, 0, MAX_PAGES, &db->aDb[1].pBt); if( rc ){ if( zName==0 ) zName = "a temporary file"; sqliteSetString(pzErrMsg, "unable to open ", zName, @@ -269,6 +269,30 @@ int sqliteOsFileExists(const char *zFilename){ /* +** Change the name of an existing file. +*/ +int sqliteOsRename(const char *zOldName, const char *zNewName){ +#if OS_UNIX + if( link(zOldName, zNewName) ){ + return SQLITE_ERROR; + } + unlink(zOldName); + return SQLITE_OK; +#endif +#if OS_WIN + if( !MoveFile(zOldName, zNewName) ){ + return SQLITE_ERROR; + } + return SQLITE_OK; +#endif +#if OS_MAC + /**** FIX ME ***/ + return SQLITE_ERROR; +#endif +} + + +/* ** Attempt to open a file for both reading and writing. If that ** fails, try opening it read-only. If the file does not exist, ** try to create it. @@ -147,6 +147,7 @@ int sqliteOsDelete(const char*); int sqliteOsFileExists(const char*); +int sqliteOsFileRename(const char*, const char*); int sqliteOsOpenReadWrite(const char*, OsFile*, int*); int sqliteOsOpenExclusive(const char*, OsFile*, int); int sqliteOsOpenReadOnly(const char*, OsFile*); diff --git a/src/pager.c b/src/pager.c index bd191d6e1..58eb8cee4 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.78 2003/02/16 19:13:37 drh Exp $ +** @(#) $Id: pager.c,v 1.79 2003/03/19 03:14:02 drh Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" @@ -1723,6 +1723,25 @@ int sqlitepager_iswriteable(void *pData){ } /* +** Replace the content of a single page with the information in the third +** argument. +*/ +int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){ + void *pPage; + int rc; + + rc = sqlitepager_get(pPager, pgno, &pPage); + if( rc==SQLITE_OK ){ + rc = sqlitepager_write(pPage); + if( rc==SQLITE_OK ){ + memcpy(pPage, pData, SQLITE_PAGE_SIZE); + } + sqlitepager_unref(pPage); + } + return rc; +} + +/* ** A call to this routine tells the pager that it is not necessary to ** write the information on page "pgno" back to the disk, even though ** that page might be marked as dirty. diff --git a/src/pager.h b/src/pager.h index 4735b82d6..bba8220ac 100644 --- a/src/pager.h +++ b/src/pager.h @@ -13,7 +13,7 @@ ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** -** @(#) $Id: pager.h,v 1.20 2003/02/12 14:09:44 drh Exp $ +** @(#) $Id: pager.h,v 1.21 2003/03/19 03:14:02 drh Exp $ */ /* @@ -59,6 +59,7 @@ int sqlitepager_unref(void*); Pgno sqlitepager_pagenumber(void*); int sqlitepager_write(void*); int sqlitepager_iswriteable(void*); +int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*); int sqlitepager_pagecount(Pager*); int sqlitepager_begin(void*); int sqlitepager_commit(Pager*); diff --git a/src/printf.c b/src/printf.c index 387ea1c2d..8587f80d2 100644 --- a/src/printf.c +++ b/src/printf.c @@ -687,7 +687,6 @@ static void mout(void *arg, char *zNewText, int nNewChar){ char *sqliteMPrintf(const char *zFormat, ...){ va_list ap; struct sgMprintf sMprintf; - char *zNew; char zBuf[200]; sMprintf.nChar = 0; diff --git a/src/select.c b/src/select.c index 4b462486d..eb2892f24 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.126 2003/02/02 12:41:26 drh Exp $ +** $Id: select.c,v 1.127 2003/03/19 03:14:02 drh Exp $ */ #include "sqliteInt.h" @@ -1768,7 +1768,6 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ Index *pIdx; int base; Vdbe *v; - int openOp; int seekOp; int cont; ExprList eList; @@ -1828,17 +1827,17 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ ** or last entry in the main table. */ if( !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){ - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); - pParse->schemaVerified = 1; + sqliteCodeVerifySchema(pParse); } - openOp = pTab->isTemp ? OP_OpenAux : OP_Open; base = p->base; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); if( pIdx==0 ){ sqliteVdbeAddOp(v, seekOp, base, 0); }else{ - sqliteVdbeAddOp(v, openOp, base+1, pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, base+1, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); sqliteVdbeAddOp(v, seekOp, base+1, 0); sqliteVdbeAddOp(v, OP_IdxRecno, base+1, 0); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index e8f168204..63610ae8e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.162 2003/02/16 22:21:32 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.163 2003/03/19 03:14:02 drh Exp $ */ #include "config.h" #include "sqlite.h" @@ -187,6 +187,21 @@ typedef struct Trigger Trigger; typedef struct TriggerStep TriggerStep; typedef struct TriggerStack TriggerStack; typedef struct FKey FKey; +typedef struct Db Db; + +/* +** Each database file to be accessed by the system is an instance +** of the following structure. There are normally two of these structures +** in the sqlite.aDb[] array. aDb[0] is the main database file and +** aDb[1] is the database file used to hold temporary tables. But +** additional databases may be attached to the engine. +*/ +struct Db { + char *zName; /* Name of this database */ + Btree *pBt; /* The B*Tree structure for this database file */ + int schema_cookie; /* Database schema version number for this file */ + u8 inTrans; /* True if a transaction is underway for this backend */ +}; /* ** Each database is an instance of the following structure. @@ -204,14 +219,14 @@ typedef struct FKey FKey; ** text datatypes. */ struct sqlite { - Btree *pBe; /* The B*Tree backend */ - Btree *pBeTemp; /* Backend for session temporary tables */ + int nDb; /* Number of backends currently in use */ + Db *aDb; /* All backends */ + Db aDbStatic[2]; /* Static space for the 2 default backends */ int flags; /* Miscellanous flags. See below */ u8 file_format; /* What file format version is this database? */ u8 safety_level; /* How aggressive at synching data to disk */ u8 want_to_close; /* Close after all VDBEs are deallocated */ - int schema_cookie; /* Magic number that changes with the schema */ - int next_cookie; /* Value of schema_cookie after commit */ + int next_cookie; /* Next value of aDb[0].schema_cookie */ int cache_size; /* Number of pages to use in the cache */ int nTable; /* Number of tables in the database */ void *pBusyArg; /* 1st Argument to the busy callback */ @@ -348,7 +363,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 isTemp; /* True if stored in db->pBeTemp instead of db->pBe */ + u8 isTemp; /* 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 */ @@ -731,7 +746,6 @@ struct AggExpr { */ struct Parse { sqlite *db; /* The main database structure */ - Btree *pBe; /* The database backend */ int rc; /* Return code from execution */ sqlite_callback xCallback; /* The callback function */ void *pArg; /* First argument to the callback function */ @@ -1003,6 +1017,8 @@ int sqliteExprAnalyzeAggregates(Parse*, Expr*); Vdbe *sqliteGetVdbe(Parse*); int sqliteRandomByte(void); int sqliteRandomInteger(void); +void sqliteRollbackAll(sqlite*); +void sqliteCodeVerifySchema(Parse*); void sqliteBeginTransaction(Parse*, int); void sqliteCommitTransaction(Parse*); void sqliteRollbackTransaction(Parse*); diff --git a/src/update.c b/src/update.c index fccd86343..c914e61ab 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.53 2003/01/13 23:27:33 drh Exp $ +** $Id: update.c,v 1.54 2003/03/19 03:14:02 drh Exp $ */ #include "sqliteInt.h" @@ -42,7 +42,6 @@ void sqliteUpdate( int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ - int openOp; /* Opcode used to open tables */ int chngRecno; /* True if the record number is being changed */ Expr *pRecnoExpr; /* Expression defining the new record number */ int openAll; /* True if all indices need to be opened */ @@ -232,7 +231,8 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0); - sqliteVdbeAddOp(v, (pTab->isTemp?OP_OpenAux:OP_Open), base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteVdbeAddOp(v, OP_Integer, 13, 0); @@ -277,8 +277,8 @@ void sqliteUpdate( ** action, then we need to open all indices because we might need ** to be deleting some records. */ - openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); if( onError==OE_Replace ){ openAll = 1; }else{ @@ -292,7 +292,8 @@ void sqliteUpdate( } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( openAll || aIdxUsed[i] ){ - sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum); assert( pParse->nTab>base+i+1 ); } } diff --git a/src/vdbe.c b/src/vdbe.c index e7b8732f9..85baf6890 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -36,7 +36,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.207 2003/03/07 19:50:07 drh Exp $ +** $Id: vdbe.c,v 1.208 2003/03/19 03:14:02 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -243,7 +243,6 @@ struct Keylist { struct Vdbe { sqlite *db; /* The whole database */ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ - Btree *pBt; /* Opaque context structure used by DB backend */ FILE *trace; /* Write an execution trace here, if not NULL */ int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Number of slots allocated for aOp[] */ @@ -315,7 +314,6 @@ Vdbe *sqliteVdbeCreate(sqlite *db){ Vdbe *p; p = sqliteMalloc( sizeof(Vdbe) ); if( p==0 ) return 0; - p->pBt = db->pBe; p->db = db; if( db->pVdbe ){ db->pVdbe->pPrev = p; @@ -1212,6 +1210,7 @@ static void Cleanup(Vdbe *p){ */ void sqliteVdbeDelete(Vdbe *p){ int i; + sqlite *db = p->db; if( p==0 ) return; Cleanup(p); if( p->pPrev ){ @@ -1233,6 +1232,13 @@ void sqliteVdbeDelete(Vdbe *p){ sqliteFree(p->aOp[i].p3); } } + for(i=2; i<db->nDb; i++){ + if( db->aDb[i].pBt && db->aDb[i].zName==0 ){ + sqliteBtreeClose(db->aDb[i].pBt); + db->aDb[i].pBt = 0; + db->aDb[i].inTrans = 0; + } + } sqliteFree(p->aOp); sqliteFree(p->aLabel); sqliteFree(p->aStack); @@ -1584,7 +1590,6 @@ int sqliteVdbeExec( int pc; /* The program counter */ Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ - Btree *pBt = p->pBt; /* The backend driver */ sqlite *db = p->db; /* The database */ char **zStack = p->zStack; /* Text stack */ Stack *aStack = p->aStack; /* Additional stack information */ @@ -1894,6 +1899,7 @@ case OP_Push: { ** to all column names is passed as the 4th parameter to the callback. */ case OP_ColumnName: { + assert( pOp->p1>=0 && pOp->p1<p->nOp ); p->azColName[pOp->p1] = pOp->p3; p->nCallback = 0; break; @@ -3154,17 +3160,21 @@ case OP_IncrKey: { break; } -/* Opcode: Checkpoint * * * +/* Opcode: Checkpoint P1 * * ** ** Begin a checkpoint. A checkpoint is the beginning of a operation that ** is part of a larger transaction but which might need to be rolled back ** itself without effecting the containing transaction. A checkpoint will ** be automatically committed or rollback when the VDBE halts. +** +** The checkpoint is begun on the database file with index P1. The main +** database file has an index of 0 and the file used for temporary tables +** has an index of 1. */ case OP_Checkpoint: { - rc = sqliteBtreeBeginCkpt(pBt); - if( rc==SQLITE_OK && db->pBeTemp ){ - rc = sqliteBtreeBeginCkpt(db->pBeTemp); + int i = pOp->p1; + if( i>=0 && i<db->nDb && db->aDb[i].pBt ){ + rc = sqliteBtreeBeginCkpt(db->aDb[i].pBt); } break; } @@ -3175,10 +3185,9 @@ case OP_Checkpoint: { ** opcode is encountered. Depending on the ON CONFLICT setting, the ** transaction might also be rolled back if an error is encountered. ** -** If P1 is true, then the transaction is started on the temporary -** tables of the database only. The main database file is not write -** locked and other processes can continue to read the main database -** file. +** P1 is the index of the database file on which the transaction is +** started. Index 0 is the main database file and index 1 is the +** file used for temporary tables. ** ** A write lock is obtained on the database file when a transaction is ** started. No other process can read or write the file while the @@ -3188,15 +3197,9 @@ case OP_Checkpoint: { */ case OP_Transaction: { int busy = 1; - if( db->pBeTemp && !p->inTempTrans ){ - rc = sqliteBtreeBeginTrans(db->pBeTemp); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - p->inTempTrans = 1; - } - while( pOp->p1==0 && busy ){ - rc = sqliteBtreeBeginTrans(pBt); + int i = pOp->p1; + while( i>=0 && i<db->nDb && db->aDb[i].pBt!=0 && busy ){ + rc = sqliteBtreeBeginTrans(db->aDb[i].pBt); switch( rc ){ case SQLITE_BUSY: { if( db->xBusyCallback==0 ){ @@ -3224,6 +3227,7 @@ case OP_Transaction: { } } } + db->aDb[i].inTrans = 1; p->undoTransOnError = 1; break; } @@ -3237,53 +3241,48 @@ case OP_Transaction: { ** A read lock continues to be held if there are still cursors open. */ case OP_Commit: { - if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){ - rc = p->inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt); + int i; + assert( rc==SQLITE_OK ); + for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ + if( db->aDb[i].inTrans ){ + rc = sqliteBtreeCommit(db->aDb[i].pBt); + db->aDb[i].inTrans = 0; + } } if( rc==SQLITE_OK ){ sqliteCommitInternalChanges(db); }else{ - if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); - sqliteBtreeRollback(pBt); - sqliteRollbackInternalChanges(db); + sqliteRollbackAll(db); } - p->inTempTrans = 0; break; } -/* Opcode: Rollback * * * +/* Opcode: Rollback P1 * * ** ** Cause all modifications to the database that have been made since the ** last Transaction to be undone. The database is restored to its state ** before the Transaction opcode was executed. No additional modifications ** are allowed until another transaction is started. ** +** P1 is the index of the database file that is committed. An index of 0 +** is used for the main database and an index of 1 is used for the file used +** to hold temporary tables. +** ** This instruction automatically closes all cursors and releases both -** the read and write locks on the database. +** the read and write locks on the indicated database. */ case OP_Rollback: { - if( db->pBeTemp ){ - sqliteBtreeRollback(db->pBeTemp); - } - rc = sqliteBtreeRollback(pBt); - sqliteRollbackInternalChanges(db); + sqliteRollbackAll(db); break; } -/* Opcode: ReadCookie * P2 * -** -** When P2==0, -** read the schema cookie from the database file and push it onto the -** stack. The schema cookie is an integer that is used like a version -** number for the database schema. Everytime the schema changes, the -** cookie changes to a new random value. This opcode is used during -** initialization to read the initial cookie value so that subsequent -** database accesses can verify that the cookie has not changed. +/* Opcode: ReadCookie P1 P2 * ** -** If P2>0, then read global database parameter number P2. There is -** a small fixed number of global database parameters. P2==1 is the -** database version number. P2==2 is the recommended pager cache size. -** Other parameters are currently unused. +** Read cookie number P2 from database P1 and push it onto the stack. +** P2==0 is the schema version. P2==1 is the database format. +** P2==2 is the recommended pager cache size, and so forth. P1==0 is +** the main database file and P1==1 is the database file used to store +** temporary tables. ** ** There must be a read-lock on the database (either a transaction ** must be started or there must be an open cursor) before @@ -3293,36 +3292,35 @@ case OP_ReadCookie: { int i = ++p->tos; int aMeta[SQLITE_N_BTREE_META]; assert( pOp->p2<SQLITE_N_BTREE_META ); - rc = sqliteBtreeGetMeta(pBt, aMeta); + assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( db->aDb[pOp->p1].pBt!=0 ); + rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); aStack[i].i = aMeta[1+pOp->p2]; aStack[i].flags = STK_Int; break; } -/* Opcode: SetCookie * P2 * +/* Opcode: SetCookie P1 P2 * ** -** When P2==0, -** this operation changes the value of the schema cookie on the database. -** The new value is top of the stack. -** When P2>0, the value of global database parameter -** number P2 is changed. See ReadCookie for more information about -** global database parametes. -** -** The schema cookie changes its value whenever the database schema changes. -** That way, other processes can recognize when the schema has changed -** and reread it. +** Write the top of the stack into cookie number P2 of database P1. +** P2==0 is the schema version. P2==1 is the database format. +** P2==2 is the recommended pager cache size, and so forth. P1==0 is +** the main database file and P1==1 is the database file used to store +** temporary tables. ** ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { int aMeta[SQLITE_N_BTREE_META]; assert( pOp->p2<SQLITE_N_BTREE_META ); + assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( db->aDb[pOp->p1].pBt!=0 ); VERIFY( if( p->tos<0 ) goto not_enough_stack; ) Integerify(p, p->tos) - rc = sqliteBtreeGetMeta(pBt, aMeta); + rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); if( rc==SQLITE_OK ){ aMeta[1+pOp->p2] = aStack[p->tos].i; - rc = sqliteBtreeUpdateMeta(pBt, aMeta); + rc = sqliteBtreeUpdateMeta(db->aDb[pOp->p1].pBt, aMeta); } POPSTACK; break; @@ -3330,10 +3328,11 @@ case OP_SetCookie: { /* Opcode: VerifyCookie P1 P2 * ** -** Check the value of global database parameter number P2 and make -** sure it is equal to P1. P2==0 is the schema cookie. P1==1 is -** the database version. If the values do not match, abort with -** an SQLITE_SCHEMA error. +** Check the value of global database parameter number 0 (the +** schema version) and make sure it is equal to P2. +** P1 is the database number which is 0 for the main database file +** and 1 for the file holding temporary tables and some higher number +** for auxiliary databases. ** ** The cookie changes its value whenever the database schema changes. ** This operation is used to detect when that the cookie has changed @@ -3345,23 +3344,27 @@ case OP_SetCookie: { */ case OP_VerifyCookie: { int aMeta[SQLITE_N_BTREE_META]; - assert( pOp->p2<SQLITE_N_BTREE_META ); - rc = sqliteBtreeGetMeta(pBt, aMeta); - if( rc==SQLITE_OK && aMeta[1+pOp->p2]!=pOp->p1 ){ + assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( db->aDb[pOp->p1].zName!=0 ); + rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); + if( rc==SQLITE_OK && aMeta[1]!=pOp->p2 ){ sqliteSetString(&p->zErrMsg, "database schema has changed", 0); rc = SQLITE_SCHEMA; } break; } -/* Opcode: Open P1 P2 P3 +/* Opcode: OpenRead P1 P2 P3 ** ** Open a read-only cursor for the database table whose root page is -** P2 in the main database file. Give the new cursor an identifier -** of P1. The P1 values need not be contiguous but all P1 values -** should be small integers. It is an error for P1 to be negative. +** P2 in a database file. The database file is determined by an +** integer from the top of the stack. 0 means the main database and +** 1 means the database used for temporary tables. Give the new +** cursor an identifier of P1. The P1 values need not be contiguous +** but all P1 values should be small integers. It is an error for +** P1 to be negative. ** -** If P2==0 then take the root page number from the top of the stack. +** If P2==0 then take the root page number from the next of the stack. ** ** There will be a read lock on the database whenever there is an ** open cursor. If the database was unlocked prior to this instruction @@ -3377,52 +3380,39 @@ case OP_VerifyCookie: { ** omitted. But the code generator usually inserts the index or ** table name into P3 to make the code easier to read. ** -** See also OpenAux and OpenWrite. -*/ -/* Opcode: OpenAux P1 P2 P3 -** -** Open a read-only cursor in the auxiliary table set. This opcode -** works exactly like OP_Open except that it opens the cursor on the -** auxiliary table set (the file used to store tables created using -** CREATE TEMPORARY TABLE) instead of in the main database file. -** See OP_Open for additional information. +** See also OpenWrite. */ /* Opcode: OpenWrite P1 P2 P3 ** ** Open a read/write cursor named P1 on the table or index whose root ** page is P2. If P2==0 then take the root page number from the stack. ** -** This instruction works just like Open except that it opens the cursor +** This instruction works just like OpenRead except that it opens the cursor ** in read/write mode. For a given table, there can be one or more read-only ** cursors or a single read/write cursor but not both. ** -** See also OpWrAux. +** See also OpenRead. */ -/* Opcode: OpenWrAux P1 P2 P3 -** -** Open a read/write cursor in the auxiliary table set. This opcode works -** just like OpenWrite except that the auxiliary table set (the file used -** to store tables created using CREATE TEMPORARY TABLE) is used in place -** of the main database file. -*/ -case OP_OpenAux: -case OP_OpenWrAux: -case OP_OpenWrite: -case OP_Open: { +case OP_OpenRead: +case OP_OpenWrite: { int busy = 0; int i = pOp->p1; int tos = p->tos; int p2 = pOp->p2; int wrFlag; Btree *pX; - switch( pOp->opcode ){ - case OP_Open: wrFlag = 0; pX = pBt; break; - case OP_OpenWrite: wrFlag = 1; pX = pBt; break; - case OP_OpenAux: wrFlag = 0; pX = db->pBeTemp; break; - case OP_OpenWrAux: wrFlag = 1; pX = db->pBeTemp; break; - } + int iDb; + + VERIFY( if( tos<0 ) goto not_enough_stack; ); + Integerify(p, tos); + iDb = p->aStack[tos].i; + tos--; + VERIFY( if( iDb<0 || iDb>=db->nDb ) goto bad_instruction; ); + VERIFY( if( db->aDb[iDb].pBt==0 ) goto bad_instruction; ); + pX = db->aDb[iDb].pBt; + wrFlag = pOp->opcode==OP_OpenWrite; if( p2<=0 ){ - if( tos<0 ) goto not_enough_stack; + VERIFY( if( tos<0 ) goto not_enough_stack; ); Integerify(p, tos); p2 = p->aStack[tos].i; POPSTACK; @@ -3464,6 +3454,7 @@ case OP_Open: { if( p2<=0 ){ POPSTACK; } + POPSTACK; break; } @@ -4448,7 +4439,7 @@ case OP_IdxGE: { ** See also: Clear */ case OP_Destroy: { - sqliteBtreeDropTable(pOp->p2 ? db->pBeTemp : pBt, pOp->p1); + sqliteBtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1); break; } @@ -4465,7 +4456,7 @@ case OP_Destroy: { ** See also: Destroy */ case OP_Clear: { - sqliteBtreeClearTable(pOp->p2 ? db->pBeTemp : pBt, pOp->p1); + sqliteBtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1); break; } @@ -4499,10 +4490,12 @@ case OP_CreateTable: { int i = ++p->tos; int pgno; assert( pOp->p3!=0 && pOp->p3type==P3_POINTER ); + assert( pOp->p2>=0 && pOp->p2<db->nDb ); + assert( db->aDb[pOp->p2].pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ - rc = sqliteBtreeCreateTable(pOp->p2 ? db->pBeTemp : pBt, &pgno); + rc = sqliteBtreeCreateTable(db->aDb[pOp->p2].pBt, &pgno); }else{ - rc = sqliteBtreeCreateIndex(pOp->p2 ? db->pBeTemp : pBt, &pgno); + rc = sqliteBtreeCreateIndex(db->aDb[pOp->p2].pBt, &pgno); } if( rc==SQLITE_OK ){ aStack[i].i = pgno; @@ -4546,7 +4539,7 @@ case OP_IntegrityCk: { toInt((char*)sqliteHashKey(i), &aRoot[j]); } aRoot[j] = 0; - z = sqliteBtreeIntegrityCheck(pOp->p2 ? db->pBeTemp : pBt, aRoot, nRoot); + z = sqliteBtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot); if( z==0 || z[0]==0 ){ if( z ) sqliteFree(z); zStack[tos] = "ok"; @@ -5671,8 +5664,7 @@ bad_instruction: */ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){ sqlite *db = p->db; - Btree *pBt = p->pBt; - int rc; + int i, rc; if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){ sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), 0); @@ -5691,23 +5683,24 @@ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){ switch( p->errorAction ){ case OE_Abort: { if( !p->undoTransOnError ){ - sqliteBtreeRollbackCkpt(pBt); - if( db->pBeTemp ) sqliteBtreeRollbackCkpt(db->pBeTemp); + for(i=0; i<db->nDb; i++){ + if( db->aDb[i].pBt ){ + sqliteBtreeRollbackCkpt(db->aDb[i].pBt); + } + } break; } /* Fall through to ROLLBACK */ } case OE_Rollback: { - sqliteBtreeRollback(pBt); - if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); + sqliteRollbackAll(db); db->flags &= ~SQLITE_InTrans; db->onError = OE_Default; break; } default: { if( p->undoTransOnError ){ - sqliteBtreeCommit(pBt); - if( db->pBeTemp ) sqliteBtreeCommit(db->pBeTemp); + sqliteRollbackAll(db); db->flags &= ~SQLITE_InTrans; db->onError = OE_Default; } @@ -5716,8 +5709,11 @@ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){ } sqliteRollbackInternalChanges(db); } - sqliteBtreeCommitCkpt(pBt); - if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp); + for(i=0; i<db->nDb; i++){ + if( db->aDb[i].pBt ){ + sqliteBtreeCommitCkpt(db->aDb[i].pBt); + } + } assert( p->tos<p->pc || sqlite_malloc_failed==1 ); #ifdef VDBE_PROFILE { diff --git a/src/where.c b/src/where.c index 7fa6f4171..3f7f306a0 100644 --- a/src/where.c +++ b/src/where.c @@ -13,7 +13,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.72 2003/01/31 17:21:50 drh Exp $ +** $Id: where.c,v 1.73 2003/03/19 03:14:03 drh Exp $ */ #include "sqliteInt.h" @@ -636,21 +636,21 @@ WhereInfo *sqliteWhereBegin( /* Open all tables in the pTabList and all indices used by those tables. */ for(i=0; i<pTabList->nSrc; i++){ - int openOp; Table *pTab; pTab = pTabList->a[i].pTab; if( pTab->isTransient || pTab->pSelect ) continue; - openOp = pTab->isTemp ? OP_OpenAux : OP_Open; - sqliteVdbeAddOp(v, openOp, base+i, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, base+i, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); if( i==0 && !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){ - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); - pParse->schemaVerified = 1; + sqliteCodeVerifySchema(pParse); } if( pWInfo->a[i].pIdx!=0 ){ - sqliteVdbeAddOp(v, openOp, pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenRead, + pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum); sqliteVdbeChangeP3(v, -1, pWInfo->a[i].pIdx->zName, P3_STATIC); } } |