aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-08-09 11:21:39 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-08-09 11:21:39 -0400
commitb919a97a6cd204cbd9b77d12c9e60ad59eea04a4 (patch)
treeec192f44be4f0ec0057539c30ba3dec587a9c9b9 /src/backend/utils/adt/ruleutils.c
parent7da1bdc2c2f17038f2ae1900be90a0d7b5e361e0 (diff)
downloadpostgresql-b919a97a6cd204cbd9b77d12c9e60ad59eea04a4.tar.gz
postgresql-b919a97a6cd204cbd9b77d12c9e60ad59eea04a4.zip
Fix "failed to find plan for subquery/CTE" errors in EXPLAIN.
To deparse a reference to a field of a RECORD-type output of a subquery, EXPLAIN normally digs down into the subquery's plan to try to discover exactly which anonymous RECORD type is meant. However, this can fail if the subquery has been optimized out of the plan altogether on the grounds that no rows could pass the WHERE quals, which has been possible at least since 3fc6e2d7f. There isn't anything remaining in the plan tree that would help us, so fall back to printing the field name as "fN" for the N'th column of the record. (This will actually be the right thing some of the time, since it matches the column names we assign to RowExprs.) In passing, fix a comment typo in create_projection_plan, which I noticed while experimenting with an alternative fix for this. Per bug #18576 from Vasya B. Back-patch to all supported branches. Richard Guo and Tom Lane Discussion: https://postgr.es/m/18576-9feac34e132fea9e@postgresql.org
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c50
1 files changed, 37 insertions, 13 deletions
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",