]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the sqliteBtreePrevious() routine to the BTree module API. This is
authordrh <drh@noemail.net>
Wed, 4 Dec 2002 13:40:25 +0000 (13:40 +0000)
committerdrh <drh@noemail.net>
Wed, 4 Dec 2002 13:40:25 +0000 (13:40 +0000)
in anticipation of implementing reverse order searching of a table. (CVS 794)

FossilOrigin-Name: 0ad1d93879bee0d34b122591c025192a51b8490f

manifest
manifest.uuid
src/btree.c
src/btree.h
src/test3.c
test/all.test
test/btree3.test
test/btree4.test [new file with mode: 0644]
test/fkey1.test

index 5a5296998578dd168442e5b8eec4bd1476f4760f..a2a3f426e90164ecda04d7199c65d5068f00c22a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Allow\san\saggregate\sfunction\sin\sthe\sHAVING\sclause\seven\sif\sno\saggregates\sappear\nin\sthe\sresult\sset.\s\sTicket\s#187.\s(CVS\s793)
-D 2002-12-03T02:34:50
+C Add\sthe\ssqliteBtreePrevious()\sroutine\sto\sthe\sBTree\smodule\sAPI.\s\sThis\sis\nin\santicipation\sof\simplementing\sreverse\sorder\ssearching\sof\sa\stable.\s(CVS\s794)
+D 2002-12-04T13:40:26
 F Makefile.in 868c17a1ae1c07603d491274cc8f86c04acf2a1e
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -18,8 +18,8 @@ F main.mk 9d13839b9697af332d788fe6e801e68da027cc5c
 F publish.sh e5b83867d14708ed58cec8cba0a4f201e969474d
 F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
 F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
-F src/btree.c cd46130a7f68e3880a59aa5502b64be37bf31e91
-F src/btree.h 0ca6c2631338df62e4f7894252d9347ae234eda9
+F src/btree.c 2ae69698a620c01b9cb88f447be01ab3e1e3355f
+F src/btree.h 17710339f7a8f46e3c7d6d0d4648ef19c584ffda
 F src/build.c 415dce8886aabb6d45851caed7014707056d668b
 F src/delete.c aad9d4051ab46e6f6391ea5f7b8994a7c05bdd15
 F src/encode.c 6c9c87d5b7b2c0101d011ebc283a80abf672a4d1
@@ -46,7 +46,7 @@ F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
 F src/tclsqlite.c 9f2c00a92338c51171ded8943bd42d77f7e69e64
 F src/test1.c a46e9f61915b32787c5d5a05a4b92e4dacc437d9
 F src/test2.c 7e501ef1eb5d6b106f1d87f00e943171cdc41624
-F src/test3.c 8303af108b3354d294c44f5b17698f2f697ebf66
+F src/test3.c c12ea7f1c3fbbd58904e81e6cb10ad424e6fc728
 F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
 F src/tokenize.c 75e3bb37305b64e118e709752066f494c4f93c30
 F src/trigger.c 5ba917fc226b96065108da28186c2efaec53e481
@@ -55,17 +55,18 @@ F src/util.c ca7650ef2cc2d50241e48029fca109a3016144ee
 F src/vdbe.c 2c2472a93d0708920384c05d6099b637ab2229ce
 F src/vdbe.h b7584044223104ba7896a7f87b66daebdd6022ba
 F src/where.c 1de1a326235bb7f9ef7d3d58c08c0ac73dcd3acf
-F test/all.test efd958d048c70a3247997c482f0b33561f7759f0
+F test/all.test 873d30e25a41b3aa48fec5633a7ec1816e107029
 F test/bigfile.test 38d1071817caceb636c613e3546082b90e749a49
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/btree.test 10e75aec120ecefc0edc4c912a0980a43db1b6c2
 F test/btree2.test e3b81ec33dc2f89b3e6087436dfe605b870c9080
-F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895
+F test/btree3.test e597fb59be2ac0ea69c62aaa2064e998e528b665
+F test/btree4.test fa955a3d7a8bc91d6084b7f494f9e5d1bdfb15b6
 F test/conflict.test 0173a12a257f73bac2c9d53ad44cac9b15ea517e
 F test/copy.test 55d60a4d5ed342a0fa08b7cd07d46d43ea0d0d7f
 F test/delete.test 5821a95a66061ae09723a88938f23d10d8a881ad
 F test/expr.test dea1cd62684a8bf116426447c948f5e8fb2c84b6
