-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
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
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
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
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
-33c6fd6b3dc271fa1f2d4500b4f76c736accefce
\ No newline at end of file
+0ad1d93879bee0d34b122591c025192a51b8490f
\ No newline at end of file
** 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
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.
*/
pCur->pBt = pBt;
pCur->wrFlag = wrFlag;
pCur->idx = 0;
+ pCur->eSkip = SKIP_INVALID;
pCur->pNext = pBt->pCursor;
if( pCur->pNext ){
pCur->pNext->pPrev = 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.
}
*pRes = 0;
rc = moveToLeftmost(pCur);
- pCur->bSkipNext = 0;
+ pCur->eSkip = SKIP_NONE;
return rc;
}
*/
int sqliteBtreeLast(BtCursor *pCur, int *pRes){
int rc;
- Pgno pgno;
if( pCur->pPage==0 ) return SQLITE_ABORT;
rc = moveToRoot(pCur);
if( rc ) return rc;
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;
}
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(;;){
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 ){
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.
**
rc = balance(pCur->pBt, pPage, pCur);
/* sqliteBtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
/* fflush(stdout); */
+ pCur->eSkip = SKIP_INVALID;
return rc;
}
**
** 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;
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);
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);
}
** 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_
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,
** 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"
/*
** 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,
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,
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
**
{ "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 },
};
#***********************************************************************
# 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
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.
# 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]
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
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
}
}
--- /dev/null
+# 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
);
}
} {}
-
+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