]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
incremental update (CVS 223)
authordrh <drh@noemail.net>
Fri, 8 Jun 2001 00:21:52 +0000 (00:21 +0000)
committerdrh <drh@noemail.net>
Fri, 8 Jun 2001 00:21:52 +0000 (00:21 +0000)
FossilOrigin-Name: 7108b699cc03d5d4dfb222ceab0196a85dbffd50

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

index 4c45b4d2d213dce1cae1ddd3a13113aa4cb48681..0b49aed1e47ca9de8b037cdea0ca9ff188e4b15c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C continued\swork\son\sbtree\s(CVS\s222)
-D 2001-06-02T02:40:57
+C incremental\supdate\s(CVS\s223)
+D 2001-06-08T00:21:52
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in acef0f0275a5ca8e68bda165f7f05d810a207664
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464
 F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
 F notes/notes3.txt cd5e7bd2167d7ef89b1077abdfa68f0af6337744
 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
-F src/btree.c 3768a7dd7f2d37ef618b2dca55b8fffb76bce52b
-F src/btree.h 4a50996c9bd912e8feeff28a45e936fe33f828c1
+F src/btree.c 2a1a6c3ae7743ebf7f0b1632bef68d2851fe9bfd
+F src/btree.h f9adc22e8414402c176d71088e76afa89cc0d4b1
 F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651
 F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af
 F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
@@ -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 d78febd197b7514b63afe6626e6639a3c3c2f0fc
-R 84627a160819d6448d4e47c4eb2cbfd2
+P d07e0e80a0b33081adda8651e9a6750b2e40141a
+R fd7886817d2a4ced202ba2cb4c6b400e
 U drh
-Z 48913baed71abfbe21354883b544670d
+Z c3bf505ea512d1148c1c303d4dd729f7
index 89d796f3fb70a8a31ce11cf608fc6bade117b8a4..3593b9b721892908ba5ccd6bf18d11847db7d0c6 100644 (file)
@@ -1 +1 @@
-d07e0e80a0b33081adda8651e9a6750b2e40141a
\ No newline at end of file
+7108b699cc03d5d4dfb222ceab0196a85dbffd50
\ No newline at end of file
index 4391a36b9973855a52049e5059269b8730a1cdad..5bec869c58c431884a143873e200db1857ebf04c 100644 (file)
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: btree.c,v 1.10 2001/06/02 02:40:57 drh Exp $
+** $Id: btree.c,v 1.11 2001/06/08 00:21:52 drh Exp $
+**
+** This file implements a external (disk-based) database using BTrees.
+** For a detailed discussion of BTrees, refer to
+**
+**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
+**     "Sorting And Searching", pages 473-480. Addison-Wesley
+**     Publishing Company, Reading, Massachusetts.
+**
+** The basic idea is that each page of the file contains N database
+** entries and N+1 pointers to subpages.
+**
+**   ----------------------------------------------------------------
+**   |  Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N) | Ptr(N+1) |
+**   ----------------------------------------------------------------
+**
+** All of the keys on the page that Ptr(0) points to have values less
+** than Key(0).  All of the keys on page Ptr(1) and its subpages have
+** values greater than Key(0) and less than Key(1).  All of the keys
+** on Ptr(N+1) and its subpages have values greater than Key(N).  And
+** so forth.
+**
+** Finding a particular key requires reading O(log(M)) pages from the file
+** where M is the number of entries in the tree.
+**
+** In this implementation, a single file can hold one or more separate 
+** BTrees.  Each BTree is identified by the index of its root page.  The
+** key and data for any entry are combined to form the "payload".  Up to
+** MX_LOCAL_PAYLOAD bytes of payload can be carried directly on the
+** database page.  If the payload is larger than MX_LOCAL_PAYLOAD bytes
+** then surplus bytes are stored on overflow pages.  The payload for an
+** entry and the preceding pointer are combined to form a "Cell".  Each 
+** page has a smaller header which contains the Ptr(N+1) pointer.
+**
+** The first page of the file contains a magic string used to verify that
+** the file really is a valid BTree database, a pointer to a list of unused
+** pages in the file, and some meta information.  The root of the first
+** BTree begins on page 2 of the file.  (Pages are numbered beginning with
+** 1, not 0.)  Thus a minimum database contains 2 pages.
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -69,7 +107,11 @@ static const char zMagicHeader[] =
 ** The first page of the database file contains a magic header string
 ** to identify the file as an SQLite database file.  It also contains
 ** a pointer to the first free page of the file.  Page 2 contains the
