1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-19 21:43:15 +03:00

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:
drh
2008-07-11 21:02:53 +00:00
parent f460809030
commit a34605859d
11 changed files with 189 additions and 59 deletions

View File

@@ -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;

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.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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}