Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ccbb54c

Browse files
committed
Further fixes for MULTIEXPR_SUBLINK fix.
Some more things I didn't think about in commits 3f7323c et al: MULTIEXPR_SUBLINK subplans might have been converted to initplans instead of regular subplans, in which case they won't show up in the modified targetlist. Fortunately, this would only happen if they have no input parameters, which means that the problem we originally needed to fix can't happen with them. Therefore, there's no need to clone their output parameters, and thus it doesn't hurt that we'll fail to see them in the first pass over the targetlist. Nonetheless, this complicates matters greatly, because now we have to distinguish output Params of initplans (which shouldn't get renumbered) from those of regular subplans (which should). This also breaks the simplistic scheme I used of assuming that the subplans found in the targetlist have consecutive subLinkIds. We really can't avoid the need to know the subplans' subLinkIds in this code. To fix that, add subLinkId as the last field of SubPlan. We can get away with that change in back branches because SubPlan nodes will never be stored in the catalogs, and there's no ABI break for external code that might be looking at the existing fields of SubPlan. Secondly, rewriteTargetListIU might have rolled up multiple FieldStores or SubscriptingRefs into one targetlist entry, breaking the assumption that there's at most one Param to fix per targetlist entry. (That assumption is OK I think in the ruleutils.c code I stole the logic from in 18f5108, because that only deals with pre-rewrite query trees. But it's definitely not OK here.) Abandon that shortcut and just do a full tree walk on the targetlist to ensure we find all the Params we have to change. Per bug #17606 from Andre Lin. As before, only v10-v13 need the patch. Discussion: https://postgr.es/m/17606-e5c8ad18d31db96a@postgresql.org
1 parent 43e409c commit ccbb54c

File tree

8 files changed

+119
-83
lines changed

8 files changed

+119
-83
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,7 @@ _copySubPlan(const SubPlan *from)
17191719
COPY_NODE_FIELD(args);
17201720
COPY_SCALAR_FIELD(startup_cost);
17211721
COPY_SCALAR_FIELD(per_call_cost);
1722+
COPY_SCALAR_FIELD(subLinkId);
17221723

17231724
return newnode;
17241725
}

src/backend/nodes/equalfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ _equalSubPlan(const SubPlan *a, const SubPlan *b)
455455
COMPARE_NODE_FIELD(args);
456456
COMPARE_SCALAR_FIELD(startup_cost);
457457
COMPARE_SCALAR_FIELD(per_call_cost);
458+
COMPARE_SCALAR_FIELD(subLinkId);
458459

459460
return true;
460461
}

src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,7 @@ _outSubPlan(StringInfo str, const SubPlan *node)
13491349
WRITE_NODE_FIELD(args);
13501350
WRITE_FLOAT_FIELD(startup_cost, "%.2f");
13511351
WRITE_FLOAT_FIELD(per_call_cost, "%.2f");
1352+
WRITE_INT_FIELD(subLinkId);
13521353
}
13531354

13541355
static void

src/backend/nodes/readfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,6 +2536,7 @@ _readSubPlan(void)
25362536
READ_NODE_FIELD(args);
25372537
READ_FLOAT_FIELD(startup_cost);
25382538
READ_FLOAT_FIELD(per_call_cost);
2539+
READ_INT_FIELD(subLinkId);
25392540

25402541
READ_DONE();
25412542
}

