Fix oversight in recent MULTIEXPR_SUBLINK fix.

Commits 3f7323cbb et al missed the possibility that the Params
they are looking for could be buried under implicit coercions,
as well as other stuff that processIndirection() could add to
the original targetlist entry.  Copy the code in ruleutils.c
that deals with such cases.  (I thought about refactoring so
that there's just one copy; but seeing that we only need this
in old back branches, it seems not worth the trouble.)

Per off-list report from Andre Lin.  As before, only v10-v13
need the patch.

Discussion: https://postgr.es/m/17596-c5357f61427a81dc@postgresql.org
This commit is contained in:
Tom Lane 2022-09-02 14:54:40 -04:00
parent d7bc6ea052
commit 18f51083c9
3 changed files with 54 additions and 20 deletions

View File

@ -914,20 +914,54 @@ SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
/* /*
* Now we must find the Param nodes that reference the MULTIEXPR outputs * Now we must find the Param nodes that reference the MULTIEXPR outputs
* and update their sublink IDs so they'll reference the new outputs. * and update their sublink IDs so they'll reference the new outputs.
* Fortunately, those too must be at top level of the cloned targetlist. * Fortunately, those too must be in the cloned targetlist, but they could
* be buried under FieldStores and SubscriptingRefs and CoerceToDomains
* (cf processIndirection()), and underneath those there could be an
* implicit type coercion.
*/ */
offset = list_length(root->multiexpr_params); offset = list_length(root->multiexpr_params);
foreach(lc, subroot->parse->targetList) foreach(lc, subroot->parse->targetList)
{ {
TargetEntry *tent = (TargetEntry *) lfirst(lc); TargetEntry *tent = (TargetEntry *) lfirst(lc);
Node *expr;
Param *p; Param *p;
int subqueryid; int subqueryid;
int colno; int colno;
if (!IsA(tent->expr, Param)) expr = (Node *) tent->expr;
while (expr)
{
if (IsA(expr, FieldStore))
{
FieldStore *fstore = (FieldStore *) expr;
expr = (Node *) linitial(fstore->newvals);
}
else if (IsA(expr, SubscriptingRef))
{
SubscriptingRef *sbsref = (SubscriptingRef *) expr;
if (sbsref->refassgnexpr == NULL)
break;
expr = (Node *) sbsref->refassgnexpr;
}
else if (IsA(expr, CoerceToDomain))
{
CoerceToDomain *cdomain = (CoerceToDomain *) expr;
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
expr = (Node *) cdomain->arg;
}
else
break;
}
expr = strip_implicit_coercions(expr);
if (expr == NULL || !IsA(expr, Param))
continue; continue;
p = (Param *) tent->expr; p = (Param *) expr;
if (p->paramkind != PARAM_MULTIEXPR) if (p->paramkind != PARAM_MULTIEXPR)
continue; continue;
subqueryid = p->paramid >> 16; subqueryid = p->paramid >> 16;

View File

@ -1717,48 +1717,48 @@ reset enable_bitmapscan;
-- --
-- Check handling of MULTIEXPR SubPlans in inherited updates -- Check handling of MULTIEXPR SubPlans in inherited updates
-- --
create table inhpar(f1 int, f2 name); create table inhpar(f1 int, f2 text[]);
insert into inhpar select generate_series(1,10); insert into inhpar select generate_series(1,10);
create table inhcld() inherits(inhpar); create table inhcld() inherits(inhpar);
insert into inhcld select generate_series(11,10000); insert into inhcld select generate_series(11,10000);
vacuum analyze inhcld; vacuum analyze inhcld;
vacuum analyze inhpar; vacuum analyze inhpar;
explain (verbose, costs off) explain (verbose, costs off)
update inhpar set (f1, f2) = (select p2.unique2, p2.stringu1 update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
from int4_tbl limit 1) from int4_tbl limit 1)
from onek p2 where inhpar.f1 = p2.unique1; from onek p2 where inhpar.f1 = p2.unique1;
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------
Update on public.inhpar Update on public.inhpar
Update on public.inhpar Update on public.inhpar
Update on public.inhcld inhpar_1 Update on public.inhcld inhpar_1
-> Merge Join -> Merge Join
Output: $4, $5, (SubPlan 1 (returns $2,$3)), inhpar.ctid, p2.ctid Output: $4, inhpar.f2[1] := $5, (SubPlan 1 (returns $2,$3)), inhpar.ctid, p2.ctid
Merge Cond: (p2.unique1 = inhpar.f1) Merge Cond: (p2.unique1 = inhpar.f1)
-> Index Scan using onek_unique1 on public.onek p2 -> Index Scan using onek_unique1 on public.onek p2
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1 Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
-> Sort -> Sort
Output: inhpar.ctid, inhpar.f1 Output: inhpar.f2, inhpar.ctid, inhpar.f1
Sort Key: inhpar.f1 Sort Key: inhpar.f1
-> Seq Scan on public.inhpar -> Seq Scan on public.inhpar
Output: inhpar.ctid, inhpar.f1 Output: inhpar.f2, inhpar.ctid, inhpar.f1
SubPlan 1 (returns $2,$3) SubPlan 1 (returns $2,$3)
-> Limit -> Limit
Output: (p2.unique2), (p2.stringu1) Output: (p2.unique2), (p2.stringu1)
-> Seq Scan on public.int4_tbl -> Seq Scan on public.int4_tbl
Output: p2.unique2, p2.stringu1 Output: p2.unique2, p2.stringu1
-> Hash Join -> Hash Join
Output: $6, $7, (SubPlan 1 (returns $2,$3)), inhpar_1.ctid, p2.ctid Output: $6, inhpar_1.f2[1] := $7, (SubPlan 1 (returns $2,$3)), inhpar_1.ctid, p2.ctid
Hash Cond: (inhpar_1.f1 = p2.unique1) Hash Cond: (inhpar_1.f1 = p2.unique1)
-> Seq Scan on public.inhcld inhpar_1 -> Seq Scan on public.inhcld inhpar_1
Output: inhpar_1.ctid, inhpar_1.f1 Output: inhpar_1.f2, inhpar_1.ctid, inhpar_1.f1
-> Hash -> Hash
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1 Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
-> Seq Scan on public.onek p2 -> Seq Scan on public.onek p2
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1 Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
(27 rows) (27 rows)
update inhpar set (f1, f2) = (select p2.unique2, p2.stringu1 update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
from int4_tbl limit 1) from int4_tbl limit 1)
from onek p2 where inhpar.f1 = p2.unique1; from onek p2 where inhpar.f1 = p2.unique1;
drop table inhpar cascade; drop table inhpar cascade;

