]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The BTree code compiles and links now, but it does not work yet. (CVS 226)
authordrh <drh@noemail.net>
Fri, 22 Jun 2001 19:15:00 +0000 (19:15 +0000)
committerdrh <drh@noemail.net>
Fri, 22 Jun 2001 19:15:00 +0000 (19:15 +0000)
FossilOrigin-Name: b31c49021c260a67b7848bc077b75a7146e31c71

Makefile.in
manifest
manifest.uuid
src/btree.c
src/btree.h
src/pager.c
src/pager.h
src/tclsqlite.c
src/test3.c

index a1362aa016947498bb81c985d69ba9beee1656fc..9c6a497b909fd2f241f8d49685539acdbe403ed1 100644 (file)
@@ -47,13 +47,14 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
 
 # Object files for the SQLite library.
 #
-LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
+LIBOBJ = btree.o build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
          main.o pager.o parse.o printf.o random.o select.o table.o \
          tokenize.o update.o util.o vdbe.o where.o tclsqlite.o
 
 # All of the source code files.
 #
 SRC = \
+  $(TOP)/src/btree.c \
   $(TOP)/src/build.c \
   $(TOP)/src/dbbe.c \
   $(TOP)/src/dbbe.h \
@@ -84,7 +85,8 @@ SRC = \
 #
 TESTSRC = \
   $(TOP)/src/test1.c \
-  $(TOP)/src/test2.c
+  $(TOP)/src/test2.c \
+  $(TOP)/src/test3.c
 
 # This is the default Makefile target.  The objects listed here
 # are what get build when you type just "make" with no arguments.
@@ -117,10 +119,15 @@ lemon:    $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
 HDR = \
    sqlite.h  \
    $(TOP)/src/sqliteInt.h  \
+   $(TOP)/src/btree.h \
    $(TOP)/src/dbbe.h  \
+   $(TOP)/src/pager.h \
    $(TOP)/src/vdbe.h  \
    parse.h
 
+btree.o:       $(TOP)/src/btree.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/btree.c
+
 build.o:       $(TOP)/src/build.c $(HDR)
        $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/build.c
 
@@ -201,7 +208,7 @@ tclsqlite:  $(TOP)/src/tclsqlite.c libsqlite.a
 
 testfixture:   $(TOP)/src/tclsqlite.c libsqlite.a $(TESTSRC)
        $(TCC) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o testfixture \
-               $(TESTSRC) $(TOP)/src/tclsqlite.c \
+               $(TESTSRC) $(TOP)/src/tclsqlite.c $(TOP)/src/btree.c \
                libsqlite.a $(LIBGDBM) $(LIBTCL)
 
 test:  testfixture sqlite
index 8c45637ac1aa668154c92a56cb405e9c0c1330a7..769448139622db44041dd8b86d4e93fa5c8b3720 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,7 +1,7 @@
-C All\sBTree\scode\sis\sin\splace.\sNow\swe\sjust\shave\sto\smake\sit\swork.\s(CVS\s225)
-D 2001-06-10T19:56:59
+C The\sBTree\scode\scompiles\sand\slinks\snow,\sbut\sit\sdoes\snot\swork\syet.\s(CVS\s226)
+D 2001-06-22T19:15:00
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
-F Makefile.in acef0f0275a5ca8e68bda165f7f05d810a207664
+F Makefile.in 65862a30703b070209b5f5e565d75cc870962b3c
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
 F VERSION 71874cb7e2a53c2bd22bb6affa7d223dd94a7a13
 F configure d2051345f49f7e48604423da26e086a745c86a47 x
@@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464
 F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
 F notes/notes3.txt 985bf688b59f1f52bfe6e4b1f896efdeffac1432
 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
-F src/btree.c 788e18f3c668732fc4f228fd24912bb5181b055f
-F src/btree.h f9adc22e8414402c176d71088e76afa89cc0d4b1
+F src/btree.c 0a2b66ce90f0bee87f0449235060529b96cc96e4
+F src/btree.h 40ae2c9b6d2ba8feb03461a589ccab9afc04ec29
 F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651
 F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af
 F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
@@ -31,8 +31,8 @@ F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7
 F src/expr.c c4c24c3af1eba094a816522eb0e085bed518ee16
 F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7
 F src/main.c 0a13c7a2beb8ce36aee43daf8c95989b200727a7
-F src/pager.c 5224dc4b7f678af2b7e9affb933eb1cee5e7977e
-F src/pager.h e527411d88e31085f07eba6776dc337b8b027921
+F src/pager.c 30c6f10a3c0cdfca3314c07d34375dbc19a48c2f
+F src/pager.h 724ac5a79b5fa704a1e1a87e421e421b3da9c1e4
 F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb
 F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
 F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
@@ -42,10 +42,10 @@ F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in 3e5906f72608f0fd4394dfbb1d7e8d35b8353677
 F src/sqliteInt.h 47845c60e2e196b5409d774936a56700b1611f00
 F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6
-F src/tclsqlite.c 1f2bf4691a6bd81fbff1856ae4a12db24d1265f7
+F src/tclsqlite.c af29a45cb4c2244a6fd032568a22d26516472b2c
 F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4
 F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a
-F src/test3.c a1868c55e03776f2e59f713247e77c734d8badfe
+F src/test3.c 405ea28287faeefc108ca362eca527731421e6bb
 F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf
 F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6
 F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc
@@ -107,7 +107,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
 F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
 F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
-P d1e211fad9d787a0fdbcd11fb364d6c592c07a05
-R 3aa8278a253756473c3d6ce9ea5ba81d
+P d4be4709ee32bab6e78104861ed4e02d153779aa
+R 0a720e1f17d26fb5cb5699a653f8a269
 U drh
-Z 55b535f0434199de5eef13cb7d2b84b8
+Z 4c2d9d4e753474964194e60669402b32
index 7659d1305d5530fda4aa390d828090cf5f278f74..536496be0ac95a725f0a4d8e117773e24471fd4d 100644 (file)
@@ -1 +1 @@
-d4be4709ee32bab6e78104861ed4e02d153779aa
\ No newline at end of file
+b31c49021c260a67b7848bc077b75a7146e31c71
\ No newline at end of file
index 7a0ff65f253a6c943d53cdd360e42961f0642f38..eee60492ba2dd71d2368b29ba88a761a6dcd71f6 100644 (file)
@@ -21,7 +21,7 @@
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: btree.c,v 1.12 2001/06/10 19:56:59 drh Exp $
+** $Id: btree.c,v 1.13 2001/06/22 19:15:00 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
 ** Change these typedefs when porting to new architectures.
 */
 typedef unsigned int uptr;
-typedef unsigned int u32;
+/*  typedef unsigned int u32; -- already defined in sqliteInt.h */
 typedef unsigned short int u16;
 typedef unsigned char u8;
 
+/*
+** This macro casts a pointer to an integer.  Useful for doing
+** pointer arithmetic.
+*/
+#define addr(X)  ((uptr)X)
+
 /*
 ** Forward declarations of structures used only in this file.
 */
