Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 174c929

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/[email protected]
1 parent cfe41f9 commit 174c929

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
@@ -1593,6 +1593,7 @@ _copySubPlan(const SubPlan *from)
15931593
COPY_NODE_FIELD(args);
15941594
COPY_SCALAR_FIELD(startup_cost);
15951595
COPY_SCALAR_FIELD(per_call_cost);
1596+
COPY_SCALAR_FIELD(subLinkId);
15961597

15971598
return newnode;
15981599
}

src/backend/nodes/equalfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ _equalSubPlan(const SubPlan *a, const SubPlan *b)
450450
COMPARE_NODE_FIELD(args);
451451
COMPARE_SCALAR_FIELD(startup_cost);
452452
COMPARE_SCALAR_FIELD(per_call_cost);
453+
COMPARE_SCALAR_FIELD(subLinkId);
453454

454455
return true;
455456
}

src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ _outSubPlan(StringInfo str, const SubPlan *node)
13361336
WRITE_NODE_FIELD(args);
13371337
WRITE_FLOAT_FIELD(startup_cost, "%.2f");
13381338
WRITE_FLOAT_FIELD(per_call_cost, "%.2f");
1339+
WRITE_INT_FIELD(subLinkId);
13391340
}
13401341

13411342
static void

src/backend/nodes/readfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,7 @@ _readSubPlan(void)
23602360
READ_NODE_FIELD(args);
23612361
READ_FLOAT_FIELD(startup_cost);
23622362
READ_FLOAT_FIELD(per_call_cost);
2363+
READ_INT_FIELD(subLinkId);
23632364

23642365
READ_DONE();
23652366
}

