]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
More bug fixes in btree.c. (CVS 1322)
authordrh <drh@noemail.net>
Fri, 7 May 2004 23:50:57 +0000 (23:50 +0000)
committerdrh <drh@noemail.net>
Fri, 7 May 2004 23:50:57 +0000 (23:50 +0000)
FossilOrigin-Name: a80939ef714ec884950b4a1f4f809ffa37fdfa59

manifest
manifest.uuid
src/btree.c
src/btree.h
src/test3.c
test/btree.test

index 9f34ea149f0826cc3fb443c7a655464c1d0738bb..dfe85bc7273357e184ecb9c9c9d7ae7b0bab63d4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C The\sbtree.c\smodule\scompiles\sand\slinks\sand\spasses\ssome\stests.\s\sMany\stests\nstill\sfail,\sthough.\s(CVS\s1321)
-D 2004-05-07T17:57:50
+C More\sbug\sfixes\sin\sbtree.c.\s(CVS\s1322)
+D 2004-05-07T23:50:57
 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -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 ff2f51fcd01c4fb9f2ce0f061a549e0c9aae9d74
-F src/btree.h 49b255b2880c50a3572a536ea935edb2dd33aae3
+F src/btree.c a5fafa6179c80ca422fea96b4553525648f5535e
+F src/btree.h ba5d3bfadc3f46f86df525ac07274dc497af856a
 F src/btree_rb.c 99feb3ff835106d018a483a1ce403e5cf9c718bc
 F src/build.c 76fbca30081decd6615dee34b48c927ed5063752
 F src/copy.c 750e13828c3e4a293123e36aaa7cf0f22466248a
@@ -54,7 +54,7 @@ F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895
 F src/tclsqlite.c 9fe6fc0c20820e9411dfea407635de9b9d3ae0e3
 F src/test1.c 9aa62b89d420e6763b5e7ae89a47f6cf87370477
 F src/test2.c 9d611c45e1b07039a2bd95f5ea73178362b23229
-F src/test3.c d6af4e26bdbb7512ab5d01e49f34e72415d58a3b
+F src/test3.c cec7eee9e64f95a3d2e4a3c242e081f560f98cf8
 F src/test4.c 6e3e31acfaf21d66420fc35fda5b17dc0000cc8d
 F src/test5.c 3ff0565057b8d23e20092d5c6c0b7cb0d932c51e
 F src/tokenize.c 6676b946fd8825b67ab52140af4fdc57a70bda48
@@ -75,7 +75,7 @@ F test/auth.test 5c4d95cdaf539c0c236e20ce1f71a93e7dde9185
 F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/bind.test 56a57043b42c4664ca705f6050e56717a8a6699a
-F test/btree.test 77f93efac02dd05c7532b366b226bfe74757af57
+F test/btree.test c8a548edf00f46ceb5b9db8bd5c250e95b744c48
 F test/btree2.test e3b81ec33dc2f89b3e6087436dfe605b870c9080
 F test/btree3.test e597fb59be2ac0ea69c62aaa2064e998e528b665
 F test/btree3rb.test 127efcf5cdfcc352054e7db12622b01cdd8b36ac
@@ -190,7 +190,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
 F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
-P dcd6b55f932a7ade4ad058534651e198b56370ad
-R cdb403059e0209ca0544e8ccaa4370da
+P d394b2b217d4d728f9eba397262bf9d36195719e
+R 04bebd7ff9ebbf1a875d441cf21d682e
 U drh
-Z 48eb767d5af5b72432055086442dfd47
+Z ae2d2cf62d0e5010d2f2e28dfebc946c
index 474a8b183d59fa2646abb55929081f8fe3694d7a..16e52167235329ae5f0c4f9ea69365c096be2415 100644 (file)
@@ -1 +1 @@
-d394b2b217d4d728f9eba397262bf9d36195719e
\ No newline at end of file
+a80939ef714ec884950b4a1f4f809ffa37fdfa59
\ No newline at end of file
index 52da31bb56868645bf9a4bf58c58819bf10f45d3..100630179dd778e2803135b27595653da8bde93e 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.111 2004/05/07 17:57:50 drh Exp $
+** $Id: btree.c,v 1.112 2004/05/07 23:50:57 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -266,18 +266,11 @@ struct BtCursor {
   MemPage *pPage;           /* Page that contains the entry */
   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 sqlite3BtreeMoveto() */
+  u8 isValid;               /* TRUE if points to a valid entry */
+  u8 status;                /* Set to SQLITE_ABORT if cursors is invalidated */
 };
 
