| /*------------------------------------------------------------------------- |
| * |
| * 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-2023, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * Portions Copyright (c) 2006-2008, Greenplum inc |
| * Portions Copyright (c) 2012-Present VMware, Inc. or its affiliates. |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/optimizer/util/var.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "access/htup.h" |
| #include "access/sysattr.h" |
| #include "nodes/nodeFuncs.h" |
| #include "optimizer/optimizer.h" |
| #include "optimizer/placeholder.h" |
| #include "optimizer/prep.h" |
| #include "optimizer/walkers.h" |
| #include "parser/parsetree.h" |
| #include "rewrite/rewriteManip.h" |
| #include "utils/lsyscache.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 |
| { |
| PlannerInfo *root; /* could be NULL! */ |
| 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 Node *add_nullingrels_if_needed(PlannerInfo *root, Node *newnode, |
| Var *oldvar); |
| static bool is_standard_join_alias_expression(Node *newnode, Var *oldvar); |
| static void adjust_standard_join_alias_expression(Node *newnode, Var *oldvar); |
| static Relids alias_relid_set(Query *query, Relids relids); |
| |
| |
| /* |
| * cdb_walk_vars |
| * Invoke callback function on each Var and/or Aggref node in an expression. |
| * If a callback returns true, no further nodes are visited, and true is |
| * returned. Otherwise after visiting all nodes, false is returned. |
| * |
| * Will recurse into sublinks. Also, may be invoked directly on a Query. |
| */ |
| typedef bool (*Cdb_walk_vars_callback_Aggref)(Aggref *aggref, void *context, int sublevelsup); |
| typedef bool (*Cdb_walk_vars_callback_Var)(Var *var, void *context, int sublevelsup); |
| typedef bool (*Cdb_walk_vars_callback_CurrentOf)(CurrentOfExpr *expr, void *context, int sublevelsup); |
| typedef bool (*Cdb_walk_vars_callback_placeholdervar)(PlaceHolderVar *expr, void *context, int sublevelsup); |
| |
| typedef struct Cdb_walk_vars_context |
| { |
| Cdb_walk_vars_callback_Var callback_var; |
| Cdb_walk_vars_callback_Aggref callback_aggref; |
| Cdb_walk_vars_callback_CurrentOf callback_currentof; |
| Cdb_walk_vars_callback_placeholdervar callback_placeholdervar; |
| void *context; |
| int sublevelsup; |
| } Cdb_walk_vars_context; |
| |
| static bool |
| cdb_walk_vars_walker(Node *node, void *wvwcontext) |
| { |
| Cdb_walk_vars_context *ctx = (Cdb_walk_vars_context *)wvwcontext; |
| |
| if (node == NULL) |
| return false; |
| |
| if (IsA(node, Var) && |
| ctx->callback_var != NULL) |
| return ctx->callback_var((Var *)node, ctx->context, ctx->sublevelsup); |
| |
| if (IsA(node, Aggref) && |
| ctx->callback_aggref != NULL) |
| return ctx->callback_aggref((Aggref *)node, ctx->context, ctx->sublevelsup); |
| |
| if (IsA(node, CurrentOfExpr) && |
| ctx->callback_currentof != NULL) |
| return ctx->callback_currentof((CurrentOfExpr *)node, ctx->context, ctx->sublevelsup); |
| |
| if (IsA(node, PlaceHolderVar) && |
| ctx->callback_placeholdervar != NULL) |
| return ctx->callback_placeholdervar((PlaceHolderVar *)node, ctx->context, ctx->sublevelsup); |
| |
| if (IsA(node, Query)) |
| { |
| bool b; |
| |
| /* Recurse into subselects */ |
| ctx->sublevelsup++; |
| b = query_tree_walker((Query *)node, cdb_walk_vars_walker, ctx, 0); |
| ctx->sublevelsup--; |
| return b; |
| } |
| return expression_tree_walker(node, cdb_walk_vars_walker, ctx); |
| } /* cdb_walk_vars_walker */ |
| |
| static bool |
| cdb_walk_vars(Node *node, |
| Cdb_walk_vars_callback_Var callback_var, |
| Cdb_walk_vars_callback_Aggref callback_aggref, |
| Cdb_walk_vars_callback_CurrentOf callback_currentof, |
| Cdb_walk_vars_callback_placeholdervar callback_placeholdervar, |
| void *context, |
| int levelsup) |
| { |
| Cdb_walk_vars_context ctx; |
| |
| ctx.callback_var = callback_var; |
| ctx.callback_aggref = callback_aggref; |
| ctx.callback_currentof = callback_currentof; |
| ctx.callback_placeholdervar = callback_placeholdervar; |
| ctx.context = context; |
| ctx.sublevelsup = 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 levelsdown. |
| */ |
| return query_or_expression_tree_walker(node, cdb_walk_vars_walker, &ctx, 0); |
| } /* cdb_walk_vars */ |
| |
| /* |
| * pull_varnos |
| * Create a set of all the distinct varnos present in a parsetree. |
| * Only varnos that reference level-zero rtable entries are considered. |
| * |
| * The result includes outer-join relids mentioned in Var.varnullingrels and |
| * PlaceHolderVar.phnullingrels fields in the parsetree. |
| * |
| * "root" can be passed as NULL if it is not necessary to process |
| * PlaceHolderVars. |
| * |
| * 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); |
| context->varnos = bms_add_members(context->varnos, |
| var->varnullingrels); |
| } |
| 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. We'll also do that when the |
| * caller doesn't pass a "root" pointer. (We probably shouldn't see |
| * PlaceHolderVars at all in such cases, but if we do, this is a |
| * reasonable behavior.) |
| */ |
| if (phv->phlevelsup == context->sublevels_up && |
| context->root != NULL) |
| { |
| /* |
| * 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). |
| * |
| * 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) |
| { |
| if (phv->phid < context->root->placeholder_array_size) |
| phinfo = context->root->placeholder_array[phv->phid]; |
| } |
| 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); |
| } |
| |
| /* |
| * In all three cases, include phnullingrels in the result. We |
| * don't worry about possibly needing to translate it, because |
| * appendrels only translate varnos of baserels, not outer joins. |
| */ |
| context->varnos = bms_add_members(context->varnos, |
| phv->phnullingrels); |
| 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); |
| } |
| |
| /* |
| * contain_vars_of_level_or_above |
| * Recursively scan a clause to discover whether it contains any Var or |
| * Aggref nodes of the specified query level or above. For example, |
| * pass 1 to detect all nonlocal Vars. |
| * |
| * Returns true if any such Var found. |
| * |
| * Will recurse into sublinks. Also, may be invoked directly on a Query. |
| */ |
| static bool |
| contain_vars_of_level_or_above_cbVar(Var *var, void *unused, int sublevelsup) |
| { |
| if ((int)var->varlevelsup >= sublevelsup) |
| return true; /* abort tree traversal and return true */ |
| return false; |
| } |
| |
| static bool |
| contain_vars_of_level_or_above_cbAggref(Aggref *aggref, void *unused, int sublevelsup) |
| { |
| if ((int)aggref->agglevelsup >= sublevelsup) |
| return true; |
| |
| /* visit aggregate's args */ |
| return cdb_walk_vars((Node *)aggref->args, |
| contain_vars_of_level_or_above_cbVar, |
| contain_vars_of_level_or_above_cbAggref, |
| NULL, |
| NULL, |
| NULL, // GPDB_84_MERGE_FIXME: Can arguments of Aggref contain PlaceHolderVars ? |
| sublevelsup); |
| } |
| |
| static bool |
| contain_vars_of_level_or_above_cbPlaceHolderVar(PlaceHolderVar *placeholdervar, void *unused, int sublevelsup) |
| { |
| if(placeholdervar->phlevelsup >= sublevelsup) |
| return true; |
| |
| /* visit placeholder's contained expression */ |
| return cdb_walk_vars((Node*)placeholdervar->phexpr, |
| contain_vars_of_level_or_above_cbVar, |
| contain_vars_of_level_or_above_cbAggref, |
| NULL, |
| contain_vars_of_level_or_above_cbPlaceHolderVar, |
| NULL, |
| sublevelsup); |
| } |
| |
| bool |
| contain_vars_of_level_or_above(Node *node, int levelsup) |
| { |
| return cdb_walk_vars(node, |
| contain_vars_of_level_or_above_cbVar, |
| contain_vars_of_level_or_above_cbAggref, |
| NULL, |
| contain_vars_of_level_or_above_cbPlaceHolderVar, |
| NULL, |
| levelsup); |
| } |
| |
| /* |
| * 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, GroupId)) |
| { |
| if (((GroupId *) node)->agglevelsup != 0) |
| elog(ERROR, "Upper-level GROUP_ID 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) |
| { |
| /* |
| * we do NOT descend into the contained expression, |
| * even if the caller asked for it, because we never |
| * actually evaluate it - the result is driven entirely |
| * off the associated GROUP BY clause, so we never need |
| * to extract the actual Vars here. |
| */ |
| return false; |
| } |
| else |
| elog(ERROR, "GROUP_ID 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+OJ 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 two important ways. First, 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. |
| * Second, we have to preserve any varnullingrels info attached to the |
| * alias Vars we're replacing. If the replacement expression is a Var or |
| * PlaceHolderVar or constructed from those, we can just add the |
| * varnullingrels bits to the existing nullingrels field(s); otherwise |
| * we have to add a PlaceHolderVar wrapper. |
| * |
| * NOTE: this is also used by the parser, to expand join alias Vars before |
| * checking GROUP BY validity. For that use-case, root will be NULL, which |
| * is why we have to pass the Query separately. We need the root itself only |
| * for making PlaceHolderVars. We can avoid making PlaceHolderVars in the |
| * parser's usage because it won't be dealing with arbitrary expressions: |
| * so long as adjust_standard_join_alias_expression can handle everything |
| * the parser would make as a join alias expression, we're OK. |
| */ |
| Node * |
| flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node) |
| { |
| flatten_join_alias_vars_context context; |
| |
| /* |
| * We do not expect this to be applied to the whole Query, only to |
| * expressions or LATERAL subqueries. Hence, if the top node is a Query, |
| * it's okay to immediately increment sublevels_up. |
| */ |
| Assert(node != (Node *) query); |
| |
| context.root = root; |
| 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; |
| ListCell *lv; |
| ListCell *ln; |
| |
| Assert(list_length(rte->joinaliasvars) == list_length(rte->eref->colnames)); |
| forboth(lv, rte->joinaliasvars, ln, rte->eref->colnames) |
| { |
| newvar = (Node *) lfirst(lv); |
| /* 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; |
| /* vartype will always be RECORDOID, so we always need colnames */ |
| rowexpr->colnames = colnames; |
| rowexpr->location = var->location; |
| |
| /* Lastly, add any varnullingrels to the replacement expression */ |
| return add_nullingrels_if_needed(context->root, (Node *) rowexpr, |
| var); |
| } |
| |
| /* 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); |
| |
| /* Lastly, add any varnullingrels to the replacement expression */ |
| return add_nullingrels_if_needed(context->root, newvar, var); |
| } |
| 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); |
| /* we *don't* change phnullingrels */ |
| } |
| 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); |
| } |
| |
| /* |
| * Add oldvar's varnullingrels, if any, to a flattened join alias expression. |
| * The newnode has been copied, so we can modify it freely. |
| */ |
| static Node * |
| add_nullingrels_if_needed(PlannerInfo *root, Node *newnode, Var *oldvar) |
| { |
| if (oldvar->varnullingrels == NULL) |
| return newnode; /* nothing to do */ |
| /* If possible, do it by adding to existing nullingrel fields */ |
| if (is_standard_join_alias_expression(newnode, oldvar)) |
| adjust_standard_join_alias_expression(newnode, oldvar); |
| else if (root) |
| { |
| /* |
| * We can insert a PlaceHolderVar to carry the nullingrels. However, |
| * deciding where to evaluate the PHV is slightly tricky. We first |
| * try to evaluate it at the natural semantic level of the new |
| * expression; but if that expression is variable-free, fall back to |
| * evaluating it at the join that the oldvar is an alias Var for. |
| */ |
| PlaceHolderVar *newphv; |
| Index levelsup = oldvar->varlevelsup; |
| Relids phrels = pull_varnos_of_level(root, newnode, levelsup); |
| |
| if (bms_is_empty(phrels)) /* variable-free? */ |
| { |
| if (levelsup != 0) /* this won't work otherwise */ |
| elog(ERROR, "unsupported join alias expression"); |
| phrels = get_relids_for_join(root->parse, oldvar->varno); |
| /* If it's an outer join, eval below not above the join */ |
| phrels = bms_del_member(phrels, oldvar->varno); |
| Assert(!bms_is_empty(phrels)); |
| } |
| newphv = make_placeholder_expr(root, (Expr *) newnode, phrels); |
| /* newphv has zero phlevelsup and NULL phnullingrels; fix it */ |
| newphv->phlevelsup = levelsup; |
| newphv->phnullingrels = bms_copy(oldvar->varnullingrels); |
| newnode = (Node *) newphv; |
| } |
| else |
| { |
| /* ooops, we're missing support for something the parser can make */ |
| elog(ERROR, "unsupported join alias expression"); |
| } |
| return newnode; |
| } |
| |
| /* |
| * Check to see if we can insert nullingrels into this join alias expression |
| * without use of a separate PlaceHolderVar. |
| * |
| * This will handle Vars, PlaceHolderVars, and implicit-coercion and COALESCE |
| * expressions built from those. This coverage needs to handle anything |
| * that the parser would put into joinaliasvars. |
| */ |
| static bool |
| is_standard_join_alias_expression(Node *newnode, Var *oldvar) |
| { |
| if (newnode == NULL) |
| return false; |
| if (IsA(newnode, Var) && |
| ((Var *) newnode)->varlevelsup == oldvar->varlevelsup) |
| return true; |
| else if (IsA(newnode, PlaceHolderVar) && |
| ((PlaceHolderVar *) newnode)->phlevelsup == oldvar->varlevelsup) |
| return true; |
| else if (IsA(newnode, FuncExpr)) |
| { |
| FuncExpr *fexpr = (FuncExpr *) newnode; |
| |
| /* |
| * We need to assume that the function wouldn't produce non-NULL from |
| * NULL, which is reasonable for implicit coercions but otherwise not |
| * so much. (Looking at its strictness is likely overkill, and anyway |
| * it would cause us to fail if someone forgot to mark an implicit |
| * coercion as strict.) |
| */ |
| if (fexpr->funcformat != COERCE_IMPLICIT_CAST || |
| fexpr->args == NIL) |
| return false; |
| |
| /* |
| * Examine only the first argument --- coercions might have additional |
| * arguments that are constants. |
| */ |
| return is_standard_join_alias_expression(linitial(fexpr->args), oldvar); |
| } |
| else if (IsA(newnode, RelabelType)) |
| { |
| RelabelType *relabel = (RelabelType *) newnode; |
| |
| /* This definitely won't produce non-NULL from NULL */ |
| return is_standard_join_alias_expression((Node *) relabel->arg, oldvar); |
| } |
| else if (IsA(newnode, CoerceViaIO)) |
| { |
| CoerceViaIO *iocoerce = (CoerceViaIO *) newnode; |
| |
| /* This definitely won't produce non-NULL from NULL */ |
| return is_standard_join_alias_expression((Node *) iocoerce->arg, oldvar); |
| } |
| else if (IsA(newnode, ArrayCoerceExpr)) |
| { |
| ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) newnode; |
| |
| /* This definitely won't produce non-NULL from NULL (at array level) */ |
| return is_standard_join_alias_expression((Node *) acoerce->arg, oldvar); |
| } |
| else if (IsA(newnode, CoalesceExpr)) |
| { |
| CoalesceExpr *cexpr = (CoalesceExpr *) newnode; |
| ListCell *lc; |
| |
| Assert(cexpr->args != NIL); |
| foreach(lc, cexpr->args) |
| { |
| if (!is_standard_join_alias_expression(lfirst(lc), oldvar)) |
| return false; |
| } |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| /* |
| * Insert nullingrels into an expression accepted by |
| * is_standard_join_alias_expression. |
| */ |
| static void |
| adjust_standard_join_alias_expression(Node *newnode, Var *oldvar) |
| { |
| if (IsA(newnode, Var) && |
| ((Var *) newnode)->varlevelsup == oldvar->varlevelsup) |
| { |
| Var *newvar = (Var *) newnode; |
| |
| newvar->varnullingrels = bms_add_members(newvar->varnullingrels, |
| oldvar->varnullingrels); |
| } |
| else if (IsA(newnode, PlaceHolderVar) && |
| ((PlaceHolderVar *) newnode)->phlevelsup == oldvar->varlevelsup) |
| { |
| PlaceHolderVar *newphv = (PlaceHolderVar *) newnode; |
| |
| newphv->phnullingrels = bms_add_members(newphv->phnullingrels, |
| oldvar->varnullingrels); |
| } |
| else if (IsA(newnode, FuncExpr)) |
| { |
| FuncExpr *fexpr = (FuncExpr *) newnode; |
| |
| adjust_standard_join_alias_expression(linitial(fexpr->args), oldvar); |
| } |
| else if (IsA(newnode, RelabelType)) |
| { |
| RelabelType *relabel = (RelabelType *) newnode; |
| |
| adjust_standard_join_alias_expression((Node *) relabel->arg, oldvar); |
| } |
| else if (IsA(newnode, CoerceViaIO)) |
| { |
| CoerceViaIO *iocoerce = (CoerceViaIO *) newnode; |
| |
| adjust_standard_join_alias_expression((Node *) iocoerce->arg, oldvar); |
| } |
| else if (IsA(newnode, ArrayCoerceExpr)) |
| { |
| ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) newnode; |
| |
| adjust_standard_join_alias_expression((Node *) acoerce->arg, oldvar); |
| } |
| else if (IsA(newnode, CoalesceExpr)) |
| { |
| CoalesceExpr *cexpr = (CoalesceExpr *) newnode; |
| ListCell *lc; |
| |
| Assert(cexpr->args != NIL); |
| foreach(lc, cexpr->args) |
| { |
| adjust_standard_join_alias_expression(lfirst(lc), oldvar); |
| } |
| } |
| else |
| Assert(false); |
| } |
| |
| /* |
| * alias_relid_set: in a set of RT indexes, replace joins by their |
| * underlying base+OJ 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; |
| } |