diff options
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 75 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 91 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 31 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 19 |
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; |