The BTree code compiles and links now, but it does not work yet. (CVS 226)

FossilOrigin-Name: b31c49021c260a67b7848bc077b75a7146e31c71
This commit is contained in:
drh 2001-06-22 19:15:00 +00:00
parent 14acc04e91
commit 8c42ca9366
9 changed files with 667 additions and 130 deletions

View File

@ -47,13 +47,14 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
# Object files for the SQLite library.
#
LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
LIBOBJ = btree.o build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
main.o pager.o parse.o printf.o random.o select.o table.o \
tokenize.o update.o util.o vdbe.o where.o tclsqlite.o
# All of the source code files.
#
SRC = \
$(TOP)/src/btree.c \
$(TOP)/src/build.c \
$(TOP)/src/dbbe.c \
$(TOP)/src/dbbe.h \
@ -84,7 +85,8 @@ SRC = \
#
TESTSRC = \
$(TOP)/src/test1.c \
$(TOP)/src/test2.c
$(TOP)/src/test2.c \
$(TOP)/src/test3.c
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
@ -117,10 +119,15 @@ lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
HDR = \
sqlite.h \
$(TOP)/src/sqliteInt.h \
$(TOP)/src/btree.h \
$(TOP)/src/dbbe.h \
$(TOP)/src/pager.h \
$(TOP)/src/vdbe.h \
parse.h
btree.o: $(TOP)/src/btree.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/btree.c
build.o: $(TOP)/src/build.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/build.c
@ -201,7 +208,7 @@ tclsqlite: $(TOP)/src/tclsqlite.c libsqlite.a
testfixture: $(TOP)/src/tclsqlite.c libsqlite.a $(TESTSRC)
$(TCC) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o testfixture \
$(TESTSRC) $(TOP)/src/tclsqlite.c \
$(TESTSRC) $(TOP)/src/tclsqlite.c $(TOP)/src/btree.c \
libsqlite.a $(LIBGDBM) $(LIBTCL)
test: testfixture sqlite

View File

@ -1,7 +1,7 @@
C All\sBTree\scode\sis\sin\splace.\sNow\swe\sjust\shave\sto\smake\sit\swork.\s(CVS\s225)
D 2001-06-10T19:56:59
C The\sBTree\scode\scompiles\sand\slinks\snow,\sbut\sit\sdoes\snot\swork\syet.\s(CVS\s226)
D 2001-06-22T19:15:00
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in acef0f0275a5ca8e68bda165f7f05d810a207664
F Makefile.in 65862a30703b070209b5f5e565d75cc870962b3c
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
F VERSION 71874cb7e2a53c2bd22bb6affa7d223dd94a7a13
F configure d2051345f49f7e48604423da26e086a745c86a47 x
@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464
F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
F notes/notes3.txt 985bf688b59f1f52bfe6e4b1f896efdeffac1432
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
F src/btree.c 788e18f3c668732fc4f228fd24912bb5181b055f
F src/btree.h f9adc22e8414402c176d71088e76afa89cc0d4b1
F src/btree.c 0a2b66ce90f0bee87f0449235060529b96cc96e4
F src/btree.h 40ae2c9b6d2ba8feb03461a589ccab9afc04ec29
F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651
F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af
F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
@ -31,8 +31,8 @@ F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7
F src/expr.c c4c24c3af1eba094a816522eb0e085bed518ee16
F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7
F src/main.c 0a13c7a2beb8ce36aee43daf8c95989b200727a7
F src/pager.c 5224dc4b7f678af2b7e9affb933eb1cee5e7977e
F src/pager.h e527411d88e31085f07eba6776dc337b8b027921
F src/pager.c 30c6f10a3c0cdfca3314c07d34375dbc19a48c2f
F src/pager.h 724ac5a79b5fa704a1e1a87e421e421b3da9c1e4
F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb
F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
@ -42,10 +42,10 @@ F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 3e5906f72608f0fd4394dfbb1d7e8d35b8353677
F src/sqliteInt.h 47845c60e2e196b5409d774936a56700b1611f00
F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6
F src/tclsqlite.c 1f2bf4691a6bd81fbff1856ae4a12db24d1265f7
F src/tclsqlite.c af29a45cb4c2244a6fd032568a22d26516472b2c
F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4
F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a
F src/test3.c a1868c55e03776f2e59f713247e77c734d8badfe
F src/test3.c 405ea28287faeefc108ca362eca527731421e6bb
F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf
F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6
F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc
@ -107,7 +107,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
P d1e211fad9d787a0fdbcd11fb364d6c592c07a05
R 3aa8278a253756473c3d6ce9ea5ba81d
P d4be4709ee32bab6e78104861ed4e02d153779aa
R 0a720e1f17d26fb5cb5699a653f8a269
U drh
Z 55b535f0434199de5eef13cb7d2b84b8
Z 4c2d9d4e753474964194e60669402b32

View File

@ -1 +1 @@
d4be4709ee32bab6e78104861ed4e02d153779aa
b31c49021c260a67b7848bc077b75a7146e31c71

View File