-F test/fkey1.test 33c850201a6ec35f0b370daf4e57f44456f1b35d
+F test/fkey1.test d65c824459916249bee501532d6154ddab0b5db7
 F test/format3.test cbb168d446152fcf1dd85be299ad2d6cd507da4e
 F test/func.test 000515779001ac6899eec4b54e65c6e2501279d4
 F test/in.test 15428c85d141edda7543bfc3f9a32ce65193717b
@@ -151,7 +152,7 @@ F www/speed.tcl a20a792738475b68756ea7a19321600f23d1d803
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P dbf7893234a6c5d6bb2d931e52080bb05784c0c9
-R a45df3b87d35f9521a7fea74e5aa15cb
+P 33c6fd6b3dc271fa1f2d4500b4f76c736accefce
+R 95b809b99d60048286e99535c505d1d3
 U drh
-Z 33cf8f63886618c20f4fd92079ef68c2
+Z 472fffa55f48afcc8bd95b1b6e83536c
index b028b263babd12aba3bbb545de9de2aae9ca0499..6f890018ddd77aa6af79bfc78764366ecebe286d 100644 (file)
@@ -1 +1 @@
-33c6fd6b3dc271fa1f2d4500b4f76c736accefce
\ No newline at end of file
+0ad1d93879bee0d34b122591c025192a51b8490f
\ No newline at end of file
index 0e85fb5fd161b1e9766e69f6b5d3bffa195dcdbb..6554584c76dbbec1082d50d742c3293daf94c4bd 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.73 2002/12/02 04:25:20 drh Exp $
+** $Id: btree.c,v 1.74 2002/12/04 13:40:26 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -361,10 +361,18 @@ struct BtCursor {
   MemPage *pPage;           /* Page that contains the entry */
   int idx;                  /* Index of the entry in pPage->apCell[] */
   u8 wrFlag;                /* True if writable */
-  u8 bSkipNext;             /* sqliteBtreeNext() is no-op if true */
+  u8 eSkip;                 /* Determines if next step operation is a no-op */
   u8 iMatch;                /* compare result from last sqliteBtreeMoveto() */
 };
 
+/*
+** Legal values for BtCursor.eSkip.
+*/
+#define SKIP_NONE     0   /* Always step the cursor */
+#define SKIP_NEXT     1   /* The next sqliteBtreeNext() is a no-op */
+#define SKIP_PREV     2   /* The next sqliteBtreePrevious() is a no-op */
+#define SKIP_INVALID  3   /* Calls to Next() and Previous() are invalid */
+
 /*
 ** Routines for byte swapping.
 */
@@ -1008,6 +1016,7 @@ int sqliteBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
   pCur->pBt = pBt;
   pCur->wrFlag = wrFlag;
   pCur->idx = 0;
