From: drh <> Date: Thu, 16 Mar 2023 10:21:52 +0000 (+0000) Subject: Do not use the one-pass optimization on an UPDATE if there is a subquery X-Git-Tag: version-3.41.2~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a0e71193e58a929fb31090fb15923e4629cc941f;p=thirdparty%2Fsqlite.git Do not use the one-pass optimization on an UPDATE if there is a subquery in the WHERE clause, since if the subquery is hidden behind a short-circuit operator, the subquery might not be evaluated until after one or more rows have been updated. FossilOrigin-Name: b5d8a9a6a58b4eb81a663192f578e0fbed72e8155bc7e1100ece04e8394a1984 --- diff --git a/manifest b/manifest index 46ed0ca09e..72ccc21ca8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sNEVER()\sfrom\sbtreeNext(). -D 2023-03-16T09:16:31.055 +C Do\snot\suse\sthe\sone-pass\soptimization\son\san\sUPDATE\sif\sthere\sis\sa\ssubquery\nin\sthe\sWHERE\sclause,\ssince\sif\sthe\ssubquery\sis\shidden\sbehind\sa\sshort-circuit\noperator,\sthe\ssubquery\smight\snot\sbe\sevaluated\suntil\safter\sone\sor\smore\srows\nhave\sbeen\supdated. +D 2023-03-16T10:21:52.307 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -686,7 +686,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 1305797eab3542a0896b552c6e7669c972c1468e11e92b370533c1f37a37082b F src/treeview.c fccf3b8c517c1f55cb380c1522febe6921fcb2bd800c16c78cab571d0eb0ccbd F src/trigger.c 6072c531d9bcc3980528150a1b03fda2e85a08c10023fafb42f93ffd68607ffe -F src/update.c f118e51768d2c1309e3c81e9f91141b22b8a1339cbc5969b1b2d810feaa25b22 +F src/update.c 76664e1beae86e8e961983ebe19a4ee9ebd7e26683ead2b288ba08f81fc7ba4e F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 3ff7bc2b48dd425b1448304bb86273b05da1621f136d51dbb9789f8803559a1f @@ -886,7 +886,7 @@ F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454 F test/corruptI.test a17bbf54fdde78d43cf3cc34b0057719fd4a173a3d824285b67dc5257c064c7b F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4 F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af -F test/corruptL.test 9d1a0055c8db19baccd12f22ac36a33ec7d63afb59e82eb30835aea8f89b94df +F test/corruptL.test b42978028afc5eefc8b51d8d7cd6a9344ba7362d7ed4511ee2070f56e06d5a1c F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067 F test/corruptN.test 7c099d153a554001b4fb829c799b01f2ea6276cbc32479131e0db0da4efd9cc4 F test/cost.test b11cdbf9f11ffe8ef99c9881bf390e61fe92baf2182bad1dbe6de59a7295c576 @@ -924,7 +924,7 @@ F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef F test/decimal.test fcf403fd5585f47342234e153c4a4338cd737b8e0884ac66fc484df47dbcf1a7 F test/default.test 9687cfb16717e4b8238c191697c98be88c0b16e568dd5368cd9284154097ef50 -F test/delete.test f66581a22960dae89b05f14755a576d935dcb94aa3aeacc0005b29bdbaa9949c +F test/delete.test 2686e1c98d552ef37d79ad55b17b93fe96fad9737786917ce3839767f734c48f F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab F test/delete4.test 51fafebe9503a40796d1aae1565c60524cada720e50eecac01b7fd0419d9ea0b @@ -1769,7 +1769,7 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 F test/unordered.test 0edaf3411d300693bca595897c5201421c6c5ec787990a1dfe2f7f60ae93f1e2 -F test/update.test eb7f4eb172ce270e51bb67d7867521f33a63635bb671e261bbafccaef3bd6db2 +F test/update.test 90772ede84cfc779fc3d31884a84ec4c74deb501eeb09a1c6c91c03d8e94c0d8 F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3 F test/upfrom1.tcl 8859d9d437f03b44174c4524a7a734a391fd4526fcff65be08285dafc9dc9041 F test/upfrom1.test 8cb06689e99cd707d884faa16da0e8eb26ff658bb01c47ddf72fadade666e6e1 @@ -2045,9 +2045,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 048711e49002e59cbaf51079403a3f37abaa7d3a04e0cb20de4aed2d56c0e837 -Q +1dffeffe150fff6777cf26bc960326ca4e4190d25d05bb066b082d1e1c8eb374 -R 514cfcdaded26951aac4ab75c255adfe +P 40623f5ab07481179f1efd5d377555c8d78bf0706a16212c0f6b954c17736a5c +Q +2c56b984a0bd3be5ec326a2109ea7b8f1d4ef63c8fc325caac9663cf2479eaff +R 6de8235dc7b56e5ae85b9cbade33385b U drh -Z f3663ffc26ca62852f1067ce8c9f730a +Z e4d27df9efe5c6e71838a2507dbb8d7d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ce5d07c31e..0cfe1faf07 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -40623f5ab07481179f1efd5d377555c8d78bf0706a16212c0f6b954c17736a5c \ No newline at end of file +b5d8a9a6a58b4eb81a663192f578e0fbed72e8155bc7e1100ece04e8394a1984 \ No newline at end of file diff --git a/src/update.c b/src/update.c index a4e57d1d42..bd1ddb1a30 100644 --- a/src/update.c +++ b/src/update.c @@ -727,12 +727,22 @@ void sqlite3Update( /* Begin the database scan. ** ** Do not consider a single-pass strategy for a multi-row update if - ** there are any triggers or foreign keys to process, or rows may - ** be deleted as a result of REPLACE conflict handling. Any of these - ** things might disturb a cursor being used to scan through the table - ** or index, causing a single-pass approach to malfunction. */ + ** there is anything that might disrupt the cursor being used to do + ** the UPDATE: + ** (1) This is a nested UPDATE + ** (2) There are triggers + ** (3) There are FOREIGN KEY constraints + ** (4) There are REPLACE conflict handlers + ** (5) There are subqueries in the WHERE clause + */ flags = WHERE_ONEPASS_DESIRED; - if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ + if( !pParse->nested + && !pTrigger + && !hasFK + && !chngKey + && !bReplace + && (sNC.ncFlags & NC_Subquery)==0 + ){ flags |= WHERE_ONEPASS_MULTIROW; } pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); diff --git a/test/corruptL.test b/test/corruptL.test index 3a841a8199..65d9821b35 100644 --- a/test/corruptL.test +++ b/test/corruptL.test @@ -1269,11 +1269,14 @@ do_test 15.0 { }]} {} extra_schema_checks 0 -do_execsql_test 15.1 { +do_catchsql_test 15.1 { PRAGMA cell_size_check = 0; UPDATE c1 SET c= NOT EXISTS(SELECT 1 FROM c1 ORDER BY (SELECT 1 FROM c1 ORDER BY a)) +10 WHERE d BETWEEN 4 AND 7; -} {} +} {1 {database disk image is malformed}} extra_schema_checks 1 +do_execsql_test 15.2 { + PRAGMA integrity_check; +} {/in database main/} #------------------------------------------------------------------------- reset_db diff --git a/test/delete.test b/test/delete.test index cdf6798ea6..214bae6f70 100644 --- a/test/delete.test +++ b/test/delete.test @@ -426,6 +426,9 @@ do_execsql_test delete-11.1 { # run after one or more rows have been deleted, which can change # the result of the subquery, and result in the wrong answer. # +# Similar problem for UPDATE tested by update-21.4 +# https://sqlite.org/forum/forumpost/0007d1fdb1 +# reset_db do_execsql_test delete-12.0 { CREATE TABLE t0(vkey INTEGER, pkey INTEGER,c1 INTEGER); diff --git a/test/update.test b/test/update.test index 66efd10ec8..47b6029bee 100644 --- a/test/update.test +++ b/test/update.test @@ -732,4 +732,37 @@ do_execsql_test update-20.30 { PRAGMA integrity_check; } {ok} +# 2023-03-16 https://sqlite.org/forum/forumpost/0007d1fdb1 +# A subquery in the WHERE clause of an UPDATE and behind a +# short-circuit evaluation caused problems because multi-row +# single-pass was selected. +# +# Similar problem for DELETE tested by delete-12.0. +# https://sqlite.org/src/info/73f0036f045bf371 +# +reset_db +do_execsql_test update-21.1 { + CREATE TABLE t1 (vkey INTEGER, c5 INTEGER); + INSERT INTO t1 VALUES(3,NULL),(6,-54); +} +db null NULL +do_execsql_test update-21.2 { + BEGIN; + UPDATE t1 SET vkey = 100 WHERE c5 is null; + SELECT * FROM t1 ORDER BY vkey, c5; + ROLLBACK; +} {6 -54 100 NULL} +do_execsql_test update-21.3 { + BEGIN; + UPDATE t1 SET vkey = 100 WHERE NOT (-10*(select min(vkey) from t1) >= c5); + SELECT * FROM t1 ORDER BY vkey, c5; + ROLLBACK; +} {3 NULL 6 -54} +do_execsql_test update-21.4 { + BEGIN; + UPDATE t1 SET vkey = 100 WHERE c5 is null OR NOT (-10*(select min(vkey) from t1) >= c5); + SELECT * FROM t1 ORDER BY vkey, c5; + ROLLBACK; +} {6 -54 100 NULL} + finish_test