diff --git a/Makefile.in b/Makefile.in index 9c6a497b90..bcccd3d211 100644 --- a/Makefile.in +++ b/Makefile.in @@ -54,7 +54,6 @@ LIBOBJ = btree.o build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \ # All of the source code files. # SRC = \ - $(TOP)/src/btree.c \ $(TOP)/src/build.c \ $(TOP)/src/dbbe.c \ $(TOP)/src/dbbe.h \ @@ -64,7 +63,6 @@ SRC = \ $(TOP)/src/expr.c \ $(TOP)/src/insert.c \ $(TOP)/src/main.c \ - $(TOP)/src/pager.c \ $(TOP)/src/parse.y \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ @@ -209,7 +207,7 @@ tclsqlite: $(TOP)/src/tclsqlite.c libsqlite.a testfixture: $(TOP)/src/tclsqlite.c libsqlite.a $(TESTSRC) $(TCC) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o testfixture \ $(TESTSRC) $(TOP)/src/tclsqlite.c $(TOP)/src/btree.c \ - libsqlite.a $(LIBGDBM) $(LIBTCL) + $(TOP)/src/pager.c libsqlite.a $(LIBGDBM) $(LIBTCL) test: testfixture sqlite ./testfixture $(TOP)/test/all.test diff --git a/manifest b/manifest index 5297a725c3..8e99792825 100644 --- a/manifest +++ b/manifest @@ -1,7 +1,7 @@ -C More\stests\sand\sbug\sfixes\sin\sbtree.c\s(CVS\s229) -D 2001-06-25T02:11:07 +C Got\sa\slot\sof\sBTree\stests\sworking.\sStill\slots\smore\sneeded.\s(CVS\s230) +D 2001-06-28T01:54:48 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 -F Makefile.in 65862a30703b070209b5f5e565d75cc870962b3c +F Makefile.in 63bc9a6a39b7160ce8d2392ae74eb4ca4ca84c6e F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 F VERSION 71874cb7e2a53c2bd22bb6affa7d223dd94a7a13 F configure d2051345f49f7e48604423da26e086a745c86a47 x @@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464 F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e F notes/notes3.txt 985bf688b59f1f52bfe6e4b1f896efdeffac1432 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4 -F src/btree.c 977621ad8c0607c13e4f404284ae1fb28adf542f -F src/btree.h 2ce445f0b733e0d077377cbb49c12316e7ce524d +F src/btree.c d55ba210df7625c1edd62a4631bb6d322d9b68ca +F src/btree.h d327e9ad671d41d41aa2dd376c9230c8d2167c8e F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651 F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8 @@ -31,8 +31,8 @@ F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7 F src/expr.c c4c24c3af1eba094a816522eb0e085bed518ee16 F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7 F src/main.c 0a13c7a2beb8ce36aee43daf8c95989b200727a7 -F src/pager.c 866d4d9a736943c9a904d291bc9b66dc4a7f23de -F src/pager.h 724ac5a79b5fa704a1e1a87e421e421b3da9c1e4 +F src/pager.c 3e864a3e6cdec6f000a343f793360b42714028d8 +F src/pager.h d85259a2fd59e39f976abfb2bf6703c6f810e993 F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9 F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a @@ -45,7 +45,7 @@ F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6 F src/tclsqlite.c af29a45cb4c2244a6fd032568a22d26516472b2c F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4 F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a -F src/test3.c a66bb93c540d53d1026b0d183faca928d6c82ba0 +F src/test3.c b55fd9d2af55b29e0a906851bb09de3006a28629 F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6 F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc @@ -53,7 +53,7 @@ F src/vdbe.c f93be4414ba892df9c5589815d2a57c1fb12c820 F src/vdbe.h dc1205da434c6a9da03b5d6b089270bbc8e6d437 F src/where.c 0c542fc44bd85152dfb8507862cfe2e60c629e9f F test/all.test 21d55a97e39e7ec5776751dc9dd8b1b51ef4a048 -F test/btree.test 9207999792e0a784821fdfa1287311e3f22ff4b0 +F test/btree.test dc07031aaa753fb230b0d30166b5f00e467afa49 F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf @@ -108,7 +108,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 85f015c9750a5eab274e82f0e2c6e8f09dc7ca70 -R 3996a4b0538db0f013a966fe30254459 +P 6b9b298b2846146b95d7df7f423867976bafa390 +R 352f1f1690fcb389985c90a80a4e8ed3 U drh -Z 373fed59edcfc36190ad26edeac1578b +Z 2f00c60c42da64c07beeccb2fde6cfaa diff --git a/manifest.uuid b/manifest.uuid index eb975e72a0..e0131c98ab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6b9b298b2846146b95d7df7f423867976bafa390 \ No newline at end of file +9cfeeb5896d2a17c8c7904136d346a6245c9e497 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index d1761c5fba..7dbac878a6 100644 --- a/src/btree.c +++ b/src/btree.c @@ -21,7 +21,7 @@ ** http://www.hwaci.com/drh/ ** ************************************************************************* -** $Id: btree.c,v 1.15 2001/06/25 02:11:07 drh Exp $ +** $Id: btree.c,v 1.16 2001/06/28 01:54:48 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -896,6 +896,8 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){ offset = 0; zBuf += a; amt -= a; + }else{ + offset -= MX_LOCAL_PAYLOAD; } if( amt>0 ){ nextPage = pCur->pPage->apCell[pCur->idx]->ovfl; @@ -1287,14 +1289,14 @@ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){ ** Add a page of the database file to the freelist. Either pgno or ** pPage but not both may be 0. ** -** sqlitepager_unref() is NOT called for pPage. The calling routine -** needs to do that. +** 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 needOvflUnref = 0; + int needUnref = 0; + MemPage *pMemPage; if( pgno==0 ){ assert( pOvfl!=0 ); @@ -1309,20 +1311,24 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){ assert( pgno>0 ); rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pOvfl); if( rc ) return rc; - needOvflUnref = 1; + needUnref = 1; } rc = sqlitepager_write(pOvfl); if( rc ){ - if( needOvflUnref ) sqlitepager_unref(pOvfl); + if( needUnref ) sqlitepager_unref(pOvfl); return rc; } pOvfl->iNext = pPage1->freeList; pPage1->freeList = pgno; pPage1->nFree++; memset(pOvfl->aPayload, 0, OVERFLOW_SIZE); - ((MemPage*)pPage)->isInit = 0; - assert( ((MemPage*)pPage)->pParent==0 ); - rc = sqlitepager_unref(pOvfl); + pMemPage = (MemPage*)pPage; + pMemPage->isInit = 0; + if( pMemPage->pParent ){ + sqlitepager_unref(pMemPage->pParent); + pMemPage->pParent = 0; + } + if( needUnref ) rc = sqlitepager_unref(pOvfl); return rc; } @@ -1347,6 +1353,7 @@ static int clearCell(Btree *pBt, Cell *pCell){ nextOvfl = pOvfl->iNext; rc = freePage(pBt, pOvfl, ovfl); if( rc ) return rc; + sqlitepager_unref(pOvfl); ovfl = nextOvfl; } return SQLITE_OK; @@ -1362,7 +1369,7 @@ static int fillInCell( void *pKey, int nKey, /* The key */ void *pData,int nData /* The data */ ){ - OverflowPage *pOvfl; + OverflowPage *pOvfl, *pPrior; Pgno *pNext; int spaceLeft; int n, rc; @@ -1381,14 +1388,19 @@ static int fillInCell( pPayload = pKey; pKey = 0; nPayload = nKey; + pPrior = 0; while( nPayload>0 ){ if( spaceLeft==0 ){ rc = allocatePage(pBt, (MemPage**)&pOvfl, pNext); if( rc ){ *pNext = 0; + } + if( pPrior ) sqlitepager_unref(pPrior); + if( rc ){ clearCell(pBt, pCell); return rc; } + pPrior = pOvfl; spaceLeft = OVERFLOW_SIZE; pSpace = pOvfl->aPayload; pNext = &pOvfl->iNext; @@ -1407,6 +1419,10 @@ static int fillInCell( spaceLeft -= n; pSpace += n; } + *pNext = 0; + if( pPrior ){ + sqlitepager_unref(pPrior); + } return SQLITE_OK; } @@ -1418,12 +1434,16 @@ static int fillInCell( static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent){ MemPage *pThis; - assert( pPager!=0 && pgno!=0 ); + if( pgno==0 ) return; + assert( pPager!=0 ); pThis = sqlitepager_lookup(pPager, pgno); - if( pThis && pThis->pParent!=pNewParent ){ - if( pThis->pParent ) sqlitepager_unref(pThis->pParent); - pThis->pParent = pNewParent; - if( pNewParent ) sqlitepager_ref(pNewParent); + if( pThis ){ + if( pThis->pParent!=pNewParent ){ + if( pThis->pParent ) sqlitepager_unref(pThis->pParent); + pThis->pParent = pNewParent; + if( pNewParent ) sqlitepager_ref(pNewParent); + } + sqlitepager_unref(pThis); } } @@ -1520,14 +1540,14 @@ static void relinkCellList(MemPage *pPage){ /* ** Make a copy of the contents of pFrom into pTo. The pFrom->apCell[] ** pointers that point intto pFrom->u.aDisk[] must be adjusted to point -** intto pTo->u.aDisk[] instead. But some pFrom->apCell[] entries might +** into pTo->u.aDisk[] instead. But some pFrom->apCell[] entries might ** not point to pFrom->u.aDisk[]. Those are unchanged. */ static void copyPage(MemPage *pTo, MemPage *pFrom){ uptr from, to; int i; memcpy(pTo->u.aDisk, pFrom->u.aDisk, SQLITE_PAGE_SIZE); - pTo->pParent = pFrom->pParent; + pTo->pParent = 0; pTo->isInit = 1; pTo->nCell = pFrom->nCell; pTo->nFree = pFrom->nFree; @@ -1538,6 +1558,8 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){ uptr x = Addr(pFrom->apCell[i]); if( x>from && xapCell[i]) = x + to - from; + }else{ + pTo->apCell[i] = pFrom->apCell[i]; } } } @@ -1666,9 +1688,9 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ if( rc ) return rc; copyPage(pChild, pPage); pChild->pParent = pPage; + sqlitepager_ref(pPage); pChild->isOverfull = 1; if( pCur ){ - sqlitepager_ref(pChild); sqlitepager_unref(pCur->pPage); pCur->pPage = pChild; } @@ -1694,8 +1716,8 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ break; } } - if( idx<0 && pPage->u.hdr.rightChild==pgno ){ - idx = pPage->nCell; + if( idx<0 && pParent->u.hdr.rightChild==pgno ){ + idx = pParent->nCell; } if( idx<0 ){ return SQLITE_CORRUPT; @@ -1762,6 +1784,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ copyPage(&aOld[i], apOld[i]); rc = freePage(pBt, apOld[i], pgnoOld[i]); if( rc ) goto balance_cleanup; + sqlitepager_unref(apOld[i]); apOld[i] = &aOld[i]; } @@ -1821,7 +1844,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ j = 0; for(i=0; inFreenFree ){ + while( jnFree>freePerPage && szCell[j]<=pNew->nFree ){ if( pCur && iCur==j ){ pCur->pPage = pNew; pCur->idx = pNew->nCell; } insertCell(pNew, pNew->nCell, apCell[j], szCell[j]); j++; @@ -1866,7 +1889,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ */ balance_cleanup: for(i=0; ipPager; +} +#endif diff --git a/src/btree.h b/src/btree.h index 17f7400938..5229b98891 100644 --- a/src/btree.h +++ b/src/btree.h @@ -24,7 +24,7 @@ ** This header file defines the interface that the sqlite B-Tree file ** subsystem. ** -** @(#) $Id: btree.h,v 1.6 2001/06/25 02:11:07 drh Exp $ +** @(#) $Id: btree.h,v 1.7 2001/06/28 01:54:49 drh Exp $ */ typedef struct Btree Btree; @@ -60,4 +60,5 @@ int sqliteBtreeUpdateMeta(Btree*, int*); #ifdef SQLITE_TEST int sqliteBtreePageDump(Btree*, int); int sqliteBtreeCursorDump(BtCursor*, int*); +Pager *sqliteBtreePager(Btree*); #endif diff --git a/src/pager.c b/src/pager.c index 4e759ac802..ebd641f342 100644 --- a/src/pager.c +++ b/src/pager.c @@ -27,7 +27,7 @@ ** all writes in order to support rollback. Locking is used to limit ** access to one or more reader or one writer. ** -** @(#) $Id: pager.c,v 1.11 2001/06/24 20:39:41 drh Exp $ +** @(#) $Id: pager.c,v 1.12 2001/06/28 01:54:49 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -158,6 +158,25 @@ static const unsigned char aJournalMagic[] = { */ #define pager_hash(PN) ((PN)%N_PG_HASH) +/* +** Enable reference count tracking here: +*/ +#if SQLITE_TEST +int pager_refinfo_enable = 0; + static void pager_refinfo(PgHdr *p){ + static int cnt = 0; + if( !pager_refinfo_enable ) return; + printf( + "REFCNT: %4d addr=0x%08x nRef=%d\n", + p->pgno, (int)PGHDR_TO_DATA(p), p->nRef + ); + cnt++; /* Something to set a breakpoint on */ + } +# define REFINFO(X) pager_refinfo(X) +#else +# define REFINFO(X) +#endif + /* ** Attempt to acquire a read lock (if wrlock==0) or a write lock (if wrlock==1) ** on the database file. Return 0 on success and non-zero if the lock @@ -579,6 +598,7 @@ static void page_ref(PgHdr *pPg){ pPg->pPager->nRef++; } pPg->nRef++; + REFINFO(pPg); } /* @@ -754,6 +774,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ pPg->inJournal = 0; pPg->dirty = 0; pPg->nRef = 1; + REFINFO(pPg); pPager->nRef++; h = pager_hash(pgno); pPg->pNextHash = pPager->aHash[h]; @@ -830,6 +851,7 @@ int sqlitepager_unref(void *pData){ assert( pPg->nRef>0 ); pPager = pPg->pPager; pPg->nRef--; + REFINFO(pPg); /* When the number of references to a page reach 0, call the ** destructor and add the page to the freelist. @@ -1034,3 +1056,17 @@ int *sqlitepager_stats(Pager *pPager){ a[8] = pPager->nOvfl; return a; } + +#if SQLITE_TEST +/* +** Print a listing of all referenced pages and their ref count. +*/ +void sqlitepager_refdump(Pager *pPager){ + PgHdr *pPg; + for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + if( pPg->nRef<=0 ) continue; + printf("PAGE %3d addr=0x%08x nRef=%d\n", + pPg->pgno, (int)PGHDR_TO_DATA(pPg), pPg->nRef); + } +} +#endif diff --git a/src/pager.h b/src/pager.h index acb7735b8c..208d898c0a 100644 --- a/src/pager.h +++ b/src/pager.h @@ -25,7 +25,7 @@ ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** -** @(#) $Id: pager.h,v 1.5 2001/06/22 19:15:01 drh Exp $ +** @(#) $Id: pager.h,v 1.6 2001/06/28 01:54:49 drh Exp $ */ /* @@ -57,3 +57,8 @@ int sqlitepager_pagecount(Pager*); int sqlitepager_commit(Pager*); int sqlitepager_rollback(Pager*); int *sqlitepager_stats(Pager*); + +#ifdef SQLITE_TEST +void sqlitepager_refdump(Pager*); +int pager_refinfo_enable; +#endif diff --git a/src/test3.c b/src/test3.c index 02c965a740..e4ed48ac34 100644 --- a/src/test3.c +++ b/src/test3.c @@ -25,7 +25,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.3 2001/06/25 02:11:07 drh Exp $ +** $Id: test3.c,v 1.4 2001/06/28 01:54:49 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -355,6 +355,64 @@ static int btree_page_dump( return TCL_OK; } +/* +** Usage: btree_pager_stats ID +** +** Returns pager statistics +*/ +static int btree_pager_stats( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + Btree *pBt; + int i; + int *a; + + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR; + a = sqlitepager_stats(sqliteBtreePager(pBt)); + for(i=0; i<9; i++){ + static char *zName[] = { + "ref", "page", "max", "size", "state", "err", + "hit", "miss", "ovfl", + }; + char zBuf[100]; + Tcl_AppendElement(interp, zName[i]); + sprintf(zBuf,"%d",a[i]); + Tcl_AppendElement(interp, zBuf); + } + return TCL_OK; +} + +/* +** Usage: btree_pager_ref_dump ID +** +** Print out all outstanding pages. +*/ +static int btree_pager_ref_dump( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + Btree *pBt; + + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR; + sqlitepager_refdump(sqliteBtreePager(pBt)); + return TCL_OK; +} + /* ** Usage: btree_cursor ID TABLENUM ** @@ -669,6 +727,8 @@ int Sqlitetest3_Init(Tcl_Interp *interp){ Tcl_CreateCommand(interp, "btree_get_meta", btree_get_meta, 0, 0); Tcl_CreateCommand(interp, "btree_update_meta", btree_update_meta, 0, 0); Tcl_CreateCommand(interp, "btree_page_dump", btree_page_dump, 0, 0); + Tcl_CreateCommand(interp, "btree_pager_stats", btree_pager_stats, 0, 0); + Tcl_CreateCommand(interp, "btree_pager_ref_dump", btree_pager_ref_dump, 0, 0); Tcl_CreateCommand(interp, "btree_cursor", btree_cursor, 0, 0); Tcl_CreateCommand(interp, "btree_close_cursor", btree_close_cursor, 0, 0); Tcl_CreateCommand(interp, "btree_move_to", btree_move_to, 0, 0); @@ -678,5 +738,7 @@ int Sqlitetest3_Init(Tcl_Interp *interp){ Tcl_CreateCommand(interp, "btree_key", btree_key, 0, 0); Tcl_CreateCommand(interp, "btree_data", btree_data, 0, 0); Tcl_CreateCommand(interp, "btree_cursor_dump", btree_cursor_dump, 0, 0); + Tcl_LinkVar(interp, "pager_refinfo_enable", (char*)&pager_refinfo_enable, + TCL_LINK_INT); return TCL_OK; } diff --git a/test/btree.test b/test/btree.test index 2167d4a71b..545671b50b 100644 --- a/test/btree.test +++ b/test/btree.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is btree database backend # -# $Id: btree.test,v 1.2 2001/06/25 02:11:07 drh Exp $ +# $Id: btree.test,v 1.3 2001/06/28 01:54:50 drh Exp $ set testdir [file dirname $argv0] @@ -38,6 +38,16 @@ do_test btree-1.1 { file delete -force test1.bt-journal set rc [catch {btree_open test1.bt} ::b1] } {0} + +# The second element of the list returned by btree_pager_stats is the +# number of pages currently checked out. We'll be checking this value +# frequently during this test script, to make sure the btree library +# is properly releasing the pages it checks out, and thus avoiding +# page leaks. +# +do_test btree-1.1.1 { + lindex [btree_pager_stats $::b1] 1 +} {0} do_test btree-1.2 { set rc [catch {btree_open test1.bt} ::b2] } {0} @@ -52,6 +62,9 @@ do_test btree-1.4 { set rc [catch {btree_begin_transaction $::b1} msg] lappend rc $msg } {0 {}} +do_test btree-1.4.1 { + lindex [btree_pager_stats $::b1] 1 +} {1} do_test btree-1.5 { set rc [catch {btree_cursor $::b1 2} ::c1] if {$rc} {lappend rc $::c1} @@ -78,6 +91,9 @@ do_test btree-1.10 { do_test btree-1.11 { file size test1.bt } {2048} +do_test btree-1.12 { + lindex [btree_pager_stats $::b1] 1 +} {0} # Reopen the database and attempt to read the record that we wrote. # @@ -101,6 +117,9 @@ do_test btree-2.5 { do_test btree-2.6 { btree_data $::c1 } {1.00} +do_test btree-2.7 { + lindex [btree_pager_stats $::b1] 1 +} {2} # Do some additional inserts # @@ -109,6 +128,9 @@ do_test btree-3.1 { btree_insert $::c1 two 2.00 btree_key $::c1 } {two} +do_test btree-3.1.1 { + lindex [btree_pager_stats $::b1] 1 +} {2} do_test btree-3.2 { btree_insert $::c1 three 3.00 btree_key $::c1 @@ -185,10 +207,16 @@ do_test btree-3.22 { set rc [catch {btree_close_cursor $::c1} msg] lappend rc $msg } {0 {}} +do_test btree-3.22.1 { + lindex [btree_pager_stats $::b1] 1 +} {1} do_test btree-3.23 { set rc [catch {btree_commit $::b1} msg] lappend rc $msg } {0 {}} +do_test btree-3.23.1 { + lindex [btree_pager_stats $::b1] 1 +} {0} do_test btree-3.24 { file size test1.bt } {2048} @@ -197,6 +225,9 @@ do_test btree-3.25 { if {$rc} {lappend rc $::c1} set rc } {0} +do_test btree-3.25.1 { + lindex [btree_pager_stats $::b1] 1 +} {2} do_test btree-3.26 { set rc [btree_move_to $::c1 {}] expr {$rc>0} @@ -249,6 +280,10 @@ do_test btree-3.39 { do_test btree-3.40 { btree_data $::c1 } {} +do_test btree-3.41 { + lindex [btree_pager_stats $::b1] 1 +} {2} + # Now try a delete # @@ -257,6 +292,9 @@ do_test btree-4.1 { btree_move_to $::c1 one btree_key $::c1 } {one} +do_test btree-4.1.1 { + lindex [btree_pager_stats $::b1] 1 +} {2} do_test btree-4.2 { btree_delete $::c1 } {} @@ -304,10 +342,19 @@ do_test btree-4.5 { # the data again. # do_test btree-4.6 { + lindex [btree_pager_stats $::b1] 1 +} {2} +do_test btree-4.7 { btree_close_cursor $::c1 + lindex [btree_pager_stats $::b1] 1 +} {0} +do_test btree-4.8 { btree_close $::b1 set ::b1 [btree_open test1.bt] set ::c1 [btree_cursor $::b1 2] + lindex [btree_pager_stats $::b1] 1 +} {2} +do_test btree-4.9 { set r {} while 1 { set key [btree_key $::c1] @@ -360,6 +407,17 @@ proc select_all {cursor} { } return $r } +proc select_keys {cursor} { + set r {} + btree_move_to $cursor {} + while 1 { + set key [btree_key $cursor] + if {$key==""} break + lappend r $key + btree_next $cursor + } + return $r +} # Try to create a new table in the database file # @@ -372,13 +430,22 @@ do_test btree-6.2 { set ::t2 [btree_create_table $::b1] } {3} do_test btree-6.2.1 { + lindex [btree_pager_stats $::b1] 1 +} {1} +do_test btree-6.2.2 { set ::c2 [btree_cursor $::b1 $::t2] + lindex [btree_pager_stats $::b1] 1 +} {2} +do_test btree-6.2.3 { btree_insert $::c2 ten 10 btree_key $::c2 } {ten} do_test btree-6.3 { btree_commit $::b1 set ::c1 [btree_cursor $::b1 2] + lindex [btree_pager_stats $::b1] 1 +} {3} +do_test btree-6.3.1 { select_all $::c1 } {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00} #btree_page_dump $::b1 3 @@ -394,6 +461,9 @@ do_test btree-6.5 { do_test btree-6.6 { btree_close_cursor $::c2 } {} +do_test btree-6.6.1 { + lindex [btree_pager_stats $::b1] 1 +} {2} do_test btree-6.7 { btree_drop_table $::b1 $::t2 } {} @@ -408,6 +478,10 @@ do_test btree-6.8.1 { } {0} do_test btree-6.9 { set ::c2 [btree_cursor $::b1 $::t2] + lindex [btree_pager_stats $::b1] 1 +} {3} + +do_test btree-6.9.1 { btree_move_to $::c2 {} btree_key $::c2 } {} @@ -428,6 +502,10 @@ do_test btree-6.11 { do_test btree-6.12 { select_all $::c2 } {} +do_test btree-6.13 { + btree_close_cursor $::c2 + lindex [btree_pager_stats $::b1] 1 +} {2} # Check to see that pages defragment properly. To do this test we will # @@ -439,6 +517,8 @@ do_test btree-6.12 { do_test btree-7.1 { btree_begin_transaction $::b1 } {} +catch {unset key} +catch {unset data} do_test btree-7.2 { for {set i 0} {$i<36} {incr i} { set key [format %03d $i] @@ -514,8 +594,11 @@ do_test btree-7.14 { lrange [btree_cursor_dump $::c1] 4 5 } {652 2} #btree_page_dump $::b1 2 +do_test btree-7.15 { + lindex [btree_pager_stats $::b1] 1 +} {2} -# Check to see that both key and data on overflow pages work correctly. +# Check to see that data on overflow pages work correctly. # do_test btree-8.1 { set data "*** This is a very long key " @@ -524,6 +607,10 @@ do_test btree-8.1 { btree_insert $::c1 020 $data } {} #btree_page_dump $::b1 2 +do_test btree-8.1.1 { + lindex [btree_pager_stats $::b1] 1 +} {2} +#btree_pager_ref_dump $::b1 do_test btree-8.2 { string length [btree_data $::c1] } [string length $::data] @@ -567,11 +654,148 @@ do_test btree-8.10 { do_test btree-8.11 { lindex [btree_get_meta $::b1] 0 } [expr {int(([string length $::data]-238+1019)/1020)}] -puts [btree_get_meta $::b1] + +# Now check out keys on overflow pages. +# +do_test btree-8.12 { + set ::keyprefix "This is a long prefix to a key " + while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix} + btree_close_cursor $::c1 + btree_drop_table $::b1 2 + lindex [btree_get_meta $::b1] 0 +} {4} +do_test btree-8.12.1 { + set ::c1 [btree_cursor $::b1 2] + btree_insert $::c1 ${::keyprefix}1 1 + btree_data $::c1 +} {1} +do_test btree-8.13 { + btree_key $::c1 +} ${keyprefix}1 +do_test btree-8.14 { + btree_insert $::c1 ${::keyprefix}2 2 + btree_insert $::c1 ${::keyprefix}3 3 + btree_key $::c1 +} ${keyprefix}3 +do_test btree-8.15 { + btree_move_to $::c1 ${::keyprefix}2 + btree_data $::c1 +} {2} +do_test btree-8.16 { + btree_move_to $::c1 ${::keyprefix}1 + btree_data $::c1 +} {1} +do_test btree-8.17 { + btree_move_to $::c1 ${::keyprefix}3 + btree_data $::c1 +} {3} +do_test btree-8.18 { + lindex [btree_get_meta $::b1] 0 +} {1} +do_test btree-8.19 { + btree_move_to $::c1 ${::keyprefix}2 + btree_key $::c1 +} ${::keyprefix}2 +#btree_page_dump $::b1 2 +do_test btree-8.20 { + btree_delete $::c1 + btree_next $::c1 + btree_key $::c1 +} ${::keyprefix}3 +#btree_page_dump $::b1 2 +do_test btree-8.21 { + lindex [btree_get_meta $::b1] 0 +} {2} +do_test btree-8.22 { + lindex [btree_pager_stats $::b1] 1 +} {2} +do_test btree-8.23 { + btree_close_cursor $::c1 + btree_drop_table $::b1 2 + set ::c1 [btree_cursor $::b1 2] + lindex [btree_get_meta $::b1] 0 +} {4} +do_test btree-8.24 { + lindex [btree_pager_stats $::b1] 1 +} {2} + +# Check page splitting logic +# +do_test btree-9.1 { + for {set i 1} {$i<=19} {incr i} { + set key [format %03d $i] + set data "*** $key *** $key *** $key *** $key ***" + btree_insert $::c1 $key $data + } +} {} +#btree_page_dump $::b1 2 +#btree_pager_ref_dump $::b1 +#set pager_refinfo_enable 1 +do_test btree-9.2 { + btree_insert $::c1 020 {*** 020 *** 020 *** 020 *** 020 ***} + select_keys $::c1 +} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020} +#btree_page_dump $::b1 5 +#btree_page_dump $::b1 2 +#btree_page_dump $::b1 7 +#btree_pager_ref_dump $::b1 +#set pager_refinfo_enable 0 + +# The previous "select_keys" command left the cursor pointing at the root +# page. So there should only be two pages checked out. 2 (the root) and +# page 1. +do_test btree-9.2.1 { + lindex [btree_pager_stats $::b1] 1 +} {2} +for {set i 1} {$i<=20} {incr i} { + do_test btree-9.3.$i.1 [subst { + btree_move_to $::c1 [format %03d $i] + btree_key $::c1 + }] [format %03d $i] + do_test btree-9.3.$i.2 [subst { + btree_move_to $::c1 [format %03d $i] + string range \[btree_data $::c1\] 0 10 + }] "*** [format %03d $i] ***" +} +do_test btree-9.4.1 { + lindex [btree_pager_stats $::b1] 1 +} {3} + +# Check the page joining logic. +# +#btree_page_dump $::b1 2 +#btree_pager_ref_dump $::b1 +do_test btree-9.4.2 { + btree_move_to $::c1 005 + btree_delete $::c1 +} {} +#btree_page_dump $::b1 2 +for {set i 1} {$i<=19} {incr i} { + if {$i==5} continue + do_test btree-9.5.$i.1 [subst { + btree_move_to $::c1 [format %03d $i] + btree_key $::c1 + }] [format %03d $i] + do_test btree-9.5.$i.2 [subst { + btree_move_to $::c1 [format %03d $i] + string range \[btree_data $::c1\] 0 10 + }] "*** [format %03d $i] ***" +} +#btree_pager_ref_dump $::b1 +do_test btree-9.6 { + btree_close_cursor $::c1 + lindex [btree_pager_stats $::b1] 1 +} {1} +do_test btree-9.7 { + btree_rollback $::b1 + lindex [btree_pager_stats $::b1] 1 +} {0} do_test btree-99.1 { btree_close $::b1 } {} +catch {unset data} +catch {unset key} } ;# end if( not mem: and has pager_open command );