Add the sqliteBtreePrevious() routine to the BTree module API. This is

in anticipation of implementing reverse order searching of a table. (CVS 794)

FossilOrigin-Name: 0ad1d93879bee0d34b122591c025192a51b8490f
This commit is contained in:
drh
2002-12-04 13:40:25 +00:00
parent c66c5a266b
commit 2dcc9aa2a8
9 changed files with 331 additions and 43 deletions
+100 -19
View 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);
}
+2 -1
View 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,
+75 -3
View 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 },
};