+  pCur->eSkip = SKIP_INVALID;
   pCur->pNext = pBt->pCursor;
   if( pCur->pNext ){
     pCur->pNext->pPrev = pCur;
@@ -1414,6 +1423,25 @@ static int moveToLeftmost(BtCursor *pCur){
   return SQLITE_OK;
 }
 
+/*
+** Move the cursor down to the right-most leaf entry beneath the
+** page to which it is currently pointing.  Notice the difference
+** between moveToLeftmost() and moveToRightmost().  moveToLeftmost()
+** finds the left-most entry beneath the *entry* whereas moveToRightmost()
+** finds the right-most entry beneath the *page*.
+*/
+static int moveToRightmost(BtCursor *pCur){
+  Pgno pgno;
+  int rc;
+
+  while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){
+    rc = moveToChild(pCur, SWAB32(pCur->pBt, pgno));
+    if( rc ) return rc;
+  }
+  pCur->idx = pCur->pPage->nCell - 1;
+  return SQLITE_OK;
+}
+
 /* Move the cursor to the first entry in the table.  Return SQLITE_OK
 ** on success.  Set *pRes to 0 if the cursor actually points to something
 ** or set *pRes to 1 if the table is empty.
@@ -1429,7 +1457,7 @@ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){
   }
   *pRes = 0;
   rc = moveToLeftmost(pCur);
-  pCur->bSkipNext = 0;
+  pCur->eSkip = SKIP_NONE;
   return rc;
 }
 
@@ -1439,7 +1467,6 @@ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){
 */
 int sqliteBtreeLast(BtCursor *pCur, int *pRes){
   int rc;
-  Pgno pgno;
   if( pCur->pPage==0 ) return SQLITE_ABORT;
   rc = moveToRoot(pCur);
   if( rc ) return rc;
@@ -1449,12 +1476,8 @@ int sqliteBtreeLast(BtCursor *pCur, int *pRes){
     return SQLITE_OK;
   }
   *pRes = 0;
-  while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){
-    rc = moveToChild(pCur, SWAB32(pCur->pBt, pgno));
-    if( rc ) return rc;
-  }
-  pCur->idx = pCur->pPage->nCell-1;
-  pCur->bSkipNext = 0;
+  rc = moveToRightmost(pCur);
+  pCur->eSkip = SKIP_NONE;
   return rc;
 }
 