src/backend/optimizer/plan/subselect.c

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ static bool subplan_is_hashable(Plan *plan);
8686
static bool testexpr_is_hashable(Node *testexpr, List *param_ids);
8787
static bool test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids);
8888
static bool hash_ok_operator(OpExpr *expr);
89+
static bool SS_make_multiexprs_unique_walker(Node *node, void *context);
8990
static bool contain_dml(Node *node);
9091
static bool contain_dml_walker(Node *node, void *context);
9192
static bool contain_outer_selfref(Node *node);
@@ -335,6 +336,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
335336
*/
336337
splan = makeNode(SubPlan);
337338
splan->subLinkType = subLinkType;
339+
splan->subLinkId = subLinkId;
338340
splan->testexpr = NULL;
339341
splan->paramIds = NIL;
340342
get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
@@ -858,12 +860,17 @@ hash_ok_operator(OpExpr *expr)
858860
* SubPlans, inheritance_planner() must call this to assign new, unique Param
859861
* IDs to the cloned MULTIEXPR_SUBLINKs' output parameters. See notes in
860862
* ExecScanSubPlan.
863+
*
864+
* We do not need to renumber Param IDs for MULTIEXPR_SUBLINK plans that are
865+
* initplans, because those don't have input parameters that could cause
866+
* confusion. Such initplans will not appear in the targetlist anyway, but
867+
* they still complicate matters because the surviving regular subplans might
868+
* not have consecutive subLinkIds.
861869
*/
862870
void
863871
SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
864872
{
865-
List *new_multiexpr_params = NIL;
866-
int offset;
873+
List *param_mapping = NIL;
867874
ListCell *lc;
868875

869876
/*
@@ -876,6 +883,9 @@ SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
876883
SubPlan *splan;
877884
Plan *plan;
878885
List *params;
886+
int oldId,
887+
newId;
888+
ListCell *lc2;
879889

880890
if (!IsA(tent->expr, SubPlan))
881891
continue;
@@ -898,86 +908,77 @@ SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
898908
&splan->setParam);
899909

900910
/*
901-
* We will append the replacement-Params lists to
902-
* root->multiexpr_params, but for the moment just make a local list.
903-
* Since we lack easy access here to the original subLinkId, we have
904-
* to fall back on the slightly shaky assumption that the MULTIEXPR
905-
* SubPlans appear in the targetlist in subLinkId order. This should
906-
* be safe enough given the way that the parser builds the targetlist
907-
* today. I wouldn't want to rely on it going forward, but since this
908-
* code has a limited lifespan it should be fine. We can partially
909-
* protect against problems with assertions below.
911+
* Append the new replacement-Params list to root->multiexpr_params.
912+
* Then its index in that list becomes the new subLinkId of the
913+
* SubPlan.
910914
*/
911-
new_multiexpr_params = lappend(new_multiexpr_params, params);
915+
root->multiexpr_params = lappend(root->multiexpr_params, params);
916+
oldId = splan->subLinkId;
917+
newId = list_length(root->multiexpr_params);
918+
Assert(newId > oldId);
919+
splan->subLinkId = newId;
920+
921+
/*
922+
* Add a mapping entry to param_mapping so that we can update the
923+
* associated Params below. Leave zeroes in the list for any
924+
* subLinkIds we don't encounter; those must have been converted to
925+
* initplans.
926+
*/
927+
while (list_length(param_mapping) < oldId)
928+
param_mapping = lappend_int(param_mapping, 0);
929+
lc2 = list_nth_cell(param_mapping, oldId - 1);
930+
lfirst_int(lc2) = newId;
912931
}
913932

914933
/*
915-
* Now we must find the Param nodes that reference the MULTIEXPR outputs
916-
* and update their sublink IDs so they'll reference the new outputs.
917-
* Fortunately, those too must be in the cloned targetlist, but they could
918-
* be buried under FieldStores and SubscriptingRefs and CoerceToDomains
919-
* (cf processIndirection()), and underneath those there could be an
920-
* implicit type coercion.
934+
* Unless all the MULTIEXPRs were converted to initplans, we must now find
935+
* the Param nodes that reference the MULTIEXPR outputs and update their
936+
* sublink IDs so they'll reference the new outputs. While such Params
937+
* must be in the cloned targetlist, they could be buried under stuff such
938+
* as FieldStores and SubscriptingRefs and type coercions.
921939
*/
922-
offset = list_length(root->multiexpr_params);
940+
if (param_mapping != NIL)
941+
SS_make_multiexprs_unique_walker((Node *) subroot->parse->targetList,
942+
(void *) param_mapping);
943+
}
923944

