diff --git a/manifest b/manifest index 5a52969985..a2a3f426e9 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index b028b263ba..6f890018dd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -33c6fd6b3dc271fa1f2d4500b4f76c736accefce \ No newline at end of file +0ad1d93879bee0d34b122591c025192a51b8490f \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 0e85fb5fd1..6554584c76 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.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->idxpPage->nCell ){ - pCur->bSkipNext = 0; + assert( pCur->eSkip!=SKIP_INVALID ); + if( pCur->pPage->nCell==0 ){ + if( pRes ) *pRes = 1; + return SQLITE_OK; + } + assert( pCur->idxpPage->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); } diff --git a/src/btree.h b/src/btree.h index ec4d68f8a9..72cdc96c3b 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.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, diff --git a/src/test3.c b/src/test3.c index a873142015..b270b3d17e 100644 --- a/src/test3.c +++ b/src/test3.c @@ -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 }, }; diff --git a/test/all.test b/test/all.test index 7767d4743a..2db39e0e5e 100644 --- a/test/all.test +++ b/test/all.test @@ -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. diff --git a/test/btree3.test b/test/btree3.test index 30bf893939..1545eb78c1 100644 --- a/test/btree3.test +++ b/test/btree3.test @@ -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 index 0000000000..64ed9f74b5 --- /dev/null +++ b/test/btree4.test @@ -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 diff --git a/test/fkey1.test b/test/fkey1.test index a853457d76..7e8065cb9e 100644 --- a/test/fkey1.test +++ b/test/fkey1.test @@ -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