incremental update (CVS 223)

FossilOrigin-Name: 7108b699cc03d5d4dfb222ceab0196a85dbffd50
This commit is contained in:
drh 2001-06-08 00:21:52 +00:00
parent bd03cae998
commit 8b2f49b288
4 changed files with 482 additions and 47 deletions

View File

@ -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

View File

@ -1 +1 @@
d07e0e80a0b33081adda8651e9a6750b2e40141a
7108b699cc03d5d4dfb222ceab0196a85dbffd50

View File

@ -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->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;
}

View 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*);