diff --git a/manifest b/manifest index 19f4d37fde..5d7ac80310 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\scontent\scolumns\sof\sthe\sindex-btree\sthat\simplements\sa\sWITHOUT\sROWID\ntable\sare\snot\sordered\sand\sso\sthe\squery\splanner\sshould\snot\sassume\sthey\nare\sordered.\s\sFix\sfor\sthe\sissue\sidentified\sby\n[forum:/forumpost/6c8960f545|forum\spost\s6c8960f545]. -D 2021-05-13T13:43:40.565 +C Modify\sthe\ssqlite3_stmt_readonly()\sinterface\sso\sthat\sit\sreturns\sfalse\sfor\nCREATE\sTABLE\sIF\sNOT\sEXISTS\sstatements\seven\sif\sthe\stable\salready\sexists\sand\nthe\sstatement\sis\sreally\sa\sread-only\sno-op.\s\sLikewise\sfor\sDROP\sTABLE,\s\nCREATE\sINDEX,\sand\sDROP\sINDEX.\s\sUpdate\sthe\sdocumentation\sfor\nsqlite3_stmt_readonly()\sto\sreflect\sthis\snew\sbehavior. +D 2021-05-13T18:24:22.567 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -487,7 +487,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 5372d8bca3374145477e129be5ed6e907731e1d117c0dc1b524b536f1cc0e577 F src/btree.h 096cc53baa58be22b02c896d1cf933c38cfc6d65f9253c1367ece8cc88a24de5 F src/btreeInt.h 7bc15a24a02662409ebcd6aeaa1065522d14b7fda71573a2b0568b458f514ae0 -F src/build.c bf4f76eb77ff0193ef826f9dbd0285e8b55fe8ecb24d1f6b14bf72b68df6a422 +F src/build.c a7866beda66a7c8971e013674c7f659d19977d9ad51e11032479b1f754d1ef21 F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410 @@ -545,7 +545,7 @@ F src/resolve.c c38bbb89d7ba7a8673ec4f59b63e0980eb859c39ff2acc5df8b3d0f2dcd33115 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 57dbb27e0d0cb2438487c797365a4c17294d0df3c25c970ca87f123105f33ed0 F src/shell.c.in 1b32ba2918ede13b68df47c7b92b72ba0d06e68d384e78bb9d7456527271d400 -F src/sqlite.h.in c0969405cc053220ebf81d487c5851007928e45e487237dd23435d13c25daab0 +F src/sqlite.h.in 5c950066775ca9efdaa49077c05d38d0bef6418f3bd07d2dce0210f1d2f3c326 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e F src/sqliteInt.h 2b1cbc7f78346ac83ea962e5daf6a201e223cf1902e0cfde54f37cf3d82fcc24 @@ -1913,7 +1913,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b2b0e23ba885f22c88b03492e42c3cd1cbd59289e452263951bb757a871699f0 -R 764f06f91ab95ce9134e7e810a3df5f2 +P c21bc5a2353e660f2acf5ed916921a4ee416910d0b3f2deb512a05c54138d1c0 +R 2e9914ba2c75f0fbf85c89829a93e146 U drh -Z 2bf41f499ef008f771df581659494cf8 +Z 708e967733b505d5e67438dc1caa81fb diff --git a/manifest.uuid b/manifest.uuid index b0a4922dc2..a7972e9082 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c21bc5a2353e660f2acf5ed916921a4ee416910d0b3f2deb512a05c54138d1c0 \ No newline at end of file +cf8eb465974e596a13df56f3efbc98e098e7b74de9af4fde9ad58312db9750e4 \ No newline at end of file diff --git a/src/build.c b/src/build.c index d86b506d62..6339a97849 100644 --- a/src/build.c +++ b/src/build.c @@ -1048,6 +1048,22 @@ i16 sqlite3TableColumnToStorage(Table *pTab, i16 iCol){ } #endif +/* +** Insert a single OP_JournalMode query opcode in order to force the +** prepared statement to return false for sqlite3_stmt_readonly(). This +** is used by CREATE TABLE IF NOT EXISTS and similar if the table already +** exists, so that the prepared statement for CREATE TABLE IF NOT EXISTS +** will return false for sqlite3_stmt_readonly() even if that statement +** is a read-only no-op. +*/ +static void sqlite3ForceNotReadOnly(Parse *pParse){ + int iReg = ++pParse->nMem; + Vdbe *v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3VdbeAddOp3(v, OP_JournalMode, 0, iReg, PAGER_JOURNALMODE_QUERY); + } +} + /* ** Begin constructing a new table representation in memory. This is ** the first of several action routines that get called in response @@ -1147,6 +1163,7 @@ void sqlite3StartTable( }else{ assert( !db->init.busy || CORRUPT_DB ); sqlite3CodeVerifySchema(pParse, iDb); + sqlite3ForceNotReadOnly(pParse); } goto begin_table_error; } @@ -3197,7 +3214,10 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ if( noErr ) db->suppressErr--; if( pTab==0 ){ - if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + if( noErr ){ + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlite3ForceNotReadOnly(pParse); + } goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -3767,6 +3787,7 @@ void sqlite3CreateIndex( }else{ assert( !db->init.busy ); sqlite3CodeVerifySchema(pParse, iDb); + sqlite3ForceNotReadOnly(pParse); } goto exit_create_index; } @@ -4285,6 +4306,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); }else{ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); + sqlite3ForceNotReadOnly(pParse); } pParse->checkSchema = 1; goto exit_drop_index; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 06bcf008e5..ef452e3244 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4198,6 +4198,15 @@ const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt); ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlite3_stmt_readonly() returns false for those commands. +** +** ^This routine returns false if there is any possibility that the +** statement might change the database file. ^A false return does +** not guarantee that the statement will change the database file. +** ^For example, an UPDATE statement might have a WHERE clause that +** makes it a no-op, but the sqlite3_stmt_readonly() result would still +** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a +** read-only no-op if the table already exists, but +** sqlite3_stmt_readonly() still returns false for such a statement. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);