]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Change sqlite3_step() to return SQLITE_LOCKED if a statement cannot be re-compiled...
authordanielk1977 <danielk1977@noemail.net>
Thu, 19 Mar 2009 07:58:31 +0000 (07:58 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 19 Mar 2009 07:58:31 +0000 (07:58 +0000)
FossilOrigin-Name: e8be1af922098e298902820730f8b28603bd6fae

manifest
manifest.uuid
src/prepare.c
src/test_thread.c
src/vdbeapi.c
test/notify2.test

index 001d6f791692c99881255ba5432e566179d0b90e..a251af1de7a096881285104dfe3c0dd0f62b7d0f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\scrash\sthat\scould\soccur\swhen\screating\san\sindex\sin\sshared-cache\smode\swith\slookaside\senabled.\s(CVS\s6358)
-D 2009-03-18T18:43:36
+C Change\ssqlite3_step()\sto\sreturn\sSQLITE_LOCKED\sif\sa\sstatement\scannot\sbe\sre-compiled\sdue\sto\slocks\son\sthe\sshared-cache\sschema.\sAlso\sadd\sa\sblocking\swrapper\sof\ssqlite3_prepare_v2()\sto\sthe\stest\scode.\s(CVS\s6359)
+D 2009-03-19T07:58:31
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 583e87706abc3026960ed759aff6371faf84c211
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -150,7 +150,7 @@ F src/pcache.c fcf7738c83c4d3e9d45836b2334c8a368cc41274
 F src/pcache.h 9b927ccc5a538e31b4c3bc7eec4f976db42a1324
 F src/pcache1.c f12518540ba776df3051215c4244e9cdc06b09cd
 F src/pragma.c 22ed04836aab8ce134c53be1ca896f3ad20fabdb
-F src/prepare.c ebd40f8c6a33c52bfea2db710394d6e82c7e594b
+F src/prepare.c 8c6ef35c30aa6b59a39b975efa4f90a66a5afb81
 F src/printf.c 9866a9a9c4a90f6d4147407f373df3fd5d5f9b6f
 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
 F src/resolve.c 094e44450371fb27869eb8bf679aacbe51fdc56d
@@ -192,7 +192,7 @@ F src/test_pcache.c 29464896d9c67832e4eef916c0682b98d7283d00
 F src/test_schema.c 4b4bf7bb329326458c491b0e6facd4c8c4c5b479
 F src/test_server.c f0a403b5f699c09bd2b1236b6f69830fd6221f6b
 F src/test_tclvar.c 9e42fa59d3d2f064b7ab8628e7ab2dc8a9fe93d4
-F src/test_thread.c 6805d05c3b7e08d51b31662b148cf2f51b9ca4cc
+F src/test_thread.c 870a862d9c740d083b93ed30a0b5c0b491b30f43
 F src/test_wsd.c c297d7d6b8a990239e1bd25935e81d612d8ae31d
 F src/tokenize.c 6987fb7f0d6a87ac53499aee568cabb05eb4bea8
 F src/trigger.c 21f39db410cdc32166a94900ac1b3df98ea560e6
@@ -203,7 +203,7 @@ F src/vacuum.c 4929a585ef0fb1dfaf46302f8a9c4aa30c2d9cf5
 F src/vdbe.c f8164c2830f82714a77b1f2a97c2e9c4efbcb3bb
 F src/vdbe.h d70a68bee196ab228914a3902c79dbd24342a0f2
 F src/vdbeInt.h 53a2f4696871712646c77351904576cca6ad9752
-F src/vdbeapi.c ffd5d8b493590da6e09fd54b1bea1a9d38247f11
+F src/vdbeapi.c e50f5f6dbd0c5354da25ee52fa5fa4bc0758ba40
 F src/vdbeaux.c e9b76cf2ca8f78b692be984381cc4b27defc902a
 F src/vdbeblob.c 2852bae14c87129835938db58a77c3121e3ae962
 F src/vdbemem.c 543a79d722734d2f8b7ad70f08218c30bcc5bbf5
@@ -479,7 +479,7 @@ F test/mutex1.test 1e5c196d5170bbe3a7d8370b1b905e8c86a9e07c
 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
 F test/nan.test c627d79b3d36ea892563fd67584b3e8a18f0618a
 F test/notify1.test 9a985a94f34de1b24daf25fd86b6d5033ba532d0
-F test/notify2.test 828511802c3e899d91f753771cefbe1d41dcb854
+F test/notify2.test 0c350ad8ff7d7d6ceb24ac84f33df0ce481f86e1
 F test/notnull.test 44d600f916b770def8b095a9962dbe3be5a70d82
 F test/null.test a8b09b8ed87852742343b33441a9240022108993
 F test/openv2.test f5dd6b23e4dce828eb211649b600763c42a668df
@@ -709,7 +709,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P d82e8cd43f46dda15fd15b567901a7dbb2276287
-R c8deeec50bc2cb39d7ab6042bd53185b
+P 097737e3689b9a7e32815fe9c6fc6eed796ae53c
+R 32a96214111535313729ca99d5be6662
 U danielk1977
-Z b4dedc49427fc75b7d301c2f3f28656d
+Z 02acbb631daf9f43302bd19b0b92f600
index a510e9165f557ecdc7571099a2b3632457e7a9d6..ac2aac9fc473fd5e8ad8dfe4e399ef619dea7d45 100644 (file)
@@ -1 +1 @@
-097737e3689b9a7e32815fe9c6fc6eed796ae53c
\ No newline at end of file
+e8be1af922098e298902820730f8b28603bd6fae
\ No newline at end of file
index 0142af557f148369acd99905754f5315c8324fc6..0b9e747dd793c7ab00e8088d1d388b75b44c24e8 100644 (file)
@@ -13,7 +13,7 @@
 ** interface, and routines that contribute to loading the database schema
 ** from disk.
 **
-** $Id: prepare.c,v 1.109 2009/03/16 13:19:36 danielk1977 Exp $
+** $Id: prepare.c,v 1.110 2009/03/19 07:58:31 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -688,8 +688,11 @@ static int sqlite3LockAndPrepare(
 
 /*
 ** Rerun the compilation of a statement after a schema change.
-** Return true if the statement was recompiled successfully.
-** Return false if there is an error of some kind.
+**
+** If the statement is successfully recompiled, return SQLITE_OK. Otherwise,
+** if the statement cannot be recompiled because another connection has
+** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error
+** occurs, return SQLITE_SCHEMA.
 */
 int sqlite3Reprepare(Vdbe *p){
   int rc;
@@ -708,7 +711,7 @@ int sqlite3Reprepare(Vdbe *p){
       db->mallocFailed = 1;
     }
     assert( pNew==0 );
-    return 0;
+    return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA;
   }else{
     assert( pNew!=0 );
   }
@@ -716,7 +719,7 @@ int sqlite3Reprepare(Vdbe *p){
   sqlite3TransferBindings(pNew, (sqlite3_stmt*)p);
   sqlite3VdbeResetStepResult((Vdbe*)pNew);
   sqlite3VdbeFinalize((Vdbe*)pNew);
-  return 1;
+  return SQLITE_OK;
 }
 
 
index 156f896ce8998ceac5559898480e3984b96ad264..20fcf73a2388014c38269a35e39757a102cf24d9 100644 (file)
@@ -14,7 +14,7 @@
 ** test that sqlite3 database handles may be concurrently accessed by 
 ** multiple threads. Right now this only works on unix.
 **
-** $Id: test_thread.c,v 1.11 2009/03/16 13:19:36 danielk1977 Exp $
+** $Id: test_thread.c,v 1.12 2009/03/19 07:58:31 danielk1977 Exp $
 */
 
 #include "sqliteInt.h"
@@ -56,8 +56,16 @@ struct EvalEvent {
 static Tcl_ObjCmdProc sqlthread_proc;
 static Tcl_ObjCmdProc clock_seconds_proc;
 static Tcl_ObjCmdProc blocking_step_proc;
+static Tcl_ObjCmdProc blocking_prepare_v2_proc;
 int Sqlitetest1_Init(Tcl_Interp *);
 
+/* Functions from test1.c */
+void *sqlite3TestTextToPtr(const char *);
+const char *sqlite3TestErrorName(int);
+int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
+int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
+int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
+
 /*
 ** Handler for events of type EvalEvent.
 */
@@ -109,6 +117,8 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
+  Tcl_CreateObjCommand(
+      interp, "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc,0,0);
 #endif
   Sqlitetest1_Init(interp);
   Sqlitetest_mutex_Init(interp);
@@ -396,7 +406,7 @@ struct UnlockNotification {
 /*
 ** This function is an unlock-notify callback registered with SQLite.
 */
-static void blocking_step_notify(void **apArg, int nArg){
+static void unlock_notify_cb(void **apArg, int nArg){
   int i;
   for(i=0; i<nArg; i++){
     UnlockNotification *p = (UnlockNotification *)apArg[i];
@@ -407,6 +417,56 @@ static void blocking_step_notify(void **apArg, int nArg){
   }
 }
 
+/*
+** This function assumes that an SQLite API call (either sqlite3_prepare_v2() 
+** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
+** associated database connection.
+**
+** This function calls sqlite3_unlock_notify() to register for an 
+** unlock-notify callback, then blocks until that callback is delivered 
+** and returns SQLITE_OK. The caller should then retry the failed operation.
+**
+** Or, if sqlite3_unlock_notify() indicates that to block would deadlock 
+** the system, then this function returns SQLITE_LOCKED immediately. In 
+** this case the caller should not retry the operation and should roll 
+** back the current transaction (if any).
+*/
+static int wait_for_unlock_notify(sqlite3 *db){
+  int rc;
+  UnlockNotification un;
+
+  /* Initialize the UnlockNotification structure. */
+  un.fired = 0;
+  pthread_mutex_init(&un.mutex, 0);
+  pthread_cond_init(&un.cond, 0);
+
+  /* Register for an unlock-notify callback. */
+  rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
+  assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
+
+  /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED 
+  ** or SQLITE_OK. 
+  **
+  ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
+  ** case this function needs to return SQLITE_LOCKED to the caller so 
+  ** that the current transaction can be rolled back. Otherwise, block
+  ** until the unlock-notify callback is invoked, then return SQLITE_OK.
+  */
+  if( rc==SQLITE_OK ){
+    pthread_mutex_lock(&un.mutex);
+    if( !un.fired ){
+      pthread_cond_wait(&un.cond, &un.mutex);
+    }
+    pthread_mutex_unlock(&un.mutex);
+  }
+
+  /* Destroy the mutex and condition variables. */
+  pthread_cond_destroy(&un.cond);
+  pthread_mutex_destroy(&un.mutex);
+
+  return rc;
+}
+
 /*
 ** This function is a wrapper around the SQLite function sqlite3_step().
 ** It functions in the same way as step(), except that if a required
@@ -419,53 +479,38 @@ static void blocking_step_notify(void **apArg, int nArg){
 ** system may become deadlocked.
 */
 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
-  int rc = SQLITE_OK;
-
-  while( rc==SQLITE_OK && SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
-    sqlite3 *db = sqlite3_db_handle(pStmt);
-    UnlockNotification un;
-
-    /* Initialize the UnlockNotification structure. */
-    un.fired = 0;
-    pthread_mutex_init(&un.mutex, 0);
-    pthread_cond_init(&un.cond, 0);
-
-    rc = sqlite3_unlock_notify(db, blocking_step_notify, (void *)&un);
-    assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
-
-    /* The call to sqlite3_unlock_notify() always returns either 
-    ** SQLITE_LOCKED or SQLITE_OK. 
-    **
-    ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
-    ** case this function needs to return SQLITE_LOCKED to the caller so 
-    ** that it can roll back the current transaction. Simply leaving rc
-    ** as it is is enough to accomplish that, as the next test of the
-    ** while() condition above will fail and the current value of rc
-    ** (SQLITE_LOCKED) will be returned to the caller. sqlite3_reset() is
-    ** not called on the statement handle, so the caller can still use either
-    ** sqlite3_finalize() or reset() to collect the statement's error code
-    ** after this function returns.
-    **
-    ** Otherwise, if SQLITE_OK was returned, do two things:
-    **
-    **   1) Reset the SQL statement.
-    **   2) Block until the unlock-notify callback is invoked.
-    */
-    if( rc==SQLITE_OK ){
-      sqlite3_reset(pStmt);
-      pthread_mutex_lock(&un.mutex);
-      if( !un.fired ){
-        pthread_cond_wait(&un.cond, &un.mutex);
-      }
-      pthread_mutex_unlock(&un.mutex);
-    }
-
-    /* Destroy the mutex and condition variables created at the top of
-    ** the while loop. */
-    pthread_cond_destroy(&un.cond);
-    pthread_mutex_destroy(&un.mutex);
+  int rc;
+  while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
+    rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
+    if( rc!=SQLITE_OK ) break;
+    sqlite3_reset(pStmt);
   }
+  return rc;
+}
 
+/*
+** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
+** It functions in the same way as prepare_v2(), except that if a required
+** shared-cache lock cannot be obtained, this function may block waiting for
+** the lock to become available. In this scenario the normal API prepare_v2()
+** function always returns SQLITE_LOCKED.
+**
+** If this function returns SQLITE_LOCKED, the caller should rollback
+** the current transaction (if any) and try again later. Otherwise, the
+** system may become deadlocked.
+*/
+int sqlite3_blocking_prepare_v2(
+  sqlite3 *db,              /* Database handle. */
+  const char *zSql,         /* UTF-8 encoded SQL statement. */
+  int nSql,                 /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
+  const char **pz           /* OUT: End of parsed string */
+){
+  int rc;
+  while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
+    rc = wait_for_unlock_notify(db);
+    if( rc!=SQLITE_OK ) break;
+  }
   return rc;
 }
 /* END_SQLITE_BLOCKING_STEP */
@@ -481,9 +526,6 @@ static int blocking_step_proc(
   int objc,
   Tcl_Obj *CONST objv[]
 ){
-  /* Functions from test1.c */
-  void *sqlite3TestTextToPtr(const char *);
-  const char *sqlite3TestErrorName(int);
 
   sqlite3_stmt *pStmt;
   int rc;
@@ -500,6 +542,54 @@ static int blocking_step_proc(
   return TCL_OK;
 }
 
+/*
+** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
+*/
+static int blocking_prepare_v2_proc(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  sqlite3 *db;
+  const char *zSql;
+  int bytes;
+  const char *zTail = 0;
+  sqlite3_stmt *pStmt = 0;
+  char zBuf[50];
+  int rc;
+
+  if( objc!=5 && objc!=4 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", 
+       Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+  zSql = Tcl_GetString(objv[2]);
+  if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+  rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, objc>=5?&zTail : 0);
+  assert(rc==SQLITE_OK || pStmt==0);
+  if( zTail && objc>=5 ){
+    if( bytes>=0 ){
+      bytes = bytes - (zTail-zSql);
+    }
+    Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
+  }
+  if( rc!=SQLITE_OK ){
+    assert( pStmt==0 );
+    sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc));
+    Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
+    return TCL_ERROR;
+  }
+
+  if( pStmt ){
+    if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+    Tcl_AppendResult(interp, zBuf, 0);
+  }
+  return TCL_OK;
+}
+
 #endif
 /*
 ** End of implementation of [sqlite3_blocking_step].
@@ -513,6 +603,8 @@ int SqlitetestThread_Init(Tcl_Interp *interp){
   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
 #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
+  Tcl_CreateObjCommand(
+      interp, "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc,0,0);
 #endif
   return TCL_OK;
 }
index 91d85ecf27fc93fba892944526d9db7390fc513b..f0c0e0674c1f22164ccefa0086ff4f51562b3bc6 100644 (file)
@@ -13,7 +13,7 @@
 ** This file contains code use to implement APIs that are part of the
 ** VDBE.
 **
-** $Id: vdbeapi.c,v 1.153 2009/03/05 04:23:47 shane Exp $
+** $Id: vdbeapi.c,v 1.154 2009/03/19 07:58:31 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "vdbeInt.h"
@@ -544,7 +544,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
     sqlite3_mutex_enter(db->mutex);
     while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
            && cnt++ < 5
-           && vdbeReprepare(v) ){
+           && (rc = vdbeReprepare(v))==SQLITE_OK ){
       sqlite3_reset(pStmt);
       v->expired = 0;
     }
index c6df6e53aa3b28ce10d0c98dd86ffa95afa4f49e..8b508b2bc47497f0ec6b37320dc9ef96b4724e11 100644 (file)
@@ -9,7 +9,7 @@
 #
 #***********************************************************************
 #
-# $Id: notify2.test,v 1.1 2009/03/16 13:19:36 danielk1977 Exp $
+# $Id: notify2.test,v 1.2 2009/03/19 07:58:31 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -63,7 +63,7 @@ set ThreadProgram {
     set rc SQLITE_OK
 
     while {$rc=="SQLITE_OK" && $zSql ne ""} {
-      set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql]
+      set STMT [$::xPrepare $db $zSql -1 zSql]
       while {[set rc [$::xStep $STMT]] eq "SQLITE_ROW"} {
         for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} {
           lappend lRes [sqlite3_column_text $STMT 0]
@@ -99,14 +99,22 @@ set ThreadProgram {
     # each operation.
     #
     for {set ii 1} {$ii <= 3} {incr ii} {
-      set SQL($ii) [string map [list xxx [select_one t1 t2 t3]] [select_one {
+      foreach {tbl database} [select_one {t1 main} {t2 aux2} {t3 aux3}] {}
+
+      set SQL($ii) [string map [list xxx $tbl yyy $database] [select_one {
             SELECT 
               (SELECT b FROM xxx WHERE a=(SELECT max(a) FROM xxx))==total(a) 
               FROM xxx WHERE a!=(SELECT max(a) FROM xxx);
       } {
             DELETE FROM xxx WHERE a<(SELECT max(a)-100 FROM xxx);
             INSERT INTO xxx SELECT NULL, total(a) FROM xxx;
-      }]]
+      } 
+#      {
+#            CREATE INDEX IF NOT EXISTS yyy.xxx_i ON xxx(b);
+#      } {
+#            DROP INDEX IF EXISTS yyy.xxx_i;
+#      }
+      ]]
     }
 
     # Execute the SQL transaction.
@@ -140,10 +148,13 @@ set ThreadProgram {
   expr 0
 }
 
-foreach {iTest xStep} {1 sqlite3_blocking_step 2 sqlite3_step} {
+foreach {iTest xStep xPrepare} {
+  1 sqlite3_blocking_step sqlite3_blocking_prepare_v2
+  2 sqlite3_step          sqlite3_prepare_v2
+} {
   file delete -force test.db test2.db test3.db
 
-  set ThreadSetup "set xStep $xStep ; set nSecond $nSecond"
+  set ThreadSetup "set xStep $xStep;set xPrepare $xPrepare;set nSecond $nSecond"
 
   # Set up the database schema used by this test. Each thread opens file
   # test.db as the main database, then attaches files test2.db and test3.db