aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/plan/createplan.c2
-rw-r--r--src/backend/utils/adt/ruleutils.c50
-rw-r--r--src/test/regress/expected/rowtypes.out54
-rw-r--r--src/test/regress/sql/rowtypes.sql21
4 files changed, 113 insertions, 14 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index c6d18ae00f3..28addc1129a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2022,7 +2022,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags)
* Convert our subpath to a Plan and determine whether we need a Result
* node.
*
- * In most cases where we don't need to project, creation_projection_path
+ * In most cases where we don't need to project, create_projection_path
* will have set dummypp, but not always. First, some createplan.c
* routines change the tlists of their nodes. (An example is that
* create_merge_append_plan might add resjunk sort columns to a
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 653685bffc5..c656afc35a1 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7895,17 +7895,31 @@ get_name_for_var_field(Var *var, int fieldno,
/*
* We're deparsing a Plan tree so we don't have complete
* RTE entries (in particular, rte->subquery is NULL). But
- * the only place we'd see a Var directly referencing a
- * SUBQUERY RTE is in a SubqueryScan plan node, and we can
- * look into the child plan's tlist instead.
+ * the only place we'd normally see a Var directly
+ * referencing a SUBQUERY RTE is in a SubqueryScan plan
+ * node, and we can look into the child plan's tlist
+ * instead. An exception occurs if the subquery was
+ * proven empty and optimized away: then we'd find such a
+ * Var in a childless Result node, and there's nothing in
+ * the plan tree that would let us figure out what it had
+ * originally referenced. In that case, fall back on
+ * printing "fN", analogously to the default column names
+ * for RowExprs.
*/
TargetEntry *tle;
deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
- elog(ERROR, "failed to find plan for subquery %s",
- rte->eref->aliasname);
+ {
+ char *dummy_name = palloc(32);
+
+ Assert(IsA(dpns->plan, Result));
+ snprintf(dummy_name, 32, "f%d", fieldno);
+ return dummy_name;
+ }
+ Assert(IsA(dpns->plan, SubqueryScan));
+
tle = get_tle_by_resno(dpns->inner_tlist, attnum);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d",
@@ -8014,20 +8028,30 @@ get_name_for_var_field(Var *var, int fieldno,
{
/*
* We're deparsing a Plan tree so we don't have a CTE
- * list. But the only places we'd see a Var directly
- * referencing a CTE RTE are in CteScan or WorkTableScan
- * plan nodes. For those cases, set_deparse_plan arranged
- * for dpns->inner_plan to be the plan node that emits the
- * CTE or RecursiveUnion result, and we can look at its
- * tlist instead.
+ * list. But the only places we'd normally see a Var
+ * directly referencing a CTE RTE are in CteScan or
+ * WorkTableScan plan nodes. For those cases,
+ * set_deparse_plan arranged for dpns->inner_plan to be
+ * the plan node that emits the CTE or RecursiveUnion
+ * result, and we can look at its tlist instead. As
+ * above, this can fail if the CTE has been proven empty,
+ * in which case fall back to "fN".
*/
TargetEntry *tle;
deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
- elog(ERROR, "failed to find plan for CTE %s",
- rte->eref->aliasname);
+ {
+ char *dummy_name = palloc(32);
+
+ Assert(IsA(dpns->plan, Result));
+ snprintf(dummy_name, 32, "f%d", fieldno);
+ return dummy_name;
+ }
+ Assert(IsA(dpns->plan, CteScan) ||
+ IsA(dpns->plan, WorkTableScan));
+
tle = get_tle_by_resno(dpns->inner_tlist, attnum);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d",
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index b400b58f763..9168979a620 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -1301,6 +1301,60 @@ select pg_get_viewdef('composite_v', true);
drop view composite_v;
--
+-- Check cases where the composite comes from a proven-dummy rel (bug #18576)
+--
+explain (verbose, costs off)
+select (ss.a).x, (ss.a).n from
+ (select information_schema._pg_expandarray(array[1,2]) AS a) ss;
+ QUERY PLAN
+------------------------------------------------------------------------
+ Subquery Scan on ss
+ Output: (ss.a).x, (ss.a).n
+ -> ProjectSet
+ Output: information_schema._pg_expandarray('{1,2}'::integer[])
+ -> Result
+(5 rows)
+
+explain (verbose, costs off)
+select (ss.a).x, (ss.a).n from
+ (select information_schema._pg_expandarray(array[1,2]) AS a) ss
+where false;
+ QUERY PLAN
+--------------------------
+ Result
+ Output: (a).f1, (a).f2
+ One-Time Filter: false
+(3 rows)
+
+explain (verbose, costs off)
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select (c).f1 from cte2 as t;
+ QUERY PLAN
+-----------------------------------
+ CTE Scan on cte
+ Output: (cte.c).f1
+ CTE cte
+ -> Result
+ Output: '(1,2)'::record
+(5 rows)
+
+explain (verbose, costs off)
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select (c).f1 from cte2 as t
+where false;
+ QUERY PLAN
+-----------------------------------
+ Result
+ Output: (cte.c).f1
+ One-Time Filter: false
+ CTE cte
+ -> Result
+ Output: '(1,2)'::record
+(6 rows)
+
+--
-- Tests for component access / FieldSelect
--
CREATE TABLE compositetable(a text, b text);
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index fd47dc9e0f6..174b062144a 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -521,6 +521,27 @@ select pg_get_viewdef('composite_v', true);
drop view composite_v;
--
+-- Check cases where the composite comes from a proven-dummy rel (bug #18576)
+--
+explain (verbose, costs off)
+select (ss.a).x, (ss.a).n from
+ (select information_schema._pg_expandarray(array[1,2]) AS a) ss;
+explain (verbose, costs off)
+select (ss.a).x, (ss.a).n from
+ (select information_schema._pg_expandarray(array[1,2]) AS a) ss
+where false;
+
+explain (verbose, costs off)
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select (c).f1 from cte2 as t;
+explain (verbose, costs off)
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select (c).f1 from cte2 as t
+where false;
+
+--
-- Tests for component access / FieldSelect
--
CREATE TABLE compositetable(a text, b text);