diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 5760b616813..7df1a1cb514 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -50,6 +50,7 @@ typedef struct process_sublinks_context { PlannerInfo *root; bool isTopQual; + bool skipPHVs; /* don't descend into PlaceHolderVars? */ } process_sublinks_context; typedef struct finalize_primnode_context @@ -376,12 +377,20 @@ build_subplan(PlannerInfo *root, Plan *plan, Path *path, * comments for SS_replace_correlation_vars). Do that now. A * PlaceHolderVar needs no such treatment: subquery_planner already * preprocessed the PHVs of its owning level, so its expression is - * fully processed and may already contain SubPlans. + * fully processed and may already contain SubPlans. The same goes + * for any PlaceHolderVars within the arguments, so skip those. */ if (IsA(arg, Aggref) || IsA(arg, GroupingFunc) || IsA(arg, ReturningExpr)) - arg = SS_process_sublinks(root, arg, false); + { + process_sublinks_context context; + + context.root = root; + context.isTopQual = false; + context.skipPHVs = true; + arg = process_sublinks_mutator(arg, &context); + } splan->parParam = lappend_int(splan->parParam, pitem->paramId); splan->args = lappend(splan->args, arg); @@ -2217,6 +2226,7 @@ SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual) context.root = root; context.isTopQual = isQual; + context.skipPHVs = false; return process_sublinks_mutator(expr, &context); } @@ -2226,6 +2236,7 @@ process_sublinks_mutator(Node *node, process_sublinks_context *context) process_sublinks_context locContext; locContext.root = context->root; + locContext.skipPHVs = context->skipPHVs; if (node == NULL) return NULL; @@ -2259,10 +2270,11 @@ process_sublinks_mutator(Node *node, process_sublinks_context *context) * ReturningExpr they'll be handled when build_subplan collects it into * the arguments to be passed down to the current subplan, while an outer * PHV's expression has already been preprocessed by its owning level. + * That also holds for a PHV within such an argument (skipPHVs). */ if (IsA(node, PlaceHolderVar)) { - if (((PlaceHolderVar *) node)->phlevelsup > 0) + if (((PlaceHolderVar *) node)->phlevelsup > 0 || context->skipPHVs) return node; } else if (IsA(node, Aggref)) diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index f8a4b14da49..88d9d362d7c 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -7430,6 +7430,31 @@ from (select case when false then remov.id else (select i41.f1) end as c1 Output: ((SubPlan expr_1)) (14 rows) +-- likewise, where the pushed-down PHV sits within an outer-level aggregate +explain (verbose, costs off) +select (select sum(ss1.c1) from int4_tbl i43) as c2 +from (select (select i41.f1) as c1 from int4_tbl i41) ss1 + right join int4_tbl i42 on true; + QUERY PLAN +--------------------------------------------------- + Aggregate + Output: (SubPlan expr_2) + -> Nested Loop Left Join + Output: ((SubPlan expr_1)) + -> Seq Scan on public.int4_tbl i42 + Output: i42.f1 + -> Materialize + Output: ((SubPlan expr_1)) + -> Seq Scan on public.int4_tbl i41 + Output: (SubPlan expr_1) + SubPlan expr_1 + -> Result + Output: i41.f1 + SubPlan expr_2 + -> Seq Scan on public.int4_tbl i43 + Output: sum(((SubPlan expr_1))) +(16 rows) + -- likewise, where the pushed-down PHV's expression contains another PHV of -- the same level, which must not be preprocessed separately from its parent explain (verbose, costs off) diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 7c5346fa462..f6cf18b8872 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -2746,6 +2746,12 @@ from (select case when false then remov.id else (select i41.f1) end as c1 from int4_tbl i41 left join a remov on i41.f1 = remov.id) ss1 right join int4_tbl i42 on true; +-- likewise, where the pushed-down PHV sits within an outer-level aggregate +explain (verbose, costs off) +select (select sum(ss1.c1) from int4_tbl i43) as c2 +from (select (select i41.f1) as c1 from int4_tbl i41) ss1 + right join int4_tbl i42 on true; + -- likewise, where the pushed-down PHV's expression contains another PHV of -- the same level, which must not be preprocessed separately from its parent explain (verbose, costs off)