@@ -99,12 +105,24 @@ typedef struct OverflowPage OverflowPage;
 
 /*
 ** This is a magic string that appears at the beginning of every
-** SQLite database in order to identify the fail as a real database.
+** SQLite database in order to identify the file as a real database.
 */
 static const char zMagicHeader[] = 
-   "** This file contains an SQLite 2.0 database **"
+   "** This file contains an SQLite 2.0 database **";
 #define MAGIC_SIZE (sizeof(zMagicHeader))
 
+/*
+** This is a magic integer also used to the integrety of the database
+** file.  This integer is used in addition to the string above so that
+** if the file is written on a little-endian architecture and read
+** on a big-endian architectures (or vice versa) we can detect the
+** problem.
+**
+** The number used was obtained at random and has no special
+** significance.
+*/
+#define MAGIC 0xdae37528
+
 /*
 ** The first page of the database file contains a magic header string
 ** to identify the file as an SQLite database file.  It also contains
@@ -121,7 +139,8 @@ static const char zMagicHeader[] =
 */
 struct PageOne {
   char zMagic[MAGIC_SIZE]; /* String that identifies the file as a database */
-  Pgno firstList;          /* First free page in a list of all free pages */
+  int iMagic;              /* Integer to verify correct byte order */
+  Pgno freeList;           /* First free page in a list of all free pages */
   int aMeta[SQLITE_N_BTREE_META];  /* User defined integers */
 };
 
@@ -156,7 +175,7 @@ struct PageHdr {
 ** the database page.
 **
 ** A definition of the complete Cell structure is given below.  The
-** header for the cell must be defined separately in order to do some
+** header for the cell must be defined first in order to do some
 ** of the sizing #defines that follow.
 */
 struct CellHdr {
@@ -164,7 +183,7 @@ struct CellHdr {
   u16 nKey;       /* Number of bytes in the key */
   u16 iNext;      /* Index in MemPage.u.aDisk[] of next cell in sorted order */
   u32 nData;      /* Number of bytes of data */
-}
+};
 
 /*
 ** The minimum size of a complete Cell.  The Cell must contain a header
@@ -179,8 +198,8 @@ struct CellHdr {
 #define MX_CELL ((SQLITE_PAGE_SIZE-sizeof(PageHdr))/MIN_CELL_SIZE)
 
 /*
-** The maximum amount of data (in bytes) that can be stored locally for a
-** database entry.  If the entry contains more data than this, the
+** The maximum amount of payload (in bytes) that can be stored locally for
+** database entry.  If the entry contains more data than this, the
 ** extra goes onto overflow pages.
 **
 ** This number is chosen so that at least 4 cells will fit on every page.
@@ -226,7 +245,7 @@ struct FreeBlk {
 
 /*
 ** When the key and data for a single entry in the BTree will not fit in
-** the MX_LOACAL_PAYLOAD bytes of space available on the database page,
+** the MX_LOCAL_PAYLOAD bytes of space available on the database page,
 ** then all extra bytes are written to a linked list of overflow pages.
 ** Each overflow page is an instance of the following structure.
 **
@@ -278,7 +297,7 @@ struct MemPage {
   int nCell;                     /* Number of entries on this page */
   int isOverfull;                /* Some apCell[] points outside u.aDisk[] */
   Cell *apCell[MX_CELL+2];       /* All data entires in sorted order */
-}
+};
 
 /*
 ** The in-memory image of a disk page has the auxiliary information appended
@@ -308,7 +327,7 @@ struct BtCursor {
   BtCursor *pNext, *pPrev;  /* Forms a linked list of all cursors */
   Pgno pgnoRoot;            /* The root page of this tree */
   MemPage *pPage;           /* Page that contains the entry */
-  u16 idx;                  /* Index of the entry in pPage->apCell[] */
+  int idx;                  /* Index of the entry in pPage->apCell[] */
   u8 bSkipNext;             /* sqliteBtreeNext() is no-op if true */
   u8 iMatch;                /* compare result from last sqliteBtreeMoveto() */
 };
