summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/placeholder.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/placeholder.c')
-rw-r--r--src/backend/optimizer/util/placeholder.c226
1 files changed, 226 insertions, 0 deletions
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;
+ }
+ }
+ }
+}