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.
This commit is contained in:
parent
5d1af6aee3
commit
989067bd22
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user