From 989067bd2208398d803e784582e96811d652f574 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 7 Apr 2004 18:17:25 +0000 Subject: [PATCH] Extend set-operation planning to keep track of the sort ordering induced by the set operation, so that redundant sorts at higher levels can be avoided. This was foreseen a good while back, but not done. Per request from Karel Zak. --- src/backend/optimizer/plan/planner.c | 27 +++++++----- src/backend/optimizer/prep/prepunion.c | 61 +++++++++++++++++++------- src/include/optimizer/prep.h | 4 +- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index fea7e4fef1..c3cfe4be71 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.167 2004/02/13 22:26:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.168 2004/04/07 18:17:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -572,11 +572,23 @@ grouping_planner(Query *parse, double tuple_fraction) if (parse->setOperations) { + List *set_sortclauses; + /* * Construct the plan for set operations. The result will not * need any work except perhaps a top-level sort and/or LIMIT. */ - result_plan = plan_set_operations(parse); + result_plan = plan_set_operations(parse, + &set_sortclauses); + + /* + * Calculate pathkeys representing the sort order (if any) of the + * set operation's result. We have to do this before overwriting + * the sort key information... + */ + current_pathkeys = make_pathkeys_for_sortclauses(set_sortclauses, + result_plan->targetlist); + current_pathkeys = canonicalize_pathkeys(parse, current_pathkeys); /* * We should not need to call preprocess_targetlist, since we must @@ -599,16 +611,7 @@ grouping_planner(Query *parse, double tuple_fraction) errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); /* - * We set current_pathkeys NIL indicating we do not know sort - * order. This is correct when the top set operation is UNION - * ALL, since the appended-together results are unsorted even if - * the subplans were sorted. For other set operations we could be - * smarter --- room for future improvement! - */ - current_pathkeys = NIL; - - /* - * Calculate pathkeys that represent ordering requirements + * Calculate pathkeys that represent result ordering requirements */ sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, tlist); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 9424fc7148..b703bf2b86 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.108 2004/01/18 00:50:02 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.109 2004/04/07 18:17:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,11 +45,12 @@ typedef struct static Plan *recurse_set_operations(Node *setOp, Query *parse, List *colTypes, bool junkOK, - int flag, List *refnames_tlist); + int flag, List *refnames_tlist, + List **sortClauses); static Plan *generate_union_plan(SetOperationStmt *op, Query *parse, - List *refnames_tlist); + List *refnames_tlist, List **sortClauses); static Plan *generate_nonunion_plan(SetOperationStmt *op, Query *parse, - List *refnames_tlist); + List *refnames_tlist, List **sortClauses); static List *recurse_union_children(Node *setOp, Query *parse, SetOperationStmt *top_union, List *refnames_tlist); @@ -75,9 +76,12 @@ static List *adjust_inherited_tlist(List *tlist, Oid old_relid, Oid new_relid); * This routine only deals with the setOperations tree of the given query. * Any top-level ORDER BY requested in parse->sortClause will be added * when we return to grouping_planner. + * + * *sortClauses is an output argument: it is set to a list of SortClauses + * representing the result ordering of the topmost set operation. */ Plan * -plan_set_operations(Query *parse) +plan_set_operations(Query *parse, List **sortClauses) { SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations; Node *node; @@ -113,7 +117,8 @@ plan_set_operations(Query *parse) */ return recurse_set_operations((Node *) topop, parse, topop->colTypes, true, -1, - leftmostQuery->targetList); + leftmostQuery->targetList, + sortClauses); } /* @@ -124,11 +129,13 @@ plan_set_operations(Query *parse) * junkOK: if true, child resjunk columns may be left in the result * flag: if >= 0, add a resjunk output column indicating value of flag * refnames_tlist: targetlist to take column names from + * *sortClauses: receives list of SortClauses for result plan, if any */ static Plan * recurse_set_operations(Node *setOp, Query *parse, List *colTypes, bool junkOK, - int flag, List *refnames_tlist) + int flag, List *refnames_tlist, + List **sortClauses) { if (IsA(setOp, RangeTblRef)) { @@ -155,6 +162,13 @@ recurse_set_operations(Node *setOp, Query *parse, NIL, rtr->rtindex, subplan); + + /* + * We don't bother to determine the subquery's output ordering + * since it won't be reflected in the set-op result anyhow. + */ + *sortClauses = NIL; + return plan; } else if (IsA(setOp, SetOperationStmt)) @@ -164,9 +178,11 @@ recurse_set_operations(Node *setOp, Query *parse, /* UNIONs are much different from INTERSECT/EXCEPT */ if (op->op == SETOP_UNION) - plan = generate_union_plan(op, parse, refnames_tlist); + plan = generate_union_plan(op, parse, refnames_tlist, + sortClauses); else - plan = generate_nonunion_plan(op, parse, refnames_tlist); + plan = generate_nonunion_plan(op, parse, refnames_tlist, + sortClauses); /* * If necessary, add a Result node to project the caller-requested @@ -206,7 +222,8 @@ recurse_set_operations(Node *setOp, Query *parse, */ static Plan * generate_union_plan(SetOperationStmt *op, Query *parse, - List *refnames_tlist) + List *refnames_tlist, + List **sortClauses) { List *planlist; List *tlist; @@ -249,7 +266,11 @@ generate_union_plan(SetOperationStmt *op, Query *parse, sortList = addAllTargetsToSortList(NULL, NIL, tlist, false); plan = (Plan *) make_sort_from_sortclauses(parse, sortList, plan); plan = (Plan *) make_unique(plan, sortList); + *sortClauses = sortList; } + else + *sortClauses = NIL; + return plan; } @@ -258,23 +279,27 @@ generate_union_plan(SetOperationStmt *op, Query *parse, */ static Plan * generate_nonunion_plan(SetOperationStmt *op, Query *parse, - List *refnames_tlist) + List *refnames_tlist, + List **sortClauses) { Plan *lplan, *rplan, *plan; List *tlist, *sortList, - *planlist; + *planlist, + *child_sortclauses; SetOpCmd cmd; /* Recurse on children, ensuring their outputs are marked */ lplan = recurse_set_operations(op->larg, parse, op->colTypes, false, 0, - refnames_tlist); + refnames_tlist, + &child_sortclauses); rplan = recurse_set_operations(op->rarg, parse, op->colTypes, false, 1, - refnames_tlist); + refnames_tlist, + &child_sortclauses); planlist = makeList2(lplan, rplan); /* @@ -315,6 +340,9 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse, break; } plan = (Plan *) make_setop(cmd, plan, sortList, length(op->colTypes) + 1); + + *sortClauses = sortList; + return plan; } @@ -329,6 +357,8 @@ recurse_union_children(Node *setOp, Query *parse, SetOperationStmt *top_union, List *refnames_tlist) { + List *child_sortclauses; + if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; @@ -359,7 +389,8 @@ recurse_union_children(Node *setOp, Query *parse, */ return makeList1(recurse_set_operations(setOp, parse, top_union->colTypes, false, - -1, refnames_tlist)); + -1, refnames_tlist, + &child_sortclauses)); } /* diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 6255c36230..7f38bb795a 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.43 2003/12/28 21:57:37 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.44 2004/04/07 18:17:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,7 +48,7 @@ extern List *preprocess_targetlist(List *tlist, int command_type, /* * prototypes for prepunion.c */ -extern Plan *plan_set_operations(Query *parse); +extern Plan *plan_set_operations(Query *parse, List **sortClauses); extern List *find_all_inheritors(Oid parentrel);