@ -21,7 +21,7 @@
** http://www.hwaci.com/drh/
**
*************************************************************************
** $Id: btree.c,v 1.12 2001/06/10 19:56:59 drh Exp $
** $Id: btree.c,v 1.13 2001/06/22 19:15:00 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@ -73,10 +73,16 @@
** Change these typedefs when porting to new architectures.
*/
typedef unsigned int uptr;
typedef unsigned int u32;
/* typedef unsigned int u32; -- already defined in sqliteInt.h */
typedef unsigned short int u16;
typedef unsigned char u8;
/*
** This macro casts a pointer to an integer. Useful for doing
** pointer arithmetic.
*/
#define addr(X) ((uptr)X)
/*
** Forward declarations of structures used only in this file.
*/
@ -99,12 +105,24 @@ typedef struct OverflowPage OverflowPage;
/*
** This is a magic string that appears at the beginning of every
** SQLite database in order to identify the fail as a real database.
** SQLite database in order to identify the file as a real database.
*/
static const char zMagicHeader[] =
"** This file contains an SQLite 2.0 database **"
"** This file contains an SQLite 2.0 database **";
#define MAGIC_SIZE (sizeof(zMagicHeader))
/*
** This is a magic integer also used to the integrety of the database
** file. This integer is used in addition to the string above so that
** if the file is written on a little-endian architecture and read
** on a big-endian architectures (or vice versa) we can detect the
** problem.
**
** The number used was obtained at random and has no special
** significance.
*/
#define MAGIC 0xdae37528
/*
** The first page of the database file contains a magic header string
** to identify the file as an SQLite database file. It also contains
@ -121,7 +139,8 @@ static const char zMagicHeader[] =
*/
struct PageOne {
char zMagic[MAGIC_SIZE]; /* String that identifies the file as a database */
Pgno firstList; /* First free page in a list of all free pages */
int iMagic; /* Integer to verify correct byte order */
Pgno freeList; /* First free page in a list of all free pages */
int aMeta[SQLITE_N_BTREE_META]; /* User defined integers */
};
@ -156,7 +175,7 @@ struct PageHdr {
** the database page.
**
** A definition of the complete Cell structure is given below. The
** header for the cell must be defined separately in order to do some
** header for the cell must be defined first in order to do some
** of the sizing #defines that follow.
*/
struct CellHdr {
@ -164,7 +183,7 @@ struct CellHdr {
u16 nKey; /* Number of bytes in the key */
u16 iNext; /* Index in MemPage.u.aDisk[] of next cell in sorted order */
u32 nData; /* Number of bytes of data */
}
};
/*
** The minimum size of a complete Cell. The Cell must contain a header
@ -179,8 +198,8 @@ struct CellHdr {
#define MX_CELL ((SQLITE_PAGE_SIZE-sizeof(PageHdr))/MIN_CELL_SIZE)
/*
** The maximum amount of data (in bytes) that can be stored locally for a
** database entry. If the entry contains more data than this, the
** The maximum amount of payload (in bytes) that can be stored locally for
** a database entry. If the entry contains more data than this, the
** extra goes onto overflow pages.
**
** This number is chosen so that at least 4 cells will fit on every page.
@ -226,7 +245,7 @@ struct FreeBlk {
/*
** When the key and data for a single entry in the BTree will not fit in
** the MX_LOACAL_PAYLOAD bytes of space available on the database page,
** the MX_LOCAL_PAYLOAD bytes of space available on the database page,
** then all extra bytes are written to a linked list of overflow pages.
** Each overflow page is an instance of the following structure.
**
@ -278,7 +297,7 @@ struct MemPage {
int nCell; /* Number of entries on this page */
int isOverfull; /* Some apCell[] points outside u.aDisk[] */
Cell *apCell[MX_CELL+2]; /* All data entires in sorted order */
}
};
/*
** The in-memory image of a disk page has the auxiliary information appended
@ -308,7 +327,7 @@ struct BtCursor {
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
Pgno pgnoRoot; /* The root page of this tree */
MemPage *pPage; /* Page that contains the entry */
u16 idx; /* Index of the entry in pPage->apCell[] */
int idx; /* Index of the entry in pPage->apCell[] */
u8 bSkipNext; /* sqliteBtreeNext() is no-op if true */
u8 iMatch; /* compare result from last sqliteBtreeMoveto() */
};
@ -317,7 +336,7 @@ struct BtCursor {
** Compute the total number of bytes that a Cell needs on the main
** database page. The number returned includes the Cell header,
** local payload storage, and the pointer to overflow pages (if
** applicable). Additional spaced allocated on overflow pages
** applicable). Additional space allocated on overflow pages
** is NOT included in the value returned from this routine.
*/
static int cellSize(Cell *pCell){
@ -345,7 +364,13 @@ static void defragmentPage(MemPage *pPage){
pPage->u.hdr.firstCell = pc;
memcpy(newPage, pPage->u.aDisk, pc);
for(i=0; i<pPage->nCell; i++){
Cell *pCell = &pPage->apCell[i];
Cell *pCell = (Cell*)&pPage->apCell[i];
/* This routine should never be called on an overfull page. The
** following asserts verify that constraint. */
assert( addr(pCell) > addr(pPage) );
assert( addr(pCell) < addr(pPage) + SQLITE_PAGE_SIZE );
n = cellSize(pCell);
pCell->h.iNext = i<pPage->nCell-1 ? pc + n : 0;
memcpy(&newPage[pc], pCell, n);
@ -354,7 +379,7 @@ static void defragmentPage(MemPage *pPage){
}
assert( pPage->nFree==SQLITE_PAGE_SIZE-pc );
memcpy(pPage->u.aDisk, newPage, pc);
pFBlk = &pPage->u.aDisk[pc];
pFBlk = (FreeBlk*)&pPage->u.aDisk[pc];
pFBlk->iSize = SQLITE_PAGE_SIZE - pc;
pFBlk->iNext = 0;
pPage->u.hdr.firstFree = pc;
@ -378,12 +403,14 @@ static int allocateSpace(MemPage *pPage, int nByte){
FreeBlk *p;
u16 *pIdx;
int start;
int cnt = 0;
assert( nByte==ROUNDUP(nByte) );
if( pPage->nFree<nByte || pPage->isOverfull ) return 0;
pIdx = &pPage->u.hdr.firstFree;
p = (FreeBlk*)&pPage->u.aDisk[*pIdx];
while( p->iSize<nByte ){
assert( cnt++ < SQLITE_PAGE_SIZE/4 );
if( p->iNext==0 ){
defragmentPage(pPage);
pIdx = &pPage->u.hdr.firstFree;
@ -396,8 +423,9 @@ static int allocateSpace(MemPage *pPage, int nByte){
start = *pIdx;
*pIdx = p->iNext;
}else{
FreeBlk *pNew;
start = *pIdx;
FreeBlk *pNew = (FreeBlk*)&pPage->u.aDisk[start + nByte];
pNew = (FreeBlk*)&pPage->u.aDisk[start + nByte];
pNew->iNext = p->iNext;
pNew->iSize = p->iSize - nByte;
*pIdx = start + nByte;
@ -431,7 +459,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
if( idx + pFBlk->iSize == start ){
pFBlk->iSize += size;
if( idx + pFBlk->iSize == pFBlk->iNext ){
pNext = (FreeBlk*)&pPage->u.aDisk[pFblk->iNext];
pNext = (FreeBlk*)&pPage->u.aDisk[pFBlk->iNext];
pFBlk->iSize += pNext->iSize;
pFBlk->iNext = pNext->iNext;
}
@ -489,8 +517,9 @@ static int initPage(MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
freeSpace = SQLITE_PAGE_SIZE - sizeof(PageHdr);
idx = pPage->u.hdr.firstCell;
while( idx!=0 ){
if( idx>SQLITE_PAGE_SIZE-MN_CELL_SIZE ) goto page_format_error;
if( idx>SQLITE_PAGE_SIZE-MIN_CELL_SIZE ) goto page_format_error;
if( idx<sizeof(PageHdr) ) goto page_format_error;
if( idx!=ROUNDUP(idx) ) goto page_format_error;
pCell = (Cell*)&pPage->u.aDisk[idx];
sz = cellSize(pCell);
if( idx+sz > SQLITE_PAGE_SIZE ) goto page_format_error;
@ -534,6 +563,9 @@ static void zeroPage(MemPage *pPage){
pFBlk = (FreeBlk*)&pHdr[1];
pFBlk->iNext = 0;
pFBlk->iSize = SQLITE_PAGE_SIZE - sizeof(*pHdr);
pPage->nFree = pFBlk->iSize;
pPage->nCell = 0;
pPage->isOverfull = 0;
}
/*
@ -559,13 +591,14 @@ static void pageDestructor(void *pData){
*/
int sqliteBtreeOpen(const char *zFilename, int mode, Btree **ppBtree){
Btree *pBt;
int rc;
pBt = sqliteMalloc( sizeof(*pBt) );
if( pBt==0 ){
**ppBtree = 0;
*ppBtree = 0;
return SQLITE_NOMEM;
}
rc = sqlitepager_open(&pBt->pPager, zFilename, 100, EXTRA_SPACE);
rc = sqlitepager_open(&pBt->pPager, zFilename, 100, EXTRA_SIZE);
if( rc!=SQLITE_OK ){
if( pBt->pPager ) sqlitepager_close(pBt->pPager);
sqliteFree(pBt);
@ -604,7 +637,7 @@ int sqliteBtreeClose(Btree *pBt){
static int lockBtree(Btree *pBt){
int rc;
if( pBt->page1 ) return SQLITE_OK;
rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1);
rc = sqlitepager_get(pBt->pPager, 1, (void**)&pBt->page1);
if( rc!=SQLITE_OK ) return rc;
/* Do some checking to help insure the file we opened really is
@ -612,7 +645,7 @@ static int lockBtree(Btree *pBt){
*/
if( sqlitepager_pagecount(pBt->pPager)>0 ){
PageOne *pP1 = pBt->page1;
if( strcmp(pP1->zMagic1,zMagicHeader)!=0 ){
if( strcmp(pP1->zMagic,zMagicHeader)!=0 || pP1->iMagic!=MAGIC ){
rc = SQLITE_CORRUPT;
goto page1_init_failed;
}
@ -626,16 +659,18 @@ page1_init_failed:
}
/*
** Create a new database by initializing the first two pages.
** Create a new database by initializing the first two pages of the
** file.
*/
static int newDatabase(Btree *pBt){
MemPage *pRoot;
PageOne *pP1;
int rc;
if( sqlitepager_pagecount(pBt->pPager)>0 ) return SQLITE_OK;
pP1 = pBt->page1;
rc = sqlitepager_write(pBt->page1);
if( rc ) return rc;
rc = sqlitepager_get(pBt->pPager, 2, &pRoot);
rc = sqlitepager_get(pBt->pPager, 2, (void**)&pRoot);
if( rc ) return rc;
rc = sqlitepager_write(pRoot);
if( rc ){
@ -643,6 +678,7 @@ static int newDatabase(Btree *pBt){
return rc;
}
strcpy(pP1->zMagic, zMagicHeader);
pP1->iMagic = MAGIC;
zeroPage(pRoot);
sqlitepager_unref(pRoot);
return SQLITE_OK;
@ -664,17 +700,20 @@ static int newDatabase(Btree *pBt){
*/
int sqliteBtreeBeginTrans(Btree *pBt){
int rc;
PageOne *pP1;
if( pBt->inTrans ) return SQLITE_ERROR;
if( pBt->page1==0 ){
rc = lockBtree(pBt);
if( rc!=SQLITE_OK ) return rc;
if( rc!=SQLITE_OK ){
return rc;
}
}
rc = sqlitepager_write(pBt->page1);
if( rc==SQLITE_OK ){
pBt->inTrans = 1;
if( rc!=SQLITE_OK ){
return rc;
}
return newDatabase(pBt);
pBt->inTrans = 1;
rc = newDatabase(pBt);
return rc;
}
/*
@ -734,7 +773,7 @@ int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){
goto create_cursor_exception;
}
pCur->pgnoRoot = (Pgno)iTable;
rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, &pCur->pPage);
rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage);
if( rc!=SQLITE_OK ){
goto create_cursor_exception;
}
@ -759,7 +798,7 @@ create_cursor_exception:
if( pCur->pPage ) sqlitepager_unref(pCur->pPage);
sqliteFree(pCur);
}
unlinkBtree(pBt);
unlockBtree(pBt);
return rc;
}
@ -769,7 +808,6 @@ create_cursor_exception:
*/
int sqliteBtreeCloseCursor(BtCursor *pCur){
Btree *pBt = pCur->pBt;
int i;
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
}else{
@ -781,6 +819,7 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){
sqlitepager_unref(pCur->pPage);
unlockBtree(pBt);
sqliteFree(pCur);
return SQLITE_OK;
}
/*
@ -819,7 +858,7 @@ int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
*pSize = 0;
}else{
pCell = pPage->apCell[pCur->idx];
*psize = pCell->h.nKey;
*pSize = pCell->h.nKey;
}
return SQLITE_OK;
}
@ -835,9 +874,10 @@ int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
char *aPayload;
Pgno nextPage;
int rc;
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pCur->nCell );
aPayload = pCur->pPage->apCell[pCur->idx].aPayload;
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
aPayload = pCur->pPage->apCell[pCur->idx]->aPayload;
if( offset<MX_LOCAL_PAYLOAD ){
int a = amt;
if( a+offset>MX_LOCAL_PAYLOAD ){
@ -852,11 +892,11 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
amt -= a;
}
if( amt>0 ){
nextPage = pCur->pPage->apCell[pCur->idx].ovfl;
nextPage = pCur->pPage->apCell[pCur->idx]->ovfl;
}
while( amt>0 && nextPage ){
OverflowPage *pOvfl;
rc = sqlitepager_get(pCur->pBt->pPager, nextPage, &pOvfl);
rc = sqlitepager_get(pCur->pBt->pPager, nextPage, (void**)&pOvfl);
if( rc!=0 ){
return rc;
}
@ -964,7 +1004,7 @@ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){
static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
Pgno nextPage;
int nKey = nKeyOrig;
int n;
int n, c, rc;
Cell *pCell;
assert( pCur->pPage );
@ -990,7 +1030,7 @@ static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
if( nextPage==0 ){
return SQLITE_CORRUPT;
}
rc = sqlitepager_get(pCur->pBt->pPager, nextPage, &pOvfl);
rc = sqlitepager_get(pCur->pBt->pPager, nextPage, (void**)&pOvfl);
if( rc ){
return rc;
}
@ -1020,7 +1060,7 @@ static int moveToChild(BtCursor *pCur, int newPgno){
int rc;
MemPage *pNewPage;
rc = sqlitepager_get(pCur->pBt->pPager, newPgno, &pNewPage);
rc = sqlitepager_get(pCur->pBt->pPager, newPgno, (void**)&pNewPage);
if( rc ){
return rc;
}
@ -1042,16 +1082,16 @@ static int moveToChild(BtCursor *pCur, int newPgno){
static int moveToParent(BtCursor *pCur){
Pgno oldPgno;
MemPage *pParent;
int i;
pParent = pCur->pPage->pParent;
if( pParent==0 ) return SQLITE_INTERNAL;
oldPgno = sqlitepager_pagenumber(pCur->pPage);
sqlitepager_ref(pParent);
sqlitepager_unref(pCur->pPage);
pCur->pPage = pParent;
pCur->idx = pPage->nCell;
for(i=0; i<pPage->nCell; i++){
if( pPage->apCell[i].h.leftChild==oldPgno ){
pCur->idx = pParent->nCell;
for(i=0; i<pParent->nCell; i++){
if( pParent->apCell[i]->h.leftChild==oldPgno ){
pCur->idx = i;
break;
}
@ -1066,7 +1106,7 @@ static int moveToRoot(BtCursor *pCur){
MemPage *pNew;
int rc;
rc = sqlitepager_get(pCur->pBt->pPager, pCur->pgnoRoot, &pNew);
rc = sqlitepager_get(pCur->pBt->pPager, pCur->pgnoRoot, (void**)&pNew);
if( rc ) return rc;
sqlitepager_unref(pCur->pPage);
pCur->pPage = pNew;
@ -1170,8 +1210,8 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
}
pCur->idx++;
if( pCur->idx>=pCur->pPage->nCell ){
if( pPage->u.hdr.rightChild ){
rc = moveToChild(pCur, pPage->u.hdr.rightChild);
if( pCur->pPage->u.hdr.rightChild ){
rc = moveToChild(pCur, pCur->pPage->u.hdr.rightChild);
if( rc ) return rc;
rc = moveToLeftmost(pCur);
if( rc ) return rc;
@ -1179,7 +1219,7 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
return SQLITE_OK;
}
do{
if( pCur->pParent==0 ){
if( pCur->pPage->pParent==0 ){
if( pRes ) *pRes = 1;
return SQLITE_OK;
}
@ -1209,12 +1249,13 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
*/
static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
PageOne *pPage1 = pBt->page1;
int rc;
if( pPage1->freeList ){
OverflowPage *pOvfl;
rc = sqlitepager_write(pPage1);
if( rc ) return rc;
*pPgno = pPage1->freeList;
rc = sqlitepager_get(pBt->pPager, pPage1->freeList, &pOvfl);
rc = sqlitepager_get(pBt->pPager, pPage1->freeList, (void**)&pOvfl);
if( rc ) return rc;
rc = sqlitepager_write(pOvfl);
if( rc ){
@ -1225,7 +1266,7 @@ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
*ppPage = (MemPage*)pOvfl;
}else{
*pPgno = sqlitepager_pagecount(pBt->pPager);
rc = sqlitepager_get(pBt->pPager, *pPgno, ppPage);
rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage);
if( rc ) return rc;
rc = sqlitepager_write(*ppPage);
}
@ -1255,7 +1296,7 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
}
if( pOvfl==0 ){
assert( pgno>0 );
rc = sqlitepager_get(pBt->pPager, pgno, &pOvfl);
rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pOvfl);
if( rc ) return rc;
needOvflUnref = 1;
}
@ -1267,8 +1308,8 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
pOvfl->iNext = pPage1->freeList;
pPage1->freeList = pgno;
memset(pOvfl->aPayload, 0, OVERFLOW_SIZE);
pPage->isInit = 0;
assert( pPage->pParent==0 );
((MemPage*)pPage)->isInit = 0;
assert( ((MemPage*)pPage)->pParent==0 );
rc = sqlitepager_unref(pOvfl);
return rc;
}
@ -1289,7 +1330,7 @@ static int clearCell(Btree *pBt, Cell *pCell){
ovfl = pCell->ovfl;
pCell->ovfl = 0;
while( ovfl ){
rc = sqlitepager_get(pPager, ovfl, &pOvfl);
rc = sqlitepager_get(pPager, ovfl, (void**)&pOvfl);
if( rc ) return rc;
nextOvfl = pOvfl->iNext;
rc = freePage(pBt, pOvfl, ovfl);
@ -1310,10 +1351,10 @@ static int fillInCell(
void *pKey, int nKey, /* The key */
void *pData,int nData /* The data */
){
int OverflowPage *pOvfl;
OverflowPage *pOvfl;
Pgno *pNext;
int spaceLeft;
int n;
int n, rc;
int nPayload;
char *pPayload;
char *pSpace;
@ -1331,7 +1372,7 @@ static int fillInCell(
nPayload = nKey;
while( nPayload>0 ){
if( spaceLeft==0 ){
rc = allocatePage(pBt, &pOvfl, pNext);
rc = allocatePage(pBt, (MemPage**)&pOvfl, pNext);
if( rc ){
*pNext = 0;
clearCell(pBt, pCell);
@ -1339,7 +1380,7 @@ static int fillInCell(
}
spaceLeft = OVERFLOW_SIZE;
pSpace = pOvfl->aPayload;
pNextPg = &pOvfl->iNext;
pNext = &pOvfl->iNext;
}
n = nPayload;
if( n>spaceLeft ) n = spaceLeft;
@ -1383,10 +1424,10 @@ static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent){
** This routine gets called after you memcpy() one page into
** another.
*/
static void reparentChildPages(Pager *pPager, Page *pPage){
static void reparentChildPages(Pager *pPager, MemPage *pPage){
int i;
for(i=0; i<pPage->nCell; i++){
reparentPage(pPager, pPage->apCell[i]->leftChild, pPage);
reparentPage(pPager, pPage->apCell[i]->h.leftChild, pPage);
}
reparentPage(pPager, pPage->u.hdr.rightChild, pPage);
}
@ -1400,16 +1441,16 @@ static void reparentChildPages(Pager *pPager, Page *pPage){
** "sz" must be the number of bytes in the cell.
**
** Do not bother maintaining the integrity of the linked list of Cells.
** Only pPage->apCell[] is important. The relinkCellList() routine
** will be called soon after this routine in order to rebuild the
** linked list.
** Only the pPage->apCell[] array is important. The relinkCellList()
** routine will be called soon after this routine in order to rebuild
** the linked list.
*/
static void dropCell(MemPage *pPage, int i, int sz){
static void dropCell(MemPage *pPage, int idx, int sz){
int j;
assert( i>=0 && i<pPage->nCell );
assert( sz==cellSize(pPage->apCell[i]);
assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pPage->apCell[idx]) );
freeSpace(pPage, idx, sz);
for(j=i, j<pPage->nCell-2; j++){
for(j=idx; j<pPage->nCell-2; j++){
pPage->apCell[j] = pPage->apCell[j+1];
}
pPage->nCell--;
@ -1424,9 +1465,9 @@ static void dropCell(MemPage *pPage, int i, int sz){
** and set pPage->isOverfull.
**
** Do not bother maintaining the integrity of the linked list of Cells.
** Only pPage->apCell[] is important. The relinkCellList() routine
** will be called soon after this routine in order to rebuild the
** linked list.
** Only the pPage->apCell[] array is important. The relinkCellList()
** routine will be called soon after this routine in order to rebuild
** the linked list.
*/
static void insertCell(MemPage *pPage, int i, Cell *pCell, int sz){
int idx, j;
@ -1442,22 +1483,23 @@ static void insertCell(MemPage *pPage, int i, Cell *pCell, int sz){
pPage->apCell[i] = pCell;
}else{
memcpy(&pPage->u.aDisk[idx], pCell, sz);
pPage->apCell[i] = (Cell*)&pPage->u.aDisk[idx]);
pPage->apCell[i] = (Cell*)&pPage->u.aDisk[idx];
}
}
/*
** Rebuild the linked list of cells on a page so that the cells
** occur in the order specified by pPage->apCell[]. Invoke this
** routine once to repair damage after one or more invocations
** of either insertCell() or dropCell().
** occur in the order specified by the pPage->apCell[] array.
** Invoke this routine once to repair damage after one or more
** invocations of either insertCell() or dropCell().
*/
static void relinkCellList(MemPage *pPage){
int i;
u16 *pIdx;
pIdx = &pPage->u.hdr.firstCell;
for(i=0; i<pPage->nCell; i++){
int idx = ((uptr)pPage->apCell[i]) - (uptr)pPage;
int idx = addr(pPage->apCell[i]) - addr(pPage);
assert( idx>0 && idx<SQLITE_PAGE_SIZE );
*pIdx = idx;
pIdx = &pPage->apCell[i]->h.iNext;
}
@ -1479,12 +1521,12 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
pTo->nCell = pFrom->nCell;
pTo->nFree = pFrom->nFree;
pTo->isOverfull = pFrom->isOverfull;
to = (unsigned int)pTo;
from = (unsigned int)pFrom;
to = addr(pTo);
from = addr(pFrom);
for(i=0; i<pTo->nCell; i++){
uptr addr = (uptr)(pFrom->apCell[i]);
if( addr>from && addr<from+SQLITE_PAGE_SIZE ){
*((uptr*)&pTo->apCell[i]) = addr + to - from;
uptr x = addr(pFrom->apCell[i]);
if( x>from && x<from+SQLITE_PAGE_SIZE ){
*((uptr*)&pTo->apCell[i]) = x + to - from;
}
}
}
@ -1499,8 +1541,9 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
** child of root) then all available siblings participate in the balancing.
**
** The number of siblings of pPage might be increased or decreased by
** one in order to keep all pages between 2/3 and completely full. If
** pPage is the root page, then the depth of the tree might be increased
** one in an effort to keep pages between 66% and 100% full. The root page
** is special and is allowed to be less than 66% full. If pPage is
** the root page, then the depth of the tree might be increased
** or decreased by one, as necessary, to keep the root page from being
** overfull or empty.
**
@ -1511,13 +1554,19 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
** routines left behind.
**
** pCur is left pointing to the same cell as when this routine was called
** event if that cell gets moved to a different page. pCur may be NULL.
** even if that cell gets moved to a different page. pCur may be NULL.
** Set the pCur parameter to NULL if you do not care about keeping track
** of a cell as that will save this routine the work of keeping track of it.
**
** Note that when this routine is called, some of the Cells on pPage
** might not actually be stored in pPage->u.aDisk[]. This can happen
** if the page is overfull. Part of the job of this routine is to
** make sure all Cells for pPage once again fit in pPage->u.aDisk[].
**
** In the course of balancing the siblings of pPage, the parent of pPage
** might become overfull or underfull. If that happens, then this routine
** is called recursively on the parent.
**
** If this routine fails for any reason, it means the database may have
** been left in a corrupted state and should be rolled back.
*/
@ -1532,7 +1581,6 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
int nCell; /* Number of cells in apCell[] */
int nOld; /* Number of pages in apOld[] */
int nNew; /* Number of pages in apNew[] */
int perPage; /* Approximate number of bytes per page */
int nDiv; /* Number of cells in apDiv[] */
int i, j, k; /* Loop counters */
int idx; /* Index of pPage in pParent->apCell[] */
@ -1541,6 +1589,8 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
int iCur; /* apCell[iCur] is the cell of the cursor */
int usedPerPage; /* Memory needed for each page */
int freePerPage; /* Average free space per page */
int totalSize; /* Total bytes for all cells */
Pgno pgno; /* Page number */
Cell *apCell[MX_CELL*3+5]; /* All cells from pages being balanceed */
int szCell[MX_CELL*3+5]; /* Local size of all cells */
Cell aTemp[2]; /* Temporary holding area for apDiv[] */
@ -1563,7 +1613,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
pParent = pPage->pParent;
if( pParent==0 ){
Pgno pgnoChild;
Page *pChild;
MemPage *pChild;
if( pPage->nCell==0 ){
if( pPage->u.hdr.rightChild ){
/*
@ -1574,7 +1624,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
rc = sqlitepager_write(pPage);
if( rc ) return rc;
pgnoChild = pPage->u.hdr.rightChild;
rc = sqlitepager_get(pBt, pgnoChild, &pChild);
rc = sqlitepager_get(pBt->pPager, pgnoChild, (void**)&pChild);
if( rc ) return rc;
memcpy(pPage, pChild, SQLITE_PAGE_SIZE);
pPage->isInit = 0;
@ -1668,11 +1718,11 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
nDiv++;
pgnoOld[i] = apDiv[i]->h.leftChild;
}else if( k==pParent->nCell ){
pgnoOld[i] = pParent->rightChild;
pgnoOld[i] = pParent->u.hdr.rightChild;
}else{
break;
}
rc = sqlitepager_get(pBt, pgnoOld[i], &apOld[i]);
rc = sqlitepager_get(pBt->pPager, pgnoOld[i], (void**)&apOld[i]);
if( rc ) goto balance_cleanup;
nOld++;
}
@ -1719,7 +1769,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
}
if( i<nOld-1 ){
szCell[nCell] = cellSize(apDiv[i]);
memcpy(aTemp[i], apDiv[i], szCell[nCell]);
memcpy(&aTemp[i], apDiv[i], szCell[nCell]);
apCell[nCell] = &aTemp[i];
dropCell(pParent, nxDiv, szCell[nCell]);
assert( apCell[nCell]->h.leftChild==pgnoOld[i] );
@ -1878,6 +1928,7 @@ int sqliteBtreeDelete(BtCursor *pCur){
MemPage *pPage = pCur->pPage;
Cell *pCell;
int rc;
Pgno pgnoChild;
if( !pCur->pBt->inTrans ){
return SQLITE_ERROR; /* Must start a transaction first */
@ -1889,13 +1940,13 @@ int sqliteBtreeDelete(BtCursor *pCur){
if( rc ) return rc;
pCell = pPage->apCell[pCur->idx];
pgnoChild = pCell->h.leftChild;
clearCell(pCell);
clearCell(pCur->pBt, pCell);
dropCell(pPage, pCur->idx, cellSize(pCell));
if( pgnoChild ){
/*
** If the entry we just deleted is not a leaf, then we've left a
** whole in an internal page. We have to fill the whole by moving
** in a page from a leaf. The next Cell after the one just deleted
** hole in an internal page. We have to fill the hole by moving
** in a cell from a leaf. The next Cell after the one just deleted
** is guaranteed to exist and to be a leaf so we can use it.
*/
BtCursor leafCur;
@ -1906,15 +1957,16 @@ int sqliteBtreeDelete(BtCursor *pCur){
if( rc!=SQLITE_OK ){
return SQLITE_CORRUPT;
}
pNext = leafCur.pPage->apCell[leafCur.idx]
pNext = leafCur.pPage->apCell[leafCur.idx];
szNext = cellSize(pNext);
pNext->h.leftChild = pgnoChild;
insertCell(pPage, pCur->idx, pNext, szNext);
rc = balance(pCur->pBt, pPage, pCur);
if( rc ) return rc;
pCur->bSkipNext = 1;
dropCell(leafCur.pPage, leafCur.idx, szNext);
rc = balance(pCur->pBt, leafCur.pPage, 0);
releaseTempCur(&leafCur);
releaseTempCursor(&leafCur);
}else{
rc = balance(pCur->pBt, pPage, pCur);
pCur->bSkipNext = 1;
@ -1949,11 +2001,10 @@ int sqliteBtreeCreateTable(Btree *pBt, int *piTable){
static int clearDatabasePage(Btree *pBt, Pgno pgno){
MemPage *pPage;
int rc;
int i;
Cell *pCell;
int idx;
rc = sqlitepager_get(pBt->pPager, pgno, &pPage);
rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pPage);
if( rc ) return rc;
idx = pPage->u.hdr.firstCell;
while( idx>0 ){
@ -1963,9 +2014,11 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno){
rc = clearDatabasePage(pBt, pCell->h.leftChild);
if( rc ) return rc;
}
rc = clearCell(pCell);
rc = clearCell(pBt, pCell);
if( rc ) return rc;
}
rc = clearDatabasePage(pBt, pPage->u.hdr.rightChild);
if( rc ) return rc;
return freePage(pBt, pPage, pgno);
}
@ -1980,8 +2033,8 @@ int sqliteBtreeClearTable(Btree *pBt, int iTable){
rc = clearDatabasePage(pBt, (Pgno)iTable);
if( rc ){
sqliteBtreeRollback(pBt);
return rc;
}
return rc;
}
/*
@ -1995,7 +2048,7 @@ int sqliteBtreeDropTable(Btree *pBt, int iTable){
if( !pBt->inTrans ){
return SQLITE_ERROR; /* Must start a transaction first */
}
rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, &pPage);
rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage);
if( rc==SQLITE_OK ){
rc = sqliteBtreeClearTable(pBt, iTable);
}
@ -2013,7 +2066,7 @@ int sqliteBtreeGetMeta(Btree *pBt, int *aMeta){
PageOne *pP1;
int rc;
rc = sqlitepager_get(pBt->pPager, 1, &pP1);
rc = sqlitepager_get(pBt->pPager, 1, (void**)&pP1);
if( rc ) return rc;
memcpy(aMeta, pP1->aMeta, sizeof(pP1->aMeta));
sqlitepager_unref(pP1);
@ -2035,3 +2088,74 @@ int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){
memcpy(pP1->aMeta, aMeta, sizeof(pP1->aMeta));
return SQLITE_OK;
}
#ifdef SQLITE_TEST
/*
** Print a disassembly of the given page on standard output. This routine
** is used for debugging and testing only.
*/
int sqliteBtreePageDump(Btree *pBt, int pgno){
int rc;
MemPage *pPage;
int i, j;
int nFree;
u16 idx;
char range[20];
unsigned char payload[20];
rc = sqlitepager_get(pBt->pPager, (Pgno)pgno, (void**)&pPage);
if( rc ){
return rc;
}
i = 0;
idx = pPage->u.hdr.firstCell;
while( idx>0 && idx<=SQLITE_PAGE_SIZE-MIN_CELL_SIZE ){
Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
int sz = cellSize(pCell);
sprintf(range,"%d..%d", idx, idx+sz-1);
if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1;
memcpy(payload, pCell->aPayload, sz);
for(j=0; j<sz; j++){
if( payload[j]<0x20 || payload[j]>0x7f ) payload[j] = '.';
}
payload[sz] = 0;
printf(
"cell %2d: i=%-10s chld=%-4d nk=%-3d nd=%-3d payload=%s\n",
i, range, (int)pCell->h.leftChild, pCell->h.nKey, pCell->h.nData,
pCell->aPayload
);
idx = pCell->h.iNext;
}
if( idx!=0 ){
printf("ERROR: next cell index out of range: %d\n", idx);
}
printf("right_child: %d\n", pPage->u.hdr.rightChild);
nFree = 0;
i = 0;
idx = pPage->u.hdr.firstFree;
while( idx>0 && idx<SQLITE_PAGE_SIZE ){
FreeBlk *p = (FreeBlk*)&pPage->u.aDisk[idx];
sprintf(range,"%d..%d", idx, idx+p->iSize-1);
nFree += p->iSize;
printf("freeblock %2d: i=%-10s size=%-4d total=%d\n",
i, range, p->iSize, nFree);
idx = p->iNext;
}
if( idx!=0 ){
printf("ERROR: next freeblock index out of range: %d\n", idx);
}
sqlitepager_unref(pPage);
return SQLITE_OK;
}
#endif
#ifdef SQLITE_TEST
/*
** Put the page number and index of a cursor into aResult[0] and aResult[1]
** This routine is used for debugging and testing only.
*/
int sqliteBtreeCursorDump(BtCursor *pCur, int *aResult){
aResult[0] = sqlitepager_pagenumber(pCur->pPage);
aResult[1] = pCur->idx;
return SQLITE_OK;
}
#endif

View File

@ -24,7 +24,7 @@
** This header file defines the interface that the sqlite B-Tree file
** subsystem.
**
** @(#) $Id: btree.h,v 1.4 2001/06/08 00:21:53 drh Exp $
** @(#) $Id: btree.h,v 1.5 2001/06/22 19:15:00 drh Exp $
*/
typedef struct Btree Btree;
@ -42,7 +42,7 @@ int sqliteBtreeDropTable(Btree*, int);
int sqliteBtreeClearTable(Btree*, int);
int sqliteBtreeCursor(Btree*, int iTable, BtCursor **ppCur);
int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, *pRes);
int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, int *pRes);
int sqliteBtreeDelete(BtCursor*);
int sqliteBtreeInsert(BtCursor*, void *pKey, int nKey, void *pData, int nData);
int sqliteBtreeNext(BtCursor*, int *pRes);
@ -55,3 +55,9 @@ int sqliteBtreeCloseCursor(BtCursor*);
#define SQLITE_N_BTREE_META 3
int sqliteBtreeGetMeta(Btree*, int*);
int sqliteBtreeUpdateMeta(Btree*, int*);
#ifdef SQLITE_TEST
int sqliteBtreePageDump(Btree*, int);
int sqliteBtreeCursorDump(BtCursor*, int*);
#endif

View File

@ -27,7 +27,7 @@
** all writes in order to support rollback. Locking is used to limit
** access to one or more reader or one writer.
**
** @(#) $Id: pager.c,v 1.8 2001/06/02 02:40:57 drh Exp $
** @(#) $Id: pager.c,v 1.9 2001/06/22 19:15:00 drh Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
@ -562,7 +562,8 @@ Pgno sqlitepager_pagenumber(void *pData){
** currently on the freelist (the reference count is zero) then
** remove it from the freelist.
*/
static void sqlitepager_ref(PgHdr *pPg){
int sqlitepager_ref(void *pData){
PgHdr *pPg = DATA_TO_PGHDR(pData);
if( pPg->nRef==0 ){
/* The page is currently on the freelist. Remove it. */
if( pPg->pPrevFree ){
@ -578,6 +579,7 @@ static void sqlitepager_ref(PgHdr *pPg){
pPg->pPager->nRef++;
}
pPg->nRef++;
return SQLITE_OK;
}
/*

View File

@ -25,7 +25,7 @@
** subsystem. The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.4 2001/05/24 21:06:36 drh Exp $
** @(#) $Id: pager.h,v 1.5 2001/06/22 19:15:01 drh Exp $
*/
/*
@ -45,10 +45,11 @@ typedef unsigned int Pgno;
typedef struct Pager Pager;
int sqlitepager_open(Pager **ppPager,const char *zFilename,int nPage,int nEx);
void sqiltepager_set_destructor(Pager*, void(*)(void*));
void sqlitepager_set_destructor(Pager*, void(*)(void*));
int sqlitepager_close(Pager *pPager);
int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage);
void *sqlitepager_lookup(Pager *pPager, Pgno pgno);
int sqlitepager_ref(void*);
int sqlitepager_unref(void*);
Pgno sqlitepager_pagenumber(void*);
int sqlitepager_write(void*);

View File

@ -23,7 +23,7 @@
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.18 2001/04/15 00:37:09 drh Exp $
** $Id: tclsqlite.c,v 1.19 2001/06/22 19:15:01 drh Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
@ -511,8 +511,10 @@ int TCLSH_MAIN(int argc, char **argv){
{
extern int Sqlitetest1_Init(Tcl_Interp*);
extern int Sqlitetest2_Init(Tcl_Interp*);
extern int Sqlitetest3_Init(Tcl_Interp*);
Sqlitetest1_Init(interp);
Sqlitetest2_Init(interp);
Sqlitetest3_Init(interp);
}
#endif
if( argc>=2 ){

View File

@ -25,7 +25,7 @@
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
** $Id: test3.c,v 1.1 2001/06/02 02:40:57 drh Exp $
** $Id: test3.c,v 1.2 2001/06/22 19:15:01 drh Exp $
*/
#include "sqliteInt.h"
#include "pager.h"
@ -71,8 +71,7 @@ static int btree_open(
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BTree *pBt;
int nPage;
Btree *pBt;
int rc;
char zBuf[100];
if( argc!=2 ){
@ -155,7 +154,7 @@ static int btree_rollback(
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
Btree *pBt
Btree *pBt;
int rc;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
@ -239,16 +238,16 @@ static int btree_drop_table(
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
Pager *pPager;
Btree *pBt;
int iTable;
char zBuf[100];
int rc;
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID TABLENUM\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
if( Tcl_GetInt(interp, argv[2], &iTable ) return TCL_ERROR;
if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
rc = sqliteBtreeDropTable(pBt, iTable);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, errorName(rc), 0);
@ -257,6 +256,390 @@ static int btree_drop_table(
return TCL_OK;
}
/*
** Usage: btree_get_meta ID
**
** Return meta data
*/
static int btree_get_meta(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
Btree *pBt;
int rc;
int i;
int aMeta[SQLITE_N_BTREE_META];
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
rc = sqliteBtreeGetMeta(pBt, aMeta);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
for(i=0; i<SQLITE_N_BTREE_META; i++){
char zBuf[30];
sprintf(zBuf,"%d",aMeta[i]);
Tcl_AppendElement(interp, zBuf);
}
return TCL_OK;
}
/*
** Usage: btree_update_meta ID METADATA...
**
** Return meta data
*/
static int btree_update_meta(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
Btree *pBt;
int rc;
int i;
int aMeta[SQLITE_N_BTREE_META];
if( argc!=2+SQLITE_N_BTREE_META ){
char zBuf[30];
sprintf(zBuf,"%d",SQLITE_N_BTREE_META);
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID METADATA...\" (METADATA is ", zBuf, " integers)", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
for(i=0; i<SQLITE_N_BTREE_META; i++){
if( Tcl_GetInt(interp, argv[i+2], &aMeta[i]) ) return TCL_ERROR;
}
rc = sqliteBtreeUpdateMeta(pBt, aMeta);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** Usage: btree_page_dump ID PAGENUM
**
** Print a disassembly of a page on standard output
*/
static int btree_page_dump(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
Btree *pBt;
int iPage;
int rc;
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
if( Tcl_GetInt(interp, argv[2], &iPage) ) return TCL_ERROR;
rc = sqliteBtreePageDump(pBt, iPage);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** Usage: btree_cursor ID TABLENUM
**
** Create a new cursor. Return the ID for the cursor.
*/
static int btree_cursor(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
Btree *pBt;
int iTable;
BtCursor *pCur;
int rc;
char zBuf[30];
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID TABLENUM\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
rc = sqliteBtreeCursor(pBt, iTable, &pCur);
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
sprintf(zBuf,"0x%x", (int)pCur);
Tcl_AppendResult(interp, zBuf, 0);
return SQLITE_OK;
}
/*
** Usage: btree_close_cursor ID
**
** Close a cursor opened using btree_cursor.
*/
static int btree_close_cursor(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
rc = sqliteBtreeCloseCursor(pCur);
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
return SQLITE_OK;
}
/*
** Usage: btree_move_to ID KEY
**
** Move the cursor to the entry with the given key.
*/
static int btree_move_to(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
int res;
char zBuf[20];
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID KEY\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
rc = sqliteBtreeMoveto(pCur, argv[2], strlen(argv[2]), &res);
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
sprintf(zBuf,"%d",res);
Tcl_AppendResult(interp, zBuf, 0);
return SQLITE_OK;
}
/*
** Usage: btree_delete ID
**
** Delete the entry that the cursor is pointing to
*/
static int btree_delete(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
rc = sqliteBtreeDelete(pCur);
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
return SQLITE_OK;
}
/*
** Usage: btree_insert ID KEY DATA
**
** Create a new entry with the given key and data. If an entry already
** exists with the same key the old entry is overwritten.
*/
static int btree_insert(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
if( argc!=4 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID KEY DATA\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
rc = sqliteBtreeInsert(pCur, argv[2], strlen(argv[2]),
argv[3], strlen(argv[3]));
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
return SQLITE_OK;
}
/*
** Usage: btree_next ID
**
** Move the cursor to the next entry in the table.
*/
static int btree_next(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
rc = sqliteBtreeNext(pCur, 0);
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
return SQLITE_OK;
}
/*
** Usage: btree_key ID
**
** Return the key for the entry at which the cursor is pointing.
*/
static int btree_key(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
int n;
char *zBuf;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
sqliteBtreeKeySize(pCur, &n);
zBuf = malloc( n+1 );
rc = sqliteBtreeKey(pCur, 0, n, zBuf);
if( rc ){
free(zBuf);
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
zBuf[n] = 0;
Tcl_AppendResult(interp, zBuf, 0);
free(zBuf);
return SQLITE_OK;
}
/*
** Usage: btree_data ID
**
** Return the data for the entry at which the cursor is pointing.
*/
static int btree_data(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
int n;
char *zBuf;
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
sqliteBtreeDataSize(pCur, &n);
zBuf = malloc( n+1 );
rc = sqliteBtreeData(pCur, 0, n, zBuf);
if( rc ){
free(zBuf);
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
zBuf[n] = 0;
Tcl_AppendResult(interp, zBuf, 0);
free(zBuf);
return SQLITE_OK;
}
/*
** Usage: btree_cursor_dump ID
**
** Return two integers which are the page number and cell index for
** the given cursor.
*/
static int btree_cursor_dump(
void *NotUsed,
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int argc, /* Number of arguments */
char **argv /* Text of each argument */
){
BtCursor *pCur;
int rc;
int aResult[2];
char zBuf[50];
if( argc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ID\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
rc = sqliteBtreeCursorDump(pCur, aResult);
if( rc ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
}
sprintf(zBuf,"%d %d",aResult[0], aResult[1]);
Tcl_AppendResult(interp, zBuf, 0);
return SQLITE_OK;
}
/*
** Register commands with the TCL interpreter.
*/
@ -269,5 +652,17 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
Tcl_CreateCommand(interp, "btree_rollback", btree_rollback, 0, 0);
Tcl_CreateCommand(interp, "btree_create_table", btree_create_table, 0, 0);
Tcl_CreateCommand(interp, "btree_drop_table", btree_drop_table, 0, 0);
Tcl_CreateCommand(interp, "btree_get_meta", btree_get_meta, 0, 0);
Tcl_CreateCommand(interp, "btree_update_meta", btree_update_meta, 0, 0);
Tcl_CreateCommand(interp, "btree_page_dump", btree_page_dump, 0, 0);
Tcl_CreateCommand(interp, "btree_cursor", btree_cursor, 0, 0);
Tcl_CreateCommand(interp, "btree_close_cursor", btree_close_cursor, 0, 0);
Tcl_CreateCommand(interp, "btree_move_to", btree_move_to, 0, 0);
Tcl_CreateCommand(interp, "btree_delete", btree_delete, 0, 0);
Tcl_CreateCommand(interp, "btree_insert", btree_insert, 0, 0);
Tcl_CreateCommand(interp, "btree_next", btree_next, 0, 0);
Tcl_CreateCommand(interp, "btree_key", btree_key, 0, 0);
Tcl_CreateCommand(interp, "btree_data", btree_data, 0, 0);
Tcl_CreateCommand(interp, "btree_cursor_dump", btree_cursor_dump, 0, 0);
return TCL_OK;
}