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
diff --git a/src/btree.c b/src/btree.c
index 8ef51f5..cf531b9 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.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 @@
 ** 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 @@
   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 @@
   return SQLITE_OK;
 
 create_cursor_exception:
-  if( pCur ){
-    releasePage(pCur->pPage);
-  }
+  releasePage(pCur->pPage);
   unlockBtreeIfUnused(pBt);
   return rc;
 }
@@ -2905,7 +2923,7 @@
   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 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 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 @@
 #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 @@
   MemPage *pPage;
 
   assert( cursorHoldsMutex(pCur) );
-  rc = restoreOrClearCursorPosition(pCur);
+  rc = restoreCursorPosition(pCur);
   if( rc!=SQLITE_OK ){
     return rc;
   }
@@ -3890,7 +3908,7 @@
   MemPage *pPage;
 
   assert( cursorHoldsMutex(pCur) );
-  rc = restoreOrClearCursorPosition(pCur);
+  rc = restoreCursorPosition(pCur);
   if( rc!=SQLITE_OK ){
     return rc;
   }
@@ -5834,7 +5852,7 @@
   ** 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 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 @@
   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;
diff --git a/src/btree.h b/src/btree.h
index 780f1d3..3fe01cb 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.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 bias,
   int *pRes
 );
+int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
 int sqlite3BtreeDelete(BtCursor*);
 int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
                                   const void *pData, int nData,
diff --git a/src/btreeInt.h b/src/btreeInt.h
index 0ec07aa..2fd4235 100644
--- a/src/btreeInt.h
+++ b/src/btreeInt.h
@@ -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 @@
 **   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 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);
diff --git a/src/select.c b/src/select.c
index e14e292..56ede61 100644
--- a/src/select.c
+++ b/src/select.c
@@ -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 @@
   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;
diff --git a/src/test_btree.c b/src/test_btree.c
index 615c960..1b33ef9 100644
--- a/src/test_btree.c
+++ b/src/test_btree.c
@@ -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 rc;
 
   if( pCur->eState==CURSOR_REQUIRESEEK ){
-    rc = sqlite3BtreeRestoreOrClearCursorPosition(pCur);
+    rc = sqlite3BtreeRestoreCursorPosition(pCur);
     if( rc!=SQLITE_OK ){
       return rc;
     }
diff --git a/src/test_func.c b/src/test_func.c
index 3704552..d43b7a9 100644
--- a/src/test_func.c
+++ b/src/test_func.c
@@ -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 @@
   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 @@
     { "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;
diff --git a/src/vdbe.c b/src/vdbe.c
index ac3e208..d65995b 100644
--- a/src/vdbe.c
+++ b/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 @@
   int flags;
   int res;
   char affinity;
-  Mem x1, x3;
 
   flags = pIn1->flags|pIn3->flags;
 
@@ -3808,6 +3807,7 @@
 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 @@
   }
   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;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 6e61ce4..79fe89f 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -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 @@
 #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;
 }