/ Check-in [e8be1af9]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Change sqlite3_step() to return SQLITE_LOCKED if a statement cannot be re-compiled due to locks on the shared-cache schema. Also add a blocking wrapper of sqlite3_prepare_v2() to the test code. (CVS 6359)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:e8be1af922098e298902820730f8b28603bd6fae
User & Date: danielk1977 2009-03-19 07:58:31
Context
2009-03-19
18:51
Fix a couple of fairly obscure cases where an assert() could fail following a malloc failure. (CVS 6360) check-in: cc0d9256 user: danielk1977 tags: trunk
07:58
Change sqlite3_step() to return SQLITE_LOCKED if a statement cannot be re-compiled due to locks on the shared-cache schema. Also add a blocking wrapper of sqlite3_prepare_v2() to the test code. (CVS 6359) check-in: e8be1af9 user: danielk1977 tags: trunk
2009-03-18
18:43
Fix a crash that could occur when creating an index in shared-cache mode with lookaside enabled. (CVS 6358) check-in: 097737e3 user: danielk1977 tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/prepare.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains the implementation of the sqlite3_prepare()
    13     13   ** interface, and routines that contribute to loading the database schema
    14     14   ** from disk.
    15     15   **
    16         -** $Id: prepare.c,v 1.109 2009/03/16 13:19:36 danielk1977 Exp $
           16  +** $Id: prepare.c,v 1.110 2009/03/19 07:58:31 danielk1977 Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   
    20     20   /*
    21     21   ** Fill the InitData structure with an error message that indicates
    22     22   ** that the database is corrupt.
    23     23   */
................................................................................
   684    684     sqlite3BtreeLeaveAll(db);
   685    685     sqlite3_mutex_leave(db->mutex);
   686    686     return rc;
   687    687   }
   688    688   
   689    689   /*
   690    690   ** Rerun the compilation of a statement after a schema change.
   691         -** Return true if the statement was recompiled successfully.
   692         -** Return false if there is an error of some kind.
          691  +**
          692  +** If the statement is successfully recompiled, return SQLITE_OK. Otherwise,
          693  +** if the statement cannot be recompiled because another connection has
          694  +** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error
          695  +** occurs, return SQLITE_SCHEMA.
   693    696   */
   694    697   int sqlite3Reprepare(Vdbe *p){
   695    698     int rc;
   696    699     sqlite3_stmt *pNew;
   697    700     const char *zSql;
   698    701     sqlite3 *db;
   699    702   
................................................................................
   704    707     assert( sqlite3_mutex_held(db->mutex) );
   705    708     rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0);
   706    709     if( rc ){
   707    710       if( rc==SQLITE_NOMEM ){
   708    711         db->mallocFailed = 1;
   709    712       }
   710    713       assert( pNew==0 );
   711         -    return 0;
          714  +    return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA;
   712    715     }else{
   713    716       assert( pNew!=0 );
   714    717     }
   715    718     sqlite3VdbeSwap((Vdbe*)pNew, p);
   716    719     sqlite3TransferBindings(pNew, (sqlite3_stmt*)p);
   717    720     sqlite3VdbeResetStepResult((Vdbe*)pNew);
   718    721     sqlite3VdbeFinalize((Vdbe*)pNew);
   719         -  return 1;
          722  +  return SQLITE_OK;
   720    723   }
   721    724   
   722    725   
   723    726   /*
   724    727   ** Two versions of the official API.  Legacy and new use.  In the legacy
   725    728   ** version, the original SQL text is not saved in the prepared statement
   726    729   ** and so if a schema change occurs, SQLITE_SCHEMA is returned by

Changes to src/test_thread.c.

    10     10   **
    11     11   *************************************************************************
    12     12   **
    13     13   ** This file contains the implementation of some Tcl commands used to
    14     14   ** test that sqlite3 database handles may be concurrently accessed by 
    15     15   ** multiple threads. Right now this only works on unix.
    16     16   **
    17         -** $Id: test_thread.c,v 1.11 2009/03/16 13:19:36 danielk1977 Exp $
           17  +** $Id: test_thread.c,v 1.12 2009/03/19 07:58:31 danielk1977 Exp $
    18     18   */
    19     19   
    20     20   #include "sqliteInt.h"
    21     21   #include <tcl.h>
    22     22   
    23     23   #if SQLITE_THREADSAFE
    24     24   