-/*
-** Legal values for BtCursor.eSkip.
-*/
-#define SKIP_NONE     0   /* Always step the cursor */
-#define SKIP_NEXT     1   /* The next sqlite3BtreeNext() is a no-op */
-#define SKIP_PREV     2   /* The next sqlite3BtreePrevious() is a no-op */
-#define SKIP_INVALID  3   /* Calls to Next() and Previous() are invalid */
-
 /*
 ** Read or write a two-, four-, and eight-byte big-endian integer values.
 */
@@ -405,6 +398,7 @@ static void defragmentPage(MemPage *pPage){
   if( !pPage->leaf ){
     n += 4;
   }
+  memcpy(&newPage[hdr], &oldPage[hdr], n-hdr);
   start = n;
   pc = get2byte(&oldPage[addr]);
   i = 0;
@@ -413,11 +407,12 @@ static void defragmentPage(MemPage *pPage){
     size = cellSize(pPage, &oldPage[pc]);
     memcpy(&newPage[n], &oldPage[pc], size);
     put2byte(&newPage[addr],n);
-    pPage->aCell[i] = &oldPage[n];
+    pPage->aCell[i++] = &oldPage[n];
     n += size;
     addr = pc;
     pc = get2byte(&oldPage[pc]);
   }
+  assert( i==pPage->nCell );
   leftover = pPage->pBt->pageSize - n;
   assert( leftover>=0 );
   assert( pPage->nFree==leftover );
@@ -426,15 +421,16 @@ static void defragmentPage(MemPage *pPage){
     leftover = 0;
     n = pPage->pBt->pageSize;
   }
-  memcpy(&oldPage[start], &newPage[start], n-start);
+  memcpy(&oldPage[hdr], &newPage[hdr], n-hdr);
   if( leftover==0 ){
-    put2byte(&oldPage[hdr+3], 0);
+    put2byte(&oldPage[hdr+1], 0);
   }else if( leftover>=4 ){
-    put2byte(&oldPage[hdr+3], n);
+    put2byte(&oldPage[hdr+1], n);
     put2byte(&oldPage[n], 0);
     put2byte(&oldPage[n+2], leftover);
     memset(&oldPage[n+4], 0, leftover-4);
   }
+  oldPage[hdr+5] = 0;
 }
 
 /*
@@ -1082,6 +1078,22 @@ int sqlite3BtreeCommit(Btree *pBt){
   return rc;
 }
 
+/*
+** Invalidate all cursors
+*/
+static void invalidateCursors(Btree *pBt){
+  BtCursor *pCur;
+  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+    MemPage *pPage = pCur->pPage;
+    if( pPage && !pPage->isInit ){
+      releasePage(pPage);
+      pCur->pPage = 0;
+      pCur->isValid = 0;
+      pCur->status = SQLITE_ABORT;
+    }
+  }
+}
+
 /*
 ** Rollback the transaction in progress.  All cursors will be
 ** invalided by this operation.  Any attempt to use a cursor
@@ -1093,18 +1105,11 @@ int sqlite3BtreeCommit(Btree *pBt){
 */
 int sqlite3BtreeRollback(Btree *pBt){
   int rc;
-  BtCursor *pCur;
   if( pBt->inTrans==0 ) return SQLITE_OK;
   pBt->inTrans = 0;
   pBt->inStmt = 0;
   rc = pBt->readOnly ? SQLITE_OK : sqlite3pager_rollback(pBt->pPager);
-  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
-    MemPage *pPage = pCur->pPage;
-    if( pPage && !pPage->isInit ){
-      releasePage(pPage);
-      pCur->pPage = 0;
-    }
-  }
+  invalidateCursors(pBt);
   unlockBtreeIfUnused(pBt);
   return rc;
 }
@@ -1155,16 +1160,9 @@ int sqlite3BtreeCommitStmt(Btree *pBt){
 */
 int sqlite3BtreeRollbackStmt(Btree *pBt){
   int rc;
-  BtCursor *pCur;
   if( pBt->inStmt==0 || pBt->readOnly ) return SQLITE_OK;
   rc = sqlite3pager_stmt_rollback(pBt->pPager);
-  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
-    MemPage *pPage = pCur->pPage;
-    if( pPage && !pPage->isInit ){
-      releasePage(pPage);
-      pCur->pPage = 0;
-    }
-  }
+  invalidateCursors(pBt);
   pBt->inStmt = 0;
   return rc;
 }
