]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allows UPDATE, INSERT, and DELETEs to occur while a SELECT is pending on
authordrh <drh@noemail.net>
Wed, 16 Aug 2006 16:42:48 +0000 (16:42 +0000)
committerdrh <drh@noemail.net>
Wed, 16 Aug 2006 16:42:48 +0000 (16:42 +0000)
the same table. (CVS 3355)

FossilOrigin-Name: 8c52d2ad468615e50a727adab2977a0bef1bc068

manifest
manifest.uuid
src/btree.c
test/btree.test
test/capi2.test
test/capi3.test
test/delete2.test
test/lock.test
test/misc2.test

index ed49ee372b30ad0ec50cad850e3b379d0d3a823b..53a64f08249ffb4bc6c4f7bd78881a6fd4a307c9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Tighten\san\sassert\s(ticket\s#1920).\s\sChange\sto\s"sqlite3.h"\sfrom\s<sqlite3.h>\non\sthe\ssqlite3ext.h\sheader\s(ticket\s#1916).\s\sFix\sa\sbug\sin\sthe\stest\sscripts.\s(CVS\s3354)
-D 2006-08-15T14:21:16
+C Allows\sUPDATE,\sINSERT,\sand\sDELETEs\sto\soccur\swhile\sa\sSELECT\sis\spending\son\nthe\ssame\stable.\s(CVS\s3355)
+D 2006-08-16T16:42:48
 F Makefile.in 986db66b0239b460fc118e7d2fa88b45b26c444e
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -34,7 +34,7 @@ F src/alter.c eba661e77bfd00282fbfa316cdb6aef04856fedc
 F src/analyze.c 7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a
 F src/attach.c b11eb4d5d3fb99a10a626956bccc7215f6b68b16
 F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f
-F src/btree.c b39b7147d400b4906a48850b83d22b0c2a641007
+F src/btree.c 8f18bb08f84d6a12104fd0f01255febe6289a186
 F src/btree.h 061c50e37de7f50b58528e352d400cf33ead7418
 F src/build.c 4359b34a36938716ed10ac037eec9dc5173b8f4b
 F src/callback.c fd9bb39f7ff6b52bad8365617abc61c720640429
@@ -133,7 +133,7 @@ F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
 F test/bind.test 941a424e7722dd8994c2d503b28d00e6a8f87f23
 F test/bindxfer.test b76bfb7df68bb0b238039f4543a84e9612291b54
 F test/blob.test 28c3b25150684ee3d108bb78cfb67a472deef2f0
-F test/btree.test b1957e39f4858b0722dc0f70f926a2143d3b25f9
+F test/btree.test 099978c3b9f0a203f4805d2bb8fdb042d5cb8ffc
 F test/btree2.test 4b56a2a4a4f84d68c77aef271223a713bf5ebafc
 F test/btree4.test 3797b4305694c7af6828675b0f4b1424b8ca30e4
 F test/btree5.test 8e5ff32c02e685d36516c6499add9375fe1377f2
@@ -141,8 +141,8 @@ F test/btree6.test a5ede6bfbbb2ec8b27e62813612c0f28e8f3e027
 F test/btree7.test a6d3b842db22af97dd14b989e90a2fd96066b72f
 F test/btree8.test fadc112bcbd6a0c622d34c813fc8a648eacf8804
 F test/busy.test 0271c854738e23ad76e10d4096a698e5af29d211
-F test/capi2.test cddd151c7b687e9e00fde408b9547ec93c2146a4
-F test/capi3.test 0d26e0ef558e3d409258f69dc74ca72f6a7aa76e
+F test/capi2.test cb478885b8b1a6a9f703a9da1c8d7d101c0970d6
+F test/capi3.test 5f54824e8356ad25ee40a101b36452e74d68a945
 F test/capi3b.test 5f0bc94b104e11086b1103b20277e1910f59c7f4
 F test/cast.test f88e7b6946e9a467cf4bb142d92bb65a83747fc2
 F test/check.test e5ea0c1a06c10e81e3434ca029e2c4a562f2b673
@@ -161,7 +161,7 @@ F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
 F test/date.test 288b41dbcc7aa114a976c53b45b78b3aa7736940
 F test/default.test 252298e42a680146b1dd64f563b95bdf088d94fb
 F test/delete.test 525a6953bc3978780cae35f3eaf1027cf4ce887d
-F test/delete2.test d20b08733243f1890079f3b48f2356fbb62212b2
+F test/delete2.test c06be3806ba804bc8c6f134476816080280b40e3
 F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
 F test/descidx1.test 2177c4ad55edcf56ad5f4c6490f307d7774e8a10
 F test/descidx2.test eb3a2882ec58aa6e1e8131d9bb54436e5b4a3ce2
@@ -197,7 +197,7 @@ F test/laststmtchanges.test 19a6d0c11f7a31dc45465b495f7b845a62cbec17
 F test/like.test 5f7d76574752a9101cac13372c8a85999d0d91e6
 F test/limit.test 2a87b9cb2165abb49ca0ddcf5cb43cf24074581f
 F test/loadext.test 6e4ecf99ec26334768c63b4322177b5e147f006a
-F test/lock.test 9b7afcb24f53d24da502abb33daaad2cd6d44107
+F test/lock.test 6825aea0b5885578b1b63a3b178803842c4ee9f1
 F test/lock2.test d83ba79d3c4fffdb5b926c7d8ca7a36c34288a55
 F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
 F test/main.test e7212ce1023957c7209778cc87fa932bd79ba89a
@@ -213,7 +213,7 @@ F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
 F test/memleak.test df2b2b96e77f8ba159a332299535b1e5f18e49ac
 F test/minmax.test 66434d8ee04869fe4c220b665b73748accbb9163
 F test/misc1.test 27a6ad11ba6e4b73aeee650ab68053ad7dfd0433
-F test/misc2.test 09388e5a2c5c1017ad3ff1c4bf469375def2a0c2
+F test/misc2.test 9740c2fb7e4a69b2bebd4c5fd9ba45ae27b27e98
 F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03
 F test/misc4.test b043a05dea037cca5989f3ae09552fa16119bc80
 F test/misc5.test 83bceca3d38ed10ced00271e02b26b24795def83
@@ -377,7 +377,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P b4d53974c30d195c061cc7605a707d7d30c52740
-R 89af5dea7bfa8fd23f7d7d4bce8f2b2b
+P 3ebedbb6f90ec0f9d3bed181f8fb5366f91fc48c
+R 80ebffa6703b819b2ae14611b1bcf70c
 U drh
-Z 69cf4aeab2e20bee08dd4d43f781d8b4
+Z 257c04f6c61217c6002648ad82ead94a
index ec522e92e6681d473a09301c4e46a8acb1a43db7..af83ba59ccab770053d9ce11d45948ad9adab660 100644 (file)
@@ -1 +1 @@
-3ebedbb6f90ec0f9d3bed181f8fb5366f91fc48c
\ No newline at end of file
+8c52d2ad468615e50a727adab2977a0bef1bc068
\ No newline at end of file
index 25306cf37af9c154fbb7f8d3b0e0c94dc5fa21cf..abd47124d3dc5019c490fa8dccf404f14e6f0eb5 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.327 2006/08/13 18:39:26 drh Exp $
+** $Id: btree.c,v 1.328 2006/08/16 16:42:48 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -387,17 +387,13 @@ struct BtCursor {
   CellInfo info;            /* A parse of the cell we are pointing at */
   u8 wrFlag;                /* True if writable */
   u8 eState;                /* One of the CURSOR_XXX constants (see below) */
-#ifndef SQLITE_OMIT_SHARED_CACHE
   void *pKey;      /* Saved key that was cursor's last known position */
   i64 nKey;        /* Size of pKey, or last integer key */
   int skip;        /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
-#endif
 };
 
 /*
-** Potential values for BtCursor.eState. The first two values (VALID and 
-** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur 
-** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined.
+** Potential values for BtCursor.eState.
 **
 ** CURSOR_VALID:
 **   Cursor points to a valid entry. getPayload() etc. may be called.
@@ -434,7 +430,7 @@ int sqlite3_btree_trace=0;  /* True to enable tracing */
 /*
 ** Forward declaration
 */
-static int checkReadLocks(BtShared*,Pgno,BtCursor*);
+static int checkReadLocks(Btree*,Pgno,BtCursor*);
 
 /*
 ** Read or write a two- and four-byte big-endian integer values.
@@ -509,105 +505,8 @@ struct BtLock {
   #define queryTableLock(a,b,c) SQLITE_OK
   #define lockTable(a,b,c) SQLITE_OK
   #define unlockAllTables(a)
-  #define restoreOrClearCursorPosition(a,b) SQLITE_OK
-  #define saveAllCursors(a,b,c) SQLITE_OK
-
 #else
 
-static void releasePage(MemPage *pPage);
-
-/*
-** Save the current cursor position in the variables BtCursor.nKey 
-** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
-*/
-static int saveCursorPosition(BtCursor *pCur){
-  int rc;
-
-  assert( CURSOR_VALID==pCur->eState );
-  assert( 0==pCur->pKey );
-
-  rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
-
-  /* If this is an intKey table, then the above call to BtreeKeySize()
-  ** stores the integer key in pCur->nKey. In this case this value is
-  ** all that is required. Otherwise, if pCur is not open on an intKey
-  ** table, then malloc space for and store the pCur->nKey bytes of key 
-  ** data.
-  */
-  if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
-    void *pKey = sqliteMalloc(pCur->nKey);
-    if( pKey ){
-      rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
-      if( rc==SQLITE_OK ){
-        pCur->pKey = pKey;
-      }else{
-        sqliteFree(pKey);
-      }
-    }else{
-      rc = SQLITE_NOMEM;
-    }
-  }
-  assert( !pCur->pPage->intKey || !pCur->pKey );
-
-  if( rc==SQLITE_OK ){
-    releasePage(pCur->pPage);
-    pCur->pPage = 0;
-    pCur->eState = CURSOR_REQUIRESEEK;
-  }
-
-  return rc;
-}
-
-/*
-** Save the positions of all cursors except pExcept open on the table 
-** with root-page iRoot. Usually, this is called just before cursor
-** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
-*/
-static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
-  BtCursor *p;
-  if( sqlite3ThreadDataReadOnly()->useSharedData ){
-    for(p=pBt->pCursor; p; p=p->pNext){
-      if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && 
-          p->eState==CURSOR_VALID ){
-        int rc = saveCursorPosition(p);
-        if( SQLITE_OK!=rc ){
-          return rc;
-        }
-      }
-    }
-  }
-  return SQLITE_OK;
-}
-
-/*
-** 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 
-** saveCursorPosition().
-**
-** If the second argument argument - doSeek - is false, then instead of 
-** returning the cursor to it's saved position, any saved position is deleted
-** and the cursor state set to CURSOR_INVALID.
-*/
-static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
-  int rc = SQLITE_OK;
-  assert( sqlite3ThreadDataReadOnly()->useSharedData );
-  assert( pCur->eState==CURSOR_REQUIRESEEK );
-  pCur->eState = CURSOR_INVALID;
-  if( doSeek ){
-    rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
-  }
-  if( rc==SQLITE_OK ){
-    sqliteFree(pCur->pKey);
-    pCur->pKey = 0;
-    assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
-  }
-  return rc;
-}
-
-#define restoreOrClearCursorPosition(p,x) \
-  (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK)
 
 /*
 ** Query to see if btree handle p may obtain a lock of type eLock 
@@ -747,6 +646,98 @@ static void unlockAllTables(Btree *p){
 }
 #endif /* SQLITE_OMIT_SHARED_CACHE */
 
