diff options
Diffstat (limited to 'src/backend/optimizer/util/var.c')
-rw-r--r-- | src/backend/optimizer/util/var.c | 135 |
1 files changed, 125 insertions, 10 deletions
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)) { |