]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Continued work on btree (CVS 219)
authordrh <drh@noemail.net>
Thu, 24 May 2001 21:06:34 +0000 (21:06 +0000)
committerdrh <drh@noemail.net>
Thu, 24 May 2001 21:06:34 +0000 (21:06 +0000)
FossilOrigin-Name: 18500cdcc1a42118cdf650681ebb1cbeac106aa7

manifest
manifest.uuid
src/btree.c
src/btree.h
src/pager.c
src/pager.h
test/pager.test

index 836e62df5b0aa45df603770522858dbae7cbddf9..fe24279bb3df31a3d8cedba4951d335e32d82819 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C :-)\s(CVS\s218)
-D 2001-05-21T13:45:10
+C Continued\swork\son\sbtree\s(CVS\s219)
+D 2001-05-24T21:06:35
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in acef0f0275a5ca8e68bda165f7f05d810a207664
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464
 F notes/notes2.txt 49b4d7ba35f183feb1fb098a27272b82f5c76eca
 F notes/notes3.txt cd5e7bd2167d7ef89b1077abdfa68f0af6337744
 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
-F src/btree.c bc1525234a8b169cae6f903f9cfacbcf971be942
-F src/btree.h f21c240d0c95f93e2a128106d04a6c448ed0eb94
+F src/btree.c 5749821afaee09f1cf87b55a4897fd65ed197dd3
+F src/btree.h a0ef90514e16fab863c7825ab22724317894e1ac
 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 e45946aaf080aed251653f21667d62c89e539a97
-F src/pager.h ed12ac3ddebd3afe61a0ed4bf530e7846d578e46
+F src/pager.c debcf7b00e73c031c47ffc12cdeed5cb5f02b761
+F src/pager.h e527411d88e31085f07eba6776dc337b8b027921
 F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb
 F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
 F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
@@ -64,7 +64,7 @@ F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
 F test/lock.test bca7d53de73138b1f670a2fbdb1f481ff7eaa45a
 F test/main.test da635f9e078cd21ddf074e727381a715064489ff
 F test/malloc.test 3daa97f6a9577d8f4c6e468b274333af19ce5861
-F test/pager.test c1eb25faa0938f803d1e6eb5201e5e976ea88256
+F test/pager.test 475835b84cbec423a7dd3d0492b2c8701435d375
 F test/printf.test 4c71871e1a75a2dacb673945fc13ddb30168798f
 F test/rowid.test 128453599def7435e988216f7fe89c7450b8a9a3
 F test/select1.test 223507655cdb4f9901d83fa7f5c5328e022c211f
@@ -106,7 +106,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
 F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
 F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
-P ee6760fb62e81af95796c0fcf1e65e5dc0701194
-R 6bf7ed657f22039e86c99aa04cca003e
+P 523d52dfa6ae3028cbcc88d406501f3ebb6cbd2d
+R a283ec9b97efb19cc771715467ad06bd
 U drh
