Skip to content

Commit 989067b

Browse files
committed
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.
1 parent 5d1af6a commit 989067b

File tree

3 files changed

+63
-29
lines changed

3 files changed

+63
-29
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.167 2004/02/13 22:26:30 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.168 2004/04/07 18:17:24 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -572,11 +572,23 @@ grouping_planner(Query *parse, double tuple_fraction)
572572

573573
if (parse->setOperations)
574574
{
575+
List *set_sortclauses;
576+
575577
/*
576578
* Construct the plan for set operations. The result will not
577579
* need any work except perhaps a top-level sort and/or LIMIT.
578580
*/
579-
result_plan = plan_set_operations(parse);
581+
result_plan = plan_set_operations(parse,
582+
&set_sortclauses);
583+
584+
/*
585+
* Calculate pathkeys representing the sort order (if any) of the
586+
* set operation's result. We have to do this before overwriting
587+
* the sort key information...
588+
*/
589+
current_pathkeys = make_pathkeys_for_sortclauses(set_sortclauses,
590+
result_plan->targetlist);
591+
current_pathkeys = canonicalize_pathkeys(parse, current_pathkeys);
580592

581593
/*
582594
* We should not need to call preprocess_targetlist, since we must
@@ -599,16 +611,7 @@ grouping_planner(Query *parse, double tuple_fraction)
599611
errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
600612

601613
/*
602-
* We set current_pathkeys NIL indicating we do not know sort
603-
* order. This is correct when the top set operation is UNION
604-
* ALL, since the appended-together results are unsorted even if
605-
* the subplans were sorted. For other set operations we could be
606-
* smarter --- room for future improvement!
607-
*/
608-
current_pathkeys = NIL;
609-
610-
/*
611-
* Calculate pathkeys that represent ordering requirements
614+
* Calculate pathkeys that represent result ordering requirements
612615
*/
613616
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
614617
tlist);

src/backend/optimizer/prep/prepunion.c

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
*
1616
* IDENTIFICATION
17-
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.108 2004/01/18 00:50:02 tgl Exp $
17+
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.109 2004/04/07 18:17:25 tgl Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -45,11 +45,12 @@ typedef struct
4545

4646
static Plan *recurse_set_operations(Node *setOp, Query *parse,
4747
List *colTypes, bool junkOK,
48-
int flag, List *refnames_tlist);
48+
int flag, List *refnames_tlist,
49+
List **sortClauses);
4950
static Plan *generate_union_plan(SetOperationStmt *op, Query *parse,
50-
List *refnames_tlist);
51+
List *refnames_tlist, List **sortClauses);
5152
static Plan *generate_nonunion_plan(SetOperationStmt *op, Query *parse,
52-
List *refnames_tlist);
53+
List *refnames_tlist, List **sortClauses);
5354
static List *recurse_union_children(Node *setOp, Query *parse,
5455
SetOperationStmt *top_union,
5556
List *refnames_tlist);
@@ -75,9 +76,12 @@ static List *adjust_inherited_tlist(List *tlist, Oid old_relid, Oid new_relid);
7576
* This routine only deals with the setOperations tree of the given query.
7677
* Any top-level ORDER BY requested in parse->sortClause will be added
7778
* when we return to grouping_planner.
79+
*
80+
* *sortClauses is an output argument: it is set to a list of SortClauses
81+
* representing the result ordering of the topmost set operation.
7882
*/
7983
Plan *
80-
plan_set_operations(Query *parse)
84+
plan_set_operations(Query *parse, List **sortClauses)
8185
{
8286
SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
8387
Node *node;
@@ -113,7 +117,8 @@ plan_set_operations(Query *parse)
113117
*/
114118
return recurse_set_operations((Node *) topop, parse,
115119
topop->colTypes, true, -1,
116-
leftmostQuery->targetList);
120+
leftmostQuery->targetList,
121+
sortClauses);
117122
}
118123

