diff options
author | drh <drh@noemail.net> | 2004-04-26 14:10:20 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2004-04-26 14:10:20 +0000 |
commit | 3aac2dd7bccac440b6cb97b7baf17c9c6c3c2708 (patch) | |
tree | 5d02b3a977c8d5e4f710a0c65b59923a3f2a863f | |
parent | 9e572e608fd19ca7b173808061282304d16dd8b0 (diff) | |
download | sqlite-3aac2dd7bccac440b6cb97b7baf17c9c6c3c2708.tar.gz sqlite-3aac2dd7bccac440b6cb97b7baf17c9c6c3c2708.zip |
Pager tests working. (CVS 1308)
FossilOrigin-Name: 910067a200c4b25b5d813a84146673d3d1c80952
-rw-r--r-- | main.mk | 20 | ||||
-rw-r--r-- | manifest | 34 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/btree.c | 1157 | ||||
-rw-r--r-- | src/btree.h | 167 | ||||
-rw-r--r-- | src/md5.c | 2 | ||||
-rw-r--r-- | src/pager.c | 176 | ||||
-rw-r--r-- | src/pager.h | 66 | ||||
-rw-r--r-- | src/sqliteInt.h | 8 | ||||
-rw-r--r-- | src/tclsqlite.c | 12 | ||||
-rw-r--r-- | src/test2.c | 55 | ||||
-rw-r--r-- | src/util.c | 4 | ||||
-rw-r--r-- | test/pager.test | 15 | ||||
-rw-r--r-- | test/tester.tcl | 7 |
14 files changed, 873 insertions, 852 deletions
@@ -54,7 +54,10 @@ TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src # Object files for the SQLite library. # -LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ +LIBOBJ = hash.o os.o pager.o random.o \ + util.o tclsqlite.o + +LIBOBJ_ORIG = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ expr.o func.o hash.o insert.o \ main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \ select.o table.o tokenize.o trigger.o update.o util.o \ @@ -63,6 +66,15 @@ LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ # All of the source code files. # SRC = \ + $(TOP)/src/hash.c \ + $(TOP)/src/hash.h \ + $(TOP)/src/os.c \ + $(TOP)/src/pager.c \ + $(TOP)/src/pager.h \ + $(TOP)/src/random.c \ + $(TOP)/src/util.c + +SRC_ORIG = \ $(TOP)/src/attach.c \ $(TOP)/src/auth.c \ $(TOP)/src/btree.c \ @@ -106,6 +118,12 @@ SRC = \ # Source code to the test files. # TESTSRC = \ + $(TOP)/src/os.c \ + $(TOP)/src/pager.c \ + $(TOP)/src/test2.c \ + $(TOP)/src/md5.c + +TESTSRC_ORIG = \ $(TOP)/src/btree.c \ $(TOP)/src/func.c \ $(TOP)/src/os.c \ @@ -1,5 +1,5 @@ -C Begin\smodifying\sthe\sBTree\scode\sfor\sthe\snew\sversion-3\sfile\sformat.\nThis\sis\sa\swork-in-progress.\nAs\sof\sthis\scheck-in,\sSQLite\swill\snot\sbuild.\s(CVS\s1306) -D 2004-04-23T23:43:10 +C Pager\stests\sworking.\s(CVS\s1308) +D 2004-04-26T14:10:21 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -15,7 +15,7 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826 -F main.mk 1b27efb94be53a96c4333584a00a59fcc87ddc37 +F main.mk 1318f38db512abb0abdbdf4d3c9dd213e2960977 F publish.sh 1cd5c982388560fa91eedf6a338e210f713b35c8 F spec.template a38492f1c1dd349fc24cb0565e08afc53045304b F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea @@ -23,8 +23,8 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/attach.c b01db0d3211f673d8e670abf7eaad04591d40d14 F src/auth.c 4fa3b05bd19445d1c474d6751c4a508d6ea0abe1 -F src/btree.c cb9836c17b6d8ee447627caaf614ca97b5dc1188 -F src/btree.h 41cb3ff6ebc3f6da2d0a074e39ff8c7a2287469f +F src/btree.c 4e2b50ae03bd4d9e678bda14b08307d80dd4471c +F src/btree.h 858659c6605ae07a2a0fb3d176b25573d30f27c5 F src/btree_rb.c 99feb3ff835106d018a483a1ce403e5cf9c718bc F src/build.c 76fbca30081decd6615dee34b48c927ed5063752 F src/copy.c 750e13828c3e4a293123e36aaa7cf0f22466248a @@ -37,11 +37,11 @@ F src/hash.c 9b56ef3b291e25168f630d5643a4264ec011c70e F src/hash.h 3247573ab95b9dd90bcca0307a75d9a16da1ccc7 F src/insert.c c0485ee2d1b99322894e2d1e0b576fd05ed75616 F src/main.c 94dd355768e2a389e184a069b6880f4bac100307 -F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 +F src/md5.c c53e7b50418afbde22d07128baf3cc615530e846 F src/os.c 5f11382733805d4529ec2a30800e117f30995ea8 F src/os.h 250a3789be609adfee5c5aa20137ce8683276f24 -F src/pager.c b246986e5ba31b15aa3cf91d3b9ec2e608aceb8e -F src/pager.h 82332878799280145639a48d88cdb4058925e3f6 +F src/pager.c 2e3af839e7aab5bfaff7a0ef4694bae487b0ffeb +F src/pager.h 138ca7f73c47bebf469591939dcefa934cdf8d26 F src/parse.y 023720cb8c3bef74e51738bca78335d0dc6d2cfd F src/pragma.c f9c157b0591419d2d3407dac90222020d2a6d822 F src/printf.c 8c58b7b6d4069eec6ebe2d46bdbc3a89a367bf95 @@ -49,17 +49,17 @@ F src/random.c 775913e0b7fbd6295d21f12a7bd35b46387c44b2 F src/select.c 3833e2b64cc6d249385ee44e13bf49c9ae5b903d F src/shell.c 920af040d3a33ea8919c82cee45b424ad841cee0 F src/sqlite.h.in 35bec264dfb4965bbfeb7e75221f8658f210c30d -F src/sqliteInt.h 235ce244b62bb26cc9ab394fb7a0724dd4e65c83 +F src/sqliteInt.h 36c649d7f2ab0affdc44e51c681dd1d5723ee1e9 F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895 -F src/tclsqlite.c 819d92d305756c4ea57de023c387d2fa8a256aff +F src/tclsqlite.c e816201db3ea6ba857a0351547be1d4b7286e95d F src/test1.c 9aa62b89d420e6763b5e7ae89a47f6cf87370477 -F src/test2.c 75819b0f2c63c6a0fd6995445881f2eb94036996 +F src/test2.c 9d611c45e1b07039a2bd95f5ea73178362b23229 F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5 F src/test4.c 6e3e31acfaf21d66420fc35fda5b17dc0000cc8d F src/tokenize.c 6676b946fd8825b67ab52140af4fdc57a70bda48 F src/trigger.c a9927b57c865b6f3df3fb5e40c9824d722660ded F src/update.c 4c50328ebc127852bde8e2950eb8933234802c21 -F src/util.c 122bc174f6c8c2eb6a9127d9f13c4c74f83b85e4 +F src/util.c b2287b07ddf55ef7aaa8888a9473123995a69f40 F src/vacuum.c a4e8464c9f6d60659c5343e9d62c742463227820 F src/vdbe.c 7c33f761fdc799633468766fb53eda4301daa6b3 F src/vdbe.h ac987945e4dd6f987bca534c6005899f089fc270 @@ -114,7 +114,7 @@ F test/misc3.test 3b5e369514a3ba3f919fb7eafa7d027440b5079e F test/misuse.test 1095f26d1aed406c65e1d2eba651c4bb7c38cbff F test/notnull.test 7a08117a71e74b0321aaa937dbeb41a09d6eb1d0 F test/null.test c14d0f4739f21e929b8115b72bf0c765b6bb1721 -F test/pager.test 331519008889d45f6df6697395e5bce6ee602fd9 +F test/pager.test 548968643d91c1c43a3a3eb1a232e9ca87b4069e F test/pragma.test 24a3f7a697b45cb90d664ebce5566bec7ac41571 F test/printf.test 46b3d07d59d871d0831b4a657f6dfcafe0574850 F test/progress.test 701b6115c2613128ececdfe1398a1bd0e1a4cfb3 x @@ -133,7 +133,7 @@ F test/table.test 371a1fc1c470982b2f68f9732f903a5d96f949c4 F test/tableapi.test e0c4cce61e58343caa84dab33fa6823cb35fe1e1 F test/tclsqlite.test a684fc191b81e6cded8a81263663d5a130fbb013 F test/temptable.test a770ba6308d7f7332fce985086b8e06bed6430c2 -F test/tester.tcl 2f1d43df1311c9dc06acaa7a82e87bfea85dea5f +F test/tester.tcl 5b47e21f442fe8afae963bc45beb54795cf5d162 F test/thread1.test 53f050d5be6932d9430df7756edd379366508ff6 F test/threadtest1.c f7f896e62ed46feae1dc411114a48c15a0f82ee2 F test/threadtest2.c d94ca4114fd1504f7e0ae724bcd83d4b40931d86 @@ -188,7 +188,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P 818bdba5dc25cda56716fdda1781a3d4ecb3a111 -R f443d88ceb0074719c3a2c8d82169104 +P ce0bbd3a7159e12c86c5cde6571d6668b234827b +R e6250126bdd2ced9348d9f2d5069495a U drh -Z 85059b1060ba6075105dfb1d0e91a8dd +Z 07ad078caa4fedefbf47bb778c51fa1e diff --git a/manifest.uuid b/manifest.uuid index 9199c8c24..067e908d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce0bbd3a7159e12c86c5cde6571d6668b234827b
\ No newline at end of file +910067a200c4b25b5d813a84146673d3d1c80952
\ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 6793f25b3..abccfb851 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.104 2004/04/23 23:43:10 drh Exp $ +** $Id: btree.c,v 1.105 2004/04/26 14:10:21 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -123,9 +123,9 @@ ** ** SIZE DESCRIPTION ** 2 Byte offset of the next cell. 0 if this is the last cell -** 4 Page number of the left child. Omitted if leaf flag is set. -** var Number of bytes of data. Omitted if the zerodata flag is set. -** var Number of bytes of key. Omitted if the intkey flag is set. +** 4 Page number of the left child. Omitted if leaf flag is set. +** var Number of bytes of data. Omitted if the zerodata flag is set. +** var Number of bytes of key. Or the key itself if intkey flag is set. ** * Payload ** 4 First page of the overflow chain. Omitted if no overflow ** @@ -153,8 +153,7 @@ #include <assert.h> /* Forward declarations */ -static BtOps sqliteBtreeOps; -static BtCursorOps sqliteBtreeCursorOps; +typedef struct MemPage MemPage; /* ** This is a magic string that appears at the beginning of every @@ -181,17 +180,18 @@ static const char zMagicHeader[] = "SQLite version 3"; ** The pageDestructor() routine handles that chore. */ struct MemPage { - struct BTree *pBt; /* Pointer back to BTree structure */ + struct Btree *pBt; /* Pointer back to BTree structure */ unsigned char *aData; /* Pointer back to the start of the page */ + u8 isInit; /* True if previously initialized */ u8 idxShift; /* True if Cell indices have changed */ - u8 isOverfull; /* Some aCell[] points outside u.aDisk[] */ + u8 isOverfull; /* Some aCell[] do not fit on page */ u8 intKey; /* True if intkey flag is set */ u8 leaf; /* True if leaf flag is set */ u8 zeroData; /* True if zero data flag is set */ u8 hdrOffset; /* 100 for page 1. 0 otherwise */ Pgno pgno; /* Page number for this page */ MemPage *pParent; /* The parent of this page. NULL for root */ - int idxParent; /* Index in pParent->apCell[] of this node */ + int idxParent; /* Index in pParent->aCell[] of this node */ int nFree; /* Number of free bytes on the page */ int nCell; /* Number of entries on this page */ unsigned char **aCell; /* Pointer to start of each cell */ @@ -202,18 +202,17 @@ struct MemPage { ** to the end. EXTRA_SIZE is the number of bytes of space needed to hold ** that extra information. */ -#define EXTRA_SIZE sizeof(Mempage) +#define EXTRA_SIZE sizeof(MemPage) /* ** Everything we need to know about an open database */ struct Btree { - BtOps *pOps; /* Function table */ Pager *pPager; /* The page cache */ BtCursor *pCursor; /* A list of all open cursors */ - MemPage *page1; /* First page of the database */ + MemPage *pPage1; /* First page of the database */ u8 inTrans; /* True if a transaction is in progress */ - u8 inCkpt; /* True if there is a checkpoint on the transaction */ + u8 inStmt; /* True if there is a checkpoint on the transaction */ u8 readOnly; /* True if the underlying file is readonly */ int pageSize; /* Number of usable bytes on each page */ int maxLocal; /* Maximum local payload */ @@ -226,13 +225,14 @@ typedef Btree Bt; ** MemPage.apCell[] of the entry. */ struct BtCursor { - BtCursorOps *pOps; /* Function table */ Btree *pBt; /* The Btree to which this cursor belongs */ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ BtCursor *pShared; /* Loop of cursors with the same root page */ + int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */ + void *pArg; /* First arg to xCompare() */ Pgno pgnoRoot; /* The root page of this tree */ MemPage *pPage; /* Page that contains the entry */ - int idx; /* Index of the entry in pPage->apCell[] */ + int idx; /* Index of the entry in pPage->aCell[] */ u8 wrFlag; /* True if writable */ u8 eSkip; /* Determines if next step operation is a no-op */ u8 iMatch; /* compare result from last sqliteBtreeMoveto() */ @@ -246,11 +246,8 @@ struct BtCursor { #define SKIP_PREV 2 /* The next sqliteBtreePrevious() is a no-op */ #define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */ -/* Forward declarations */ -static int fileBtreeCloseCursor(BtCursor *pCur); - /* -** Read or write a two-, four-, and eight-byte integer values +** Read or write a two-, four-, and eight-byte big-endian integer values. */ static u32 get2byte(unsigned char *p){ return (p[0]<<8) | p[1]; @@ -258,7 +255,7 @@ static u32 get2byte(unsigned char *p){ static u32 get4byte(unsigned char *p){ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; } -static u64 get4byte(unsigned char *p){ +static u64 get8byte(unsigned char *p){ u64 v = get4byte(p); return (v<<32) | get4byte(&p[4]); } @@ -306,34 +303,49 @@ static unsigned int putVarint(unsigned char *p, u64 v){ } /* -** 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 space allocated on overflow pages -** is NOT included in the value returned from this routine. +** Parse a cell header and fill in the CellInfo structure. */ -static int cellSize(MemPage *pPage, unsigned char *pCell){ - int n, nPayload; - u64 nData, nKey; - int maxPayload; +static void parseCellHeader( + MemPage *pPage, /* Page containing the cell */ + unsigned char *pCell, /* The cell */ + u64 *pnData, /* Number of bytes of data in payload */ + u64 *pnKey, /* Number of bytes of key, or key value for intKey */ + int *pnHeader /* Size of header in bytes. Offset to payload */ +){ + int n; if( pPage->leaf ){ n = 2; }else{ n = 6; } if( pPage->zeroData ){ - nData = 0; + *pnData = 0; }else{ - n += getVarint(&pCell[n], &nData); + n += getVarint(&pCell[n], pnData); } - if( pPage->intKey ){ - u64 dummy; - nKey = getVarint(&pCell[n], &dummy); - }else{ - n += getVarint(pCell, &nKey); + n += getVarint(pCell, pnKey); + *pnHeader = n; +} + +/* +** 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 space allocated on overflow pages +** is NOT included in the value returned from this routine. +*/ +static int cellSize(MemPage *pPage, unsigned char *pCell){ + CellInfo info; + int n; + u64 nData, nKey; + int nPayload, maxPayload; + + parseCellHeader(pPage, pCell, &nData, &nKey, &n); + nPayload = (int)nData; + if( !pPage->intKey ){ + nPayload += (int)nKey; } - nPayload = nKey + nData; - maxPayload = pPage->pBt->maxPayload; + maxPayload = pPage->pBt->maxLocal; if( nPayload>maxPayload ){ nPayload = maxPayload + 4; } @@ -346,8 +358,8 @@ static int cellSize(MemPage *pPage, unsigned char *pCell){ ** into one big FreeBlk at the end of the page. */ static void defragmentPage(MemPage *pPage){ - int pc, i, n; - int start, hdr; + int pc, i, n, addr; + int start, hdr, size; int leftover; unsigned char *oldPage; unsigned char newPage[SQLITE_PAGE_SIZE]; @@ -357,31 +369,31 @@ static void defragmentPage(MemPage *pPage){ assert( pPage->pageSize <= SQLITE_PAGE_SIZE ); oldPage = pPage->aData; hdr = pPage->hdrOffset; - ptr = 3+hdr; + addr = 3+hdr; n = 6+hdr; if( !pPage->leaf ){ n += 4; } start = n; - pc = get2byte(&oldPage[ptr]); + pc = get2byte(&oldPage[addr]); i = 0; while( pc>0 ){ - assert( n<pPage->pageSize ); + assert( n<pPage->pBt->pageSize ); size = cellSize(pPage, &oldPage[pc]); memcpy(&newPage[n], &oldPage[pc], size); - put2byte(&newPage[ptr],n); + put2byte(&newPage[addr],n); pPage->aCell[i] = &oldPage[n]; n += size; - ptr = pc; + addr = pc; pc = get2byte(&oldPage[pc]); } - leftover = pPage->pageSize - n; + leftover = pPage->pBt->pageSize - n; assert( leftover>=0 ); assert( pPage->nFree==leftover ); if( leftover<4 ){ oldPage[hdr+5] = leftover; leftover = 0; - n = pPage->pageSize; + n = pPage->pBt->pageSize; } memcpy(&oldPage[start], &newPage[start], n-start); if( leftover==0 ){ @@ -411,7 +423,7 @@ static void defragmentPage(MemPage *pPage){ ** nByte in size or that larger. */ static int allocateSpace(MemPage *pPage, int nByte){ - int ptr, pc, hdr; + int addr, pc, hdr; int size; unsigned char *data; #ifndef NDEBUG @@ -427,31 +439,31 @@ static int allocateSpace(MemPage *pPage, int nByte){ if( data[hdr+5]>=252 ){ defragmentPage(pPage); } - ptr = hdr+1; - pc = get2byte(&data[ptr]); - assert( ptr<pc ); + addr = hdr+1; + pc = get2byte(&data[addr]); + assert( addr<pc ); assert( pc<=pPage->pageSize-4 ); - while( (size = get2byte(&data[pc+2])<nByte ){ - ptr = pc; - pc = get2byte(&data[ptr]); + while( (size = get2byte(&data[pc+2]))<nByte ){ + addr = pc; + pc = get2byte(&data[addr]); assert( pc<=pPage->pageSize-4 ); - assert( pc>=ptr+size+4 || pc==0 ); + assert( pc>=addr+size+4 || pc==0 ); if( pc==0 ){ assert( (cnt++)==0 ); defragmentPage(pPage); assert( data[hdr+5]==0 ); - ptr = pPage->hdrOffset+1; - pc = get2byte(&data[ptr]); + addr = pPage->hdrOffset+1; + pc = get2byte(&data[addr]); } } assert( pc>0 && size>=nByte ); assert( pc+size<=pPage->pageSize ); if( size>nByte+4 ){ - put2byte(&data[ptr], pc+nByte); + put2byte(&data[addr], pc+nByte); put2byte(&data[pc+size], get2byte(&data[pc])); put2byte(&data[pc+size+2], size-nByte); }else{ - put2byte(&data[ptr], get2byte(&data[pc])); + put2byte(&data[addr], get2byte(&data[pc])); data[hdr+5] += size-nByte; } pPage->nFree -= nByte; @@ -469,7 +481,7 @@ static int allocateSpace(MemPage *pPage, int nByte){ */ static void freeSpace(MemPage *pPage, int start, int size){ int end = start + size; /* End of the segment being freed */ - int ptr, pbegin, pend; + int addr, pbegin, pend; #ifndef NDEBUG int tsize = 0; /* Total size of all freeblocks */ #endif @@ -482,24 +494,24 @@ static void freeSpace(MemPage *pPage, int start, int size){ if( size<4 ) size = 4; /* Add the space back into the linked list of freeblocks */ - ptr = pPage->hdrOffset + 1; - while( (pbegin = get2byte(&data[ptr]))<start && pbegin>0 ){ + addr = pPage->hdrOffset + 1; + while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){ assert( pbegin<=pPage->pBt->pageSize-4 ); - assert( pbegin>ptr ); - ptr = pbegin; + assert( pbegin>addr ); + addr = pbegin; } assert( pbegin<=pPage->pBt->pageSize-4 ); - assert( pbegin>ptr || pbegin==0 ); - put2bytes(&data[ptr], start); + assert( pbegin>addr || pbegin==0 ); + put2bytes(&data[addr], start); put2bytes(&data[start], pbegin); put2bytes(&data[start+2], size); pPage->nFree += size; /* Coalesce adjacent free blocks */ - ptr = pPage->hdrOffset + 1; - while( (pbegin = get2byte(&data[ptr]))>0 ){ + addr = pPage->hdrOffset + 1; + while( (pbegin = get2byte(&data[addr]))>0 ){ int pnext, psize; - assert( pbegin>ptr ); + assert( pbegin>addr ); assert( pbegin<pPage->pBt->pageSize-4 ); pnext = get2byte(&data[pbegin]); psize = get2byte(&data[pbegin+2]); @@ -511,12 +523,13 @@ static void freeSpace(MemPage *pPage, int start, int size){ put2byte(&data[pbegin+2], pnext+get2byte(&data[pnext+2])-pbegin); }else{ assert( (tsize += psize)>0 ); - ptr = pbegin; + addr = pbegin; } } assert( tsize+data[pPage->hdrOffset+5]==pPage->nFree ); } +#if 0 /* ** The following is the default comparison function for (non-integer) ** keys in the btrees. This function returns negative, zero, or @@ -586,7 +599,7 @@ static int keyComp( bad_key: return 1; } - +#endif /* ** Initialize the auxiliary information for a disk block. @@ -602,17 +615,14 @@ bad_key: ** we failed to detect any corruption. */ static int initPage( - Bt *pBt, /* The Btree */ - unsigned char *data, /* Start of the data for the page */ - Pgno pgnoThis, /* The page number */ + MemPage *pPage, /* The page to be initialized */ MemPage *pParent /* The parent. Might be NULL */ ){ - MemPage *pPage; - int c, pc, i; + int c, pc, i, hdr; int sumCell = 0; /* Total size of all cells */ - unsigned char *data; - pPage = (MemPage*)&aData[pBt->pageSize]; + assert( pPage->pBt!=0 ); + assert( pPage->aData == &((unsigned char*)pPage)[pPage->pBt->pageSize] ); if( pPage->pParent ){ assert( pPage->pParent==pParent ); return SQLITE_OK; @@ -621,12 +631,11 @@ static int initPage( pPage->pParent = pParent; sqlitepager_ref(pParent->aData); } - if( pPage->pBt!=0 ) return SQLITE_OK; - pPage->pBt = pBt; + if( pPage->isInit ) return SQLITE_OK; pPage->nCell = 0; - pPage->pgno = pgnoThis; + assert( sqlitepager_pagenumber(pPage->aData)==pPage->pgno ); pPage->hdrOffset = hdr = pgnoThis==1 ? 100 : 0; - c = data[pPage->hdrOffset]; + c = pPage->aData[pPage->hdrOffset]; pPage->intKey = (c & PTF_INTKEY)!=0; pPage->zeroData = (c & PTF_ZERODATA)!=0; pPage->leaf = (c & PTF_INTKEY)!=0; @@ -658,7 +667,7 @@ static int initPage( if( pc>=pBt->pageSize ) return SQLITE_CORRUPT; next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); - if( next>0 && next<=pc+size+3 ) return SQLITE_CURRUPT; + if( next>0 && next<=pc+size+3 ) return SQLITE_CORRUPT; pPage->nFree += size; pc = next; } @@ -680,7 +689,7 @@ static int initPage( static void zeroPage(MemPage *pPage, int flags){ unsigned char *data = pPage->aData; Btree *pBt = pPage->pBt; - int hdr = pPage->pgno==1 ? 100 : 0; + int hdr = pPage->hdrOffset; int first; assert( sqlitepager_iswriteable(data) ); @@ -700,6 +709,37 @@ static void zeroPage(MemPage *pPage, int flags){ } /* +** Get a page from the pager. Initialize the MemPage.pBt and +** MemPage.aData elements if needed. +*/ +static int getPage(Btree *pBt, Pgno pgno, MemPage **ppPage){ + int rc; + unsigned char *aData; + MemPage *pPage; + rc = sqlitepager_get(pBt->pPager, pgno, &aData); + if( rc ) return rc; + pPage = (MemPage*)aData[pBt->pageSize]; + pPage->aData = aData; + pPage->pBt = pBt; + pPage->pgno = pgno; + *ppPage = pPage; + return SQLITE_OK; +} + +/* +** Release a MemPage. This should be called once for each prior +** call to getPage. +*/ +static int releasePage(MemPage *pPage){ + if( pPage ){ + assert( pPage->aData ); + assert( pPage->pBt ); + assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage ); + sqlitepager_unref(pPage->aData); + } +} + +/* ** This routine is called when the reference count for a page ** reaches zero. We need to unref the pParent pointer when that ** happens. @@ -709,10 +749,11 @@ static void pageDestructor(void *pData){ if( pPage->pParent ){ MemPage *pParent = pPage->pParent; pPage->pParent = 0; - sqlitepager_unref(pParent->aData); + releasepage(pParent); } sqliteFree(pPage->aCell); pPage->aCell = 0; + pPage->isInit = 0; } /* @@ -727,13 +768,15 @@ static void pageDestructor(void *pData){ ** database file will be deleted when sqliteBtreeClose() is called. */ int sqliteBtreeOpen( - const char *zFilename, /* Name of the file containing the BTree database */ - int omitJournal, /* if TRUE then do not journal this file */ - int nCache, /* How many pages in the page cache */ - Btree **ppBtree /* Pointer to new Btree object written here */ + const char *zFilename, /* Name of the file containing the BTree database */ + Btree **ppBtree, /* Pointer to new Btree object written here */ + int nCache, /* Number of cache pages */ + int flags /* Options */ ){ Btree *pBt; - int rc; + int rc, i; + int nCache = 2000; + int omitJournal = 0; /* ** The following asserts make sure that structures used by the btree are @@ -754,7 +797,7 @@ int sqliteBtreeOpen( } if( nCache<10 ) nCache = 10; rc = sqlitepager_open(&pBt->pPager, zFilename, nCache, EXTRA_SIZE, - !omitJournal); + (flags & BTREE_OMIT_JOURNAL)==0); if( rc!=SQLITE_OK ){ if( pBt->pPager ) sqlitepager_close(pBt->pPager); sqliteFree(pBt); @@ -765,7 +808,6 @@ int sqliteBtreeOpen( pBt->pCursor = 0; pBt->page1 = 0; pBt->readOnly = sqlitepager_isreadonly(pBt->pPager); - pBt->pOps = &sqliteBtreeOps; pBt->pageSize = SQLITE_PAGE_SIZE; pBt->maxLocal = (SQLITE_PAGE_SIZE-10)/4-12; *ppBtree = pBt; @@ -775,9 +817,9 @@ int sqliteBtreeOpen( /* ** Close an open database and invalidate all cursors. */ -static int fileBtreeClose(Btree *pBt){ +int sqlite3BtreeClose(Btree *pBt){ while( pBt->pCursor ){ - fileBtreeCloseCursor(pBt->pCursor); + sqlite3BtreeCloseCursor(pBt->pCursor); } sqlitepager_close(pBt->pPager); sqliteFree(pBt); @@ -799,7 +841,7 @@ static int fileBtreeClose(Btree *pBt){ ** Synchronous is on by default so database corruption is not ** normally a worry. */ -static int fileBtreeSetCacheSize(Btree *pBt, int mxPage){ +int sqilte3BtreeSetCacheSize(Btree *pBt, int mxPage){ sqlitepager_set_cachesize(pBt->pPager, mxPage); return SQLITE_OK; } @@ -812,7 +854,7 @@ static int fileBtreeSetCacheSize(Btree *pBt, int mxPage){ ** is a very low but non-zero probability of damage. Level 3 reduces the ** probability of damage to near zero but with a write performance reduction. */ -static int fileBtreeSetSafetyLevel(Btree *pBt, int level){ +int sqlite3BtreeSetSafetyLevel(Btree *pBt, int level){ sqlitepager_set_safety_level(pBt->pPager, level); return SQLITE_OK; } @@ -829,27 +871,28 @@ static int fileBtreeSetSafetyLevel(Btree *pBt, int level){ */ static int lockBtree(Btree *pBt){ int rc; - unsigned char *data; + MemPage *pPage1; if( pBt->page1 ) return SQLITE_OK; - rc = sqlitepager_get(pBt->pPager, 1, (void**)&data); + rc = getPage(pBt, 1, &pPage1); if( rc!=SQLITE_OK ) return rc; + /* Do some checking to help insure the file we opened really is ** a valid database file. */ if( sqlitepager_pagecount(pBt->pPager)>0 ){ - if( memcmp(data, zMagicHeader, 16)!=0 ){ + if( memcmp(pPage1->aData, zMagicHeader, 16)!=0 ){ rc = SQLITE_NOTADB; goto page1_init_failed; } /*** TBD: Other header checks such as page size ****/ } - pBt->page1 = (MemPage*)&data[SQLITE_PAGE_SIZE]; + pBt->pPage1 = pPage1; return rc; page1_init_failed: - sqlitepager_unref(pBt->data); - pBt->page1 = 0; + releasePage(pPage1); + pBt->pPage1 = 0; return rc; } @@ -864,11 +907,11 @@ page1_init_failed: ** If there is a transaction in progress, this routine is a no-op. */ static void unlockBtreeIfUnused(Btree *pBt){ - if( pBt->inTrans==0 && pBt->pCursor==0 && pBt->page1!=0 ){ - sqlitepager_unref(pBt->page1->aData); - pBt->page1 = 0; + if( pBt->inTrans==0 && pBt->pCursor==0 && pBt->pPage1!=0 ){ + releasePage(pBt->pPage1); + pBt->pPage1 = 0; pBt->inTrans = 0; - pBt->inCkpt = 0; + pBt->inStmt = 0; } } @@ -881,7 +924,7 @@ static int newDatabase(Btree *pBt){ unsigned char *data; int rc; if( sqlitepager_pagecount(pBt->pPager)>1 ) return SQLITE_OK; - pP1 = pBt->page1; + pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; rc = sqlitepager_write(data); @@ -911,23 +954,23 @@ static int newDatabase(Btree *pBt){ ** sqliteBtreeDelete() ** sqliteBtreeUpdateMeta() */ -static int fileBtreeBeginTrans(Btree *pBt){ +int sqlite3BtreeBeginTrans(Btree *pBt){ int rc; if( pBt->inTrans ) return SQLITE_ERROR; if( pBt->readOnly ) return SQLITE_READONLY; - if( pBt->page1==0 ){ + if( pBt->pPage1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ return rc; } } - rc = sqlitepager_begin(pBt->page1); + rc = sqlitepager_begin(pBt->pPage1->aData); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } if( rc==SQLITE_OK ){ pBt->inTrans = 1; - pBt->inCkpt = 0; + pBt->inStmt = 0; }else{ unlockBtreeIfUnused(pBt); } @@ -940,11 +983,11 @@ static int fileBtreeBeginTrans(Btree *pBt){ ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ -static int fileBtreeCommit(Btree *pBt){ +int sqlite3BtreeCommit(Btree *pBt){ int rc; rc = pBt->readOnly ? SQLITE_OK : sqlitepager_commit(pBt->pPager); pBt->inTrans = 0; - pBt->inCkpt = 0; + pBt->inStmt = 0; unlockBtreeIfUnused(pBt); return rc; } @@ -958,16 +1001,17 @@ static int fileBtreeCommit(Btree *pBt){ ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ -static int fileBtreeRollback(Btree *pBt){ +int sqlite3BtreeRollback(Btree *pBt){ int rc; BtCursor *pCur; if( pBt->inTrans==0 ) return SQLITE_OK; pBt->inTrans = 0; - pBt->inCkpt = 0; + pBt->inStmt = 0; rc = pBt->readOnly ? SQLITE_OK : sqlitepager_rollback(pBt->pPager); for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->pPage && pCur->pPage->pBt==0 ){ - sqlitepager_unref(pCur->pPage); + MemPage *pPage = pCur->pPage; + if( pPage && !pPage->isInit ){ + releasePage(pPage); pCur->pPage = 0; } } @@ -985,13 +1029,13 @@ static int fileBtreeRollback(Btree *pBt){ ** Only one checkpoint may be active at a time. It is an error to try ** to start a new checkpoint if another checkpoint is already active. */ -static int fileBtreeBeginCkpt(Btree *pBt){ +int sqlite3BtreeBeginStmt(Btree *pBt){ int rc; - if( !pBt->inTrans || pBt->inCkpt ){ + if( !pBt->inTrans || pBt->inStmt ){ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } rc = pBt->readOnly ? SQLITE_OK : sqlitepager_ckpt_begin(pBt->pPager); - pBt->inCkpt = 1; + pBt->inStmt = 1; return rc; } @@ -1000,14 +1044,14 @@ static int fileBtreeBeginCkpt(Btree *pBt){ ** Commit a checkpoint to transaction currently in progress. If no ** checkpoint is active, this is a no-op. */ -static int fileBtreeCommitCkpt(Btree *pBt){ +int sqlite3BtreeCommitStmt(Btree *pBt){ int rc; - if( pBt->inCkpt && !pBt->readOnly ){ + if( pBt->inStmt && !pBt->readOnly ){ rc = sqlitepager_ckpt_commit(pBt->pPager); }else{ rc = SQLITE_OK; } - pBt->inCkpt = 0; + pBt->inStmt = 0; return rc; } @@ -1019,22 +1063,40 @@ static int fileBtreeCommitCkpt(Btree *pBt){ ** to use a cursor that was open at the beginning of this operation ** will result in an error. */ -static int fileBtreeRollbackCkpt(Btree *pBt){ +int sqlite3BtreeRollbackStmt(Btree *pBt){ int rc; BtCursor *pCur; - if( pBt->inCkpt==0 || pBt->readOnly ) return SQLITE_OK; + if( pBt->inStmt==0 || pBt->readOnly ) return SQLITE_OK; rc = sqlitepager_ckpt_rollback(pBt->pPager); for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->pPage && pCur->pPage->isInit==0 ){ - sqlitepager_unref(pCur->pPage); + MemPage *pPage = pCur->pPage; + if( pPage && !pPage->isInit ){ + releasePage(pPage); pCur->pPage = 0; } } - pBt->inCkpt = 0; + pBt->inStmt = 0; return rc; } /* +** Default key comparison function to be used if no comparison function +** is specified on the sqlite3BtreeCursor() call. +*/ +static int dfltCompare( + void *NotUsed, /* User data is not used */ + int n1, const void *p1, /* First key to compare */ + int n2, const void *p2 /* Second key to compare */ +){ + int c; + c = memcmp(p1, p2, n1<n2 ? n1 : n2); + if( c==0 ){ + c = n1 - n2; + } + return c; +} + +/* ** 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. @@ -1069,9 +1131,21 @@ static int fileBtreeRollbackCkpt(Btree *pBt){ ** No checking is done to make sure that page iTable really is the ** root page of a b-tree. If it is not, then the cursor acquired ** will not work correctly. -*/ -static -int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){ +** +** The comparison function must be logically the same for every cursor +** on a particular table. Changing the comparison function will result +** in incorrect operations. If the comparison function is NULL, a +** default comparison function is used. The comparison function is +** always ignored for INTKEY tables. +*/ +int sqlite3BtreeCursor( + Btree *pBt, /* The btree */ + int iTable, /* Root page of table to open */ + int wrFlag, /* 1 to write. 0 read-only */ + int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */ + void *pArg, /* First arg to xCompare() */ + BtCursor **ppCur /* Write new cursor here */ +){ int rc; BtCursor *pCur, *pRing; @@ -1092,15 +1166,16 @@ int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){ goto create_cursor_exception; } pCur->pgnoRoot = (Pgno)iTable; - rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage); + rc = getPage(pBt, pCur->pgnoRoot, (void**)&pCur->pPage); if( rc!=SQLITE_OK ){ goto create_cursor_exception; } - rc = initPage(pBt, pCur->pPage, pCur->pgnoRoot, 0); + rc = initPage(pCur->pPage, 0); if( rc!=SQLITE_OK ){ goto create_cursor_exception; } - pCur->pOps = &sqliteBtreeCursorOps; + pCur->xCompare = xCmp ? xCmp : dfltCompare; + pCur->pArg = pArg; pCur->pBt = pBt; pCur->wrFlag = wrFlag; pCur->idx = 0; @@ -1125,7 +1200,7 @@ int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){ create_cursor_exception: *ppCur = 0; if( pCur ){ - if( pCur->pPage ) sqlitepager_unref(pCur->pPage); + releasePage(pCur->pPage); sqliteFree(pCur); } unlockBtreeIfUnused(pBt); @@ -1136,7 +1211,7 @@ create_cursor_exception: ** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. */ -static int fileBtreeCloseCursor(BtCursor *pCur){ +int sqlite3BtreeCloseCursor(BtCursor *pCur){ Btree *pBt = pCur->pBt; if( pCur->pPrev ){ pCur->pPrev->pNext = pCur->pNext; @@ -1146,9 +1221,7 @@ static int fileBtreeCloseCursor(BtCursor *pCur){ if( pCur->pNext ){ pCur->pNext->pPrev = pCur->pPrev; } - if( pCur->pPage ){ - sqlitepager_unref(pCur->pPage); - } + releasePage(pCur->pPage); if( pCur->pShared!=pCur ){ BtCursor *pRing = pCur->pShared; while( pRing->pShared!=pCur ){ pRing = pRing->pShared; } @@ -1168,7 +1241,7 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ pTempCur->pNext = 0; pTempCur->pPrev = 0; if( pTempCur->pPage ){ - sqlitepager_ref(pTempCur->pPage); + sqlitepager_ref(pTempCur->pPage->aData); } } @@ -1178,19 +1251,19 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ */ static void releaseTempCursor(BtCursor *pCur){ if( pCur->pPage ){ - sqlitepager_unref(pCur->pPage); + sqlitepager_unref(pCur->pPage->aData); } } /* -** Set *pSize to the number of bytes of key in the entry the -** cursor currently points to. Always return SQLITE_OK. -** Failure is not possible. If the cursor is not currently -** pointing to an entry (which can happen, for example, if -** the database is empty) then *pSize is set to 0. +** Set *pSize to the size of the buffer needed to hold the value of +** the key for the current entry. If the cursor is not pointing +** to a valid entry, *pSize is set to 0. +** +** For a table with the intKey flag set, this routine returns the key +** itself, not the number of bytes in the key. */ -static int fileBtreeKeySize(BtCursor *pCur, int *pSize){ - Cell *pCell; +int sqlite3BtreeKeySize(BtCursor *pCur, u64 *pSize){ MemPage *pPage; pPage = pCur->pPage; @@ -1198,8 +1271,15 @@ static int fileBtreeKeySize(BtCursor *pCur, int *pSize){ if( pCur->idx >= pPage->nCell ){ *pSize = 0; }else{ - pCell = pPage->apCell[pCur->idx]; - *pSize = NKEY(pCur->pBt, pCell->h); + unsigned char *cell = pPage->aCell[pCur->idx]; + cell += 2; /* Skip the offset to the next cell */ + if( pPage->leaf ){ + cell += 4; /* Skip the child pointer */ + } + if( !pPage->zeroData ){ + while( (0x80&*(data++))!=0 ){} /* Skip the data size number */ + } + getVarint(data, pSize); } return SQLITE_OK; } @@ -1212,18 +1292,51 @@ static int fileBtreeKeySize(BtCursor *pCur, int *pSize){ ** This routine does not make a distinction between key and data. ** It just reads bytes from the payload area. */ -static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){ - char *aPayload; +static int getPayload( + BtCursor *pCur, /* Cursor pointing to entry to read from */ + int offset, /* Begin reading this far into payload */ + int amt, /* Read this many bytes */ + unsigned char *pBuf, /* Write the bytes into this buffer */ + int skipKey /* offset begins at data if this is true */ +){ + unsigned char *aPayload; Pgno nextPage; int rc; - Btree *pBt = pCur->pBt; + MemPage *pPage; + Btree *pBt; + u64 nData, nKey; + int maxLocal, ovflSize; + assert( pCur!=0 && pCur->pPage!=0 ); - assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); - aPayload = pCur->pPage->apCell[pCur->idx]->aPayload; - if( offset<MX_LOCAL_PAYLOAD ){ + pBt = pCur->pBt; + pPage = pCur->pPage; + assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); + aPayload = pPage->aCell[pCur->idx]; + aPayload += 2; /* Skip the next cell index */ + if( pPage->leaf ){ + aPayload += 4; /* Skip the child pointer */ + } + if( pPage->zeroData ){ + nData = 0; + }else{ + aPayload += getVarint(aPayload, &nData); + } + aPayload += getVarInt(aPayload, &nKey); + if( pPage->intKey ){ + nKey = 0; + } + assert( offset>=0 ); + if( skipKey ){ + offset += nKey; + } + if( offset+amt > nKey+nData ){ + sqlite SQLITE_ERROR; + } + maxLocal = pBt->maxLocal + if( offset<maxLocal ){ int a = amt; - if( a+offset>MX_LOCAL_PAYLOAD ){ - a = MX_LOCAL_PAYLOAD - offset; + if( a+offset>maxLocal ){ + a = maxLocal - offset; } memcpy(zBuf, &aPayload[offset], a); if( a==amt ){ @@ -1233,31 +1346,31 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){ zBuf += a; amt -= a; }else{ - offset -= MX_LOCAL_PAYLOAD; + offset -= maxLocal; } if( amt>0 ){ - nextPage = SWAB32(pBt, pCur->pPage->apCell[pCur->idx]->ovfl); + nextPage = get4bytes(&aPayload[maxLocal]); } + ovflSize = pBt->pageSize - 4; while( amt>0 && nextPage ){ - OverflowPage *pOvfl; - rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&pOvfl); + rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&aPayload); if( rc!=0 ){ return rc; } - nextPage = SWAB32(pBt, pOvfl->iNext); - if( offset<OVERFLOW_SIZE ){ + nextPage = get4bytes(aPayload); + if( offset<ovflSize ){ int a = amt; - if( a + offset > OVERFLOW_SIZE ){ - a = OVERFLOW_SIZE - offset; + if( a + offset > ovflSize ){ + a = ovflSize - offset; } - memcpy(zBuf, &pOvfl->aPayload[offset], a); + memcpy(zBuf, &aPayload[offset], a); offset = 0; amt -= a; zBuf += a; }else{ - offset -= OVERFLOW_SIZE; + offset -= ovflSize; } - sqlitepager_unref(pOvfl); + sqlitepager_unref(aPayload); } if( amt>0 ){ return SQLITE_CORRUPT; @@ -1266,33 +1379,67 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){ } /* -** Read part of the key associated with cursor pCur. A maximum -** of "amt" bytes will be transfered into zBuf[]. The transfer -** begins at "offset". The number of bytes actually read is -** returned. +** Read part of the key associated with cursor pCur. Exactly +** "amt" bytes will be transfered into zBuf[]. The transfer +** begins at "offset". ** -** Change: It used to be that the amount returned will be smaller -** than the amount requested if there are not enough bytes in the key -** to satisfy the request. But now, it must be the case that there -** is enough data available to satisfy the request. If not, an exception -** is raised. The change was made in an effort to boost performance -** by eliminating unneeded tests. +** Return SQLITE_OK on success or an error code if anything goes +** wrong. An error is returned if "offset+amt" is larger than +** the available payload. */ -static int fileBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){ +int sqlite3BtreeKey(BtCursor *pCur, int offset, int amt, void *pBuf){ MemPage *pPage; assert( amt>=0 ); assert( offset>=0 ); assert( pCur->pPage!=0 ); pPage = pCur->pPage; - if( pCur->idx >= pPage->nCell ){ + if( pCur->idx >= pPage->nCell || pPage->intKey ){ + assert( amt==0 ); + return SQLITE_OK; + } + return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); +} + +/* +** Return a pointer to the key of record that cursor pCur +** is point to if the entire key is in contiguous memory. +** If the key is split up among multiple tables, return 0. +** If pCur is not pointing to a valid entry return 0. +** +** The pointer returned is ephemeral. The key may move +** or be destroyed on the next call to any Btree routine. +** +** This routine is used to do quick key comparisons in the +** common case where the entire key fits in the payload area +** of a cell and does not overflow onto secondary pages. +*/ +void *sqlite3BtreeKeyFetch(BtCursor *pCur){ + unsigned char *aPayload; + MemPage *pPage; + Btree *pBt; + u64 nData, nKey; + + assert( pCur!=0 && pCur->pPage!=0 ); + pBt = pCur->pBt; + pPage = pCur->pPage; + assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); + aPayload = pPage->aCell[pCur->idx]; + aPayload += 2; /* Skip the next cell index */ + if( pPage->leaf ){ + aPayload += 4; /* Skip the child pointer */ + } + if( !pPage->zeroData ){ + aPayload += getVarint(aPayload, &nData); + } + aPayload += getVarInt(aPayload, &nKey); + if( pPage->intKey || nKey>pBt->maxLocal ){ return 0; } - assert( amt+offset <= NKEY(pCur->pBt, pPage->apCell[pCur->idx]->h) ); - getPayload(pCur, offset, amt, zBuf); - return amt; + return aPayload; } + /* ** Set *pSize to the number of bytes of data in the entry the ** cursor currently points to. Always return SQLITE_OK. @@ -1300,31 +1447,38 @@ static int fileBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){ ** pointing to an entry (which can happen, for example, if ** the database is empty) then *pSize is set to 0. */ -static int fileBtreeDataSize(BtCursor *pCur, int *pSize){ - Cell *pCell; +int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ MemPage *pPage; pPage = pCur->pPage; assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pCur->idx >= pPage->nCell || pPage->zeroData ){ *pSize = 0; }else{ - pCell = pPage->apCell[pCur->idx]; - *pSize = NDATA(pCur->pBt, pCell->h); + unsigned char *cell; + u64 size; + cell = pPage->aCell[pCur->idx]; + cell += 2; /* Skip the offset to the next cell */ + if( pPage->leaf ){ + cell += 4; /* Skip the child pointer */ + } + getVarint(data, size); + assert( (size & 0x00000000ffffffff)==size ); + *pSize = size; } return SQLITE_OK; } /* -** Read part of the data associated with cursor pCur. A maximum -** of "amt" bytes will be transfered into zBuf[]. The transfer -** begins at "offset". The number of bytes actually read is -** returned. The amount returned will be smaller than the -** amount requested if there are not enough bytes in the data -** to satisfy the request. +** Read part of the data associated with cursor pCur. Exactly +** "amt" bytes will be transfered into zBuf[]. The transfer +** begins at "offset". +** +** Return SQLITE_OK on success or an error code if anything goes +** wrong. An error is returned if "offset+amt" is larger than +** the available payload. */ -static int fileBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ - Cell *pCell; +int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ MemPage *pPage; assert( amt>=0 ); @@ -1335,115 +1489,28 @@ static int fileBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ return 0; } pCell = pPage->apCell[pCur->idx]; - assert( amt+offset <= NDATA(pCur->pBt, pCell->h) ); - getPayload(pCur, offset + NKEY(pCur->pBt, pCell->h), amt, zBuf); - return amt; -} - -/* -** Compare an external key against the key on the entry that pCur points to. -** -** The external key is pKey and is nKey bytes long. The last nIgnore bytes -** of the key associated with pCur are ignored, as if they do not exist. -** (The normal case is for nIgnore to be zero in which case the entire -** internal key is used in the comparison.) -** -** The comparison result is written to *pRes as follows: -** -** *pRes<0 This means pCur<pKey -** -** *pRes==0 This means pCur==pKey for all nKey bytes -** -** *pRes>0 This means pCur>pKey -** -** When one key is an exact prefix of the other, the shorter key is -** considered less than the longer one. In order to be equal the -** keys must be exactly the same length. (The length of the pCur key -** is the actual key length minus nIgnore bytes.) -*/ -static int fileBtreeKeyCompare( - BtCursor *pCur, /* Pointer to entry to compare against */ - const void *pKey, /* Key to compare against entry that pCur points to */ - int nKey, /* Number of bytes in pKey */ - int nIgnore, /* Ignore this many bytes at the end of pCur */ - int *pResult /* Write the result here */ -){ - Pgno nextPage; - int n, c, rc, nLocal; - Cell *pCell; - Btree *pBt = pCur->pBt; - const char *zKey = (const char*)pKey; - - assert( pCur->pPage ); - assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); - pCell = pCur->pPage->apCell[pCur->idx]; - nLocal = NKEY(pBt, pCell->h) - nIgnore; - if( nLocal<0 ) nLocal = 0; - n = nKey<nLocal ? nKey : nLocal; - if( n>MX_LOCAL_PAYLOAD ){ - n = MX_LOCAL_PAYLOAD; - } - c = memcmp(pCell->aPayload, zKey, n); - if( c!=0 ){ - *pResult = c; - return SQLITE_OK; - } - zKey += n; - nKey -= n; - nLocal -= n; - nextPage = SWAB32(pBt, pCell->ovfl); - while( nKey>0 && nLocal>0 ){ - OverflowPage *pOvfl; - if( nextPage==0 ){ - return SQLITE_CORRUPT; - } - rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&pOvfl); - if( rc ){ - return rc; - } - nextPage = SWAB32(pBt, pOvfl->iNext); - n = nKey<nLocal ? nKey : nLocal; - if( n>OVERFLOW_SIZE ){ - n = OVERFLOW_SIZE; - } - c = memcmp(pOvfl->aPayload, zKey, n); - sqlitepager_unref(pOvfl); - if( c!=0 ){ - *pResult = c; - return SQLITE_OK; - } - nKey -= n; - nLocal -= n; - zKey += n; - } - if( c==0 ){ - c = nLocal - nKey; - } - *pResult = c; - return SQLITE_OK; + return getPayload(pCur, offset, amt, pBuf, 1); } /* ** Move the cursor down to a new child page. The newPgno argument is the ** page number of the child page in the byte order of the disk image. */ -static int moveToChild(BtCursor *pCur, int newPgno){ +static int moveToChild(BtCursor *pCur, u32 newPgno){ int rc; MemPage *pNewPage; + MemPage *pOldPage; + unsigned char *aData; Btree *pBt = pCur->pBt; - newPgno = SWAB32(pBt, newPgno); - rc = sqlitepager_get(pBt->pPager, newPgno, (void**)&pNewPage); + rc = getPage(pBt, newPgno, &pNewPage); if( rc ) return rc; - rc = initPage(pBt, pNewPage, newPgno, pCur->pPage); + rc = initPage(pNewPage, pCur->pPage); if( rc ) return rc; - assert( pCur->idx>=pCur->pPage->nCell - || pCur->pPage->apCell[pCur->idx]->h.leftChild==SWAB32(pBt,newPgno) ); - assert( pCur->idx<pCur->pPage->nCell - || pCur->pPage->u.hdr.rightChild==SWAB32(pBt,newPgno) ); pNewPage->idxParent = pCur->idx; - pCur->pPage->idxShift = 0; - sqlitepager_unref(pCur->pPage); + pOldPage = pCur->pPage; + pOldPage->idxShift = 0; + releasePage(pOldPage); pCur->pPage = pNewPage; pCur->idx = 0; if( pNewPage->nCell<1 ){ @@ -1465,13 +1532,15 @@ static void moveToParent(BtCursor *pCur){ MemPage *pParent; MemPage *pPage; int idxParent; + pPage = pCur->pPage; assert( pPage!=0 ); pParent = pPage->pParent; assert( pParent!=0 ); idxParent = pPage->idxParent; - sqlitepager_ref(pParent); - sqlitepager_unref(pPage); + sqlitepager_ref(pParent->aData); + oldPgno = pPage->pgno; + releasePage(pPage); pCur->pPage = pParent; assert( pParent->idxShift==0 ); if( pParent->idxShift==0 ){ @@ -1480,11 +1549,10 @@ static void moveToParent(BtCursor *pCur){ /* Verify that pCur->idx is the correct index to point back to the child ** page we just came from */ - oldPgno = SWAB32(pCur->pBt, sqlitepager_pagenumber(pPage)); if( pCur->idx<pParent->nCell ){ - assert( pParent->apCell[idxParent]->h.leftChild==oldPgno ); + assert( get4Byte(&pParent->aCell[idxParent][2])==oldPgno ); }else{ - assert( pParent->u.hdr.rightChild==oldPgno ); + assert( get4Byte(&pParent->aData[pParent->hdrOffset+6])==oldPgno ); } #endif }else{ @@ -1495,9 +1563,8 @@ static void moveToParent(BtCursor *pCur){ */ int i; pCur->idx = pParent->nCell; - oldPgno = SWAB32(pCur->pBt, sqlitepager_pagenumber(pPage)); for(i=0; i<pParent->nCell; i++){ - if( pParent->apCell[i]->h.leftChild==oldPgno ){ + if( get4byte(&pParent->aCell[i][2])==oldPgno ){ pCur->idx = i; break; } @@ -1509,16 +1576,16 @@ static void moveToParent(BtCursor *pCur){ ** Move the cursor to the root page */ static int moveToRoot(BtCursor *pCur){ - MemPage *pNew; + MemPage *pRoot; int rc; Btree *pBt = pCur->pBt; - rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pNew); + rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, &pRoot); if( rc ) return rc; - rc = initPage(pBt, pNew, pCur->pgnoRoot, 0); + rc = initPage(pRoot, 0); if( rc ) return rc; - sqlitepager_unref(pCur->pPage); - pCur->pPage = pNew; + releasePage(pCur->pPage); + pCur->pPage = pRoot; pCur->idx = 0; return SQLITE_OK; } @@ -1530,8 +1597,11 @@ static int moveToRoot(BtCursor *pCur){ static int moveToLeftmost(BtCursor *pCur){ Pgno pgno; int rc; + MemPage *pPage; - while( (pgno = pCur->pPage->apCell[pCur->idx]->h.leftChild)!=0 ){ + while( !(pPage = pCur->pPage)->leaf ){ + assert( pCur->idx>=0 && pCur->idx<pPage->nPage ); + pgno = get4byte(pPage->aCell[pCur->idx][2]); rc = moveToChild(pCur, pgno); if( rc ) return rc; } @@ -1548,13 +1618,15 @@ static int moveToLeftmost(BtCursor *pCur){ static int moveToRightmost(BtCursor *pCur){ Pgno pgno; int rc; + MemPage *pPage; - while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){ - pCur->idx = pCur->pPage->nCell; + while( !(pPage = pCur->pPage)->leaf ){ + pgno = get4byte(&pPage->aData[pPage->hdrOffset+6]); + pCur->idx = pPage->nCell; rc = moveToChild(pCur, pgno); if( rc ) return rc; } - pCur->idx = pCur->pPage->nCell - 1; + pCur->idx = pPage->nCell - 1; return SQLITE_OK; } @@ -1562,7 +1634,7 @@ static int moveToRightmost(BtCursor *pCur){ ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ -static int fileBtreeFirst(BtCursor *pCur, int *pRes){ +int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ int rc; if( pCur->pPage==0 ) return SQLITE_ABORT; rc = moveToRoot(pCur); @@ -1581,7 +1653,7 @@ static int fileBtreeFirst(BtCursor *pCur, int *pRes){ ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ -static int fileBtreeLast(BtCursor *pCur, int *pRes){ +int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ int rc; if( pCur->pPage==0 ) return SQLITE_ABORT; rc = moveToRoot(pCur); @@ -1597,9 +1669,14 @@ static int fileBtreeLast(BtCursor *pCur, int *pRes){ return rc; } -/* Move the cursor so that it points to an entry near pKey. +/* Move the cursor so that it points to an entry near pKey/nKey. ** Return a success code. ** +** For INTKEY tables, only the nKey parameter is used. pKey is +** ignored. For other tables, nKey is the number of bytes of data +** in nKey. The comparison function specified when the cursor was +** created is used to compare keys. +** ** If an exact match is not found, then the cursor is always ** left pointing at a leaf page which would hold the entry if it ** were present. The cursor might point to an entry that comes @@ -1620,8 +1697,7 @@ static int fileBtreeLast(BtCursor *pCur, int *pRes){ ** *pRes>0 The cursor is left pointing at an entry that ** is larger than pKey. */ -static -int fileBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ +int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){ int rc; if( pCur->pPage==0 ) return SQLITE_ABORT; pCur->eSkip = SKIP_NONE; @@ -1635,9 +1711,28 @@ int fileBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ lwr = 0; upr = pPage->nCell-1; while( lwr<=upr ){ + void *pCellKey; + u64 nCellKey; pCur->idx = (lwr+upr)/2; - rc = fileBtreeKeyCompare(pCur, pKey, nKey, 0, &c); - if( rc ) return rc; + nCellKey = sqlite3BtreeKeySize(pCur, &nCellKey); + if( pPage->intKey ){ + if( nCellKey<nKey ){ + c = -1; + }else if( nCellKey>nKey ){ + c = +1; + }else{ + c = 0; + } + }else if( (pCellKey = sqlite3BtreeKeyFetch(pCur))!=0 ){ + c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); + }else{ + pCellKey = sqliteMalloc( nCellKey ); + if( pCellKey==0 ) return SQLITE_NOMEM; + rc = sqlite3BtreeKey(pCur, 0, nCellKey, pCellKey); + c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); + sqliteFree(pCellKey); + if( rc ) return rc; + } if( c==0 ){ pCur->iMatch = c; if( pRes ) *pRes = 0; @@ -1651,10 +1746,12 @@ int fileBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ } assert( lwr==upr+1 ); assert( pPage->isInit ); - if( lwr>=pPage->nCell ){ - chldPg = pPage->u.hdr.rightChild; + if( pPage->leaf ){ + chldpg = 0; + }else if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+6]); }else{ - chldPg = pPage->apCell[lwr]->h.leftChild; + chldPg = get4byte(&pPage->aCell[lwr][2]); } if( chldPg==0 ){ pCur->iMatch = c; @@ -1674,7 +1771,7 @@ int fileBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. */ -static int fileBtreeNext(BtCursor *pCur, int *pRes){ +int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; MemPage *pPage = pCur->pPage; assert( pRes!=0 ); @@ -1697,8 +1794,8 @@ static int fileBtreeNext(BtCursor *pCur, int *pRes){ pCur->eSkip = SKIP_NONE; pCur->idx++; if( pCur->idx>=pPage->nCell ){ - if( pPage->u.hdr.rightChild ){ - rc = moveToChild(pCur, pPage->u.hdr.rightChild); + if( !pPage->left ){ + rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+6]); if( rc ) return rc; rc = moveToLeftmost(pCur); *pRes = 0; @@ -1716,7 +1813,7 @@ static int fileBtreeNext(BtCursor *pCur, int *pRes){ return SQLITE_OK; } *pRes = 0; - if( pPage->u.hdr.rightChild==0 ){ + if( pPage->leaf ){ return SQLITE_OK; } rc = moveToLeftmost(pCur); @@ -1729,7 +1826,7 @@ static int fileBtreeNext(BtCursor *pCur, int *pRes){ ** was already pointing to the first entry in the database before ** this routine was called, then set *pRes=1. */ -static int fileBtreePrevious(BtCursor *pCur, int *pRes){ +int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int rc; Pgno pgno; MemPage *pPage; @@ -1751,7 +1848,8 @@ static int fileBtreePrevious(BtCursor *pCur, int *pRes){ } pCur->eSkip = SKIP_NONE; assert( pCur->idx>=0 ); - if( (pgno = pPage->apCell[pCur->idx]->h.leftChild)!=0 ){ + if( !pPage->left ){ + pgno = get4byte(&pPage->aCell[pCur->idx][2]); rc = moveToChild(pCur, pgno); if( rc ) return rc; rc = moveToRightmost(pCur); @@ -1789,227 +1887,239 @@ static int fileBtreePrevious(BtCursor *pCur, int *pRes){ ** which in turn can make database access faster. */ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno, Pgno nearby){ - PageOne *pPage1 = pBt->page1; + u32 pn; + MemPage *pPage1; + MemPage *pPage; int rc; - if( pPage1->freeList ){ - OverflowPage *pOvfl; - FreelistInfo *pInfo; - - rc = sqlitepager_write(pPage1); + int n; /* Number of pages on the freelist */ + int k; /* Number of leaves on the trunk of the freelist */ + + pPage1 = pBt->pPage1; + n = get4byte(&pPage1->aData[36]); + if( n>0 ){ + /* There exists pages on the freelist. Reuse one of those pages. */ + MemPage *pTrunk; + rc = sqlitepager_write(pPage1->aData); if( rc ) return rc; - SWAB_ADD(pBt, pPage1->nFree, -1); - rc = sqlitepager_get(pBt->pPager, SWAB32(pBt, pPage1->freeList), - (void**)&pOvfl); + put4byte(&pPage1->aData[36], n-1); + rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk); if( rc ) return rc; - rc = sqlitepager_write(pOvfl); + rc = sqlitepager_write(pTrunk->aData); if( rc ){ - sqlitepager_unref(pOvfl); + releasePage(pTrunk); return rc; } - pInfo = (FreelistInfo*)pOvfl->aPayload; - if( pInfo->nFree==0 ){ - *pPgno = SWAB32(pBt, pPage1->freeList); - pPage1->freeList = pOvfl->iNext; - *ppPage = (MemPage*)pOvfl; + k = get4byte(&pTrunk->aData[4]); + if( k==0 ){ + /* The trunk has no leaves. So extract the trunk page itself and + ** use it as the newly allocated page */ + *pPgno = get4byte(pPage1->aData[32]); + memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4); + *ppPage = pTrunk; }else{ - int closest, n; - n = SWAB32(pBt, pInfo->nFree); - if( n>1 && nearby>0 ){ + /* Extract a leaf from the trunk */ + int closest; + unsigned char *aData = pTrunk->aData; + if( nearby>0 ){ int i, dist; closest = 0; - dist = SWAB32(pBt, pInfo->aFree[0]) - nearby; + dist = get4byte(&aData[8]) - nearby; if( dist<0 ) dist = -dist; for(i=1; i<n; i++){ - int d2 = SWAB32(pBt, pInfo->aFree[i]) - nearby; + int d2 = get4byte(&aData[8+i*4]) - nearby; if( d2<0 ) d2 = -d2; if( d2<dist ) closest = i; } }else{ closest = 0; } - SWAB_ADD(pBt, pInfo->nFree, -1); - *pPgno = SWAB32(pBt, pInfo->aFree[closest]); - pInfo->aFree[closest] = pInfo->aFree[n-1]; - rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage); - sqlitepager_unref(pOvfl); + put4byte(&aData[4], n-1); + *pPgno = get4data(&aData[8+closest*4]); + memcpy(&aData[8+closest*4], &aData[4+closest*n], 4); + rc = getPage(pBt, *pPgno, ppPage); + releasePage(pTrunk); if( rc==SQLITE_OK ){ sqlitepager_dont_rollback(*ppPage); - rc = sqlitepager_write(*ppPage); + rc = sqlitepager_write((*ppPage)->aData); } } }else{ + /* There are no pages on the freelist, so create a new page at the + ** end of the file */ *pPgno = sqlitepager_pagecount(pBt->pPager) + 1; - rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage); + rc = getPage(pBt, *pPgno, ppPage); if( rc ) return rc; - rc = sqlitepager_write(*ppPage); + rc = sqlitepager_write((*ppPage)->aData); } return rc; } /* -** Add a page of the database file to the freelist. Either pgno or -** pPage but not both may be 0. +** Add a page of the database file to the freelist. ** ** sqlitepager_unref() is NOT called for pPage. */ -static int freePage(Btree *pBt, void *pPage, Pgno pgno){ - PageOne *pPage1 = pBt->page1; - OverflowPage *pOvfl = (OverflowPage*)pPage; - int rc; - int needUnref = 0; - MemPage *pMemPage; - - if( pgno==0 ){ - assert( pOvfl!=0 ); - pgno = sqlitepager_pagenumber(pOvfl); - } - assert( pgno>2 ); - assert( sqlitepager_pagenumber(pOvfl)==pgno ); - pMemPage = (MemPage*)pPage; - pMemPage->isInit = 0; - if( pMemPage->pParent ){ - sqlitepager_unref(pMemPage->pParent); - pMemPage->pParent = 0; - } - rc = sqlitepager_write(pPage1); - if( rc ){ - return rc; - } - SWAB_ADD(pBt, pPage1->nFree, 1); - if( pPage1->nFree!=0 && pPage1->freeList!=0 ){ - OverflowPage *pFreeIdx; - rc = sqlitepager_get(pBt->pPager, SWAB32(pBt, pPage1->freeList), - (void**)&pFreeIdx); - if( rc==SQLITE_OK ){ - FreelistInfo *pInfo = (FreelistInfo*)pFreeIdx->aPayload; - int n = SWAB32(pBt, pInfo->nFree); - if( n<(sizeof(pInfo->aFree)/sizeof(pInfo->aFree[0])) ){ - rc = sqlitepager_write(pFreeIdx); - if( rc==SQLITE_OK ){ - pInfo->aFree[n] = SWAB32(pBt, pgno); - SWAB_ADD(pBt, pInfo->nFree, 1); - sqlitepager_unref(pFreeIdx); - sqlitepager_dont_write(pBt->pPager, pgno); - return rc; - } - } - sqlitepager_unref(pFreeIdx); - } - } - if( pOvfl==0 ){ - assert( pgno>0 ); - rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pOvfl); +static int freePage(MemPage *pPage){ + Btree *pBt = pPage->pBt; + MemPage *pPage1 = pBt->pPage1; + int rc, n, k; + + /* Prepare the page for freeing */ + assert( pPage->pgno>1 ); + pPage->isInit = 0; + releasePage(pPage->pParent); + pPage->pParent = 0; + + /* Increment the free page count on page1 */ + rc = sqlitepager_write(pPage1->aData); + if( rc ) return rc; + n = get4byte(&pPage1->aData[36]); + put4byte(&pPage1->aData[36], n+1); + + if( n==0 ){ + /* This is the first free page */ + memset(pPage->aData, 0, 8); + put4byte(pPage1->aData[32], pPage->pgno); + }else{ + /* Other free pages already exist. Retrive the first trunk page + ** of the freelist and find out how many leaves it has. */ + MemPage *pTrunk + rc = getPage(pBt, get4byte(pPage1->aData[32], &pTrunk); if( rc ) return rc; - needUnref = 1; - } - rc = sqlitepager_write(pOvfl); - if( rc ){ - if( needUnref ) sqlitepager_unref(pOvfl); - return rc; + k = get4byte(&pTrunk->aData[4]); + if( k==pBt->pageSize/4 - 8 ){ + /* The trunk is full. Turn the page being freed into a new + ** trunk page with no leaves. */ + rc = sqlitepager_write(pPage->aData); + if( rc ) return rc; + put4byte(pPage->aData, pTrunk->pgno); + put4byte(&pPage->aData[4], 0); + put4byte(&pPage1->aData[32], pPage->pgno); + }else{ + /* Add the newly freed page as a leaf on the current trunk */ + rc = sqlitepager_write(pTrunk->aData); + if( rc ) return rc; + put4byte(&pTrunk->aData[4], k+1); + put4byte(&pTrunk->aData[8+k*4], pPage->pgno); + sqlitepager_dont_write(pBt->pPager, pPage->pgno); + } + releasePage(pTrunk); } - pOvfl->iNext = pPage1->freeList; - pPage1->freeList = SWAB32(pBt, pgno); - memset(pOvfl->aPayload, 0, OVERFLOW_SIZE); - if( needUnref ) rc = sqlitepager_unref(pOvfl); return rc; } /* -** Erase all the data out of a cell. This involves returning overflow -** pages back the freelist. +** Free any overflow pages associated with the given Cell. */ -static int clearCell(Btree *pBt, Cell *pCell){ - Pager *pPager = pBt->pPager; - OverflowPage *pOvfl; - Pgno ovfl, nextOvfl; - int rc; +static int clearCell(MemPage *pPage, unsigned char *pCell){ + Btree *pBt = pPage->pBt; + int rc, n; + u64 nData, nKey; + Pgno ovflPgno; - if( NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h) <= MX_LOCAL_PAYLOAD ){ - return SQLITE_OK; + parseCellHeader(pPage, pCell, &nData, &nKey, &n); + nPayload = nData; + if( !pPage->intKey ){ + nPayload += nKey; } - ovfl = SWAB32(pBt, pCell->ovfl); - pCell->ovfl = 0; - while( ovfl ){ - rc = sqlitepager_get(pPager, ovfl, (void**)&pOvfl); + if( nPayload<=pBt->maxLocal ){ + return; /* There are no overflow pages. Return without doing anything */ + } + ovflPgno = get4byte(&pCell[n+pBt->maxLocal]); + while( ovflPgno!=0 ){ + MemPage *pOvfl; + rc = getPage(pBt, ovflPgno, &pOvfl); if( rc ) return rc; - nextOvfl = SWAB32(pBt, pOvfl->iNext); + ovflPgno = get4byte(pOvfl->aData); rc = freePage(pBt, pOvfl, ovfl); if( rc ) return rc; - sqlitepager_unref(pOvfl); - ovfl = nextOvfl; + sqlitepager_unref(pOvfl->aData); } return SQLITE_OK; } /* -** Create a new cell from key and data. Overflow pages are allocated as -** necessary and linked to this cell. +** Compute the number of bytes required by a cell header. If the *pHeader +** argument is not NULL, fill it in with the bytes of the header. +*/ +static int makeCellHeader( + MemPage *pPage, /* The page that will contain the cell */ + u64 nKey, /* Size of key, or the key value if intKey */ + int nData, /* Size of data. Ignored for zerodata */ + unsigned char *pHeader /* Write header bytes here */ +){ + int n = 2; + if( !pPage->leaf ) n += 4; + if( !pPage->zeroData ){ + n += putVarint(&pHeader[n], nData); + } + n += putVarint(&pHeader[n], nKey); + return n; +} + +/* +** Fill in the payload section of a cell into the space provided. If +** the payload will not completely fit in the cell, allocate additional +** overflow pages and fill them in. */ static int fillInCell( - Btree *pBt, /* The whole Btree. Needed to allocate pages */ - Cell *pCell, /* Populate this Cell structure */ - const void *pKey, int nKey, /* The key */ + MemPage *pPage, /* The page that contains the cell */ + unsigned char *pCell, /* Pointer to start of payload area */ + int nHeader, /* Number of bytes in the cell header */ + const void *pKey, u64 nKey, /* The key */ const void *pData,int nData /* The data */ ){ - OverflowPage *pOvfl, *pPrior; - Pgno *pNext; - int spaceLeft; - int n, rc; int nPayload; - const char *pPayload; - char *pSpace; - Pgno nearby = 0; - - pCell->h.leftChild = 0; - pCell->h.nKey = SWAB16(pBt, nKey & 0xffff); - pCell->h.nKeyHi = nKey >> 16; - pCell->h.nData = SWAB16(pBt, nData & 0xffff); - pCell->h.nDataHi = nData >> 16; - pCell->h.iNext = 0; - - pNext = &pCell->ovfl; - pSpace = pCell->aPayload; - spaceLeft = MX_LOCAL_PAYLOAD; - pPayload = pKey; - pKey = 0; - nPayload = nKey; - pPrior = 0; + const void *pSrc; + int nSrc, nSrc2; + int spaceLeft; + MemPage *pOvfl = 0; + unsigned char *pPrior; + unsigned char *pPayload; + Btree *pBt = pPage->pBt; + Pgno pgnoOvfl = 0; + + nPayload = nData; + if( pPage->intKey ){ + pSrc = pData; + nSrc = nData; + nSrc2 = 0; + }else{ + nPayload += nKey; + pSrc = pKey; + nSrc = nKey; + } + spaceLeft = pBt->maxLocal; + pPayload = &pCell[nHeader]; + pPrior = &pPayload[pBt->maxLocal]; + while( nPayload>0 ){ if( spaceLeft==0 ){ - rc = allocatePage(pBt, (MemPage**)&pOvfl, pNext, nearby); - if( rc ){ - *pNext = 0; - }else{ - nearby = *pNext; - } - if( pPrior ) sqlitepager_unref(pPrior); + rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl); if( rc ){ - clearCell(pBt, pCell); + clearCell(pPage, pCell); return rc; } - if( pBt->needSwab ) *pNext = swab32(*pNext); - pPrior = pOvfl; - spaceLeft = OVERFLOW_SIZE; - pSpace = pOvfl->aPayload; - pNext = &pOvfl->iNext; + put4byte(pPrior, pgnoOvfl); + pPrior = pOvfl->aData; + put4byte(pPrior, 0); + pPayload = &pOvfl->aData[4]; + spaceLeft = pBt->pageSize - 4; } n = nPayload; if( n>spaceLeft ) n = spaceLeft; - memcpy(pSpace, pPayload, n); + if( n>nSrc ) n = nSrc; + memcpy(pPayload, pSrc, n); nPayload -= n; - if( nPayload==0 && pData ){ - pPayload = pData; - nPayload = nData; - pData = 0; - }else{ - pPayload += n; - } + nSrc -= n; spaceLeft -= n; - pSpace += n; - } - *pNext = 0; - if( pPrior ){ - sqlitepager_unref(pPrior); + if( nSrc==0 ){ + nSrc = nData; + pSrc = pData; + } + if( pOvfl && (spaceLeft==0 || nPayload==0) ){ + releasePage(pOvfl); + } } return SQLITE_OK; } @@ -2655,7 +2765,7 @@ static int checkReadLocks(BtCursor *pCur){ ** define what database the record should be inserted into. The cursor ** is left pointing at the new record. */ -static int fileBtreeInsert( +int sqlite3BtreeInsert( BtCursor *pCur, /* Insert data into the table of this cursor */ const void *pKey, int nKey, /* The key of the new record */ const void *pData, int nData /* The data of the new record */ @@ -2681,7 +2791,7 @@ static int fileBtreeInsert( if( checkReadLocks(pCur) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } - rc = fileBtreeMoveto(pCur, pKey, nKey, &loc); + rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc); if( rc ) return rc; pPage = pCur->pPage; assert( pPage->isInit ); @@ -2723,7 +2833,7 @@ static int fileBtreeInsert( ** sqliteBtreePrevious() will always leave the cursor pointing at the ** entry immediately before the one that was deleted. */ -static int fileBtreeDelete(BtCursor *pCur){ +int sqlite3BtreeDelete(BtCursor *pCur){ MemPage *pPage = pCur->pPage; Cell *pCell; int rc; @@ -2766,7 +2876,7 @@ static int fileBtreeDelete(BtCursor *pCur){ int szNext; int notUsed; getTempCursor(pCur, &leafCur); - rc = fileBtreeNext(&leafCur, ¬Used); + rc = sqlite3BtreeNext(&leafCur, ¬Used); if( rc!=SQLITE_OK ){ if( rc!=SQLITE_NOMEM ) rc = SQLITE_CORRUPT; return rc; @@ -2812,7 +2922,7 @@ static int fileBtreeDelete(BtCursor *pCur){ ** BTree indices are restricted to having an arbitrary key and no data. ** But for now, this routine also serves to create indices. */ -static int fileBtreeCreateTable(Btree *pBt, int *piTable){ +int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){ MemPage *pRoot; Pgno pgnoRoot; int rc; @@ -2875,7 +2985,7 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){ /* ** Delete all information from a single table in the database. */ -static int fileBtreeClearTable(Btree *pBt, int iTable){ +int sqlite3BtreeClearTable(Btree *pBt, int iTable){ int rc; BtCursor *pCur; if( !pBt->inTrans ){ @@ -2889,7 +2999,7 @@ static int fileBtreeClearTable(Btree *pBt, int iTable){ } rc = clearDatabasePage(pBt, (Pgno)iTable, 0); if( rc ){ - fileBtreeRollback(pBt); + sqlite3BtreeRollback(pBt); } return rc; } @@ -2899,7 +3009,7 @@ static int fileBtreeClearTable(Btree *pBt, int iTable){ ** the freelist. Except, the root of the principle table (the one on ** page 2) is never added to the freelist. */ -static int fileBtreeDropTable(Btree *pBt, int iTable){ +int sqlite3BtreeDropTable(Btree *pBt, int iTable){ int rc; MemPage *pPage; BtCursor *pCur; @@ -2913,7 +3023,7 @@ static int fileBtreeDropTable(Btree *pBt, int iTable){ } rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage); if( rc ) return rc; - rc = fileBtreeClearTable(pBt, iTable); + rc = sqlite3BtreeClearTable(pBt, iTable); if( rc ) return rc; if( iTable>2 ){ rc = freePage(pBt, pPage, iTable); @@ -3026,8 +3136,7 @@ static int copyDatabasePage( /* ** Read the meta-information out of a database file. */ -static int fileBtreeGetMeta(Btree *pBt, int *aMeta){ - PageOne *pP1; +int sqlite3BtreeGetMeta(Btree *pBt, int idx, u32 *pMeta){ int rc; int i; @@ -3044,7 +3153,7 @@ static int fileBtreeGetMeta(Btree *pBt, int *aMeta){ /* ** Write meta-information back into the database. */ -static int fileBtreeUpdateMeta(Btree *pBt, int *aMeta){ +int sqlite3BtreeUpdateMeta(Btree *pBt, int idx, u32 iMeta){ PageOne *pP1; int rc, i; if( !pBt->inTrans ){ @@ -3188,7 +3297,7 @@ static int fileBtreeCursorDump(BtCursor *pCur, int *aResult){ ** Return the pager associated with a BTree. This routine is used for ** testing and debugging only. */ -static Pager *fileBtreePager(Btree *pBt){ +Pager *sqlite3BtreePager(Btree *pBt){ return pBt->pPager; } @@ -3460,7 +3569,7 @@ static int checkTreePage( ** and a pointer to that error message is returned. The calling function ** is responsible for freeing the error message when it is done. */ -char *fileBtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ +char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ int i; int nRef; IntegrityCk sCheck; @@ -3524,7 +3633,7 @@ char *fileBtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ /* ** Return the full pathname of the underlying database file. */ -static const char *fileBtreeGetFilename(Btree *pBt){ +const char *sqlite3BtreeGetFilename(Btree *pBt){ assert( pBt->pPager!=0 ); return sqlitepager_filename(pBt->pPager); } @@ -3536,7 +3645,7 @@ static const char *fileBtreeGetFilename(Btree *pBt){ ** The size of file pBtFrom may be reduced by this operation. ** If anything goes wrong, the transaction on pBtFrom is rolled back. */ -static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ +int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ int rc = SQLITE_OK; Pgno i, nPage, nToPage; @@ -3567,57 +3676,7 @@ static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ rc = sqlitepager_truncate(pBtTo->pPager, nPage); } if( rc ){ - fileBtreeRollback(pBtTo); + sqlite3BtreeRollback(pBtTo); } return rc; } - -/* -** The following tables contain pointers to all of the interface -** routines for this implementation of the B*Tree backend. To -** substitute a different implemention of the backend, one has merely -** to provide pointers to alternative functions in similar tables. -*/ -static BtOps sqliteBtreeOps = { - fileBtreeClose, - fileBtreeSetCacheSize, - fileBtreeSetSafetyLevel, - fileBtreeBeginTrans, - fileBtreeCommit, - fileBtreeRollback, - fileBtreeBeginCkpt, - fileBtreeCommitCkpt, - fileBtreeRollbackCkpt, - fileBtreeCreateTable, - fileBtreeCreateTable, /* Really sqliteBtreeCreateIndex() */ - fileBtreeDropTable, - fileBtreeClearTable, - fileBtreeCursor, - fileBtreeGetMeta, - fileBtreeUpdateMeta, - fileBtreeIntegrityCheck, - fileBtreeGetFilename, - fileBtreeCopyFile, - fileBtreePager, -#ifdef SQLITE_TEST - fileBtreePageDump, -#endif -}; -static BtCursorOps sqliteBtreeCursorOps = { - fileBtreeMoveto, - fileBtreeDelete, - fileBtreeInsert, - fileBtreeFirst, - fileBtreeLast, - fileBtreeNext, - fileBtreePrevious, - fileBtreeKeySize, - fileBtreeKey, - fileBtreeKeyCompare, - fileBtreeDataSize, - fileBtreeData, - fileBtreeCloseCursor, -#ifdef SQLITE_TEST - fileBtreeCursorDump, -#endif -}; diff --git a/src/btree.h b/src/btree.h index 7b11da6f6..8e32e2ab9 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.36 2004/02/10 02:57:59 drh Exp $ +** @(#) $Id: btree.h,v 1.37 2004/04/26 14:10:21 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -23,134 +23,61 @@ */ typedef struct Btree Btree; typedef struct BtCursor BtCursor; -typedef struct BtOps BtOps; -typedef struct BtCursorOps BtCursorOps; -/* -** An instance of the following structure contains pointers to all -** methods against an open BTree. Alternative BTree implementations -** (examples: file based versus in-memory) can be created by substituting -** different methods. Users of the BTree cannot tell the difference. -** -** In C++ we could do this by defining a virtual base class and then -** creating subclasses for each different implementation. But this is -** C not C++ so we have to be a little more explicit. -*/ -struct BtOps { - int (*Close)(Btree*); - int (*SetCacheSize)(Btree*, int); - int (*SetSafetyLevel)(Btree*, int); - int (*BeginTrans)(Btree*); - int (*Commit)(Btree*); - int (*Rollback)(Btree*); - int (*BeginCkpt)(Btree*); - int (*CommitCkpt)(Btree*); - int (*RollbackCkpt)(Btree*); - int (*CreateTable)(Btree*, int*); - int (*CreateIndex)(Btree*, int*); - int (*DropTable)(Btree*, int); - int (*ClearTable)(Btree*, int); - int (*Cursor)(Btree*, int iTable, int wrFlag, BtCursor **ppCur); - int (*GetMeta)(Btree*, int*); - int (*UpdateMeta)(Btree*, int*); - char *(*IntegrityCheck)(Btree*, int*, int); - const char *(*GetFilename)(Btree*); - int (*Copyfile)(Btree*,Btree*); - struct Pager *(*Pager)(Btree*); -#ifdef SQLITE_TEST - int (*PageDump)(Btree*, int, int); -#endif -}; +int sqlite3BtreeOpen(const char *zFilename, Btree **, int nCache, int flags); -/* -** An instance of this structure defines all of the methods that can -** be executed against a cursor. +/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the +** following values. */ -struct BtCursorOps { - int (*Moveto)(BtCursor*, const void *pKey, int nKey, int *pRes); - int (*Delete)(BtCursor*); - int (*Insert)(BtCursor*, const void *pKey, int nKey, - const void *pData, int nData); - int (*First)(BtCursor*, int *pRes); - int (*Last)(BtCursor*, int *pRes); - int (*Next)(BtCursor*, int *pRes); - int (*Previous)(BtCursor*, int *pRes); - int (*KeySize)(BtCursor*, int *pSize); - int (*Key)(BtCursor*, int offset, int amt, char *zBuf); - int (*KeyCompare)(BtCursor*, const void *pKey, int nKey, - int nIgnore, int *pRes); - int (*DataSize)(BtCursor*, int *pSize); - int (*Data)(BtCursor*, int offset, int amt, char *zBuf); - int (*CloseCursor)(BtCursor*); -#ifdef SQLITE_TEST - int (*CursorDump)(BtCursor*, int*); -#endif -}; +#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ +#define BTREE_MEMORY 2 /* In-memory DB. No argument */ -/* -** The number of 4-byte "meta" values contained on the first page of each -** database file. -*/ -#define SQLITE_N_BTREE_META 10 +int sqlite3BtreeClose(Btree*); +int sqlite3BtreeSetCacheSize(Btree*,int); +int sqlite3BtreeSetSafetyLevel(Btree*,int); +int sqlite3BtreeBeginTrans(Btree*); +int sqlite3BtreeCommit(Btree*); +int sqlite3BtreeRollback(Btree*); +int sqlite3BtreeBeginStmt(Btree*); +int sqlite3BtreeCommitStmt(Btree*); +int sqlite3BtreeRollbackStmt(Btree*); +int sqlite3BtreeCreateTable(Btree*, int*, int flags); -int sqliteBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree); -int sqliteRbtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree); +/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR +** of the following flags: +*/ +#define BTREE_INTKEY 1 /* Table has only 64-bit integer keys */ +#define BTREE_ZERODATA 2 /* Table has keys only - no data */ -#define btOps(pBt) (*((BtOps **)(pBt))) -#define btCOps(pCur) (*((BtCursorOps **)(pCur))) +int sqlite3BtreeDropTable(Btree*, int); +int sqlite3BtreeClearTable(Btree*, int); +int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); -#define sqliteBtreeClose(pBt) (btOps(pBt)->Close(pBt)) -#define sqliteBtreeSetCacheSize(pBt, sz) (btOps(pBt)->SetCacheSize(pBt, sz)) -#define sqliteBtreeSetSafetyLevel(pBt, sl) (btOps(pBt)->SetSafetyLevel(pBt, sl)) -#define sqliteBtreeBeginTrans(pBt) (btOps(pBt)->BeginTrans(pBt)) -#define sqliteBtreeCommit(pBt) (btOps(pBt)->Commit(pBt)) -#define sqliteBtreeRollback(pBt) (btOps(pBt)->Rollback(pBt)) -#define sqliteBtreeBeginCkpt(pBt) (btOps(pBt)->BeginCkpt(pBt)) -#define sqliteBtreeCommitCkpt(pBt) (btOps(pBt)->CommitCkpt(pBt)) -#define sqliteBtreeRollbackCkpt(pBt) (btOps(pBt)->RollbackCkpt(pBt)) -#define sqliteBtreeCreateTable(pBt,piTable)\ - (btOps(pBt)->CreateTable(pBt,piTable)) -#define sqliteBtreeCreateIndex(pBt, piIndex)\ - (btOps(pBt)->CreateIndex(pBt, piIndex)) -#define sqliteBtreeDropTable(pBt, iTable) (btOps(pBt)->DropTable(pBt, iTable)) -#define sqliteBtreeClearTable(pBt, iTable)\ - (btOps(pBt)->ClearTable(pBt, iTable)) -#define sqliteBtreeCursor(pBt, iTable, wrFlag, ppCur)\ - (btOps(pBt)->Cursor(pBt, iTable, wrFlag, ppCur)) -#define sqliteBtreeMoveto(pCur, pKey, nKey, pRes)\ - (btCOps(pCur)->Moveto(pCur, pKey, nKey, pRes)) -#define sqliteBtreeDelete(pCur) (btCOps(pCur)->Delete(pCur)) -#define sqliteBtreeInsert(pCur, pKey, nKey, pData, nData) \ - (btCOps(pCur)->Insert(pCur, pKey, nKey, pData, nData)) -#define sqliteBtreeFirst(pCur, pRes) (btCOps(pCur)->First(pCur, pRes)) -#define sqliteBtreeLast(pCur, pRes) (btCOps(pCur)->Last(pCur, pRes)) -#define sqliteBtreeNext(pCur, pRes) (btCOps(pCur)->Next(pCur, pRes)) -#define sqliteBtreePrevious(pCur, pRes) (btCOps(pCur)->Previous(pCur, pRes)) -#define sqliteBtreeKeySize(pCur, pSize) (btCOps(pCur)->KeySize(pCur, pSize) ) -#define sqliteBtreeKey(pCur, offset, amt, zBuf)\ - (btCOps(pCur)->Key(pCur, offset, amt, zBuf)) -#define sqliteBtreeKeyCompare(pCur, pKey, nKey, nIgnore, pRes)\ - (btCOps(pCur)->KeyCompare(pCur, pKey, nKey, nIgnore, pRes)) -#define sqliteBtreeDataSize(pCur, pSize) (btCOps(pCur)->DataSize(pCur, pSize)) -#define sqliteBtreeData(pCur, offset, amt, zBuf)\ - (btCOps(pCur)->Data(pCur, offset, amt, zBuf)) -#define sqliteBtreeCloseCursor(pCur) (btCOps(pCur)->CloseCursor(pCur)) -#define sqliteBtreeGetMeta(pBt, aMeta) (btOps(pBt)->GetMeta(pBt, aMeta)) -#define sqliteBtreeUpdateMeta(pBt, aMeta) (btOps(pBt)->UpdateMeta(pBt, aMeta)) -#define sqliteBtreeIntegrityCheck(pBt, aRoot, nRoot)\ - (btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot)) -#define sqliteBtreeGetFilename(pBt) (btOps(pBt)->GetFilename(pBt)) -#define sqliteBtreeCopyFile(pBt1, pBt2) (btOps(pBt1)->Copyfile(pBt1, pBt2)) -#define sqliteBtreePager(pBt) (btOps(pBt)->Pager(pBt)) +int sqlite3BtreeCursor( + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + int(*)(void*,int,const void*,int,const void*), /* Key comparison function */ + void*, /* First argument to compare function */ + BtCursor **ppCursor /* Returned cursor */ +); -#ifdef SQLITE_TEST -#define sqliteBtreePageDump(pBt, pgno, recursive)\ - (btOps(pBt)->PageDump(pBt, pgno, recursive)) -#define sqliteBtreeCursorDump(pCur, aResult)\ - (btCOps(pCur)->CursorDump(pCur, aResult)) -int btree_native_byte_order; -#endif /* SQLITE_TEST */ +int sqlite3BtreeCursorClose(BtCursor*); +int sqlite3BtreeMoveto(BtCursor*, const void *pKey, u64 nKey, int *pRes); +int sqlite3BtreeDelete(BtCursor*); +int sqlite3BtreeInsert(BtCursor*, const void *pKey, u64 nKey, + const void *pData, int nData); +int sqlite3BtreeFirst(BtCursor*, int *pRes); +int sqlite3BtreeLast(BtCursor*, int *pRes); +int sqlite3BtreeNext(BtCursor*, int *pRes); +int sqlite3BtreePrevious(BtCursor*, int *pRes); +int sqlite3BtreeKeySize(BtCursor*, u64 *pSize); +int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); +void *sqlite3BtreeKeyFetch(BtCursor*); +int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); #endif /* _BTREE_H_ */ @@ -352,6 +352,7 @@ int Md5_Init(Tcl_Interp *interp){ return TCL_OK; } +#if 0 /* ** During testing, the special md5sum() aggregate function is available. ** inside SQLite. The following routines implement that function. @@ -383,3 +384,4 @@ static void md5finalize(sqlite_func *context){ void Md5_Register(sqlite *db){ sqlite_create_aggregate(db, "md5sum", -1, md5step, md5finalize, 0); } +#endif diff --git a/src/pager.c b/src/pager.c index 8b6a15465..41db493e7 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.101 2004/02/25 02:20:41 drh Exp $ +** @(#) $Id: pager.c,v 1.102 2004/04/26 14:10:21 drh Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" @@ -85,8 +85,8 @@ static Pager *mainPager = 0; ** This header is only visible to this pager module. The client ** code that calls pager sees only the data that follows the header. ** -** Client code should call sqlitepager_write() on a page prior to making -** any modifications to that page. The first time sqlitepager_write() +** Client code should call sqlite3pager_write() on a page prior to making +** any modifications to that page. The first time sqlite3pager_write() ** is called, the original page contents are written into the rollback ** journal and PgHdr.inJournal and PgHdr.needSync are set. Later, once ** the journal page has made it onto the disk surface, PgHdr.needSync @@ -94,7 +94,7 @@ static Pager *mainPager = 0; ** database file until the journal pages has been synced to disk and the ** PgHdr.needSync has been cleared. ** -** The PgHdr.dirty flag is set when sqlitepager_write() is called and +** The PgHdr.dirty flag is set when sqlite3pager_write() is called and ** is cleared again when the page content is written back to the original ** database file. */ @@ -288,10 +288,10 @@ int journal_format = 3; ** Enable reference count tracking here: */ #ifdef SQLITE_TEST - int pager_refinfo_enable = 0; + int pager3_refinfo_enable = 0; static void pager_refinfo(PgHdr *p){ static int cnt = 0; - if( !pager_refinfo_enable ) return; + if( !pager3_refinfo_enable ) return; printf( "REFCNT: %4d addr=0x%08x nRef=%d\n", p->pgno, (int)PGHDR_TO_DATA(p), p->nRef @@ -386,11 +386,11 @@ static int pager_errcode(Pager *pPager){ ** checkpoint journal. ** ** The Pager keeps a separate list of pages that are currently in -** the checkpoint journal. This helps the sqlitepager_ckpt_commit() +** the checkpoint journal. This helps the sqlite3pager_stmt_commit() ** routine run MUCH faster for the common case where there are many ** pages in memory but only a few are in the checkpoint journal. */ -static void page_add_to_ckpt_list(PgHdr *pPg){ +static void page_add_to_stmt_list(PgHdr *pPg){ Pager *pPager = pPg->pPager; if( pPg->inCkpt ) return; assert( pPg->pPrevCkpt==0 && pPg->pNextCkpt==0 ); @@ -402,7 +402,7 @@ static void page_add_to_ckpt_list(PgHdr *pPg){ pPager->pCkpt = pPg; pPg->inCkpt = 1; } -static void page_remove_from_ckpt_list(PgHdr *pPg){ +static void page_remove_from_stmt_list(PgHdr *pPg){ if( !pPg->inCkpt ) return; if( pPg->pPrevCkpt ){ assert( pPg->pPrevCkpt->pNextCkpt==pPg ); @@ -451,7 +451,7 @@ static void pager_reset(Pager *pPager){ memset(pPager->aHash, 0, sizeof(pPager->aHash)); pPager->nPage = 0; if( pPager->state>=SQLITE_WRITELOCK ){ - sqlitepager_rollback(pPager); + sqlite3pager_rollback(pPager); } sqliteOsUnlock(&pPager->fd); pPager->state = SQLITE_UNLOCK; @@ -474,7 +474,7 @@ static int pager_unwritelock(Pager *pPager){ int rc; PgHdr *pPg; if( pPager->state<SQLITE_WRITELOCK ) return SQLITE_OK; - sqlitepager_ckpt_commit(pPager); + sqlite3pager_stmt_commit(pPager); if( pPager->ckptOpen ){ sqliteOsClose(&pPager->cpfd); pPager->ckptOpen = 0; @@ -772,7 +772,7 @@ end_playback: ** playback all pages of the transaction journal beginning ** at offset pPager->ckptJSize. */ -static int pager_ckpt_playback(Pager *pPager){ +static int pager_stmt_playback(Pager *pPager){ off_t szJ; /* Size of the full journal */ int nRec; /* Number of Records */ int i; /* Loop counter */ @@ -797,7 +797,7 @@ static int pager_ckpt_playback(Pager *pPager){ for(i=nRec-1; i>=0; i--){ rc = pager_playback_one_page(pPager, &pPager->cpfd, 2); assert( rc!=SQLITE_DONE ); - if( rc!=SQLITE_OK ) goto end_ckpt_playback; + if( rc!=SQLITE_OK ) goto end_stmt_playback; } /* Figure out how many pages need to be copied out of the transaction @@ -805,22 +805,22 @@ static int pager_ckpt_playback(Pager *pPager){ */ rc = sqliteOsSeek(&pPager->jfd, pPager->ckptJSize); if( rc!=SQLITE_OK ){ - goto end_ckpt_playback; + goto end_stmt_playback; } rc = sqliteOsFileSize(&pPager->jfd, &szJ); if( rc!=SQLITE_OK ){ - goto end_ckpt_playback; + goto end_stmt_playback; } nRec = (szJ - pPager->ckptJSize)/JOURNAL_PG_SZ(journal_format); for(i=nRec-1; i>=0; i--){ rc = pager_playback_one_page(pPager, &pPager->jfd, journal_format); if( rc!=SQLITE_OK ){ assert( rc!=SQLITE_DONE ); - goto end_ckpt_playback; + goto end_stmt_playback; } } -end_ckpt_playback: +end_stmt_playback: if( rc!=SQLITE_OK ){ pPager->errMask |= PAGER_ERR_CORRUPT; rc = SQLITE_CORRUPT; @@ -838,7 +838,7 @@ end_ckpt_playback: ** failure, the database file might be left in an inconsistent and ** unrepairable state. */ -void sqlitepager_set_cachesize(Pager *pPager, int mxPage){ +void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){ if( mxPage>=0 ){ pPager->noSync = pPager->tempFile; if( pPager->noSync==0 ) pPager->needSync = 0; @@ -877,7 +877,7 @@ void sqlitepager_set_cachesize(Pager *pPager, int mxPage){ ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ -void sqlitepager_set_safety_level(Pager *pPager, int level){ +void sqlite3pager_set_safety_level(Pager *pPager, int level){ pPager->noSync = level==1 || pPager->tempFile; pPager->fullSync = level==3 && !pPager->tempFile; if( pPager->noSync==0 ) pPager->needSync = 0; @@ -892,7 +892,7 @@ void sqlitepager_set_safety_level(Pager *pPager, int level){ ** The OS will automatically delete the temporary file when it is ** closed. */ -static int sqlitepager_opentemp(char *zFile, OsFile *fd){ +static int sqlite3pager_opentemp(char *zFile, OsFile *fd){ int cnt = 8; int rc; do{ @@ -906,14 +906,14 @@ static int sqlitepager_opentemp(char *zFile, OsFile *fd){ /* ** Create a new page cache and put a pointer to the page cache in *ppPager. ** The file to be cached need not exist. The file is not locked until -** the first call to sqlitepager_get() and is only held open until the -** last page is released using sqlitepager_unref(). +** the first call to sqlite3pager_get() and is only held open until the +** last page is released using sqlite3pager_unref(). ** ** If zFilename is NULL then a randomly-named temporary file is created ** and used as the file to be cached. The file will be deleted ** automatically when it is closed. */ -int sqlitepager_open( +int sqlite3pager_open( Pager **ppPager, /* Return the Pager structure here */ const char *zFilename, /* Name of the database file to open */ int mxPage, /* Max number of in-memory cache pages */ @@ -938,7 +938,7 @@ int sqlitepager_open( rc = sqliteOsOpenReadWrite(zFullPathname, &fd, &readOnly); tempFile = 0; }else{ - rc = sqlitepager_opentemp(zTemp, &fd); + rc = sqlite3pager_opentemp(zTemp, &fd); zFilename = zTemp; zFullPathname = sqliteOsFullPathname(zFilename); tempFile = 1; @@ -999,10 +999,10 @@ int sqlitepager_open( ** when the reference count on each page reaches zero. The destructor can ** be used to clean up information in the extra segment appended to each page. ** -** The destructor is not called as a result sqlitepager_close(). -** Destructors are only called by sqlitepager_unref(). +** The destructor is not called as a result sqlite3pager_close(). +** Destructors are only called by sqlite3pager_unref(). */ -void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){ +void sqlite3pager_set_destructor(Pager *pPager, void (*xDesc)(void*)){ pPager->xDestructor = xDesc; } @@ -1010,7 +1010,7 @@ void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){ ** Return the total number of pages in the disk file associated with ** pPager. */ -int sqlitepager_pagecount(Pager *pPager){ +int sqlite3pager_pagecount(Pager *pPager){ off_t n; assert( pPager!=0 ); if( pPager->dbSize>=0 ){ @@ -1035,10 +1035,10 @@ static int syncJournal(Pager*); /* ** Truncate the file to the number of pages specified. */ -int sqlitepager_truncate(Pager *pPager, Pgno nPage){ +int sqlite3pager_truncate(Pager *pPager, Pgno nPage){ int rc; if( pPager->dbSize<0 ){ - sqlitepager_pagecount(pPager); + sqlite3pager_pagecount(pPager); } if( pPager->errMask!=0 ){ rc = pager_errcode(pPager); @@ -1064,11 +1064,11 @@ int sqlitepager_truncate(Pager *pPager, Pgno nPage){ ** with this page cache after this function returns will likely ** result in a coredump. */ -int sqlitepager_close(Pager *pPager){ +int sqlite3pager_close(Pager *pPager){ PgHdr *pPg, *pNext; switch( pPager->state ){ case SQLITE_WRITELOCK: { - sqlitepager_rollback(pPager); + sqlite3pager_rollback(pPager); sqliteOsUnlock(&pPager->fd); assert( pPager->journalOpen==0 ); break; @@ -1107,7 +1107,7 @@ int sqlitepager_close(Pager *pPager){ /* ** Return the page number for the given page data. */ -Pgno sqlitepager_pagenumber(void *pData){ +Pgno sqlite3pager_pagenumber(void *pData){ PgHdr *p = DATA_TO_PGHDR(pData); return p->pgno; } @@ -1146,7 +1146,7 @@ static void _page_ref(PgHdr *pPg){ ** Increment the reference count for a page. The input pointer is ** a reference to the page data. */ -int sqlitepager_ref(void *pData){ +int sqlite3pager_ref(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); page_ref(pPg); return SQLITE_OK; @@ -1300,7 +1300,7 @@ static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ ** The acquisition might fail for several reasons. In all cases, ** an appropriate error code is returned and *ppPage is set to NULL. ** -** See also sqlitepager_lookup(). Both this routine and _lookup() attempt +** See also sqlite3pager_lookup(). Both this routine and _lookup() attempt ** to find a page in the in-memory cache first. If the page is not already ** in memory, this routine goes to disk to read it in whereas _lookup() ** just returns 0. This routine acquires a read-lock the first time it @@ -1308,7 +1308,7 @@ static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ ** Since _lookup() never goes to disk, it never has to deal with locks ** or journal files. */ -int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ +int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ PgHdr *pPg; int rc; @@ -1414,7 +1414,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ if( pPg==0 ){ int rc = syncJournal(pPager); if( rc!=0 ){ - sqlitepager_rollback(pPager); + sqlite3pager_rollback(pPager); return SQLITE_IOERR; } pPg = pPager->pFirst; @@ -1428,7 +1428,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ pPg->pDirty = 0; rc = pager_write_pagelist( pPg ); if( rc!=SQLITE_OK ){ - sqlitepager_rollback(pPager); + sqlite3pager_rollback(pPager); return SQLITE_IOERR; } } @@ -1491,9 +1491,9 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ } if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){ - page_add_to_ckpt_list(pPg); + page_add_to_stmt_list(pPg); }else{ - page_remove_from_ckpt_list(pPg); + page_remove_from_stmt_list(pPg); } pPg->dirty = 0; pPg->nRef = 1; @@ -1509,9 +1509,9 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ if( pPager->nExtra>0 ){ memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra); } - if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager); + if( pPager->dbSize<0 ) sqlite3pager_pagecount(pPager); if( pPager->errMask!=0 ){ - sqlitepager_unref(PGHDR_TO_DATA(pPg)); + sqlite3pager_unref(PGHDR_TO_DATA(pPg)); rc = pager_errcode(pPager); return rc; } @@ -1527,7 +1527,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ off_t fileSize; if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK || fileSize>=pgno*SQLITE_PAGE_SIZE ){ - sqlitepager_unref(PGHDR_TO_DATA(pPg)); + sqlite3pager_unref(PGHDR_TO_DATA(pPg)); return rc; }else{ memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE); @@ -1548,13 +1548,13 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ ** not read the page from disk. Return a pointer to the page, ** or 0 if the page is not in cache. ** -** See also sqlitepager_get(). The difference between this routine -** and sqlitepager_get() is that _get() will go to the disk and read +** See also sqlite3pager_get(). The difference between this routine +** and sqlite3pager_get() is that _get() will go to the disk and read ** in the page if the page is not already in cache. This routine ** returns NULL if the page is not in cache or if a disk I/O error ** has ever happened. */ -void *sqlitepager_lookup(Pager *pPager, Pgno pgno){ +void *sqlite3pager_lookup(Pager *pPager, Pgno pgno){ PgHdr *pPg; assert( pPager!=0 ); @@ -1580,7 +1580,7 @@ void *sqlitepager_lookup(Pager *pPager, Pgno pgno){ ** are released, a rollback occurs and the lock on the database is ** removed. */ -int sqlitepager_unref(void *pData){ +int sqlite3pager_unref(void *pData){ PgHdr *pPg; /* Decrement the reference count for this page @@ -1635,7 +1635,7 @@ static int pager_open_journal(Pager *pPager){ assert( pPager->state==SQLITE_WRITELOCK ); assert( pPager->journalOpen==0 ); assert( pPager->useJournal ); - sqlitepager_pagecount(pPager); + sqlite3pager_pagecount(pPager); pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 ); if( pPager->aInJournal==0 ){ sqliteOsReadLock(&pPager->fd); @@ -1680,7 +1680,7 @@ static int pager_open_journal(Pager *pPager){ rc = write32bits(&pPager->jfd, pPager->dbSize); } if( pPager->ckptAutoopen && rc==SQLITE_OK ){ - rc = sqlitepager_ckpt_begin(pPager); + rc = sqlite3pager_stmt_begin(pPager); } if( rc!=SQLITE_OK ){ rc = pager_unwritelock(pPager); @@ -1695,10 +1695,10 @@ static int pager_open_journal(Pager *pPager){ ** Acquire a write-lock on the database. The lock is removed when ** the any of the following happen: ** -** * sqlitepager_commit() is called. -** * sqlitepager_rollback() is called. -** * sqlitepager_close() is called. -** * sqlitepager_unref() is called to on every outstanding page. +** * sqlite3pager_commit() is called. +** * sqlite3pager_rollback() is called. +** * sqlite3pager_close() is called. +** * sqlite3pager_unref() is called to on every outstanding page. ** ** The parameter to this routine is a pointer to any open page of the ** database file. Nothing changes about the page - it is used merely @@ -1711,7 +1711,7 @@ static int pager_open_journal(Pager *pPager){ ** ** If the database is already write-locked, this routine is a no-op. */ -int sqlitepager_begin(void *pData){ +int sqlite3pager_begin(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager; int rc = SQLITE_OK; @@ -1747,10 +1747,10 @@ int sqlitepager_begin(void *pData){ ** If the journal file could not be written because the disk is full, ** then this routine returns SQLITE_FULL and does an immediate rollback. ** All subsequent write attempts also return SQLITE_FULL until there -** is a call to sqlitepager_commit() or sqlitepager_rollback() to +** is a call to sqlite3pager_commit() or sqlite3pager_rollback() to ** reset. */ -int sqlitepager_write(void *pData){ +int sqlite3pager_write(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager; int rc = SQLITE_OK; @@ -1781,7 +1781,7 @@ int sqlitepager_write(void *pData){ ** create it if it does not. */ assert( pPager->state!=SQLITE_UNLOCK ); - rc = sqlitepager_begin(pData); + rc = sqlite3pager_begin(pData); if( rc!=SQLITE_OK ){ return rc; } @@ -1818,7 +1818,7 @@ int sqlitepager_write(void *pData){ *(u32*)PGHDR_TO_EXTRA(pPg) = saved; } if( rc!=SQLITE_OK ){ - sqlitepager_rollback(pPager); + sqlite3pager_rollback(pPager); pPager->errMask |= PAGER_ERR_FULL; return rc; } @@ -1829,7 +1829,7 @@ int sqlitepager_write(void *pData){ pPg->inJournal = 1; if( pPager->ckptInUse ){ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_ckpt_list(pPg); + page_add_to_stmt_list(pPg); } }else{ pPg->needSync = !pPager->journalStarted && !pPager->noSync; @@ -1853,14 +1853,14 @@ int sqlitepager_write(void *pData){ TRACE2("CKPT-JOURNAL %d\n", pPg->pgno); CODEC(pPager, pData, pPg->pgno, 0); if( rc!=SQLITE_OK ){ - sqlitepager_rollback(pPager); + sqlite3pager_rollback(pPager); pPager->errMask |= PAGER_ERR_FULL; return rc; } pPager->ckptNRec++; assert( pPager->aInCkpt!=0 ); pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_ckpt_list(pPg); + page_add_to_stmt_list(pPg); } /* Update the database size and return. @@ -1873,10 +1873,10 @@ int sqlitepager_write(void *pData){ /* ** Return TRUE if the page given in the argument was previously passed -** to sqlitepager_write(). In other words, return TRUE if it is ok +** to sqlite3pager_write(). In other words, return TRUE if it is ok ** to change the content of the page. */ -int sqlitepager_iswriteable(void *pData){ +int sqlite3pager_iswriteable(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); return pPg->dirty; } @@ -1885,17 +1885,17 @@ int sqlitepager_iswriteable(void *pData){ ** Replace the content of a single page with the information in the third ** argument. */ -int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){ +int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void *pData){ void *pPage; int rc; - rc = sqlitepager_get(pPager, pgno, &pPage); + rc = sqlite3pager_get(pPager, pgno, &pPage); if( rc==SQLITE_OK ){ - rc = sqlitepager_write(pPage); + rc = sqlite3pager_write(pPage); if( rc==SQLITE_OK ){ memcpy(pPage, pData, SQLITE_PAGE_SIZE); } - sqlitepager_unref(pPage); + sqlite3pager_unref(pPage); } return rc; } @@ -1910,11 +1910,11 @@ int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){ ** that it does not get written to disk. ** ** Tests show that this optimization, together with the -** sqlitepager_dont_rollback() below, more than double the speed +** sqlite3pager_dont_rollback() below, more than double the speed ** of large INSERT operations and quadruple the speed of large DELETEs. ** ** When this routine is called, set the alwaysRollback flag to true. -** Subsequent calls to sqlitepager_dont_rollback() for the same page +** Subsequent calls to sqlite3pager_dont_rollback() for the same page ** will thereafter be ignored. This is necessary to avoid a problem ** where a page with data is added to the freelist during one part of ** a transaction then removed from the freelist during a later part @@ -1924,7 +1924,7 @@ int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){ ** critical data, we still need to be sure it gets rolled back in spite ** of the dont_rollback() call. */ -void sqlitepager_dont_write(Pager *pPager, Pgno pgno){ +void sqlite3pager_dont_write(Pager *pPager, Pgno pgno){ PgHdr *pPg; pPg = pager_lookup(pPager, pgno); @@ -1952,7 +1952,7 @@ void sqlitepager_dont_write(Pager *pPager, Pgno pgno){ ** means that the pager does not have to record the given page in the ** rollback journal. */ -void sqlitepager_dont_rollback(void *pData){ +void sqlite3pager_dont_rollback(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager; @@ -1964,7 +1964,7 @@ void sqlitepager_dont_rollback(void *pData){ pPg->inJournal = 1; if( pPager->ckptInUse ){ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_ckpt_list(pPg); + page_add_to_stmt_list(pPg); } TRACE2("DONT_ROLLBACK %d\n", pPg->pgno); } @@ -1972,7 +1972,7 @@ void sqlitepager_dont_rollback(void *pData){ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); assert( pPager->aInCkpt!=0 ); pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_ckpt_list(pPg); + page_add_to_stmt_list(pPg); } } @@ -1983,12 +1983,12 @@ void sqlitepager_dont_rollback(void *pData){ ** and an error code is returned. If the commit worked, SQLITE_OK ** is returned. */ -int sqlitepager_commit(Pager *pPager){ +int sqlite3pager_commit(Pager *pPager){ int rc; PgHdr *pPg; if( pPager->errMask==PAGER_ERR_FULL ){ - rc = sqlitepager_rollback(pPager); + rc = sqlite3pager_rollback(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } @@ -2029,7 +2029,7 @@ int sqlitepager_commit(Pager *pPager){ /* Jump here if anything goes wrong during the commit process. */ commit_abort: - rc = sqlitepager_rollback(pPager); + rc = sqlite3pager_rollback(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } @@ -2048,7 +2048,7 @@ commit_abort: ** codes are returned for all these occasions. Otherwise, ** SQLITE_OK is returned. */ -int sqlitepager_rollback(Pager *pPager){ +int sqlite3pager_rollback(Pager *pPager){ int rc; TRACE1("ROLLBACK\n"); if( !pPager->dirtyFile || !pPager->journalOpen ){ @@ -2079,14 +2079,14 @@ int sqlitepager_rollback(Pager *pPager){ ** Return TRUE if the database file is opened read-only. Return FALSE ** if the database is (in theory) writable. */ -int sqlitepager_isreadonly(Pager *pPager){ +int sqlite3pager_isreadonly(Pager *pPager){ return pPager->readOnly; } /* ** This routine is used for testing and analysis only. */ -int *sqlitepager_stats(Pager *pPager){ +int *sqlite3pager_stats(Pager *pPager){ static int a[9]; a[0] = pPager->nRef; a[1] = pPager->nPage; @@ -2107,7 +2107,7 @@ int *sqlitepager_stats(Pager *pPager){ ** open. A new checkpoint journal is created that can be used to rollback ** changes of a single SQL command within a larger transaction. */ -int sqlitepager_ckpt_begin(Pager *pPager){ +int sqlite3pager_stmt_begin(Pager *pPager){ int rc; char zTemp[SQLITE_TEMPNAME_SIZE]; if( !pPager->journalOpen ){ @@ -2131,7 +2131,7 @@ int sqlitepager_ckpt_begin(Pager *pPager){ + JOURNAL_HDR_SZ(journal_format); pPager->ckptSize = pPager->dbSize; if( !pPager->ckptOpen ){ - rc = sqlitepager_opentemp(zTemp, &pPager->cpfd); + rc = sqlite3pager_opentemp(zTemp, &pPager->cpfd); if( rc ) goto ckpt_begin_failed; pPager->ckptOpen = 1; pPager->ckptNRec = 0; @@ -2150,7 +2150,7 @@ ckpt_begin_failed: /* ** Commit a checkpoint. */ -int sqlitepager_ckpt_commit(Pager *pPager){ +int sqlite3pager_stmt_commit(Pager *pPager){ if( pPager->ckptInUse ){ PgHdr *pPg, *pNext; sqliteOsSeek(&pPager->cpfd, 0); @@ -2174,11 +2174,11 @@ int sqlitepager_ckpt_commit(Pager *pPager){ /* ** Rollback a checkpoint. */ -int sqlitepager_ckpt_rollback(Pager *pPager){ +int sqlite3pager_stmt_rollback(Pager *pPager){ int rc; if( pPager->ckptInUse ){ - rc = pager_ckpt_playback(pPager); - sqlitepager_ckpt_commit(pPager); + rc = pager_stmt_playback(pPager); + sqlite3pager_stmt_commit(pPager); }else{ rc = SQLITE_OK; } @@ -2189,14 +2189,14 @@ int sqlitepager_ckpt_rollback(Pager *pPager){ /* ** Return the full pathname of the database file. */ -const char *sqlitepager_filename(Pager *pPager){ +const char *sqlite3pager_filename(Pager *pPager){ return pPager->zFilename; } /* ** Set the codec for this pager */ -void sqlitepager_set_codec( +void sqlite3pager_set_codec( Pager *pPager, void (*xCodec)(void*,void*,Pgno,int), void *pCodecArg @@ -2209,7 +2209,7 @@ void sqlitepager_set_codec( /* ** Print a listing of all referenced pages and their ref count. */ -void sqlitepager_refdump(Pager *pPager){ +void sqlite3pager_refdump(Pager *pPager){ PgHdr *pPg; for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ if( pPg->nRef<=0 ) continue; diff --git a/src/pager.h b/src/pager.h index 09bc7aede..e506e0426 100644 --- a/src/pager.h +++ b/src/pager.h @@ -13,7 +13,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.26 2004/02/11 02:18:07 drh Exp $ +** @(#) $Id: pager.h,v 1.27 2004/04/26 14:10:22 drh Exp $ */ /* @@ -50,8 +50,7 @@ #define SQLITE_USABLE_SIZE (SQLITE_PAGE_SIZE-SQLITE_PAGE_RESERVE) /* -** Maximum number of pages in one database. (This is a limitation of -** imposed by 4GB files size limits.) +** Maximum number of pages in one database. */ #define SQLITE_MAX_PAGE 1073741823 @@ -70,38 +69,37 @@ typedef struct Pager Pager; ** See source code comments for a detailed description of the following ** routines: */ -int sqlitepager_open(Pager **ppPager, const char *zFilename, +int sqlite3pager_open(Pager **ppPager, const char *zFilename, int nPage, int nExtra, int useJournal); -void sqlitepager_set_destructor(Pager*, void(*)(void*)); -void sqlitepager_set_cachesize(Pager*, int); -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*); -int sqlitepager_iswriteable(void*); -int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*); -int sqlitepager_pagecount(Pager*); -int sqlitepager_truncate(Pager*,Pgno); -int sqlitepager_begin(void*); -int sqlitepager_commit(Pager*); -int sqlitepager_rollback(Pager*); -int sqlitepager_isreadonly(Pager*); -int sqlitepager_ckpt_begin(Pager*); -int sqlitepager_ckpt_commit(Pager*); -int sqlitepager_ckpt_rollback(Pager*); -void sqlitepager_dont_rollback(void*); -void sqlitepager_dont_write(Pager*, Pgno); -int *sqlitepager_stats(Pager*); -void sqlitepager_set_safety_level(Pager*,int); -const char *sqlitepager_filename(Pager*); -int sqlitepager_rename(Pager*, const char *zNewName); -void sqlitepager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*); +void sqlite3pager_set_destructor(Pager*, void(*)(void*)); +void sqlite3pager_set_cachesize(Pager*, int); +int sqlite3pager_close(Pager *pPager); +int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage); +void *sqlite3pager_lookup(Pager *pPager, Pgno pgno); +int sqlite3pager_ref(void*); +int sqlite3pager_unref(void*); +Pgno sqlite3pager_pagenumber(void*); +int sqlite3pager_write(void*); +int sqlite3pager_iswriteable(void*); +int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void*); +int sqlite3pager_pagecount(Pager*); +int sqlite3pager_truncate(Pager*,Pgno); +int sqlite3pager_begin(void*); +int sqlite3pager_commit(Pager*); +int sqlite3pager_rollback(Pager*); +int sqlite3pager_isreadonly(Pager*); +int sqlite3pager_stmt_begin(Pager*); +int sqlite3pager_stmt_commit(Pager*); +int sqlite3pager_stmt_rollback(Pager*); +void sqlite3pager_dont_rollback(void*); +void sqlite3pager_dont_write(Pager*, Pgno); +int *sqlite3pager_stats(Pager*); +void sqlite3pager_set_safety_level(Pager*,int); +const char *sqlite3pager_filename(Pager*); +int sqlite3pager_rename(Pager*, const char *zNewName); +void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*); #ifdef SQLITE_TEST -void sqlitepager_refdump(Pager*); -int pager_refinfo_enable; -int journal_format; +void sqlite3pager_refdump(Pager*); +int pager3_refinfo_enable; #endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4c2b64340..c3e3de6c0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,13 +11,12 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.220 2004/02/25 13:47:33 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.221 2004/04/26 14:10:22 drh Exp $ */ #include "config.h" #include "sqlite.h" #include "hash.h" #include "parse.h" -#include "btree.h" #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -96,6 +95,9 @@ ** ** cc '-DUINTPTR_TYPE=long long int' ... */ +#ifndef UINT64_TYPE +# define UINT64_TYPE unsigned long long int +#endif #ifndef UINT32_TYPE # define UINT32_TYPE unsigned int #endif @@ -115,6 +117,7 @@ # define INTPTR_TYPE long long # endif #endif +typedef UINT64_TYPE u64; /* 8-byte unsigned integer */ typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ @@ -126,6 +129,7 @@ typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */ ** Defer sourcing vdbe.h until after the "u8" typedef is defined. */ #include "vdbe.h" +#include "btree.h" /* ** Most C compilers these days recognize "long double", don't they? diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 4e3976478..e1ca48373 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.59 2004/02/25 22:51:06 rdc Exp $ +** $Id: tclsqlite.c,v 1.60 2004/04/26 14:10:22 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -21,6 +21,7 @@ #include <string.h> #include <assert.h> +#if 0 /* ** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we ** have to do a translation when going between the two. Set the @@ -1155,6 +1156,7 @@ int Sqlite_SafeInit(Tcl_Interp *interp){ int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } +#endif #if 0 /* @@ -1199,7 +1201,7 @@ int TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; Tcl_FindExecutable(argv[0]); interp = Tcl_CreateInterp(); - Sqlite_Init(interp); + /* Sqlite_Init(interp); */ #ifdef SQLITE_TEST { extern int Sqlitetest1_Init(Tcl_Interp*); @@ -1207,10 +1209,10 @@ int TCLSH_MAIN(int argc, char **argv){ extern int Sqlitetest3_Init(Tcl_Interp*); extern int Sqlitetest4_Init(Tcl_Interp*); extern int Md5_Init(Tcl_Interp*); - Sqlitetest1_Init(interp); + /* Sqlitetest1_Init(interp); */ Sqlitetest2_Init(interp); - Sqlitetest3_Init(interp); - Sqlitetest4_Init(interp); + /* Sqlitetest3_Init(interp); */ + /* Sqlitetest4_Init(interp); */ Md5_Init(interp); } #endif diff --git a/src/test2.c b/src/test2.c index 3fb198bf9..e7f6024d9 100644 --- a/src/test2.c +++ b/src/test2.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test2.c,v 1.16 2004/02/10 01:54:28 drh Exp $ +** $Id: test2.c,v 1.17 2004/04/26 14:10:22 drh Exp $ */ #include "os.h" #include "sqliteInt.h" @@ -76,7 +76,7 @@ static int pager_open( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR; - rc = sqlitepager_open(&pPager, argv[1], nPage, 0, 1); + rc = sqlite3pager_open(&pPager, argv[1], nPage, 0, 1); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -105,7 +105,7 @@ static int pager_close( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - rc = sqlitepager_close(pPager); + rc = sqlite3pager_close(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -132,7 +132,7 @@ static int pager_rollback( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - rc = sqlitepager_rollback(pPager); + rc = sqlite3pager_rollback(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -159,7 +159,7 @@ static int pager_commit( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - rc = sqlitepager_commit(pPager); + rc = sqlite3pager_commit(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -168,11 +168,11 @@ static int pager_commit( } /* -** Usage: pager_ckpt_begin ID +** Usage: pager_stmt_begin ID ** ** Start a new checkpoint. */ -static int pager_ckpt_begin( +static int pager_stmt_begin( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -186,7 +186,7 @@ static int pager_ckpt_begin( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - rc = sqlitepager_ckpt_begin(pPager); + rc = sqlite3pager_stmt_begin(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -195,11 +195,11 @@ static int pager_ckpt_begin( } /* -** Usage: pager_ckpt_rollback ID +** Usage: pager_stmt_rollback ID ** ** Rollback changes to a checkpoint */ -static int pager_ckpt_rollback( +static int pager_stmt_rollback( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -213,7 +213,7 @@ static int pager_ckpt_rollback( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - rc = sqlitepager_ckpt_rollback(pPager); + rc = sqlite3pager_stmt_rollback(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -222,11 +222,11 @@ static int pager_ckpt_rollback( } /* -** Usage: pager_ckpt_commit ID +** Usage: pager_stmt_commit ID ** ** Commit changes to a checkpoint */ -static int pager_ckpt_commit( +static int pager_stmt_commit( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -240,7 +240,7 @@ static int pager_ckpt_commit( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - rc = sqlitepager_ckpt_commit(pPager); + rc = sqlite3pager_stmt_commit(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -267,7 +267,7 @@ static int pager_stats( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - a = sqlitepager_stats(pPager); + a = sqlite3pager_stats(pPager); for(i=0; i<9; i++){ static char *zName[] = { "ref", "page", "max", "size", "state", "err", @@ -300,7 +300,7 @@ static int pager_pagecount( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; - sprintf(zBuf,"%d",sqlitepager_pagecount(pPager)); + sprintf(zBuf,"%d",sqlite3pager_pagecount(pPager)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } @@ -328,7 +328,7 @@ static int page_get( } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; - rc = sqlitepager_get(pPager, pgno, &pPage); + rc = sqlite3pager_get(pPager, pgno, &pPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -361,7 +361,7 @@ static int page_lookup( } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; - pPage = sqlitepager_lookup(pPager, pgno); + pPage = sqlite3pager_lookup(pPager, pgno); if( pPage ){ sprintf(zBuf,"0x%x",(int)pPage); Tcl_AppendResult(interp, zBuf, 0); @@ -388,7 +388,7 @@ static int page_unref( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; - rc = sqlitepager_unref(pPage); + rc = sqlite3pager_unref(pPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -439,7 +439,7 @@ static int page_number( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; - sprintf(zBuf, "%d", sqlitepager_pagenumber(pPage)); + sprintf(zBuf, "%d", sqlite3pager_pagenumber(pPage)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } @@ -463,7 +463,7 @@ static int page_write( return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; - rc = sqlitepager_write(pPage); + rc = sqlite3pager_write(pPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; @@ -534,9 +534,9 @@ int Sqlitetest2_Init(Tcl_Interp *interp){ { "pager_close", (Tcl_CmdProc*)pager_close }, { "pager_commit", (Tcl_CmdProc*)pager_commit }, { "pager_rollback", (Tcl_CmdProc*)pager_rollback }, - { "pager_ckpt_begin", (Tcl_CmdProc*)pager_ckpt_begin }, - { "pager_ckpt_commit", (Tcl_CmdProc*)pager_ckpt_commit }, - { "pager_ckpt_rollback", (Tcl_CmdProc*)pager_ckpt_rollback }, + { "pager_stmt_begin", (Tcl_CmdProc*)pager_stmt_begin }, + { "pager_stmt_commit", (Tcl_CmdProc*)pager_stmt_commit }, + { "pager_stmt_rollback", (Tcl_CmdProc*)pager_stmt_rollback }, { "pager_stats", (Tcl_CmdProc*)pager_stats }, { "pager_pagecount", (Tcl_CmdProc*)pager_pagecount }, { "page_get", (Tcl_CmdProc*)page_get }, @@ -554,8 +554,11 @@ int Sqlitetest2_Init(Tcl_Interp *interp){ Tcl_LinkVar(interp, "sqlite_io_error_pending", (char*)&sqlite_io_error_pending, TCL_LINK_INT); #ifdef SQLITE_TEST - Tcl_LinkVar(interp, "journal_format", - (char*)&journal_format, TCL_LINK_INT); + { + extern int journal_format; + Tcl_LinkVar(interp, "journal_format", + (char*)&journal_format, TCL_LINK_INT); + } #endif sprintf(zBuf, "%d", SQLITE_PAGE_SIZE); Tcl_SetVar(interp, "SQLITE_PAGE_SIZE", zBuf, TCL_GLOBAL_ONLY); diff --git a/src/util.c b/src/util.c index 16b3b46a0..1f1d5462b 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.74 2004/02/22 17:49:34 drh Exp $ +** $Id: util.c,v 1.75 2004/04/26 14:10:22 drh Exp $ */ #include "sqliteInt.h" #include <stdarg.h> @@ -405,6 +405,7 @@ void sqliteSetNString(char **pz, ...){ va_end(ap); } +#if 0 /* ** Add an error message to pParse->zErrMsg and increment pParse->nErr. ** The following formatting characters are allowed: @@ -423,6 +424,7 @@ void sqliteErrorMsg(Parse *pParse, const char *zFormat, ...){ pParse->zErrMsg = sqliteVMPrintf(zFormat, ap); va_end(ap); } +#endif /* ** Convert an SQL-style quoted string into a normal string by removing diff --git a/test/pager.test b/test/pager.test index b76c48eed..81381245a 100644 --- a/test/pager.test +++ b/test/pager.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is page cache subsystem. # -# $Id: pager.test,v 1.14 2004/02/25 02:20:42 drh Exp $ +# $Id: pager.test,v 1.15 2004/04/26 14:10:22 drh Exp $ set testdir [file dirname $argv0] @@ -305,7 +305,7 @@ for {set i 1} {$i<20} {incr i} { page_write $gx "Page-$j v$i" page_unref $gx if {$j==$i} { - pager_ckpt_begin $p1 + pager_stmt_begin $p1 } } } {} @@ -346,12 +346,12 @@ for {set i 1} {$i<20} {incr i} { page_write $gx "Page-$j v$i" page_unref $gx if {$j==$i} { - pager_ckpt_begin $p1 + pager_stmt_begin $p1 } } } {} do_test pager-4.5.$i.7 { - pager_ckpt_rollback $p1 + pager_stmt_rollback $p1 for {set j 2} {$j<=20} {incr j} { set gx [page_get $p1 $j] set value [page_read $gx] @@ -373,12 +373,12 @@ for {set i 1} {$i<20} {incr i} { page_write $gx "Page-$j v$i" page_unref $gx if {$j==$i} { - pager_ckpt_begin $p1 + pager_stmt_begin $p1 } } } {} do_test pager-4.5.$i.9 { - pager_ckpt_commit $p1 + pager_stmt_commit $p1 for {set j 2} {$j<=20} {incr j} { set gx [page_get $p1 $j] set value [page_read $gx] @@ -406,6 +406,7 @@ do_test pager-4.99 { } ;# end if( not mem: and has pager_open command ); +if 0 { # Ticket #615: an assertion fault inside the pager. It is a benign # fault, but we might as well test for it. # @@ -418,6 +419,6 @@ do_test pager-5.1 { COMMIT; } } {} - +} finish_test diff --git a/test/tester.tcl b/test/tester.tcl index d46dbb989..30603f47c 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -11,8 +11,9 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.28 2004/02/14 01:39:50 drh Exp $ +# $Id: tester.tcl,v 1.29 2004/04/26 14:10:22 drh Exp $ +if 0 { # Make sure tclsqlite was compiled correctly. Abort now with an # error message if not. # @@ -62,6 +63,8 @@ sqlite db ./test.db if {[info exists ::SETUP_SQL]} { db eval $::SETUP_SQL } +} +proc db {args} {} # Abort early if this script has been run before. # @@ -180,10 +183,12 @@ proc finalize_testing {} { puts "$nProb probabilistic tests also failed, but this does" puts "not necessarily indicate a malfunction." } + if 0 { if {$sqlite_open_file_count} { puts "$sqlite_open_file_count files were left open" incr nErr } + } exit [expr {$nErr>0}] } |