summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-10-21 20:42:53 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-10-21 20:42:53 +0000
commite6ae3b5dbf2c07bceb737c5a0ff199b1156051d1 (patch)
treeb8616c6b889741b53c282574195fc41317778dfd /src/backend/optimizer/util
parent831abae5061b98b7f203a24af7badeaaeb15808f (diff)
Add a concept of "placeholder" variables to the planner. These are variables
that represent some expression that we desire to compute below the top level of the plan, and then let that value "bubble up" as though it were a plain Var (ie, a column value). The immediate application is to allow sub-selects to be flattened even when they are below an outer join and have non-nullable output expressions. Formerly we couldn't flatten because such an expression wouldn't properly go to NULL when evaluated above the outer join. Now, we wrap it in a PlaceHolderVar and arrange for the actual evaluation to occur below the outer join. When the resulting Var bubbles up through the join, it will be set to NULL if necessary, yielding the correct results. This fixes a planner limitation that's existed since 7.1. In future we might want to use this mechanism to re-introduce some form of Hellerstein's "expensive functions" optimization, ie place the evaluation of an expensive function at the most suitable point in the plan tree.
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r--src/backend/optimizer/util/Makefile4
-rw-r--r--src/backend/optimizer/util/clauses.c29
-rw-r--r--src/backend/optimizer/util/placeholder.c226
-rw-r--r--src/backend/optimizer/util/relnode.c18
-rw-r--r--src/backend/optimizer/util/tlist.c10
-rw-r--r--src/backend/optimizer/util/var.c135
6 files changed, 400 insertions, 22 deletions
diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefile
index 0ea1dc32cc1..d13b18c53f4 100644
--- a/src/backend/optimizer/util/Makefile
+++ b/src/backend/optimizer/util/Makefile
@@ -4,7 +4,7 @@
# Makefile for optimizer/util
#
# IDENTIFICATION
-# $PostgreSQL: pgsql/src/backend/optimizer/util/Makefile,v 1.18 2008/02/19 10:30:07 petere Exp $
+# $PostgreSQL: pgsql/src/backend/optimizer/util/Makefile,v 1.19 2008/10/21 20:42:53 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -12,7 +12,7 @@ subdir = src/backend/optimizer/util
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = clauses.o joininfo.o pathnode.o plancat.o predtest.o \
+OBJS = clauses.o joininfo.o pathnode.o placeholder.o plancat.o predtest.o \
relnode.o restrictinfo.o tlist.o var.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 14b9313a9aa..c826ecb2ad2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.269 2008/10/09 19:27:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.270 2008/10/21 20:42:53 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1188,6 +1188,12 @@ find_nonnullable_rels_walker(Node *node, bool top_level)
result = find_nonnullable_rels_walker((Node *) expr->quals,
top_level);
}
+ else if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ result = find_nonnullable_rels_walker((Node *) phv->phexpr, top_level);
+ }
return result;
}
@@ -1393,6 +1399,12 @@ find_nonnullable_vars_walker(Node *node, bool top_level)
result = find_nonnullable_vars_walker((Node *) expr->quals,
top_level);
}
+ else if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ result = find_nonnullable_vars_walker((Node *) phv->phexpr, top_level);
+ }
return result;
}
@@ -1921,6 +1933,7 @@ eval_const_expressions(PlannerInfo *root, Node *node)
* constant. This effectively means that we plan using the first supplied
* value of the Param.
* 2. Fold stable, as well as immutable, functions to constants.
+ * 3. Reduce PlaceHolderVar nodes to their contained expressions.
*--------------------
*/
Node *
@@ -2823,6 +2836,20 @@ eval_const_expressions_mutator(Node *node,
newfslink->quals = quals;
return (Node *) newfslink;
}
+ if (IsA(node, PlaceHolderVar) && context->estimate)
+ {
+ /*
+ * In estimation mode, just strip the PlaceHolderVar node altogether;
+ * this amounts to estimating that the contained value won't be forced
+ * to null by an outer join. In regular mode we just use the default
+ * behavior (ie, simplify the expression but leave the PlaceHolderVar
+ * node intact).
+ */
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ return eval_const_expressions_mutator((Node *) phv->phexpr,
+ context);
+ }
/*
* For any node type not handled above, we recurse using
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
new file mode 100644
index 00000000000..aef212b3604
--- /dev/null
+++ b/src/backend/optimizer/util/placeholder.c
@@ -0,0 +1,226 @@
+/*-------------------------------------------------------------------------
+ *
+ * placeholder.c
+ * PlaceHolderVar and PlaceHolderInfo manipulation routines
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/placeholder.c,v 1.1 2008/10/21 20:42:53 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/nodeFuncs.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/placeholder.h"
+#include "optimizer/planmain.h"
+#include "optimizer/var.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * make_placeholder_expr
+ * Make a PlaceHolderVar (and corresponding PlaceHolderInfo)
+ * for the given expression.
+ *
+ * phrels is the syntactic location (as a set of baserels) to attribute
+ * to the expression.
+ */
+PlaceHolderVar *
+make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
+{
+ PlaceHolderVar *phv = makeNode(PlaceHolderVar);
+ PlaceHolderInfo *phinfo = makeNode(PlaceHolderInfo);
+
+ phv->phexpr = expr;
+ phv->phrels = phrels;
+ phv->phid = ++(root->glob->lastPHId);
+ phv->phlevelsup = 0;
+
+ phinfo->phid = phv->phid;
+ phinfo->ph_var = copyObject(phv);
+ phinfo->ph_eval_at = pull_varnos((Node *) phv);
+ /* ph_eval_at may change later, see fix_placeholder_eval_levels */
+ phinfo->ph_needed = NULL; /* initially it's unused */
+ /* for the moment, estimate width using just the datatype info */
+ phinfo->ph_width = get_typavgwidth(exprType((Node *) expr),
+ exprTypmod((Node *) expr));
+
+ root->placeholder_list = lappend(root->placeholder_list, phinfo);
+
+ return phv;
+}
+
+/*
+ * find_placeholder_info
+ * Fetch the PlaceHolderInfo for the given PHV; error if not found
+ */
+PlaceHolderInfo *
+find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
+{
+ ListCell *lc;
+
+ /* if this ever isn't true, we'd need to be able to look in parent lists */
+ Assert(phv->phlevelsup == 0);
+
+ foreach(lc, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+ if (phinfo->phid == phv->phid)
+ return phinfo;
+ }
+ elog(ERROR, "could not find PlaceHolderInfo with id %u", phv->phid);
+ return NULL; /* keep compiler quiet */
+}
+
+/*
+ * fix_placeholder_eval_levels
+ * Adjust the target evaluation levels for placeholders
+ *
+ * The initial eval_at level set by make_placeholder_expr was the set of
+ * rels used in the placeholder's expression (or the whole subselect if
+ * the expr is variable-free). If the subselect contains any outer joins
+ * that can null any of those rels, we must delay evaluation to above those
+ * joins.
+ *
+ * In future we might want to put additional policy/heuristics here to
+ * try to determine an optimal evaluation level. The current rules will
+ * result in evaluation at the lowest possible level.
+ */
+void
+fix_placeholder_eval_levels(PlannerInfo *root)
+{
+ ListCell *lc1;
+
+ foreach(lc1, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc1);
+ Relids syn_level = phinfo->ph_var->phrels;
+ Relids eval_at = phinfo->ph_eval_at;
+ BMS_Membership eval_membership;
+ bool found_some;
+ ListCell *lc2;
+
+ /*
+ * Ignore unreferenced placeholders. Note: if a placeholder is
+ * referenced only by some other placeholder's expr, we will do
+ * the right things because the referencing placeholder must appear
+ * earlier in the list.
+ */
+ if (bms_is_empty(phinfo->ph_needed))
+ continue;
+
+ /*
+ * Check for delays due to lower outer joins. This is the same logic
+ * as in check_outerjoin_delay in initsplan.c, except that we don't
+ * want to modify the delay_upper_joins flags; that was all handled
+ * already during distribute_qual_to_rels.
+ */
+ do
+ {
+ found_some = false;
+ foreach(lc2, root->join_info_list)
+ {
+ SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc2);
+
+ /* disregard joins not within the expr's sub-select */
+ if (!bms_is_subset(sjinfo->syn_lefthand, syn_level) ||
+ !bms_is_subset(sjinfo->syn_righthand, syn_level))
+ continue;
+
+ /* do we reference any nullable rels of this OJ? */
+ if (bms_overlap(eval_at, sjinfo->min_righthand) ||
+ (sjinfo->jointype == JOIN_FULL &&
+ bms_overlap(eval_at, sjinfo->min_lefthand)))
+ {
+ /* yes; have we included all its rels in eval_at? */
+ if (!bms_is_subset(sjinfo->min_lefthand, eval_at) ||
+ !bms_is_subset(sjinfo->min_righthand, eval_at))
+ {
+ /* no, so add them in */
+ eval_at = bms_add_members(eval_at,
+ sjinfo->min_lefthand);
+ eval_at = bms_add_members(eval_at,
+ sjinfo->min_righthand);
+ /* we'll need another iteration */
+ found_some = true;
+ }
+ }
+ }
+ } while (found_some);
+
+ phinfo->ph_eval_at = eval_at;
+
+ /*
+ * Now that we know where to evaluate the placeholder, make sure that
+ * any vars or placeholders it uses will be available at that join
+ * level. (Note that this has to be done within this loop to make
+ * sure we don't skip over such placeholders when we get to them.)
+ */
+ eval_membership = bms_membership(eval_at);
+ if (eval_membership == BMS_MULTIPLE)
+ {
+ List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
+ true);
+
+ add_vars_to_targetlist(root, vars, eval_at);
+ list_free(vars);
+ }
+
+ /*
+ * Also, if the placeholder can be computed at a base rel and is
+ * needed above it, add it to that rel's targetlist. (This is
+ * essentially the same logic as in add_placeholders_to_joinrel, but
+ * we can't do that part until joinrels are formed.)
+ */
+ if (eval_membership == BMS_SINGLETON)
+ {
+ int varno = bms_singleton_member(eval_at);
+ RelOptInfo *rel = find_base_rel(root, varno);
+
+ if (bms_nonempty_difference(phinfo->ph_needed, rel->relids))
+ rel->reltargetlist = lappend(rel->reltargetlist,
+ copyObject(phinfo->ph_var));
+ }
+ }
+}
+
+/*
+ * add_placeholders_to_joinrel
+ * Add any required PlaceHolderVars to a join rel's targetlist.
+ *
+ * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
+ * this join level and (b) the PHV can be computed at or below this level.
+ * At this time we do not need to distinguish whether the PHV will be
+ * computed here or copied up from below.
+ */
+void
+add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel)
+{
+ Relids relids = joinrel->relids;
+ ListCell *lc;
+
+ foreach(lc, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+ /* Is it still needed above this joinrel? */
+ if (bms_nonempty_difference(phinfo->ph_needed, relids))
+ {
+ /* Is it computable here? */
+ if (bms_is_subset(phinfo->ph_eval_at, relids))
+ {
+ /* Yup, add it to the output */
+ joinrel->reltargetlist = lappend(joinrel->reltargetlist,
+ phinfo->ph_var);
+ joinrel->width += phinfo->ph_width;
+ }
+ }
+ }
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 2fb6cd2efe6..ed177fa929d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.91 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.92 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,7 @@
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
#include "optimizer/plancat.h"
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
@@ -354,6 +355,7 @@ build_join_rel(PlannerInfo *root,
*/
build_joinrel_tlist(root, joinrel, outer_rel);
build_joinrel_tlist(root, joinrel, inner_rel);
+ add_placeholders_to_joinrel(root, joinrel);
/*
* Construct restrict and join clause lists for the new joinrel. (The
@@ -403,7 +405,8 @@ build_join_rel(PlannerInfo *root,
/*
* build_joinrel_tlist
- * Builds a join relation's target list.
+ * Builds a join relation's target list from an input relation.
+ * (This is invoked twice to handle the two input relations.)
*
* The join's targetlist includes all Vars of its member relations that
* will still be needed above the join. This subroutine adds all such
@@ -421,16 +424,23 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
foreach(vars, input_rel->reltargetlist)
{
- Var *origvar = (Var *) lfirst(vars);
+ Node *origvar = (Node *) lfirst(vars);
Var *var;
RelOptInfo *baserel;
int ndx;
/*
+ * Ignore PlaceHolderVars in the input tlists; we'll make our
+ * own decisions about whether to copy them.
+ */
+ if (IsA(origvar, PlaceHolderVar))
+ continue;
+
+ /*
* We can't run into any child RowExprs here, but we could find a
* whole-row Var with a ConvertRowtypeExpr atop it.
*/
- var = origvar;
+ var = (Var *) origvar;
while (!IsA(var, Var))
{
if (IsA(var, ConvertRowtypeExpr))
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 2fd16afb5f9..968f4ae367a 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.82 2008/08/25 22:42:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.83 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -91,7 +91,7 @@ tlist_member_ignore_relabel(Node *node, List *targetlist)
List *
flatten_tlist(List *tlist)
{
- List *vlist = pull_var_clause((Node *) tlist, false);
+ List *vlist = pull_var_clause((Node *) tlist, true);
List *new_tlist;
new_tlist = add_to_flat_tlist(NIL, vlist);
@@ -104,7 +104,7 @@ flatten_tlist(List *tlist)
* Add more vars to a flattened tlist (if they're not already in it)
*
* 'tlist' is the flattened tlist
- * 'vars' is a list of var nodes
+ * 'vars' is a list of Var and/or PlaceHolderVar nodes
*
* Returns the extended tlist.
*/
@@ -116,9 +116,9 @@ add_to_flat_tlist(List *tlist, List *vars)
foreach(v, vars)
{
- Var *var = (Var *) lfirst(v);
+ Node *var = (Node *) lfirst(v);
- if (!tlist_member((Node *) var, tlist))
+ if (!tlist_member(var, tlist))
{
TargetEntry *tle;
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 2e230913965..31749e46c05 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -3,12 +3,18 @@
* var.c
* Var node manipulation routines
*
+ * Note: for most purposes, PlaceHolderVar is considered a Var too,
+ * even if its contained expression is variable-free. Also, CurrentOfExpr
+ * is treated as a Var for purposes of determining whether an expression
+ * contains variables.
+ *
+ *
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.80 2008/10/06 17:39:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.81 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -50,7 +56,7 @@ typedef struct
typedef struct
{
List *varlist;
- bool includeUpperVars;
+ bool includePlaceHolderVars;
} pull_var_clause_context;
typedef struct
@@ -128,6 +134,26 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
context->varnos = bms_add_member(context->varnos, cexpr->cvarno);
return false;
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ /*
+ * Normally, we can just take the varnos in the contained expression.
+ * But if it is variable-free, use the PHV's syntactic relids.
+ */
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ pull_varnos_context subcontext;
+
+ subcontext.varnos = NULL;
+ subcontext.sublevels_up = context->sublevels_up;
+ (void) pull_varnos_walker((Node *) phv->phexpr, &subcontext);
+
+ if (bms_is_empty(subcontext.varnos) &&
+ phv->phlevelsup == context->sublevels_up)
+ context->varnos = bms_add_members(context->varnos, phv->phrels);
+ else
+ context->varnos = bms_join(context->varnos, subcontext.varnos);
+ return false;
+ }
if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
@@ -215,6 +241,12 @@ contain_var_clause_walker(Node *node, void *context)
}
if (IsA(node, CurrentOfExpr))
return true;
+ if (IsA(node, PlaceHolderVar))
+ {
+ if (((PlaceHolderVar *) node)->phlevelsup == 0)
+ return true; /* abort the tree traversal and return true */
+ /* else fall through to check the contained expr */
+ }
return expression_tree_walker(node, contain_var_clause_walker, context);
}
@@ -256,6 +288,12 @@ contain_vars_of_level_walker(Node *node, int *sublevels_up)
return true;
return false;
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ if (((PlaceHolderVar *) node)->phlevelsup == *sublevels_up)
+ return true; /* abort the tree traversal and return true */
+ /* else fall through to check the contained expr */
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
@@ -329,6 +367,7 @@ locate_var_of_level_walker(Node *node,
/* since CurrentOfExpr doesn't carry location, nothing we can do */
return false;
}
+ /* No extra code needed for PlaceHolderVar; just look in contained expr */
if (IsA(node, Query))
{
/* Recurse into subselects */
@@ -398,6 +437,7 @@ locate_var_of_relation_walker(Node *node,
/* since CurrentOfExpr doesn't carry location, nothing we can do */
return false;
}
+ /* No extra code needed for PlaceHolderVar; just look in contained expr */
if (IsA(node, Query))
{
/* Recurse into subselects */
@@ -527,6 +567,30 @@ find_minimum_var_level_walker(Node *node,
}
}
}
+ /* Likewise, make sure PlaceHolderVar is treated correctly */
+ if (IsA(node, PlaceHolderVar))
+ {
+ int phlevelsup = ((PlaceHolderVar *) node)->phlevelsup;
+
+ /* convert levelsup to frame of reference of original query */
+ phlevelsup -= context->sublevels_up;
+ /* ignore local vars of subqueries */
+ if (phlevelsup >= 0)
+ {
+ if (context->min_varlevel < 0 ||
+ context->min_varlevel > phlevelsup)
+ {
+ context->min_varlevel = phlevelsup;
+
+ /*
+ * As soon as we find a local variable, we can abort the tree
+ * traversal, since min_varlevel is then certainly 0.
+ */
+ if (phlevelsup == 0)
+ return true;
+ }
+ }
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
@@ -548,25 +612,30 @@ find_minimum_var_level_walker(Node *node,
/*
* pull_var_clause
- * Recursively pulls all var nodes from an expression clause.
+ * Recursively pulls all Var nodes from an expression clause.
+ *
+ * PlaceHolderVars are included too, if includePlaceHolderVars is true.
+ * If it isn't true, an error is thrown if any are found.
+ * Note that Vars within a PHV's expression are *not* included.
+ *
+ * CurrentOfExpr nodes are *not* included.
*
- * Upper-level vars (with varlevelsup > 0) are included only
- * if includeUpperVars is true. Most callers probably want
- * to ignore upper-level vars.
+ * Upper-level vars (with varlevelsup > 0) are not included.
+ * (These probably represent errors too, but we don't complain.)
*
- * Returns list of varnodes found. Note the varnodes themselves are not
+ * Returns list of nodes found. Note the nodes themselves are not
* copied, only referenced.
*
* Does not examine subqueries, therefore must only be used after reduction
* of sublinks to subplans!
*/
List *
-pull_var_clause(Node *node, bool includeUpperVars)
+pull_var_clause(Node *node, bool includePlaceHolderVars)
{
pull_var_clause_context context;
context.varlist = NIL;
- context.includeUpperVars = includeUpperVars;
+ context.includePlaceHolderVars = includePlaceHolderVars;
pull_var_clause_walker(node, &context);
return context.varlist;
@@ -579,10 +648,19 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
return false;
if (IsA(node, Var))
{
- if (((Var *) node)->varlevelsup == 0 || context->includeUpperVars)
+ if (((Var *) node)->varlevelsup == 0)
context->varlist = lappend(context->varlist, node);
return false;
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ if (!context->includePlaceHolderVars)
+ elog(ERROR, "PlaceHolderVar found where not expected");
+ if (((PlaceHolderVar *) node)->phlevelsup == 0)
+ context->varlist = lappend(context->varlist, node);
+ /* we do NOT descend into the contained expression */
+ return false;
+ }
return expression_tree_walker(node, pull_var_clause_walker,
(void *) context);
}
@@ -597,6 +675,9 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
* is necessary since we will not scan the JOIN as a base relation, which
* is the only way that the executor can directly handle whole-row Vars.
*
+ * This also adjusts relid sets found in some expression node types to
+ * substitute the contained base rels for any join relid.
+ *
* NOTE: this is used on not-yet-planned expressions. We do not expect it
* to be applied directly to a Query node.
*/
@@ -703,6 +784,40 @@ flatten_join_alias_vars_mutator(Node *node,
}
return (Node *) fslink;
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ /* Copy the PlaceHolderVar node with correct mutation of subnodes */
+ PlaceHolderVar *phv;
+
+ phv = (PlaceHolderVar *) expression_tree_mutator(node,
+ flatten_join_alias_vars_mutator,
+ (void *) context);
+ /* now fix PlaceHolderVar's relid sets */
+ if (phv->phlevelsup == context->sublevels_up)
+ {
+ phv->phrels = alias_relid_set(context->root,
+ phv->phrels);
+ }
+ return (Node *) phv;
+ }
+ if (IsA(node, PlaceHolderInfo))
+ {
+ /* Copy the PlaceHolderInfo node with correct mutation of subnodes */
+ PlaceHolderInfo *phinfo;
+
+ phinfo = (PlaceHolderInfo *) expression_tree_mutator(node,
+ flatten_join_alias_vars_mutator,
+ (void *) context);
+ /* now fix PlaceHolderInfo's relid sets */
+ if (context->sublevels_up == 0)
+ {
+ phinfo->ph_eval_at = alias_relid_set(context->root,
+ phinfo->ph_eval_at);
+ phinfo->ph_needed = alias_relid_set(context->root,
+ phinfo->ph_needed);
+ }
+ return (Node *) phinfo;
+ }
if (IsA(node, Query))
{