src/backend/optimizer/plan/subselect.c

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static bool subplan_is_hashable(Plan *plan);
7676
static bool testexpr_is_hashable(Node *testexpr, List *param_ids);
7777
static bool test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids);
7878
static bool hash_ok_operator(OpExpr *expr);
79+
static bool SS_make_multiexprs_unique_walker(Node *node, void *context);
7980
static bool simplify_EXISTS_query(PlannerInfo *root, Query *query);
8081
static Query *convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
8182
Node **testexpr, List **paramIds);
@@ -336,6 +337,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
336337
*/
337338
splan = makeNode(SubPlan);
338339
splan->subLinkType = subLinkType;
340+
splan->subLinkId = subLinkId;
339341
splan->testexpr = NULL;
340342
splan->paramIds = NIL;
341343
get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
@@ -859,12 +861,17 @@ hash_ok_operator(OpExpr *expr)
859861
* SubPlans, inheritance_planner() must call this to assign new, unique Param
860862
* IDs to the cloned MULTIEXPR_SUBLINKs' output parameters. See notes in
861863
* ExecScanSubPlan.
864+
*
865+
* We do not need to renumber Param IDs for MULTIEXPR_SUBLINK plans that are
866+
* initplans, because those don't have input parameters that could cause
867+
* confusion. Such initplans will not appear in the targetlist anyway, but
868+
* they still complicate matters because the surviving regular subplans might
869+
* not have consecutive subLinkIds.
862870
*/
863871
void
864872
SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
865873
{
866-
List *new_multiexpr_params = NIL;
867-
int offset;
874+
List *param_mapping = NIL;
868875
ListCell *lc;
869876

870877
/*
@@ -877,6 +884,9 @@ SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
877884
SubPlan *splan;
878885
Plan *plan;
879886
List *params;
887+
int oldId,
888+
newId;
889+
ListCell *lc2;
880890

881891
if (!IsA(tent->expr, SubPlan))
882892
continue;
@@ -899,86 +909,77 @@ SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
899909
&splan->setParam);
900910

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

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

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

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

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

984985

@@ -1061,6 +1062,7 @@ SS_process_ctes(PlannerInfo *root)
10611062
*/
10621063
splan = makeNode(SubPlan);
10631064
splan->subLinkType = CTE_SUBLINK;
1065+
splan->subLinkId = 0;
10641066
splan->testexpr = NULL;
10651067
splan->paramIds = NIL;
10661068
get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
@@ -2845,6 +2847,7 @@ SS_make_initplan_from_plan(PlannerInfo *root,
28452847
*/
28462848
node = makeNode(SubPlan);
28472849
node->subLinkType = EXPR_SUBLINK;
2850+
node->subLinkId = 0;
28482851
node->plan_id = list_length(root->glob->subplans);
28492852
node->plan_name = psprintf("InitPlan %d (returns $%d)",
28502853
node->plan_id, prm->paramid);

src/include/nodes/primnodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,8 @@ typedef struct SubPlan
714714
/* Estimated execution costs: */
715715
Cost startup_cost; /* one-time setup cost */
716716
Cost per_call_cost; /* cost for each subplan evaluation */
717+
/* Copied from original SubLink, but placed at end for ABI stability */
718+
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
717719
} SubPlan;
718720

719721
/*

src/test/regress/expected/inherit.out

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,23 +1699,36 @@ reset enable_bitmapscan;
16991699
--
17001700
-- Check handling of MULTIEXPR SubPlans in inherited updates
17011701
--
1702-
create table inhpar(f1 int, f2 text[]);
1702+
create table inhpar(f1 int, f2 text[], f3 int);
17031703
insert into inhpar select generate_series(1,10);
17041704
create table inhcld() inherits(inhpar);
17051705
insert into inhcld select generate_series(11,10000);
17061706
vacuum analyze inhcld;
17071707
vacuum analyze inhpar;
17081708
explain (verbose, costs off)
1709-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
1710-
from int4_tbl limit 1)
1709+
update inhpar set
1710+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1711+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
1712+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1713+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
17111714
from onek p2 where inhpar.f1 = p2.unique1;
1712-
QUERY PLAN
1713-
-------------------------------------------------------------------------------------------
1715+
QUERY PLAN
1716+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
17141717
Update on public.inhpar
17151718
Update on public.inhpar
17161719
Update on public.inhcld
1720+
InitPlan 2 (returns $4,$5)
1721+
-> Limit
1722+
Output: 'x'::text, 'y'::text
1723+
-> Seq Scan on public.int4_tbl int4_tbl_1
1724+
Output: 'x'::text, 'y'::text
1725+
InitPlan 4 (returns $10,$11)
1726+
-> Limit
1727+
Output: 'x'::text, 'y'::text
1728+
-> Seq Scan on public.int4_tbl int4_tbl_3
1729+
Output: 'x'::text, 'y'::text
17171730
-> Merge Join
1718-
Output: $4, inhpar.f2[1] := $5, (SubPlan 1 (returns $2,$3)), inhpar.ctid, p2.ctid
1731+
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
17191732
Merge Cond: (p2.unique1 = inhpar.f1)
17201733
-> Index Scan using onek_unique1 on public.onek p2
17211734
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
@@ -1729,19 +1742,27 @@ from onek p2 where inhpar.f1 = p2.unique1;
17291742
Output: (p2.unique2), (p2.stringu1)
17301743
-> Seq Scan on public.int4_tbl
17311744
Output: p2.unique2, p2.stringu1
1745+
SubPlan 3 (returns $8,$9)
1746+
-> Limit
1747+
Output: (p2.unique2), (p2.stringu1)
1748+
-> Seq Scan on public.int4_tbl int4_tbl_2
1749+
Output: p2.unique2, p2.stringu1
17321750
-> Hash Join
1733-
Output: $6, inhcld.f2[1] := $7, (SubPlan 1 (returns $2,$3)), inhcld.ctid, p2.ctid
1751+
Output: $16, (((((inhcld.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, inhcld.ctid, p2.ctid
17341752
Hash Cond: (inhcld.f1 = p2.unique1)
17351753
-> Seq Scan on public.inhcld
17361754
Output: inhcld.f2, inhcld.ctid, inhcld.f1
17371755
-> Hash
17381756
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
17391757
-> Seq Scan on public.onek p2
17401758
Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
1741-
(27 rows)
1759+
(42 rows)
17421760

1743-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
1744-
from int4_tbl limit 1)
1761+
update inhpar set
1762+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1763+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
1764+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
1765+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
17451766
from onek p2 where inhpar.f1 = p2.unique1;
17461767
drop table inhpar cascade;
17471768
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
@@ -622,19 +622,25 @@ reset enable_bitmapscan;
622622
--
623623
-- Check handling of MULTIEXPR SubPlans in inherited updates
624624
--
625-
create table inhpar(f1 int, f2 text[]);
625+
create table inhpar(f1 int, f2 text[], f3 int);
626626
insert into inhpar select generate_series(1,10);
627627
create table inhcld() inherits(inhpar);
628628
insert into inhcld select generate_series(11,10000);
629629
vacuum analyze inhcld;
630630
vacuum analyze inhpar;
631631

632632
explain (verbose, costs off)
633-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
634-
from int4_tbl limit 1)
633+
update inhpar set
634+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
635+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
636+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
637+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
635638
from onek p2 where inhpar.f1 = p2.unique1;
636-
update inhpar set (f1, f2[1]) = (select p2.unique2, p2.stringu1
637-
from int4_tbl limit 1)
639+
update inhpar set
640+
(f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
641+
(f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
642+
(f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
643+
(f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
638644
from onek p2 where inhpar.f1 = p2.unique1;
639645

640646
drop table inhpar cascade;

0 commit comments

Comments
 (0)