View File

@ -632,7 +632,7 @@ reset enable_bitmapscan;
-- --
-- Check handling of MULTIEXPR SubPlans in inherited updates -- Check handling of MULTIEXPR SubPlans in inherited updates
-- --
create table inhpar(f1 int, f2 name); create table inhpar(f1 int, f2 text[]);
insert into inhpar select generate_series(1,10); insert into inhpar select generate_series(1,10);
create table inhcld() inherits(inhpar); create table inhcld() inherits(inhpar);
insert into inhcld select generate_series(11,10000); insert into inhcld select generate_series(11,10000);
@ -640,10 +640,10 @@ vacuum analyze inhcld;
vacuum analyze inhpar; vacuum analyze inhpar;
explain (verbose, costs off) explain (verbose, costs off)
update inhpar set (f1, f2) = (select p2.unique2, p2.stringu1 update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
from int4_tbl limit 1) from int4_tbl limit 1)
from onek p2 where inhpar.f1 = p2.unique1; from onek p2 where inhpar.f1 = p2.unique1;
update inhpar set (f1, f2) = (select p2.unique2, p2.stringu1 update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
from int4_tbl limit 1) from int4_tbl limit 1)
from onek p2 where inhpar.f1 = p2.unique1; from onek p2 where inhpar.f1 = p2.unique1;