-** root of the BTree.
+** root of the principle BTree.  The file might contain other BTrees
+** rooted on pages above 2.
+**
+** The first page also contains SQLITE_N_BTREE_META integers that
+** can be used by higher-level routines.
 **
 ** Remember that pages are numbered beginning with 1.  (See pager.c
 ** for additional information.)  Page 0 does not exist and a page
@@ -78,21 +120,26 @@ 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 aMeta[SQLITE_N_BTREE_META];  /* User defined integers */
 };
 
 /*
 ** Each database page has a header that is an instance of this
 ** structure.
 **
-** MemPage.pHdr always points to the rightmost_pgno.  First_free is
-** 0 if there is no free space on this page.  Otherwise, first_free is
-** the index in MemPage.aDisk[] of a FreeBlk structure that describes
-** the first block of free space.  All free space is defined by a linked
-** list of FreeBlk structures.
+** PageHdr.firstFree is 0 if there is no free space on this page.
+** Otherwise, PageHdr.firstFree is the index in MemPage.aDisk[] of a 
+** FreeBlk structure that describes the first block of free space.  
+** All free space is defined by a linked list of FreeBlk structures.
 **
-** Data is stored in a linked list of Cell structures.  First_cell is
-** the index into MemPage.aDisk[] of the first cell on the page.  The
+** Data is stored in a linked list of Cell structures.  PageHdr.firstCell
+** is the index into MemPage.aDisk[] of the first cell on the page.  The
 ** Cells are kept in sorted order.
+**
+** A Cell contains all information about a database entry and a pointer
+** to a child page that contains other entries less than itself.  In
+** other words, the i-th Cell contains both Ptr(i) and Key(i).  The
+** right-most pointer of the page is contained in PageHdr.rightChild.
 */
 struct PageHdr {
   Pgno rightChild;  /* Child page that comes after all cells on this page */
@@ -163,7 +210,7 @@ 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.  The
-** linked list of freeblocks is always kept in order by address.
+** linked list of FreeBlks is always kept in order by address.
 */
 struct FreeBlk {
   u16 iSize;      /* Number of bytes in this block of free space */
@@ -178,7 +225,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,
-** then all extra data is written to a linked list of overflow pages.
+** then all extra bytes are written to a linked list of overflow pages.
 ** Each overflow page is an instance of the following structure.
 **
 ** Unused pages in the database are also represented by instances of
@@ -215,7 +262,7 @@ struct MemPage {
   MemPage *pParent;              /* The parent of this page.  NULL for root */
   int nFree;                     /* Number of free bytes in aDisk[] */
   int nCell;                     /* Number of entries on this page */
-  Cell *apCell[MX_CELL];         /* All data entires in sorted order */
+  Cell *apCell[MX_CELL+1];       /* All data entires in sorted order */
 }
 
 /*
@@ -244,6 +291,7 @@ typedef Btree Bt;
 struct BtCursor {
   Btree *pBt;               /* The Btree to which this cursor belongs */
   BtCursor *pPrev, *pNext;  /* 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[] */
   u8 bSkipNext;             /* sqliteBtreeNext() is no-op if true */
@@ -300,17 +348,17 @@ static void defragmentPage(MemPage *pPage){
 }
 
 /*
-** Allocate space on a page.  The space needs to be at least
-** nByte bytes in size.  nByte must be a multiple of 4.
+** Allocate nByte bytes of space on a page.  nByte must be a 
+** multiple of 4.
 **
 ** Return the index into pPage->aDisk[] of the first byte of
 ** the new allocation. Or return 0 if there is not enough free
 ** space on the page to satisfy the allocation request.
 **
 ** 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.
+** nBytes of contiguous free space, then this routine automatically
+** calls defragementPage() to consolidate all free space before 
+** allocating the new chunk.
 */
 static int allocateSpace(MemPage *pPage, int nByte){
   FreeBlk *p;
@@ -347,7 +395,7 @@ static int allocateSpace(MemPage *pPage, int nByte){
 /*
 ** Return a section of the MemPage.aDisk[] to the freelist.
 ** The first byte of the new free block is pPage->aDisk[start]
-** and the size of the block is "size".
+** and the size of the block is "size" bytes.
 **
 ** Most of the effort here is involved in coalesing adjacent
 ** free blocks into a single big free block.
@@ -396,7 +444,8 @@ static void freeSpace(MemPage *pPage, int start, int size){
 **
 ** The pParent parameter must be a pointer to the MemPage which
 ** is the parent of the page being initialized.  The root of the
-** BTree (page 2) has no parent and so for that page, pParent==NULL.
+** BTree (usually page 2) has no parent and so for that page, 
+** pParent==NULL.
 **
 ** Return SQLITE_OK on success.  If we see that the page does
 ** not contained a well-formed database page, then return 
@@ -444,6 +493,11 @@ static int initPage(MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
     if( pFBlk->iNext <= idx ) goto page_format_error;
     idx = pFBlk->iNext;
   }
+  if( pPage->nCell==0 && pPage->nFree==0 ){
+    /* As a special case, an uninitialized root page appears to be
+    ** an empty database */
+    return SQLITE_OK;
+  }
   if( pPage->nFree!=freeSpace ) goto page_format_error;
   return SQLITE_OK;
 
@@ -453,7 +507,7 @@ page_format_error:
 
 /*
 ** Recompute the MemPage.apCell[], MemPage.nCell, and MemPage.nFree parameters
-** for a cell after the content has be changed significantly.
+** for a cell after the MemPage.aDisk[] content has be changed significantly.
 **
 ** The computation here is similar to initPage() except that in this case
 ** the MemPage.aDisk[] field has been set up internally (instead of 
@@ -482,7 +536,8 @@ static void reinitPage(MemPage *pPage){
 }
 
 /*
-** Initialize a database page so that it holds no entries at all.
+** Set up a raw page so that it looks like a database page holding
+** no entries.
 */
 static void zeroPage(MemPage *pPage){
   PageHdr *pHdr;
@@ -585,12 +640,46 @@ page1_init_failed:
   return rc;
 }
 
+/*
+** Create a new database by initializing the first two pages.
+*/
+static int newDatabase(Btree *pBt){
+  MemPage *pRoot;
+  PageOne *pP1;
+  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);
+  if( rc ) return rc;
+  rc = sqlitepager_write(pRoot);
+  if( rc ){
+    sqlitepager_unref(pRoot);
+    return rc;
+  }
+  strcpy(pP1->zMagic, zMagicHeader);
+  zeroPage(pRoot);
+  sqlitepager_unref(pRoot);
+  return SQLITE_OK;
+}
+
 /*
 ** Attempt to start a new transaction.
+**
+** A transaction must be started before attempting any changes
+** to the database.  None of the following routines will work
+** unless a transaction is started first:
+**
+**      sqliteBtreeCreateTable()
+**      sqliteBtreeClearTable()
+**      sqliteBtreeDropTable()
+**      sqliteBtreeInsert()
+**      sqliteBtreeDelete()
+**      sqliteBtreeUpdateMeta()
 */
 int sqliteBtreeBeginTrans(Btree *pBt){
   int rc;
-  Page1Header *pP1;
+  PageOne *pP1;
   if( pBt->inTrans ) return SQLITE_ERROR;
   if( pBt->page1==0 ){
     rc = lockBtree(pBt);
@@ -600,12 +689,7 @@ 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;
+  return newDatabase(pBt);
 }
 
 /*
@@ -645,10 +729,11 @@ int sqliteBtreeRollback(Btree *pBt){
 }
 
 /*
-** Create a new cursor.  The act of acquiring a cursor
-** gets a read lock on the database file.
+** Create a new cursor for the BTree whose root is on the page
+** iTable.  The act of acquiring a cursor gets a read lock on 
+** the database file.
 */
-int sqliteBtreeCursor(Btree *pBt, BtCursor **ppCur){
+int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){
   int rc;
   BtCursor *pCur;
   if( pBt->page1==0 ){
@@ -663,11 +748,12 @@ int sqliteBtreeCursor(Btree *pBt, BtCursor **ppCur){
     rc = SQLITE_NOMEM;
     goto create_cursor_exception;
   }
-  rc = sqlitepager_get(pBt->pPager, 2, &pCur->pPage);
+  pCur->pgnoRoot = (Pgno)iTable;
+  rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, &pCur->pPage);
   if( rc!=SQLITE_OK ){
     goto create_cursor_exception;
   }
-  rc = initPage(pCur->pPage, 2, 0);
+  rc = initPage(pCur->pPage, pCur->pgnoRoot, 0);
   if( rc!=SQLITE_OK ){
     goto create_cursor_exception;
   }
@@ -995,7 +1081,7 @@ static int moveToRoot(BtCursor *pCur){
   MemPage *pNew;
   int rc;
 
-  rc = sqlitepager_get(pCur->pBt->pPager, 2, &pNew);
+  rc = sqlitepager_get(pCur->pBt->pPager, pCur->pgnoRoot, &pNew);
   if( rc ) return rc;
   sqlitepager_unref(pCur->pPage);
   pCur->pPage = pNew;
@@ -1049,10 +1135,10 @@ int sqliteBtreeMoveto(BtCursor *pCur, void *pKey, int nKey, int *pRes){
     int lwr, upr;
     Pgno chldPg;
     MemPage *pPage = pCur->pPage;
+    int c = -1;
     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;
@@ -1173,6 +1259,7 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
   OverflowPage *pOvfl = (OverflowPage*)pPage;
   int rc;
   int needOvflUnref = 0;
+
   if( pgno==0 ){
     assert( pOvfl!=0 );
     pgno = sqlitepager_pagenumber(pOvfl);
@@ -1288,7 +1375,7 @@ static int fillInCell(
 
 /*
 ** Change the MemPage.pParent pointer on the page whose number is
-** given in the second argument sot that MemPage.pParent holds the
+** given in the second argument so that MemPage.pParent holds the
 ** pointer in the third argument.
 */
 static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent){
@@ -1319,6 +1406,228 @@ static void reparentChildPages(Pager *pPager, Page *pPage){
   reparentPage(pPager, ((PageHdr*)pPage)->rightChild, pPage);
 }
 
+/*
+** This routine redistributes Cells on pPage and up to two siblings
+** of pPage so that all pages have about the same amount of free space.
+** Usually one siblings on either side of pPage are used in the repack,
+** though both siblings might come from one side if pPage is the first
+** or last child of its parent.  If pPage has fewer than two siblings
+** (something which can only happen if pPage is the root page or a 
+** child of root) then all siblings participate in the repack.
+**
+** 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
+** or decreased by one, as necessary, to keep the root page from being
+** overfull or empty.
+**
+** Note that when this routine is called, some of the Cells on pPage
+** might not actually be stored in pPage->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->aDisk[].
+*/
+static int repack(Btree *pBt, MemPage *pPage){
+  MemPage *pParent;            /* The parent of pPage */
+  MemPage *apOld[3];           /* pPage and up to two siblings before repack */
+  Pgno pgnoOld[3];             /* Page numbers for each page in apOld[] */
+  MemPage *apNew[4];           /* pPage and up to 3 siblings after repack */
+  int idxDiv[3];               /* Indices of divider cells in pParent */
+  Cell *apDiv[3];              /* Divider cells in pParent */
+  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[] */
+  Cell *apCell[MX_CELL*3+5];   /* All cells from pages being repacked */
+  int unrefPage = 0;           /* If true, then unref pPage when done */
+
+  /*
+  ** Early out if no repacking is needed.
+  */
+  if( pPage->nFree>=0 && pPage->nFree<SQLITE_PAGE_SIZE/2 ){
+    return SQLITE_OK;
+  }
+
+  /*
+  ** Find the parent of the page to be repacked.
+  */
+  pParent = pPage->pParent;
+
+  /*
+  ** If there is no parent, it means the page is the root page.
+  ** special rules apply.
+  */
+  if( pParent==0 ){
+    Pgno pgnoChild;
+    Page *pChild;
+    if( pPage->nCell==0 ){
+      if( ((PageHdr*)pPage)->rightChild ){
+        /* The root page is under full.  Copy the one child page
+        ** into the root page and return.  This reduces the depth
+        ** of the BTree by one.
+        */
+        pgnoChild = ((PageHdr*)pPage->rightChild;
+        rc = sqlitepager_get(pBt, pgnoChild, &pChild);
+        if( rc ) return rc;
+        memcpy(pPage, pChild, SQLITE_PAGE_SIZE);
+        pPage->isInit = 0;
+        initPage(pPage, sqlitepager_pagenumber(pPage), 0);
+        reparentChildPages(pBt->pPager, pPage);
+        freePage(pBt, pChild, pgnoChild);
+        sqlitepager_unref(pChild);
+      }
+      return SQLITE_OK;
+    }
+    if( pPage->nFree>=0 ){
+      /* It is OK for the root page to be less than half full.
+      */
+      return SQLITE_OK;
+    }
+    /* If we get to here, it means the root page is over full.
+    ** When this happens, Create a new child page and copy the
+    ** contents of the root into the child.  Then make the root
+    ** page and empty page with rightChild pointing to the new
+    ** child.  Then fall thru to the code below which will cause
+    ** the overfull child page to be split.
+    */
+    rc = allocatePage(pBt, &pChild, &pgnoChild);
+    if( rc ) return rc;
+    memcpy(pChild, pPage, SQLITE_PAGE_SIZE);
+    for(i=0; i<pPage->nCell; i++){
+      if( pPage->apCell[i]>(Cell*)pPage && pPage->apCell[i]<(Cell*)&pPage[1] ){
+        int offset = (int)pPage->apCell[i] - (int)pPage;
+        pChild->apCell[i] = (Cell*)((int)pChild + offset);
+      }else{
+        pChild->apCell[i] = pPage->apCell[i];
+      }
+    }
+    pChild->isInit = 1;
+    pChild->nCell = pPage->nCell;
+    pChild->nFree = pPage->nFree;
+    /* reparentChildPages(pBt->pPager, pChild); */
+    zeroPage(pPage);
+    ((PageHdr*)pPage)->rightChild = pgnoChild;
+    pParent = pPage;
+    pPage = pChild;
+    unrefPage = 1;
+  }
+
+  /*
+  ** Find the Cell in the parent page that refers to the page
+  ** to be repacked.
+  */
+  idx = -1;
+  pgno = sqlitepager_pagenumber(pPage);
+  for(i=0; i<pParent->nCell; i++){
+    if( pParent->apCell[i]->h.leftChild==pgno ){
+      idx = i;
+      break;
+    }
+  }
+  if( idx<0 && ((PageHdr*)pPage)->rightChild==pgno ){
+    idx = pPage->nCell;
+  }
+  if( idx<0 ){
+    rc = SQLITE_CORRUPT;
+    goto end_of_repack;
+  }
+
+  /*
+  ** Get sibling pages and their dividers
+  */
+  if( idx==pPage->nCell ){
+    idx -= 2;
+  }else{
+    idx--;
+  }
+  if( idx<0 ) idx = 0;
+  nDiv = 0;
+  nOld = 0;
+  for(i=0; i<3; i++){
+    if( i+idx<pParent->nCell ){
+      idxDiv[i] = i+idx;
+      apDiv[i] = pParent->apCell[i+idx];
+      nDiv++;
+      pgnoOld[i] = apDiv[i]->h.leftChild;
+      rc = sqlitepager_get(pBt, pgnoOld[i], &apOld[i]);
+      if( rc ) goto end_of_repack;
+      nOld++;
+    }
+    if( i+idx==pParent->nCell ){
+      pgnoOld[i] = pParent->rightChild;
+      rc = sqlitepager_get(pBt, pgnoOld[i], &apOld[i]);
+      if( rc ) goto end_of_repack;
+      nOld++;
+    }
+  }
+
+  /*
+  ** Get all cells
+  */
+  nCell = 0;
+  for(i=0; i<nOld; i++){
+    MemPage *pOld = apOld[i];
+    for(j=0; j<pOld->nCell; j++){
+      apCell[nCell++] = pOld->apCell[j];
+    }
+    if( i<nOld-1 ){
+      apCell[nCell++] = apDiv[i];
+    }
+  }
+
+  /*
+  ** Estimate the number of pages needed
+  */
+  totalSize = 0;
+  for(i=0; i<nCell; i++){
+    totalSize += cellSize(apCell[i]);
+  }
+  nNew = (totalSize + (SQLITE_PAGE_SIZE - sizeof(PageHdr) - 1)) /
+            (SQLITE_PAGE_SIZE - sizeof(PageHdr));
+  perPage = totalSize/nNew;
+  
+
+  /*
+  ** Allocate new pages
+  */
+  for(i=0; i<nNew; i++){
+    rc = allocatePage(pBt, &apNew[i], &pgnoNew[i]);
+    if( rc ) goto end_of_repack;
+    zeroPage(apNew[i]);
+  }
+
+  /*
+  ** Copy data into the new pages
+  */
+  for(i=0; i<nNew; i++){
+  }
+
+  /*
+  ** Reparent children of all cells
+  */
+
+  /*
+  ** Release the old pages
+  */
+  for(i=0; i<nOld; i++){
+    releasePage(pBt, apOld[i], 0);
+  }
+
+  /*
+  ** Repack the parent page, if necessary
+  */
+  if( needToRepackParent ){
+    return repack(pParent);
+  }
+  rc = SQLITE_OK;
+
+end_of_repack:
+  if( unrefPage ){
+    sqlitepager_unref(pPage);
+  }
+  return rc;
+}
+
 /*
 ** Attempt to move N or more bytes out of the page that the cursor
 ** points to into the left sibling page.  (The left sibling page
@@ -1674,6 +1983,9 @@ int sqliteBtreeInsert(
   MemPage *pPage;
   Btree *pBt = pCur->pBt;
 
+  if( !pCur->pBt->inTrans ){
+    return SQLITE_ERROR;  /* Must start a transaction first */
+  }
   rc = sqliteBtreeMoveTo(pCur, pKey, nKey, &loc);
   if( rc ) return rc;
   rc = sqlitepager_write(pCur->pPage);
@@ -1717,7 +2029,7 @@ static int refillPage(BtCursor *pCur){
     MemPage *pChild;
     Pgno pgnoChild;
     assert( pPage->pParent==0 );
-    assert( sqlitepager_pagenumber(pPage)==2 );
+    assert( sqlitepager_pagenumber(pPage)==pCur->pgnoRoot );
     pgnoChild = ((PageHdr*)pPage)->rightChild;
     if( pgnoChild==0 ){
       return SQLITE_OK;
@@ -1728,7 +2040,7 @@ static int refillPage(BtCursor *pCur){
     memset(&pPage->aDisk[SQLITE_PAGE_SIZE], 0, EXTRA_SIZE);
     freePage(pCur->pBt, pChild, pgnoChild);
     sqlitepager_unref(pChild);
-    rc = initPage(pPage, 2, 0);
+    rc = initPage(pPage, pCur->pgnoRoot, 0);
     reparentChildPages(pPager, pPage);
     return SQLITE_OK;
   }
@@ -1775,6 +2087,10 @@ int sqliteBtreeDelete(BtCursor *pCur){
   MemPage *pPage = pCur->pPage;
   Cell *pCell;
   int rc;
+
+  if( !pCur->pBt->inTrans ){
+    return SQLITE_ERROR;  /* Must start a transaction first */
+  }
   if( pCur->idx >= pPage->nCell ){
     return SQLITE_ERROR;  /* The cursor is not pointing to anything */
   }
@@ -1811,3 +2127,117 @@ int sqliteBtreeDelete(BtCursor *pCur){
   rc = refillPage(pCur);
   return rc;
 }
+
+/*
+** Create a new BTree in the same file.  Write into *piTable the index
+** of the root page of the new table.
+*/
+int sqliteBtreeCreateTable(Btree *pBt, int *piTable){
+  MemPage *pRoot;
+  Pgno pgnoRoot;
+  int rc;
+  if( !pBt->inTrans ){
+    return SQLITE_ERROR;  /* Must start a transaction first */
+  }
+  rc = allocatePage(pBt, &pRoot, &pgnoRoot);
+  if( rc ) return rc;
+  sqlitepager_write(pRoot);
+  zeroPage(pRoot);
+  sqlitepager_unref(pRoot);
+  *piTable = (int)pgnoRoot;
+  return SQLITE_OK;
+}
+
+/*
+** Erase the given database page and all its children.  Return
+** the page to the freelist.
+*/
+static int clearDatabasePage(Btree *pBt, Pgno pgno){
+  MemPage *pPage;
+  int rc;
+  int i;
+  Cell *pCell;
+  int idx;
+
+  rc = sqlitepager_get(pBt->pPager, pgno, &pPage);
+  if( rc ) return rc;
+  idx = ((PageHdr*)pPage)->firstCell;
+  while( idx>0 ){
+    pCell = (Cell*)&pPage->aDisk[idx];
+    idx = pCell->h.iNext;
+    if( pCell->h.leftChild ){
+      rc = clearDatabasePage(pBt, pCell->h.leftChild);
+      if( rc ) return rc;
+    }
+    rc = clearCell(pCell);
+    if( rc ) return rc;
+  }
+  return freePage(pBt, pPage, pgno);
+}
+
+/*
+** Delete all information from a single table in the database.
+*/
+int sqliteBtreeClearTable(Btree *pBt, int iTable){
+  int rc;
+  if( !pBt->inTrans ){
+    return SQLITE_ERROR;  /* Must start a transaction first */
+  }
+  rc = clearDatabasePage(pBt, (Pgno)iTable);
+  if( rc ){
+    sqliteBtreeRollback(pBt);
+    return rc;
+  }
+}
+
+/*
+** Erase all information in a table and add the root of the table to
+** the freelist.  Except, the root of the principle table (the one on
+** page 2) is never added to the freelist.
+*/
+int sqliteBtreeDropTable(Btree *pBt, int iTable){
+  int rc;
+  MemPage *pPage;
+  if( !pBt->inTrans ){
+    return SQLITE_ERROR;  /* Must start a transaction first */
+  }
+  rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, &pPage);
+  if( rc==SQLITE_OK ){
+    rc = sqliteBtreeClearTable(pBt, iTable);
+  }
+  if( rc==SQLITE_OK && iTable!=2 ){
+    rc = freePage(pBt, pPage, (Pgno)iTable);
+  }
+  sqlitepager_unref(pPage);
+  return rc;  
+}
+
+/*
+** Read the meta-information out of a database file.
+*/
+int sqliteBtreeGetMeta(Btree *pBt, int *aMeta){
+  PageOne *pP1;
+  int rc;
+
+  rc = sqlitepager_get(pBt->pPager, 1, &pP1);
+  if( rc ) return rc;
+  memcpy(aMeta, pP1->aMeta, sizeof(pP1->aMeta));
+  sqlitepager_unref(pP1);
+  return SQLITE_OK;
+}
+
+/*
+** Write meta-information back into the database.
+*/
+int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){
+  PageOne *pP1;
+  int rc;
+  if( !pBt->inTrans ){
+    return SQLITE_ERROR;  /* Must start a transaction first */
+  }
+  pP1 = pBt->page1;
+  rc = sqlitepager_write(pP1);
+  if( rc ) return rc;
+  memcpy(pP1->aMeta, aMeta, sizeof(pP1->aMeta));
+  return SQLITE_OK;
+}
index 9396a776d5e1971928102dba588d6137ee54a9f6..f63b198a14e1d01db1d652915dd073f120d616bc 100644 (file)
@@ -24,7 +24,7 @@
 ** This header file defines the interface that the sqlite B-Tree file
 ** subsystem.
 **
-** @(#) $Id: btree.h,v 1.3 2001/06/02 02:40:57 drh Exp $
+** @(#) $Id: btree.h,v 1.4 2001/06/08 00:21:53 drh Exp $
 */
 
 typedef struct Btree Btree;
@@ -39,6 +39,7 @@ int sqliteBtreeRollback(Btree*);
 
 int sqliteBtreeCreateTable(Btree*, int*);
 int sqliteBtreeDropTable(Btree*, int);
+int sqliteBtreeClearTable(Btree*, int);
 
 int sqliteBtreeCursor(Btree*, int iTable, BtCursor **ppCur);
 int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, *pRes);
@@ -50,3 +51,7 @@ int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf);
 int sqliteBtreeDataSize(BtCursor*, int *pSize);
 int sqliteBtreeData(BtCursor*, int offset, int amt, char *zBuf);
 int sqliteBtreeCloseCursor(BtCursor*);
+
+#define SQLITE_N_BTREE_META 3
+int sqliteBtreeGetMeta(Btree*, int*);
+int sqliteBtreeUpdateMeta(Btree*, int*);