aboutsummaryrefslogtreecommitdiff
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.c75
-rw-r--r--src/backend/optimizer/plan/planner.c91
-rw-r--r--src/backend/optimizer/plan/setrefs.c31
-rw-r--r--src/backend/optimizer/plan/subselect.c19
4 files changed, 167 insertions, 49 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 0bb53d33089..b29b0765919 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.263 2009/09/17 20:49:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.264 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -579,7 +579,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
subplans = lappend(subplans, create_plan(root, subpath));
}
- plan = make_append(subplans, false, tlist);
+ plan = make_append(subplans, tlist);
return (Plan *) plan;
}
@@ -2621,7 +2621,7 @@ make_worktablescan(List *qptlist,
}
Append *
-make_append(List *appendplans, bool isTarget, List *tlist)
+make_append(List *appendplans, List *tlist)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
@@ -2657,7 +2657,6 @@ make_append(List *appendplans, bool isTarget, List *tlist)
plan->lefttree = NULL;
plan->righttree = NULL;
node->appendplans = appendplans;
- node->isTarget = isTarget;
return node;
}
@@ -3712,6 +3711,73 @@ make_result(PlannerInfo *root,
}
/*
+ * make_modifytable
+ * Build a ModifyTable plan node
+ *
+ * Currently, we don't charge anything extra for the actual table modification
+ * work, nor for the RETURNING expressions if any. It would only be window
+ * dressing, since these are always top-level nodes and there is no way for
+ * the costs to change any higher-level planning choices. But we might want
+ * to make it look better sometime.
+ */
+ModifyTable *
+make_modifytable(CmdType operation, List *resultRelations,
+ List *subplans, List *returningLists)
+{
+ ModifyTable *node = makeNode(ModifyTable);
+ Plan *plan = &node->plan;
+ double total_size;
+ ListCell *subnode;
+
+ Assert(list_length(resultRelations) == list_length(subplans));
+ Assert(returningLists == NIL ||
+ list_length(resultRelations) == list_length(returningLists));
+
+ /*
+ * Compute cost as sum of subplan costs.
+ */
+ plan->startup_cost = 0;
+ plan->total_cost = 0;
+ plan->plan_rows = 0;
+ total_size = 0;
+ foreach(subnode, subplans)
+ {
+ Plan *subplan = (Plan *) lfirst(subnode);
+
+ if (subnode == list_head(subplans)) /* first node? */
+ plan->startup_cost = subplan->startup_cost;
+ plan->total_cost += subplan->total_cost;
+ plan->plan_rows += subplan->plan_rows;
+ total_size += subplan->plan_width * subplan->plan_rows;
+ }
+ if (plan->plan_rows > 0)
+ plan->plan_width = rint(total_size / plan->plan_rows);
+ else
+ plan->plan_width = 0;
+
+ node->plan.lefttree = NULL;
+ node->plan.righttree = NULL;
+ node->plan.qual = NIL;
+
+ /*
+ * Set up the visible plan targetlist as being the same as the first
+ * RETURNING list. This is for the use of EXPLAIN; the executor won't
+ * pay any attention to the targetlist.
+ */
+ if (returningLists)
+ node->plan.targetlist = copyObject(linitial(returningLists));
+ else
+ node->plan.targetlist = NIL;
+
+ node->operation = operation;
+ node->resultRelations = resultRelations;
+ node->plans = subplans;
+ node->returningLists = returningLists;
+
+ return node;
+}
+
+/*
* is_projection_capable_plan
* Check whether a given Plan node is able to do projection.
*/
@@ -3727,6 +3793,7 @@ is_projection_capable_plan(Plan *plan)
case T_Unique:
case T_SetOp:
case T_Limit:
+ case T_ModifyTable:
case T_Append:
case T_RecursiveUnion:
return false;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 64f77a5c71e..4b06c823b6c 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.257 2009/10/08 02:39:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.258 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -217,6 +217,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result = makeNode(PlannedStmt);
result->commandType = parse->commandType;
+ result->hasReturning = (parse->returningList != NIL);
result->canSetTag = parse->canSetTag;
result->transientPlan = glob->transientPlan;
result->planTree = top_plan;
@@ -226,7 +227,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->intoClause = parse->intoClause;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
- result->returningLists = root->returningLists;
result->rowMarks = parse->rowMarks;
result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems;
@@ -478,7 +478,39 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
rt_fetch(parse->resultRelation, parse->rtable)->inh)
plan = inheritance_planner(root);
else
+ {
plan = grouping_planner(root, tuple_fraction);
+ /* If it's not SELECT, we need a ModifyTable node */
+ if (parse->commandType != CMD_SELECT)
+ {
+ /*
+ * Deal with the RETURNING clause if any. It's convenient to pass
+ * the returningList through setrefs.c now rather than at top
+ * level (if we waited, handling inherited UPDATE/DELETE would be
+ * much harder).
+ */
+ List *returningLists;
+
+ if (parse->returningList)
+ {
+ List *rlist;
+
+ Assert(parse->resultRelation);
+ rlist = set_returning_clause_references(root->glob,
+ parse->returningList,
+ plan,
+ parse->resultRelation);
+ returningLists = list_make1(rlist);
+ }
+ else
+ returningLists = NIL;
+
+ plan = (Plan *) make_modifytable(parse->commandType,
+ copyObject(root->resultRelations),
+ list_make1(plan),
+ returningLists);
+ }
+ }
/*
* If any subplans were generated, or if we're inside a subplan, build
@@ -625,9 +657,7 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode)
* is an inheritance set. Source inheritance is expanded at the bottom of the
* plan tree (see allpaths.c), but target inheritance has to be expanded at
* the top. The reason is that for UPDATE, each target relation needs a
- * different targetlist matching its own column set. Also, for both UPDATE
- * and DELETE, the executor needs the Append plan node at the top, else it
- * can't keep track of which table is the current target table. Fortunately,
+ * different targetlist matching its own column set. Fortunately,
* the UPDATE/DELETE target can never be the nullable side of an outer join,
* so it's OK to generate the plan this way.
*
@@ -642,7 +672,7 @@ inheritance_planner(PlannerInfo *root)
List *resultRelations = NIL;
List *returningLists = NIL;
List *rtable = NIL;
- List *tlist = NIL;
+ List *tlist;
PlannerInfo subroot;
ListCell *l;
@@ -662,7 +692,6 @@ 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 */
/* There shouldn't be any OJ info to translate, as yet */
@@ -680,12 +709,9 @@ inheritance_planner(PlannerInfo *root)
if (is_dummy_plan(subplan))
continue;
- /* Save rtable and tlist from first rel for use below */
+ /* Save rtable from first rel for use below */
if (subplans == NIL)
- {
rtable = subroot.parse->rtable;
- tlist = subplan->targetlist;
- }
subplans = lappend(subplans, subplan);
@@ -698,20 +724,24 @@ inheritance_planner(PlannerInfo *root)
/* Build list of per-relation RETURNING targetlists */
if (parse->returningList)
{
- Assert(list_length(subroot.returningLists) == 1);
- returningLists = list_concat(returningLists,
- subroot.returningLists);
+ List *rlist;
+
+ rlist = set_returning_clause_references(root->glob,
+ subroot.parse->returningList,
+ subplan,
+ appinfo->child_relid);
+ returningLists = lappend(returningLists, rlist);
}
}
root->resultRelations = resultRelations;
- root->returningLists = returningLists;
/* Mark result as unordered (probably unnecessary) */
root->query_pathkeys = NIL;
/*
- * If we managed to exclude every child rel, return a dummy plan
+ * If we managed to exclude every child rel, return a dummy plan;
+ * it doesn't even need a ModifyTable node.
*/
if (subplans == NIL)
{
@@ -738,11 +768,11 @@ inheritance_planner(PlannerInfo *root)
*/
parse->rtable = rtable;
- /* Suppress Append if there's only one surviving child rel */
- if (list_length(subplans) == 1)
- return (Plan *) linitial(subplans);
-
- return (Plan *) make_append(subplans, true, tlist);
+ /* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
+ return (Plan *) make_modifytable(parse->commandType,
+ copyObject(root->resultRelations),
+ subplans,
+ returningLists);
}
/*--------------------
@@ -1569,25 +1599,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
count_est);
}
- /*
- * Deal with the RETURNING clause if any. It's convenient to pass the
- * returningList through setrefs.c now rather than at top level (if we
- * waited, handling inherited UPDATE/DELETE would be much harder).
- */
- if (parse->returningList)
- {
- List *rlist;
-
- Assert(parse->resultRelation);
- rlist = set_returning_clause_references(root->glob,
- parse->returningList,
- result_plan,
- parse->resultRelation);
- root->returningLists = list_make1(rlist);
- }
- else
- root->returningLists = NIL;
-
/* Compute result-relations list if needed */
if (parse->resultRelation)
root->resultRelations = list_make1_int(parse->resultRelation);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 11e14f96c55..9b10b381ac7 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.150 2009/06/11 14:48:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.151 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -442,6 +442,29 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
fix_scan_expr(glob, splan->resconstantqual, rtoffset);
}
break;
+ case T_ModifyTable:
+ {
+ ModifyTable *splan = (ModifyTable *) plan;
+
+ /*
+ * planner.c already called set_returning_clause_references,
+ * so we should not process either the targetlist or the
+ * returningLists.
+ */
+ Assert(splan->plan.qual == NIL);
+
+ foreach(l, splan->resultRelations)
+ {
+ lfirst_int(l) += rtoffset;
+ }
+ foreach(l, splan->plans)
+ {
+ lfirst(l) = set_plan_refs(glob,
+ (Plan *) lfirst(l),
+ rtoffset);
+ }
+ }
+ break;
case T_Append:
{
Append *splan = (Append *) plan;
@@ -1600,7 +1623,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
*
* If the query involves more than just the result table, we have to
* adjust any Vars that refer to other tables to reference junk tlist
- * entries in the top plan's targetlist. Vars referencing the result
+ * entries in the top subplan's targetlist. Vars referencing the result
* table should be left alone, however (the executor will evaluate them
* using the actual heap tuple, after firing triggers if any). In the
* adjusted RETURNING list, result-table Vars will still have their
@@ -1610,8 +1633,8 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
* glob->relationOids.
*
* 'rlist': the RETURNING targetlist to be fixed
- * 'topplan': the top Plan node for the query (not yet passed through
- * set_plan_references)
+ * 'topplan': the top subplan node that will be just below the ModifyTable
+ * node (note it's not yet passed through set_plan_references)
* 'resultRelation': RT index of the associated result relation
*
* Note: we assume that result relations will have rtoffset zero, that is,
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 78809474a3b..6a813106d18 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.153 2009/09/12 22:12:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.154 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1937,6 +1937,23 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
((WorkTableScan *) plan)->wtParam);
break;
+ case T_ModifyTable:
+ {
+ ListCell *l;
+
+ finalize_primnode((Node *) ((ModifyTable *) plan)->returningLists,
+ &context);
+ foreach(l, ((ModifyTable *) plan)->plans)
+ {
+ context.paramids =
+ bms_add_members(context.paramids,
+ finalize_plan(root,
+ (Plan *) lfirst(l),
+ valid_params));
+ }
+ }
+ break;
+
case T_Append:
{
ListCell *l;