diff options
Diffstat (limited to 'src/backend/optimizer/util/var.c')
-rw-r--r-- | src/backend/optimizer/util/var.c | 898 |
1 files changed, 898 insertions, 0 deletions
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c new file mode 100644 index 0000000..4e0b533 --- /dev/null +++ b/src/backend/optimizer/util/var.c @@ -0,0 +1,898 @@ +/*------------------------------------------------------------------------- + * + * 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-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/optimizer/util/var.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/sysattr.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/optimizer.h" +#include "optimizer/placeholder.h" +#include "optimizer/prep.h" +#include "parser/parsetree.h" +#include "rewrite/rewriteManip.h" + + +typedef struct +{ + Relids varnos; + PlannerInfo *root; + int sublevels_up; +} pull_varnos_context; + +typedef struct +{ + Bitmapset *varattnos; + Index varno; +} pull_varattnos_context; + +typedef struct +{ + List *vars; + int sublevels_up; +} pull_vars_context; + +typedef struct +{ + int var_location; + int sublevels_up; +} locate_var_of_level_context; + +typedef struct +{ + List *varlist; + int flags; +} pull_var_clause_context; + +typedef struct +{ + Query *query; /* outer Query */ + int sublevels_up; + bool possible_sublink; /* could aliases include a SubLink? */ + bool inserted_sublink; /* have we inserted a SubLink? */ +} flatten_join_alias_vars_context; + +static bool pull_varnos_walker(Node *node, + pull_varnos_context *context); +static bool pull_varattnos_walker(Node *node, pull_varattnos_context *context); +static bool pull_vars_walker(Node *node, pull_vars_context *context); +static bool contain_var_clause_walker(Node *node, void *context); +static bool contain_vars_of_level_walker(Node *node, int *sublevels_up); +static bool locate_var_of_level_walker(Node *node, + locate_var_of_level_context *context); +static bool pull_var_clause_walker(Node *node, + pull_var_clause_context *context); +static Node *flatten_join_alias_vars_mutator(Node *node, + flatten_join_alias_vars_context *context); +static Relids alias_relid_set(Query *query, Relids relids); + + +/* + * pull_varnos + * Create a set of all the distinct varnos present in a parsetree. + * Only varnos that reference level-zero rtable entries are considered. + * + * NOTE: this is used on not-yet-planned expressions. It may therefore find + * bare SubLinks, and if so it needs to recurse into them to look for uplevel + * references to the desired rtable level! But when we find a completed + * SubPlan, we only need to look at the parameters passed to the subplan. + */ +Relids +pull_varnos(PlannerInfo *root, Node *node) +{ + pull_varnos_context context; + + context.varnos = NULL; + context.root = root; + context.sublevels_up = 0; + + /* + * Must be prepared to start with a Query or a bare expression tree; if + * it's a Query, we don't want to increment sublevels_up. + */ + query_or_expression_tree_walker(node, + pull_varnos_walker, + (void *) &context, + 0); + + return context.varnos; +} + +/* + * pull_varnos_of_level + * Create a set of all the distinct varnos present in a parsetree. + * Only Vars of the specified level are considered. + */ +Relids +pull_varnos_of_level(PlannerInfo *root, Node *node, int levelsup) +{ + pull_varnos_context context; + + context.varnos = NULL; + context.root = root; + context.sublevels_up = levelsup; + + /* + * Must be prepared to start with a Query or a bare expression tree; if + * it's a Query, we don't want to increment sublevels_up. + */ + query_or_expression_tree_walker(node, + pull_varnos_walker, + (void *) &context, + 0); + + return context.varnos; +} + +static bool +pull_varnos_walker(Node *node, pull_varnos_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup == context->sublevels_up) + context->varnos = bms_add_member(context->varnos, var->varno); + return false; + } + if (IsA(node, CurrentOfExpr)) + { + CurrentOfExpr *cexpr = (CurrentOfExpr *) node; + + if (context->sublevels_up == 0) + context->varnos = bms_add_member(context->varnos, cexpr->cvarno); + return false; + } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + /* + * If a PlaceHolderVar is not of the target query level, ignore it, + * instead recursing into its expression to see if it contains any + * vars that are of the target level. + */ + if (phv->phlevelsup == context->sublevels_up) + { + /* + * Ideally, the PHV's contribution to context->varnos is its + * ph_eval_at set. However, this code can be invoked before + * that's been computed. If we cannot find a PlaceHolderInfo, + * fall back to the conservative assumption that the PHV will be + * evaluated at its syntactic level (phv->phrels). + * + * There is a second hazard: this code is also used to examine + * qual clauses during deconstruct_jointree, when we may have a + * PlaceHolderInfo but its ph_eval_at value is not yet final, so + * that theoretically we could obtain a relid set that's smaller + * than we'd see later on. That should never happen though, + * because we deconstruct the jointree working upwards. Any outer + * join that forces delay of evaluation of a given qual clause + * will be processed before we examine that clause here, so the + * ph_eval_at value should have been updated to include it. + * + * Another problem is that a PlaceHolderVar can appear in quals or + * tlists that have been translated for use in a child appendrel. + * Typically such a PHV is a parameter expression sourced by some + * other relation, so that the translation from parent appendrel + * to child doesn't change its phrels, and we should still take + * ph_eval_at at face value. But in corner cases, the PHV's + * original phrels can include the parent appendrel itself, in + * which case the translated PHV will have the child appendrel in + * phrels, and we must translate ph_eval_at to match. + */ + PlaceHolderInfo *phinfo = NULL; + + if (phv->phlevelsup == 0) + { + ListCell *lc; + + foreach(lc, context->root->placeholder_list) + { + phinfo = (PlaceHolderInfo *) lfirst(lc); + if (phinfo->phid == phv->phid) + break; + phinfo = NULL; + } + } + if (phinfo == NULL) + { + /* No PlaceHolderInfo yet, use phrels */ + context->varnos = bms_add_members(context->varnos, + phv->phrels); + } + else if (bms_equal(phv->phrels, phinfo->ph_var->phrels)) + { + /* Normal case: use ph_eval_at */ + context->varnos = bms_add_members(context->varnos, + phinfo->ph_eval_at); + } + else + { + /* Translated PlaceHolderVar: translate ph_eval_at to match */ + Relids newevalat, + delta; + + /* remove what was removed from phv->phrels ... */ + delta = bms_difference(phinfo->ph_var->phrels, phv->phrels); + newevalat = bms_difference(phinfo->ph_eval_at, delta); + /* ... then if that was in fact part of ph_eval_at ... */ + if (!bms_equal(newevalat, phinfo->ph_eval_at)) + { + /* ... add what was added */ + delta = bms_difference(phv->phrels, phinfo->ph_var->phrels); + newevalat = bms_join(newevalat, delta); + } + context->varnos = bms_join(context->varnos, + newevalat); + } + return false; /* don't recurse into expression */ + } + } + else if (IsA(node, Query)) + { + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, pull_varnos_walker, + (void *) context, 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, pull_varnos_walker, + (void *) context); +} + + +/* + * pull_varattnos + * Find all the distinct attribute numbers present in an expression tree, + * and add them to the initial contents of *varattnos. + * Only Vars of the given varno and rtable level zero are considered. + * + * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that + * we can include system attributes (e.g., OID) in the bitmap representation. + * + * Currently, this does not support unplanned subqueries; that is not needed + * for current uses. It will handle already-planned SubPlan nodes, though, + * looking into only the "testexpr" and the "args" list. (The subplan cannot + * contain any other references to Vars of the current level.) + */ +void +pull_varattnos(Node *node, Index varno, Bitmapset **varattnos) +{ + pull_varattnos_context context; + + context.varattnos = *varattnos; + context.varno = varno; + + (void) pull_varattnos_walker(node, &context); + + *varattnos = context.varattnos; +} + +static bool +pull_varattnos_walker(Node *node, pull_varattnos_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varno == context->varno && var->varlevelsup == 0) + context->varattnos = + bms_add_member(context->varattnos, + var->varattno - FirstLowInvalidHeapAttributeNumber); + return false; + } + + /* Should not find an unplanned subquery */ + Assert(!IsA(node, Query)); + + return expression_tree_walker(node, pull_varattnos_walker, + (void *) context); +} + + +/* + * pull_vars_of_level + * Create a list of all Vars (and PlaceHolderVars) referencing the + * specified query level in the given parsetree. + * + * Caution: the Vars are not copied, only linked into the list. + */ +List * +pull_vars_of_level(Node *node, int levelsup) +{ + pull_vars_context context; + + context.vars = NIL; + context.sublevels_up = levelsup; + + /* + * Must be prepared to start with a Query or a bare expression tree; if + * it's a Query, we don't want to increment sublevels_up. + */ + query_or_expression_tree_walker(node, + pull_vars_walker, + (void *) &context, + 0); + + return context.vars; +} + +static bool +pull_vars_walker(Node *node, pull_vars_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup == context->sublevels_up) + context->vars = lappend(context->vars, var); + return false; + } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + if (phv->phlevelsup == context->sublevels_up) + context->vars = lappend(context->vars, phv); + /* we don't want to look into the contained expression */ + return false; + } + if (IsA(node, Query)) + { + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, pull_vars_walker, + (void *) context, 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, pull_vars_walker, + (void *) context); +} + + +/* + * contain_var_clause + * Recursively scan a clause to discover whether it contains any Var nodes + * (of the current query level). + * + * Returns true if any varnode found. + * + * Does not examine subqueries, therefore must only be used after reduction + * of sublinks to subplans! + */ +bool +contain_var_clause(Node *node) +{ + return contain_var_clause_walker(node, NULL); +} + +static bool +contain_var_clause_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + if (((Var *) node)->varlevelsup == 0) + return true; /* abort the tree traversal and return true */ + return false; + } + 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); +} + + +/* + * contain_vars_of_level + * Recursively scan a clause to discover whether it contains any Var nodes + * of the specified query level. + * + * Returns true if any such Var found. + * + * Will recurse into sublinks. Also, may be invoked directly on a Query. + */ +bool +contain_vars_of_level(Node *node, int levelsup) +{ + int sublevels_up = levelsup; + + return query_or_expression_tree_walker(node, + contain_vars_of_level_walker, + (void *) &sublevels_up, + 0); +} + +static bool +contain_vars_of_level_walker(Node *node, int *sublevels_up) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + if (((Var *) node)->varlevelsup == *sublevels_up) + return true; /* abort tree traversal and return true */ + return false; + } + if (IsA(node, CurrentOfExpr)) + { + if (*sublevels_up == 0) + 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 */ + bool result; + + (*sublevels_up)++; + result = query_tree_walker((Query *) node, + contain_vars_of_level_walker, + (void *) sublevels_up, + 0); + (*sublevels_up)--; + return result; + } + return expression_tree_walker(node, + contain_vars_of_level_walker, + (void *) sublevels_up); +} + + +/* + * locate_var_of_level + * Find the parse location of any Var of the specified query level. + * + * Returns -1 if no such Var is in the querytree, or if they all have + * unknown parse location. (The former case is probably caller error, + * but we don't bother to distinguish it from the latter case.) + * + * Will recurse into sublinks. Also, may be invoked directly on a Query. + * + * Note: it might seem appropriate to merge this functionality into + * contain_vars_of_level, but that would complicate that function's API. + * Currently, the only uses of this function are for error reporting, + * and so shaving cycles probably isn't very important. + */ +int +locate_var_of_level(Node *node, int levelsup) +{ + locate_var_of_level_context context; + + context.var_location = -1; /* in case we find nothing */ + context.sublevels_up = levelsup; + + (void) query_or_expression_tree_walker(node, + locate_var_of_level_walker, + (void *) &context, + 0); + + return context.var_location; +} + +static bool +locate_var_of_level_walker(Node *node, + locate_var_of_level_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup == context->sublevels_up && + var->location >= 0) + { + context->var_location = var->location; + return true; /* abort tree traversal and return true */ + } + return false; + } + if (IsA(node, CurrentOfExpr)) + { + /* 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 */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, + locate_var_of_level_walker, + (void *) context, + 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, + locate_var_of_level_walker, + (void *) context); +} + + +/* + * pull_var_clause + * Recursively pulls all Var nodes from an expression clause. + * + * Aggrefs are handled according to these bits in 'flags': + * PVC_INCLUDE_AGGREGATES include Aggrefs in output list + * PVC_RECURSE_AGGREGATES recurse into Aggref arguments + * neither flag throw error if Aggref found + * Vars within an Aggref's expression are included in the result only + * when PVC_RECURSE_AGGREGATES is specified. + * + * WindowFuncs are handled according to these bits in 'flags': + * PVC_INCLUDE_WINDOWFUNCS include WindowFuncs in output list + * PVC_RECURSE_WINDOWFUNCS recurse into WindowFunc arguments + * neither flag throw error if WindowFunc found + * Vars within a WindowFunc's expression are included in the result only + * when PVC_RECURSE_WINDOWFUNCS is specified. + * + * PlaceHolderVars are handled according to these bits in 'flags': + * PVC_INCLUDE_PLACEHOLDERS include PlaceHolderVars in output list + * PVC_RECURSE_PLACEHOLDERS recurse into PlaceHolderVar arguments + * neither flag throw error if PlaceHolderVar found + * Vars within a PHV's expression are included in the result only + * when PVC_RECURSE_PLACEHOLDERS is specified. + * + * GroupingFuncs are treated exactly like Aggrefs, and so do not need + * their own flag bits. + * + * CurrentOfExpr nodes are ignored in all cases. + * + * Upper-level vars (with varlevelsup > 0) should not be seen here, + * likewise for upper-level Aggrefs and PlaceHolderVars. + * + * 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, int flags) +{ + pull_var_clause_context context; + + /* Assert that caller has not specified inconsistent flags */ + Assert((flags & (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES)) + != (PVC_INCLUDE_AGGREGATES | PVC_RECURSE_AGGREGATES)); + Assert((flags & (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS)) + != (PVC_INCLUDE_WINDOWFUNCS | PVC_RECURSE_WINDOWFUNCS)); + Assert((flags & (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS)) + != (PVC_INCLUDE_PLACEHOLDERS | PVC_RECURSE_PLACEHOLDERS)); + + context.varlist = NIL; + context.flags = flags; + + pull_var_clause_walker(node, &context); + return context.varlist; +} + +static bool +pull_var_clause_walker(Node *node, pull_var_clause_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + if (((Var *) node)->varlevelsup != 0) + elog(ERROR, "Upper-level Var found where not expected"); + context->varlist = lappend(context->varlist, node); + return false; + } + else if (IsA(node, Aggref)) + { + if (((Aggref *) node)->agglevelsup != 0) + elog(ERROR, "Upper-level Aggref found where not expected"); + if (context->flags & PVC_INCLUDE_AGGREGATES) + { + context->varlist = lappend(context->varlist, node); + /* we do NOT descend into the contained expression */ + return false; + } + else if (context->flags & PVC_RECURSE_AGGREGATES) + { + /* fall through to recurse into the aggregate's arguments */ + } + else + elog(ERROR, "Aggref found where not expected"); + } + else if (IsA(node, GroupingFunc)) + { + if (((GroupingFunc *) node)->agglevelsup != 0) + elog(ERROR, "Upper-level GROUPING found where not expected"); + if (context->flags & PVC_INCLUDE_AGGREGATES) + { + context->varlist = lappend(context->varlist, node); + /* we do NOT descend into the contained expression */ + return false; + } + else if (context->flags & PVC_RECURSE_AGGREGATES) + { + /* fall through to recurse into the GroupingFunc's arguments */ + } + else + elog(ERROR, "GROUPING found where not expected"); + } + else if (IsA(node, WindowFunc)) + { + /* WindowFuncs have no levelsup field to check ... */ + if (context->flags & PVC_INCLUDE_WINDOWFUNCS) + { + context->varlist = lappend(context->varlist, node); + /* we do NOT descend into the contained expressions */ + return false; + } + else if (context->flags & PVC_RECURSE_WINDOWFUNCS) + { + /* fall through to recurse into the windowfunc's arguments */ + } + else + elog(ERROR, "WindowFunc found where not expected"); + } + else if (IsA(node, PlaceHolderVar)) + { + if (((PlaceHolderVar *) node)->phlevelsup != 0) + elog(ERROR, "Upper-level PlaceHolderVar found where not expected"); + if (context->flags & PVC_INCLUDE_PLACEHOLDERS) + { + context->varlist = lappend(context->varlist, node); + /* we do NOT descend into the contained expression */ + return false; + } + else if (context->flags & PVC_RECURSE_PLACEHOLDERS) + { + /* fall through to recurse into the placeholder's expression */ + } + else + elog(ERROR, "PlaceHolderVar found where not expected"); + } + return expression_tree_walker(node, pull_var_clause_walker, + (void *) context); +} + + +/* + * flatten_join_alias_vars + * Replace Vars that reference JOIN outputs with references to the original + * relation variables instead. This allows quals involving such vars to be + * pushed down. Whole-row Vars that reference JOIN relations are expanded + * into RowExpr constructs that name the individual output Vars. This + * 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. + * + * If a JOIN contains sub-selects that have been flattened, its join alias + * entries might now be arbitrary expressions, not just Vars. This affects + * this function in one important way: we might find ourselves inserting + * SubLink expressions into subqueries, and we must make sure that their + * Query.hasSubLinks fields get set to true if so. If there are any + * SubLinks in the join alias lists, the outer Query should already have + * hasSubLinks = true, so this is only relevant to un-flattened subqueries. + * + * NOTE: this is used on not-yet-planned expressions. We do not expect it + * to be applied directly to the whole Query, so if we see a Query to start + * with, we do want to increment sublevels_up (this occurs for LATERAL + * subqueries). + */ +Node * +flatten_join_alias_vars(Query *query, Node *node) +{ + flatten_join_alias_vars_context context; + + context.query = query; + context.sublevels_up = 0; + /* flag whether join aliases could possibly contain SubLinks */ + context.possible_sublink = query->hasSubLinks; + /* if hasSubLinks is already true, no need to work hard */ + context.inserted_sublink = query->hasSubLinks; + + return flatten_join_alias_vars_mutator(node, &context); +} + +static Node * +flatten_join_alias_vars_mutator(Node *node, + flatten_join_alias_vars_context *context) +{ + if (node == NULL) + return NULL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + RangeTblEntry *rte; + Node *newvar; + + /* No change unless Var belongs to a JOIN of the target level */ + if (var->varlevelsup != context->sublevels_up) + return node; /* no need to copy, really */ + rte = rt_fetch(var->varno, context->query->rtable); + if (rte->rtekind != RTE_JOIN) + return node; + if (var->varattno == InvalidAttrNumber) + { + /* Must expand whole-row reference */ + RowExpr *rowexpr; + List *fields = NIL; + List *colnames = NIL; + AttrNumber attnum; + ListCell *lv; + ListCell *ln; + + attnum = 0; + Assert(list_length(rte->joinaliasvars) == list_length(rte->eref->colnames)); + forboth(lv, rte->joinaliasvars, ln, rte->eref->colnames) + { + newvar = (Node *) lfirst(lv); + attnum++; + /* Ignore dropped columns */ + if (newvar == NULL) + continue; + newvar = copyObject(newvar); + + /* + * If we are expanding an alias carried down from an upper + * query, must adjust its varlevelsup fields. + */ + if (context->sublevels_up != 0) + IncrementVarSublevelsUp(newvar, context->sublevels_up, 0); + /* Preserve original Var's location, if possible */ + if (IsA(newvar, Var)) + ((Var *) newvar)->location = var->location; + /* Recurse in case join input is itself a join */ + /* (also takes care of setting inserted_sublink if needed) */ + newvar = flatten_join_alias_vars_mutator(newvar, context); + fields = lappend(fields, newvar); + /* We need the names of non-dropped columns, too */ + colnames = lappend(colnames, copyObject((Node *) lfirst(ln))); + } + rowexpr = makeNode(RowExpr); + rowexpr->args = fields; + rowexpr->row_typeid = var->vartype; + rowexpr->row_format = COERCE_IMPLICIT_CAST; + rowexpr->colnames = colnames; + rowexpr->location = var->location; + + return (Node *) rowexpr; + } + + /* Expand join alias reference */ + Assert(var->varattno > 0); + newvar = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1); + Assert(newvar != NULL); + newvar = copyObject(newvar); + + /* + * If we are expanding an alias carried down from an upper query, must + * adjust its varlevelsup fields. + */ + if (context->sublevels_up != 0) + IncrementVarSublevelsUp(newvar, context->sublevels_up, 0); + + /* Preserve original Var's location, if possible */ + if (IsA(newvar, Var)) + ((Var *) newvar)->location = var->location; + + /* Recurse in case join input is itself a join */ + newvar = flatten_join_alias_vars_mutator(newvar, context); + + /* Detect if we are adding a sublink to query */ + if (context->possible_sublink && !context->inserted_sublink) + context->inserted_sublink = checkExprHasSubLink(newvar); + + return newvar; + } + 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->query, + phv->phrels); + } + return (Node *) phv; + } + + if (IsA(node, Query)) + { + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ + Query *newnode; + bool save_inserted_sublink; + + context->sublevels_up++; + save_inserted_sublink = context->inserted_sublink; + context->inserted_sublink = ((Query *) node)->hasSubLinks; + newnode = query_tree_mutator((Query *) node, + flatten_join_alias_vars_mutator, + (void *) context, + QTW_IGNORE_JOINALIASES); + newnode->hasSubLinks |= context->inserted_sublink; + context->inserted_sublink = save_inserted_sublink; + context->sublevels_up--; + return (Node *) newnode; + } + /* Already-planned tree not supported */ + Assert(!IsA(node, SubPlan)); + /* Shouldn't need to handle these planner auxiliary nodes here */ + Assert(!IsA(node, SpecialJoinInfo)); + Assert(!IsA(node, PlaceHolderInfo)); + Assert(!IsA(node, MinMaxAggInfo)); + + return expression_tree_mutator(node, flatten_join_alias_vars_mutator, + (void *) context); +} + +/* + * alias_relid_set: in a set of RT indexes, replace joins by their + * underlying base relids + */ +static Relids +alias_relid_set(Query *query, Relids relids) +{ + Relids result = NULL; + int rtindex; + + rtindex = -1; + while ((rtindex = bms_next_member(relids, rtindex)) >= 0) + { + RangeTblEntry *rte = rt_fetch(rtindex, query->rtable); + + if (rte->rtekind == RTE_JOIN) + result = bms_join(result, get_relids_for_join(query, rtindex)); + else + result = bms_add_member(result, rtindex); + } + return result; +} |