From: drh <> Date: Wed, 24 Apr 2024 16:36:37 +0000 (+0000) Subject: If a RETURNING clause contains a subquery that references the table that is X-Git-Tag: version-3.46.0~46 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0868d58e2caa16f19e843602bf2ca58653653162;p=thirdparty%2Fsqlite.git If a RETURNING clause contains a subquery that references the table that is being updated in the statement that includes the RETURNING clause, then mark the subquery as correlated sot hat it is recomputed for each result and not just computed once and reused. See [forum:/forumpost/2c83569ce8945d39|forum post 2c83569ce8945d39]. FossilOrigin-Name: 9ea6bcc8fdf6aadb756ec5bcaaa7af314167f8973bdd32fd23f83bd964f0c21e --- diff --git a/manifest b/manifest index b4352e70e4..e18f26c318 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\scase\swhere\sa\scorrupt\sstat4\srecord\scould\sgo\sunrecognized\sdue\sto\sinteger\soverflow. -D 2024-04-24T14:18:20.595 +C If\sa\sRETURNING\sclause\scontains\sa\ssubquery\sthat\sreferences\sthe\stable\sthat\sis\nbeing\supdated\sin\sthe\sstatement\sthat\sincludes\sthe\sRETURNING\sclause,\sthen\smark\nthe\ssubquery\sas\scorrelated\ssot\shat\sit\sis\srecomputed\sfor\seach\sresult\sand\snot\njust\scomputed\sonce\sand\sreused.\s\sSee\n[forum:/forumpost/2c83569ce8945d39|forum\spost\s2c83569ce8945d39]. +D 2024-04-24T16:36:37.548 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -815,7 +815,7 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68 F src/treeview.c a8aa3086c886c6eb2ff2b6354b0f23d251f7219bf08fad52d4f2791e55324f1b -F src/trigger.c d087b9f50463871fc22c812835cff48d23496279758bc077bbffc17ebfe0470d +F src/trigger.c 0858f75818ed1580332db274f1032bcc5effe567cb132df5c5be8b1d800ca97f F src/update.c 732404a04d1737ef14bb6ec6b84f74edf28b3c102a92ae46b4855438a710efe7 F src/upsert.c 2e60567a0e9e8520c18671b30712a88dc73534474304af94f32bb5f3ef65ac65 F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e @@ -1530,7 +1530,7 @@ F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed13 F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d F test/resetdb.test 54c06f18bc832ac6d6319e5ab23d5c8dd49fdbeec7c696d791682a8006bd5fc3 F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb -F test/returning1.test b27646c5e25431dd47cf72f525d1b2d5ad4ae14e21d9cf3d65769f05e48f64f3 +F test/returning1.test 2ebfe6e56f3de9b194295620f46a747c26cebdab937defb9afd84b96dd202ccb F test/returningfault.test ae4c4b5e8745813287a359d9ccdb9d5c883c2e68afb18fb0767937d5de5692a4 F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa F test/rollback2.test 3f3a4e20401825017df7e7671e9f31b6de5fae5620c2b9b49917f52f8c160a8f @@ -2185,8 +2185,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6f0e7e195275aeb4aefd9da20348af35e3ef7f0a6b2768a34824daeace16eff1 -R 8aa170676bd507dd4717ebad9ca82758 -U dan -Z 3e9a5a5005e3601585922bc0e00c59cc +P 240a4a48b27a2b1070bba6d7da76a8df8e3f1808e262045d7ad2cf910df08f3d +R 0443bbe68d7ebf5917ca0dd9f2f0960d +U drh +Z a1811f8f003e2400c1c50cad60d7b2cc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1442db0c13..62d8aa4756 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -240a4a48b27a2b1070bba6d7da76a8df8e3f1808e262045d7ad2cf910df08f3d \ No newline at end of file +9ea6bcc8fdf6aadb756ec5bcaaa7af314167f8973bdd32fd23f83bd964f0c21e \ No newline at end of file diff --git a/src/trigger.c b/src/trigger.c index 34537918c9..98e8da58c1 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -951,6 +951,72 @@ static ExprList *sqlite3ExpandReturning( return pNew; } +/* If the Expr node is a subquery or an EXISTS operator or an IN operator that +** uses a subquery, and if the subquery is SF_Correlated, then mark the +** expression as EP_VarSelect. +*/ +static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ + UNUSED_PARAMETER(NotUsed); + if( ExprUseXSelect(pExpr) + && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 + ){ + testcase( ExprHasProperty(pExpr, EP_VarSelect) ); + ExprSetProperty(pExpr, EP_VarSelect); + } + return WRC_Continue; +} + + +/* +** If the SELECT references the table pWalker->u.pTab, then do two things: +** +** (1) Mark the SELECT as as SF_Correlated. +** (2) Set pWalker->eCode to non-zero so that the caller will know +** that (1) has happened. +*/ +static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ + int i; + SrcList *pSrc; + assert( pSelect!=0 ); + pSrc = pSelect->pSrc; + assert( pSrc!=0 ); + for(i=0; inSrc; i++){ + if( pSrc->a[i].pTab==pWalker->u.pTab ){ + testcase( pSelect->selFlags & SF_Correlated ); + pSelect->selFlags |= SF_Correlated; + pWalker->eCode = 1; + break; + } + } + return WRC_Continue; +} + +/* +** Scan the expression list that is the argument to RETURNING looking +** for subqueries that depend on the table which is being modified in the +** statement that is hosting the RETURNING clause (pTab). Mark all such +** subqueries as SF_Correlated. If the subqueries are part of an +** expression, mark the expression as EP_VarSelect. +** +** https://sqlite.org/forum/forumpost/2c83569ce8945d39 +*/ +static void sqlite3ProcessReturningSubqueries( + ExprList *pEList, + Table *pTab +){ + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = sqlite3ExprWalkNoop; + w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; + w.u.pTab = pTab; + sqlite3WalkExprList(&w, pEList); + if( w.eCode ){ + w.xExprCallback = sqlite3ReturningSubqueryVarSelect; + w.xSelectCallback = sqlite3SelectWalkNoop; + sqlite3WalkExprList(&w, pEList); + } +} + /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING @@ -1014,6 +1080,7 @@ static void codeReturningTrigger( int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; + sqlite3ProcessReturningSubqueries(pNew, pTab); pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; i3 + RETURNING a, + (SELECT min(a) FROM t1), + (SELECT max(a) FROM t1), + (SELECT round(avg(a),2) FROM t1); + ROLLBACK; +} { + 1 2 8 4.6 + 2 3 8 5.25 + 4 3 8 5.67 + 6 3 8 5.5 + 8 3 3 3.0 +} +do_execsql_test 20.2 { + BEGIN; + DELETE FROM t1 + RETURNING a, + (SELECT min(a) FROM t1), + (SELECT max(a) FROM t1), + (SELECT round(avg(a),2) FROM t1); + ROLLBACK; +} { + 1 2 8 4.6 + 2 3 8 5.25 + 3 4 8 6.0 + 4 6 8 7.0 + 6 8 8 8.0 + 8 N N N +} +do_execsql_test 20.3 { + BEGIN; + DELETE FROM t1 + RETURNING a, + (SELECT min(t2.a)+t1.a*100 FROM t1 AS t2), + (SELECT max(t2.a)+t1.a*100 FROM t1 AS t2), + (SELECT round(avg(t2.a),2)+t1.a*100 FROM t1 AS t2); + ROLLBACK; +} { + 1 102 108 104.6 + 2 203 208 205.25 + 3 304 308 306.0 + 4 406 408 407.0 + 6 608 608 608.0 + 8 N N N +} + finish_test