Modifications to the VDBE to support more than one database file. (CVS 878)

FossilOrigin-Name: 875da9eed981bfa27b98e95025f9fdbed74b4098
diff --git a/src/btree.c b/src/btree.c
index 9745f6d..a8fd80e 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 @@
   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 b92dce6..ef6f781 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 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 45b82b0..0c763d0 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 @@
     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 @@
 ** 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 @@
 ** 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 @@
   /* 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 @@
       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 @@
 ** 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 @@
     }
     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 @@
     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 @@
     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 @@
   }
   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 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 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 @@
   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 @@
 }
 
 /*
+** 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 @@
   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 @@
       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 @@
       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 @@
       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 @@
       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 @@
   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 65e5075..62bcf9c 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 @@
   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 @@
       ** 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 @@
     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 @@
     ** 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 f90e2ab..5b96f48 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 @@
   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 @@
     ** 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 @@
   /* 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 @@
     /* 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 8fa81d8..cf14814 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 @@
 
   /* 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 @@
 
   /* 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 @@
   */
   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 @@
   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 @@
   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 @@
     sqliteStrRealloc(pzErrMsg);
     return 0;
   }
+  db->aDb[0].zName = "main";
 
   /* Attempt to read the schema */
   sqliteRegisterBuiltinFunctions(db);
@@ -412,9 +413,9 @@
       &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 @@
 */
 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 @@
 }
 
 /*
+** 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 @@
   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 @@
   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 @@
   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, 
diff --git a/src/os.c b/src/os.c
index d0b50dc..77bdf3a 100644
--- a/src/os.c
+++ b/src/os.c
@@ -269,6 +269,30 @@
 
 
 /*
+** 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.
diff --git a/src/os.h b/src/os.h
index 8491d90..f9784ef 100644
--- a/src/os.h
+++ b/src/os.h
@@ -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 bd191d6..58eb8ce 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 @@
 }
 
 /*
+** 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 4735b82..bba8220 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 @@
 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 387ea1c..8587f80 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -687,7 +687,6 @@
 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 4b46248..eb2892f 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 @@
   Index *pIdx;
   int base;
   Vdbe *v;
-  int openOp;
   int seekOp;
   int cont;
   ExprList eList;
@@ -1828,17 +1827,17 @@
   ** 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 e8f1682..63610ae 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 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 @@
 **                       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 @@
   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 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 @@
 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 fccd863..c914e61 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 @@
   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 @@
     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 @@
   ** 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 @@
   }
   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 e7b8732..85baf68 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 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 *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 @@
 */
 void sqliteVdbeDelete(Vdbe *p){
   int i;
+  sqlite *db = p->db;
   if( p==0 ) return;
   Cleanup(p);
   if( p->pPrev ){
@@ -1233,6 +1232,13 @@
       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 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 @@
 ** 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 @@
   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 @@
 ** 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_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 @@
       }
     }
   }
+  db->aDb[i].inTrans = 1;
   p->undoTransOnError = 1;
   break;
 }
@@ -3237,53 +3241,48 @@
 ** 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 *
+/* Opcode: ReadCookie P1 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.
-**
-** 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 @@
   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 @@
 
 /* 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_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 @@
 ** 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 @@
   if( p2<=0 ){
     POPSTACK;
   }
+  POPSTACK;
   break;
 }
 
@@ -4448,7 +4439,7 @@
 ** 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 @@
 ** 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 @@
   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 @@
     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 @@
 */
 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 @@
     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 @@
     }
     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 7fa6f41..3f7f306 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 @@
   /* 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);
     }
   }