@@ -317,7 +336,7 @@ struct BtCursor {
 ** Compute the total number of bytes that a Cell needs on the main
 ** database page.  The number returned includes the Cell header,
 ** local payload storage, and the pointer to overflow pages (if
-** applicable).  Additional spaced allocated on overflow pages
+** applicable).  Additional space allocated on overflow pages
 ** is NOT included in the value returned from this routine.
 */
 static int cellSize(Cell *pCell){
@@ -345,7 +364,13 @@ static void defragmentPage(MemPage *pPage){
   pPage->u.hdr.firstCell = pc;
   memcpy(newPage, pPage->u.aDisk, pc);
   for(i=0; i<pPage->nCell; i++){
-    Cell *pCell = &pPage->apCell[i];
+    Cell *pCell = (Cell*)&pPage->apCell[i];
+
+    /* This routine should never be called on an overfull page.  The
+    ** following asserts verify that constraint. */
+    assert( addr(pCell) > addr(pPage) );
+    assert( addr(pCell) < addr(pPage) + SQLITE_PAGE_SIZE );
+
     n = cellSize(pCell);
     pCell->h.iNext = i<pPage->nCell-1 ? pc + n : 0;
     memcpy(&newPage[pc], pCell, n);
@@ -354,7 +379,7 @@ static void defragmentPage(MemPage *pPage){
   }
   assert( pPage->nFree==SQLITE_PAGE_SIZE-pc );
   memcpy(pPage->u.aDisk, newPage, pc);
-  pFBlk = &pPage->u.aDisk[pc];
+  pFBlk = (FreeBlk*)&pPage->u.aDisk[pc];
   pFBlk->iSize = SQLITE_PAGE_SIZE - pc;
   pFBlk->iNext = 0;
   pPage->u.hdr.firstFree = pc;
@@ -378,12 +403,14 @@ static int allocateSpace(MemPage *pPage, int nByte){
   FreeBlk *p;
   u16 *pIdx;
   int start;
+  int cnt = 0;
 
   assert( nByte==ROUNDUP(nByte) );
   if( pPage->nFree<nByte || pPage->isOverfull ) return 0;
   pIdx = &pPage->u.hdr.firstFree;
   p = (FreeBlk*)&pPage->u.aDisk[*pIdx];
   while( p->iSize<nByte ){
+    assert( cnt++ < SQLITE_PAGE_SIZE/4 );
     if( p->iNext==0 ){
       defragmentPage(pPage);
       pIdx = &pPage->u.hdr.firstFree;
@@ -396,8 +423,9 @@ static int allocateSpace(MemPage *pPage, int nByte){
     start = *pIdx;
     *pIdx = p->iNext;
   }else{
+    FreeBlk *pNew;
     start = *pIdx;
-    FreeBlk *pNew = (FreeBlk*)&pPage->u.aDisk[start + nByte];
+    pNew = (FreeBlk*)&pPage->u.aDisk[start + nByte];
     pNew->iNext = p->iNext;
     pNew->iSize = p->iSize - nByte;
     *pIdx = start + nByte;
@@ -431,7 +459,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
     if( idx + pFBlk->iSize == start ){
       pFBlk->iSize += size;
       if( idx + pFBlk->iSize == pFBlk->iNext ){
-        pNext = (FreeBlk*)&pPage->u.aDisk[pFblk->iNext];
+        pNext = (FreeBlk*)&pPage->u.aDisk[pFBlk->iNext];
         pFBlk->iSize += pNext->iSize;
         pFBlk->iNext = pNext->iNext;
       }
@@ -489,8 +517,9 @@ static int initPage(MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
   freeSpace = SQLITE_PAGE_SIZE - sizeof(PageHdr);
   idx = pPage->u.hdr.firstCell;
   while( idx!=0 ){
-    if( idx>SQLITE_PAGE_SIZE-MN_CELL_SIZE ) goto page_format_error;
+    if( idx>SQLITE_PAGE_SIZE-MIN_CELL_SIZE ) goto page_format_error;
     if( idx<sizeof(PageHdr) ) goto page_format_error;
+    if( idx!=ROUNDUP(idx) ) goto page_format_error;
     pCell = (Cell*)&pPage->u.aDisk[idx];
     sz = cellSize(pCell);
     if( idx+sz > SQLITE_PAGE_SIZE ) goto page_format_error;
@@ -534,6 +563,9 @@ static void zeroPage(MemPage *pPage){
   pFBlk = (FreeBlk*)&pHdr[1];
   pFBlk->iNext = 0;
   pFBlk->iSize = SQLITE_PAGE_SIZE - sizeof(*pHdr);
+  pPage->nFree = pFBlk->iSize;
+  pPage->nCell = 0;
+  pPage->isOverfull = 0;
 }
 
 /*
@@ -559,13 +591,14 @@ static void pageDestructor(void *pData){
 */
 int sqliteBtreeOpen(const char *zFilename, int mode, Btree **ppBtree){
   Btree *pBt;
+  int rc;
 
   pBt = sqliteMalloc( sizeof(*pBt) );
   if( pBt==0 ){
-    **ppBtree = 0;
+    *ppBtree = 0;
     return SQLITE_NOMEM;
   }
-  rc = sqlitepager_open(&pBt->pPager, zFilename, 100, EXTRA_SPACE);
+  rc = sqlitepager_open(&pBt->pPager, zFilename, 100, EXTRA_SIZE);
   if( rc!=SQLITE_OK ){
     if( pBt->pPager ) sqlitepager_close(pBt->pPager);
     sqliteFree(pBt);
@@ -604,7 +637,7 @@ int sqliteBtreeClose(Btree *pBt){
 static int lockBtree(Btree *pBt){
   int rc;
   if( pBt->page1 ) return SQLITE_OK;
-  rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1);
+  rc = sqlitepager_get(pBt->pPager, 1, (void**)&pBt->page1);
   if( rc!=SQLITE_OK ) return rc;
 
   /* Do some checking to help insure the file we opened really is
@@ -612,7 +645,7 @@ static int lockBtree(Btree *pBt){
   */
   if( sqlitepager_pagecount(pBt->pPager)>0 ){
     PageOne *pP1 = pBt->page1;
-    if( strcmp(pP1->zMagic1,zMagicHeader)!=0 ){
+    if( strcmp(pP1->zMagic,zMagicHeader)!=0 || pP1->iMagic!=MAGIC ){
       rc = SQLITE_CORRUPT;
       goto page1_init_failed;
     }
@@ -626,16 +659,18 @@ page1_init_failed:
 }
 
 /*
-** Create a new database by initializing the first two pages.
+** Create a new database by initializing the first two pages of the
+** file.
 */
 static int newDatabase(Btree *pBt){
   MemPage *pRoot;
   PageOne *pP1;
+  int rc;
   if( sqlitepager_pagecount(pBt->pPager)>0 ) return SQLITE_OK;
   pP1 = pBt->page1;
   rc = sqlitepager_write(pBt->page1);
   if( rc ) return rc;
-  rc = sqlitepager_get(pBt->pPager, 2, &pRoot);
+  rc = sqlitepager_get(pBt->pPager, 2, (void**)&pRoot);
   if( rc ) return rc;
   rc = sqlitepager_write(pRoot);
   if( rc ){
@@ -643,6 +678,7 @@ static int newDatabase(Btree *pBt){
     return rc;
   }
   strcpy(pP1->zMagic, zMagicHeader);
+  pP1->iMagic = MAGIC;
   zeroPage(pRoot);
   sqlitepager_unref(pRoot);
   return SQLITE_OK;
@@ -664,17 +700,20 @@ static int newDatabase(Btree *pBt){
 */
 int sqliteBtreeBeginTrans(Btree *pBt){
   int rc;
-  PageOne *pP1;
   if( pBt->inTrans ) return SQLITE_ERROR;
   if( pBt->page1==0 ){
     rc = lockBtree(pBt);
-    if( rc!=SQLITE_OK ) return rc;
+    if( rc!=SQLITE_OK ){
+      return rc;
+    }
   }
   rc = sqlitepager_write(pBt->page1);
-  if( rc==SQLITE_OK ){
-    pBt->inTrans = 1;
+  if( rc!=SQLITE_OK ){
+    return rc;
   }
-  return newDatabase(pBt);
+  pBt->inTrans = 1;
+  rc = newDatabase(pBt);
+  return rc;
 }
 
 /*
@@ -734,7 +773,7 @@ int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){
     goto create_cursor_exception;
   }
   pCur->pgnoRoot = (Pgno)iTable;
-  rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, &pCur->pPage);
+  rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage);
   if( rc!=SQLITE_OK ){
     goto create_cursor_exception;
   }
@@ -759,7 +798,7 @@ create_cursor_exception:
     if( pCur->pPage ) sqlitepager_unref(pCur->pPage);
     sqliteFree(pCur);
   }
-  unlinkBtree(pBt);
+  unlockBtree(pBt);
   return rc;
 }
 
@@ -769,7 +808,6 @@ create_cursor_exception:
 */
 int sqliteBtreeCloseCursor(BtCursor *pCur){
   Btree *pBt = pCur->pBt;
-  int i;
   if( pCur->pPrev ){
     pCur->pPrev->pNext = pCur->pNext;
   }else{
@@ -781,6 +819,7 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){
   sqlitepager_unref(pCur->pPage);
   unlockBtree(pBt);
   sqliteFree(pCur);
+  return SQLITE_OK;
 }
 
 /*
@@ -819,7 +858,7 @@ int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
     *pSize = 0;
   }else{
     pCell = pPage->apCell[pCur->idx];
-    *psize = pCell->h.nKey;
+    *pSize = pCell->h.nKey;
   }
   return SQLITE_OK;
 }
@@ -835,9 +874,10 @@ int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
 static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
   char *aPayload;
   Pgno nextPage;
+  int rc;
   assert( pCur!=0 && pCur->pPage!=0 );
-  assert( pCur->idx>=0 && pCur->idx<pCur->nCell );
-  aPayload = pCur->pPage->apCell[pCur->idx].aPayload;
+  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+  aPayload = pCur->pPage->apCell[pCur->idx]->aPayload;
   if( offset<MX_LOCAL_PAYLOAD ){
     int a = amt;
     if( a+offset>MX_LOCAL_PAYLOAD ){
@@ -852,11 +892,11 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
     amt -= a;
   }
   if( amt>0 ){
-    nextPage = pCur->pPage->apCell[pCur->idx].ovfl;
+    nextPage = pCur->pPage->apCell[pCur->idx]->ovfl;
   }
   while( amt>0 && nextPage ){
     OverflowPage *pOvfl;
-    rc = sqlitepager_get(pCur->pBt->pPager, nextPage, &pOvfl);
+    rc = sqlitepager_get(pCur->pBt->pPager, nextPage, (void**)&pOvfl);
     if( rc!=0 ){
       return rc;
     }
@@ -964,7 +1004,7 @@ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){
 static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
   Pgno nextPage;
   int nKey = nKeyOrig;
-  int n;
+  int n, c, rc;
   Cell *pCell;
 
   assert( pCur->pPage );
@@ -990,7 +1030,7 @@ static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
     if( nextPage==0 ){
       return SQLITE_CORRUPT;
     }
-    rc = sqlitepager_get(pCur->pBt->pPager, nextPage, &pOvfl);
+    rc = sqlitepager_get(pCur->pBt->pPager, nextPage, (void**)&pOvfl);
     if( rc ){
       return rc;
     }
@@ -1020,7 +1060,7 @@ static int moveToChild(BtCursor *pCur, int newPgno){
   int rc;
   MemPage *pNewPage;
 
-  rc = sqlitepager_get(pCur->pBt->pPager, newPgno, &pNewPage);
+  rc = sqlitepager_get(pCur->pBt->pPager, newPgno, (void**)&pNewPage);
   if( rc ){
     return rc;
   }
@@ -1042,16 +1082,16 @@ static int moveToChild(BtCursor *pCur, int newPgno){
 static int moveToParent(BtCursor *pCur){
   Pgno oldPgno;
   MemPage *pParent;
-
+  int i;
   pParent = pCur->pPage->pParent;
   if( pParent==0 ) return SQLITE_INTERNAL;
   oldPgno = sqlitepager_pagenumber(pCur->pPage);
   sqlitepager_ref(pParent);
   sqlitepager_unref(pCur->pPage);
   pCur->pPage = pParent;
-  pCur->idx = pPage->nCell;
-  for(i=0; i<pPage->nCell; i++){
-    if( pPage->apCell[i].h.leftChild==oldPgno ){
+  pCur->idx = pParent->nCell;
+  for(i=0; i<pParent->nCell; i++){
+    if( pParent->apCell[i]->h.leftChild==oldPgno ){
       pCur->idx = i;
       break;
     }
@@ -1066,7 +1106,7 @@ static int moveToRoot(BtCursor *pCur){
   MemPage *pNew;
   int rc;
 
-  rc = sqlitepager_get(pCur->pBt->pPager, pCur->pgnoRoot, &pNew);
+  rc = sqlitepager_get(pCur->pBt->pPager, pCur->pgnoRoot, (void**)&pNew);
   if( rc ) return rc;
   sqlitepager_unref(pCur->pPage);
   pCur->pPage = pNew;
@@ -1170,8 +1210,8 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
   }
   pCur->idx++;
   if( pCur->idx>=pCur->pPage->nCell ){
-    if( pPage->u.hdr.rightChild ){
-      rc = moveToChild(pCur, pPage->u.hdr.rightChild);
+    if( pCur->pPage->u.hdr.rightChild ){
+      rc = moveToChild(pCur, pCur->pPage->u.hdr.rightChild);
       if( rc ) return rc;
       rc = moveToLeftmost(pCur);
       if( rc ) return rc;
@@ -1179,7 +1219,7 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
       return SQLITE_OK;
     }
     do{
-      if( pCur->pParent==0 ){
+      if( pCur->pPage->pParent==0 ){
         if( pRes ) *pRes = 1;
         return SQLITE_OK;
       }
@@ -1209,12 +1249,13 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
 */
 static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
   PageOne *pPage1 = pBt->page1;
+  int rc;
   if( pPage1->freeList ){
     OverflowPage *pOvfl;
     rc = sqlitepager_write(pPage1);
     if( rc ) return rc;
     *pPgno = pPage1->freeList;
-    rc = sqlitepager_get(pBt->pPager, pPage1->freeList, &pOvfl);
+    rc = sqlitepager_get(pBt->pPager, pPage1->freeList, (void**)&pOvfl);
     if( rc ) return rc;
     rc = sqlitepager_write(pOvfl);
     if( rc ){
@@ -1225,7 +1266,7 @@ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
     *ppPage = (MemPage*)pOvfl;
   }else{
     *pPgno = sqlitepager_pagecount(pBt->pPager);
-    rc = sqlitepager_get(pBt->pPager, *pPgno, ppPage);
+    rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage);
     if( rc ) return rc;
     rc = sqlitepager_write(*ppPage);
   }
@@ -1255,7 +1296,7 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
   }
   if( pOvfl==0 ){
     assert( pgno>0 );
-    rc = sqlitepager_get(pBt->pPager, pgno, &pOvfl);
+    rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pOvfl);
     if( rc ) return rc;
     needOvflUnref = 1;
   }
@@ -1267,8 +1308,8 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
   pOvfl->iNext = pPage1->freeList;
   pPage1->freeList = pgno;
   memset(pOvfl->aPayload, 0, OVERFLOW_SIZE);
-  pPage->isInit = 0;
-  assert( pPage->pParent==0 );
+  ((MemPage*)pPage)->isInit = 0;
+  assert( ((MemPage*)pPage)->pParent==0 );
   rc = sqlitepager_unref(pOvfl);
   return rc;
 }
@@ -1289,7 +1330,7 @@ static int clearCell(Btree *pBt, Cell *pCell){
   ovfl = pCell->ovfl;
   pCell->ovfl = 0;
   while( ovfl ){
-    rc = sqlitepager_get(pPager, ovfl, &pOvfl);
+    rc = sqlitepager_get(pPager, ovfl, (void**)&pOvfl);
     if( rc ) return rc;
     nextOvfl = pOvfl->iNext;
     rc = freePage(pBt, pOvfl, ovfl);
@@ -1310,10 +1351,10 @@ static int fillInCell(
   void *pKey, int nKey,    /* The key */
   void *pData,int nData    /* The data */
 ){
-  int OverflowPage *pOvfl;
+  OverflowPage *pOvfl;
   Pgno *pNext;
   int spaceLeft;
-  int n;
+  int n, rc;
   int nPayload;
   char *pPayload;
   char *pSpace;
@@ -1331,7 +1372,7 @@ static int fillInCell(
   nPayload = nKey;
   while( nPayload>0 ){
     if( spaceLeft==0 ){
-      rc = allocatePage(pBt, &pOvfl, pNext);
+      rc = allocatePage(pBt, (MemPage**)&pOvfl, pNext);
       if( rc ){
         *pNext = 0;
         clearCell(pBt, pCell);
@@ -1339,7 +1380,7 @@ static int fillInCell(
       }
       spaceLeft = OVERFLOW_SIZE;
       pSpace = pOvfl->aPayload;
-      pNextPg = &pOvfl->iNext;
+      pNext = &pOvfl->iNext;
     }
     n = nPayload;
     if( n>spaceLeft ) n = spaceLeft;
@@ -1383,10 +1424,10 @@ static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent){
 ** This routine gets called after you memcpy() one page into
 ** another.
 */
-static void reparentChildPages(Pager *pPager, Page *pPage){
+static void reparentChildPages(Pager *pPager, MemPage *pPage){
   int i;
   for(i=0; i<pPage->nCell; i++){
-    reparentPage(pPager, pPage->apCell[i]->leftChild, pPage);
+    reparentPage(pPager, pPage->apCell[i]->h.leftChild, pPage);
   }
   reparentPage(pPager, pPage->u.hdr.rightChild, pPage);
 }
@@ -1400,16 +1441,16 @@ static void reparentChildPages(Pager *pPager, Page *pPage){
 ** "sz" must be the number of bytes in the cell.
 **
 ** Do not bother maintaining the integrity of the linked list of Cells.
-** Only pPage->apCell[] is important.  The relinkCellList() routine
-** will be called soon after this routine in order to rebuild the
-** linked list.
+** Only the pPage->apCell[] array is important.  The relinkCellList() 
+** routine will be called soon after this routine in order to rebuild 
+** the linked list.
 */
-static void dropCell(MemPage *pPage, int i, int sz){
+static void dropCell(MemPage *pPage, int idx, int sz){
   int j;
-  assert( i>=0 && i<pPage->nCell );
-  assert( sz==cellSize(pPage->apCell[i]);
+  assert( idx>=0 && idx<pPage->nCell );
+  assert( sz==cellSize(pPage->apCell[idx]) );
   freeSpace(pPage, idx, sz);
-  for(j=i, j<pPage->nCell-2; j++){
+  for(j=idx; j<pPage->nCell-2; j++){
     pPage->apCell[j] = pPage->apCell[j+1];
   }
   pPage->nCell--;
@@ -1424,9 +1465,9 @@ static void dropCell(MemPage *pPage, int i, int sz){
 ** and set pPage->isOverfull.  
 **
 ** Do not bother maintaining the integrity of the linked list of Cells.
-** Only pPage->apCell[] is important.  The relinkCellList() routine
-** will be called soon after this routine in order to rebuild the
-** linked list.
+** Only the pPage->apCell[] array is important.  The relinkCellList() 
+** routine will be called soon after this routine in order to rebuild 
+** the linked list.
 */
 static void insertCell(MemPage *pPage, int i, Cell *pCell, int sz){
   int idx, j;
@@ -1442,22 +1483,23 @@ static void insertCell(MemPage *pPage, int i, Cell *pCell, int sz){
     pPage->apCell[i] = pCell;
   }else{
     memcpy(&pPage->u.aDisk[idx], pCell, sz);
-    pPage->apCell[i] = (Cell*)&pPage->u.aDisk[idx]);
+    pPage->apCell[i] = (Cell*)&pPage->u.aDisk[idx];
   }
 }
 
 /*
 ** Rebuild the linked list of cells on a page so that the cells
-** occur in the order specified by pPage->apCell[].  Invoke this
-** routine once to repair damage after one or more invocations
-** of either insertCell() or dropCell().
+** occur in the order specified by the pPage->apCell[] array.  
+** Invoke this routine once to repair damage after one or more
+** invocations of either insertCell() or dropCell().
 */
 static void relinkCellList(MemPage *pPage){
   int i;
   u16 *pIdx;
   pIdx = &pPage->u.hdr.firstCell;
   for(i=0; i<pPage->nCell; i++){
-    int idx = ((uptr)pPage->apCell[i]) - (uptr)pPage;
+    int idx = addr(pPage->apCell[i]) - addr(pPage);
+    assert( idx>0 && idx<SQLITE_PAGE_SIZE );
     *pIdx = idx;
     pIdx = &pPage->apCell[i]->h.iNext;
   }
@@ -1479,12 +1521,12 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
   pTo->nCell = pFrom->nCell;
   pTo->nFree = pFrom->nFree;
   pTo->isOverfull = pFrom->isOverfull;
-  to = (unsigned int)pTo;
-  from = (unsigned int)pFrom;
+  to = addr(pTo);
+  from = addr(pFrom);
   for(i=0; i<pTo->nCell; i++){
-    uptr addr = (uptr)(pFrom->apCell[i]);
-    if( addr>from && addr<from+SQLITE_PAGE_SIZE ){
-      *((uptr*)&pTo->apCell[i]) = addr + to - from;
+    uptr x = addr(pFrom->apCell[i]);
+    if( x>from && x<from+SQLITE_PAGE_SIZE ){
+      *((uptr*)&pTo->apCell[i]) = x + to - from;
     }
   }
 }
@@ -1499,8 +1541,9 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
 ** child of root) then all available siblings participate in the balancing.
 **
 ** The number of siblings of pPage might be increased or decreased by
-** one in order to keep all pages between 2/3 and completely full.  If
-** pPage is the root page, then the depth of the tree might be increased
+** one in an effort to keep pages between 66% and 100% full. The root page
+** is special and is allowed to be less than 66% full. If pPage is 
+** the root page, then the depth of the tree might be increased
 ** or decreased by one, as necessary, to keep the root page from being
 ** overfull or empty.
 **
@@ -1511,13 +1554,19 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
 ** routines left behind.
 **
 ** pCur is left pointing to the same cell as when this routine was called
-** event if that cell gets moved to a different page.  pCur may be NULL.
+** even if that cell gets moved to a different page.  pCur may be NULL.
+** Set the pCur parameter to NULL if you do not care about keeping track
+** of a cell as that will save this routine the work of keeping track of it.
 **
 ** Note that when this routine is called, some of the Cells on pPage
 ** might not actually be stored in pPage->u.aDisk[].  This can happen
 ** if the page is overfull.  Part of the job of this routine is to
 ** make sure all Cells for pPage once again fit in pPage->u.aDisk[].
 **
+** In the course of balancing the siblings of pPage, the parent of pPage
+** might become overfull or underfull.  If that happens, then this routine
+** is called recursively on the parent.
+**
 ** If this routine fails for any reason, it means the database may have
 ** been left in a corrupted state and should be rolled back.
 */
@@ -1532,7 +1581,6 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
   int nCell;                   /* Number of cells in apCell[] */
   int nOld;                    /* Number of pages in apOld[] */
   int nNew;                    /* Number of pages in apNew[] */
-  int perPage;                 /* Approximate number of bytes per page */
   int nDiv;                    /* Number of cells in apDiv[] */
   int i, j, k;                 /* Loop counters */
   int idx;                     /* Index of pPage in pParent->apCell[] */
@@ -1541,6 +1589,8 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
   int iCur;                    /* apCell[iCur] is the cell of the cursor */
   int usedPerPage;             /* Memory needed for each page */
   int freePerPage;             /* Average free space per page */
+  int totalSize;               /* Total bytes for all cells */
+  Pgno pgno;                   /* Page number */
   Cell *apCell[MX_CELL*3+5];   /* All cells from pages being balanceed */
   int szCell[MX_CELL*3+5];     /* Local size of all cells */
   Cell aTemp[2];               /* Temporary holding area for apDiv[] */
@@ -1563,7 +1613,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
   pParent = pPage->pParent;
   if( pParent==0 ){
     Pgno pgnoChild;
-    Page *pChild;
+    MemPage *pChild;
     if( pPage->nCell==0 ){
       if( pPage->u.hdr.rightChild ){
         /*
@@ -1574,7 +1624,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
         rc = sqlitepager_write(pPage);
         if( rc ) return rc;
         pgnoChild = pPage->u.hdr.rightChild;
-        rc = sqlitepager_get(pBt, pgnoChild, &pChild);
+        rc = sqlitepager_get(pBt->pPager, pgnoChild, (void**)&pChild);
         if( rc ) return rc;
         memcpy(pPage, pChild, SQLITE_PAGE_SIZE);
         pPage->isInit = 0;
@@ -1668,11 +1718,11 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
       nDiv++;
       pgnoOld[i] = apDiv[i]->h.leftChild;
     }else if( k==pParent->nCell ){
-      pgnoOld[i] = pParent->rightChild;
+      pgnoOld[i] = pParent->u.hdr.rightChild;
     }else{
       break;
     }
-    rc = sqlitepager_get(pBt, pgnoOld[i], &apOld[i]);
+    rc = sqlitepager_get(pBt->pPager, pgnoOld[i], (void**)&apOld[i]);
     if( rc ) goto balance_cleanup;
     nOld++;
   }
@@ -1719,7 +1769,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
     }
     if( i<nOld-1 ){
       szCell[nCell] = cellSize(apDiv[i]);
-      memcpy(aTemp[i], apDiv[i], szCell[nCell]);
+      memcpy(&aTemp[i], apDiv[i], szCell[nCell]);
       apCell[nCell] = &aTemp[i];
       dropCell(pParent, nxDiv, szCell[nCell]);
       assert( apCell[nCell]->h.leftChild==pgnoOld[i] );
@@ -1878,6 +1928,7 @@ int sqliteBtreeDelete(BtCursor *pCur){
   MemPage *pPage = pCur->pPage;
   Cell *pCell;
   int rc;
+  Pgno pgnoChild;
 
   if( !pCur->pBt->inTrans ){
     return SQLITE_ERROR;  /* Must start a transaction first */
@@ -1889,13 +1940,13 @@ int sqliteBtreeDelete(BtCursor *pCur){
   if( rc ) return rc;
   pCell = pPage->apCell[pCur->idx];
   pgnoChild = pCell->h.leftChild;
-  clearCell(pCell);
+  clearCell(pCur->pBt, pCell);
   dropCell(pPage, pCur->idx, cellSize(pCell));
   if( pgnoChild ){
     /*
     ** If the entry we just deleted is not a leaf, then we've left a
-    ** whole in an internal page.  We have to fill the whole by moving
-    ** in a page from a leaf.  The next Cell after the one just deleted
+    ** hole in an internal page.  We have to fill the hole by moving
+    ** in a cell from a leaf.  The next Cell after the one just deleted
     ** is guaranteed to exist and to be a leaf so we can use it.
     */
     BtCursor leafCur;
@@ -1906,15 +1957,16 @@ int sqliteBtreeDelete(BtCursor *pCur){
     if( rc!=SQLITE_OK ){
       return SQLITE_CORRUPT;
     }
-    pNext = leafCur.pPage->apCell[leafCur.idx]
+    pNext = leafCur.pPage->apCell[leafCur.idx];
     szNext = cellSize(pNext);
+    pNext->h.leftChild = pgnoChild;
     insertCell(pPage, pCur->idx, pNext, szNext);
     rc = balance(pCur->pBt, pPage, pCur);
     if( rc ) return rc;
     pCur->bSkipNext = 1;
     dropCell(leafCur.pPage, leafCur.idx, szNext);
     rc = balance(pCur->pBt, leafCur.pPage, 0);
-    releaseTempCur(&leafCur);
+    releaseTempCursor(&leafCur);
   }else{
     rc = balance(pCur->pBt, pPage, pCur);
     pCur->bSkipNext = 1;
@@ -1949,11 +2001,10 @@ int sqliteBtreeCreateTable(Btree *pBt, int *piTable){
 static int clearDatabasePage(Btree *pBt, Pgno pgno){
   MemPage *pPage;
   int rc;
-  int i;
   Cell *pCell;
   int idx;
 
-  rc = sqlitepager_get(pBt->pPager, pgno, &pPage);
+  rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pPage);
   if( rc ) return rc;
   idx = pPage->u.hdr.firstCell;
   while( idx>0 ){
@@ -1963,9 +2014,11 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno){
       rc = clearDatabasePage(pBt, pCell->h.leftChild);
       if( rc ) return rc;
     }
-    rc = clearCell(pCell);
+    rc = clearCell(pBt, pCell);
     if( rc ) return rc;
   }
+  rc = clearDatabasePage(pBt, pPage->u.hdr.rightChild);
+  if( rc ) return rc;
   return freePage(pBt, pPage, pgno);
 }
 
@@ -1980,8 +2033,8 @@ int sqliteBtreeClearTable(Btree *pBt, int iTable){
   rc = clearDatabasePage(pBt, (Pgno)iTable);
   if( rc ){
     sqliteBtreeRollback(pBt);
-    return rc;
   }
+  return rc;
 }
 
 /*
@@ -1995,7 +2048,7 @@ int sqliteBtreeDropTable(Btree *pBt, int iTable){
   if( !pBt->inTrans ){
     return SQLITE_ERROR;  /* Must start a transaction first */
   }
-  rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, &pPage);
+  rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage);
   if( rc==SQLITE_OK ){
     rc = sqliteBtreeClearTable(pBt, iTable);
   }
@@ -2013,7 +2066,7 @@ int sqliteBtreeGetMeta(Btree *pBt, int *aMeta){
   PageOne *pP1;
   int rc;
 
-  rc = sqlitepager_get(pBt->pPager, 1, &pP1);
+  rc = sqlitepager_get(pBt->pPager, 1, (void**)&pP1);
   if( rc ) return rc;
   memcpy(aMeta, pP1->aMeta, sizeof(pP1->aMeta));
   sqlitepager_unref(pP1);
@@ -2035,3 +2088,74 @@ int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){
   memcpy(pP1->aMeta, aMeta, sizeof(pP1->aMeta));
   return SQLITE_OK;
 }
+
+#ifdef SQLITE_TEST
+/*
+** Print a disassembly of the given page on standard output.  This routine
+** is used for debugging and testing only.
+*/
+int sqliteBtreePageDump(Btree *pBt, int pgno){
+  int rc;
+  MemPage *pPage;
+  int i, j;
+  int nFree;
+  u16 idx;
+  char range[20];
+  unsigned char payload[20];
+  rc = sqlitepager_get(pBt->pPager, (Pgno)pgno, (void**)&pPage);
+  if( rc ){
+    return rc;
+  }
+  i = 0;
+  idx = pPage->u.hdr.firstCell;
+  while( idx>0 && idx<=SQLITE_PAGE_SIZE-MIN_CELL_SIZE ){
+    Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
+    int sz = cellSize(pCell);
+    sprintf(range,"%d..%d", idx, idx+sz-1);
+    if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1;
+    memcpy(payload, pCell->aPayload, sz);
+    for(j=0; j<sz; j++){
+      if( payload[j]<0x20 || payload[j]>0x7f ) payload[j] = '.';
+    }
+    payload[sz] = 0;
+    printf(
+      "cell %2d: i=%-10s chld=%-4d nk=%-3d nd=%-3d payload=%s\n",
+      i, range, (int)pCell->h.leftChild, pCell->h.nKey, pCell->h.nData,
+      pCell->aPayload
+    );
+    idx = pCell->h.iNext;
+  }
+  if( idx!=0 ){
+    printf("ERROR: next cell index out of range: %d\n", idx);
+  }
+  printf("right_child: %d\n", pPage->u.hdr.rightChild);
+  nFree = 0;
+  i = 0;
+  idx = pPage->u.hdr.firstFree;
+  while( idx>0 && idx<SQLITE_PAGE_SIZE ){
+    FreeBlk *p = (FreeBlk*)&pPage->u.aDisk[idx];
+    sprintf(range,"%d..%d", idx, idx+p->iSize-1);
+    nFree += p->iSize;
+    printf("freeblock %2d: i=%-10s size=%-4d total=%d\n",
+       i, range, p->iSize, nFree);
+    idx = p->iNext;
+  }
+  if( idx!=0 ){
+    printf("ERROR: next freeblock index out of range: %d\n", idx);
+  }
+  sqlitepager_unref(pPage);
+  return SQLITE_OK;
+}
+#endif
+
+#ifdef SQLITE_TEST
+/*
+** Put the page number and index of a cursor into aResult[0] and aResult[1]
+** This routine is used for debugging and testing only.
+*/
+int sqliteBtreeCursorDump(BtCursor *pCur, int *aResult){
+  aResult[0] = sqlitepager_pagenumber(pCur->pPage);
+  aResult[1] = pCur->idx;
+  return SQLITE_OK;
+}
+#endif
index f63b198a14e1d01db1d652915dd073f120d616bc..b751c2f8437af9bcc05ea495db6cf77b8f6c0c56 100644 (file)
@@ -24,7 +24,7 @@
 ** This header file defines the interface that the sqlite B-Tree file
 ** subsystem.
 **
-** @(#) $Id: btree.h,v 1.4 2001/06/08 00:21:53 drh Exp $
+** @(#) $Id: btree.h,v 1.5 2001/06/22 19:15:00 drh Exp $
 */
 
 typedef struct Btree Btree;
@@ -42,7 +42,7 @@ int sqliteBtreeDropTable(Btree*, int);
 int sqliteBtreeClearTable(Btree*, int);
 
 int sqliteBtreeCursor(Btree*, int iTable, BtCursor **ppCur);
-int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, *pRes);
+int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, int *pRes);
 int sqliteBtreeDelete(BtCursor*);
 int sqliteBtreeInsert(BtCursor*, void *pKey, int nKey, void *pData, int nData);
 int sqliteBtreeNext(BtCursor*, int *pRes);
@@ -55,3 +55,9 @@ int sqliteBtreeCloseCursor(BtCursor*);
 #define SQLITE_N_BTREE_META 3
 int sqliteBtreeGetMeta(Btree*, int*);
 int sqliteBtreeUpdateMeta(Btree*, int*);
+
+
+#ifdef SQLITE_TEST
+int sqliteBtreePageDump(Btree*, int);
+int sqliteBtreeCursorDump(BtCursor*, int*);
+#endif
index 6915ef2776268f72b0ae33ad561993363745ad4d..b102e04868a7f9d40d65e6da383409aacadf208c 100644 (file)
@@ -27,7 +27,7 @@
 ** all writes in order to support rollback.  Locking is used to limit
 ** access to one or more reader or one writer.
 **
-** @(#) $Id: pager.c,v 1.8 2001/06/02 02:40:57 drh Exp $
+** @(#) $Id: pager.c,v 1.9 2001/06/22 19:15:00 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -562,7 +562,8 @@ Pgno sqlitepager_pagenumber(void *pData){
 ** currently on the freelist (the reference count is zero) then
 ** remove it from the freelist.
 */
-static void sqlitepager_ref(PgHdr *pPg){
+int sqlitepager_ref(void *pData){
+  PgHdr *pPg = DATA_TO_PGHDR(pData);
   if( pPg->nRef==0 ){
     /* The page is currently on the freelist.  Remove it. */
     if( pPg->pPrevFree ){
@@ -578,6 +579,7 @@ static void sqlitepager_ref(PgHdr *pPg){
     pPg->pPager->nRef++;
   }
   pPg->nRef++;
+  return SQLITE_OK;
 }
 
 /*
index f12fe09a659f437ed896d06584a1eed5d6973ab7..acb7735b8c0e62e7d696659f794a15b44a2f91c7 100644 (file)
@@ -25,7 +25,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.4 2001/05/24 21:06:36 drh Exp $
+** @(#) $Id: pager.h,v 1.5 2001/06/22 19:15:01 drh Exp $
 */
 
 /*
@@ -45,10 +45,11 @@ typedef unsigned int Pgno;
 typedef struct Pager Pager;
 
 int sqlitepager_open(Pager **ppPager,const char *zFilename,int nPage,int nEx);
-void sqiltepager_set_destructor(Pager*, void(*)(void*));
+void sqlitepager_set_destructor(Pager*, void(*)(void*));
 int sqlitepager_close(Pager *pPager);
 int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage);
 void *sqlitepager_lookup(Pager *pPager, Pgno pgno);
+int sqlitepager_ref(void*);
 int sqlitepager_unref(void*);
 Pgno sqlitepager_pagenumber(void*);
 int sqlitepager_write(void*);
index 6d5c2c6b0be3334f4a239dfe0fc52b4e83808dbd..d364cb7b355abb2c6476a914a31b6f755b524c83 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** A TCL Interface to SQLite
 **
-** $Id: tclsqlite.c,v 1.18 2001/04/15 00:37:09 drh Exp $
+** $Id: tclsqlite.c,v 1.19 2001/06/22 19:15:01 drh Exp $
 */
 #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
 
@@ -511,8 +511,10 @@ int TCLSH_MAIN(int argc, char **argv){
   {
     extern int Sqlitetest1_Init(Tcl_Interp*);
     extern int Sqlitetest2_Init(Tcl_Interp*);
+    extern int Sqlitetest3_Init(Tcl_Interp*);
     Sqlitetest1_Init(interp);
     Sqlitetest2_Init(interp);
+    Sqlitetest3_Init(interp);
   }
 #endif
   if( argc>=2 ){
index 769218e980eda187c90cc8e825c581d1f6b889e4..87b36e37e5f73d3bd18c59438e301181bfeae42a 100644 (file)
@@ -25,7 +25,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test3.c,v 1.1 2001/06/02 02:40:57 drh Exp $
+** $Id: test3.c,v 1.2 2001/06/22 19:15:01 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -71,8 +71,7 @@ static int btree_open(
   int argc,              /* Number of arguments */
   char **argv            /* Text of each argument */
 ){
-  BTree *pBt;
-  int nPage;
+  Btree *pBt;
   int rc;
   char zBuf[100];
   if( argc!=2 ){
@@ -155,7 +154,7 @@ static int btree_rollback(
   int argc,              /* Number of arguments */
   char **argv            /* Text of each argument */
 ){
-  Btree *pBt
+  Btree *pBt;
   int rc;
   if( argc!=2 ){
     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@@ -239,16 +238,16 @@ static int btree_drop_table(
   int argc,              /* Number of arguments */
   char **argv            /* Text of each argument */
 ){
-  Pager *pPager;
+  Btree *pBt;
   int iTable;
-  char zBuf[100];
+  int rc;
   if( argc!=3 ){
     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
        " ID TABLENUM\"", 0);
     return TCL_ERROR;
   }
   if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
-  if( Tcl_GetInt(interp, argv[2], &iTable ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
   rc = sqliteBtreeDropTable(pBt, iTable);
   if( rc!=SQLITE_OK ){
     Tcl_AppendResult(interp, errorName(rc), 0);
@@ -257,6 +256,390 @@ static int btree_drop_table(
   return TCL_OK;
 }
 
+/*
+** Usage:   btree_get_meta ID
+**
+** Return meta data
+*/
+static int btree_get_meta(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  int i;
+  int aMeta[SQLITE_N_BTREE_META];
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
+  rc = sqliteBtreeGetMeta(pBt, aMeta);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  for(i=0; i<SQLITE_N_BTREE_META; i++){
+    char zBuf[30];
+    sprintf(zBuf,"%d",aMeta[i]);
+    Tcl_AppendElement(interp, zBuf);
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_update_meta ID METADATA...
+**
+** Return meta data
+*/
+static int btree_update_meta(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  int i;
+  int aMeta[SQLITE_N_BTREE_META];
+
+  if( argc!=2+SQLITE_N_BTREE_META ){
+    char zBuf[30];
+    sprintf(zBuf,"%d",SQLITE_N_BTREE_META);
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID METADATA...\" (METADATA is ", zBuf, " integers)", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
+  for(i=0; i<SQLITE_N_BTREE_META; i++){
+    if( Tcl_GetInt(interp, argv[i+2], &aMeta[i]) ) return TCL_ERROR;
+  }
+  rc = sqliteBtreeUpdateMeta(pBt, aMeta);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_page_dump ID PAGENUM
+**
+** Print a disassembly of a page on standard output
+*/
+static int btree_page_dump(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  Btree *pBt;
+  int iPage;
+  int rc;
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[2], &iPage) ) return TCL_ERROR;
+  rc = sqliteBtreePageDump(pBt, iPage);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_cursor ID TABLENUM
+**
+** Create a new cursor.  Return the ID for the cursor.
+*/
+static int btree_cursor(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  Btree *pBt;
+  int iTable;
+  BtCursor *pCur;
+  int rc;
+  char zBuf[30];
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID TABLENUM\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+  rc = sqliteBtreeCursor(pBt, iTable, &pCur);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sprintf(zBuf,"0x%x", (int)pCur);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_close_cursor ID
+**
+** Close a cursor opened using btree_cursor.
+*/
+static int btree_close_cursor(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeCloseCursor(pCur);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_move_to ID KEY
+**
+** Move the cursor to the entry with the given key.
+*/
+static int btree_move_to(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int res;
+  char zBuf[20];
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID KEY\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeMoveto(pCur, argv[2], strlen(argv[2]), &res);  
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sprintf(zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_delete ID
+**
+** Delete the entry that the cursor is pointing to
+*/
+static int btree_delete(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeDelete(pCur);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_insert ID KEY DATA
+**
+** Create a new entry with the given key and data.  If an entry already
+** exists with the same key the old entry is overwritten.
+*/
+static int btree_insert(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+
+  if( argc!=4 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID KEY DATA\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeInsert(pCur, argv[2], strlen(argv[2]),
+                         argv[3], strlen(argv[3]));
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_next ID
+**
+** Move the cursor to the next entry in the table.
+*/
+static int btree_next(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeNext(pCur, 0);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_key ID
+**
+** Return the key for the entry at which the cursor is pointing.
+*/
+static int btree_key(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int n;
+  char *zBuf;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  sqliteBtreeKeySize(pCur, &n);
+  zBuf = malloc( n+1 );
+  rc = sqliteBtreeKey(pCur, 0, n, zBuf);
+  if( rc ){
+    free(zBuf);
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  zBuf[n] = 0;
+  Tcl_AppendResult(interp, zBuf, 0);
+  free(zBuf);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_data ID
+**
+** Return the data for the entry at which the cursor is pointing.
+*/
+static int btree_data(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int n;
+  char *zBuf;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  sqliteBtreeDataSize(pCur, &n);
+  zBuf = malloc( n+1 );
+  rc = sqliteBtreeData(pCur, 0, n, zBuf);
+  if( rc ){
+    free(zBuf);
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  zBuf[n] = 0;
+  Tcl_AppendResult(interp, zBuf, 0);
+  free(zBuf);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_cursor_dump ID
+**
+** Return two integers which are the page number and cell index for
+** the given cursor.
+*/
+static int btree_cursor_dump(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int aResult[2];
+  char zBuf[50];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeCursorDump(pCur, aResult);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sprintf(zBuf,"%d %d",aResult[0], aResult[1]);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
 /*
 ** Register commands with the TCL interpreter.
 */
@@ -269,5 +652,17 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
   Tcl_CreateCommand(interp, "btree_rollback", btree_rollback, 0, 0);
   Tcl_CreateCommand(interp, "btree_create_table", btree_create_table, 0, 0);
   Tcl_CreateCommand(interp, "btree_drop_table", btree_drop_table, 0, 0);
+  Tcl_CreateCommand(interp, "btree_get_meta", btree_get_meta, 0, 0);
+  Tcl_CreateCommand(interp, "btree_update_meta", btree_update_meta, 0, 0);
+  Tcl_CreateCommand(interp, "btree_page_dump", btree_page_dump, 0, 0);
+  Tcl_CreateCommand(interp, "btree_cursor", btree_cursor, 0, 0);
+  Tcl_CreateCommand(interp, "btree_close_cursor", btree_close_cursor, 0, 0);
+  Tcl_CreateCommand(interp, "btree_move_to", btree_move_to, 0, 0);
+  Tcl_CreateCommand(interp, "btree_delete", btree_delete, 0, 0);
+  Tcl_CreateCommand(interp, "btree_insert", btree_insert, 0, 0);
+  Tcl_CreateCommand(interp, "btree_next", btree_next, 0, 0);
+  Tcl_CreateCommand(interp, "btree_key", btree_key, 0, 0);
+  Tcl_CreateCommand(interp, "btree_data", btree_data, 0, 0);
+  Tcl_CreateCommand(interp, "btree_cursor_dump", btree_cursor_dump, 0, 0);
   return TCL_OK;
 }