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
This commit is contained in:
drh 2024-04-24 16:36:37 +00:00
parent dfa6289153
commit 0868d58e2c
4 changed files with 136 additions and 9 deletions

View File

@ -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.

View File

@ -1 +1 @@
240a4a48b27a2b1070bba6d7da76a8df8e3f1808e262045d7ad2cf910df08f3d
9ea6bcc8fdf6aadb756ec5bcaaa7af314167f8973bdd32fd23f83bd964f0c21e

View File

@ -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; i<pSrc->nSrc; 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; i<nCol; i++){

View File

@ -460,4 +460,64 @@ do_catchsql_test 19.1 {
END;
} {0 {}}
# 2024-04-24
# https://sqlite.org/forum/forumpost/2c83569ce8945d39
#
# If the RETURNING clause includes subqueries that reference the
# table being modified, make sure that the subqueries are identified
# as correlated so that the results are recomputed after each step
# instead of being computed once and reused.
#
reset_db
db null N
do_execsql_test 20.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT);
INSERT INTO t1 VALUES(1,10),(2,20),(3,30),(4,40),(6,60),(8,80);
BEGIN;
DELETE FROM t1 WHERE a<>3
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