aboutsummaryrefslogtreecommitdiff
path: root/src/backend/nodes/nodeFuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2019-12-14 13:49:15 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2019-12-14 13:49:15 -0500
commit6ea364e7e7d5f298fc965006caa6c228c743fe77 (patch)
tree41b5e34aef5e8bdd2e5ac387dcc05141a0c4c2de /src/backend/nodes/nodeFuncs.c
parente0e569e1d192c3fed942257302f24b550cf982f4 (diff)
downloadpostgresql-6ea364e7e7d5f298fc965006caa6c228c743fe77.tar.gz
postgresql-6ea364e7e7d5f298fc965006caa6c228c743fe77.zip
Prevent overly-aggressive collapsing of joins to RTE_RESULT relations.
The RTE_RESULT simplification logic added by commit 4be058fe9 had a flaw: it would collapse out a RTE_RESULT that is due to compute a PlaceHolderVar, and reassign the PHV to the parent join level, even if another input relation of the join contained a lateral reference to the PHV. That can't work because the PHV would be computed too late. In practice it led to failures of internal sanity checks later in planning (either assertion failures or errors such as "failed to construct the join relation"). To fix, add code to check for the presence of such PHVs in relevant portions of the query tree. Notably, this required refactoring range_table_walker so that a caller could ask to walk individual RTEs not the whole list. (It might be a good idea to refactor range_table_mutator in the same way, if only to keep those functions looking similar; but I didn't do so here as it wasn't necessary for the bug fix.) This exercise also taught me that find_dependent_phvs(), as it stood, could only safely be used on the entire Query, not on subtrees. Adjust its API to reflect that; which in passing allows it to have a fast path for the common case of no PHVs anywhere. Per report from Will Leinweber. Back-patch to v12 where the bug was introduced. Discussion: https://postgr.es/m/CALLb-4xJMd4GZt2YCecMC95H-PafuWNKcmps4HLRx2NHNBfB4g@mail.gmail.com
Diffstat (limited to 'src/backend/nodes/nodeFuncs.c')
-rw-r--r--src/backend/nodes/nodeFuncs.c107
1 files changed, 61 insertions, 46 deletions
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 880b0ec6846..34eda88099b 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -2378,59 +2378,74 @@ range_table_walker(List *rtable,
foreach(rt, rtable)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+ RangeTblEntry *rte = lfirst_node(RangeTblEntry, rt);
- /*
- * Walkers might need to examine the RTE node itself either before or
- * after visiting its contents (or, conceivably, both). Note that if
- * you specify neither flag, the walker won't visit the RTE at all.
- */
- if (flags & QTW_EXAMINE_RTES_BEFORE)
- if (walker(rte, context))
- return true;
+ if (range_table_entry_walker(rte, walker, context, flags))
+ return true;
+ }
+ return false;
+}
- switch (rte->rtekind)
- {
- case RTE_RELATION:
- if (walker(rte->tablesample, context))
- return true;
- break;
- case RTE_SUBQUERY:
- if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
- if (walker(rte->subquery, context))
- return true;
- break;
- case RTE_JOIN:
- if (!(flags & QTW_IGNORE_JOINALIASES))
- if (walker(rte->joinaliasvars, context))
- return true;
- break;
- case RTE_FUNCTION:
- if (walker(rte->functions, context))
- return true;
- break;
- case RTE_TABLEFUNC:
- if (walker(rte->tablefunc, context))
+/*
+ * Some callers even want to scan the expressions in individual RTEs.
+ */
+bool
+range_table_entry_walker(RangeTblEntry *rte,
+ bool (*walker) (),
+ void *context,
+ int flags)
+{
+ /*
+ * Walkers might need to examine the RTE node itself either before or
+ * after visiting its contents (or, conceivably, both). Note that if you
+ * specify neither flag, the walker won't be called on the RTE at all.
+ */
+ if (flags & QTW_EXAMINE_RTES_BEFORE)
+ if (walker(rte, context))
+ return true;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ if (walker(rte->tablesample, context))
+ return true;
+ break;
+ case RTE_SUBQUERY:
+ if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
+ if (walker(rte->subquery, context))
return true;
- break;
- case RTE_VALUES:
- if (walker(rte->values_lists, context))
+ break;
+ case RTE_JOIN:
+ if (!(flags & QTW_IGNORE_JOINALIASES))
+ if (walker(rte->joinaliasvars, context))
return true;
- break;
- case RTE_CTE:
- case RTE_NAMEDTUPLESTORE:
- case RTE_RESULT:
- /* nothing to do */
- break;
- }
+ break;
+ case RTE_FUNCTION:
+ if (walker(rte->functions, context))
+ return true;
+ break;
+ case RTE_TABLEFUNC:
+ if (walker(rte->tablefunc, context))
+ return true;
+ break;
+ case RTE_VALUES:
+ if (walker(rte->values_lists, context))
+ return true;
+ break;
+ case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
+ case RTE_RESULT:
+ /* nothing to do */
+ break;
+ }
- if (walker(rte->securityQuals, context))
+ if (walker(rte->securityQuals, context))
+ return true;
+
+ if (flags & QTW_EXAMINE_RTES_AFTER)
+ if (walker(rte, context))
return true;
- if (flags & QTW_EXAMINE_RTES_AFTER)
- if (walker(rte, context))
- return true;
- }
return false;
}