summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util
diff options
context:
space:
mode:
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 0ea1dc32cc..d13b18c53f 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 14b9313a9a..c826ecb2ad 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 0000000000..aef212b360
--- /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 2fb6cd2efe..ed177fa929 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 2fd16afb5f..968f4ae367 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 2e23091396..31749e46c0 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))
{