924-
foreach(lc, subroot->parse->targetList)
945+
/*
946+
* Locate PARAM_MULTIEXPR Params in an expression tree, and update as needed.
947+
* (We can update-in-place because the tree was already copied.)
948+
*/
949+
static bool
950+
SS_make_multiexprs_unique_walker(Node *node, void *context)
951+
{
952+
if (node == NULL)
953+
return false;
954+
if (IsA(node, Param))
925955
{
926-
TargetEntry *tent = (TargetEntry *) lfirst(lc);
927-
Node *expr;
928-
Param *p;
956+
Param *p = (Param *) node;
957+
List *param_mapping = (List *) context;
929958
int subqueryid;
930959
int colno;
960+
int newId;
931961

932-
expr = (Node *) tent->expr;
933-
while (expr)
934-
{
935-
if (IsA(expr, FieldStore))
936-
{
937-
FieldStore *fstore = (FieldStore *) expr;
938-
939-
expr = (Node *) linitial(fstore->newvals);
940-
}
941-
else if (IsA(expr, SubscriptingRef))
942-
{
943-
SubscriptingRef *sbsref = (SubscriptingRef *) expr;
944-
945-
if (sbsref->refassgnexpr == NULL)
946-
break;
947-
948-
expr = (Node *) sbsref->refassgnexpr;
949-
}
950-
else if (IsA(expr, CoerceToDomain))
951-
{
952-
CoerceToDomain *cdomain = (CoerceToDomain *) expr;
953-
954-
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
955-
break;
956-
expr = (Node *) cdomain->arg;
957-
}
958-
else
959-
break;
960-
}
961-
expr = strip_implicit_coercions(expr);
962-
if (expr == NULL || !IsA(expr, Param))
963-
continue;
964-
p = (Param *) expr;
965962
if (p->paramkind != PARAM_MULTIEXPR)
966-
continue;
963+
return false;
967964
subqueryid = p->paramid >> 16;
968965
colno = p->paramid & 0xFFFF;
969-
Assert(subqueryid > 0 &&
970-
subqueryid <= list_length(new_multiexpr_params));
971-
Assert(colno > 0 &&
972-
colno <= list_length((List *) list_nth(new_multiexpr_params,
973-
subqueryid - 1)));
974-
subqueryid += offset;
975-
p->paramid = (subqueryid << 16) + colno;
976-
}
977966

978-
/* Finally, attach new replacement lists to the global list */
979-
root->multiexpr_params = list_concat(root->multiexpr_params,
980-
new_multiexpr_params);
967+
/*
968+
* If subqueryid doesn't have a mapping entry, it must refer to an
969+
* initplan, so don't change the Param.
970+
*/
971+
Assert(subqueryid > 0);
972+
if (subqueryid > list_length(param_mapping))
973+
return false;
974+
newId = list_nth_int(param_mapping, subqueryid - 1);
975+
if (newId == 0)
976+
return false;
977+
p->paramid = (newId << 16) + colno;
978+
return false;
979+
}
980+
return expression_tree_walker(node, SS_make_multiexprs_unique_walker,
981+
context);
981982
}
982983

983984

@@ -1110,6 +1111,7 @@ SS_process_ctes(PlannerInfo *root)
11101111
*/
11111112
splan = makeNode(SubPlan);
11121113
splan->subLinkType = CTE_SUBLINK;
1114+
splan->subLinkId = 0;
11131115
splan->testexpr = NULL;
11141116
splan->paramIds = NIL;
11151117
get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
@@ -3075,6 +3077,7 @@ SS_make_initplan_from_plan(PlannerInfo *root,
30753077
*/
30763078
node = makeNode(SubPlan);
30773079
node->subLinkType = EXPR_SUBLINK;
3080+
node->subLinkId = 0;
30783081
node->plan_id = list_length(root->glob->subplans);
30793082
node->plan_name = psprintf("InitPlan %d (returns $%d)",
30803083
node->plan_id, prm->paramid);

src/include/nodes/primnodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,8 @@ typedef struct SubPlan
731731
/* Estimated execution costs: */
732732
Cost startup_cost; /* one-time setup cost */
733733
Cost per_call_cost; /* cost for each subplan evaluation */
734+
/* Copied from original SubLink, but placed at end for ABI stability */
735+
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
734736
} SubPlan;
735737