@@ -1265,7 +1263,6 @@ int sqlite3BtreeCursor(
   pCur->pBt = pBt;
   pCur->wrFlag = wrFlag;
   pCur->idx = 0;
-  pCur->eSkip = SKIP_INVALID;
   pCur->pNext = pBt->pCursor;
   if( pCur->pNext ){
     pCur->pNext->pPrev = pCur;
@@ -1280,6 +1277,8 @@ int sqlite3BtreeCursor(
     pCur->pShared = pCur;
   }
   pBt->pCursor = pCur;
+  pCur->isValid = 0;
+  pCur->status = SQLITE_OK;
   *ppCur = pCur;
   return SQLITE_OK;
 
@@ -1351,13 +1350,15 @@ static void releaseTempCursor(BtCursor *pCur){
 */
 int sqlite3BtreeKeySize(BtCursor *pCur, u64 *pSize){
   MemPage *pPage;
+  unsigned char *cell;
 
-  pPage = pCur->pPage;
-  assert( pPage!=0 );
-  if( pCur->idx >= pPage->nCell ){
+  if( !pCur->isValid ){
     *pSize = 0;
   }else{
-    unsigned char *cell = pPage->aCell[pCur->idx];
+    pPage = pCur->pPage;
+    assert( pPage!=0 );
+    assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+    cell = pPage->aCell[pCur->idx];
     cell += 2;   /* Skip the offset to the next cell */
     if( !pPage->leaf ){
       cell += 4;  /* Skip the child pointer */
@@ -1394,6 +1395,7 @@ static int getPayload(
   int maxLocal, ovflSize;
 
   assert( pCur!=0 && pCur->pPage!=0 );
+  assert( pCur->isValid );
   pBt = pCur->pBt;
   pPage = pCur->pPage;
   assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
@@ -1474,16 +1476,14 @@ static int getPayload(
 ** the available payload.
 */
 int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
-  MemPage *pPage;
-
   assert( amt>=0 );
   assert( offset>=0 );
-  assert( pCur->pPage!=0 );
-  pPage = pCur->pPage;
-  if( pCur->idx >= pPage->nCell || pPage->intKey ){
-    assert( amt==0 );
-    return SQLITE_OK;
+  if( pCur->isValid==0 ){
+    return pCur->status;
   }
+  assert( pCur->pPage!=0 );
+  assert( pCur->pPage->intKey==0 );
+  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
   return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
 }
 
@@ -1512,10 +1512,16 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){
   Btree *pBt;
   u64 nData, nKey;
 
-  assert( pCur!=0 && pCur->pPage!=0 );
+  assert( pCur!=0 );
+  if( !pCur->isValid ){
+    return 0;
+  }
+  assert( pCur->pPage!=0 );
+  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
   pBt = pCur->pBt;
   pPage = pCur->pPage;
   assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+  assert( pPage->intKey==0 );
   aPayload = pPage->aCell[pCur->idx];
   aPayload += 2;  /* Skip the next cell index */
   if( !pPage->leaf ){
@@ -1525,7 +1531,7 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){
     aPayload += getVarint(aPayload, &nData);
   }
   aPayload += getVarint(aPayload, &nKey);
-  if( pPage->intKey || nKey>pBt->maxLocal ){
+  if( nKey>pBt->maxLocal ){
     return 0;
   }
   return aPayload;
@@ -1541,14 +1547,19 @@ void *sqlite3BtreeKeyFetch(BtCursor *pCur){
 */
 int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
   MemPage *pPage;
+  unsigned char *cell;
+  u64 size;
 
+  if( !pCur->isValid ){
+    return pCur->status ? pCur->status : SQLITE_INTERNAL;
+  }
   pPage = pCur->pPage;
   assert( pPage!=0 );
-  if( pCur->idx >= pPage->nCell || pPage->zeroData ){
+  assert( pPage->isInit );
+  if( pPage->zeroData ){
     *pSize = 0;
   }else{
-    unsigned char *cell;
-    u64 size;
+    assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
     cell = pPage->aCell[pCur->idx];
     cell += 2;   /* Skip the offset to the next cell */
     if( !pPage->leaf ){
@@ -1571,15 +1582,13 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
 ** the available payload.
 */
 int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
-  MemPage *pPage;
-
+  if( !pCur->isValid ){
+    return pCur->status ? pCur->status : SQLITE_INTERNAL;
+  }
   assert( amt>=0 );
   assert( offset>=0 );
   assert( pCur->pPage!=0 );
-  pPage = pCur->pPage;
-  if( pCur->idx >= pPage->nCell ){
-    return 0;
-  }
+  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
   return getPayload(pCur, offset, amt, pBuf, 1);
 }
 
@@ -1593,6 +1602,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
   MemPage *pOldPage;
   Btree *pBt = pCur->pBt;
 
+  assert( pCur->isValid );
   rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
   if( rc ) return rc;
   pNewPage->idxParent = pCur->idx;
@@ -1637,6 +1647,7 @@ static void moveToParent(BtCursor *pCur){
   MemPage *pPage;
   int idxParent;
 
+  assert( pCur->isValid );
   pPage = pCur->pPage;
   assert( pPage!=0 );
   assert( !isRootPage(pPage) );
@@ -1686,7 +1697,10 @@ static int moveToRoot(BtCursor *pCur){
   Btree *pBt = pCur->pBt;
 
   rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0);
-  if( rc ) return rc;
+  if( rc ){
+    pCur->isValid = 0;
+    return rc;
+  }
   releasePage(pCur->pPage);
   pCur->pPage = pRoot;
   pCur->idx = 0;
@@ -1697,6 +1711,7 @@ static int moveToRoot(BtCursor *pCur){
     assert( subpage>0 );
     rc = moveToChild(pCur, subpage);
   }
+  pCur->isValid = pCur->pPage->nCell>0;
   return rc;
 }
 
@@ -1709,6 +1724,7 @@ static int moveToLeftmost(BtCursor *pCur){
   int rc;
   MemPage *pPage;
 
+  assert( pCur->isValid );
   while( !(pPage = pCur->pPage)->leaf ){
     assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
     pgno = get4byte(&pPage->aCell[pCur->idx][2]);
@@ -1730,6 +1746,7 @@ static int moveToRightmost(BtCursor *pCur){
   int rc;
   MemPage *pPage;
 
+  assert( pCur->isValid );
   while( !(pPage = pCur->pPage)->leaf ){
     pgno = get4byte(&pPage->aData[pPage->hdrOffset+6]);
     pCur->idx = pPage->nCell;
@@ -1746,16 +1763,19 @@ static int moveToRightmost(BtCursor *pCur){
 */
 int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
   int rc;
-  if( pCur->pPage==0 ) return SQLITE_ABORT;
+  if( pCur->status ){
+    return pCur->status;
+  }
   rc = moveToRoot(pCur);
   if( rc ) return rc;
-  if( pCur->pPage->nCell==0 ){
+  if( pCur->isValid==0 ){
+    assert( pCur->pPage->nCell==0 );
     *pRes = 1;
     return SQLITE_OK;
   }
+  assert( pCur->pPage->nCell>0 );
   *pRes = 0;
   rc = moveToLeftmost(pCur);
-  pCur->eSkip = SKIP_NONE;
   return rc;
 }
 
@@ -1765,17 +1785,19 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
 */
 int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
   int rc;
-  if( pCur->pPage==0 ) return SQLITE_ABORT;
+  if( pCur->status ){
+    return pCur->status;
+  }
   rc = moveToRoot(pCur);
   if( rc ) return rc;
-  assert( pCur->pPage->isInit );
-  if( pCur->pPage->nCell==0 ){
+  if( pCur->isValid==0 ){
+    assert( pCur->pPage->nCell==0 );
     *pRes = 1;
     return SQLITE_OK;
   }
+  assert( pCur->isValid );
   *pRes = 0;
   rc = moveToRightmost(pCur);
-  pCur->eSkip = SKIP_NONE;
   return rc;
 }
 
@@ -1809,10 +1831,18 @@ int sqlite3BtreeLast(BtCursor *pCur, 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;
+
+  if( pCur->status ){
+    return pCur->status;
+  }
   rc = moveToRoot(pCur);
   if( rc ) return rc;
+  assert( pCur->pPage );
+  assert( pCur->pPage->isInit );
+  if( pCur->isValid==0 ){
+    assert( pCur->pPage->nCell==0 );
+    return SQLITE_OK;
+  }
   for(;;){
     int lwr, upr;
     Pgno chldPg;
@@ -1865,16 +1895,30 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, u64 nKey, int *pRes){
     }
     if( chldPg==0 ){
       pCur->iMatch = c;
+      assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
       if( pRes ) *pRes = c;
       return SQLITE_OK;
     }
     pCur->idx = lwr;
     rc = moveToChild(pCur, chldPg);
-    if( rc ) return rc;
+    if( rc ){
+      return rc;
+    }
   }
   /* NOT REACHED */
 }
 
+/*
+** Return TRUE if the cursor is not pointing at an entry of the table.
+**
+** TRUE will be returned after a call to sqlite3BtreeNext() moves
+** past the last entry in the table or sqlite3BtreePrev() moves past
+** the first entry.  TRUE is also returned if the table is empty.
+*/
+int sqlite3BtreeEof(BtCursor *pCur){
+  return pCur->isValid==0;
+}
+
 /*
 ** Advance the cursor to the next entry in the database.  If
 ** successful then set *pRes=0.  If the cursor
@@ -1885,23 +1929,12 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
   int rc;
   MemPage *pPage = pCur->pPage;
   assert( pRes!=0 );
-  if( pPage==0 ){
-    *pRes = 1;
-    return SQLITE_ABORT;
-  }
-  assert( pPage->isInit );
-  assert( pCur->eSkip!=SKIP_INVALID );
-  if( pPage->nCell==0 ){
+  if( pCur->isValid==0 ){
     *pRes = 1;
     return SQLITE_OK;
   }
+  assert( pPage->isInit );
   assert( pCur->idx<pPage->nCell );
-  if( pCur->eSkip==SKIP_NEXT ){
-    pCur->eSkip = SKIP_NONE;
-    *pRes = 0;
-    return SQLITE_OK;
-  }
-  pCur->eSkip = SKIP_NONE;
   pCur->idx++;
   if( pCur->idx>=pPage->nCell ){
     if( !pPage->leaf ){
@@ -1914,6 +1947,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
     do{
       if( isRootPage(pPage) ){
         *pRes = 1;
+        pCur->isValid = 0;
         return SQLITE_OK;
       }
       moveToParent(pCur);
@@ -1940,23 +1974,12 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   int rc;
   Pgno pgno;
   MemPage *pPage;
-  pPage = pCur->pPage;
-  if( pPage==0 ){
-    *pRes = 1;
-    return SQLITE_ABORT;
-  }
-  assert( pPage->isInit );
-  assert( pCur->eSkip!=SKIP_INVALID );
-  if( pPage->nCell==0 ){
+  if( pCur->isValid==0 ){
     *pRes = 1;
     return SQLITE_OK;
   }
-  if( pCur->eSkip==SKIP_PREV ){
-    pCur->eSkip = SKIP_NONE;
-    *pRes = 0;
-    return SQLITE_OK;
-  }
-  pCur->eSkip = SKIP_NONE;
+  pPage = pCur->pPage;
+  assert( pPage->isInit );
   assert( pCur->idx>=0 );
   if( !pPage->leaf ){
     pgno = get4byte(&pPage->aCell[pCur->idx][2]);
@@ -1966,7 +1989,8 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   }else{
     while( pCur->idx==0 ){
       if( isRootPage(pPage) ){
-        if( pRes ) *pRes = 1;
+        pCur->isValid = 0;
+        *pRes = 1;
         return SQLITE_OK;
       }
       moveToParent(pCur);
@@ -2924,8 +2948,8 @@ int sqlite3BtreeInsert(
   unsigned char *oldCell;
   unsigned char newCell[MX_CELL_SIZE];
 
-  if( pCur->pPage==0 ){
-    return SQLITE_ABORT;  /* A rollback destroyed this cursor */
+  if( pCur->status ){
+    return pCur->status;  /* A rollback destroyed this cursor */
   }
   if( !pBt->inTrans || nKey+nData==0 ){
     /* Must start a transaction before doing an insert */
@@ -2969,7 +2993,6 @@ int sqlite3BtreeInsert(
   /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
   /* fflush(stdout); */
   moveToRoot(pCur);
-  pCur->eSkip = SKIP_INVALID;
   return rc;
 }
 
@@ -2985,8 +3008,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   Btree *pBt = pCur->pBt;
 
   assert( pPage->isInit );
-  if( pCur->pPage==0 ){
-    return SQLITE_ABORT;  /* A rollback destroyed this cursor */
+  if( pCur->status ){
+    return pCur->status;  /* A rollback destroyed this cursor */
   }
   if( !pBt->inTrans ){
     /* Must start a transaction before doing a delete */
@@ -3276,7 +3299,7 @@ int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
   nFree = 0;
   i = 0;
   idx = get2byte(&pPage->aData[hdrOffset+1]);
-  while( idx>0 && idx<SQLITE_USABLE_SIZE ){
+  while( idx>0 && idx<pPage->pBt->pageSize ){
     int sz = get2byte(&pPage->aData[idx+2]);
     sprintf(range,"%d..%d", idx, idx+sz-1);
     nFree += sz;
@@ -3346,7 +3369,7 @@ int sqlite3BtreeCursorDump(BtCursor *pCur, int *aResult){
   aResult[4] = pPage->nFree;
   cnt = 0;
   idx = get2byte(&pPage->aData[pPage->hdrOffset+1]);
-  while( idx>0 && idx<SQLITE_USABLE_SIZE ){
+  while( idx>0 && idx<pPage->pBt->pageSize ){
     cnt++;
     idx = get2byte(&pPage->aData[idx]);
   }
@@ -3707,7 +3730,7 @@ int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
 
   if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
   if( pBtTo->pCursor ) return SQLITE_BUSY;
-  memcpy(pBtTo->pPage1, pBtFrom->pPage1, SQLITE_USABLE_SIZE);
+  memcpy(pBtTo->pPage1, pBtFrom->pPage1, pBtFrom->pageSize);
   rc = sqlite3pager_overwrite(pBtTo->pPager, 1, pBtFrom->pPage1);
   nToPage = sqlite3pager_pagecount(pBtTo->pPager);
   nPage = sqlite3pager_pagecount(pBtFrom->pPager);
index 6d8caa1df78e554e97fd5ac11b85c68d4395a153..a8d673f7168f6cbbc6b25a8a8b31ba8cb2bda2b1 100644 (file)
@@ -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.38 2004/05/07 13:30:42 drh Exp $
+** @(#) $Id: btree.h,v 1.39 2004/05/07 23:50:57 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -72,6 +72,7 @@ int sqlite3BtreeInsert(BtCursor*, const void *pKey, u64 nKey,
 int sqlite3BtreeFirst(BtCursor*, int *pRes);
 int sqlite3BtreeLast(BtCursor*, int *pRes);
 int sqlite3BtreeNext(BtCursor*, int *pRes);
+int sqlite3BtreeEof(BtCursor*);
 int sqlite3BtreePrevious(BtCursor*, int *pRes);
 int sqlite3BtreeKeySize(BtCursor*, u64 *pSize);
 int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
index e5a3f1b9ea003c94bc28c0fee8ab86389882695d..bc66659fcdb1f73c5ab8b905ebcd8b800f99c339 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test3.c,v 1.27 2004/05/07 17:57:50 drh Exp $
+** $Id: test3.c,v 1.28 2004/05/07 23:50:57 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -810,6 +810,32 @@ static int btree_last(
   return SQLITE_OK;
 }
 
+/*
+** Usage:   btree_eof ID
+**
+** Return TRUE if the given cursor is not pointing at a valid entry.
+** Return FALSE if the cursor does point to a valid entry.
+*/
+static int btree_eof(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  char zBuf[50];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  sprintf(zBuf, "%d", sqlite3BtreeEof(pCur));
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
 /*
 ** Usage:   btree_keysize ID
 **
@@ -1022,6 +1048,7 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
      { "btree_insert",             (Tcl_CmdProc*)btree_insert             },
      { "btree_next",               (Tcl_CmdProc*)btree_next               },
      { "btree_prev",               (Tcl_CmdProc*)btree_prev               },
+     { "btree_eof",                (Tcl_CmdProc*)btree_eof                },
      { "btree_keysize",            (Tcl_CmdProc*)btree_keysize            },
      { "btree_key",                (Tcl_CmdProc*)btree_key                },
      { "btree_data",               (Tcl_CmdProc*)btree_data               },
index 9c417ecbe0ba479798e205c7ab9c339a6ee939db..71326a53db461de3719d839a937b677e1f80a7fe 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script is btree database backend
 #
-# $Id: btree.test,v 1.16 2004/05/07 17:57:50 drh Exp $
+# $Id: btree.test,v 1.17 2004/05/07 23:50:58 drh Exp $
 
 
 set testdir [file dirname $argv0]
@@ -185,13 +185,17 @@ do_test btree-3.18 {
 do_test btree-3.19 {
   btree_data $::c1
 } {6.00}
-do_test btree-3.20 {
+do_test btree-3.20.1 {
   btree_next $::c1
   btree_key $::c1
 } {0}
+do_test btree-3.20.2 {
+  btree_eof $::c1
+} {1}
 do_test btree-3.21 {
-  btree_data $::c1
-} {}
+  set rc [catch {btree_data $::c1} res]
+  lappend rc $res
+} {1 SQLITE_INTERNAL}
 
 # Commit the changes, reopen and reread the data
 #
@@ -270,8 +274,9 @@ do_test btree-3.39 {
   btree_key $::c1
 } {0}
 do_test btree-3.40 {
-  btree_data $::c1
-} {}
+  set rc [catch {btree_data $::c1} res]
+  lappend rc $res
+} {1 SQLITE_INTERNAL}
 do_test btree-3.41 {
   lindex [btree_pager_stats $::b1] 1
 } {1}
@@ -307,7 +312,7 @@ do_test btree-4.4 {
   set r {}
   while 1 {
     set key [btree_key $::c1]
-    if {$key==0} break
+    if {[btree_eof $::c1]} break
     lappend r $key
     lappend r [btree_data $::c1]
     btree_next $::c1
@@ -323,7 +328,7 @@ do_test btree-4.5 {
   set r {}
   while 1 {
     set key [btree_key $::c1]
-    if {$key==0} break
+    if {[btree_eof $::c1]} break
     lappend r $key
     lappend r [btree_data $::c1]
     btree_next $::c1
@@ -352,7 +357,7 @@ do_test btree-4.9 {
   btree_first $::c1
   while 1 {
     set key [btree_key $::c1]
-    if {$key==0} break
+    if {[btree_eof $::c1]} break
     lappend r $key
     lappend r [btree_data $::c1]
     btree_next $::c1
@@ -396,22 +401,9 @@ do_test btree-5.6 {
 
 proc select_all {cursor} {
   set r {}
-  btree_move_to $cursor {}
-  while 1 {
-    set key [btree_key $cursor]
-    if {$key==""} break
-    lappend r $key
-    lappend r [btree_data $cursor]
-    btree_next $cursor
-  }
-  return $r
-}
-proc select_all_intkey {cursor} {
-  set r {}
-  btree_move_to $cursor 0
-  while 1 {
+  btree_first $cursor
+  while {![btree_eof $cursor]} {
     set key [btree_key $cursor]
-    if {$key==0} break
     lappend r $key
     lappend r [btree_data $cursor]
     btree_next $cursor
@@ -420,10 +412,9 @@ proc select_all_intkey {cursor} {
 }
 proc select_keys {cursor} {
   set r {}
-  btree_move_to $cursor {}
-  while 1 {
+  btree_first $cursor
+  while {![btree_eof $cursor]} {
     set key [btree_key $cursor]
-    if {$key==""} break
     lappend r $key
     btree_next $cursor
   }
@@ -449,6 +440,7 @@ do_test btree-6.2.2 {
 } {2}
 do_test btree-6.2.3 {
   btree_insert $::c2 ten 10
+  btree_move_to $::c2 ten
   btree_key $::c2
 } {ten}
 do_test btree-6.3 {
@@ -457,7 +449,7 @@ do_test btree-6.3 {
   lindex [btree_pager_stats $::b1] 1
 } {2}
 do_test btree-6.3.1 {
-  select_all_intkey $::c1
+  select_all $::c1
 } {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00}
 #btree_page_dump $::b1 3
 do_test btree-6.4 {
@@ -497,15 +489,15 @@ do_test btree-6.9.1 {
   btree_key $::c2
 } {}
 
-# If we drop table 2 it just clears the table.  Table 2 always exists.
+# If we drop table 1 it just clears the table.  Table 1 always exists.
 #
 do_test btree-6.10 {
   btree_close_cursor $::c1
-  btree_drop_table $::b1 2
-  set ::c1 [btree_cursor $::b1 2 1]
-  btree_move_to $::c1 {}
-  btree_key $::c1
-} {}
+  btree_drop_table $::b1 1
+  set ::c1 [btree_cursor $::b1 1 1]
+  btree_first $::c1
+  btree_eof $::c1
+} {1}
 do_test btree-6.11 {
   btree_commit $::b1
   select_all $::c1
@@ -516,12 +508,12 @@ do_test btree-6.12 {
 do_test btree-6.13 {
   btree_close_cursor $::c2
   lindex [btree_pager_stats $::b1] 1
-} {2}
+} {1}
 
 # Check to see that pages defragment properly.  To do this test we will
 # 
-#   1.  Fill the first page table 2 with data.
-#   2.  Delete every other entry of table 2. 
+#   1.  Fill the first page of table 1 with data.
+#   2.  Delete every other entry of table 1.
 #   3.  Insert a single entry that requires more contiguous
 #       space than is available.
 #
@@ -531,98 +523,112 @@ do_test btree-7.1 {
 catch {unset key}
 catch {unset data}
 do_test btree-7.2 {
-  for {set i 0} {$i<36} {incr i} {
-    set key [format %03d $i]
-    set data "*** $key ***"
+  # Each record will be 10 bytes in size.
+  #   + 100 bytes of database header
+  #   + 6 bytes of table header
+  #   + 91*10=910 bytes of cells
+  # Totals 1016 bytes.  8 bytes left over
+  # Keys are 1000 through 1090.
+  for {set i 1000} {$i<1091} {incr i} {
+    set key $i
+    set data [format %5d $i]
     btree_insert $::c1 $key $data
   }
   lrange [btree_cursor_dump $::c1] 4 5
 } {8 1}
 do_test btree-7.3 {
-  btree_move_to $::c1 000
-  while {[btree_key $::c1]!=""} {
+  for {set i 1001} {$i<1091} {incr i 2} {
+    btree_move_to $::c1 $i
     btree_delete $::c1
-    btree_next $::c1
-    btree_next $::c1
   }
+  # Freed 45 blocks.  Total freespace is 458
+  # Keys remaining are even numbers between 1000 and 1090, inclusive
   lrange [btree_cursor_dump $::c1] 4 5
-} {512 19}
+} {458 46}
 #btree_page_dump $::b1 2
 do_test btree-7.4 {
-  btree_insert $::c1 018 {*** 018 ***+++}
+  # The largest free block is 10 bytes long.  So if we insert
+  # a record bigger than 10 bytes it should force a defrag
+  # The record is 20 bytes long.
+  btree_insert $::c1 2000 {123456789_12345}
+  btree_move_to $::c1 2000
   btree_key $::c1
-} {018}
+} {2000}
 do_test btree-7.5 {
   lrange [btree_cursor_dump $::c1] 4 5
-} {480 1}
+} {438 1}
 #btree_page_dump $::b1 2
 
 # Delete an entry to make a hole of a known size, then immediately recreate
 # that entry.  This tests the path into allocateSpace where the hole exactly
 # matches the size of the desired space.
 #
+# Keys are even numbers between 1000 and 1090 and one record of 2000.
+# There are 47 keys total.
+#
 do_test btree-7.6 {
-  btree_move_to $::c1 007
+  btree_move_to $::c1 1006
   btree_delete $::c1
-  btree_move_to $::c1 011
+  btree_move_to $::c1 1010
   btree_delete $::c1
 } {}
 do_test btree-7.7 {
-  lindex [btree_cursor_dump $::c1] 5
-} {3}
+  lrange [btree_cursor_dump $::c1] 4 5
+} {458 3}   ;# Create two new holes of 10 bytes each
 #btree_page_dump $::b1 2
 do_test btree-7.8 {
-  btree_insert $::c1 007 {*** 007 ***}
-  lindex [btree_cursor_dump $::c1] 5
-} {2}
+  btree_insert $::c1 1006 { 1006}
+  lrange [btree_cursor_dump $::c1] 4 5
+} {448 2}   ;# Filled in the first hole
 #btree_page_dump $::b1 2
 
 # Make sure the freeSpace() routine properly coaleses adjacent memory blocks
 #
 do_test btree-7.9 {
-  btree_move_to $::c1 013
+  btree_move_to $::c1 1012
   btree_delete $::c1
   lrange [btree_cursor_dump $::c1] 4 5
-} {536 2}
+} {458 2}  ;# Coalesce with the whole before
 do_test btree-7.10 {
-  btree_move_to $::c1 009
+  btree_move_to $::c1 1008
   btree_delete $::c1
   lrange [btree_cursor_dump $::c1] 4 5
-} {564 2}
+} {468 2}  ;# Coalesce with whole after
 do_test btree-7.11 {
-  btree_move_to $::c1 018
+  btree_move_to $::c1 1030
   btree_delete $::c1
   lrange [btree_cursor_dump $::c1] 4 5
-} {596 2}
+} {478 3}   ;# Make a new hole
 do_test btree-7.13 {
-  btree_move_to $::c1 033
+  btree_move_to $::c1 1034
   btree_delete $::c1
   lrange [btree_cursor_dump $::c1] 4 5
-} {624 3}
+} {488 4}   ;# Make another hole
 do_test btree-7.14 {
-  btree_move_to $::c1 035
+  btree_move_to $::c1 1032
   btree_delete $::c1
   lrange [btree_cursor_dump $::c1] 4 5
-} {652 2}
+} {498 3}   ;# The freed space should coalesce on both ends
 #btree_page_dump $::b1 2
 do_test btree-7.15 {
   lindex [btree_pager_stats $::b1] 1
-} {2}
+} {1}
 
 # Check to see that data on overflow pages work correctly.
 #
 do_test btree-8.1 {
   set data "*** This is a very long key "
-  while {[string length $data]<256} {append data $data}
+  while {[string length $data]<1234} {append data $data}
   set ::data $data
-  btree_insert $::c1 020 $data
+  btree_insert $::c1 2020 $data
 } {}
 #btree_page_dump $::b1 2
 do_test btree-8.1.1 {
   lindex [btree_pager_stats $::b1] 1
-} {2}
+} {1}
 #btree_pager_ref_dump $::b1
 do_test btree-8.2 {
+  btree_move_to $::c1 2020
   string length [btree_data $::c1]
 } [string length $::data]
 do_test btree-8.3 {
@@ -638,9 +644,10 @@ do_test btree-8.5 {
   set data "*** This is an even longer key"
   while {[string length $data]<2000} {append data $data}
   set ::data $data
-  btree_insert $::c1 020 $data
+  btree_insert $::c1 2030 $data
 } {}
 do_test btree-8.6 {
+  btree_move_to 2030
   string length [btree_data $::c1]
 } [string length $::data]
 do_test btree-8.7 {
@@ -654,8 +661,8 @@ do_test btree-8.9 {
   btree_close_cursor $::c1
   btree_close $::b1
   set ::b1 [btree_open test1.bt 2000 0]
-  set ::c1 [btree_cursor $::b1 2 1]
-  btree_move_to $::c1 020
+  set ::c1 [btree_cursor $::b1 1 1]
+  btree_move_to $::c1 2030
   btree_data $::c1
 } $::data
 do_test btree-8.10 {
@@ -664,7 +671,7 @@ do_test btree-8.10 {
 } {}
 do_test btree-8.11 {
   lindex [btree_get_meta $::b1] 0
-} [expr {int(([string length $::data]-238+1019)/1020)}]
+} {}
 
 # Now check out keys on overflow pages.
 #