From c25cfcfa372bbda514002cfc648e071544251b7d Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 20 Jul 2017 16:55:51 +0000 Subject: [PATCH] Experimental API sqlite3_stmt_refresh() to force a prepared statement to recompile if it needs to due to a schema change or other factor. FossilOrigin-Name: 10556ee454edf60dd6542fdf9017f713c783b9ca1b86f25f45e3c17ffb184e24 --- manifest | 26 ++++++++++++++------------ manifest.uuid | 2 +- src/sqlite.h.in | 11 +++++++++++ src/test1.c | 27 +++++++++++++++++++++++++++ src/vdbe.c | 3 +++ src/vdbeInt.h | 1 + src/vdbeapi.c | 22 ++++++++++++++++++++++ test/capi3d.test | 25 +++++++++++++++++++++++++ 8 files changed, 104 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 4932481eb6..1de462db99 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\sbuilt-in\sdate/time\sfunctions\sso\sthat\sthey\scan\sbe\sused\sin\nCHECK\sconstraints,\sin\sthe\sWHERE\sclause\sor\spartial\sindexes,\sand\sindex\nexpressions,\sprovided\sthat\snone\sof\sthe\snon-deterministic\skeywords\n("now",\s"localtime",\s"utc")\sare\sused\sas\sarguments. -D 2017-07-20T15:08:43.378 +C Experimental\sAPI\ssqlite3_stmt_refresh()\sto\sforce\sa\sprepared\sstatement\sto\nrecompile\sif\sit\sneeds\sto\sdue\sto\sa\sschema\schange\sor\sother\sfactor. +D 2017-07-20T16:55:51.434 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016 @@ -455,7 +455,7 @@ F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c c6bf96a7f9d7d68f929de84738c599a30d0a725ab0b54420e70545743cd5ee7b F src/shell.c bd6a37cbe8bf64ef6a6a74fdc50f067d3148149b4ce2b4d03154663e66ded55f F src/shell.c.in b5725acacba95ccefa57b6d068f710e29ba8239c3aa704628a1902a1f729c175 -F src/sqlite.h.in dad804d4e1979a2ddec33cc8da6aa50c04e6ba0dcb4058e7b3609588d010e041 +F src/sqlite.h.in d100c0942b5a6c20ec5c4a5a0b752be62530f80babd854544d1dbd2f23f91023 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 967154985ed2ae62f90d9029bb5b5071793d847f1696a2ebe9e8cc0b042ae60b F src/sqliteInt.h 96197a18f041b9ab99e6cee0db39dbf771ac7762d9f0f63d9e719285f0478664 @@ -463,7 +463,7 @@ F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/tclsqlite.c 2c29b0b76e91edfd1b43bf135c32c8674710089197327682b6b7e6af88062c3d -F src/test1.c cfb78b728b37ae3a2b14fe1b3a6c766e0da41370eda112594e698c94011b622e +F src/test1.c 84f9472ca24c2c2aa2d76256f19db3ce0bb0224c0553db793f0fb2c0969c869e F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 @@ -520,10 +520,10 @@ F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23 F src/vacuum.c 874c0f2f15ab2908748297d587d22d485ea96d55aaec91d4775dddb2e24d2ecf -F src/vdbe.c 1e541ec7ff409bbabcc6b4f154957296fff5827c16c2ab0056348acae75685bf +F src/vdbe.c 53f3f4d1ee9bd4985cc0b199bdf6a6c5e99c526eb15f100fed59653c79cf8fa6 F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97 -F src/vdbeInt.h 19bd04a4211fe56c712ab35b48be77fd5a0579b851e9dea2cb8deade359b72b9 -F src/vdbeapi.c 52844a5a71712197be45f1c63d730c48a745c7457c959465cfb2b969af40a266 +F src/vdbeInt.h e202dc9f7b00722a85f609053610d40afa9b6a836a42810b2ef2d2a7b9e099ba +F src/vdbeapi.c d83d30e1130c96d5bb1132275275402502b17cd7434065ea93b4cd1eccab8c65 F src/vdbeaux.c 42e215cc6f69e91eebdd2b886ec1ff2b0d5e44331bd8b88a0a44e78bbeed7133 F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9 F src/vdbemem.c fe8fce1cdc258320b465934039fe4b1230d63f81d6b81b1eac775b6eec00af0d @@ -632,7 +632,7 @@ F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738 F test/capi3.test 986e57cea8ab423b3fc8c2e3b69330394252d3d2a4496122ff3749e258305695 F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3c.test 7ebed1d8fa2f3190149d556fe8cff5a006be62af437c5c4640db614470126098 -F test/capi3d.test 485048dc5cd07bc68011e4917ad035ad6047ab82 +F test/capi3d.test 876b9dda41f003aed30df91f0b8653e576a956ab46efa00eadd35d6920cb94e2 F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef @@ -1637,8 +1637,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d14fc621e918915bbf8e04597eb238ea78dff3d9c5eb4402cb88692d00dbdfee b7f70c7fcabc10b8b3e62fe5ac68476cec23acaee037c7250ff70bca3f3ab541 -R 636bb2618e46215e27cfa1dc613ec36a -T +closed b7f70c7fcabc10b8b3e62fe5ac68476cec23acaee037c7250ff70bca3f3ab541 +P a90c062d46c63a1e6f83064b1c5afb26a16e93b6ee8620ca46d169fdb325c488 +R 25671fe00f59e885629fa15763c01ca2 +T *branch * sqlite3_stmt_refresh +T *sym-sqlite3_stmt_refresh * +T -sym-trunk * U drh -Z 8d21c041010891259262753e78bdd45f +Z 3a35a5d7b215280c71ff1c56378d2e53 diff --git a/manifest.uuid b/manifest.uuid index 9f9976a109..5897d5c6e5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a90c062d46c63a1e6f83064b1c5afb26a16e93b6ee8620ca46d169fdb325c488 \ No newline at end of file +10556ee454edf60dd6542fdf9017f713c783b9ca1b86f25f45e3c17ffb184e24 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 427b6f747e..64fac3a1c4 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -3742,6 +3742,17 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); */ int sqlite3_stmt_busy(sqlite3_stmt*); +/* +** CAPI3REF: Recompile A Prepared Statement If Needed +** METHOD: sqlite3_stmt +** +** ^The sqlite3_stmt_refresh(S) examines the [prepared statement] S +** and attempts to recompile it if it has expired, for example due +** to a schema change. ^If the prepared statement S is up-to-date and +** ready for use, then sqlite3_stmt_refresh(S) is a no-op. +*/ +int sqlite3_stmt_refresh(sqlite3_stmt*); + /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} diff --git a/src/test1.c b/src/test1.c index df7685f244..a75d6f8de1 100644 --- a/src/test1.c +++ b/src/test1.c @@ -2638,6 +2638,32 @@ static int SQLITE_TCLAPI test_stmt_busy( return TCL_OK; } +/* +** Usage: sqlite3_stmt_refresh STMT +** +** Recompile the STMT prepared statement if it needs to be recompiled. +*/ +static int SQLITE_TCLAPI test_stmt_refresh( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + int rc; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " STMT", 0); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + rc = sqlite3_stmt_refresh(pStmt); + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc)); + return TCL_OK; +} + /* ** Usage: uses_stmt_journal STMT ** @@ -7491,6 +7517,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_next_stmt", test_next_stmt ,0 }, { "sqlite3_stmt_readonly", test_stmt_readonly ,0 }, { "sqlite3_stmt_busy", test_stmt_busy ,0 }, + { "sqlite3_stmt_refresh", test_stmt_refresh ,0 }, { "uses_stmt_journal", uses_stmt_journal ,0 }, { "sqlite3_release_memory", test_release_memory, 0}, diff --git a/src/vdbe.c b/src/vdbe.c index 3e6231e0d8..76a0bb2419 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3191,6 +3191,9 @@ case OP_Transaction: { rc = SQLITE_SCHEMA; } if( rc ) goto abort_due_to_error; + if( p->stopAfterInit && pOp[1].opcode!=OP_Transaction ){ + goto vdbe_return; + } break; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 599fe70414..be288e1e47 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -392,6 +392,7 @@ struct Vdbe { bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ + bft stopAfterInit:1; /* Halt after running the last OP_Transaction */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 35b194fffe..df2b4cbf15 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -1608,6 +1608,28 @@ int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0; } +/* +** Recompile the prepared statement if it has expired +*/ +int sqlite3_stmt_refresh(sqlite3_stmt *pStmt){ + int rc = SQLITE_OK; + if( !sqlite3_stmt_busy(pStmt) ){ + VdbeOp *aOp; + Vdbe *v = (Vdbe*)pStmt; + sqlite3_mutex_enter(v->db->mutex); + aOp = v->aOp; + assert( aOp[0].opcode==OP_Init ); + if( aOp[aOp[0].p2].opcode==OP_Transaction ){ + v->stopAfterInit = 1; + rc = sqlite3_step(pStmt); + v->stopAfterInit = 0; + sqlite3_reset(pStmt); + } + sqlite3_mutex_leave(v->db->mutex); + } + return rc; +} + /* ** Return a pointer to the next prepared statement after pStmt associated ** with database connection pDb. If pStmt is NULL, return the first diff --git a/test/capi3d.test b/test/capi3d.test index 3b9b8375d1..ead409eba7 100644 --- a/test/capi3d.test +++ b/test/capi3d.test @@ -179,5 +179,30 @@ do_test capi3d-4.2.6 { sqlite3_finalize $::s1 } {SQLITE_OK} +# Tests for the sqlite3_stmt_refresh() interface +# +do_execsql_test capi3d-5.1 { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5(a,b); + INSERT INTO t5 VALUES(1,2),(3,4); +} +do_test capi3d-5.2 { + set ::s1 [sqlite3_prepare_v2 db "SELECT * FROM t5" -1 notused] + sqlite3 db2 test.db + db2 eval {SELECT * FROM t5 ORDER BY a} +} {1 2 3 4} +do_test capi3d-5.3 { + sqlite3_column_count $::s1 +} 2 +do_test capi3d-5.4 { + db2 eval {ALTER TABLE t5 ADD COLUMN c DEFAULT 9} + sqlite3_column_count $::s1 +} 2 +do_test capi3d-5.5 { + sqlite3_stmt_refresh $::s1 + sqlite3_column_count $::s1 +} 3 +sqlite3_finalize $::s1 +db2 close finish_test -- 2.39.5