736738
/*

src/test/regress/expected/inherit.out

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,23 +1717,36 @@ reset enable_bitmapscan;
17171717
--
17181718
-- Check handling of MULTIEXPR SubPlans in inherited updates
17191719
--
1720-
create table inhpar(f1 int, f2 text[]);
1720+
create table inhpar(f1 int, f2 text[], f3 int);
17211721
insert into inhpar select generate_series(1,10);
17221722
create table inhcld() inherits(inhpar);
17231723
insert into inhcld select generate_series(11,10000);
17241724
vacuum analyze inhcld;
17251725
vacuum analyze inhpar;
17261726
explain (verbose, costs off)
1727-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
1728-
from int4_tbl limit 1)
1727+
update inhpar set
1728+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1729+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
1730+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1731+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
17291732
from onek p2 where inhpar.f1 = p2.unique1;
1730-
QUERY PLAN
1731-
-----------------------------------------------------------------------------------------------
1733+
QUERY PLAN
1734+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
17321735
Update on public.inhpar
17331736
Update on public.inhpar
17341737
Update on public.inhcld inhpar_1
1738+
InitPlan 2 (returns $4,$5)
1739+
-> Limit
1740+
Output: 'x'::text, 'y'::text
1741+
-> Seq Scan on public.int4_tbl int4_tbl_1
1742+
Output: 'x'::text, 'y'::text
1743+
InitPlan 4 (returns $10,$11)
1744+
-> Limit
1745+
Output: 'x'::text, 'y'::text
1746+
-> Seq Scan on public.int4_tbl int4_tbl_3
1747+
Output: 'x'::text, 'y'::text
17351748
-> Merge Join
1736-
Output: $4, inhpar.f2[1] := $5, (SubPlan 1 (returns $2,$3)), inhpar.ctid, p2.ctid
1749+
Output: $12, (((((inhpar.f2[1] := $13)[2] := $4)[3] := $5)[4] := $15)[5] := $10)[6] := $11, $14, (SubPlan 1 (returns $2,$3)), NULL::record, (SubPlan 3 (returns $8,$9)), NULL::record, inhpar.ctid, p2.ctid
17371750
Merge Cond: (p2.unique1 = inhpar.f1)
17381751
-> Index Scan using onek_unique1 on public.onek p2
17391752
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
@@ -1747,19 +1760,27 @@ from onek p2 where inhpar.f1 = p2.unique1;
17471760
Output: (p2.unique2), (p2.stringu1)
17481761
-> Seq Scan on public.int4_tbl
17491762
Output: p2.unique2, p2.stringu1
1763+
SubPlan 3 (returns $8,$9)
1764+
-> Limit
1765+
Output: (p2.unique2), (p2.stringu1)
1766+
-> Seq Scan on public.int4_tbl int4_tbl_2
1767+
Output: p2.unique2, p2.stringu1
17501768
-> Hash Join
1751-
Output: $6, inhpar_1.f2[1] := $7, (SubPlan 1 (returns $2,$3)), inhpar_1.ctid, p2.ctid
1769+
Output: $16, (((((inhpar_1.f2[1] := $17)[2] := $4)[3] := $5)[4] := $19)[5] := $10)[6] := $11, $18, (SubPlan 1 (returns $2,$3)), NULL::record, (SubPlan 3 (returns $8,$9)), NULL::record, inhpar_1.ctid, p2.ctid
17521770
Hash Cond: (inhpar_1.f1 = p2.unique1)
17531771
-> Seq Scan on public.inhcld inhpar_1
17541772
Output: inhpar_1.f2, inhpar_1.ctid, inhpar_1.f1
17551773
-> Hash
17561774
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
17571775
-> Seq Scan on public.onek p2
17581776
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
1759-
(27 rows)
1777+
(42 rows)
17601778

1761-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
1762-
from int4_tbl limit 1)
1779+
update inhpar set
1780+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1781+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
1782+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1783+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
17631784
from onek p2 where inhpar.f1 = p2.unique1;
17641785
drop table inhpar cascade;
17651786
NOTICE: drop cascades to table inhcld

src/test/regress/sql/inherit.sql

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -632,19 +632,25 @@ reset enable_bitmapscan;
632632
--
633633
-- Check handling of MULTIEXPR SubPlans in inherited updates
634634
--
635-
create table inhpar(f1 int, f2 text[]);
635+
create table inhpar(f1 int, f2 text[], f3 int);
636636
insert into inhpar select generate_series(1,10);
637637
create table inhcld() inherits(inhpar);
638638
insert into inhcld select generate_series(11,10000);
639639
vacuum analyze inhcld;
640640
vacuum analyze inhpar;
641641

642642
explain (verbose, costs off)
643-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
644-
from int4_tbl limit 1)
643+
update inhpar set
644+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
645+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
646+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
647+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
645648
from onek p2 where inhpar.f1 = p2.unique1;
646-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
647-
from int4_tbl limit 1)
649+
update inhpar set
650+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
651+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
652+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
653+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
648654
from onek p2 where inhpar.f1 = p2.unique1;
649655

650656
drop table inhpar cascade;

0 commit comments

Comments
 (0)