* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.408 2008/10/07 19:27:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.409 2008/10/21 20:42:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return newnode;
}
+/*
+ * _copyPlaceHolderVar
+ */
+static PlaceHolderVar *
+_copyPlaceHolderVar(PlaceHolderVar *from)
+{
+ PlaceHolderVar *newnode = makeNode(PlaceHolderVar);
+
+ COPY_NODE_FIELD(phexpr);
+ COPY_BITMAPSET_FIELD(phrels);
+ COPY_SCALAR_FIELD(phid);
+ COPY_SCALAR_FIELD(phlevelsup);
+
+ return newnode;
+}
+
/*
* _copySpecialJoinInfo
*/
return newnode;
}
+/*
+ * _copyPlaceHolderInfo
+ */
+static PlaceHolderInfo *
+_copyPlaceHolderInfo(PlaceHolderInfo *from)
+{
+ PlaceHolderInfo *newnode = makeNode(PlaceHolderInfo);
+
+ COPY_SCALAR_FIELD(phid);
+ COPY_NODE_FIELD(ph_var);
+ COPY_BITMAPSET_FIELD(ph_eval_at);
+ COPY_BITMAPSET_FIELD(ph_needed);
+ COPY_SCALAR_FIELD(ph_width);
+
+ return newnode;
+}
+
/* ****************************************************************
* parsenodes.h copy functions
* ****************************************************************
case T_FlattenedSubLink:
retval = _copyFlattenedSubLink(from);
break;
+ case T_PlaceHolderVar:
+ retval = _copyPlaceHolderVar(from);
+ break;
case T_SpecialJoinInfo:
retval = _copySpecialJoinInfo(from);
break;
case T_AppendRelInfo:
retval = _copyAppendRelInfo(from);
break;
+ case T_PlaceHolderInfo:
+ retval = _copyPlaceHolderInfo(from);
+ break;
/*
* VALUE NODES
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.333 2008/10/06 17:39:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.334 2008/10/21 20:42:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return true;
}
+static bool
+_equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b)
+{
+ /*
+ * We intentionally do not compare phexpr. Two PlaceHolderVars with the
+ * same ID and levelsup should be considered equal even if the contained
+ * expressions have managed to mutate to different states. One way in
+ * which that can happen is that initplan sublinks would get replaced by
+ * differently-numbered Params when sublink folding is done. (The end
+ * result of such a situation would be some unreferenced initplans, which
+ * is annoying but not really a problem.)
+ *
+ * COMPARE_NODE_FIELD(phexpr);
+ */
+ COMPARE_BITMAPSET_FIELD(phrels);
+ COMPARE_SCALAR_FIELD(phid);
+ COMPARE_SCALAR_FIELD(phlevelsup);
+
+ return true;
+}
+
static bool
_equalSpecialJoinInfo(SpecialJoinInfo *a, SpecialJoinInfo *b)
{
return true;
}
+static bool
+_equalPlaceHolderInfo(PlaceHolderInfo *a, PlaceHolderInfo *b)
+{
+ COMPARE_SCALAR_FIELD(phid);
+ COMPARE_NODE_FIELD(ph_var);
+ COMPARE_BITMAPSET_FIELD(ph_eval_at);
+ COMPARE_BITMAPSET_FIELD(ph_needed);
+ COMPARE_SCALAR_FIELD(ph_width);
+
+ return true;
+}
+
/*
* Stuff from parsenodes.h
case T_FlattenedSubLink:
retval = _equalFlattenedSubLink(a, b);
break;
+ case T_PlaceHolderVar:
+ retval = _equalPlaceHolderVar(a, b);
+ break;
case T_SpecialJoinInfo:
retval = _equalSpecialJoinInfo(a, b);
break;
case T_AppendRelInfo:
retval = _equalAppendRelInfo(a, b);
break;
+ case T_PlaceHolderInfo:
+ retval = _equalPlaceHolderInfo(a, b);
+ break;
+
case T_List:
case T_IntList:
case T_OidList:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.34 2008/10/06 17:39:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.35 2008/10/21 20:42:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
case T_CurrentOfExpr:
type = BOOLOID;
break;
+ case T_PlaceHolderVar:
+ type = exprType((Node *) ((PlaceHolderVar *) expr)->phexpr);
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
return ((CoerceToDomainValue *) expr)->typeMod;
case T_SetToDefault:
return ((SetToDefault *) expr)->typeMod;
+ case T_PlaceHolderVar:
+ return exprTypmod((Node *) ((PlaceHolderVar *) expr)->phexpr);
default:
break;
}
case T_CommonTableExpr:
loc = ((CommonTableExpr *) expr)->location;
break;
+ case T_PlaceHolderVar:
+ /* just use argument's location */
+ loc = exprLocation((Node *) ((PlaceHolderVar *) expr)->phexpr);
+ break;
default:
/* for any other node type it's just unknown... */
loc = -1;
{
FlattenedSubLink *fslink = (FlattenedSubLink *) node;
- if (expression_tree_walker((Node *) fslink->quals,
- walker, context))
+ if (walker(fslink->quals, context))
return true;
}
break;
+ case T_PlaceHolderVar:
+ return walker(((PlaceHolderVar *) node)->phexpr, context);
case T_AppendRelInfo:
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
return true;
}
break;
+ case T_PlaceHolderInfo:
+ return walker(((PlaceHolderInfo *) node)->ph_var, context);
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
return (Node *) newnode;
}
break;
+ case T_PlaceHolderVar:
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ PlaceHolderVar *newnode;
+
+ FLATCOPY(newnode, phv, PlaceHolderVar);
+ MUTATE(newnode->phexpr, phv->phexpr, Expr *);
+ /* Assume we need not copy the relids bitmapset */
+ return (Node *) newnode;
+ }
+ break;
case T_AppendRelInfo:
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
return (Node *) newnode;
}
break;
+ case T_PlaceHolderInfo:
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+ PlaceHolderInfo *newnode;
+
+ FLATCOPY(newnode, phinfo, PlaceHolderInfo);
+ MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *);
+ /* Assume we need not copy the relids bitmapsets */
+ return (Node *) newnode;
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.342 2008/10/07 19:27:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.343 2008/10/21 20:42:52 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_FIELD(finalrtable);
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
+ WRITE_UINT_FIELD(lastPHId);
+ WRITE_BOOL_FIELD(transientPlan);
}
static void
WRITE_NODE_FIELD(full_join_clauses);
WRITE_NODE_FIELD(join_info_list);
WRITE_NODE_FIELD(append_rel_list);
+ WRITE_NODE_FIELD(placeholder_list);
WRITE_NODE_FIELD(query_pathkeys);
WRITE_NODE_FIELD(group_pathkeys);
WRITE_NODE_FIELD(distinct_pathkeys);
WRITE_NODE_FIELD(quals);
}
+static void
+_outPlaceHolderVar(StringInfo str, PlaceHolderVar *node)
+{
+ WRITE_NODE_TYPE("PLACEHOLDERVAR");
+
+ WRITE_NODE_FIELD(phexpr);
+ WRITE_BITMAPSET_FIELD(phrels);
+ WRITE_UINT_FIELD(phid);
+ WRITE_UINT_FIELD(phlevelsup);
+}
+
static void
_outSpecialJoinInfo(StringInfo str, SpecialJoinInfo *node)
{
WRITE_OID_FIELD(parent_reloid);
}
+static void
+_outPlaceHolderInfo(StringInfo str, PlaceHolderInfo *node)
+{
+ WRITE_NODE_TYPE("PLACEHOLDERINFO");
+
+ WRITE_UINT_FIELD(phid);
+ WRITE_NODE_FIELD(ph_var);
+ WRITE_BITMAPSET_FIELD(ph_eval_at);
+ WRITE_BITMAPSET_FIELD(ph_needed);
+ WRITE_INT_FIELD(ph_width);
+}
+
static void
_outPlannerParamItem(StringInfo str, PlannerParamItem *node)
{
case T_FlattenedSubLink:
_outFlattenedSubLink(str, obj);
break;
+ case T_PlaceHolderVar:
+ _outPlaceHolderVar(str, obj);
+ break;
case T_SpecialJoinInfo:
_outSpecialJoinInfo(str, obj);
break;
case T_AppendRelInfo:
_outAppendRelInfo(str, obj);
break;
+ case T_PlaceHolderInfo:
+ _outPlaceHolderInfo(str, obj);
+ break;
case T_PlannerParamItem:
_outPlannerParamItem(str, obj);
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.174 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.175 2008/10/21 20:42:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Var *parentvar = (Var *) lfirst(parentvars);
Var *childvar = (Var *) lfirst(childvars);
+ /*
+ * Accumulate per-column estimates too. Whole-row Vars and
+ * PlaceHolderVars can be ignored here.
+ */
if (IsA(parentvar, Var) &&
IsA(childvar, Var))
{
* Examine all Vars used in clause; since it's a restriction clause, all
* such Vars must refer to subselect output columns.
*/
- vars = pull_var_clause(qual, false);
+ vars = pull_var_clause(qual, true);
foreach(vl, vars)
{
Var *var = (Var *) lfirst(vl);
TargetEntry *tle;
+ /*
+ * XXX Punt if we find any PlaceHolderVars in the restriction clause.
+ * It's not clear whether a PHV could safely be pushed down, and even
+ * less clear whether such a situation could arise in any cases of
+ * practical interest anyway. So for the moment, just refuse to push
+ * down.
+ */
+ if (!IsA(var, Var))
+ {
+ safe = false;
+ break;
+ }
+
Assert(var->varno == rti);
/* Check point 2 */
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.199 2008/10/17 20:27:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.200 2008/10/21 20:42:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
+#include "optimizer/placeholder.h"
#include "optimizer/planmain.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
{
Oid reloid = planner_rt_fetch(rel->relid, root)->relid;
int32 tuple_width = 0;
- ListCell *tllist;
+ ListCell *lc;
- foreach(tllist, rel->reltargetlist)
+ foreach(lc, rel->reltargetlist)
{
- Var *var = (Var *) lfirst(tllist);
- int ndx;
- int32 item_width;
+ Node *node = (Node *) lfirst(lc);
- /* For now, punt on whole-row child Vars */
- if (!IsA(var, Var))
+ if (IsA(node, Var))
{
- tuple_width += 32; /* arbitrary */
- continue;
- }
-
- Assert(var->varno == rel->relid);
- Assert(var->varattno >= rel->min_attr);
- Assert(var->varattno <= rel->max_attr);
+ Var *var = (Var *) node;
+ int ndx;
+ int32 item_width;
- ndx = var->varattno - rel->min_attr;
+ Assert(var->varno == rel->relid);
+ Assert(var->varattno >= rel->min_attr);
+ Assert(var->varattno <= rel->max_attr);
- /*
- * The width probably hasn't been cached yet, but may as well check
- */
- if (rel->attr_widths[ndx] > 0)
- {
- tuple_width += rel->attr_widths[ndx];
- continue;
- }
+ ndx = var->varattno - rel->min_attr;
- if (reloid != InvalidOid)
- {
- item_width = get_attavgwidth(reloid, var->varattno);
- if (item_width > 0)
+ /*
+ * The width probably hasn't been cached yet, but may as well check
+ */
+ if (rel->attr_widths[ndx] > 0)
{
- rel->attr_widths[ndx] = item_width;
- tuple_width += item_width;
+ tuple_width += rel->attr_widths[ndx];
continue;
}
+
+ /* Try to get column width from statistics */
+ if (reloid != InvalidOid)
+ {
+ item_width = get_attavgwidth(reloid, var->varattno);
+ if (item_width > 0)
+ {
+ rel->attr_widths[ndx] = item_width;
+ tuple_width += item_width;
+ continue;
+ }
+ }
+
+ /*
+ * Not a plain relation, or can't find statistics for it. Estimate
+ * using just the type info.
+ */
+ item_width = get_typavgwidth(var->vartype, var->vartypmod);
+ Assert(item_width > 0);
+ rel->attr_widths[ndx] = item_width;
+ tuple_width += item_width;
}
+ else if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
- /*
- * Not a plain relation, or can't find statistics for it. Estimate
- * using just the type info.
- */
- item_width = get_typavgwidth(var->vartype, var->vartypmod);
- Assert(item_width > 0);
- rel->attr_widths[ndx] = item_width;
- tuple_width += item_width;
+ tuple_width += phinfo->ph_width;
+ }
+ else
+ {
+ /* For now, punt on whole-row child Vars */
+ tuple_width += 32; /* arbitrary */
+ }
}
Assert(tuple_width >= 0);
rel->width = tuple_width;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.12 2008/08/25 22:42:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.13 2008/10/21 20:42:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
foreach(lc, ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
- List *vars = pull_var_clause((Node *) cur_em->em_expr, false);
+ List *vars = pull_var_clause((Node *) cur_em->em_expr, true);
add_vars_to_targetlist(root, vars, ec->ec_relids);
list_free(vars);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.250 2008/10/07 19:27:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.251 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
static List *build_relation_tlist(RelOptInfo *rel);
-static bool use_physical_tlist(RelOptInfo *rel);
+static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
static void disuse_physical_tlist(Plan *plan, Path *path);
static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
* planner.c may replace the tlist we generate here, forcing projection to
* occur.)
*/
- if (use_physical_tlist(rel))
+ if (use_physical_tlist(root, rel))
{
tlist = build_physical_tlist(root, rel);
/* if fail because of dropped cols, use regular method */
foreach(v, rel->reltargetlist)
{
/* Do we really need to copy here? Not sure */
- Var *var = (Var *) copyObject(lfirst(v));
+ Node *node = (Node *) copyObject(lfirst(v));
- tlist = lappend(tlist, makeTargetEntry((Expr *) var,
+ tlist = lappend(tlist, makeTargetEntry((Expr *) node,
resno,
NULL,
false));
* rather than only those Vars actually referenced.
*/
static bool
-use_physical_tlist(RelOptInfo *rel)
+use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
{
int i;
+ ListCell *lc;
/*
* We can do this for real relation scans, subquery scans, function scans,
return false;
/*
- * Can't do it if any system columns or whole-row Vars are requested,
- * either. (This could possibly be fixed but would take some fragile
- * assumptions in setrefs.c, I think.)
+ * Can't do it if any system columns or whole-row Vars are requested.
+ * (This could possibly be fixed but would take some fragile assumptions
+ * in setrefs.c, I think.)
*/
for (i = rel->min_attr; i <= 0; i++)
{
return false;
}
+ /*
+ * Can't do it if the rel is required to emit any placeholder expressions,
+ * either.
+ */
+ foreach(lc, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+ if (bms_nonempty_difference(phinfo->ph_needed, rel->relids) &&
+ bms_is_subset(phinfo->ph_eval_at, rel->relids))
+ return false;
+ }
+
return true;
}
if (em->em_is_const || em->em_is_child)
continue;
sortexpr = em->em_expr;
- exprvars = pull_var_clause((Node *) sortexpr, false);
+ exprvars = pull_var_clause((Node *) sortexpr, true);
foreach(k, exprvars)
{
if (!tlist_member_ignore_relabel(lfirst(k), tlist))
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.142 2008/08/17 01:19:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.143 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
void
build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
{
- List *tlist_vars = pull_var_clause((Node *) final_tlist, false);
+ List *tlist_vars = pull_var_clause((Node *) final_tlist, true);
if (tlist_vars != NIL)
{
* relation's targetlist if not already present, and mark the variable
* as being needed for the indicated join (or for final output if
* where_needed includes "relation 0").
+ *
+ * The list may also contain PlaceHolderVars. These don't necessarily
+ * have a single owning relation; we keep their attr_needed info in
+ * root->placeholder_list instead.
*/
void
add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed)
foreach(temp, vars)
{
- Var *var = (Var *) lfirst(temp);
- RelOptInfo *rel = find_base_rel(root, var->varno);
- int attrno = var->varattno;
+ Node *node = (Node *) lfirst(temp);
+
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ RelOptInfo *rel = find_base_rel(root, var->varno);
+ int attno = var->varattno;
- Assert(attrno >= rel->min_attr && attrno <= rel->max_attr);
- attrno -= rel->min_attr;
- if (bms_is_empty(rel->attr_needed[attrno]))
+ Assert(attno >= rel->min_attr && attno <= rel->max_attr);
+ attno -= rel->min_attr;
+ if (rel->attr_needed[attno] == NULL)
+ {
+ /* Variable not yet requested, so add to reltargetlist */
+ /* XXX is copyObject necessary here? */
+ rel->reltargetlist = lappend(rel->reltargetlist,
+ copyObject(var));
+ }
+ rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno],
+ where_needed);
+ }
+ else if (IsA(node, PlaceHolderVar))
{
- /* Variable not yet requested, so add to reltargetlist */
- /* XXX is copyObject necessary here? */
- rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var));
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
+
+ phinfo->ph_needed = bms_add_members(phinfo->ph_needed,
+ where_needed);
}
- rel->attr_needed[attrno] = bms_add_members(rel->attr_needed[attrno],
- where_needed);
+ else
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
}
}
*/
if (bms_membership(relids) == BMS_MULTIPLE)
{
- List *vars = pull_var_clause(clause, false);
+ List *vars = pull_var_clause(clause, true);
add_vars_to_targetlist(root, vars, relids);
list_free(vars);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.110 2008/08/14 18:47:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.111 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "utils/selfuncs.h"
* for "simple" rels.
*
* NOTE: append_rel_list was set up by subquery_planner, so do not touch
- * here; eq_classes may contain data already, too.
+ * here; ditto placeholder_list; eq_classes may contain data already, too.
*/
root->simple_rel_array_size = list_length(parse->rtable) + 1;
root->simple_rel_array = (RelOptInfo **)
* added to appropriate lists belonging to the mentioned relations. We
* also build EquivalenceClasses for provably equivalent expressions, and
* form a target joinlist for make_one_rel() to work from.
- *
- * Note: all subplan nodes will have "flat" (var-only) tlists. This
- * implies that all expression evaluations are done at the root of the
- * plan tree. Once upon a time there was code to try to push expensive
- * function calls down to lower plan nodes, but that's dead code and has
- * been for a long time...
*/
build_base_rel_tlists(root, tlist);
root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys);
root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys);
+ /*
+ * Examine any "placeholder" expressions generated during subquery pullup.
+ * Make sure that we know what level to evaluate them at, and that the
+ * Vars they need are marked as needed.
+ */
+ fix_placeholder_eval_levels(root);
+
/*
* Ready to do the primary planning.
*/
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.244 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.245 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define EXPRKIND_RTFUNC 2
#define EXPRKIND_VALUES 3
#define EXPRKIND_LIMIT 4
-#define EXPRKIND_APPINFO 5
+#define EXPRKIND_AUXINFO 5
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
glob->finalrtable = NIL;
glob->relationOids = NIL;
glob->invalItems = NIL;
+ glob->lastPHId = 0;
glob->transientPlan = false;
/* Determine what fraction of the plan is likely to be scanned */
root->cte_plan_ids = NIL;
root->eq_classes = NIL;
root->append_rel_list = NIL;
+ root->placeholder_list = NIL;
root->hasRecursion = hasRecursion;
if (hasRecursion)
root->append_rel_list = (List *)
preprocess_expression(root, (Node *) root->append_rel_list,
- EXPRKIND_APPINFO);
+ EXPRKIND_AUXINFO);
+ root->placeholder_list = (List *)
+ preprocess_expression(root, (Node *) root->placeholder_list,
+ EXPRKIND_AUXINFO);
/* Also need to preprocess expressions for function and values RTEs */
foreach(l, parse->rtable)
subroot.parse = (Query *)
adjust_appendrel_attrs((Node *) parse,
appinfo);
+ subroot.returningLists = NIL;
subroot.init_plans = NIL;
+ /* We needn't modify the child's append_rel_list */
+ subroot.placeholder_list = (List *)
+ adjust_appendrel_attrs((Node *) root->placeholder_list,
+ appinfo);
/* There shouldn't be any OJ info to translate, as yet */
Assert(subroot.join_info_list == NIL);
* Vars; they will be replaced by Params later on).
*/
sub_tlist = flatten_tlist(tlist);
- extravars = pull_var_clause(parse->havingQual, false);
+ extravars = pull_var_clause(parse->havingQual, true);
sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
list_free(extravars);
*need_tlist_eval = false; /* only eval if not flat tlist */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.145 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.146 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
List *tlist; /* underlying target list */
int num_vars; /* number of plain Var tlist entries */
- bool has_non_vars; /* are there non-plain-Var entries? */
+ bool has_ph_vars; /* are there PlaceHolderVar entries? */
+ bool has_non_vars; /* are there other entries? */
/* array of num_vars entries: */
tlist_vinfo vars[1]; /* VARIABLE LENGTH ARRAY */
} indexed_tlist; /* VARIABLE LENGTH STRUCT */
context.glob = glob;
context.rtoffset = rtoffset;
- if (rtoffset != 0)
+ if (rtoffset != 0 || glob->lastPHId != 0)
{
return fix_scan_expr_mutator(node, &context);
}
else
{
/*
- * If rtoffset == 0, we don't need to change any Vars, which makes
- * it OK to just scribble on the input node tree instead of copying
+ * If rtoffset == 0, we don't need to change any Vars, and if there
+ * are no placeholders anywhere we won't need to remove them. Then
+ * it's OK to just scribble on the input node tree instead of copying
* (since the only change, filling in any unset opfuncid fields,
* is harmless). This saves just enough cycles to be noticeable on
* trivial queries.
cexpr->cvarno += context->rtoffset;
return (Node *) cexpr;
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ /* At scan level, we should always just evaluate the contained expr */
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+ }
fix_expr_common(context->glob, node);
return expression_tree_mutator(node, fix_scan_expr_mutator,
(void *) context);
{
if (node == NULL)
return false;
+ Assert(!IsA(node, PlaceHolderVar));
fix_expr_common(context->glob, node);
return expression_tree_walker(node, fix_scan_expr_walker,
(void *) context);
list_length(tlist) * sizeof(tlist_vinfo));
itlist->tlist = tlist;
+ itlist->has_ph_vars = false;
itlist->has_non_vars = false;
/* Find the Vars and fill in the index array */
vinfo->resno = tle->resno;
vinfo++;
}
+ else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+ itlist->has_ph_vars = true;
else
itlist->has_non_vars = true;
}
* build_tlist_index_other_vars --- build a restricted tlist index
*
* This is like build_tlist_index, but we only index tlist entries that
- * are Vars and belong to some rel other than the one specified.
+ * are Vars belonging to some rel other than the one specified. We will set
+ * has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
+ * (so nothing other than Vars and PlaceHolderVars can be matched).
*/
static indexed_tlist *
build_tlist_index_other_vars(List *tlist, Index ignore_rel)
list_length(tlist) * sizeof(tlist_vinfo));
itlist->tlist = tlist;
+ itlist->has_ph_vars = false;
itlist->has_non_vars = false;
/* Find the desired Vars and fill in the index array */
vinfo++;
}
}
+ else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+ itlist->has_ph_vars = true;
}
itlist->num_vars = (vinfo - itlist->vars);
* If a match is found, return a Var constructed to reference the tlist item.
* If no match, return NULL.
*
- * NOTE: it is a waste of time to call this if !itlist->has_non_vars
+ * NOTE: it is a waste of time to call this unless itlist->has_ph_vars or
+ * itlist->has_non_vars
*/
static Var *
search_indexed_tlist_for_non_var(Node *node,
/* No referent found for Var */
elog(ERROR, "variable not found in subplan target lists");
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ /* See if the PlaceHolderVar has bubbled up from a lower plan node */
+ if (context->outer_itlist->has_ph_vars)
+ {
+ newvar = search_indexed_tlist_for_non_var((Node *) phv,
+ context->outer_itlist,
+ OUTER);
+ if (newvar)
+ return (Node *) newvar;
+ }
+ if (context->inner_itlist && context->inner_itlist->has_ph_vars)
+ {
+ newvar = search_indexed_tlist_for_non_var((Node *) phv,
+ context->inner_itlist,
+ INNER);
+ if (newvar)
+ return (Node *) newvar;
+ }
+
+ /* If not supplied by input plans, evaluate the contained expr */
+ return fix_join_expr_mutator((Node *) phv->phexpr, context);
+ }
/* Try matching more complex expressions too, if tlists have any */
if (context->outer_itlist->has_non_vars)
{
elog(ERROR, "variable not found in subplan target list");
return (Node *) newvar;
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ /* See if the PlaceHolderVar has bubbled up from a lower plan node */
+ if (context->subplan_itlist->has_ph_vars)
+ {
+ newvar = search_indexed_tlist_for_non_var((Node *) phv,
+ context->subplan_itlist,
+ OUTER);
+ if (newvar)
+ return (Node *) newvar;
+ }
+ /* If not supplied by input plan, evaluate the contained expr */
+ return fix_upper_expr_mutator((Node *) phv->phexpr, context);
+ }
/* Try matching more complex expressions too, if tlist has any */
if (context->subplan_itlist->has_non_vars)
{
* top plan's targetlist for Vars of non-result relations, and use
* fix_join_expr to convert RETURNING Vars into references to those tlist
* entries, while leaving result-rel Vars as-is.
+ *
+ * PlaceHolderVars will also be sought in the targetlist, but no
+ * more-complex expressions will be. Note that it is not possible for
+ * a PlaceHolderVar to refer to the result relation, since the result
+ * is never below an outer join. If that case could happen, we'd have
+ * to be prepared to pick apart the PlaceHolderVar and evaluate its
+ * contained expression instead.
*/
itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
{
if (node == NULL)
return false;
+ Assert(!IsA(node, PlaceHolderVar));
/* Extract function dependencies and check for regclass Consts */
fix_expr_common(context, node);
if (IsA(node, Query))
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.141 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.142 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* And finally, build the FlattenedSubLink node.
+ *
+ * Note: at this point left_varnos may well contain join relids, since
+ * the testexpr hasn't been run through flatten_join_alias_vars. This
+ * will get fixed when flatten_join_alias_vars is run.
*/
fslink = makeNode(FlattenedSubLink);
fslink->jointype = JOIN_SEMI;
/*
* And finally, build the FlattenedSubLink node.
+ *
+ * Note: at this point left_varnos and subselect_varnos may well contain
+ * join relids. This will get fixed when flatten_join_alias_vars is run.
*/
fslink = makeNode(FlattenedSubLink);
fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.56 2008/10/09 19:27:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.57 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/placeholder.h"
#include "optimizer/prep.h"
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
static bool is_simple_union_all(Query *subquery);
static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,
List *colTypes);
-static bool has_nullable_targetlist(Query *subquery);
+static List *insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
+ int varno, bool wrap_non_vars);
static bool is_safe_append_member(Query *subquery);
static void resolvenew_in_jointree(Node *jtnode, int varno,
RangeTblEntry *rte, List *subtlist);
Relids nonnullable_rels,
List *nonnullable_vars,
List *forced_null_vars);
-static void fix_flattened_sublink_relids(Node *node,
- int varno, Relids subrelids);
+static void substitute_multiple_relids(Node *node,
+ int varno, Relids subrelids);
static void fix_append_rel_relids(List *append_rel_list, int varno,
Relids subrelids);
static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
* converted into "append relations".
*
* below_outer_join is true if this jointree node is within the nullable
- * side of an outer join. This restricts what we can do.
+ * side of an outer join. This forces use of the PlaceHolderVar mechanism
+ * for non-nullable targetlist items.
*
* append_rel_member is true if we are looking at a member subquery of
- * an append relation. This puts some different restrictions on what
- * we can do.
+ * an append relation. This forces use of the PlaceHolderVar mechanism
+ * for all non-Var targetlist items, and puts some additional restrictions
+ * on what can be pulled up.
*
* A tricky aspect of this code is that if we pull up a subquery we have
* to replace Vars that reference the subquery's outputs throughout the
/*
* Is this a subquery RTE, and if so, is the subquery simple enough to
- * pull up? (If not, do nothing at this node.)
- *
- * If we are inside an outer join, only pull up subqueries whose
- * targetlists are nullable --- otherwise substituting their tlist
- * entries for upper Var references would do the wrong thing (the
- * results wouldn't become NULL when they're supposed to).
- *
- * XXX This could be improved by generating pseudo-variables for such
- * expressions; we'd have to figure out how to get the pseudo-
- * variables evaluated at the right place in the modified plan tree.
- * Fix it someday.
+ * pull up?
*
* If we are looking at an append-relation member, we can't pull it up
* unless is_safe_append_member says so.
*/
if (rte->rtekind == RTE_SUBQUERY &&
is_simple_subquery(rte->subquery) &&
- (!below_outer_join || has_nullable_targetlist(rte->subquery)) &&
(!append_rel_member || is_safe_append_member(rte->subquery)))
return pull_up_simple_subquery(root, jtnode, rte,
below_outer_join,
/*
* Alternatively, is it a simple UNION ALL subquery? If so, flatten
- * into an "append relation". We can do this regardless of
- * nullability considerations since this transformation does not
- * result in propagating non-Var expressions into upper levels of the
- * query.
+ * into an "append relation".
*
- * It's also safe to do this regardless of whether this query is
+ * It's safe to do this regardless of whether this query is
* itself an appendrel member. (If you're thinking we should try to
* flatten the two levels of appendrel together, you're right; but we
* handle that in set_append_rel_pathlist, not here.)
if (rte->rtekind == RTE_SUBQUERY &&
is_simple_union_all(rte->subquery))
return pull_up_simple_union_all(root, jtnode, rte);
+
+ /* Otherwise, do nothing at this node. */
}
else if (IsA(jtnode, FromExpr))
{
subroot->cte_plan_ids = NIL;
subroot->eq_classes = NIL;
subroot->append_rel_list = NIL;
+ subroot->placeholder_list = NIL;
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
subroot->non_recursive_plan = NULL;
* pull_up_subqueries.
*/
if (is_simple_subquery(subquery) &&
- (!below_outer_join || has_nullable_targetlist(subquery)) &&
(!append_rel_member || is_safe_append_member(subquery)))
{
/* good to go */
/*
* Adjust level-0 varnos in subquery so that we can append its rangetable
* to upper query's. We have to fix the subquery's append_rel_list
- * as well.
+ * and placeholder_list as well.
*/
rtoffset = list_length(parse->rtable);
OffsetVarNodes((Node *) subquery, rtoffset, 0);
OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
+ OffsetVarNodes((Node *) subroot->placeholder_list, rtoffset, 0);
/*
* Upper-level vars in subquery are now one level closer to their parent
*/
IncrementVarSublevelsUp((Node *) subquery, -1, 1);
IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
+ IncrementVarSublevelsUp((Node *) subroot->placeholder_list, -1, 1);
+
+ /*
+ * The subquery's targetlist items are now in the appropriate form to
+ * insert into the top query, but if we are under an outer join then
+ * non-nullable items have to be turned into PlaceHolderVars. If we
+ * are dealing with an appendrel member then anything that's not a
+ * simple Var has to be turned into a PlaceHolderVar.
+ */
+ if (below_outer_join || append_rel_member)
+ subtlist = insert_targetlist_placeholders(root, subquery->targetList,
+ varno, append_rel_member);
+ else
+ subtlist = subquery->targetList;
/*
* Replace all of the top query's references to the subquery's outputs
* replace any of the jointree structure. (This'd be a lot cleaner if we
* could use query_tree_mutator.)
*/
- subtlist = subquery->targetList;
parse->targetList = (List *)
ResolveNew((Node *) parse->targetList,
varno, 0, rte,
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
/*
- * We also have to fix the relid sets of any FlattenedSubLink nodes in
- * the parent query. (This could perhaps be done by ResolveNew, but it
- * would clutter that routine's API unreasonably.)
+ * We also have to fix the relid sets of any FlattenedSubLink,
+ * PlaceHolderVar, and PlaceHolderInfo nodes in the parent query.
+ * (This could perhaps be done by ResolveNew, but it would clutter that
+ * routine's API unreasonably.) Note in particular that any placeholder
+ * nodes just created by insert_targetlist_placeholders() wiil be adjusted.
*
* Likewise, relids appearing in AppendRelInfo nodes have to be fixed (but
* we took care of their translated_vars lists above). We already checked
* that this won't require introducing multiple subrelids into the
* single-slot AppendRelInfo structs.
*/
- if (parse->hasSubLinks || root->append_rel_list)
+ if (parse->hasSubLinks || root->placeholder_list || root->append_rel_list)
{
Relids subrelids;
subrelids = get_relids_in_jointree((Node *) subquery->jointree, false);
- fix_flattened_sublink_relids((Node *) parse, varno, subrelids);
- fix_append_rel_relids(root->append_rel_list, varno, subrelids);
+ substitute_multiple_relids((Node *) parse,
+ varno, subrelids);
+ substitute_multiple_relids((Node *) root->placeholder_list,
+ varno, subrelids);
+ fix_append_rel_relids(root->append_rel_list,
+ varno, subrelids);
}
/*
- * And now add subquery's AppendRelInfos to our list.
+ * And now add subquery's AppendRelInfos and PlaceHolderInfos to our lists.
+ * Note that any placeholders pulled up from the subquery will appear
+ * after any we just created; this preserves the property that placeholders
+ * can only refer to other placeholders that appear later in the list
+ * (needed by fix_placeholder_eval_levels).
*/
root->append_rel_list = list_concat(root->append_rel_list,
subroot->append_rel_list);
+ root->placeholder_list = list_concat(root->placeholder_list,
+ subroot->placeholder_list);
/*
* We don't have to do the equivalent bookkeeping for outer-join info,
* Don't pull up a subquery that has any volatile functions in its
* targetlist. Otherwise we might introduce multiple evaluations of these
* functions, if they get copied to multiple places in the upper query,
- * leading to surprising results.
+ * leading to surprising results. (Note: the PlaceHolderVar mechanism
+ * doesn't quite guarantee single evaluation; else we could pull up anyway
+ * and just wrap such items in PlaceHolderVars ...)
*/
if (contain_volatile_functions((Node *) subquery->targetList))
return false;
* Hack: don't try to pull up a subquery with an empty jointree.
* query_planner() will correctly generate a Result plan for a jointree
* that's totally empty, but I don't think the right things happen if an
- * empty FromExpr appears lower down in a jointree. Not worth working hard
- * on this, just to collapse SubqueryScan/Result into Result...
+ * empty FromExpr appears lower down in a jointree. It would pose a
+ * problem for the PlaceHolderVar mechanism too, since we'd have no
+ * way to identify where to evaluate a PHV coming out of the subquery.
+ * Not worth working hard on this, just to collapse SubqueryScan/Result
+ * into Result; especially since the SubqueryScan can often be optimized
+ * away by setrefs.c anyway.
*/
if (subquery->jointree->fromlist == NIL)
return false;
}
/*
- * has_nullable_targetlist
- * Check a subquery in the range table to see if all the non-junk
- * targetlist items are simple variables or strict functions of simple
- * variables (and, hence, will correctly go to NULL when examined above
- * the point of an outer join).
+ * insert_targetlist_placeholders
+ * Insert PlaceHolderVar nodes into any non-junk targetlist items that are
+ * not simple variables or strict functions of simple variables (and hence
+ * might not correctly go to NULL when examined above the point of an outer
+ * join). We assume we can modify the tlist items in-place.
*
- * NOTE: it would be correct (and useful) to ignore output columns that aren't
- * actually referenced by the enclosing query ... but we do not have that
- * information available at this point.
+ * varno is the upper-query relid of the subquery; this is used as the
+ * syntactic location of the PlaceHolderVars.
+ * If wrap_non_vars is true then *only* simple Var references escape being
+ * wrapped with PlaceHolderVars.
*/
-static bool
-has_nullable_targetlist(Query *subquery)
+static List *
+insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
+ int varno, bool wrap_non_vars)
{
- ListCell *l;
+ ListCell *lc;
- foreach(l, subquery->targetList)
+ foreach(lc, tlist)
{
- TargetEntry *tle = (TargetEntry *) lfirst(l);
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
/* ignore resjunk columns */
if (tle->resjunk)
continue;
- /* Must contain a Var of current level */
- if (!contain_vars_of_level((Node *) tle->expr, 0))
- return false;
+ /*
+ * Simple Vars always escape being wrapped. This is common enough
+ * to deserve a fast path even if we aren't doing wrap_non_vars.
+ */
+ if (tle->expr && IsA(tle->expr, Var) &&
+ ((Var *) tle->expr)->varlevelsup == 0)
+ continue;
- /* Must not contain any non-strict constructs */
- if (contain_nonstrict_functions((Node *) tle->expr))
- return false;
+ if (!wrap_non_vars)
+ {
+ /*
+ * If it contains a Var of current level, and does not contain
+ * any non-strict constructs, then it's certainly nullable and we
+ * don't need to insert a PlaceHolderVar. (Note: in future maybe
+ * we should insert PlaceHolderVars anyway, when a tlist item is
+ * expensive to evaluate?
+ */
+ if (contain_vars_of_level((Node *) tle->expr, 0) &&
+ !contain_nonstrict_functions((Node *) tle->expr))
+ continue;
+ }
- /* This one's OK, keep scanning */
+ /* Else wrap it in a PlaceHolderVar */
+ tle->expr = (Expr *) make_placeholder_expr(root,
+ tle->expr,
+ bms_make_singleton(varno));
}
- return true;
+ return tlist;
}
/*
is_safe_append_member(Query *subquery)
{
FromExpr *jtnode;
- ListCell *l;
/*
* It's only safe to pull up the child if its jointree contains exactly
if (!IsA(jtnode, RangeTblRef))
return false;
- /*
- * XXX For the moment we also have to insist that the subquery's tlist
- * includes only simple Vars. This is pretty annoying, but fixing it
- * seems to require nontrivial changes --- mainly because joinrel tlists
- * are presently assumed to contain only Vars. Perhaps a pseudo-variable
- * mechanism similar to the one speculated about in pull_up_subqueries'
- * comments would help? FIXME someday.
- */
- foreach(l, subquery->targetList)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(l);
-
- if (tle->resjunk)
- continue;
- if (!(tle->expr && IsA(tle->expr, Var)))
- return false;
- }
-
return true;
}
}
/*
- * fix_flattened_sublink_relids - adjust FlattenedSubLink nodes after
- * pulling up a subquery
+ * substitute_multiple_relids - adjust node relid sets after pulling up
+ * a subquery
*
- * Find any FlattenedSubLink nodes in the given tree that reference the
- * pulled-up relid, and change them to reference the replacement relid(s).
- * We do not need to recurse into subqueries, since no subquery of the
- * current top query could contain such a reference.
+ * Find any FlattenedSubLink, PlaceHolderVar, or PlaceHolderInfo nodes in the
+ * given tree that reference the pulled-up relid, and change them to reference
+ * the replacement relid(s). We do not need to recurse into subqueries, since
+ * no subquery of the current top query could (yet) contain such a reference.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. This should be OK since the tree was copied by ResolveNew
- * earlier.
+ * earlier. Avoid scribbling on the original values of the bitmapsets, though,
+ * because expression_tree_mutator doesn't copy those.
*/
typedef struct
{
int varno;
Relids subrelids;
-} fix_flattened_sublink_relids_context;
+} substitute_multiple_relids_context;
static bool
-fix_flattened_sublink_relids_walker(Node *node,
- fix_flattened_sublink_relids_context *context)
+substitute_multiple_relids_walker(Node *node,
+ substitute_multiple_relids_context *context)
{
if (node == NULL)
return false;
if (bms_is_member(context->varno, fslink->lefthand))
{
+ fslink->lefthand = bms_union(fslink->lefthand,
+ context->subrelids);
fslink->lefthand = bms_del_member(fslink->lefthand,
context->varno);
- fslink->lefthand = bms_add_members(fslink->lefthand,
- context->subrelids);
}
if (bms_is_member(context->varno, fslink->righthand))
{
+ fslink->righthand = bms_union(fslink->righthand,
+ context->subrelids);
fslink->righthand = bms_del_member(fslink->righthand,
context->varno);
- fslink->righthand = bms_add_members(fslink->righthand,
- context->subrelids);
}
/* fall through to examine children */
}
- return expression_tree_walker(node, fix_flattened_sublink_relids_walker,
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ if (bms_is_member(context->varno, phv->phrels))
+ {
+ phv->phrels = bms_union(phv->phrels,
+ context->subrelids);
+ phv->phrels = bms_del_member(phv->phrels,
+ context->varno);
+ }
+ /* fall through to examine children */
+ }
+ if (IsA(node, PlaceHolderInfo))
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+ if (bms_is_member(context->varno, phinfo->ph_eval_at))
+ {
+ phinfo->ph_eval_at = bms_union(phinfo->ph_eval_at,
+ context->subrelids);
+ phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at,
+ context->varno);
+ }
+ if (bms_is_member(context->varno, phinfo->ph_needed))
+ {
+ phinfo->ph_needed = bms_union(phinfo->ph_needed,
+ context->subrelids);
+ phinfo->ph_needed = bms_del_member(phinfo->ph_needed,
+ context->varno);
+ }
+ /* fall through to examine children */
+ }
+ return expression_tree_walker(node, substitute_multiple_relids_walker,
(void *) context);
}
static void
-fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids)
+substitute_multiple_relids(Node *node, int varno, Relids subrelids)
{
- fix_flattened_sublink_relids_context context;
+ substitute_multiple_relids_context context;
context.varno = varno;
context.subrelids = subrelids;
* Must be prepared to start with a Query or a bare expression tree.
*/
query_or_expression_tree_walker(node,
- fix_flattened_sublink_relids_walker,
+ substitute_multiple_relids_walker,
(void *) &context,
0);
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.91 2008/08/28 23:09:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.92 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *vars;
ListCell *l;
- vars = pull_var_clause((Node *) parse->returningList, false);
+ vars = pull_var_clause((Node *) parse->returningList, true);
foreach(l, vars)
{
Var *var = (Var *) lfirst(l);
TargetEntry *tle;
- if (var->varno == result_relation)
+ if (IsA(var, Var) &&
+ var->varno == result_relation)
continue; /* don't need it */
if (tlist_member((Node *) var, tlist))
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.158 2008/10/07 19:27:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.159 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
context->child_relid);
return (Node *) fslink;
}
- /* Shouldn't need to handle SpecialJoinInfo or AppendRelInfo here */
+ if (IsA(node, PlaceHolderVar))
+ {
+ /* Copy the PlaceHolderVar node with correct mutation of subnodes */
+ PlaceHolderVar *phv;
+
+ phv = (PlaceHolderVar *) expression_tree_mutator(node,
+ adjust_appendrel_attrs_mutator,
+ (void *) context);
+ /* now fix PlaceHolderVar's relid sets */
+ if (phv->phlevelsup == 0)
+ phv->phrels = adjust_relid_set(phv->phrels,
+ context->parent_relid,
+ context->child_relid);
+ return (Node *) phv;
+ }
+ if (IsA(node, PlaceHolderInfo))
+ {
+ /* Copy the PlaceHolderInfo node with correct mutation of subnodes */
+ PlaceHolderInfo *phinfo;
+
+ phinfo = (PlaceHolderInfo *) expression_tree_mutator(node,
+ adjust_appendrel_attrs_mutator,
+ (void *) context);
+ /* now fix PlaceHolderInfo's relid sets */
+ phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at,
+ context->parent_relid,
+ context->child_relid);
+ phinfo->ph_needed = adjust_relid_set(phinfo->ph_needed,
+ context->parent_relid,
+ context->child_relid);
+ return (Node *) phinfo;
+ }
+ /* Shouldn't need to handle other planner auxiliary nodes here */
Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
# 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 $
#
#-------------------------------------------------------------------------
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
*
*
* 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
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;
}
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;
}
* 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 *
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
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+ }
+ }
+ }
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
*/
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
/*
* 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
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))
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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.
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
typedef struct
{
List *varlist;
- bool includeUpperVars;
+ bool includePlaceHolderVars;
} pull_var_clause_context;
typedef struct
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 */
}
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);
}
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 */
/* 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 */
/* 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 */
}
}
}
+ /* 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 */
/*
* 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;
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);
}
* 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.
*/
}
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))
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.115 2008/10/06 17:39:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.116 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/* fall through to examine children */
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ if (phv->phlevelsup == context->sublevels_up)
+ {
+ phv->phrels = offset_relid_set(phv->phrels,
+ context->offset);
+ }
+ /* fall through to examine children */
+ }
if (IsA(node, AppendRelInfo))
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
}
/* fall through to examine children */
}
+ if (IsA(node, PlaceHolderInfo))
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+ if (context->sublevels_up == 0)
+ {
+ phinfo->ph_eval_at = offset_relid_set(phinfo->ph_eval_at,
+ context->offset);
+ phinfo->ph_needed = offset_relid_set(phinfo->ph_needed,
+ context->offset);
+ }
+ /* fall through to examine children */
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
}
/* fall through to examine children */
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ if (phv->phlevelsup == context->sublevels_up)
+ {
+ phv->phrels = adjust_relid_set(phv->phrels,
+ context->rt_index,
+ context->new_index);
+ }
+ /* fall through to examine children */
+ }
if (IsA(node, AppendRelInfo))
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
}
/* fall through to examine children */
}
+ if (IsA(node, PlaceHolderInfo))
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+ if (context->sublevels_up == 0)
+ {
+ phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at,
+ context->rt_index,
+ context->new_index);
+ phinfo->ph_needed = adjust_relid_set(phinfo->ph_needed,
+ context->rt_index,
+ context->new_index);
+ }
+ /* fall through to examine children */
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
agg->agglevelsup += context->delta_sublevels_up;
/* fall through to recurse into argument */
}
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ if (phv->phlevelsup >= context->min_sublevels_up)
+ phv->phlevelsup += context->delta_sublevels_up;
+ /* fall through to recurse into argument */
+ }
if (IsA(node, RangeTblEntry))
{
RangeTblEntry *rte = (RangeTblEntry *) node;
}
/* Shouldn't need to handle planner auxiliary nodes here */
Assert(!IsA(node, FlattenedSubLink));
+ Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
+ Assert(!IsA(node, PlaceHolderInfo));
if (IsA(node, Query))
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.255 2008/09/28 20:42:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.256 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Else pull out the component Vars
*/
- varshere = pull_var_clause(groupexpr, false);
+ varshere = pull_var_clause(groupexpr, true);
/*
* If we find any variable-free GROUP BY item, then either it is a
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.213 2008/10/04 21:56:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.214 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
T_RestrictInfo,
T_InnerIndexscanInfo,
T_FlattenedSubLink,
+ T_PlaceHolderVar,
T_SpecialJoinInfo,
T_AppendRelInfo,
+ T_PlaceHolderInfo,
T_PlannerParamItem,
/*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.161 2008/10/17 20:23:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.162 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *invalItems; /* other dependencies, as PlanInvalItems */
+ Index lastPHId; /* highest PlaceHolderVar ID assigned */
+
bool transientPlan; /* redo plan when TransactionXmin changes? */
} PlannerGlobal;
List *append_rel_list; /* list of AppendRelInfos */
+ List *placeholder_list; /* list of PlaceHolderInfos */
+
List *query_pathkeys; /* desired pathkeys for query_planner(), and
* actual pathkeys afterwards */
* clauses have been applied (ie, output rows of a plan for it)
* width - avg. number of bytes per tuple in the relation after the
* appropriate projections have been done (ie, output width)
- * reltargetlist - List of Var nodes for the attributes we need to
- * output from this relation (in no particular order,
- * but all rels of an appendrel set must use same order)
+ * reltargetlist - List of Var and PlaceHolderVar nodes for the values
+ * we need to output from this relation.
+ * List is in no particular order, but all rels of an
+ * appendrel set must use corresponding orders.
* NOTE: in a child relation, may contain RowExpr or
- * ConvertRowtypeExpr representing a whole-row Var
+ * ConvertRowtypeExpr representing a whole-row Var.
* pathlist - List of Path nodes, one for each potentially useful
* method of generating the relation
* cheapest_startup_path - the pathlist member with lowest startup cost
Expr *quals; /* join quals (in explicit-AND format) */
} FlattenedSubLink;
+/*
+ * Placeholder node for an expression to be evaluated below the top level
+ * of a plan tree. This is used during planning to represent the contained
+ * expression. At the end of the planning process it is replaced by either
+ * the contained expression or a Var referring to a lower-level evaluation of
+ * the contained expression. Typically the evaluation occurs below an outer
+ * join, and Var references above the outer join might thereby yield NULL
+ * instead of the expression value.
+ *
+ * Although the planner treats this as an expression node type, it is not
+ * recognized by the parser or executor, so we declare it here rather than
+ * in primnodes.h.
+ */
+
+typedef struct PlaceHolderVar
+{
+ Expr xpr;
+ Expr *phexpr; /* the represented expression */
+ Relids phrels; /* base relids syntactically within expr src */
+ Index phid; /* ID for PHV (unique within planner run) */
+ Index phlevelsup; /* > 0 if PHV belongs to outer query */
+} PlaceHolderVar;
+
/*
* "Special join" info.
*
Oid parent_reloid; /* OID of parent relation */
} AppendRelInfo;
+/*
+ * For each distinct placeholder expression generated during planning, we
+ * store a PlaceHolderInfo node in the PlannerInfo node's placeholder_list.
+ * This stores info that is needed centrally rather than in each copy of the
+ * PlaceHolderVar. The phid fields identify which PlaceHolderInfo goes with
+ * each PlaceHolderVar. Note that phid is unique throughout a planner run,
+ * not just within a query level --- this is so that we need not reassign ID's
+ * when pulling a subquery into its parent.
+ *
+ * The idea is to evaluate the expression at (only) the ph_eval_at join level,
+ * then allow it to bubble up like a Var until the ph_needed join level.
+ * ph_needed has the same definition as attr_needed for a regular Var.
+ */
+
+typedef struct PlaceHolderInfo
+{
+ NodeTag type;
+
+ Index phid; /* ID for PH (unique within planner run) */
+ PlaceHolderVar *ph_var; /* copy of PlaceHolderVar tree */
+ Relids ph_eval_at; /* lowest level we can evaluate value at */
+ Relids ph_needed; /* highest level the value is needed at */
+ int32 ph_width; /* estimated attribute width */
+} PlaceHolderInfo;
+
/*
* glob->paramlist keeps track of the PARAM_EXEC slots that we have decided
* we need for the query. At runtime these slots are used to pass values
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * placeholder.h
+ * prototypes for optimizer/util/placeholder.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/optimizer/placeholder.h,v 1.1 2008/10/21 20:42:53 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PLACEHOLDER_H
+#define PLACEHOLDER_H
+
+#include "nodes/relation.h"
+
+
+extern PlaceHolderVar *make_placeholder_expr(PlannerInfo *root, Expr *expr,
+ Relids phrels);
+extern PlaceHolderInfo *find_placeholder_info(PlannerInfo *root,
+ PlaceHolderVar *phv);
+extern void fix_placeholder_eval_levels(PlannerInfo *root);
+extern void add_placeholders_to_joinrel(PlannerInfo *root,
+ RelOptInfo *joinrel);
+
+#endif /* PLACEHOLDER_H */
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.38 2008/09/01 20:42:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.39 2008/10/21 20:42:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern int locate_var_of_level(Node *node, int levelsup);
extern int locate_var_of_relation(Node *node, int relid, int levelsup);
extern int find_minimum_var_level(Node *node);
-extern List *pull_var_clause(Node *node, bool includeUpperVars);
+extern List *pull_var_clause(Node *node, bool includePlaceHolderVars);
extern Node *flatten_join_alias_vars(PlannerInfo *root, Node *node);
#endif /* VAR_H */