Detect and handles the case where a row is modified or deleted while it
is being read during SELECT processing. (CVS 5399) FossilOrigin-Name: c80a5d09935c60a2a50bc262c172a94073355f0d
This commit is contained in:
parent
f460809030
commit
a34605859d
27
manifest
27
manifest
@ -1,5 +1,5 @@
|
||||
C Tweak\sto\sthe\s".timer"\scommand\sin\sthe\sCLI\sto\shelp\sit\swork\sbetter\swith\sGCC.\s(CVS\s5398)
|
||||
D 2008-07-11T17:23:25
|
||||
C Detect\sand\shandles\sthe\scase\swhere\sa\srow\sis\smodified\sor\sdeleted\swhile\sit\nis\sbeing\sread\sduring\sSELECT\sprocessing.\s(CVS\s5399)
|
||||
D 2008-07-11T21:02:54
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in a03f7cb4f7ad50bc53a788c6c544430e81f95de4
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@ -95,9 +95,9 @@ F src/attach.c b18ba42c77f7d3941f5d23d2ca20fa1d841a4e91
|
||||
F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
|
||||
F src/bitvec.c 95c86bd18d8fedf0533f5af196192546e10a7e7d
|
||||
F src/btmutex.c 483ced3c52205b04b97df69161fadbf87f4f1ea2
|
||||
F src/btree.c 8d08214728e231ba9ee2753bb224abbfddad6a6c
|
||||
F src/btree.h 0063e9853aa1ffb55b46f0e798eea8934f032ffd
|
||||
F src/btreeInt.h d59e58d39950a17c0fb7e004c90ab7696d3e7df5
|
||||
F src/btree.c dcbb9624c99e53cbb1d63e1c980fc9e93a02a06b
|
||||
F src/btree.h 03256ed7ee42b5ecacbe887070b0f8249e7d069d
|
||||
F src/btreeInt.h 346cef00620c91dd7c632b57defd1332d0847edc
|
||||
F src/build.c bac7233d984be3805aaa41cf500f7ee12dc97249
|
||||
F src/callback.c aa492a0ad8c2d454edff9fb8a57fae13743cf71d
|
||||
F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
|
||||
@ -140,7 +140,7 @@ F src/pragma.c 6fad83fbcc7ec6e76d91fe2805fe972ff3af6a0c
|
||||
F src/prepare.c c9bb0aacb7a571d049805699ed18f2bb136ea091
|
||||
F src/printf.c 8e5d410220cf8650f502caf71f0de979a3f9031e
|
||||
F src/random.c 5c754319d38abdd6acd74601ee0105504adc508a
|
||||
F src/select.c fcf51df1818a448edebf55b032d89771ba4536ef
|
||||
F src/select.c b909e21358f141627d1cadfdfbdb43ec08ed0a4b
|
||||
F src/shell.c 4b835fe734304ac22a3385868cd3790c1e4f7aa1
|
||||
F src/sqlite.h.in a573ab3a22256fc6c28b8f27d5af446f2b712a31
|
||||
F src/sqlite3ext.h 1e3887c9bd3ae66cb599e922824b04cd0d0f2c3e
|
||||
@ -160,10 +160,10 @@ F src/test8.c 3b85c05b15ccfc6844093791e83d5c8a1e0354ca
|
||||
F src/test9.c 904ebe0ed1472d6bad17a81e2ecbfc20017dc237
|
||||
F src/test_async.c da9f58f49faccd3a26ba89f58de125862351b6e2
|
||||
F src/test_autoext.c f53b0cdf7bf5f08100009572a5d65cdb540bd0ad
|
||||
F src/test_btree.c fd982a4242449a462f5a87f120d6e8fcc5e24a83
|
||||
F src/test_btree.c af13f421952807ceba599bdb63b652ac68cd6c9f
|
||||
F src/test_config.c 7d3c3bb37e12d8ed22c5e0900e1361178b0580a3
|
||||
F src/test_devsym.c 6012cb8e3acf812513511025a4fa5d626e0ba19b
|
||||
F src/test_func.c ade4f0117cc96c5a05682e1ef38b84320a866ad9
|
||||
F src/test_func.c 24a556989685495013e08f311ae31c4ef86ddb8c
|
||||
F src/test_hexio.c 2f1122aa3f012fa0142ee3c36ce5c902a70cd12f
|
||||
F src/test_loadext.c df8ab3a6481ddebbdf0d28ebac5d9e0790f7860f
|
||||
F src/test_malloc.c e655e3ff61845fc23886ee81973638e795477c9e
|
||||
@ -181,11 +181,11 @@ F src/update.c 4e698fcc0c91c241a960304c4236dc3a49603155
|
||||
F src/utf.c 8d52f620a7153d90b058502124fe51d821fcdf57
|
||||
F src/util.c a8f80412a536ea309c1887e5d4b58a3fa6eca69c
|
||||
F src/vacuum.c ef342828002debc97514617af3424aea8ef8522c
|
||||
F src/vdbe.c 14d98d1d47041a99a9c98034842772dbbcc062fb
|
||||
F src/vdbe.c 179dbe5f08b17c712be65e951eaada3b3ca52092
|
||||
F src/vdbe.h c46155c221418bea29ee3a749d5950fcf85a70e2
|
||||
F src/vdbeInt.h 30535c1d30ba1b5fb58d8f0e1d1261af976558aa
|
||||
F src/vdbeapi.c a7c6b8db324cf7eccff32de871dea36aa305c994
|
||||
F src/vdbeaux.c 24ecae4e3e7ebcfb604d6ef0dc268da2cfb3ffa7
|
||||
F src/vdbeaux.c 25d562cda55cb6d9796110ee916a524bdd03a0fb
|
||||
F src/vdbeblob.c a20fe9345062b1a1b4cc187dc5fad45c9414033b
|
||||
F src/vdbefifo.c c46dae1194e4277bf007144d7e5b0c0b1c24f136
|
||||
F src/vdbemem.c 0c72b58ffd759676ce4829f42bacb83842a58c21
|
||||
@ -274,6 +274,7 @@ F test/distinctagg.test 2b89d1c5220d966a30ba4b40430338669301188b
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
|
||||
F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
|
||||
F test/eval.test 020a21a236667bd4c56205d999c9992f1d944cac
|
||||
F test/exclusive.test 5390ddf1f90a6d055111c0ebe6311045dd3035e1
|
||||
F test/exclusive2.test d13bf66753dca46e61241d35d36ab7c868b0d313
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
@ -604,7 +605,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
P 6dcce6b9748c6148a768a4f6b69f33f70edc3993
|
||||
R 0792731ecd027d8cdcf662cd0517070f
|
||||
P 1041deb6ae03c52ce1fb8170d11913cc0bbf3a6e
|
||||
R 14bde473a1bc0347c0884dfdfef2d004
|
||||
U drh
|
||||
Z 9e913ba85dc9c52696a07ed2d3591212
|
||||
Z 356e23cc4752d813c3f7c25ef3dd2a11
|
||||
|
@ -1 +1 @@
|
||||
1041deb6ae03c52ce1fb8170d11913cc0bbf3a6e
|
||||
c80a5d09935c60a2a50bc262c172a94073355f0d
|
62
src/btree.c
62
src/btree.c
@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.480 2008/07/11 16:39:23 drh Exp $
|
||||
** $Id: btree.c,v 1.481 2008/07/11 21:02:54 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** See the header comment on "btreeInt.h" for additional information.
|
||||
@ -361,14 +361,10 @@ static void clearCursorPosition(BtCursor *pCur){
|
||||
** Restore the cursor to the position it was in (or as close to as possible)
|
||||
** when saveCursorPosition() was called. Note that this call deletes the
|
||||
** saved position info stored by saveCursorPosition(), so there can be
|
||||
** at most one effective restoreOrClearCursorPosition() call after each
|
||||
** at most one effective restoreCursorPosition() call after each
|
||||
** saveCursorPosition().
|
||||
**
|
||||
** If the second argument argument - doSeek - is false, then instead of
|
||||
** returning the cursor to its saved position, any saved position is deleted
|
||||
** and the cursor state set to CURSOR_INVALID.
|
||||
*/
|
||||
int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){
|
||||
int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur){
|
||||
int rc;
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->eState>=CURSOR_REQUIRESEEK );
|
||||
@ -385,11 +381,35 @@ int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define restoreOrClearCursorPosition(p) \
|
||||
#define restoreCursorPosition(p) \
|
||||
(p->eState>=CURSOR_REQUIRESEEK ? \
|
||||
sqlite3BtreeRestoreOrClearCursorPosition(p) : \
|
||||
sqlite3BtreeRestoreCursorPosition(p) : \
|
||||
SQLITE_OK)
|
||||
|
||||
/*
|
||||
** Determine whether or not a cursor has moved from the position it
|
||||
** was last placed at. Cursor can move when the row they are pointing
|
||||
** at is deleted out from under them.
|
||||
**
|
||||
** This routine returns an error code if something goes wrong. The
|
||||
** integer *pHasMoved is set to one if the cursor has moved and 0 if not.
|
||||
*/
|
||||
int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
|
||||
int rc;
|
||||
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc ){
|
||||
*pHasMoved = 1;
|
||||
return rc;
|
||||
}
|
||||
if( pCur->eState!=CURSOR_VALID || pCur->skip!=0 ){
|
||||
*pHasMoved = 1;
|
||||
}else{
|
||||
*pHasMoved = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
/*
|
||||
** Given a page number of a regular database page, return the page
|
||||
@ -2768,9 +2788,7 @@ static int btreeCursor(
|
||||
return SQLITE_OK;
|
||||
|
||||
create_cursor_exception:
|
||||
if( pCur ){
|
||||
releasePage(pCur->pPage);
|
||||
}
|
||||
releasePage(pCur->pPage);
|
||||
unlockBtreeIfUnused(pBt);
|
||||
return rc;
|
||||
}
|
||||
@ -2905,7 +2923,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
|
||||
int rc;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
rc = restoreOrClearCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
|
||||
if( pCur->eState==CURSOR_INVALID ){
|
||||
@ -2929,7 +2947,7 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
|
||||
int rc;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
rc = restoreOrClearCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
|
||||
if( pCur->eState==CURSOR_INVALID ){
|
||||
@ -3237,7 +3255,7 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
int rc;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
rc = restoreOrClearCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->pPage!=0 );
|
||||
@ -3270,7 +3288,7 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
#endif
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
rc = restoreOrClearCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->pPage!=0 );
|
||||
@ -3821,7 +3839,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
MemPage *pPage;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
rc = restoreOrClearCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@ -3890,7 +3908,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
||||
MemPage *pPage;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
rc = restoreOrClearCursorPosition(pCur);
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@ -5834,7 +5852,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
** that the entry will be deleted from.
|
||||
*/
|
||||
if(
|
||||
(rc = restoreOrClearCursorPosition(pCur))!=0 ||
|
||||
(rc = restoreCursorPosition(pCur))!=0 ||
|
||||
(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur))!=0 ||
|
||||
(rc = sqlite3PagerWrite(pPage->pDbPage))!=0
|
||||
){
|
||||
@ -6362,10 +6380,10 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
|
||||
*/
|
||||
int sqlite3BtreeFlags(BtCursor *pCur){
|
||||
/* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
|
||||
** restoreOrClearCursorPosition() here.
|
||||
** restoreCursorPosition() here.
|
||||
*/
|
||||
MemPage *pPage;
|
||||
restoreOrClearCursorPosition(pCur);
|
||||
restoreCursorPosition(pCur);
|
||||
pPage = pCur->pPage;
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pPage->pBt==pCur->pBt );
|
||||
@ -7176,7 +7194,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
||||
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
|
||||
assert(pCsr->isIncrblobHandle);
|
||||
|
||||
restoreOrClearCursorPosition(pCsr);
|
||||
restoreCursorPosition(pCsr);
|
||||
assert( pCsr->eState!=CURSOR_REQUIRESEEK );
|
||||
if( pCsr->eState!=CURSOR_VALID ){
|
||||
return SQLITE_ABORT;
|
||||
|
@ -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.101 2008/07/11 16:15:18 drh Exp $
|
||||
** @(#) $Id: btree.h,v 1.102 2008/07/11 21:02:54 drh Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@ -142,6 +142,7 @@ int sqlite3BtreeMoveto(
|
||||
int bias,
|
||||
int *pRes
|
||||
);
|
||||
int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData,
|
||||
|
@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btreeInt.h,v 1.24 2008/07/10 00:32:42 drh Exp $
|
||||
** $Id: btreeInt.h,v 1.25 2008/07/11 21:02:54 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@ -466,7 +466,7 @@ struct BtCursor {
|
||||
** The table that this cursor was opened on still exists, but has been
|
||||
** modified since the cursor was last used. The cursor position is saved
|
||||
** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
|
||||
** this state, restoreOrClearCursorPosition() can be called to attempt to
|
||||
** this state, restoreCursorPosition() can be called to attempt to
|
||||
** seek the cursor to the saved position.
|
||||
**
|
||||
** CURSOR_FAULT:
|
||||
@ -631,7 +631,7 @@ int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int);
|
||||
int sqlite3BtreeInitPage(MemPage *pPage, MemPage *pParent);
|
||||
void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*);
|
||||
void sqlite3BtreeParseCell(MemPage*, int, CellInfo*);
|
||||
int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur);
|
||||
int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur);
|
||||
void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur);
|
||||
void sqlite3BtreeReleaseTempCursor(BtCursor *pCur);
|
||||
int sqlite3BtreeIsRootPage(MemPage *pPage);
|
||||
|
@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.454 2008/07/10 17:59:12 danielk1977 Exp $
|
||||
** $Id: select.c,v 1.455 2008/07/11 21:02:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -1165,7 +1165,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
|
||||
assert( pTab->nCol>0 );
|
||||
pTab->aCol = aCol = sqlite3DbMallocZero(db, sizeof(pTab->aCol[0])*pTab->nCol);
|
||||
for(i=0, pCol=aCol; i<pTab->nCol; i++, pCol++){
|
||||
Expr *p, *pR;
|
||||
Expr *p;
|
||||
char *zType;
|
||||
char *zName;
|
||||
int nName;
|
||||
|
@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test_btree.c,v 1.4 2008/07/10 00:32:42 drh Exp $
|
||||
** $Id: test_btree.c,v 1.5 2008/07/11 21:02:54 drh Exp $
|
||||
*/
|
||||
#include "btreeInt.h"
|
||||
#include <tcl.h>
|
||||
@ -87,7 +87,7 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
|
||||
int rc;
|
||||
|
||||
if( pCur->eState==CURSOR_REQUIRESEEK ){
|
||||
rc = sqlite3BtreeRestoreOrClearCursorPosition(pCur);
|
||||
rc = sqlite3BtreeRestoreCursorPosition(pCur);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
** Code for testing all sorts of SQLite interfaces. This code
|
||||
** implements new SQL functions used by the test scripts.
|
||||
**
|
||||
** $Id: test_func.c,v 1.7 2008/07/08 14:17:35 danielk1977 Exp $
|
||||
** $Id: test_func.c,v 1.8 2008/07/11 21:02:54 drh Exp $
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include "tcl.h"
|
||||
@ -230,6 +230,38 @@ static void test_isolation(
|
||||
sqlite3_result_value(pCtx, argv[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke an SQL statement recursively. The function result is the
|
||||
** first column of the first row of the result set.
|
||||
*/
|
||||
static void test_eval(
|
||||
sqlite3_context *pCtx,
|
||||
int nArg,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
sqlite3 *db = sqlite3_context_db_handle(pCtx);
|
||||
const char *zSql;
|
||||
|
||||
zSql = (char*)sqlite3_value_text(argv[0]);
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0));
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
if( rc ){
|
||||
char *zErr;
|
||||
assert( pStmt==0 );
|
||||
zErr = sqlite3_mprintf("sqlite3_prepare_v2() error: %s",sqlite3_errmsg(db));
|
||||
sqlite3_result_text(pCtx, zErr, -1, sqlite3_free);
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int registerTestFunctions(sqlite3 *db){
|
||||
static const struct {
|
||||
@ -245,6 +277,7 @@ static int registerTestFunctions(sqlite3 *db){
|
||||
{ "test_auxdata", -1, SQLITE_UTF8, test_auxdata},
|
||||
{ "test_error", 1, SQLITE_UTF8, test_error},
|
||||
{ "test_error", 2, SQLITE_UTF8, test_error},
|
||||
{ "test_eval", 1, SQLITE_UTF8, test_eval},
|
||||
{ "test_isolation", 2, SQLITE_UTF8, test_isolation},
|
||||
};
|
||||
int i;
|
||||
|
24
src/vdbe.c
24
src/vdbe.c
@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.760 2008/07/10 00:32:42 drh Exp $
|
||||
** $Id: vdbe.c,v 1.761 2008/07/11 21:02:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@ -1670,7 +1670,6 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
int flags;
|
||||
int res;
|
||||
char affinity;
|
||||
Mem x1, x3;
|
||||
|
||||
flags = pIn1->flags|pIn3->flags;
|
||||
|
||||
@ -3808,6 +3807,7 @@ case OP_Prev: /* jump */
|
||||
case OP_Next: { /* jump */
|
||||
Cursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
int res;
|
||||
|
||||
CHECK_FOR_INTERRUPT;
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
@ -3817,19 +3817,17 @@ case OP_Next: { /* jump */
|
||||
}
|
||||
pCrsr = pC->pCursor;
|
||||
assert( pCrsr );
|
||||
if( pC->nullRow==0 ){
|
||||
int res = 1;
|
||||
assert( pC->deferredMoveto==0 );
|
||||
rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
|
||||
sqlite3BtreePrevious(pCrsr, &res);
|
||||
pC->nullRow = res;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
if( res==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
res = 1;
|
||||
assert( pC->deferredMoveto==0 );
|
||||
rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
|
||||
sqlite3BtreePrevious(pCrsr, &res);
|
||||
pC->nullRow = res;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
if( res==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_search_count++;
|
||||
sqlite3_search_count++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
pC->rowidIsValid = 0;
|
||||
break;
|
||||
|
@ -14,7 +14,7 @@
|
||||
** to version 2.8.7, all this code was combined into the vdbe.c source file.
|
||||
** But that file was getting too big so this subroutines were split out.
|
||||
**
|
||||
** $Id: vdbeaux.c,v 1.396 2008/07/11 16:15:18 drh Exp $
|
||||
** $Id: vdbeaux.c,v 1.397 2008/07/11 21:02:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@ -1864,6 +1864,14 @@ int sqlite3VdbeCursorMoveto(Cursor *p){
|
||||
#endif
|
||||
p->deferredMoveto = 0;
|
||||
p->cacheStatus = CACHE_STALE;
|
||||
}else if( p->pCursor ){
|
||||
int hasMoved;
|
||||
int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved);
|
||||
if( rc ) return rc;
|
||||
if( hasMoved ){
|
||||
p->cacheStatus = CACHE_STALE;
|
||||
p->nullRow = 1;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
71
test/eval.test
Normal file
71
test/eval.test
Normal file
@ -0,0 +1,71 @@
|
||||
# 2008 July 11
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# This file experiments with recursion using the "test_eval()" SQL function
|
||||
# in order to make sure that SQLite is reentrant.
|
||||
#
|
||||
# $Id: eval.test,v 1.1 2008/07/11 21:02:54 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Create a table to work with.
|
||||
#
|
||||
do_test eval-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 SELECT x+2 FROM t1;
|
||||
INSERT INTO t1 SELECT x+4 FROM t1;
|
||||
INSERT INTO t1 SELECT x+8 FROM t1;
|
||||
INSERT INTO t1 SELECT x+16 FROM t1;
|
||||
INSERT INTO t1 SELECT x+32 FROM t1;
|
||||
INSERT INTO t1 SELECT x+64 FROM t1;
|
||||
INSERT INTO t1 SELECT x+128 FROM t1;
|
||||
INSERT INTO t1 SELECT x+256 FROM t1;
|
||||
SELECT count(*), max(x) FROM t1;
|
||||
}
|
||||
} {512 512}
|
||||
do_test eval-1.2 {
|
||||
execsql {
|
||||
SELECT x, test_eval('SELECT max(x) FROM t1 WHERE x<' || x) FROM t1 LIMIT 5
|
||||
}
|
||||
} {1 {} 2 1 3 2 4 3 5 4}
|
||||
|
||||
# Delete a row out from under a read cursor in the middle of
|
||||
# collecting the arguments for a single row in a result set.
|
||||
# Verify that subsequent rows come out as NULL.
|
||||
#
|
||||
do_test eval-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(x,y);
|
||||
INSERT INTO t2 SELECT x, x+1 FROM t1 WHERE x<5;
|
||||
SELECT x, test_eval('DELETE FROM t2 WHERE x='||x), y FROM t2;
|
||||
}
|
||||
} {1 {} {} 2 {} {} 3 {} {} 4 {} {}}
|
||||
do_test eval-2.2 {
|
||||
execsql {
|
||||
SELECT * FROM t2
|
||||
}
|
||||
} {}
|
||||
|
||||
# Modify a row while it is being read.
|
||||
#
|
||||
do_test eval-3.1 {
|
||||
execsql {
|
||||
INSERT INTO t2 SELECT x, x+1 FROM t1 WHERE x<5;
|
||||
SELECT x, test_eval('UPDATE t2 SET y=y+100 WHERE x='||x), y FROM t2;
|
||||
}
|
||||
} {1 {} 102 2 {} 103 3 {} 104 4 {} 105}
|
||||
|
||||
finish_test
|
Loading…
x
Reference in New Issue
Block a user