diff options
Diffstat (limited to 'src/backend/optimizer/util')
| -rw-r--r-- | src/backend/optimizer/util/Makefile | 4 | ||||
| -rw-r--r-- | src/backend/optimizer/util/clauses.c | 29 | ||||
| -rw-r--r-- | src/backend/optimizer/util/placeholder.c | 226 | ||||
| -rw-r--r-- | src/backend/optimizer/util/relnode.c | 18 | ||||
| -rw-r--r-- | src/backend/optimizer/util/tlist.c | 10 | ||||
| -rw-r--r-- | src/backend/optimizer/util/var.c | 135 |
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)) { |