@@ -1483,7 +1506,7 @@ int sqliteBtreeLast(BtCursor *pCur, int *pRes){
 int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){
   int rc;
   if( pCur->pPage==0 ) return SQLITE_ABORT;
-  pCur->bSkipNext = 0;
+  pCur->eSkip = SKIP_NONE;
   rc = moveToRoot(pCur);
   if( rc ) return rc;
   for(;;){
@@ -1539,11 +1562,18 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
     return SQLITE_ABORT;
   }
   assert( pCur->pPage->isInit );
-  if( pCur->bSkipNext && pCur->idx<pCur->pPage->nCell ){
-    pCur->bSkipNext = 0;
+  assert( pCur->eSkip!=SKIP_INVALID );
+  if( pCur->pPage->nCell==0 ){
+    if( pRes ) *pRes = 1;
+    return SQLITE_OK;
+  }
+  assert( pCur->idx<pCur->pPage->nCell );
+  if( pCur->eSkip==SKIP_NEXT ){
+    pCur->eSkip = SKIP_NONE;
     if( pRes ) *pRes = 0;
     return SQLITE_OK;
   }
+  pCur->eSkip = SKIP_NONE;
   pCur->idx++;
   if( pCur->idx>=pCur->pPage->nCell ){
     if( pCur->pPage->u.hdr.rightChild ){
@@ -1571,6 +1601,52 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
   return SQLITE_OK;
 }
 
+/*
+** Step the cursor to the back to the previous entry in the database.  If
+** successful and pRes!=NULL then set *pRes=0.  If the cursor
+** was already pointing to the first entry in the database before
+** this routine was called, then set *pRes=1 if pRes!=NULL.
+*/
+int sqliteBtreePrevious(BtCursor *pCur, int *pRes){
+  int rc;
+  Pgno pgno;
+  if( pCur->pPage==0 ){
+    if( pRes ) *pRes = 1;
+    return SQLITE_ABORT;
+  }
+  assert( pCur->pPage->isInit );
+  assert( pCur->eSkip!=SKIP_INVALID );
+  if( pCur->pPage->nCell==0 ){
+    if( pRes ) *pRes = 1;
+    return SQLITE_OK;
+  }
+  if( pCur->eSkip==SKIP_PREV ){
+    pCur->eSkip = SKIP_NONE;
+    if( pRes ) *pRes = 0;
+    return SQLITE_OK;
+  }
+  pCur->eSkip = SKIP_NONE;
+  assert( pCur->idx>=0 );
+  if( (pgno = pCur->pPage->apCell[pCur->idx]->h.leftChild)!=0 ){
+    rc = moveToChild(pCur, SWAB32(pCur->pBt, pgno));
+    if( rc ) return rc;
+    rc = moveToRightmost(pCur);
+  }else{
+    while( pCur->idx==0 ){
+      if( pCur->pPage->pParent==0 ){
+        if( pRes ) *pRes = 1;
+        return SQLITE_OK;
+      }
+      rc = moveToParent(pCur);
+      if( rc ) return rc;
+    }
+    pCur->idx--;
+    rc = SQLITE_OK;
+  }
+  if( pRes ) *pRes = 0;
+  return rc;
+}
+
 /*
 ** Allocate a new page from the database file.
 **
@@ -2485,6 +2561,7 @@ int sqliteBtreeInsert(
   rc = balance(pCur->pBt, pPage, pCur);
   /* sqliteBtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
   /* fflush(stdout); */
+  pCur->eSkip = SKIP_INVALID;
   return rc;
 }
 
@@ -2493,10 +2570,14 @@ int sqliteBtreeInsert(
 **
 ** The cursor is left pointing at either the next or the previous
 ** entry.  If the cursor is left pointing to the next entry, then 
-** the pCur->bSkipNext flag is set which forces the next call to 
+** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to 
 ** sqliteBtreeNext() to be a no-op.  That way, you can always call
 ** sqliteBtreeNext() after a delete and the cursor will be left
-** pointing to the first entry after the deleted entry.
+** pointing to the first entry after the deleted entry.  Similarly,
+** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to
+** the entry prior to the deleted entry so that a subsequent call to
+** sqliteBtreePrevious() will always leave the cursor pointing at the
+** entry immediately before the one that was deleted.
 */
 int sqliteBtreeDelete(BtCursor *pCur){
   MemPage *pPage = pCur->pPage;
@@ -2553,7 +2634,7 @@ int sqliteBtreeDelete(BtCursor *pCur){
     insertCell(pBt, pPage, pCur->idx, pNext, szNext);
     rc = balance(pBt, pPage, pCur);
     if( rc ) return rc;
-    pCur->bSkipNext = 1;
+    pCur->eSkip = SKIP_NEXT;
     dropCell(pBt, leafCur.pPage, leafCur.idx, szNext);
     rc = balance(pBt, leafCur.pPage, pCur);
     releaseTempCursor(&leafCur);
@@ -2563,12 +2644,12 @@ int sqliteBtreeDelete(BtCursor *pCur){
       pCur->idx = pPage->nCell-1;
       if( pCur->idx<0 ){ 
         pCur->idx = 0;
-        pCur->bSkipNext = 1;
+        pCur->eSkip = SKIP_NEXT;
       }else{
-        pCur->bSkipNext = 0;
+        pCur->eSkip = SKIP_PREV;
       }
     }else{
-      pCur->bSkipNext = 1;
+      pCur->eSkip = SKIP_NEXT;
     }
     rc = balance(pBt, pPage, pCur);
   }
index ec4d68f8a93f85691a12e998c8a05083f64e9aac..72cdc96c3b26ea3d0aa9f240e0f50f758764520b 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.25 2002/08/11 20:10:48 drh Exp $
+** @(#) $Id: btree.h,v 1.26 2002/12/04 13:40:26 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -45,6 +45,7 @@ int sqliteBtreeInsert(BtCursor*, const void *pKey, int nKey,
 int sqliteBtreeFirst(BtCursor*, int *pRes);
 int sqliteBtreeLast(BtCursor*, int *pRes);
 int sqliteBtreeNext(BtCursor*, int *pRes);
+int sqliteBtreePrevious(BtCursor*, int *pRes);
 int sqliteBtreeKeySize(BtCursor*, int *pSize);
 int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf);
 int sqliteBtreeKeyCompare(BtCursor*, const void *pKey, int nKey,
index a8731420152a35fe7fc86aa4d8b4038592352591..b270b3d17e46839313e15ac8e4edb2905a79f11e 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.21 2002/12/02 04:25:21 drh Exp $
+** $Id: test3.c,v 1.22 2002/12/04 13:40:26 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -660,7 +660,9 @@ static int btree_insert(
 /*
 ** Usage:   btree_next ID
 **
-** Move the cursor to the next entry in the table.
+** Move the cursor to the next entry in the table.  Return 0 on success
+** or 1 if the cursor was already on the last entry in the table or if
+** the table is empty.
 */
 static int btree_next(
   void *NotUsed,
@@ -689,10 +691,45 @@ static int btree_next(
   return SQLITE_OK;
 }
 
+/*
+** Usage:   btree_prev ID
+**
+** Move the cursor to the previous entry in the table.  Return 0 on
+** success and 1 if the cursor was already on the first entry in
+** the table or if the table was empty.
+*/
+static int btree_prev(
+  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;
+  int rc;
+  int res = 0;
+  char zBuf[100];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreePrevious(pCur, &res);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sprintf(zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
 /*
 ** Usage:   btree_first ID
 **
-** Move the cursor to the first entry in the table.
+** Move the cursor to the first entry in the table.  Return 0 if the
+** cursor was left point to something and 1 if the table is empty.
 */
 static int btree_first(
   void *NotUsed,
@@ -721,6 +758,39 @@ static int btree_first(
   return SQLITE_OK;
 }
 
+/*
+** Usage:   btree_last ID
+**
+** Move the cursor to the last entry in the table.  Return 0 if the
+** cursor was left point to something and 1 if the table is empty.
+*/
+static int btree_last(
+  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;
+  int rc;
+  int res = 0;
+  char zBuf[100];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&pCur) ) return TCL_ERROR;
+  rc = sqliteBtreeLast(pCur, &res);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sprintf(zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
 /*
 ** Usage:   btree_key ID
 **
@@ -900,10 +970,12 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
      { "btree_delete",             (Tcl_CmdProc*)btree_delete             },
      { "btree_insert",             (Tcl_CmdProc*)btree_insert             },
      { "btree_next",               (Tcl_CmdProc*)btree_next               },
+     { "btree_prev",               (Tcl_CmdProc*)btree_prev               },
      { "btree_key",                (Tcl_CmdProc*)btree_key                },
      { "btree_data",               (Tcl_CmdProc*)btree_data               },
      { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
      { "btree_first",              (Tcl_CmdProc*)btree_first              },
+     { "btree_last",               (Tcl_CmdProc*)btree_last               },
      { "btree_cursor_dump",        (Tcl_CmdProc*)btree_cursor_dump        },
      { "btree_integrity_check",    (Tcl_CmdProc*)btree_integrity_check    },
   };
index 7767d4743a86e377bb2cf2192860ded0a8b42529..2db39e0e5efe7e29d77580a5f92d0802f4a220e6 100644 (file)
@@ -10,7 +10,7 @@
 #***********************************************************************
 # This file runs all tests.
 #
-# $Id: all.test,v 1.17 2002/08/24 18:24:57 drh Exp $
+# $Id: all.test,v 1.18 2002/12/04 13:40:27 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -23,6 +23,24 @@ if {[file exists ./sqlite_test_count]} {
   set COUNT 4
 }
 
+if {[llength $argv]>0} {
+  foreach {name value} $argv {
+    switch -- $name {
+      -count {
+         set COUNT $value
+      }
+      -quick {
+         set ISQUICK $value
+      }
+      default {
+         puts stderr "Unknown option: $name"
+         exit
+      }
+    }
+  }
+}
+set argv {}
+
 # LeakList will hold a list of the number of unfreed mallocs after
 # each round of the test.  This number should be constant.  If it
 # grows, it may mean there is a memory leak in the library.
index 30bf8939390de56bce8808eda01d92e0b99ff370..1545eb78c1f6c92d11f7a5cfa2df40bb3afa389e 100644 (file)
@@ -20,7 +20,7 @@
 # cursor pointing at the first entry past the one that was deleted.
 # This test is designed to verify that behavior.
 #
-# $Id: btree3.test,v 1.1 2001/11/23 00:24:12 drh Exp $
+# $Id: btree3.test,v 1.2 2002/12/04 13:40:27 drh Exp $
 
 
 set testdir [file dirname $argv0]
@@ -47,7 +47,7 @@ for {set k 2} {$k<=10} {incr k} {
     set jkey [format %02d $j]
     btree_clear_table $::b1 2
     set ::c1 [btree_cursor $::b1 2 1]
-    for {set i 1} {$i<=$k+1} {incr i} {
+    for {set i 1} {$i<=$k} {incr i} {
       set key [format %02d $i]
       do_test btree3-$k.$j.1.$i {
         btree_insert $::c1 $::key $::data
@@ -61,10 +61,18 @@ for {set k 2} {$k<=10} {incr k} {
     do_test btree3-$k.$j.3 {
       btree_delete $::c1
     } {}
-    do_test btree3-$k.$j.4 {
-      btree_next $::c1
-      btree_key $::c1
-    } [format %02d [expr $j+1]]
+    if {$j<$k} {
+      do_test btree3-$k.$j.4 {
+        btree_next $::c1
+        btree_key $::c1
+      } [format %02d [expr $j+1]]
+    }
+    if {$j>1} {
+      do_test btree3-$k.$j.5 {
+        btree_prev $::c1
+        btree_key $::c1
+      } [format %02d [expr $j-1]]
+    }
     btree_close_cursor $::c1
   }
 }
diff --git a/test/btree4.test b/test/btree4.test
new file mode 100644 (file)
index 0000000..64ed9f7
--- /dev/null
@@ -0,0 +1,98 @@
+# 2002 December 03
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this script is btree database backend
+#
+# This file focuses on testing the sqliteBtreeNext() and 
+# sqliteBtreePrevious() procedures and making sure they are able
+# to step through an entire table from either direction.
+#
+# $Id: btree4.test,v 1.1 2002/12/04 13:40:27 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {[info commands btree_open]!=""} {
+
+# Open a test database.
+#
+file delete -force test1.bt
+file delete -force test1.bt-journal
+set b1 [btree_open test1.bt]
+btree_begin_transaction $::b1
+
+set data {abcdefghijklmnopqrstuvwxyz0123456789}
+append data $data
+append data $data
+append data $data
+append data $data
+
+foreach N {10 100 1000} {
+  btree_clear_table $::b1 2
+  set ::c1 [btree_cursor $::b1 2 1]
+  do_test btree4-$N.1 {
+    for {set i 1} {$i<=$N} {incr i} {
+      btree_insert $::c1 [format k-%05d $i] $::data-$i
+    }
+    btree_first $::c1
+    btree_key $::c1
+  } {k-00001}
+  do_test btree4-$N.2 {
+    btree_data $::c1
+  } $::data-1
+  for {set i 2} {$i<=$N} {incr i} {
+    do_test btree-$N.3.$i.1 {
+      btree_next $::c1
+    } 0
+    do_test btree-$N.3.$i.2 {
+      btree_key $::c1
+    } [format k-%05d $i]
+    do_test btree-$N.3.$i.3 {
+      btree_data $::c1
+    } $::data-$i
+  }
+  do_test btree4-$N.4 {
+    btree_next $::c1
+  } 1
+  do_test btree4-$N.5 {
+    btree_last $::c1
+  } 0
+  do_test btree4-$N.6 {
+    btree_key $::c1
+  } [format k-%05d $N]
+  do_test btree4-$N.7 {
+    btree_data $::c1
+  } $::data-$N
+  for {set i [expr {$N-1}]} {$i>=1} {incr i -1} {
+    do_test btree4-$N.8.$i.1 {
+      btree_prev $::c1
+    } 0
+    do_test btree4-$N.8.$i.2 {
+      btree_key $::c1
+    } [format k-%05d $i]
+    do_test btree4-$N.8.$i.3 {
+      btree_data $::c1
+    } $::data-$i
+  }
+  do_test btree4-$N.9 {
+    btree_prev $::c1
+  } 1
+  btree_close_cursor $::c1
+}
+
+btree_rollback $::b1    
+btree_pager_ref_dump $::b1
+btree_close $::b1
+
+} ;# end if( not mem: and has pager_open command );
+
+finish_test
index a853457d76543aec32b8d3f781020c29814d68a6..7e8065cb9ea51c3cb1cdf54712e224d7bf1f967f 100644 (file)
@@ -38,7 +38,16 @@ do_test fkey1-1.1 {
     );
   }
 } {}
-
+do_test fkey1-1.2 {
+  execsql {
+    CREATE TABLE t3(
+      a INTEGER REFERENCES t2,
+      b INTEGER REFERENCES t1,
+      FOREIGN KEY (a,b) REFERENCES t2(x,y)
+    );
+  }
+} {}
+   
 
 
 finish_test