119124
/*
@@ -124,11 +129,13 @@ plan_set_operations(Query *parse)
124129
* junkOK: if true, child resjunk columns may be left in the result
125130
* flag: if >= 0, add a resjunk output column indicating value of flag
126131
* refnames_tlist: targetlist to take column names from
132+
* *sortClauses: receives list of SortClauses for result plan, if any
127133
*/
128134
static Plan *
129135
recurse_set_operations(Node *setOp, Query *parse,
130136
List *colTypes, bool junkOK,
131-
int flag, List *refnames_tlist)
137+
int flag, List *refnames_tlist,
138+
List **sortClauses)
132139
{
133140
if (IsA(setOp, RangeTblRef))
134141
{
@@ -155,6 +162,13 @@ recurse_set_operations(Node *setOp, Query *parse,
155162
NIL,
156163
rtr->rtindex,
157164
subplan);
165+
166+
/*
167+
* We don't bother to determine the subquery's output ordering
168+
* since it won't be reflected in the set-op result anyhow.
169+
*/
170+
*sortClauses = NIL;
171+
158172
return plan;
159173
}
160174
else if (IsA(setOp, SetOperationStmt))
@@ -164,9 +178,11 @@ recurse_set_operations(Node *setOp, Query *parse,
164178

165179
/* UNIONs are much different from INTERSECT/EXCEPT */
166180
if (op->op == SETOP_UNION)
167-
plan = generate_union_plan(op, parse, refnames_tlist);
181+
plan = generate_union_plan(op, parse, refnames_tlist,
182+
sortClauses);
168183
else
169-
plan = generate_nonunion_plan(op, parse, refnames_tlist);
184+
plan = generate_nonunion_plan(op, parse, refnames_tlist,
185+
sortClauses);
170186

171187
/*
172188
* If necessary, add a Result node to project the caller-requested
@@ -206,7 +222,8 @@ recurse_set_operations(Node *setOp, Query *parse,
206222
*/
207223
static Plan *
208224
generate_union_plan(SetOperationStmt *op, Query *parse,
209-
List *refnames_tlist)
225+
List *refnames_tlist,
226+
List **sortClauses)
210227
{
211228
List *planlist;
212229
List *tlist;
@@ -249,7 +266,11 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
249266
sortList = addAllTargetsToSortList(NULL, NIL, tlist, false);
250267
plan = (Plan *) make_sort_from_sortclauses(parse, sortList, plan);
251268
plan = (Plan *) make_unique(plan, sortList);
269+
*sortClauses = sortList;
252270
}
271+
else
272+
*sortClauses = NIL;
273+
253274
return plan;
254275
}
255276

@@ -258,23 +279,27 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
258279
*/
259280
static Plan *
260281
generate_nonunion_plan(SetOperationStmt *op, Query *parse,
261-
List *refnames_tlist)
282+
List *refnames_tlist,
283+
List **sortClauses)
262284
{
263285
Plan *lplan,
264286
*rplan,
265287
*plan;
266288
List *tlist,
267289
*sortList,
268-
*planlist;
290+
*planlist,
291+
*child_sortclauses;
269292
SetOpCmd cmd;
270293

271294
/* Recurse on children, ensuring their outputs are marked */
272295
lplan = recurse_set_operations(op->larg, parse,
273296
op->colTypes, false, 0,
274-
refnames_tlist);
297+
refnames_tlist,
298+
&child_sortclauses);
275299
rplan = recurse_set_operations(op->rarg, parse,
276300
op->colTypes, false, 1,
277-
refnames_tlist);
301+
refnames_tlist,
302+
&child_sortclauses);
278303
planlist = makeList2(lplan, rplan);
279304

280305
/*
@@ -315,6 +340,9 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
315340
break;
316341
}
317342
plan = (Plan *) make_setop(cmd, plan, sortList, length(op->colTypes) + 1);
343+
344+
*sortClauses = sortList;
345+
318346
return plan;
319347
}
320348

@@ -329,6 +357,8 @@ recurse_union_children(Node *setOp, Query *parse,
329357
SetOperationStmt *top_union,
330358
List *refnames_tlist)
331359
{
360+
List *child_sortclauses;
361+
332362
if (IsA(setOp, SetOperationStmt))
333363
{
334364
SetOperationStmt *op = (SetOperationStmt *) setOp;
@@ -359,7 +389,8 @@ recurse_union_children(Node *setOp, Query *parse,
359389
*/
360390
return makeList1(recurse_set_operations(setOp, parse,
361391
top_union->colTypes, false,
362-
-1, refnames_tlist));
392+
-1, refnames_tlist,
393+
&child_sortclauses));
363394
}
364395

365396
/*

src/include/optimizer/prep.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.43 2003/12/28 21:57:37 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.44 2004/04/07 18:17:25 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -48,7 +48,7 @@ extern List *preprocess_targetlist(List *tlist, int command_type,
4848
/*
4949
* prototypes for prepunion.c
5050
*/
51-
extern Plan *plan_set_operations(Query *parse);
51+
extern Plan *plan_set_operations(Query *parse, List **sortClauses);
5252

5353
extern List *find_all_inheritors(Oid parentrel);
5454

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy