Do not abort running queries due to a CREATE INDEX statement. Allow them
to run to completion before being reprepared. Fix for ticket
[c694113e50321afdf9].
FossilOrigin-Name: 2bd593332da0aade467e7a4ee89e966aa6302f37540a2c5e23671f98a6cb599c
diff --git a/src/auth.c b/src/auth.c
index 03e4cf9..a708d0c 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -78,7 +78,7 @@
sqlite3_mutex_enter(db->mutex);
db->xAuth = (sqlite3_xauth)xAuth;
db->pAuthArg = pArg;
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
diff --git a/src/build.c b/src/build.c
index 6b761da..af899d2 100644
--- a/src/build.c
+++ b/src/build.c
@@ -3468,7 +3468,7 @@
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
- sqlite3VdbeAddOp0(v, OP_Expire);
+ sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
}
sqlite3VdbeJumpHere(v, pIndex->tnum);
diff --git a/src/main.c b/src/main.c
index 9c10cab..07f6688 100644
--- a/src/main.c
+++ b/src/main.c
@@ -849,7 +849,7 @@
db->flags &= ~aFlagOp[i].mask;
}
if( oldFlags!=db->flags ){
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
}
if( pRes ){
*pRes = (db->flags & aFlagOp[i].mask)!=0;
@@ -1292,7 +1292,7 @@
sqlite3EndBenignMalloc();
if( schemaChange ){
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
sqlite3ResetAllSchemasOfConnection(db);
}
sqlite3BtreeLeaveAll(db);
@@ -1747,7 +1747,7 @@
assert( !db->mallocFailed );
return SQLITE_BUSY;
}else{
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
}
}
@@ -2522,7 +2522,7 @@
"unable to delete/modify collation sequence due to active statements");
return SQLITE_BUSY;
}
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
/* If collation sequence pColl was created directly by a call to
** sqlite3_create_collation, and not generated by synthCollSeq(),
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index f84cd78..2c52dfe 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -4223,7 +4223,7 @@
void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
int sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...);
-void sqlite3ExpirePreparedStatements(sqlite3*);
+void sqlite3ExpirePreparedStatements(sqlite3*, int);
int sqlite3CodeSubselect(Parse*, Expr *, int, int);
void sqlite3SelectPrep(Parse*, Select*, NameContext*);
void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
diff --git a/src/vdbe.c b/src/vdbe.c
index f9c7eba..39b3079 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -3102,7 +3102,7 @@
}
}
if( isSchemaChange ){
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
sqlite3ResetAllSchemasOfConnection(db);
db->mDbFlags |= DBFLAG_SchemaChange;
}
@@ -3391,7 +3391,7 @@
if( pOp->p1==1 ){
/* Invalidate all prepared statements whenever the TEMP database
** schema is changed. Ticket #1644 */
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
p->expired = 0;
}
if( rc ) goto abort_due_to_error;
@@ -3509,7 +3509,7 @@
assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
|| p->readOnly==0 );
- if( p->expired ){
+ if( p->expired==1 ){
rc = SQLITE_ABORT_ROLLBACK;
goto abort_due_to_error;
}
@@ -6690,7 +6690,7 @@
}
#endif
-/* Opcode: Expire P1 * * * *
+/* Opcode: Expire P1 P2 * * *
**
** Cause precompiled statements to expire. When an expired statement
** is executed using sqlite3_step() it will either automatically
@@ -6699,12 +6699,19 @@
**
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
** then only the currently executing statement is expired.
+**
+** If P2 is 0, then SQL statements are expired immediately. If P2 is 1,
+** then running SQL statements are allowed to continue to run to completion.
+** The P2==1 case occurs when a CREATE INDEX or similar schema change happens
+** that might help the statement run faster but which does not affect the
+** correctness of operation.
*/
case OP_Expire: {
+ assert( pOp->p2==0 || pOp->p2==1 );
if( !pOp->p1 ){
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, pOp->p2);
}else{
- p->expired = 1;
+ p->expired = pOp->p2+1;
}
break;
}
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index d6c5660..e291825 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -390,9 +390,9 @@
u8 errorAction; /* Recovery action to do in case of an error */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
- bft expired:1; /* True if the VM needs to be recompiled */
- bft doingRerun:1; /* True if rerunning after an auto-reprepare */
+ bft expired:2; /* 1: recompile VM immediately 2: when convenient */
bft explain:2; /* True if EXPLAIN present on SQL command */
+ bft doingRerun:1; /* True if rerunning after an auto-reprepare */
bft changeCntOn:1; /* True to update the change-counter */
bft runOnlyOnce:1; /* Automatically expire on reset */
bft usesStmtJournal:1; /* True if uses a statement journal */
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index d6efead..4550c7c 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -4659,11 +4659,19 @@
** programs obsolete. Removing user-defined functions or collating
** sequences, or changing an authorization function are the types of
** things that make prepared statements obsolete.
+**
+** If iCode is 1, then expiration is advisory. The statement should
+** be reprepared before being restarted, but if it is already running
+** it is allowed to run to completion.
+**
+** Internally, this function just sets the Vdbe.expired flag on all
+** prepared statements. The flag is set to 1 for an immediate expiration
+** and set to 2 for an advisory expiration.
*/
-void sqlite3ExpirePreparedStatements(sqlite3 *db){
+void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){
Vdbe *p;
for(p = db->pVdbe; p; p=p->pNext){
- p->expired = 1;
+ p->expired = iCode+1;
}
}
diff --git a/src/vtab.c b/src/vtab.c
index 7894bbc..c9ce838 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -262,7 +262,7 @@
assert( sqlite3_mutex_held(db->mutex) );
if( p ){
- sqlite3ExpirePreparedStatements(db);
+ sqlite3ExpirePreparedStatements(db, 0);
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);