]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Got a lot of BTree tests working. Still lots more needed. (CVS 230)
authordrh <drh@noemail.net>
Thu, 28 Jun 2001 01:54:48 +0000 (01:54 +0000)
committerdrh <drh@noemail.net>
Thu, 28 Jun 2001 01:54:48 +0000 (01:54 +0000)
FossilOrigin-Name: 9cfeeb5896d2a17c8c7904136d346a6245c9e497

Makefile.in
manifest
manifest.uuid
src/btree.c
src/btree.h
src/pager.c
src/pager.h
src/test3.c
test/btree.test

index 9c6a497b909fd2f241f8d49685539acdbe403ed1..bcccd3d2119b4632b41ae40e59e39e4e4b09afdf 100644 (file)
@@ -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
index 5297a725c355cb694bb6e4a85ccea316916c7261..8e99792825aaf13d1df9faedf2752af495a6d599 100644 (file)
--- 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
index eb975e72a0af65b721de5bc5512a063eb60dc1b2..e0131c98ab3efe3c345bef7f974a72aad6a7f0b4 100644 (file)
@@ -1 +1 @@
-6b9b298b2846146b95d7df7f423867976bafa390
\ No newline at end of file
+9cfeeb5896d2a17c8c7904136d346a6245c9e497
\ No newline at end of file
index d1761c5fba1baea4918c2773e66e3904e324ac89..7dbac878a6c1f8916856bd6e321d6d7071bd79f4 100644 (file)
@@ -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 && x<from+SQLITE_PAGE_SIZE ){
       *((uptr*)&pTo->apCell[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; i<nNew; i++){
     MemPage *pNew = apNew[i];
-    while( j<nCell && pNew->nFree<freePerPage && szCell[j]<=pNew->nFree ){
+    while( j<nCell && pNew->nFree>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; i<nOld; i++){
-    sqlitepager_unref(apOld[i]);
+    if( apOld[i]!=&aOld[i] ) sqlitepager_unref(apOld[i]);
   }
   for(i=0; i<nNew; i++){
     sqlitepager_unref(apNew[i]);
@@ -2037,6 +2060,7 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){
   }else{
     zeroPage(pPage);
   }
+  sqlitepager_unref(pPage);
   return rc;
 }
 
@@ -2074,8 +2098,8 @@ int sqliteBtreeDropTable(Btree *pBt, int iTable){
     rc = freePage(pBt, pPage, iTable);
   }else{
     zeroPage(pPage);
-    sqlitepager_unref(pPage);
   }
+  sqlitepager_unref(pPage);
   return rc;  
 }
 
@@ -2214,3 +2238,12 @@ int sqliteBtreeCursorDump(BtCursor *pCur, int *aResult){
   return SQLITE_OK;
 }
 #endif
+
+#ifdef SQLITE_TEST
+/*
+** Return the pager associated with a BTree
+*/
+Pager *sqliteBtreePager(Btree *pBt){
+  return pBt->pPager;
+}
+#endif
index 17f74009380121bda08233ccb165382655ab0df1..5229b988916b1b26c503bacb492a8de144e64b4e 100644 (file)
@@ -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
index 4e759ac802b362359a29e021e8f3415a63b31d8d..ebd641f34223a9b28905010aca25b72044ef4842 100644 (file)
@@ -27,7 +27,7 @@
 ** all writes in order to support rollback.  Locking is used to limit
 ** access to one or more reader or one writer.
 **
-** @(#) $Id: pager.c,v 1.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
index acb7735b8c0e62e7d696659f794a15b44a2f91c7..208d898c0af07215d0222e921ca96a664ff5409f 100644 (file)
@@ -25,7 +25,7 @@
 ** subsystem.  The page cache subsystem reads and writes a file a page
 ** at a time and provides a journal for rollback.
 **
-** @(#) $Id: pager.h,v 1.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
index 02c965a7403099eb262c788ebbf209931e54200b..e4ed48ac34bbfc249b63aaa90b6f2c7b67678d17 100644 (file)
@@ -25,7 +25,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test3.c,v 1.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;
 }
index 2167d4a71b1d371377a95a9889f6c876ffc9388b..545671b50b3a16f1d460ce10ef3db2fbd895b460 100644 (file)
@@ -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 );