................................................................................
    52     52     char *zScript;           /* The script to execute. */
    53     53     Tcl_Interp *interp;      /* The interpreter to execute it in. */
    54     54   };
    55     55   
    56     56   static Tcl_ObjCmdProc sqlthread_proc;
    57     57   static Tcl_ObjCmdProc clock_seconds_proc;
    58     58   static Tcl_ObjCmdProc blocking_step_proc;
           59  +static Tcl_ObjCmdProc blocking_prepare_v2_proc;
    59     60   int Sqlitetest1_Init(Tcl_Interp *);
    60     61   
           62  +/* Functions from test1.c */
           63  +void *sqlite3TestTextToPtr(const char *);
           64  +const char *sqlite3TestErrorName(int);
           65  +int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
           66  +int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
           67  +int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
           68  +
    61     69   /*
    62     70   ** Handler for events of type EvalEvent.
    63     71   */
    64     72   static int tclScriptEvent(Tcl_Event *evPtr, int flags){
    65     73     int rc;
    66     74     EvalEvent *p = (EvalEvent *)evPtr;
    67     75     rc = Tcl_Eval(p->interp, p->zScript);
................................................................................
   105    113     extern int Sqlitetest_mutex_Init(Tcl_Interp*);
   106    114   
   107    115     interp = Tcl_CreateInterp();
   108    116     Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
   109    117     Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
   110    118   #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
   111    119     Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
          120  +  Tcl_CreateObjCommand(
          121  +      interp, "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc,0,0);
   112    122   #endif
   113    123     Sqlitetest1_Init(interp);
   114    124     Sqlitetest_mutex_Init(interp);
   115    125   
   116    126     rc = Tcl_Eval(interp, p->zScript);
   117    127     pRes = Tcl_GetObjResult(interp);
   118    128     pList = Tcl_NewObj();
................................................................................
   392    402     pthread_cond_t cond;                 /* Condition variable to wait on */
   393    403     pthread_mutex_t mutex;               /* Mutex to protect structure */
   394    404   };
   395    405   
   396    406   /*
   397    407   ** This function is an unlock-notify callback registered with SQLite.
   398    408   */
   399         -static void blocking_step_notify(void **apArg, int nArg){
          409  +static void unlock_notify_cb(void **apArg, int nArg){
   400    410     int i;
   401    411     for(i=0; i<nArg; i++){
   402    412       UnlockNotification *p = (UnlockNotification *)apArg[i];
   403    413       pthread_mutex_lock(&p->mutex);
   404    414       p->fired = 1;
   405    415       pthread_cond_signal(&p->cond);
   406    416       pthread_mutex_unlock(&p->mutex);
   407    417     }
   408    418   }
          419  +
          420  +/*
          421  +** This function assumes that an SQLite API call (either sqlite3_prepare_v2() 
          422  +** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
          423  +** associated database connection.
          424  +**
          425  +** This function calls sqlite3_unlock_notify() to register for an 
          426  +** unlock-notify callback, then blocks until that callback is delivered 
          427  +** and returns SQLITE_OK. The caller should then retry the failed operation.
          428  +**
          429  +** Or, if sqlite3_unlock_notify() indicates that to block would deadlock 
          430  +** the system, then this function returns SQLITE_LOCKED immediately. In 
          431  +** this case the caller should not retry the operation and should roll 
          432  +** back the current transaction (if any).
          433  +*/
          434  +static int wait_for_unlock_notify(sqlite3 *db){
          435  +  int rc;
          436  +  UnlockNotification un;
          437  +
          438  +  /* Initialize the UnlockNotification structure. */
          439  +  un.fired = 0;
          440  +  pthread_mutex_init(&un.mutex, 0);
          441  +  pthread_cond_init(&un.cond, 0);
          442  +
          443  +  /* Register for an unlock-notify callback. */
          444  +  rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
          445  +  assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
          446  +
          447  +  /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED 
          448  +  ** or SQLITE_OK. 
          449  +  **
          450  +  ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
          451  +  ** case this function needs to return SQLITE_LOCKED to the caller so 
          452  +  ** that the current transaction can be rolled back. Otherwise, block
          453  +  ** until the unlock-notify callback is invoked, then return SQLITE_OK.
          454  +  */
          455  +  if( rc==SQLITE_OK ){
          456  +    pthread_mutex_lock(&un.mutex);
          457  +    if( !un.fired ){
          458  +      pthread_cond_wait(&un.cond, &un.mutex);
          459  +    }
          460  +    pthread_mutex_unlock(&un.mutex);
          461  +  }
          462  +
          463  +  /* Destroy the mutex and condition variables. */
          464  +  pthread_cond_destroy(&un.cond);
          465  +  pthread_mutex_destroy(&un.mutex);
          466  +
          467  +  return rc;
          468  +}
   409    469   
   410    470   /*
   411    471   ** This function is a wrapper around the SQLite function sqlite3_step().
   412    472   ** It functions in the same way as step(), except that if a required
   413    473   ** shared-cache lock cannot be obtained, this function may block waiting for
   414    474   ** the lock to become available. In this scenario the normal API step()
   415    475   ** function always returns SQLITE_LOCKED.
   416    476   **
   417    477   ** If this function returns SQLITE_LOCKED, the caller should rollback
   418    478   ** the current transaction (if any) and try again later. Otherwise, the
   419    479   ** system may become deadlocked.
   420    480   */
   421    481   int sqlite3_blocking_step(sqlite3_stmt *pStmt){
   422         -  int rc = SQLITE_OK;
   423         -
   424         -  while( rc==SQLITE_OK && SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
   425         -    sqlite3 *db = sqlite3_db_handle(pStmt);
   426         -    UnlockNotification un;
   427         -
   428         -    /* Initialize the UnlockNotification structure. */
   429         -    un.fired = 0;
   430         -    pthread_mutex_init(&un.mutex, 0);
   431         -    pthread_cond_init(&un.cond, 0);
          482  +  int rc;
          483  +  while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
          484  +    rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
          485  +    if( rc!=SQLITE_OK ) break;
          486  +    sqlite3_reset(pStmt);
          487  +  }
          488  +  return rc;
          489  +}
   432    490   
   433         -    rc = sqlite3_unlock_notify(db, blocking_step_notify, (void *)&un);
   434         -    assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
   435         -
   436         -    /* The call to sqlite3_unlock_notify() always returns either 
   437         -    ** SQLITE_LOCKED or SQLITE_OK. 
   438         -    **
   439         -    ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
   440         -    ** case this function needs to return SQLITE_LOCKED to the caller so 
   441         -    ** that it can roll back the current transaction. Simply leaving rc
   442         -    ** as it is is enough to accomplish that, as the next test of the
   443         -    ** while() condition above will fail and the current value of rc
   444         -    ** (SQLITE_LOCKED) will be returned to the caller. sqlite3_reset() is
   445         -    ** not called on the statement handle, so the caller can still use either
   446         -    ** sqlite3_finalize() or reset() to collect the statement's error code
   447         -    ** after this function returns.
   448         -    **
   449         -    ** Otherwise, if SQLITE_OK was returned, do two things:
   450         -    **
   451         -    **   1) Reset the SQL statement.
   452         -    **   2) Block until the unlock-notify callback is invoked.
   453         -    */
   454         -    if( rc==SQLITE_OK ){
   455         -      sqlite3_reset(pStmt);
   456         -      pthread_mutex_lock(&un.mutex);
   457         -      if( !un.fired ){
   458         -        pthread_cond_wait(&un.cond, &un.mutex);
   459         -      }
   460         -      pthread_mutex_unlock(&un.mutex);
   461         -    }
   462         -
   463         -    /* Destroy the mutex and condition variables created at the top of
   464         -    ** the while loop. */
   465         -    pthread_cond_destroy(&un.cond);
   466         -    pthread_mutex_destroy(&un.mutex);
   467         -  }
   468         -
          491  +/*
          492  +** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
          493  +** It functions in the same way as prepare_v2(), except that if a required
          494  +** shared-cache lock cannot be obtained, this function may block waiting for
          495  +** the lock to become available. In this scenario the normal API prepare_v2()
          496  +** function always returns SQLITE_LOCKED.
          497  +**
          498  +** If this function returns SQLITE_LOCKED, the caller should rollback
          499  +** the current transaction (if any) and try again later. Otherwise, the
          500  +** system may become deadlocked.
          501  +*/
          502  +int sqlite3_blocking_prepare_v2(
          503  +  sqlite3 *db,              /* Database handle. */
          504  +  const char *zSql,         /* UTF-8 encoded SQL statement. */
          505  +  int nSql,                 /* Length of zSql in bytes. */
          506  +  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
          507  +  const char **pz           /* OUT: End of parsed string */
          508  +){
          509  +  int rc;
          510  +  while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
          511  +    rc = wait_for_unlock_notify(db);
          512  +    if( rc!=SQLITE_OK ) break;
          513  +  }
   469    514     return rc;
   470    515   }
   471    516   /* END_SQLITE_BLOCKING_STEP */
   472    517   
   473    518   /*
   474    519   ** Usage: sqlite3_blocking_step STMT
   475    520   **
................................................................................
   477    522   */
   478    523   static int blocking_step_proc(
   479    524     void * clientData,
   480    525     Tcl_Interp *interp,
   481    526     int objc,
   482    527     Tcl_Obj *CONST objv[]
   483    528   ){
   484         -  /* Functions from test1.c */
   485         -  void *sqlite3TestTextToPtr(const char *);
   486         -  const char *sqlite3TestErrorName(int);
   487    529   
   488    530     sqlite3_stmt *pStmt;
   489    531     int rc;
   490    532   
   491    533     if( objc!=2 ){
   492    534       Tcl_WrongNumArgs(interp, 1, objv, "STMT");
   493    535       return TCL_ERROR;
................................................................................
   495    537   
   496    538     pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
   497    539     rc = sqlite3_blocking_step(pStmt);
   498    540   
   499    541     Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0);
   500    542     return TCL_OK;
   501    543   }
          544  +
          545  +/*
          546  +** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
          547  +*/
          548  +static int blocking_prepare_v2_proc(
          549  +  void * clientData,
          550  +  Tcl_Interp *interp,
          551  +  int objc,
          552  +  Tcl_Obj *CONST objv[]
          553  +){
          554  +  sqlite3 *db;
          555  +  const char *zSql;
          556  +  int bytes;
          557  +  const char *zTail = 0;
          558  +  sqlite3_stmt *pStmt = 0;
          559  +  char zBuf[50];
          560  +  int rc;
          561  +
          562  +  if( objc!=5 && objc!=4 ){
          563  +    Tcl_AppendResult(interp, "wrong # args: should be \"", 
          564  +       Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
          565  +    return TCL_ERROR;
          566  +  }
          567  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
          568  +  zSql = Tcl_GetString(objv[2]);
          569  +  if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
          570  +
          571  +  rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, objc>=5?&zTail : 0);
          572  +  assert(rc==SQLITE_OK || pStmt==0);
          573  +  if( zTail && objc>=5 ){
          574  +    if( bytes>=0 ){
          575  +      bytes = bytes - (zTail-zSql);
          576  +    }
          577  +    Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
          578  +  }
          579  +  if( rc!=SQLITE_OK ){
          580  +    assert( pStmt==0 );
          581  +    sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc));
          582  +    Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
          583  +    return TCL_ERROR;
          584  +  }
          585  +
          586  +  if( pStmt ){
          587  +    if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
          588  +    Tcl_AppendResult(interp, zBuf, 0);
          589  +  }
          590  +  return TCL_OK;
          591  +}
   502    592   
   503    593   #endif
   504    594   /*
   505    595   ** End of implementation of [sqlite3_blocking_step].
   506    596   ************************************************************************/
   507    597   
   508    598   /*
................................................................................
   509    599   ** Register commands with the TCL interpreter.
   510    600   */
   511    601   int SqlitetestThread_Init(Tcl_Interp *interp){
   512    602     Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
   513    603     Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
   514    604   #if defined(OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
   515    605     Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
          606  +  Tcl_CreateObjCommand(
          607  +      interp, "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc,0,0);
   516    608   #endif
   517    609     return TCL_OK;
   518    610   }
   519    611   #else
   520    612   int SqlitetestThread_Init(Tcl_Interp *interp){
   521    613     return TCL_OK;
   522    614   }
   523    615   #endif

Changes to src/vdbeapi.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   **
    13     13   ** This file contains code use to implement APIs that are part of the
    14     14   ** VDBE.
    15     15   **
    16         -** $Id: vdbeapi.c,v 1.153 2009/03/05 04:23:47 shane Exp $
           16  +** $Id: vdbeapi.c,v 1.154 2009/03/19 07:58:31 danielk1977 Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "vdbeInt.h"
    20     20   
    21     21   #if 0 && defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
    22     22   /*
    23     23   ** The following structure contains pointers to the end points of a
................................................................................
   540    540     if( pStmt ){
   541    541       int cnt = 0;
   542    542       Vdbe *v = (Vdbe*)pStmt;
   543    543       sqlite3 *db = v->db;
   544    544       sqlite3_mutex_enter(db->mutex);
   545    545       while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
   546    546              && cnt++ < 5
   547         -           && vdbeReprepare(v) ){
          547  +           && (rc = vdbeReprepare(v))==SQLITE_OK ){
   548    548         sqlite3_reset(pStmt);
   549    549         v->expired = 0;
   550    550       }
   551    551       if( rc==SQLITE_SCHEMA && v->isPrepareV2 && db->pErr ){
   552    552         /* This case occurs after failing to recompile an sql statement. 
   553    553         ** The error message from the SQL compiler has already been loaded 
   554    554         ** into the database handle. This block copies the error message 

Changes to test/notify2.test.

     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12         -# $Id: notify2.test,v 1.1 2009/03/16 13:19:36 danielk1977 Exp $
           12  +# $Id: notify2.test,v 1.2 2009/03/19 07:58:31 danielk1977 Exp $
    13     13   
    14     14   set testdir [file dirname $argv0]
    15     15   source $testdir/tester.tcl
    16     16   
    17     17   # The tests in this file test the sqlite3_blocking_step() function in
    18     18   # test_thread.c. sqlite3_blocking_step() is not an SQLite API function,
    19     19   # it is just a demonstration of how the sqlite3_unlock_notify() function
................................................................................
    59     59     # Proc used by threads to execute SQL.
    60     60     #
    61     61     proc execsql_blocking {db zSql} {
    62     62       set lRes [list]
    63     63       set rc SQLITE_OK
    64     64   
    65     65       while {$rc=="SQLITE_OK" && $zSql ne ""} {
    66         -      set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql]
           66  +      set STMT [$::xPrepare $db $zSql -1 zSql]
    67     67         while {[set rc [$::xStep $STMT]] eq "SQLITE_ROW"} {
    68     68           for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} {
    69     69             lappend lRes [sqlite3_column_text $STMT 0]
    70     70           }
    71     71         }
    72     72         set rc [sqlite3_finalize $STMT]
    73     73       }
................................................................................
    95     95   
    96     96       # Each transaction does 3 operations. Each operation is either a read
    97     97       # or write of a randomly selected table (t1, t2 or t3). Set the variables
    98     98       # $SQL(1), $SQL(2) and $SQL(3) to the SQL commands used to implement
    99     99       # each operation.
   100    100       #
   101    101       for {set ii 1} {$ii <= 3} {incr ii} {
   102         -      set SQL($ii) [string map [list xxx [select_one t1 t2 t3]] [select_one {
          102  +      foreach {tbl database} [select_one {t1 main} {t2 aux2} {t3 aux3}] {}
          103  +
          104  +      set SQL($ii) [string map [list xxx $tbl yyy $database] [select_one {
   103    105               SELECT 
   104    106                 (SELECT b FROM xxx WHERE a=(SELECT max(a) FROM xxx))==total(a) 
   105    107                 FROM xxx WHERE a!=(SELECT max(a) FROM xxx);
   106    108         } {
   107    109               DELETE FROM xxx WHERE a<(SELECT max(a)-100 FROM xxx);
   108    110               INSERT INTO xxx SELECT NULL, total(a) FROM xxx;
   109         -      }]]
          111  +      } 
          112  +#      {
          113  +#            CREATE INDEX IF NOT EXISTS yyy.xxx_i ON xxx(b);
          114  +#      } {
          115  +#            DROP INDEX IF EXISTS yyy.xxx_i;
          116  +#      }
          117  +      ]]
   110    118       }
   111    119   
   112    120       # Execute the SQL transaction.
   113    121       #
   114    122       set rc [catch { execsql_blocking $::DB "
   115    123           BEGIN;
   116    124             $SQL(1);
................................................................................
   136    144   
   137    145     # Close the database connection and return 0.
   138    146     #
   139    147     sqlite3_close $::DB
   140    148     expr 0
   141    149   }
   142    150   
   143         -foreach {iTest xStep} {1 sqlite3_blocking_step 2 sqlite3_step} {
          151  +foreach {iTest xStep xPrepare} {
          152  +  1 sqlite3_blocking_step sqlite3_blocking_prepare_v2
          153  +  2 sqlite3_step          sqlite3_prepare_v2
          154  +} {
   144    155     file delete -force test.db test2.db test3.db
   145    156   
   146         -  set ThreadSetup "set xStep $xStep ; set nSecond $nSecond"
          157  +  set ThreadSetup "set xStep $xStep;set xPrepare $xPrepare;set nSecond $nSecond"
   147    158   
   148    159     # Set up the database schema used by this test. Each thread opens file
   149    160     # test.db as the main database, then attaches files test2.db and test3.db
   150    161     # as auxillary databases. Each file contains a single table (t1, t2 and t3, in
   151    162     # files test.db, test2.db and test3.db, respectively). 
   152    163     #
   153    164     do_test notify2-$iTest.1.1 {