+static void releasePage(MemPage *pPage);  /* Forward reference */
+
+/*
+** Save the current cursor position in the variables BtCursor.nKey 
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+  int rc;
+
+  assert( CURSOR_VALID==pCur->eState );
+  assert( 0==pCur->pKey );
+
+  rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
+
+  /* If this is an intKey table, then the above call to BtreeKeySize()
+  ** stores the integer key in pCur->nKey. In this case this value is
+  ** all that is required. Otherwise, if pCur is not open on an intKey
+  ** table, then malloc space for and store the pCur->nKey bytes of key 
+  ** data.
+  */
+  if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
+    void *pKey = sqliteMalloc(pCur->nKey);
+    if( pKey ){
+      rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
+      if( rc==SQLITE_OK ){
+        pCur->pKey = pKey;
+      }else{
+        sqliteFree(pKey);
+      }
+    }else{
+      rc = SQLITE_NOMEM;
+    }
+  }
+  assert( !pCur->pPage->intKey || !pCur->pKey );
+
+  if( rc==SQLITE_OK ){
+    releasePage(pCur->pPage);
+    pCur->pPage = 0;
+    pCur->eState = CURSOR_REQUIRESEEK;
+  }
+
+  return rc;
+}
+
+/*
+** Save the positions of all cursors except pExcept open on the table 
+** with root-page iRoot. Usually, this is called just before cursor
+** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+*/
+static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
+  BtCursor *p;
+  for(p=pBt->pCursor; p; p=p->pNext){
+    if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && 
+        p->eState==CURSOR_VALID ){
+      int rc = saveCursorPosition(p);
+      if( SQLITE_OK!=rc ){
+        return rc;
+      }
+    }
+  }
+  return SQLITE_OK;
+}
+
+/*
+** 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 
+** saveCursorPosition().
+**
+** If the second argument argument - doSeek - is false, then instead of 
+** returning the cursor to it's saved position, any saved position is deleted
+** and the cursor state set to CURSOR_INVALID.
+*/
+static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
+  int rc = SQLITE_OK;
+  assert( pCur->eState==CURSOR_REQUIRESEEK );
+  pCur->eState = CURSOR_INVALID;
+  if( doSeek ){
+    rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
+  }
+  if( rc==SQLITE_OK ){
+    sqliteFree(pCur->pKey);
+    pCur->pKey = 0;
+    assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
+  }
+  return rc;
+}
+
+#define restoreOrClearCursorPosition(p,x) \
+  (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK)
+
 #ifndef SQLITE_OMIT_AUTOVACUUM
 /*
 ** These macros define the location of the pointer-map entry for a 
@@ -1591,9 +1582,9 @@ int sqlite3BtreeOpen(
   */
 #if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM)
   #ifdef SQLITE_OMIT_MEMORYDB