-Z 4c32ca5182d7191fa6afdab861fed35d
+Z b4b0b3463533c30725394cfe4ae63d4d
index 847d746e246003b88bd3aaf9c1ae33bd18409112..c3b260934fbb20ea1d776903d50a3892d0c83398 100644 (file)
@@ -1 +1 @@
-523d52dfa6ae3028cbcc88d406501f3ebb6cbd2d
\ No newline at end of file
+18500cdcc1a42118cdf650681ebb1cbeac106aa7
\ No newline at end of file
index 767a2218e4b8f36ecbb3f94fec6becbb5f5155b8..864c846f3ea44bb7aeaf45692439dbba560fdcc6 100644 (file)
@@ -21,7 +21,7 @@
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: btree.c,v 1.6 2001/05/21 13:45:10 drh Exp $
+** $Id: btree.c,v 1.7 2001/05/24 21:06:35 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -97,7 +97,7 @@ struct Page1Header {
 /*
 ** Each database page has a header as follows:
 **
-**      page1_header          Extra numbers found on page 1 only.
+**      page1_header          Optional instance of Page1Header structure
 **      rightmost_pgno        Page number of the right-most child page
 **      first_cell            Index into MemPage.aPage of first cell
 **      first_free            Index of first free block
@@ -138,10 +138,11 @@ struct Cell {
 /*
 ** Free space on a page is remembered using a linked list of the FreeBlk
 ** structures.  Space on a database page is allocated in increments of
-** at least 4 bytes and is always aligned to a 4-byte boundry.
+** at least 4 bytes and is always aligned to a 4-byte boundry.  The
+** linked list of freeblocks is always kept in order by address.
 */
 struct FreeBlk {
-  u16 iSize;      /* Number of u32-sized slots in the block of free space */
+  u16 iSize;      /* Number of bytes in this block of free space */
   u16 iNext;      /* Index in MemPage.aPage[] of the next free block */
 };
 
@@ -158,7 +159,7 @@ struct FreeBlk {
 */
 struct OverflowPage {
   Pgno next;
-  char aData[SQLITE_PAGE_SIZE-sizeof(Pgno)];
+  char aData[OVERFLOW_SIZE];
 };
 
 /*
@@ -169,17 +170,21 @@ struct OverflowPage {
 ** data is meaningless for overflow pages and pages on the freelist.
 **
 ** Of particular interest in the auxiliary data is the aCell[] entry.  Each
-** aCell[] entry is a pointer to a Cell structure in aPage[].  The cells
+** aCell[] entry is a pointer to a Cell structure in aPage[].  The cells are
 ** put in this array so that they can be accessed in constant time, rather
 ** than in linear time which would be needed if we walked the linked list.
+**
+** The pParent field points back to the parent page.  This allows us to
+** walk up the BTree from any leaf to the root.  Care must be taken to
+** unref() the parent page pointer when this page is no longer referenced.
+** The pageDestructor() routine handles that.
 */
 struct MemPage {
   char aPage[SQLITE_PAGE_SIZE];  /* Page data stored on disk */
   unsigned char isInit;          /* True if auxiliary data is initialized */
-  unsigned char validUp;         /* True if MemPage.up is valid */
   unsigned char validLeft;       /* True if MemPage.left is valid */
   unsigned char validRight;      /* True if MemPage.right is valid */
-  Pgno up;                       /* The parent page. 0 means this is the root */
+  MemPage *pParent;              /* The parent of this page.  NULL for root */
   Pgno left;                     /* Left sibling page.  0==none */
   Pgno right;                    /* Right sibling page.  0==none */
   int idxStart;                  /* Index in aPage[] of real data */
@@ -205,18 +210,18 @@ typedef Btree Bt;
 ** The entry is identified by its MemPage and the index in
 ** MemPage.aCell[] of the entry.
 */
-struct Cursor {
-  Btree *pBt;            /* The pointer back to the BTree */
-  Cursor *pPrev, *pNext; /* List of all cursors */
-  MemPage *pPage;        /* Page that contains the entry */
-  int idx;               /* Index of the entry in pPage->aCell[] */
-  int skip_incr;         /* */
+struct BtCursor {
+  Btree *pBt;                     /* The pointer back to the BTree */
+  BtCursor *pPrev, *pNext;        /* List of all cursors */
+  MemPage *pPage;                 /* Page that contains the entry */
+  int idx;                        /* Index of the entry in pPage->aCell[] */
+  int skip_incr;                  /* */
 };
 
 /*
-** Defragment the page given.  All of the free space
-** is collected into one big block at the end of the
-** page.
+** Defragment the page given.  All Cells are moved to the
+** beginning of the page and all free space is collected 
+** into one big FreeBlk at the end of the page.
 */
 static void defragmentPage(MemPage *pPage){
   int pc;
@@ -238,7 +243,7 @@ static void defragmentPage(MemPage *pPage){
     pPage->aCell[i] = (Cell*)&pPage->aPage[pc];
     pc += n;
   }
-  assert( pPage->nFree==pc );
+  assert( pPage->nFree==SQLITE_PAGE_SIZE-pc );
   memcpy(pPage->aPage, newPage, pc);
   pFBlk = &pPage->aPage[pc];
   pFBlk->iSize = SQLITE_PAGE_SIZE - pc;
@@ -255,13 +260,16 @@ static void defragmentPage(MemPage *pPage){
 ** Or return 0 if there is not enough free space on the page to
 ** satisfy the allocation request.
 **
-** This routine will call defragmentPage if necessary to consolidate
-** free space.  
+** If the page contains nBytes of free space but does not contain
+** nBytes of contiguous free space, then defragementPage() is
+** called to consolidate all free space before allocating the
+** new chunk.
 */
 static int allocSpace(MemPage *pPage, int nByte){
   FreeBlk *p;
   u16 *pIdx;
   int start;
+
   nByte = ROUNDUP(nByte);
   if( pPage->nFree<nByte ) return 0;
   pIdx = &pPage->pStart->firstFree;
@@ -279,8 +287,11 @@ static int allocSpace(MemPage *pPage, int nByte){
     start = *pIdx;
     *pIdx = p->iNext;
   }else{
-    p->iSize -= nByte;
-    start = *pIdx + p->iSize;
+    start = *pIdx;
+    FreeBlk *pNew = (FreeBlk*)&pPage->aPage[start + nByte];
+    pNew->iNext = p->iNext;
+    pNew->iSize = p->iSize - nByte;
+    *pIdx = start + nByte;
   }
   pPage->nFree -= nByte;
   return start;
@@ -335,8 +346,14 @@ static void freeSpace(MemPage *pPage, int start, int size){
 
 /*
 ** Initialize the auxiliary information for a disk block.
+**
+** Return SQLITE_OK on success.  If we see that the page does
+** not contained 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.
 */
-static int initPage(MemPage *pPage, Pgno pgnoThis, Pgno pgnoParent){
+static int initPage(MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
   int idx;
   Cell *pCell;
   FreeBlk *pFBlk;
@@ -344,8 +361,9 @@ static int initPage(MemPage *pPage, Pgno pgnoThis, Pgno pgnoParent){
   pPage->idxStart = (pgnoThis==1) ? sizeof(Page1Header) : 0;
   pPage->pStart = (PageHdr*)&pPage->aPage[pPage->idxStart];
   pPage->isInit = 1;
-  pPage->validUp = 1;
-  pPage->up = pgnoParent;
+  assert( pPage->pParent==0 );
+  pPage->pParent = pParent;
+  if( pParent ) sqlitepager_ref(pParent);
   pPage->nCell = 0;
   idx = pPage->pStart->firstCell;
   while( idx!=0 ){
@@ -371,12 +389,26 @@ page_format_error:
   return SQLITE_CORRUPT;
 }
 
+/*
+** 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(void *pData){
+  MemPage *pPage = (MemPage*)pData;
+  if( pPage->pParent ){
+    MemPage *pParent = pPage->pParent;
+    pPage->pParent = 0;
+    sqlitepager_unref(pParent);
+  }
+}
+
 /*
 ** Open a new database.
 **
 ** Actually, this routine just sets up the internal data structures
-** for accessing the database.  We do not actually open the database
-** file until the first page is loaded.
+** for accessing the database.  We do not open the database file 
+** until the first page is loaded.
 */
 int sqliteBtreeOpen(const char *zFilename, int mode, Btree **ppBtree){
   Btree *pBt;
@@ -393,6 +425,7 @@ int sqliteBtreeOpen(const char *zFilename, int mode, Btree **ppBtree){
     *ppBtree = 0;
     return rc;
   }
+  sqlitepager_set_destructor(pBt->pPager, pageDestructor);
   pBt->pCursor = 0;
   pBt->page1 = 0;
   *ppBtree = pBt;
@@ -427,7 +460,7 @@ static int lockBtree(Btree *pBt){
   rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1);
   if( rc!=SQLITE_OK ) return rc;
   rc = initPage(pBt->page1, 1, 0);
-  if( rc!=SQLITE_OK ) goto lock_failed;
+  if( rc!=SQLITE_OK ) goto page1_init_failed;
 
   /* Do some checking to help insure the file we opened really is
   ** a valid database file. 
@@ -436,21 +469,23 @@ static int lockBtree(Btree *pBt){
     Page1Header *pP1 = (Page1Header*)pBt->page1;
     if( pP1->magic1!=MAGIC_1 || pP1->magic2!=MAGIC_2 ){
       rc = SQLITE_CORRUPT;
-      goto lock_failed;
+      goto page1_init_failed;
     }
   }
   return rc;
 
-lock_failed:
+page1_init_failed:
   sqlitepager_unref(pBt->page1);
   pBt->page1 = 0;
+  return rc;
 }
 
 /*
-** Start a new transaction
+** Attempt to start a new transaction.
 */
 int sqliteBtreeBeginTrans(Btree *pBt){
   int rc;
+  Page1Header *pP1;
   if( pBt->inTrans ) return SQLITE_ERROR;
   if( pBt->page1==0 ){
     rc = lockBtree(pBt);
@@ -460,6 +495,11 @@ int sqliteBtreeBeginTrans(Btree *pBt){
   if( rc==SQLITE_OK ){
     pBt->inTrans = 1;
   }
+  pP1 = (Page1Header*)pBt->page1;
+  if( pP1->magic1==0 ){
+    pP1->magic1 = MAGIC_1;
+    pP1->magic2 = MAGIC_2;
+  }
   return rc;
 }
 
@@ -481,7 +521,7 @@ static void unlockBtree(Btree *pBt){
 */
 int sqliteBtreeCommit(Btree *pBt){
   int rc;
-  assert( pBt->pCursor==0 );
+  if( pBt->pCursor!=0 ) return SQLITE_ERROR;
   rc = sqlitepager_commit(pBt->pPager);
   unlockBtree(pBt);
   return rc;
@@ -493,7 +533,7 @@ int sqliteBtreeCommit(Btree *pBt){
 */
 int sqliteBtreeRollback(Btree *pBt){
   int rc;
-  assert( pBt->pCursor==0 );
+  if( pBt->pCursor!=0 ) return SQLITE_ERROR;
   rc = sqlitepager_rollback(pBt->pPager);
   unlockBtree(pBt);
   return rc;
@@ -533,9 +573,11 @@ int sqliteBtreeCursor(Btree *pBt, BtCursor **ppCur){
     return rc;
   }
   if( !pCur->pPage->isInit ){
-    initPage(pCur->pPage);
+    initPage(pCur->pPage, 1, 0);
   }
   pCur->idx = 0;
+  pCur->depth = 0;
+  pCur->aPage[0] = pCur->pPage;
   *ppCur = pCur;
   return SQLITE_OK;
 }
@@ -562,23 +604,38 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){
 }
 
 /*
-** Return the number of bytes in the key of the entry to which
-** the cursor is currently point.  If the cursor has not been
-** initialized or is pointed to a deleted entry, then return 0.
+** Write the number of bytes of key for the entry the cursor is
+** pointing to into *pSize.  Return SQLITE_OK.  Failure is not
+** possible.
 */
-int sqliteBtreeKeySize(BtCursor *pCur){
+int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
   Cell *pCell;
   MemPage *pPage;
 
   pPage = pCur->pPage;
-  if( pCur->idx >= pPage->nCell ) return 0;
-  pCell = pPage->aCell[pCur->idx];
-  return pCell->nKey;
+  assert( pPage!=0 );
+  if( pCur->idx >= pPage->nCell ){
+    *pSize = 0;
+  }else{
+    pCell = pPage->aCell[pCur->idx];
+    *psize = pCell->nKey;
+  }
+  return SQLITE_OK;
 }
 
+/*
+** Read payload information from the entry that the pCur cursor is
+** pointing to.  Begin reading the payload at "offset" and read
+** a total of "amt" bytes.  Put the result in zBuf.
+**
+** This routine does not make a distinction between key and data.
+** It just reads bytes from the payload area.
+*/
 static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
   char *aData;
   Pgno nextPage;
+  assert( pCur!=0 && pCur->pPage!=0 );
+  assert( pCur->idx>=0 && pCur->idx<pCur->nCell );
   aData = pCur->pPage->aCell[pCur->idx].aData;
   if( offset<MX_LOCAL_PAYLOAD ){
     int a = amt;
@@ -619,10 +676,73 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
   return amt==0 ? SQLITE_OK : SQLITE_CORRUPT;
 }
 
-int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf);
-int sqliteBtreeDataSize(BtCursor*);
-int sqliteBtreeData(BtCursor*, int offset, int amt, char *zBuf);
+/*
+** Read part of the key associated with cursor pCur.  A total
+** of "amt" bytes will be transfered into zBuf[].  The transfer
+** begins at "offset".  If the key does not contain enough data
+** to satisfy the request, no data is fetched and this routine
+** returns SQLITE_ERROR.
+*/
+int sqliteBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){
+  Cell *pCell;
+  MemPage *pPage;
 
+  if( amt<0 ) return SQLITE_ERROR;
+  if( offset<0 ) return SQLITE_ERROR;
+  if( amt==0 ) return SQLITE_OK;
+  pPage = pCur->pPage;
+  assert( pPage!=0 );
+  if( pCur->idx >= pPage->nCell ){
+    return SQLITE_ERROR;
+  }
+  pCell = pPage->aCell[pCur->idx];
+  if( amt+offset > pCell->nKey ){
+  return getPayload(pCur, offset, amt, zBuf);
+}
+
+/*
+** Write the number of bytes of data on the entry that the cursor
+** is pointing to into *pSize.  Return SQLITE_OK.  Failure is
+** not possible.
+*/
+int sqliteBtreeDataSize(BtCursor *pCur, int *pSize){
+  Cell *pCell;
+  MemPage *pPage;
+
+  pPage = pCur->pPage;
+  assert( pPage!=0 );
+  if( pCur->idx >= pPage->nCell ){
+    *pSize = 0;
+  }else{
+    pCell = pPage->aCell[pCur->idx];
+    *pSize = pCell->nData;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Read part of the data associated with cursor pCur.  A total
+** of "amt" bytes will be transfered into zBuf[].  The transfer
+** begins at "offset".  If the size of the data in the record
+** is insufficent to satisfy this request then no data is read
+** and this routine returns SQLITE_ERROR.
+*/
+int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){
+  Cell *pCell;
+  MemPage *pPage;
+
+  if( amt<0 ) return SQLITE_ERROR;
+  if( offset<0 ) return SQLITE_ERROR;
+  if( amt==0 ) return SQLITE_OK;
+  pPage = pCur->pPage;
+  assert( pPage!=0 );
+  if( pCur->idx >= pPage->nCell ){
+    return SQLITE_ERROR;
+  }
+  pCell = pPage->aCell[pCur->idx];
+  if( amt+offset > pCell->nKey ){
+  return getPayload(pCur, offset + pCell->nKey, amt, zBuf);
+}
 
 /*
 ** Compare the key for the entry that pCur points to against the 
@@ -665,7 +785,7 @@ static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
       return SQLITE_CORRUPT;
     }
     rc = sqlitepager_get(pCur->pBt->pPager, nextPage, &pOvfl);
-    if( rc!=0 ){
+    if( rc ){
       return rc;
     }
     nextPage = pOvfl->next;
@@ -687,13 +807,152 @@ static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
   return SQLITE_OK;
 }
 
+/*
+** Move the cursor down to a new child page.
+*/
+static int childPage(BtCursor *pCur, int newPgno){
+  int rc;
+  MemPage *pNewPage;
+
+  rc = sqlitepager_get(pCur->pBt->pPager, newPgno, &pNewPage);
+  if( rc ){
+    return rc;
+  }
+  if( !pNewPage->isInit ){
+    initPage(pNewPage, newPgno, pCur->pPage);
+  }
+  sqlitepager_unref(pCur->pPage);
+  pCur->pPage = pNewPage;
+  pCur->idx = 0;
+  return SQLITE_OK;
+}
+
+/*
+** Move the cursor up to the parent page
+*/
+static int parentPage(BtCursor *pCur){
+  Pgno oldPgno;
+  MemPage *pParent;
+
+  pParent = pCur->pPage->pParent;
+  oldPgno = sqlitepager_pagenumber(pCur->pPage);
+  if( pParent==0 ){
+    return SQLITE_INTERNAL;
+  }
+  sqlitepager_ref(pParent);
+  sqlitepager_unref(pCur->pPage);
+  pCur->pPage = pParent;
+  pCur->idx = pPage->nCell;
+  for(i=0; i<pPage->nCell; i++){
+    if( pPage->aCell[i].pgno==oldPgno ){
+      pCur->idx = i;
+      break;
+    }
+  }
+}
+
+/*
+** Move the cursor to the root page
+*/
+static int rootPage(BtCursor *pCur){
+  MemPage *pNew;
+  pNew = pCur->pBt->page1;
+  sqlitepager_ref(pNew);
+  sqlitepager_unref(pCur->pPage);
+  pCur->pPage = pNew;
+  pCur->idx = 0;
+  return SQLITE_OK;
+}
 
 /* Move the cursor so that it points to an entry near pKey.
-** Return 0 if the cursor is left pointing exactly at pKey.
-** Return -1 if the cursor points to the largest entry less than pKey.
-** Return 1 if the cursor points to the smallest entry greater than pKey.
+** Return a success code.
+**
+** If pRes!=NULL, then *pRes is written with an integer code to
+** describe the results.  *pRes is set to 0 if the cursor is left 
+** pointing at an entry that exactly matches pKey.  *pRes is made
+** negative if the cursor is on the largest entry less than pKey.
+** *pRes is set positive if the cursor is on the smallest entry
+** greater than pKey.  *pRes is not changed if the return value
+** is something other than SQLITE_OK;
+*/
+int sqliteBtreeMoveto(BtCursor *pCur, void *pKey, int nKey, int *pRes){
+  int rc;
+  rc = rootPage(pCur);
+  if( rc ) return rc;
+  for(;;){
+    int lwr, upr;
+    Pgno chldPg;
+    MemPage *pPage = pCur->pPage;
+    lwr = 0;
+    upr = pPage->nCell-1;
+    while( lwr<=upr ){
+      int c;
+      pCur->idx = (lwr+upr)/2;
+      rc = compareKey(pCur, pKey, nKey, &c);
+      if( rc ) return rc;
+      if( c==0 ){
+        if( pRes ) *pRes = 0;
+        return SQLITE_OK;
+      }
+      if( c<0 ){
+        lwr = pCur->idx+1;
+      }else{
+        upr = pCur->idx-1;
+      }
+    }
+    assert( lwr==upr+1 );
+    if( lwr>=pPage->nCell ){
+      chldPg = pPage->pStart->pgno;
+    }else{
+      chldPg = pPage->aCell[lwr].pgno;
+    }
+    if( chldPg==0 ){
+      if( pRes ) *pRes = c;
+      return SQLITE_OK;
+    }
+    rc = childPage(pCur, chldPg);
+    if( rc ) return rc;
+  }
+}
+
+/*
+** Advance the cursor to the next entry in the database.  If pRes!=NULL
+** then set *pRes=0 on success and set *pRes=1 if the cursor was
+** pointing to the last entry in the database.
 */
-int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey);
-int sqliteBtreeDelete(BtCursor*);
+int sqliteBtreeNext(BtCursor *pCur, int *pRes){
+  MemPage *pPage;
+  int rc;
+  int moved = 0;
+  if( pCur->skip_next ){
+    pCur->skip_next = 0;
+    if( pRes ) *pRes = 0;
+    return SQLITE_OK;
+  }
+  pPage = pCur->pPage;
+  pCur->idx++;
+  while( pCur->idx>=pPage->nCell ){
+    if( pCur->depth==0 ){
+      if( pRes ) *pRes = 1;
+      return SQLITE_OK;
+    }
+    rc = parentPage(pCur);
+    if( rc ) return rc;
+    moved = 1;
+    pPage = pCur->pPage;
+  }
+  if( moved ){
+    if( pRes ) *pRes = 0;
+    return SQLITE_OK;
+  }
+  while( pCur->idx<pPage->nCell && pPage->aCell[pCur->idx].pgno>0 ){
+    rc = childPage(pCur, pPage->aCell[pCur->idx].pgno);
+    if( rc ) return rc;
+    pPage = pCur->pPage;
+  }
+  if( pRes ) *pRes = 0;
+  return SQLITE_OK;
+}
+
 int sqliteBtreeInsert(BtCursor*, void *pKey, int nKey, void *pData, int nData);
-int sqliteBtreeNext(BtCursor*);
+int sqliteBtreeDelete(BtCursor*);
index 572cd4ef31ca42c8e01b6dfd5e91331b042cb6f1..31675addfeb0881a738b2f4772c177e02732a6a7 100644 (file)
@@ -24,7 +24,7 @@
 ** This header file defines the interface that the sqlite B-Tree file
 ** subsystem.
 **
-** @(#) $Id: btree.h,v 1.1 2001/04/17 20:09:11 drh Exp $
+** @(#) $Id: btree.h,v 1.2 2001/05/24 21:06:36 drh Exp $
 */
 
 typedef struct Btree Btree;
@@ -39,18 +39,12 @@ int sqliteBtreeRollback(Btree*);
 
 
 int sqliteBtreeCursor(Btree*, BtCursor **ppCur);
-
-/* Move the cursor so that it points to an entry near pKey.
-** Return 0 if the cursor is left pointing exactly at pKey.
-** Return -1 if the cursor points to the largest entry less than pKey.
-** Return 1 if the cursor points to the smallest entry greater than pKey.
-*/
-int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey);
+int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, *pRes);
 int sqliteBtreeDelete(BtCursor*);
 int sqliteBtreeInsert(BtCursor*, void *pKey, int nKey, void *pData, int nData);
-int sqliteBtreeNext(BtCursor*);
-int sqliteBtreeKeySize(BtCursor*);
+int sqliteBtreeNext(BtCursor*, int *pRes);
+int sqliteBtreeKeySize(BtCursor*, int *pSize);
 int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf);
-int sqliteBtreeDataSize(BtCursor*);
+int sqliteBtreeDataSize(BtCursor*, int *pSize);
 int sqliteBtreeData(BtCursor*, int offset, int amt, char *zBuf);
 int sqliteBtreeCloseCursor(BtCursor*);
index 0424497aba90d769a954f730751d27c899e1989d..d582f21f80ec0fbe1de605f6f0c331301d542bfe 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.6 2001/05/21 13:45:10 drh Exp $
+** @(#) $Id: pager.c,v 1.7 2001/05/24 21:06:36 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -113,6 +113,7 @@ struct Pager {
   int dbSize;                 /* Number of pages in the file */
   int origDbSize;             /* dbSize before the current change */
   int nExtra;                 /* Add this many bytes to each in-memory page */
+  void (*xDestructor)(void*); /* Call this routine when freeing pages */
   int nPage;                  /* Total number of in-memory pages */
   int nRef;                   /* Number of in-memory pages with PgHdr.nRef>0 */
   int mxPage;                 /* Maximum number of pages to hold in cache */
@@ -478,6 +479,17 @@ int sqlitepager_open(
   return SQLITE_OK;
 }
 
+/*
+** Set the destructor for this pager.  If not NULL, the destructor is called
+** when the reference count on the page reaches zero.  
+**
+** The destructor is not called as a result sqlitepager_close().  
+** Destructors are only called by sqlitepager_unref().
+*/
+void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){
+  pPager->xDestructor = xDesc;
+}
+
 /*
 ** Return the total number of pages in the file opened by pPager.
 */
@@ -806,8 +818,8 @@ int sqlitepager_unref(void *pData){
   pPager = pPg->pPager;
   pPg->nRef--;
 
-  /* When the number of references to a page reach 0, add the
-  ** page to the freelist.
+  /* When the number of references to a page reach 0, call the
+  ** destructor and add the page to the freelist.
   */
   if( pPg->nRef==0 ){
     pPg->pNextFree = 0;
@@ -818,6 +830,9 @@ int sqlitepager_unref(void *pData){
     }else{
       pPager->pFirst = pPg;
     }
+    if( pPager->xDestructor ){
+      pPager->xDestructor(pData);
+    }
   
     /* When all pages reach the freelist, drop the read lock from
     ** the database file.
index 577f5482bf906ef123371e2f88a6ae982a9e400f..f12fe09a659f437ed896d06584a1eed5d6973ab7 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.3 2001/04/28 16:52:42 drh Exp $
+** @(#) $Id: pager.h,v 1.4 2001/05/24 21:06:36 drh Exp $
 */
 
 /*
@@ -44,7 +44,8 @@ typedef unsigned int Pgno;
 */
 typedef struct Pager Pager;
 
-int sqlitepager_open(Pager **ppPager, const char *zFilename,int nPage,int nEx);
+int sqlitepager_open(Pager **ppPager,const char *zFilename,int nPage,int nEx);
+void sqiltepager_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);
index 4aae5d0d448fbcf4e662244e3d77fd78e34a4e77..102c8280b33a56efccdc93907858c74926a8bc1c 100644 (file)
@@ -23,7 +23,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script is page cache subsystem.
 #
-# $Id: pager.test,v 1.4 2001/05/21 13:45:10 drh Exp $
+# $Id: pager.test,v 1.5 2001/05/24 21:06:36 drh Exp $
 
 
 set testdir [file dirname $argv0]
@@ -66,13 +66,36 @@ do_test pager-2.2 {
   } msg]
   lappend v $msg
 } {1 SQLITE_ERROR}
-do_test pager-2.3 {
+do_test pager-2.3.1 {
+  set ::gx [page_lookup $::p1 1]
+} {}
+do_test pager-2.3.2 {
+  pager_stats $::p1
+} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0}
+do_test pager-2.3.3 {
   set v [catch {
     set ::g1 [page_get $::p1 1]
   } msg]
   if {$v} {lappend v $msg}
   set v
 } {0}
+do_test pager-2.3.3 {
+  pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.3.4 {
+  set ::gx [page_lookup $::p1 1]
+  expr {$::gx!=""}
+} {1}
+do_test pager-2.3.5 {
+  pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
+do_test pager-2.3.6 {
+  expr $::g1==$::gx
+} {1}
+do_test pager-2.3.7 {
+  page_unref $::gx
+  pager_stats $::p1
+} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}
 do_test pager-2.4 {
   pager_stats $::p1
 } {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0}