From 8b2f49b2883361d23d00f6ffae2458c641ce91a9 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 8 Jun 2001 00:21:52 +0000 Subject: [PATCH] incremental update (CVS 223) FossilOrigin-Name: 7108b699cc03d5d4dfb222ceab0196a85dbffd50 --- manifest | 14 +- manifest.uuid | 2 +- src/btree.c | 506 ++++++++++++++++++++++++++++++++++++++++++++++---- src/btree.h | 7 +- 4 files changed, 482 insertions(+), 47 deletions(-) diff --git a/manifest b/manifest index 4c45b4d2d2..0b49aed1e4 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 89d796f3fb..3593b9b721 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d07e0e80a0b33081adda8651e9a6750b2e40141a \ No newline at end of file +7108b699cc03d5d4dfb222ceab0196a85dbffd50 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 4391a36b99..5bec869c58 100644 --- a/src/btree.c +++ b/src/btree.c @@ -21,7 +21,45 @@ ** 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->nFreepParent; + + /* + ** 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; inCell; 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; inCell; 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+idxnCell ){ + 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; inCell; j++){ + apCell[nCell++] = pOld->apCell[j]; + } + if( ipBt; + 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; +} diff --git a/src/btree.h b/src/btree.h index 9396a776d5..f63b198a14 100644 --- a/src/btree.h +++ b/src/btree.h @@ -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*);