-  const int isMemdb = !zFilename;
+    const int isMemdb = 0;
   #else
-  const int isMemdb = !zFilename || (strcmp(zFilename, ":memory:")?0:1);
+    const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");
   #endif
 #endif
 
@@ -2778,7 +2769,7 @@ int sqlite3BtreeCursor(
     if( pBt->readOnly ){
       return SQLITE_READONLY;
     }
-    if( checkReadLocks(pBt, iTable, 0) ){
+    if( checkReadLocks(p, iTable, 0) ){
       return SQLITE_LOCKED;
     }
   }
@@ -5215,27 +5206,35 @@ static int balance(MemPage *pPage, int insert){
 
 /*
 ** This routine checks all cursors that point to table pgnoRoot.
-** If any of those cursors other than pExclude were opened with 
-** wrFlag==0 then this routine returns SQLITE_LOCKED.  If all
-** cursors that point to pgnoRoot were opened with wrFlag==1
-** then this routine returns SQLITE_OK.
+** If any of those cursors were opened with wrFlag==0 in a different
+** database connection (a database connection that shares the pager
+** cache with the current connection) and that other connection 
+** is not in the ReadUncommmitted state, then this routine returns 
+** SQLITE_LOCKED.
 **
 ** In addition to checking for read-locks (where a read-lock 
 ** means a cursor opened with wrFlag==0) this routine also moves
-** all cursors other than pExclude so that they are pointing to the 
-** first Cell on root page.  This is necessary because an insert 
+** all cursors write cursors so that they are pointing to the 
+** first Cell on the root page.  This is necessary because an insert 
 ** or delete might change the number of cells on a page or delete
 ** a page entirely and we do not want to leave any cursors 
 ** pointing to non-existant pages or cells.
 */
-static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
+static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){
   BtCursor *p;
+  BtShared *pBt = pBtree->pBt;
+  sqlite3 *db = pBtree->pSqlite;
   for(p=pBt->pCursor; p; p=p->pNext){
-    u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0);
-    if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
-    if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue;
-    if( p->wrFlag==0 ) return SQLITE_LOCKED;
-    if( p->pPage->pgno!=p->pgnoRoot ){
+    if( p==pExclude ) continue;
+    if( p->eState!=CURSOR_VALID ) continue;
+    if( p->pgnoRoot!=pgnoRoot ) continue;
+    if( p->wrFlag==0 ){
+      sqlite3 *dbOther = p->pBtree->pSqlite;
+      if( dbOther==0 ||
+         (dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){
+        return SQLITE_LOCKED;
+      }
+    }else if( p->pPage->pgno!=p->pgnoRoot ){
       moveToRoot(p);
     }
   }
@@ -5272,7 +5271,7 @@ int sqlite3BtreeInsert(
   if( !pCur->wrFlag ){
     return SQLITE_PERM;   /* Cursor not open for writing */
   }
-  if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+  if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
 
@@ -5354,7 +5353,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   if( !pCur->wrFlag ){
     return SQLITE_PERM;   /* Did not open this cursor for writing */
   }
-  if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+  if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
 
@@ -5631,25 +5630,13 @@ cleardatabasepage_out:
 */
 int sqlite3BtreeClearTable(Btree *p, int iTable){
   int rc;
-  BtCursor *pCur;
   BtShared *pBt = p->pBt;
-  sqlite3 *db = p->pSqlite;
   if( p->inTrans!=TRANS_WRITE ){
     return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
   }
-
-  /* If this connection is not in read-uncommitted mode and currently has
-  ** a read-cursor open on the table being cleared, return SQLITE_LOCKED.
-  */
-  if( 0==db || 0==(db->flags&SQLITE_ReadUncommitted) ){
-    for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
-      if( pCur->pBtree==p && pCur->pgnoRoot==(Pgno)iTable ){
-        if( 0==pCur->wrFlag ){
-          return SQLITE_LOCKED;
-        }
-        moveToRoot(pCur);
-      }
-    }
+  rc = checkReadLocks(p, iTable, 0);
+  if( rc ){
+    return rc;
   }
 
   /* Save the position of all cursors open on this table */
index ae1b78ffbe815ef9733907e511e3c2dfd50551e9..8d440aebde8302413dd68e7597e384b2cfc0e7e9 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script is btree database backend
 #
-# $Id: btree.test,v 1.36 2006/03/19 13:00:25 drh Exp $
+# $Id: btree.test,v 1.37 2006/08/16 16:42:48 drh Exp $
 
 
 set testdir [file dirname $argv0]
@@ -1018,22 +1018,30 @@ do_test btree-16.10 {
   catch {btree_delete $::c1} msg
   set msg
 } {SQLITE_PERM}
+
+# As of 2006-08-16 (version 3.3.7+) a read cursor will no
+# longer block a write cursor from the same database
+# connectiin.  The following three tests uses to return
+# the SQLITE_LOCK error, but no more.
+#
 do_test btree-16.11 {
   btree_close_cursor $::c1
   set ::c2 [btree_cursor $::b1 2 1]
   set ::c1 [btree_cursor $::b1 2 0]
   catch {btree_insert $::c2 101 helloworld} msg
   set msg
-} {SQLITE_LOCKED}
+} {}
 do_test btree-16.12 {
   btree_first $::c2
   catch {btree_delete $::c2} msg
   set msg
-} {SQLITE_LOCKED}
+} {}
 do_test btree-16.13 {
   catch {btree_clear_table $::b1 2} msg
   set msg
-} {SQLITE_LOCKED}
+} {}
+
+
 do_test btree-16.14 {
   btree_close_cursor $::c1
   btree_close_cursor $::c2
index 79283ac2cf9fba78f32a393f28913ef048285b28..dc7182cf3b3f71aa1883f40ae3f0263c95440e5a 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script testing the callback-free C/C++ API.
 #
-# $Id: capi2.test,v 1.31 2006/02/10 13:33:31 danielk1977 Exp $
+# $Id: capi2.test,v 1.32 2006/08/16 16:42:48 drh Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -470,9 +470,11 @@ do_test capi2-6.12 {
        [get_column_names $VM1]
 } {SQLITE_ROW 1 5 {x counter}}
 
-do_test capi2-6.13 {
-  catchsql {UPDATE t3 SET x=x+1}
-} {1 {database table is locked}}
+# A read no longer blocks a write in the same connection.
+#do_test capi2-6.13 {
+#  catchsql {UPDATE t3 SET x=x+1}
+#} {1 {database table is locked}}
+
 do_test capi2-6.14 {
   list [sqlite3_step $VM1] \
        [sqlite3_column_count $VM1] \
index 7d7a9bda69d81b0bbae0478d87d259d23a996aed..35b74680424533d290dae1b740c4063159681a70 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script testing the callback-free C/C++ API.
 #
-# $Id: capi3.test,v 1.45 2006/04/07 13:50:38 drh Exp $
+# $Id: capi3.test,v 1.46 2006/08/16 16:42:48 drh Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -912,8 +912,8 @@ do_test capi3-11.21 {
 
 # The following tests - capi3-12.* - check that it's Ok to start a
 # transaction while other VMs are active, and that it's Ok to execute
-# atomic updates in the same situation (so long as they are on a different
-# table).
+# atomic updates in the same situation 
+#
 do_test capi3-12.1 {
   set STMT [sqlite3_prepare $DB "SELECT a FROM t2" -1 TAIL]
   sqlite3_step $STMT
@@ -923,12 +923,11 @@ do_test capi3-12.2 {
     INSERT INTO t1 VALUES(3, NULL);
   }
 } {0 {}}
-
 do_test capi3-12.3 {
   catchsql {
     INSERT INTO t2 VALUES(4);
   }
-} {1 {database table is locked}}
+} {0 {}}
 do_test capi3-12.4 {
   catchsql {
     BEGIN;
@@ -938,6 +937,9 @@ do_test capi3-12.4 {
 do_test capi3-12.5 {
   sqlite3_step $STMT
 } {SQLITE_ROW}
+do_test capi3-12.5.1 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
 do_test capi3-12.6 {
   sqlite3_step $STMT
 } {SQLITE_DONE}
index f0e5d86a97f83e3ded67329c8ae84bee36ecc170..659cc5673529a5c58e5eea26f2b616c9b7cfc06b 100644 (file)
@@ -29,7 +29,7 @@
 # The solution to the problem was to detect that the table is locked
 # before the index entry is deleted.
 #
-# $Id: delete2.test,v 1.6 2006/01/05 11:34:34 danielk1977 Exp $
+# $Id: delete2.test,v 1.7 2006/08/16 16:42:48 drh Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -65,20 +65,20 @@ do_test delete2-1.4 {
 } SQLITE_ROW
 integrity_check delete2-1.5
 
-# Try to delete a row from the table. The delete should fail.
+# Try to delete a row from the table while a read is in process.
+# As of 2006-08-16, this is allowed.  (It used to fail with SQLITE_LOCKED.)
 #
-breakpoint
 do_test delete2-1.6 {
   catchsql {
     DELETE FROM q WHERE rowid=1
   }
-} {1 {database table is locked}}
+} {0 {}}
 integrity_check delete2-1.7
 do_test delete2-1.8 {
   execsql {
     SELECT * FROM q;
   }
-} {hello id.1 goodbye id.2 again id.3}
+} {goodbye id.2 again id.3}
 
 # Finalize the query, thus clearing the lock on the table.  Then
 # retry the delete.  The delete should work this time.
index c2f09eb196a893bfb75f9eac5f467e7c3c5412b7..e453ffd28bcb17398ed816432c556a9ba7b95f0c 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script is database locks.
 #
-# $Id: lock.test,v 1.32 2005/03/29 03:11:00 danielk1977 Exp $
+# $Id: lock.test,v 1.33 2006/08/16 16:42:48 drh Exp $
 
 
 set testdir [file dirname $argv0]
@@ -100,13 +100,17 @@ do_test lock-1.17 {
 
 # You cannot UPDATE a table from within the callback of a SELECT
 # on that same table because the SELECT has the table locked.
-do_test lock-1.18 {
-  db eval {SELECT * FROM t1} qv {
-    set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg]
-    lappend r $msg
-  }
-  set r
-} {1 {database table is locked}}
+#
+# 2006-08-16:  Reads no longer block writes within the same
+# database connection.
+#
+#do_test lock-1.18 {
+#  db eval {SELECT * FROM t1} qv {
+#    set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg]
+#    lappend r $msg
+#  }
+#  set r
+#} {1 {database table is locked}}
 
 # But you can UPDATE a different table from the one that is used in
 # the SELECT.
index 8271846ab79dcb41fd1ea80ab2903eafc1da258e..44cb47d58e4db0511e078868baffcd92c654c2a4 100644 (file)
@@ -13,7 +13,7 @@
 # This file implements tests for miscellanous features that were
 # left out of other test files.
 #
-# $Id: misc2.test,v 1.24 2006/01/17 09:35:02 danielk1977 Exp $
+# $Id: misc2.test,v 1.25 2006/08/16 16:42:48 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -145,6 +145,9 @@ do_test misc2-6.1 {
 # update a table from within the callback of a select on that same
 # table.
 #
+# 2006-08-16:  This has changed.  It is now permitted to update
+# the table being SELECTed from within the callback of the query.
+#
 do_test misc2-7.1 {
   db close
   file delete -force test.db
@@ -152,45 +155,94 @@ do_test misc2-7.1 {
   execsql {
     CREATE TABLE t1(x);
     INSERT INTO t1 VALUES(1);
+    INSERT INTO t1 VALUES(2);
+    INSERT INTO t1 VALUES(3);
+    SELECT * FROM t1;
   }
+} {1 2 3}
+do_test misc2-7.2 {
   set rc [catch {
     db eval {SELECT rowid FROM t1} {} {
       db eval "DELETE FROM t1 WHERE rowid=$rowid"
     }
   } msg]
   lappend rc $msg
-} {1 {database table is locked}}
-do_test misc2-7.2 {
-  set rc [catch {
-    db eval {SELECT rowid FROM t1} {} {
-      db eval "INSERT INTO t1 VALUES(3)"
+} {0 {}}
+do_test misc2-7.3 {
+  execsql {SELECT * FROM t1}
+} {}
+do_test misc2-7.4 {
+  execsql {
+    DELETE FROM t1;
+    INSERT INTO t1 VALUES(1);
+    INSERT INTO t1 VALUES(2);
+    INSERT INTO t1 VALUES(3);
+    INSERT INTO t1 VALUES(4);
+  }
+  db eval {SELECT rowid, x FROM t1} {
+    if {$x & 1} {
+      db eval {DELETE FROM t1 WHERE rowid=$rowid}
     }
-  } msg]
-  lappend rc $msg
-} {1 {database table is locked}}
-ifcapable memorydb {
-  do_test misc2-7.3 {
-    sqlite3 db :memory:
-    execsql {
-      CREATE TABLE t1(x);
-      INSERT INTO t1 VALUES(1);
+  }
+  execsql {SELECT * FROM t1}
+} {2 4}
+do_test misc2-7.5 {
+  execsql {
+    DELETE FROM t1;
+    INSERT INTO t1 VALUES(1);
+    INSERT INTO t1 VALUES(2);
+    INSERT INTO t1 VALUES(3);
+    INSERT INTO t1 VALUES(4);
+  }
+  db eval {SELECT rowid, x FROM t1} {
+    if {$x & 1} {
+      db eval {DELETE FROM t1 WHERE rowid=$rowid+1}
     }
-    set rc [catch {
-      db eval {SELECT rowid FROM t1} {} {
-        db eval "DELETE FROM t1 WHERE rowid=$rowid"
-      }
-    } msg]
-    lappend rc $msg
-  } {1 {database table is locked}}
-  do_test misc2-7.4 {
-    set rc [catch {
-      db eval {SELECT rowid FROM t1} {} {
-        db eval "INSERT INTO t1 VALUES(3)"
-      }
-    } msg]
-    lappend rc $msg
-  } {1 {database table is locked}}
-}
+  }
+  execsql {SELECT * FROM t1}
+} {1 3}
+do_test misc2-7.6 {
+  execsql {
+    DELETE FROM t1;
+    INSERT INTO t1 VALUES(1);
+    INSERT INTO t1 VALUES(2);
+    INSERT INTO t1 VALUES(3);
+    INSERT INTO t1 VALUES(4);
+  }
+  db eval {SELECT rowid, x FROM t1} {
+    if {$x & 1} {
+      db eval {DELETE FROM t1}
+    }
+  }
+  execsql {SELECT * FROM t1}
+} {}
+do_test misc2-7.7 {
+  execsql {
+    DELETE FROM t1;
+    INSERT INTO t1 VALUES(1);
+    INSERT INTO t1 VALUES(2);
+    INSERT INTO t1 VALUES(3);
+    INSERT INTO t1 VALUES(4);
+  }
+  db eval {SELECT rowid, x FROM t1} {
+    if {$x & 1} {
+      db eval {UPDATE t1 SET x=x+100 WHERE rowid=$rowid}
+    }
+  }
+  execsql {SELECT * FROM t1}
+} {101 2 103 4}
+do_test misc2-7.8 {
+  execsql {
+    DELETE FROM t1;
+    INSERT INTO t1 VALUES(1);
+  }
+  db eval {SELECT rowid, x FROM t1} {
+    if {$x<10} {
+      db eval {INSERT INTO t1 VALUES($x+1)}
+    }
+  }
+  execsql {SELECT * FROM t1}
+} {1 2 3 4 5 6 7 8 9 10}
 
 db close
 file delete -force test.db