summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/createplan.c34
-rw-r--r--src/backend/optimizer/plan/initsplan.c49
-rw-r--r--src/backend/optimizer/plan/planmain.c18
-rw-r--r--src/backend/optimizer/plan/planner.c18
-rw-r--r--src/backend/optimizer/plan/setrefs.c82
-rw-r--r--src/backend/optimizer/plan/subselect.c9
6 files changed, 166 insertions, 44 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index f812c93979..7bddf33e5c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -37,7 +37,7 @@
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);
@@ -212,7 +212,7 @@ create_scan_plan(PlannerInfo *root, Path *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 */
@@ -325,9 +325,9 @@ build_relation_tlist(RelOptInfo *rel)
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));
@@ -342,9 +342,10 @@ build_relation_tlist(RelOptInfo *rel)
* 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,
@@ -365,9 +366,9 @@ use_physical_tlist(RelOptInfo *rel)
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++)
{
@@ -375,6 +376,19 @@ use_physical_tlist(RelOptInfo *rel)
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;
}
@@ -2925,7 +2939,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
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))
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 49c2cdf2f5..1208262b8e 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#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"
@@ -131,7 +132,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
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)
{
@@ -146,6 +147,10 @@ build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
* 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)
@@ -156,20 +161,36 @@ 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));
}
}
@@ -934,7 +955,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
*/
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);
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 7dcdaf250f..171724983d 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,7 @@
#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"
@@ -132,7 +133,7 @@ query_planner(PlannerInfo *root, List *tlist,
* 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 **)
@@ -204,12 +205,6 @@ query_planner(PlannerInfo *root, List *tlist,
* 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);
@@ -241,6 +236,13 @@ query_planner(PlannerInfo *root, List *tlist,
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.
*/
final_rel = make_one_rel(root, joinlist);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0a704dc654..05de001a87 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -55,7 +55,7 @@ planner_hook_type planner_hook = NULL;
#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);
@@ -141,6 +141,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
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 */
@@ -273,6 +274,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->cte_plan_ids = NIL;
root->eq_classes = NIL;
root->append_rel_list = NIL;
+ root->placeholder_list = NIL;
root->hasRecursion = hasRecursion;
if (hasRecursion)
@@ -378,7 +380,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
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)
@@ -656,7 +661,12 @@ inheritance_planner(PlannerInfo *root)
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);
@@ -2046,7 +2056,7 @@ make_subplanTargetList(PlannerInfo *root,
* 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 */
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 6d7ec283b3..9bec109f6f 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -38,7 +38,8 @@ typedef struct
{
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 */
@@ -741,15 +742,16 @@ fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset)
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.
@@ -790,6 +792,13 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
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);
@@ -800,6 +809,7 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *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);
@@ -1199,6 +1209,7 @@ build_tlist_index(List *tlist)
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 */
@@ -1216,6 +1227,8 @@ build_tlist_index(List *tlist)
vinfo->resno = tle->resno;
vinfo++;
}
+ else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+ itlist->has_ph_vars = true;
else
itlist->has_non_vars = true;
}
@@ -1229,7 +1242,9 @@ build_tlist_index(List *tlist)
* 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)
@@ -1244,6 +1259,7 @@ 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 */
@@ -1264,6 +1280,8 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
vinfo++;
}
}
+ else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+ itlist->has_ph_vars = true;
}
itlist->num_vars = (vinfo - itlist->vars);
@@ -1314,7 +1332,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
* 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,
@@ -1429,6 +1448,31 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
/* 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)
{
@@ -1512,6 +1556,22 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
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)
{
@@ -1564,6 +1624,13 @@ set_returning_clause_references(PlannerGlobal *glob,
* 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);
@@ -1721,6 +1788,7 @@ extract_query_dependencies_walker(Node *node, PlannerGlobal *context)
{
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))
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 00d84d5682..b80427f041 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -1040,6 +1040,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
/*
* 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;
@@ -1189,6 +1193,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
/*
* 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;