]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Instead of storing a pointer to the parent page in the MemPage structure, have each...
authordanielk1977 <danielk1977@noemail.net>
Mon, 29 Sep 2008 11:49:47 +0000 (11:49 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Mon, 29 Sep 2008 11:49:47 +0000 (11:49 +0000)
FossilOrigin-Name: 40425e93421286cca1965d7a5769084526210c7a

manifest
manifest.uuid
src/btree.c
src/btreeInt.h
src/pager.c
src/pager.h
src/pcache.c
src/pcache.h
src/test2.c
src/test_btree.c
test/corrupt2.test

index f8e4eb3aafca92505fa6a8f5d8b486702278e689..413de553c3d8d961863c11af2600119ab7c7fa24 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C fix\s#3077:\suse\sfull\sversion\sin\spkg-config\sfiles\s(CVS\s5746)
-D 2008-09-29T00:11:22
+C Instead\sof\sstoring\sa\spointer\sto\sthe\sparent\spage\sin\sthe\sMemPage\sstructure,\shave\seach\sB-Tree\scursor\skeep\strack\sof\sthe\sancestry\sof\sthe\scurrent\spage.\s(CVS\s5747)
+D 2008-09-29T11:49:48
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in e4ab842f9a64ef61d57093539a8aab76b12810db
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -99,9 +99,9 @@ F src/attach.c db3f4a60538733c1e4dcb9d0217a6e0d6ccd615b
 F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
 F src/bitvec.c 95c86bd18d8fedf0533f5af196192546e10a7e7d
 F src/btmutex.c 709cad2cdca0afd013f0f612363810e53f59ec53
-F src/btree.c 850d7ede7fe58f8bfad78131278ceff83817abf5
+F src/btree.c 8fc7c0bd577e0e8850d95b0f8d927c983e53deab
 F src/btree.h 6371c5e599fab391a150c96afbc10062b276d107
-F src/btreeInt.h e36f77e6621d671beb19ae581af1eba116cdfdc4
+F src/btreeInt.h a9388a5e5998cec0b79c2024e6740d12d094ad17
 F src/build.c 160c71acca8f643f436ed6c1ee2f684c88df4dfe
 F src/callback.c 7a40fd44da3eb89e7f6eff30aa6f940c45d73a97
 F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
@@ -137,11 +137,11 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
 F src/os_os2.c e391fc95adc744bbdcefd4d11e3066998185a0a0
 F src/os_unix.c f33b69d8a85372b270fe37ee664a4c2140a5217d
 F src/os_win.c 3209dc0ed734291764393ea8d534ba0d8696a540
-F src/pager.c 6426902ba983ce6872fcec20ba72796a79d6cead
-F src/pager.h 30e71f447ecbcfef707bb72cc813ce37ab5a2bc9
+F src/pager.c 44eba010e81dcc9b772401b90d6a1c61ec24345b
+F src/pager.h 9c1917be28fff58118e1fe0ddbc7adfb8dd4f44d
 F src/parse.y d0f76d2cb8d6883d5600dc20beb961a6022b94b8
-F src/pcache.c 52108517c38bcf023f3977085a66aacb520a5385
-F src/pcache.h 0b6871e820159629915e8688b5c67a81a203773f
+F src/pcache.c f8d7beceba164a34441ac37e88abb3a404f968a7
+F src/pcache.h 28d9ce2d66909db1f01652586450b62b64793093
 F src/pragma.c 0b1c2d2a241dd79a7361bbeb8ff575a9e9d7cd71
 F src/prepare.c c7e00ed1b0bdcf699b1aad651247d4dc3d281b0b
 F src/printf.c 785f87120589c1db672e37c6eb1087c456e6f84d
@@ -157,7 +157,7 @@ F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
 F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8
 F src/tclsqlite.c 6a7eeff5afd8f5f10fcb7fd7806e56c725dd2b07
 F src/test1.c c4de690aad182606e5914f6f3c8f43869fbdaaa8
-F src/test2.c eaa77124786649eedf47d3c5e94d8070c0da228f
+F src/test2.c 897528183edf2839c2a3c991d415905db56f1240
 F src/test3.c e85b7ce5c28c3ce7fbdbf7f98e1467b19786c62b
 F src/test4.c 41056378671e7b00e6305fa9ac6fa27e6f96f406
 F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
@@ -167,7 +167,7 @@ F src/test8.c 3637439424d0d21ff2dcf9b015c30fcc1e7bcb24
 F src/test9.c 904ebe0ed1472d6bad17a81e2ecbfc20017dc237
 F src/test_async.c 45024094ed7cf780c5d5dccda645145f95cf78ef
 F src/test_autoext.c f53b0cdf7bf5f08100009572a5d65cdb540bd0ad
-F src/test_btree.c 8d5b835054f1dd15992e09864a8bc04386bab701
+F src/test_btree.c d7b8716544611c323860370ee364e897c861f1b0
 F src/test_config.c db72e95bafdd53c05ceb8735f833cc5dc1f48782
 F src/test_devsym.c 802d10e65b4217208cb47059b84adf46318bcdf4
 F src/test_func.c a55c4d5479ff2eb5c0a22d4d88e9528ab59c953b
@@ -253,7 +253,7 @@ F test/colmeta.test 087c42997754b8c648819832241daf724f813322
 F test/colname.test 0f05fea3e5c9260ece53117ad400657515c34280
 F test/conflict.test bb29b052c60a1f7eb6382be77902061d1f305318
 F test/corrupt.test 5bcf7a986358123b8055dfa64b45fc2fb54dcaa9
-F test/corrupt2.test 08fb049fdf4f72902ff39b1c609e7c1c2e985d8b
+F test/corrupt2.test b83f690753525ddac4a9c96e6ba11886ddaf24ea
 F test/corrupt3.test 263e8bb04e2728df832fddf6973cf54c91db0c32
 F test/corrupt4.test acdb01afaedf529004b70e55de1a6f5a05ae7fff
 F test/corrupt5.test 7796d5bdfe155ed824cee9dff371f49da237cfe0
@@ -637,7 +637,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P 7c561f2e9264de676c1028943f6c3d06542fd802
-R 02a6492d8d58a0069722b4e87a758448
-U vapier
-Z f2828935781c5b8b7b8d7e5f72f7da88
+P efe095e0cb8e0f8a11fd9cc321ec83b556458bf2
+R a0f3c37847099f2dcfb6efb98a6a77ac
+U danielk1977
+Z 079550270237333fc6211bd885a42871
index 2f2779978f8287dface4a413d187d1e99438580d..b57fb3d6a67362e1c03361ad2ae05af2a2277e4b 100644 (file)
@@ -1 +1 @@
-efe095e0cb8e0f8a11fd9cc321ec83b556458bf2
\ No newline at end of file
+40425e93421286cca1965d7a5769084526210c7a
\ No newline at end of file
index 9e105326b7002b6057ed61c51374f4a0d7ff15fb..2b0b386db4d17dcd31cd04035e577a54e3c03faf 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.517 2008/09/26 17:31:55 danielk1977 Exp $
+** $Id: btree.c,v 1.518 2008/09/29 11:49:48 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -301,7 +301,7 @@ static int saveCursorPosition(BtCursor *pCur){
   ** table, then malloc space for and store the pCur->nKey bytes of key 
   ** data.
   */
-  if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
+  if( rc==SQLITE_OK && 0==pCur->apPage[0]->intKey){
     void *pKey = sqlite3Malloc(pCur->nKey);
     if( pKey ){
       rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
@@ -314,11 +314,15 @@ static int saveCursorPosition(BtCursor *pCur){
       rc = SQLITE_NOMEM;
     }
   }
-  assert( !pCur->pPage->intKey || !pCur->pKey );
+  assert( !pCur->apPage[0]->intKey || !pCur->pKey );
 
   if( rc==SQLITE_OK ){
-    releasePage(pCur->pPage);
-    pCur->pPage = 0;
+    int i;
+    for(i=0; i<=pCur->iPage; i++){
+      releasePage(pCur->apPage[i]);
+      pCur->apPage[i] = 0;
+    }
+    pCur->iPage = -1;
     pCur->eState = CURSOR_REQUIRESEEK;
   }
 
@@ -908,50 +912,32 @@ static int decodeFlags(MemPage *pPage, int flagByte){
 /*
 ** Initialize the auxiliary information for a disk block.
 **
-** The pParent parameter must be a pointer to the MemPage which
-** is the parent of the page being initialized.  The root of a
-** BTree has no parent and so for that page, pParent==NULL.
-**
 ** Return SQLITE_OK on success.  If we see that the page does
 ** not contain a well-formed database page, then return 
 ** SQLITE_CORRUPT.  Note that a return of SQLITE_OK does not
 ** guarantee that the page is well-formed.  It only shows that
 ** we failed to detect any corruption.
 */
-int sqlite3BtreeInitPage(
-  MemPage *pPage,        /* The page to be initialized */
-  MemPage *pParent       /* The parent.  Might be NULL */
-){
-  int pc;            /* Address of a freeblock within pPage->aData[] */
-  int hdr;           /* Offset to beginning of page header */
-  u8 *data;          /* Equal to pPage->aData */
-  BtShared *pBt;        /* The main btree structure */
-  int usableSize;    /* Amount of usable space on each page */
-  int cellOffset;    /* Offset from start of page to first cell pointer */
-  int nFree;         /* Number of unused bytes on the page */
-  int top;           /* First byte of the cell content area */
+int sqlite3BtreeInitPage(MemPage *pPage){
 
-  pBt = pPage->pBt;
-  assert( pBt!=0 );
-  assert( pParent==0 || pParent->pBt==pBt );
-  assert( sqlite3_mutex_held(pBt->mutex) );
+  assert( pPage->pBt!=0 );
+  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
   assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
   assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
-  if( pPage==pParent ){
-    return SQLITE_CORRUPT_BKPT;
-  }
-  if( (pPage->pParent!=pParent)
-   && (pPage->pParent!=0 || pPage->isInit==PAGE_ISINIT_FULL) ){
-    /* The parent page should never change unless the file is corrupt */
-    return SQLITE_CORRUPT_BKPT;
-  }
-  if( pPage->isInit==PAGE_ISINIT_FULL ) return SQLITE_OK;
-  if( pParent!=0 ){
-    pPage->pParent = pParent;
-    sqlite3PagerRef(pParent->pDbPage);
-  }
-  if( pPage->isInit==PAGE_ISINIT_NONE ){
+
+  if( !pPage->isInit ){
+    int pc;            /* Address of a freeblock within pPage->aData[] */
+    int hdr;           /* Offset to beginning of page header */
+    u8 *data;          /* Equal to pPage->aData */
+    BtShared *pBt;        /* The main btree structure */
+    int usableSize;    /* Amount of usable space on each page */
+    int cellOffset;    /* Offset from start of page to first cell pointer */
+    int nFree;         /* Number of unused bytes on the page */
+    int top;           /* First byte of the cell content area */
+
+    pBt = pPage->pBt;
+
     hdr = pPage->hdrOffset;
     data = pPage->aData;
     if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
@@ -967,10 +953,6 @@ int sqlite3BtreeInitPage(
       /* To many cells for a single page.  The page must be corrupt */
       return SQLITE_CORRUPT_BKPT;
     }
-    if( pPage->nCell==0 && pParent!=0 && pParent->pgno!=1 ){
-      /* All pages must have at least one cell, except for root pages */
-      return SQLITE_CORRUPT_BKPT;
-    }
   
     /* Compute the total free space on the page */
     pc = get2byte(&data[hdr+1]);
@@ -995,7 +977,6 @@ int sqlite3BtreeInitPage(
       /* Free space cannot exceed total page size */
       return SQLITE_CORRUPT_BKPT; 
     }
-  }
 
 #if 0
   /* Check that all the offsets in the cell offset array are within range. 
@@ -1017,7 +998,8 @@ int sqlite3BtreeInitPage(
   }
 #endif
 
-  pPage->isInit = PAGE_ISINIT_FULL;
+    pPage->isInit = 1;
+  }
   return SQLITE_OK;
 }
 
@@ -1051,7 +1033,7 @@ static void zeroPage(MemPage *pPage, int flags){
   pPage->maskPage = pBt->pageSize - 1;
   pPage->idxShift = 0;
   pPage->nCell = 0;
-  pPage->isInit = PAGE_ISINIT_FULL;
+  pPage->isInit = 1;
 }
 
 
@@ -1115,15 +1097,13 @@ static int pagerPagecount(Pager *pPager){
 static int getAndInitPage(
   BtShared *pBt,          /* The database file */
   Pgno pgno,           /* Number of the page to get */
-  MemPage **ppPage,    /* Write the page pointer here */
-  MemPage *pParent     /* Parent of the page */
+  MemPage **ppPage     /* Write the page pointer here */
 ){
   int rc;
   DbPage *pDbPage;
   MemPage *pPage;
 
   assert( sqlite3_mutex_held(pBt->mutex) );
-  assert( !pParent || pParent->isInit==PAGE_ISINIT_FULL );
   if( pgno==0 ){
     return SQLITE_CORRUPT_BKPT; 
   }
@@ -1147,14 +1127,8 @@ static int getAndInitPage(
     if( rc ) return rc;
     pPage = *ppPage;
   }
-  if( pPage->isInit!=PAGE_ISINIT_FULL ){
-    rc = sqlite3BtreeInitPage(pPage, pParent);
-  }else if( pParent && (pPage==pParent || pPage->pParent!=pParent) ){
-    /* This condition indicates a loop in the b-tree structure (the scenario
-    ** where database corruption has caused a page to be a direct or
-    ** indirect descendant of itself).
-    */ 
-    rc = SQLITE_CORRUPT_BKPT;
+  if( !pPage->isInit ){
+    rc = sqlite3BtreeInitPage(pPage);
   }
   if( rc!=SQLITE_OK ){
     releasePage(pPage);
@@ -1178,30 +1152,6 @@ static void releasePage(MemPage *pPage){
   }
 }
 
-/*
-** This routine is called when the reference count for a page
-** reaches zero.  We need to unref the pParent pointer when that
-** happens.
-*/
-static void pageDestructor(DbPage *pData){
-  MemPage *pPage;
-  pPage = (MemPage *)sqlite3PagerGetExtra(pData);
-  if( pPage ){
-    assert( pPage->isInit!=PAGE_ISINIT_FULL 
-         || sqlite3_mutex_held(pPage->pBt->mutex) 
-    );
-    if( pPage->pParent ){
-      MemPage *pParent = pPage->pParent;
-      assert( pParent->pBt==pPage->pBt );
-      pPage->pParent = 0;
-      releasePage(pParent);
-    }
-    if( pPage->isInit==PAGE_ISINIT_FULL ){
-      pPage->isInit = PAGE_ISINIT_DATA;
-    }
-  }
-}
-
 /*
 ** During a rollback, when the pager reloads information into the cache
 ** so that the cache is restored to its original state at the start of
@@ -1213,12 +1163,12 @@ static void pageDestructor(DbPage *pData){
 static void pageReinit(DbPage *pData){
   MemPage *pPage;
   pPage = (MemPage *)sqlite3PagerGetExtra(pData);
-  if( pPage->isInit==PAGE_ISINIT_FULL ){
+  if( pPage->isInit ){
     assert( sqlite3_mutex_held(pPage->pBt->mutex) );
     pPage->isInit = 0;
-    sqlite3BtreeInitPage(pPage, pPage->pParent);
-  }else if( pPage->isInit==PAGE_ISINIT_DATA ){
-    pPage->isInit = 0;
+    if( sqlite3PagerPageRefcount(pData)>0 ){
+      sqlite3BtreeInitPage(pPage);
+    }
   }
 }
 
@@ -1344,7 +1294,7 @@ int sqlite3BtreeOpen(
     }
     pBt->busyHdr.xFunc = sqlite3BtreeInvokeBusyHandler;
     pBt->busyHdr.pArg = pBt;
-    rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, pageDestructor,
+    rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
                           EXTRA_SIZE, flags, vfsFlags);
     if( rc==SQLITE_OK ){
       rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
@@ -2092,7 +2042,7 @@ static int setChildPtrmaps(MemPage *pPage){
   Pgno pgno = pPage->pgno;
 
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
-  rc = sqlite3BtreeInitPage(pPage, pPage->pParent);
+  rc = sqlite3BtreeInitPage(pPage);
   if( rc!=SQLITE_OK ){
     goto set_child_ptrmaps_out;
   }
@@ -2151,7 +2101,7 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
     int i;
     int nCell;
 
-    sqlite3BtreeInitPage(pPage, 0);
+    sqlite3BtreeInitPage(pPage);
     nCell = pPage->nCell;
 
     for(i=0; i<nCell; i++){
@@ -2811,6 +2761,9 @@ int sqlite3BtreeRollbackStmt(Btree *p){
 ** No checking is done to make sure that page iTable really is the
 ** root page of a b-tree.  If it is not, then the cursor acquired
 ** will not work correctly.
+**
+** It is assumed that the sqlite3BtreeCursorSize() bytes of memory 
+** pointed to by pCur have been zeroed by the caller.
 */
 static int btreeCursor(
   Btree *p,                              /* The btree */
@@ -2846,7 +2799,7 @@ static int btreeCursor(
     rc = SQLITE_EMPTY;
     goto create_cursor_exception;
   }
-  rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->pPage, 0);
+  rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]);
   if( rc!=SQLITE_OK ){
     goto create_cursor_exception;
   }
@@ -2869,7 +2822,7 @@ static int btreeCursor(
   return SQLITE_OK;
 
 create_cursor_exception:
-  releasePage(pCur->pPage);
+  releasePage(pCur->apPage[0]);
   unlockBtreeIfUnused(pBt);
   return rc;
 }
@@ -2900,6 +2853,7 @@ int sqlite3BtreeCursorSize(){
 int sqlite3BtreeCloseCursor(BtCursor *pCur){
   Btree *pBtree = pCur->pBtree;
   if( pBtree ){
+    int i;
     BtShared *pBt = pCur->pBt;
     sqlite3BtreeEnter(pBtree);
     pBt->db = pBtree->db;
@@ -2912,7 +2866,9 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
     if( pCur->pNext ){
       pCur->pNext->pPrev = pCur->pPrev;
     }
-    releasePage(pCur->pPage);
+    for(i=0; i<=pCur->iPage; i++){
+      releasePage(pCur->apPage[i]);
+    }
     unlockBtreeIfUnused(pBt);
     invalidateOverflowCache(pCur);
     /* sqlite3_free(pCur); */
@@ -2926,12 +2882,13 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
 ** The temporary cursor is not on the cursor list for the Btree.
 */
 void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
+  int i;
   assert( cursorHoldsMutex(pCur) );
-  memcpy(pTempCur, pCur, sizeof(*pCur));
+  memcpy(pTempCur, pCur, sizeof(BtCursor));
   pTempCur->pNext = 0;
   pTempCur->pPrev = 0;
-  if( pTempCur->pPage ){
-    sqlite3PagerRef(pTempCur->pPage->pDbPage);
+  for(i=0; i<=pTempCur->iPage; i++){
+    sqlite3PagerRef(pTempCur->apPage[i]->pDbPage);
   }
 }
 
@@ -2940,9 +2897,10 @@ void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
 ** function above.
 */
 void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
+  int i;
   assert( cursorHoldsMutex(pCur) );
-  if( pCur->pPage ){
-    sqlite3PagerUnref(pCur->pPage->pDbPage);
+  for(i=0; i<=pCur->iPage; i++){
+    sqlite3PagerUnref(pCur->apPage[i]->pDbPage);
   }
 }
 
@@ -2964,8 +2922,9 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
 #ifndef NDEBUG
   static void assertCellInfo(BtCursor *pCur){
     CellInfo info;
+    int iPage = pCur->iPage;
     memset(&info, 0, sizeof(info));
-    sqlite3BtreeParseCell(pCur->pPage, pCur->idx, &info);
+    sqlite3BtreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
     assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
   }
 #else
@@ -2975,7 +2934,8 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
   /* Use a real function in MSVC to work around bugs in that compiler. */
   static void getCellInfo(BtCursor *pCur){
     if( pCur->info.nSize==0 ){
-      sqlite3BtreeParseCell(pCur->pPage, pCur->idx, &pCur->info);
+      int iPage = pCur->iPage;
+      sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
       pCur->validNKey = 1;
     }else{
       assertCellInfo(pCur);
@@ -2983,12 +2943,13 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
   }
 #else /* if not _MSC_VER */
   /* Use a macro in all other compilers so that the function is inlined */
-#define getCellInfo(pCur)                                               \
-  if( pCur->info.nSize==0 ){                                            \
-    sqlite3BtreeParseCell(pCur->pPage, pCur->idx, &pCur->info);         \
-    pCur->validNKey = 1;                                                \
-  }else{                                                                \
-    assertCellInfo(pCur);                                               \
+#define getCellInfo(pCur)                                                      \
+  if( pCur->info.nSize==0 ){                                                   \
+    int iPage = pCur->iPage;                                                   \
+    sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
+    pCur->validNKey = 1;                                                       \
+  }else{                                                                       \
+    assertCellInfo(pCur);                                                      \
   }
 #endif /* _MSC_VER */
 
@@ -3201,12 +3162,12 @@ static int accessPayload(
   int rc = SQLITE_OK;
   u32 nKey;
   int iIdx = 0;
-  MemPage *pPage = pCur->pPage;     /* Btree page of current cursor entry */
-  BtShared *pBt;                   /* Btree this cursor belongs to */
+  MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
+  BtShared *pBt;                              /* Btree this cursor belongs to */
 
   assert( pPage );
   assert( pCur->eState==CURSOR_VALID );
-  assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+  assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
   assert( offset>=0 );
   assert( cursorHoldsMutex(pCur) );
 
@@ -3339,12 +3300,11 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
   rc = restoreCursorPosition(pCur);
   if( rc==SQLITE_OK ){
     assert( pCur->eState==CURSOR_VALID );
-    assert( pCur->pPage!=0 );
-    if( pCur->pPage->intKey ){
+    assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] );
+    if( pCur->apPage[0]->intKey ){
       return SQLITE_CORRUPT_BKPT;
     }
-    assert( pCur->pPage->intKey==0 );
-    assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+    assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
     rc = accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0, 0);
   }
   return rc;
@@ -3372,8 +3332,8 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
   rc = restoreCursorPosition(pCur);
   if( rc==SQLITE_OK ){
     assert( pCur->eState==CURSOR_VALID );
-    assert( pCur->pPage!=0 );
-    assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+    assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] );
+    assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
     rc = accessPayload(pCur, offset, amt, pBuf, 1, 0);
   }
   return rc;
@@ -3408,11 +3368,11 @@ static const unsigned char *fetchPayload(
   u32 nKey;
   int nLocal;
 
-  assert( pCur!=0 && pCur->pPage!=0 );
+  assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
   assert( pCur->eState==CURSOR_VALID );
   assert( cursorHoldsMutex(pCur) );
-  pPage = pCur->pPage;
-  assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+  pPage = pCur->apPage[pCur->iPage];
+  assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
   getCellInfo(pCur);
   aPayload = pCur->info.pCell;
   aPayload += pCur->info.nHeader;
@@ -3471,20 +3431,23 @@ const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
 */
 static int moveToChild(BtCursor *pCur, u32 newPgno){
   int rc;
+  int i = pCur->iPage;
   MemPage *pNewPage;
-  MemPage *pOldPage;
   BtShared *pBt = pCur->pBt;
 
   assert( cursorHoldsMutex(pCur) );
   assert( pCur->eState==CURSOR_VALID );
-  rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
+  assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
+  if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
+    return SQLITE_CORRUPT_BKPT;
+  }
+  rc = getAndInitPage(pBt, newPgno, &pNewPage);
   if( rc ) return rc;
-  pNewPage->idxParent = pCur->idx;
-  pOldPage = pCur->pPage;
-  pOldPage->idxShift = 0;
-  releasePage(pOldPage);
-  pCur->pPage = pNewPage;
-  pCur->idx = 0;
+  pCur->apPage[i]->idxShift = 0;
+  pCur->apPage[i+1] = pNewPage;
+  pCur->aiIdx[i+1] = 0;
+  pCur->iPage++;
+
   pCur->info.nSize = 0;
   pCur->validNKey = 0;
   if( pNewPage->nCell<1 ){
@@ -3493,26 +3456,6 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
   return SQLITE_OK;
 }
 
-/*
-** Return true if the page is the virtual root of its table.
-**
-** The virtual root page is the root page for most tables.  But
-** for the table rooted on page 1, sometime the real root page
-** is empty except for the right-pointer.  In such cases the
-** virtual root page is the page that the right-pointer of page
-** 1 is pointing to.
-*/
-int sqlite3BtreeIsRootPage(MemPage *pPage){
-  MemPage *pParent;
-
-  assert( sqlite3_mutex_held(pPage->pBt->mutex) );
-  pParent = pPage->pParent;
-  if( pParent==0 ) return 1;
-  if( pParent->pgno>1 ) return 0;
-  if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1;
-  return 0;
-}
-
 /*
 ** Move the cursor up to the parent page.
 **
@@ -3522,26 +3465,16 @@ int sqlite3BtreeIsRootPage(MemPage *pPage){
 ** the largest cell index.
 */
 void sqlite3BtreeMoveToParent(BtCursor *pCur){
-  MemPage *pParent;
-  MemPage *pPage;
-  int idxParent;
-
   assert( cursorHoldsMutex(pCur) );
   assert( pCur->eState==CURSOR_VALID );
-  pPage = pCur->pPage;
-  assert( pPage!=0 );
-  assert( !sqlite3BtreeIsRootPage(pPage) );
-  pParent = pPage->pParent;
-  assert( pParent!=0 );
-  assert( pPage->pDbPage->nRef>0 );
-  idxParent = pPage->idxParent;
-  sqlite3PagerRef(pParent->pDbPage);
-  releasePage(pPage);
-  pCur->pPage = pParent;
+  assert( pCur->iPage>0 );
+  assert( pCur->apPage[pCur->iPage] );
+
+  releasePage(pCur->apPage[pCur->iPage]);
+  pCur->iPage--;
   pCur->info.nSize = 0;
   pCur->validNKey = 0;
-  assert( pParent->idxShift==0 );
-  pCur->idx = idxParent;
+  assert( pCur->apPage[pCur->iPage]->idxShift==0 );
 }
 
 /*
@@ -3563,38 +3496,29 @@ static int moveToRoot(BtCursor *pCur){
     }
     clearCursorPosition(pCur);
   }
-  pRoot = pCur->pPage;
-  if( pRoot && pRoot->isInit ){
-    /* If the page the cursor is currently pointing to is fully initialized,
-    ** then the root page can be found by following the MemPage.pParent
-    ** pointers. This is faster than requesting a reference to the root
-    ** page from the pager layer.
-    */
-    while( pRoot->pParent ){
-      assert( pRoot->isInit==PAGE_ISINIT_FULL );
-      pRoot = pRoot->pParent;
-    }
-    assert( pRoot->isInit==PAGE_ISINIT_FULL );
-    if( pRoot!=pCur->pPage ){
-      sqlite3PagerRef(pRoot->pDbPage);
-      releasePage(pCur->pPage);
-      pCur->pPage = pRoot;
+
+  if( pCur->iPage>=0 ){
+    int i;
+    for(i=1; i<=pCur->iPage; i++){
+      releasePage(pCur->apPage[i]);
     }
   }else{
     if( 
-      SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0))
+      SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]))
     ){
       pCur->eState = CURSOR_INVALID;
       return rc;
     }
-    releasePage(pCur->pPage);
-    pCur->pPage = pRoot;
   }
-  assert( pCur->pPage->pgno==pCur->pgnoRoot );
-  pCur->idx = 0;
+
+  pRoot = pCur->apPage[0];
+  assert( pRoot->pgno==pCur->pgnoRoot );
+  pCur->iPage = 0;
+  pCur->aiIdx[0] = 0;
   pCur->info.nSize = 0;
   pCur->atLast = 0;
   pCur->validNKey = 0;
+
   if( pRoot->nCell==0 && !pRoot->leaf ){
     Pgno subpage;
     assert( pRoot->pgno==1 );
@@ -3602,8 +3526,9 @@ static int moveToRoot(BtCursor *pCur){
     assert( subpage>0 );
     pCur->eState = CURSOR_VALID;
     rc = moveToChild(pCur, subpage);
+  }else{
+    pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
   }
-  pCur->eState = ((pCur->pPage->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
   return rc;
 }
 
@@ -3621,9 +3546,9 @@ static int moveToLeftmost(BtCursor *pCur){
 
   assert( cursorHoldsMutex(pCur) );
   assert( pCur->eState==CURSOR_VALID );
-  while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
-    assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
-    pgno = get4byte(findCell(pPage, pCur->idx));
+  while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
+    assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
+    pgno = get4byte(findCell(pPage, pCur->aiIdx[pCur->iPage]));
     rc = moveToChild(pCur, pgno);
   }
   return rc;
@@ -3646,13 +3571,13 @@ static int moveToRightmost(BtCursor *pCur){
 
   assert( cursorHoldsMutex(pCur) );
   assert( pCur->eState==CURSOR_VALID );
-  while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
+  while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
     pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
-    pCur->idx = pPage->nCell;
+    pCur->aiIdx[pCur->iPage] = pPage->nCell;
     rc = moveToChild(pCur, pgno);
   }
   if( rc==SQLITE_OK ){
-    pCur->idx = pPage->nCell - 1;
+    pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
     pCur->info.nSize = 0;
     pCur->validNKey = 0;
   }
@@ -3671,11 +3596,11 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
   rc = moveToRoot(pCur);
   if( rc==SQLITE_OK ){
     if( pCur->eState==CURSOR_INVALID ){
-      assert( pCur->pPage->nCell==0 );
+      assert( pCur->apPage[pCur->iPage]->nCell==0 );
       *pRes = 1;
       rc = SQLITE_OK;
     }else{
-      assert( pCur->pPage->nCell>0 );
+      assert( pCur->apPage[pCur->iPage]->nCell>0 );
       *pRes = 0;
       rc = moveToLeftmost(pCur);
     }
@@ -3695,7 +3620,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
   rc = moveToRoot(pCur);
   if( rc==SQLITE_OK ){
     if( CURSOR_INVALID==pCur->eState ){
-      assert( pCur->pPage->nCell==0 );
+      assert( pCur->apPage[pCur->iPage]->nCell==0 );
       *pRes = 1;
     }else{
       assert( pCur->eState==CURSOR_VALID );
@@ -3749,7 +3674,9 @@ int sqlite3BtreeMovetoUnpacked(
 
   /* If the cursor is already positioned at the point we are trying
   ** to move to, then just return without doing any work */
-  if( pCur->eState==CURSOR_VALID && pCur->validNKey && pCur->pPage->intKey ){
+  if( pCur->eState==CURSOR_VALID && pCur->validNKey 
+   && pCur->apPage[0]->intKey 
+  ){
     if( pCur->info.nKey==intKey ){
       *pRes = 0;
       return SQLITE_OK;
@@ -3764,18 +3691,18 @@ int sqlite3BtreeMovetoUnpacked(
   if( rc ){
     return rc;
   }
-  assert( pCur->pPage );
-  assert( pCur->pPage->isInit==PAGE_ISINIT_FULL );
+  assert( pCur->apPage[pCur->iPage] );
+  assert( pCur->apPage[pCur->iPage]->isInit );
   if( pCur->eState==CURSOR_INVALID ){
     *pRes = -1;
-    assert( pCur->pPage->nCell==0 );
+    assert( pCur->apPage[pCur->iPage]->nCell==0 );
     return SQLITE_OK;
   }
-  assert( pCur->pPage->intKey || pIdxKey );
+  assert( pCur->apPage[0]->intKey || pIdxKey );
   for(;;){
     int lwr, upr;
     Pgno chldPg;
-    MemPage *pPage = pCur->pPage;
+    MemPage *pPage = pCur->apPage[pCur->iPage];
     int c = -1;  /* pRes return if table is empty must be -1 */
     lwr = 0;
     upr = pPage->nCell-1;
@@ -3784,18 +3711,19 @@ int sqlite3BtreeMovetoUnpacked(
       goto moveto_finish;
     }
     if( biasRight ){
-      pCur->idx = upr;
+      pCur->aiIdx[pCur->iPage] = upr;
     }else{
-      pCur->idx = (upr+lwr)/2;
+      pCur->aiIdx[pCur->iPage] = (upr+lwr)/2;
     }
     if( lwr<=upr ) for(;;){
       void *pCellKey;
       i64 nCellKey;
+      int idx = pCur->aiIdx[pCur->iPage];
       pCur->info.nSize = 0;
       pCur->validNKey = 1;
       if( pPage->intKey ){
         u8 *pCell;
-        pCell = findCell(pPage, pCur->idx) + pPage->childPtrSize;
+        pCell = findCell(pPage, idx) + pPage->childPtrSize;
         if( pPage->hasData ){
           u32 dummy;
           pCell += getVarint32(pCell, dummy);
@@ -3830,7 +3758,7 @@ int sqlite3BtreeMovetoUnpacked(
       if( c==0 ){
         pCur->info.nKey = nCellKey;
         if( pPage->intKey && !pPage->leaf ){
-          lwr = pCur->idx;
+          lwr = idx;
           upr = lwr - 1;
           break;
         }else{
@@ -3840,18 +3768,18 @@ int sqlite3BtreeMovetoUnpacked(
         }
       }
       if( c<0 ){
-        lwr = pCur->idx+1;
+        lwr = idx+1;
       }else{
-        upr = pCur->idx-1;
+        upr = idx-1;
       }
       if( lwr>upr ){
         pCur->info.nKey = nCellKey;
         break;
       }
-      pCur->idx = (lwr+upr)/2;
+      pCur->aiIdx[pCur->iPage] = (lwr+upr)/2;
     }
     assert( lwr==upr+1 );
-    assert( pPage->isInit==PAGE_ISINIT_FULL );
+    assert( pPage->isInit );
     if( pPage->leaf ){
       chldPg = 0;
     }else if( lwr>=pPage->nCell ){
@@ -3860,12 +3788,12 @@ int sqlite3BtreeMovetoUnpacked(
       chldPg = get4byte(findCell(pPage, lwr));
     }
     if( chldPg==0 ){
-      assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+      assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
       if( pRes ) *pRes = c;
       rc = SQLITE_OK;
       goto moveto_finish;
     }
-    pCur->idx = lwr;
+    pCur->aiIdx[pCur->iPage] = lwr;
     pCur->info.nSize = 0;
     pCur->validNKey = 0;
     rc = moveToChild(pCur, chldPg);
@@ -3937,6 +3865,7 @@ sqlite3 *sqlite3BtreeCursorDb(const BtCursor *pCur){
 */
 int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
   int rc;
+  int idx;
   MemPage *pPage;
 
   assert( cursorHoldsMutex(pCur) );
@@ -3945,7 +3874,6 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
     return rc;
   }
   assert( pRes!=0 );
-  pPage = pCur->pPage;
   if( CURSOR_INVALID==pCur->eState ){
     *pRes = 1;
     return SQLITE_OK;
@@ -3957,13 +3885,14 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
   }
   pCur->skip = 0;
 
-  assert( pPage->isInit==PAGE_ISINIT_FULL );
-  assert( pCur->idx<pPage->nCell );
+  pPage = pCur->apPage[pCur->iPage];
+  idx = ++pCur->aiIdx[pCur->iPage];
+  assert( pPage->isInit );
+  assert( idx<=pPage->nCell );
 
-  pCur->idx++;
   pCur->info.nSize = 0;
   pCur->validNKey = 0;
-  if( pCur->idx>=pPage->nCell ){
+  if( idx>=pPage->nCell ){
     if( !pPage->leaf ){
       rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
       if( rc ) return rc;
@@ -3972,14 +3901,14 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
       return rc;
     }
     do{
-      if( sqlite3BtreeIsRootPage(pPage) ){
+      if( pCur->iPage==0 ){
         *pRes = 1;
         pCur->eState = CURSOR_INVALID;
         return SQLITE_OK;
       }
       sqlite3BtreeMoveToParent(pCur);
-      pPage = pCur->pPage;
-    }while( pCur->idx>=pPage->nCell );
+      pPage = pCur->apPage[pCur->iPage];
+    }while( pCur->aiIdx[pCur->iPage]>=pPage->nCell );
     *pRes = 0;
     if( pPage->intKey ){
       rc = sqlite3BtreeNext(pCur, pRes);
@@ -4005,7 +3934,6 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
 */
 int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   int rc;
-  Pgno pgno;
   MemPage *pPage;
 
   assert( cursorHoldsMutex(pCur) );
@@ -4025,29 +3953,29 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   }
   pCur->skip = 0;
 
-  pPage = pCur->pPage;
-  assert( pPage->isInit==PAGE_ISINIT_FULL );
-  assert( pCur->idx>=0 );
+  pPage = pCur->apPage[pCur->iPage];
+  assert( pPage->isInit );
   if( !pPage->leaf ){
-    pgno = get4byte( findCell(pPage, pCur->idx) );
-    rc = moveToChild(pCur, pgno);
+    int idx = pCur->aiIdx[pCur->iPage];
+    rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
     if( rc ){
       return rc;
     }
     rc = moveToRightmost(pCur);
   }else{
-    while( pCur->idx==0 ){
-      if( sqlite3BtreeIsRootPage(pPage) ){
+    while( pCur->aiIdx[pCur->iPage]==0 ){
+      if( pCur->iPage==0 ){
         pCur->eState = CURSOR_INVALID;
         *pRes = 1;
         return SQLITE_OK;
       }
       sqlite3BtreeMoveToParent(pCur);
-      pPage = pCur->pPage;
     }
-    pCur->idx--;
     pCur->info.nSize = 0;
     pCur->validNKey = 0;
+
+    pCur->aiIdx[pCur->iPage]--;
+    pPage = pCur->apPage[pCur->iPage];
     if( pPage->intKey && !pPage->leaf ){
       rc = sqlite3BtreePrevious(pCur, pRes);
     }else{
@@ -4317,12 +4245,9 @@ static int allocateBtreePage(
 end_allocate_page:
   releasePage(pTrunk);
   releasePage(pPrevTrunk);
-  if( rc==SQLITE_OK ){
-    if( (*ppPage)->isInit==PAGE_ISINIT_FULL ){
-      releasePage(*ppPage);
-      return SQLITE_CORRUPT_BKPT;
-    }
-    (*ppPage)->isInit = 0;
+  if( rc==SQLITE_OK && sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
+    releasePage(*ppPage);
+    return SQLITE_CORRUPT_BKPT;
   }
   return rc;
 }
@@ -4341,8 +4266,6 @@ static int freePage(MemPage *pPage){
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   assert( pPage->pgno>1 );
   pPage->isInit = 0;
-  releasePage(pPage->pParent);
-  pPage->pParent = 0;
 
   /* Increment the free page count on pPage1 */
   rc = sqlite3PagerWrite(pPage1->pDbPage);
@@ -4613,28 +4536,7 @@ static int reparentPage(
   int idx,                      /* Index of child page pgno in pNewParent */
   int updatePtrmap              /* If true, update pointer-map for pgno */
 ){
-  MemPage *pThis;
   DbPage *pDbPage;
-
-  assert( sqlite3_mutex_held(pBt->mutex) );
-  assert( pNewParent!=0 );
-  if( pgno==0 ) return SQLITE_OK;
-  assert( pBt->pPager!=0 );
-  pDbPage = sqlite3PagerLookup(pBt->pPager, pgno);
-  if( pDbPage ){
-    pThis = (MemPage *)sqlite3PagerGetExtra(pDbPage);
-    if( pThis->isInit==PAGE_ISINIT_FULL ){
-      assert( pThis->aData==sqlite3PagerGetData(pDbPage) );
-      if( pThis->pParent!=pNewParent ){
-        if( pThis->pParent ) sqlite3PagerUnref(pThis->pParent->pDbPage);
-        pThis->pParent = pNewParent;
-        sqlite3PagerRef(pNewParent->pDbPage);
-      }
-      pThis->idxParent = idx;
-    }
-    sqlite3PagerUnref(pDbPage);
-  }
-
   if( ISAUTOVACUUM && updatePtrmap ){
     return ptrmapPut(pBt, pgno, PTRMAP_BTREE, pNewParent->pgno);
   }
@@ -4884,7 +4786,7 @@ static void assemblePage(
 #define NB (NN*2+1)      /* Total pages involved in the balance */
 
 /* Forward reference */
-static int balance(MemPage*, int);
+static int balance(BtCursor*, int);
 
 #ifndef SQLITE_OMIT_QUICKBALANCE
 /*
@@ -4904,13 +4806,15 @@ static int balance(MemPage*, int);
 ** pParent is its parent.  pPage must have a single overflow entry
 ** which is also the right-most entry on the page.
 */
-static int balance_quick(MemPage *pPage, MemPage *pParent){
+static int balance_quick(BtCursor *pCur){
   int rc;
   MemPage *pNew = 0;
   Pgno pgnoNew;
   u8 *pCell;
   u16 szCell;
   CellInfo info;
+  MemPage *pPage = pCur->apPage[pCur->iPage];
+  MemPage *pParent = pCur->apPage[pCur->iPage-1];
   BtShared *pBt = pPage->pBt;
   int parentIdx = pParent->nCell;   /* pParent new divider cell index */
   int parentSize;                   /* Size of new divider cell */
@@ -4930,8 +4834,10 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
     pPage->nOverflow = 0;
   
     /* Set the parent of the newly allocated page to pParent. */
+#if 0
     pNew->pParent = pParent;
     sqlite3PagerRef(pParent->pDbPage);
+#endif
   
     /* pPage is currently the right-child of pParent. Change this
     ** so that the right-child is the new page allocated above and
@@ -4986,14 +4892,15 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
   ** the page data and contents of MemPage are consistent.
   */
   pPage->isInit = 0;
-  sqlite3BtreeInitPage(pPage, pPage->pParent);
-  sqlite3PagerUnref(pPage->pParent->pDbPage);
+  sqlite3BtreeInitPage(pPage);
 
   /* If everything else succeeded, balance the parent page, in 
   ** case the divider cell inserted caused it to become overfull.
   */
   if( rc==SQLITE_OK ){
-    rc = balance(pParent, 0);
+    releasePage(pPage);
+    pCur->iPage--;
+    rc = balance(pCur, 0);
   }
   return rc;
 }
@@ -5028,7 +4935,8 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
 ** in a corrupted state.  So if this routine fails, the database should
 ** be rolled back.
 */
-static int balance_nonroot(MemPage *pPage){
+static int balance_nonroot(BtCursor *pCur){
+  MemPage *pPage;              /* The over or underfull page to balance */
   MemPage *pParent;            /* The parent of pPage */
   BtShared *pBt;               /* The whole database */
   int nCell = 0;               /* Number of cells in apCell[] */
@@ -5063,15 +4971,17 @@ static int balance_nonroot(MemPage *pPage){
   u8 *aSpace2 = 0;       /* Space for overflow dividers cells after balance */
   u8 *aFrom = 0;
 
+  pPage = pCur->apPage[pCur->iPage];
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
 
   /* 
   ** Find the parent page.
   */
-  assert( pPage->isInit==PAGE_ISINIT_FULL );
+  assert( pCur->iPage>0 );
+  assert( pPage->isInit );
   assert( sqlite3PagerIswriteable(pPage->pDbPage) || pPage->nOverflow==1 );
   pBt = pPage->pBt;
-  pParent = pPage->pParent;
+  pParent = pCur->apPage[pCur->iPage-1];
   assert( pParent );
   if( SQLITE_OK!=(rc = sqlite3PagerWrite(pParent->pDbPage)) ){
     return rc;
@@ -5092,7 +5002,7 @@ static int balance_nonroot(MemPage *pPage){
       pPage->intKey &&
       pPage->nOverflow==1 &&
       pPage->aOvfl[0].idx==pPage->nCell &&
-      pPage->pParent->pgno!=1 &&
+      pParent->pgno!=1 &&
       get4byte(&pParent->aData[pParent->hdrOffset+8])==pPage->pgno
   ){
     assert( pPage->intKey );
@@ -5100,7 +5010,7 @@ static int balance_nonroot(MemPage *pPage){
     ** TODO: Check the siblings to the left of pPage. It may be that
     ** they are not full and no new page is required.
     */
-    return balance_quick(pPage, pParent);
+    return balance_quick(pCur);
   }
 #endif
 
@@ -5125,7 +5035,7 @@ static int balance_nonroot(MemPage *pPage){
     assert( idx<pParent->nCell
              || get4byte(&pParent->aData[pParent->hdrOffset+8])==pgno );
   }else{
-    idx = pPage->idxParent;
+    idx = pCur->aiIdx[pCur->iPage-1];
   }
 
   /*
@@ -5133,7 +5043,7 @@ static int balance_nonroot(MemPage *pPage){
   ** directly to balance_cleanup at any moment.
   */
   nOld = nNew = 0;
-  sqlite3PagerRef(pParent->pDbPage);
+  /* sqlite3PagerRef(pParent->pDbPage); */
 
   /*
   ** Find sibling pages to pPage and the cells in pParent that divide
@@ -5161,9 +5071,9 @@ static int balance_nonroot(MemPage *pPage){
     }else{
       break;
     }
-    rc = getAndInitPage(pBt, pgnoOld[i], &apOld[i], pParent);
+    rc = getAndInitPage(pBt, pgnoOld[i], &apOld[i]);
     if( rc ) goto balance_cleanup;
-    apOld[i]->idxParent = k;
+    /* apOld[i]->idxParent = k; */
     apCopy[i] = 0;
     assert( i==nOld );
     nOld++;
@@ -5632,10 +5542,12 @@ static int balance_nonroot(MemPage *pPage){
   ** have been added to the freelist so it might no longer be initialized.
   ** But the parent page will always be initialized.
   */
-  assert( pParent->isInit==PAGE_ISINIT_FULL );
+  assert( pParent->isInit );
   sqlite3ScratchFree(apCell);
   apCell = 0;
-  rc = balance(pParent, 0);
+  releasePage(pPage);
+  pCur->iPage--;
+  rc = balance(pCur, 0);
   
   /*
   ** Cleanup before returning.
@@ -5650,7 +5562,7 @@ balance_cleanup:
     releasePage(apNew[i]);
   }
 
-  releasePage(pParent);
+  /* releasePage(pParent); */
   TRACE(("BALANCE: finished with %d: old=%d new=%d cells=%d\n",
           pPage->pgno, nOld, nNew, nCell));
 
@@ -5662,7 +5574,8 @@ balance_cleanup:
 ** page contains no cells.  This is an opportunity to make the tree
 ** shallower by one level.
 */
-static int balance_shallower(MemPage *pPage){
+static int balance_shallower(BtCursor *pCur){
+  MemPage *pPage;              /* Root page of B-Tree */
   MemPage *pChild;             /* The only child page of pPage */
   Pgno pgnoChild;              /* Page number for pChild */
   int rc = SQLITE_OK;          /* Return code from subprocedures */
@@ -5671,7 +5584,9 @@ static int balance_shallower(MemPage *pPage){
   u8 **apCell;                 /* All cells from pages being balanced */
   u16 *szCell;                 /* Local size of all cells */
 
-  assert( pPage->pParent==0 );
+  assert( pCur->iPage==0 );
+  pPage = pCur->apPage[0];
+
   assert( pPage->nCell==0 );
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   pBt = pPage->pBt;
@@ -5701,7 +5616,7 @@ static int balance_shallower(MemPage *pPage){
     rc = sqlite3BtreeGetPage(pPage->pBt, pgnoChild, &pChild, 0);
     if( rc ) goto end_shallow_balance;
     if( pPage->pgno==1 ){
-      rc = sqlite3BtreeInitPage(pChild, pPage);
+      rc = sqlite3BtreeInitPage(pChild);
       if( rc ) goto end_shallow_balance;
       assert( pChild->nOverflow==0 );
       if( pChild->nFree>=100 ){
@@ -5727,8 +5642,7 @@ static int balance_shallower(MemPage *pPage){
     }else{
       memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize);
       pPage->isInit = 0;
-      pPage->pParent = 0;
-      rc = sqlite3BtreeInitPage(pPage, 0);
+      rc = sqlite3BtreeInitPage(pPage);
       assert( rc==SQLITE_OK );
       freePage(pChild);
       TRACE(("BALANCE: transfer child %d into root %d\n",
@@ -5762,8 +5676,9 @@ end_shallow_balance:
 ** child.   Finally, call balance_internal() on the new child
 ** to cause it to split.
 */
-static int balance_deeper(MemPage *pPage){
+static int balance_deeper(BtCursor *pCur){
   int rc;             /* Return value from subprocedures */
+  MemPage *pPage;     /* Pointer to the root page */
   MemPage *pChild;    /* Pointer to a new child page */
   Pgno pgnoChild;     /* Page number of the new child page */
   BtShared *pBt;         /* The BTree */
@@ -5773,8 +5688,10 @@ static int balance_deeper(MemPage *pPage){
   int hdr;            /* Offset to page header in parent */
   int cbrk;           /* Offset to content of first cell in parent */
 
-  assert( pPage->pParent==0 );
-  assert( pPage->nOverflow>0 );
+  assert( pCur->iPage==0 );
+  assert( pCur->apPage[0]->nOverflow>0 );
+
+  pPage = pCur->apPage[0];
   pBt = pPage->pBt;
   assert( sqlite3_mutex_held(pBt->mutex) );
   rc = allocateBtreePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0);
@@ -5787,58 +5704,68 @@ static int balance_deeper(MemPage *pPage){
   cdata = pChild->aData;
   memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
   memcpy(&cdata[cbrk], &data[cbrk], usableSize-cbrk);
-  if( pChild->isInit==PAGE_ISINIT_FULL ) return SQLITE_CORRUPT;
-  rc = sqlite3BtreeInitPage(pChild, pPage);
-  if( rc ) goto balancedeeper_out;
-  memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0]));
-  pChild->nOverflow = pPage->nOverflow;
-  if( pChild->nOverflow ){
-    pChild->nFree = 0;
-  }
-  assert( pChild->nCell==pPage->nCell );
-  zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
-  put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
-  TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
-  if( ISAUTOVACUUM ){
-    int i;
-    rc = ptrmapPut(pBt, pChild->pgno, PTRMAP_BTREE, pPage->pgno);
-    if( rc ) goto balancedeeper_out;
-    for(i=0; i<pChild->nCell; i++){
-      rc = ptrmapPutOvfl(pChild, i);
-      if( rc!=SQLITE_OK ){
-        goto balancedeeper_out;
+  
+  rc = sqlite3BtreeInitPage(pChild);
+  if( rc==SQLITE_OK ){
+    int nCopy = pPage->nOverflow*sizeof(pPage->aOvfl[0]);
+    memcpy(pChild->aOvfl, pPage->aOvfl, nCopy);
+    pChild->nOverflow = pPage->nOverflow;
+    if( pChild->nOverflow ){
+      pChild->nFree = 0;
+    }
+    assert( pChild->nCell==pPage->nCell );
+    zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
+    put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
+    TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
+    if( ISAUTOVACUUM ){
+      int i;
+      rc = ptrmapPut(pBt, pChild->pgno, PTRMAP_BTREE, pPage->pgno);
+      for(i=0; rc==SQLITE_OK && i<pChild->nCell; i++){
+        rc = ptrmapPutOvfl(pChild, i);
+      }
+      if( rc==SQLITE_OK ){
+        rc = reparentChildPages(pChild, 1);
       }
     }
-    rc = reparentChildPages(pChild, 1);
   }
+
   if( rc==SQLITE_OK ){
-    rc = balance_nonroot(pChild);
+    pCur->iPage++;
+    pCur->apPage[1] = pChild;
+    rc = balance_nonroot(pCur);
+  }else{
+    releasePage(pChild);
   }
 
-balancedeeper_out:
-  releasePage(pChild);
   return rc;
 }
 
 /*
-** Decide if the page pPage needs to be balanced.  If balancing is
-** required, call the appropriate balancing routine.
+** The page that pCur currently points to has just been modified in
+** some way. This function figures out if this modification means the
+** tree needs to be balanced, and if so calls the appropriate balancing 
+** routine.
+** 
+** Parameter isInsert is true if a new cell was just inserted into the
+** page, or false otherwise.
 */
-static int balance(MemPage *pPage, int insert){
+static int balance(BtCursor *pCur, int isInsert){
   int rc = SQLITE_OK;
+  MemPage *pPage = pCur->apPage[pCur->iPage];
+
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
-  if( pPage->pParent==0 ){
+  if( pCur->iPage==0 ){
     rc = sqlite3PagerWrite(pPage->pDbPage);
     if( rc==SQLITE_OK && pPage->nOverflow>0 ){
-      rc = balance_deeper(pPage);
+      rc = balance_deeper(pCur);
     }
     if( rc==SQLITE_OK && pPage->nCell==0 ){
-      rc = balance_shallower(pPage);
+      rc = balance_shallower(pCur);
     }
   }else{
     if( pPage->nOverflow>0 || 
-        (!insert && pPage->nFree>pPage->pBt->usableSize*2/3) ){
-      rc = balance_nonroot(pPage);
+        (!isInsert && pPage->nFree>pPage->pBt->usableSize*2/3) ){
+      rc = balance_nonroot(pCur);
     }
   }
   return rc;
@@ -5932,6 +5859,7 @@ int sqlite3BtreeInsert(
   int rc;
   int loc;
   int szNew;
+  int idx;
   MemPage *pPage;
   Btree *p = pCur->pBtree;
   BtShared *pBt = p->pBt;
@@ -5964,13 +5892,13 @@ int sqlite3BtreeInsert(
     return rc;
   }
 
-  pPage = pCur->pPage;
+  pPage = pCur->apPage[pCur->iPage];
   assert( pPage->intKey || nKey>=0 );
   assert( pPage->leaf || !pPage->intKey );
   TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
           pCur->pgnoRoot, nKey, nData, pPage->pgno,
           loc==0 ? "overwrite" : "new entry"));
-  assert( pPage->isInit==PAGE_ISINIT_FULL );
+  assert( pPage->isInit );
   allocateTempSpace(pBt);
   newCell = pBt->pTmpSpace;
   if( newCell==0 ) return SQLITE_NOMEM;
@@ -5978,40 +5906,34 @@ int sqlite3BtreeInsert(
   if( rc ) goto end_insert;
   assert( szNew==cellSizePtr(pPage, newCell) );
   assert( szNew<=MX_CELL_SIZE(pBt) );
+  idx = pCur->aiIdx[pCur->iPage];
   if( loc==0 && CURSOR_VALID==pCur->eState ){
     u16 szOld;
-    assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+    assert( idx<pPage->nCell );
     rc = sqlite3PagerWrite(pPage->pDbPage);
     if( rc ){
       goto end_insert;
     }
-    oldCell = findCell(pPage, pCur->idx);
+    oldCell = findCell(pPage, idx);
     if( !pPage->leaf ){
       memcpy(newCell, oldCell, 4);
     }
     szOld = cellSizePtr(pPage, oldCell);
     rc = clearCell(pPage, oldCell);
     if( rc ) goto end_insert;
-    dropCell(pPage, pCur->idx, szOld);
+    dropCell(pPage, idx, szOld);
   }else if( loc<0 && pPage->nCell>0 ){
     assert( pPage->leaf );
-    pCur->idx++;
+    idx = ++pCur->aiIdx[pCur->iPage];
     pCur->info.nSize = 0;
     pCur->validNKey = 0;
   }else{
     assert( pPage->leaf );
   }
-  rc = insertCell(pPage, pCur->idx, newCell, szNew, 0, 0);
+  rc = insertCell(pPage, idx, newCell, szNew, 0, 0);
   if( rc!=SQLITE_OK ) goto end_insert;
-  rc = balance(pPage, 1);
+  rc = balance(pCur, 1);
   if( rc==SQLITE_OK ){
-    /* balance() may have messed up the chain of MemPage.pParent pointers.
-    ** To prevent moveToRoot() from trying to use them to locate the root
-    ** page of this table, release the reference to the current page before
-    ** calling it.
-    */
-    releasePage(pCur->pPage);
-    pCur->pPage = 0;
     moveToRoot(pCur);
   }
 end_insert:
@@ -6023,7 +5945,8 @@ end_insert:
 ** is left pointing at a random location.
 */
 int sqlite3BtreeDelete(BtCursor *pCur){
-  MemPage *pPage = pCur->pPage;
+  MemPage *pPage = pCur->apPage[pCur->iPage];
+  int idx;
   unsigned char *pCell;
   int rc;
   Pgno pgnoChild = 0;
@@ -6031,7 +5954,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   BtShared *pBt = p->pBt;
 
   assert( cursorHoldsMutex(pCur) );
-  assert( pPage->isInit==PAGE_ISINIT_FULL );
+  assert( pPage->isInit );
   if( pBt->inTransaction!=TRANS_WRITE ){
     /* Must start a transaction before doing a delete */
     rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
@@ -6041,7 +5964,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   if( pCur->eState==CURSOR_FAULT ){
     return pCur->skip;
   }
-  if( pCur->idx >= pPage->nCell ){
+  if( pCur->aiIdx[pCur->iPage]>=pPage->nCell ){
     return SQLITE_ERROR;  /* The cursor is not pointing to anything */
   }
   if( !pCur->wrFlag ){
@@ -6068,7 +5991,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   ** data. The clearCell() call frees any overflow pages associated with the
   ** cell. The cell itself is still intact.
   */
-  pCell = findCell(pPage, pCur->idx);
+  idx = pCur->aiIdx[pCur->iPage];
+  pCell = findCell(pPage, idx);
   if( !pPage->leaf ){
     pgnoChild = get4byte(pCell);
   }
@@ -6086,6 +6010,9 @@ int sqlite3BtreeDelete(BtCursor *pCur){
     ** to be a leaf so we can use it.
     */
     BtCursor leafCur;
+    MemPage *pLeafPage;
+    int iLeafIdx;
+
     unsigned char *pNext;
     int notUsed;
     unsigned char *tempCell = 0;
@@ -6093,15 +6020,17 @@ int sqlite3BtreeDelete(BtCursor *pCur){
     sqlite3BtreeGetTempCursor(pCur, &leafCur);
     rc = sqlite3BtreeNext(&leafCur, &notUsed);
     if( rc==SQLITE_OK ){
-      rc = sqlite3PagerWrite(leafCur.pPage->pDbPage);
+      pLeafPage = leafCur.apPage[leafCur.iPage];
+      iLeafIdx = leafCur.aiIdx[leafCur.iPage];
+      rc = sqlite3PagerWrite(pLeafPage->pDbPage);
     }
     if( rc==SQLITE_OK ){
       u16 szNext;
       TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
-         pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
-      dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
-      pNext = findCell(leafCur.pPage, leafCur.idx);
-      szNext = cellSizePtr(leafCur.pPage, pNext);
+         pCur->pgnoRoot, pPage->pgno, pLeafPage->pgno));
+      dropCell(pPage, idx, cellSizePtr(pPage, pCell));
+      pNext = findCell(pLeafPage, iLeafIdx);
+      szNext = cellSizePtr(pLeafPage, pNext);
       assert( MX_CELL_SIZE(pBt)>=szNext+4 );
       allocateTempSpace(pBt);
       tempCell = pBt->pTmpSpace;
@@ -6109,32 +6038,25 @@ int sqlite3BtreeDelete(BtCursor *pCur){
         rc = SQLITE_NOMEM;
       }
       if( rc==SQLITE_OK ){
-        rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell, 0);
+        rc = insertCell(pPage, idx, pNext-4, szNext+4, tempCell, 0);
       }
       if( rc==SQLITE_OK ){
-        put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
-        rc = balance(pPage, 0);
+        put4byte(findOverflowCell(pPage, idx), pgnoChild);
+        rc = balance(pCur, 0);
       }
       if( rc==SQLITE_OK ){
-        dropCell(leafCur.pPage, leafCur.idx, szNext);
-        rc = balance(leafCur.pPage, 0);
+        dropCell(pLeafPage, iLeafIdx, szNext);
+        rc = balance(&leafCur, 0);
       }
     }
     sqlite3BtreeReleaseTempCursor(&leafCur);
   }else{
     TRACE(("DELETE: table=%d delete from leaf %d\n",
        pCur->pgnoRoot, pPage->pgno));
-    dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
-    rc = balance(pPage, 0);
+    dropCell(pPage, idx, cellSizePtr(pPage, pCell));
+    rc = balance(pCur, 0);
   }
   if( rc==SQLITE_OK ){
-    /* balance() may have messed up the chain of MemPage.pParent pointers.
-    ** To prevent moveToRoot() from trying to use them to locate the root
-    ** page of this table, release the reference to the current page before
-    ** calling it.
-    */
-    releasePage(pCur->pPage);
-    pCur->pPage = 0;
     moveToRoot(pCur);
   }
   return rc;
@@ -6311,7 +6233,7 @@ static int clearDatabasePage(
     return SQLITE_CORRUPT_BKPT;
   }
 
-  rc = getAndInitPage(pBt, pgno, &pPage, pParent);
+  rc = getAndInitPage(pBt, pgno, &pPage);
   if( rc ) goto cleardatabasepage_out;
   for(i=0; i<pPage->nCell; i++){
     pCell = findCell(pPage, i);
@@ -6614,7 +6536,7 @@ int sqlite3BtreeFlags(BtCursor *pCur){
   */
   MemPage *pPage;
   restoreCursorPosition(pCur);
-  pPage = pCur->pPage;
+  pPage = pCur->apPage[pCur->iPage];
   assert( cursorHoldsMutex(pCur) );
   assert( pPage->pBt==pCur->pBt );
   return pPage ? pPage->aData[pPage->hdrOffset] : 0;
@@ -6830,7 +6752,7 @@ static int checkTreePage(
        "unable to get the page. error code=%d", rc);
     return 0;
   }
-  if( (rc = sqlite3BtreeInitPage(pPage, pParent))!=0 ){
+  if( (rc = sqlite3BtreeInitPage(pPage))!=0 ){
     checkAppendMsg(pCheck, zContext, 
                    "sqlite3BtreeInitPage() returns error code %d", rc);
     releasePage(pPage);
@@ -7461,7 +7383,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
   if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0) ){
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
-  if( pCsr->eState==CURSOR_INVALID || !pCsr->pPage->intKey ){
+  if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){
     return SQLITE_ERROR;
   }
 
index 2a7bf9c6b627377d8556df4c2c483bb48bd70efe..a10eb488b3cd56870174bc1391dfe1ed7d68ad82 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btreeInt.h,v 1.31 2008/09/18 17:34:44 danielk1977 Exp $
+** $Id: btreeInt.h,v 1.32 2008/09/29 11:49:48 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -281,7 +281,6 @@ struct MemPage {
   u16 maxLocal;        /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
   u16 minLocal;        /* Copy of BtShared.minLocal or BtShared.minLeaf */
   u16 cellOffset;      /* Index in aData of first cell pointer */
-  u16 idxParent;       /* Index in parent of this node */
   u16 nFree;           /* Number of free bytes on the page */
   u16 nCell;           /* Number of cells on this page, local and ovfl */
   u16 maskPage;        /* Mask for page offset */
@@ -293,21 +292,8 @@ struct MemPage {
   u8 *aData;           /* Pointer to disk image of the page data */
   DbPage *pDbPage;     /* Pager page handle */
   Pgno pgno;           /* Page number for this page */
-  MemPage *pParent;    /* The parent of this page.  NULL for root */
 };
 
-/*
-** Possible values for the MemPage.isInit variable. When a page is first
-** loaded or if the data stored in the MemPage struct is invalidated, 
-** MemPage.isInit is set to PAGE_ISINIT_NONE. If the MemPage structure
-** is fully initialized, then MemPage.isInit is set to PAGE_ISINIT_FULL.
-** MemPage.isInit is set to PAGE_ISINIT_DATA when the MemPage struct is
-** populated, but the MemPage.pParent variable is not necessarily correct.
-*/
-#define PAGE_ISINIT_NONE 0
-#define PAGE_ISINIT_DATA 1
-#define PAGE_ISINIT_FULL 2
-
 /*
 ** The in-memory image of a disk page has the auxiliary information appended
 ** to the end.  EXTRA_SIZE is the number of bytes of space needed to hold
@@ -426,6 +412,17 @@ struct CellInfo {
   u16 nSize;     /* Size of the cell content on the main b-tree page */
 };
 
+/*
+** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than
+** this will be declared corrupt. This value is calculated based on a
+** maximum database size of 2^31 pages a minimum fanout of 2 for a
+** root-node and 3 for all other internal nodes.
+**
+** If a tree that appears to be taller than this is encountered, it is
+** assumed that the database is corrupt.
+*/
+#define BTCURSOR_MAX_DEPTH 20
+
 /*
 ** A cursor is a pointer to a particular entry within a particular
 ** b-tree within a database file.
@@ -446,8 +443,6 @@ struct BtCursor {
   BtCursor *pNext, *pPrev;  /* Forms a linked list of all cursors */
   struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
   Pgno pgnoRoot;            /* The root page of this tree */
-  MemPage *pPage;           /* Page that contains the entry */
-  int idx;                  /* Index of the entry in pPage->aCell[] */
   CellInfo info;            /* A parse of the cell we are pointing at */
   u8 wrFlag;                /* True if writable */
   u8 atLast;                /* Cursor pointing to the last entry */
@@ -460,6 +455,10 @@ struct BtCursor {
   u8 isIncrblobHandle;      /* True if this cursor is an incr. io handle */
   Pgno *aOverflow;          /* Cache of overflow page locations */
 #endif
+
+  i16 iPage;                            /* Index of current page in apPage */
+  MemPage *apPage[BTCURSOR_MAX_DEPTH];  /* Pages from root to current page */
+  u16 aiIdx[BTCURSOR_MAX_DEPTH];        /* Current index in apPage[i] */
 };
 
 /*
@@ -629,11 +628,10 @@ struct IntegrityCk {
 ** Internal routines that should be accessed by the btree layer only.
 */
 int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int);
-int sqlite3BtreeInitPage(MemPage *pPage, MemPage *pParent);
+int sqlite3BtreeInitPage(MemPage *pPage);
 void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*);
 void sqlite3BtreeParseCell(MemPage*, int, CellInfo*);
 int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur);
 void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur);
 void sqlite3BtreeReleaseTempCursor(BtCursor *pCur);
-int sqlite3BtreeIsRootPage(MemPage *pPage);
 void sqlite3BtreeMoveToParent(BtCursor *pCur);
index 5011418447d9968979aa04757b382f22af3ecd38..d5dd891f6cd6c5f7636f5288dc81e6078819fa1c 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.495 2008/09/26 21:08:08 drh Exp $
+** @(#) $Id: pager.c,v 1.496 2008/09/29 11:49:48 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -1728,7 +1728,6 @@ int sqlite3PagerOpen(
   sqlite3_vfs *pVfs,       /* The virtual file system to use */
   Pager **ppPager,         /* Return the Pager structure here */
   const char *zFilename,   /* Name of the database file to open */
-  void (*xDesc)(DbPage*),  /* Page destructor function */
   int nExtra,              /* Extra bytes append to each in-memory page */
   int flags,               /* flags controlling this file */
   int vfsFlags             /* flags passed through to sqlite3_vfs.xOpen() */
@@ -1870,7 +1869,7 @@ int sqlite3PagerOpen(
     return ((rc==SQLITE_OK)?SQLITE_NOMEM:rc);
   }
   nExtra = FORCE_ALIGNMENT(nExtra);
-  sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, xDesc, 
+  sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
                     !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
 
   PAGERTRACE3("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename);
@@ -3875,6 +3874,13 @@ int sqlite3PagerRefcount(Pager *pPager){
   return sqlite3PcacheRefCount(pPager->pPCache);
 }
 
+/*
+** Return the number of references to the specified page.
+*/
+int sqlite3PagerPageRefcount(DbPage *pPage){
+  return sqlite3PcachePageRefcount(pPage);
+}
+
 #ifdef SQLITE_TEST
 /*
 ** This routine is used for testing and analysis only.
@@ -4176,7 +4182,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
 ** Return a pointer to the data for the specified page.
 */
 void *sqlite3PagerGetData(DbPage *pPg){
-  assert( pPg->nRef>0 );
+  assert( pPg->nRef>0 || pPg->pPager->memDb );
   return pPg->pData;
 }
 
index 41332d0201c93a132676f8331febe4df351630cd..accf049e9d3865f323add8a0f03afc88a3105b9b 100644 (file)
@@ -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.84 2008/09/26 21:08:08 drh Exp $
+** @(#) $Id: pager.h,v 1.85 2008/09/29 11:49:48 danielk1977 Exp $
 */
 
 #ifndef _PAGER_H_
@@ -71,7 +71,7 @@ typedef struct PgHdr DbPage;
 ** See source code comments for a detailed description of the following
 ** routines:
 */
-int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, void(*)(DbPage*), int,int,int);
+int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int);
 void sqlite3PagerSetBusyhandler(Pager*, BusyHandler *pBusyHandler);
 void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*));
 int sqlite3PagerSetPagesize(Pager*, u16*);
@@ -82,6 +82,7 @@ int sqlite3PagerClose(Pager *pPager);
 int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
 #define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
 DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
+int sqlite3PagerPageRefcount(DbPage*);
 int sqlite3PagerRef(DbPage*);
 int sqlite3PagerUnref(DbPage*);
 int sqlite3PagerWrite(DbPage*);
index d0dbb41c2e64c9ff416a1cf21e4fe0af03fe84fb..f55049eb58a441d5b7ef236841cc63ad52037701 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file implements that page cache.
 **
-** @(#) $Id: pcache.c,v 1.32 2008/09/24 09:12:47 danielk1977 Exp $
+** @(#) $Id: pcache.c,v 1.33 2008/09/29 11:49:48 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -43,7 +43,6 @@ struct PCache {
   int szPage;                         /* Size of every page in this cache */
   int szExtra;                        /* Size of extra space for each page */
   int bPurgeable;                     /* True if pages are on backing store */
-  void (*xDestroy)(PgHdr*);           /* Called when refcnt goes 1->0 */
   int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */
   void *pStress;                      /* Argument to xStress */
   /**********************************************************************
@@ -643,7 +642,6 @@ void sqlite3PcacheOpen(
   int szPage,                  /* Size of every page */
   int szExtra,                 /* Extra space associated with each page */
   int bPurgeable,              /* True if pages are on backing store */
-  void (*xDestroy)(PgHdr*),    /* Called to destroy a page */
   int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
   void *pStress,               /* Argument to xStress */
   PCache *p                    /* Preallocated space for the PCache */
@@ -653,7 +651,6 @@ void sqlite3PcacheOpen(
   p->szPage = szPage;
   p->szExtra = szExtra;
   p->bPurgeable = bPurgeable;
-  p->xDestroy = xDestroy;
   p->xStress = xStress;
   p->pStress = pStress;
   p->nMax = 100;
@@ -752,9 +749,6 @@ void sqlite3PcacheRelease(PgHdr *p){
   p->nRef--;
   if( p->nRef==0 ){
     PCache *pCache = p->pCache;
-    if( p->pCache->xDestroy ){
-      p->pCache->xDestroy(p);
-    }
     pCache->nRef--;
     if( (p->flags&PGHDR_DIRTY)==0 ){
       pCache->nPinned--;
@@ -1163,6 +1157,10 @@ int sqlite3PcacheRefCount(PCache *pCache){
   return pCache->nRef;
 }
 
+int sqlite3PcachePageRefcount(PgHdr *p){
+  return p->nRef;
+}
+
 /* 
 ** Return the total number of pages in the cache.
 */
index 73e6e26d40fc465c124ec492a67ae4c78c7dc030..1fadc32047cca5f1f113594149b26f2e1f07a067 100644 (file)
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the sqlite page cache
 ** subsystem. 
 **
-** @(#) $Id: pcache.h,v 1.11 2008/09/18 17:34:44 danielk1977 Exp $
+** @(#) $Id: pcache.h,v 1.12 2008/09/29 11:49:48 danielk1977 Exp $
 */
 
 #ifndef _PCACHE_H_
@@ -79,7 +79,6 @@ void sqlite3PcacheOpen(
   int szPage,                    /* Size of every page */
   int szExtra,                   /* Extra space associated with each page */
   int bPurgeable,                /* True if pages are on backing store */
-  void (*xDestroy)(PgHdr *),     /* Called to destroy a page */
   int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */
   void *pStress,                 /* Argument to xStress */
   PCache *pToInit                /* Preallocated space for the PCache */
@@ -143,6 +142,8 @@ int sqlite3PcacheRefCount(PCache*);
 /* Increment the reference count of an existing page */
 void sqlite3PcacheRef(PgHdr*);
 
+int sqlite3PcachePageRefcount(PgHdr*);
+
 /* Return the total number of pages stored in the cache */
 int sqlite3PcachePagecount(PCache*);
 
index 5188257d439a8aa57f81e598aca45f8fe8ccc4cc..083cc13a47046b9934f8386c24a26678c9747f13 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test2.c,v 1.61 2008/08/26 18:05:48 danielk1977 Exp $
+** $Id: test2.c,v 1.62 2008/09/29 11:49:48 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -78,7 +78,7 @@ static int pager_open(
     return TCL_ERROR;
   }
   if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
-  rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0, 0,
+  rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0,
       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
   if( rc!=SQLITE_OK ){
     Tcl_AppendResult(interp, errorName(rc), 0);
index cc1e1c92495222b39e371906d836af1941dd83bf..e29399321ecbb9df0917114f1af69e90335ea06e 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test_btree.c,v 1.7 2008/09/02 00:52:52 drh Exp $
+** $Id: test_btree.c,v 1.8 2008/09/29 11:49:48 danielk1977 Exp $
 */
 #include "btreeInt.h"
 #include <tcl.h>
@@ -52,11 +52,11 @@ void sqlite3BtreeCursorList(Btree *p){
   BtCursor *pCur;
   BtShared *pBt = p->pBt;
   for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
-    MemPage *pPage = pCur->pPage;
+    MemPage *pPage = pCur->apPage[pCur->iPage];
     char *zMode = pCur->wrFlag ? "rw" : "ro";
     sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
        pCur, pCur->pgnoRoot, zMode,
-       pPage ? pPage->pgno : 0, pCur->idx,
+       pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage],
        (pCur->eState==CURSOR_VALID) ? "" : " eof"
     );
   }
@@ -83,8 +83,9 @@ void sqlite3BtreeCursorList(Btree *p){
 ** This routine is used for testing and debugging only.
 */
 int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
+#if 0
   int cnt, idx;
-  MemPage *pPage = pCur->pPage;
+  MemPage *pPage = pCur->apPage[pCur->iPage];
   BtCursor tmpCur;
   int rc;
 
@@ -136,5 +137,6 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
     aResult[10] = 0;
   }
   sqlite3BtreeReleaseTempCursor(&tmpCur);
+#endif
   return SQLITE_OK;
 }
index b786b0bab6769a686e2af5e9f1b19213af858d4d..652f7844adf0ccdbb3d454a61e19d8b29ee9bb05 100644 (file)
@@ -13,7 +13,7 @@
 # This file implements tests to make sure SQLite does not crash or
 # segfault if it sees a corrupt database file.
 #
-# $Id: corrupt2.test,v 1.17 2008/09/10 11:28:38 danielk1977 Exp $
+# $Id: corrupt2.test,v 1.18 2008/09/29 11:49:48 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -225,8 +225,6 @@ do_test corrupt2-5.1 {
   }
   set result
 } {{*** in database main ***
-Page 10: sqlite3BtreeInitPage() returns error code 11
-On tree page 3 cell 1: Child page depth differs
 On tree page 2 cell 0: 2nd reference to page 10
 On tree page 2 cell 1: Child page depth differs
 Page 4 is never used}}