Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5dad529

Browse files
guofengrichardCommitfest Bot
authored and
Commitfest Bot
committed
Consider explicit incremental sort for Append and MergeAppend
For an ordered Append or MergeAppend, we need to inject an explicit sort into any subpath that is not already well enough ordered. Currently, only explicit full sorts are considered; incremental sorts are not yet taken into account. In this patch, for subpaths of an ordered Append or MergeAppend, we choose to use explicit incremental sort if it is enabled and there are presorted keys. The rationale is based on the assumption that incremental sort is always faster than full sort when there are presorted keys, a premise that has been applied in various parts of the code. In addition, the current cost model tends to favor incremental sort as being cheaper than full sort in the presence of presorted keys, making it reasonable not to consider full sort in such cases. No backpatch as this could result in plan changes.
1 parent e050af2 commit 5dad529

File tree

7 files changed

+217
-52
lines changed

7 files changed

+217
-52
lines changed

src/backend/optimizer/path/costsize.c

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,7 +2247,7 @@ append_nonpartial_cost(List *subpaths, int numpaths, int parallel_workers)
22472247
* Determines and returns the cost of an Append node.
22482248
*/
22492249
void
2250-
cost_append(AppendPath *apath)
2250+
cost_append(AppendPath *apath, PlannerInfo *root)
22512251
{
22522252
ListCell *l;
22532253

@@ -2309,26 +2309,52 @@ cost_append(AppendPath *apath)
23092309
foreach(l, apath->subpaths)
23102310
{
23112311
Path *subpath = (Path *) lfirst(l);
2312-
Path sort_path; /* dummy for result of cost_sort */
2312+
int presorted_keys;
2313+
Path sort_path; /* dummy for result of
2314+
* cost_sort/cost_incremental_sort */
23132315

2314-
if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
2316+
if (!pathkeys_count_contained_in(pathkeys, subpath->pathkeys,
2317+
&presorted_keys))
23152318
{
23162319
/*
23172320
* We'll need to insert a Sort node, so include costs for
2318-
* that. We can use the parent's LIMIT if any, since we
2321+
* that. We choose to use incremental sort if it is
2322+
* enabled and there are presorted keys; otherwise we use
2323+
* full sort.
2324+
*
2325+
* We can use the parent's LIMIT if any, since we
23192326
* certainly won't pull more than that many tuples from
23202327
* any child.
23212328
*/
2322-
cost_sort(&sort_path,
2323-
NULL, /* doesn't currently need root */
2324-
pathkeys,
2325-
subpath->disabled_nodes,
2326-
subpath->total_cost,
2327-
subpath->rows,
2328-
subpath->pathtarget->width,
2329-
0.0,
2330-
work_mem,
2331-
apath->limit_tuples);
2329+
if (enable_incremental_sort && presorted_keys > 0)
2330+
{
2331+
cost_incremental_sort(&sort_path,
2332+
root,
2333+
pathkeys,
2334+
presorted_keys,
2335+
subpath->disabled_nodes,
2336+
subpath->startup_cost,
2337+
subpath->total_cost,
2338+
subpath->rows,
2339+
subpath->pathtarget->width,
2340+
0.0,
2341+
work_mem,
2342+
apath->limit_tuples);
2343+
}
2344+
else
2345+
{
2346+
cost_sort(&sort_path,
2347+
root,
2348+
pathkeys,
2349+
subpath->disabled_nodes,
2350+
subpath->total_cost,
2351+
subpath->rows,
2352+
subpath->pathtarget->width,
2353+
0.0,
2354+
work_mem,
2355+
apath->limit_tuples);
2356+
}
2357+
23322358
subpath = &sort_path;
23332359
}
23342360

src/backend/optimizer/plan/createplan.c

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
13181318
Oid *sortOperators;
13191319
Oid *collations;
13201320
bool *nullsFirst;
1321+
int presorted_keys;
13211322

13221323
/*
13231324
* Compute sort column info, and adjust subplan's tlist as needed.
@@ -1353,14 +1354,38 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
13531354
numsortkeys * sizeof(bool)) == 0);
13541355

13551356
/* Now, insert a Sort node if subplan isn't sufficiently ordered */
1356-
if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
1357+
if (!pathkeys_count_contained_in(pathkeys, subpath->pathkeys,
1358+
&presorted_keys))
13571359
{
1358-
Sort *sort = make_sort(subplan, numsortkeys,
1360+
Plan *sort_plan;
1361+
1362+
/*
1363+
* We choose to use incremental sort if it is enabled and
1364+
* there are presorted keys; otherwise we use full sort.
1365+
*/
1366+
if (enable_incremental_sort && presorted_keys > 0)
1367+
{
1368+
sort_plan = (Plan *)
1369+
make_incrementalsort(subplan, numsortkeys, presorted_keys,
13591370
sortColIdx, sortOperators,
13601371
collations, nullsFirst);
13611372

1362-
label_sort_with_costsize(root, sort, best_path->limit_tuples);
1363-
subplan = (Plan *) sort;
1373+
label_incrementalsort_with_costsize(root,
1374+
(IncrementalSort *) sort_plan,
1375+
pathkeys,
1376+
best_path->limit_tuples);
1377+
}
1378+
else
1379+
{
1380+
sort_plan = (Plan *) make_sort(subplan, numsortkeys,
1381+
sortColIdx, sortOperators,
1382+
collations, nullsFirst);
1383+
1384+
label_sort_with_costsize(root, (Sort *) sort_plan,
1385+
best_path->limit_tuples);
1386+
}
1387+
1388+
subplan = sort_plan;
13641389
}
13651390
}
13661391

@@ -1491,6 +1516,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
14911516
Oid *sortOperators;
14921517
Oid *collations;
14931518
bool *nullsFirst;
1519+
int presorted_keys;
14941520

14951521
/* Build the child plan */
14961522
/* Must insist that all children return the same tlist */
@@ -1525,14 +1551,38 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
15251551
numsortkeys * sizeof(bool)) == 0);
15261552

15271553
/* Now, insert a Sort node if subplan isn't sufficiently ordered */
1528-
if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
1554+
if (!pathkeys_count_contained_in(pathkeys, subpath->pathkeys,
1555+
&presorted_keys))
15291556
{
1530-
Sort *sort = make_sort(subplan, numsortkeys,
1557+
Plan *sort_plan;
1558+
1559+
/*
1560+
* We choose to use incremental sort if it is enabled and there
1561+
* are presorted keys; otherwise we use full sort.
1562+
*/
1563+
if (enable_incremental_sort && presorted_keys > 0)
1564+
{
1565+
sort_plan = (Plan *)
1566+
make_incrementalsort(subplan, numsortkeys, presorted_keys,
15311567
sortColIdx, sortOperators,
15321568
collations, nullsFirst);
15331569

1534-
label_sort_with_costsize(root, sort, best_path->limit_tuples);
1535-
subplan = (Plan *) sort;
1570+
label_incrementalsort_with_costsize(root,
1571+
(IncrementalSort *) sort_plan,
1572+
pathkeys,
1573+
best_path->limit_tuples);
1574+
}
1575+
else
1576+
{
1577+
sort_plan = (Plan *) make_sort(subplan, numsortkeys,
1578+
sortColIdx, sortOperators,
1579+
collations, nullsFirst);
1580+
1581+
label_sort_with_costsize(root, (Sort *) sort_plan,
1582+
best_path->limit_tuples);
1583+
}
1584+
1585+
subplan = sort_plan;
15361586
}
15371587

15381588
subplans = lappend(subplans, subplan);

src/backend/optimizer/util/pathnode.c

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,12 +1404,12 @@ create_append_path(PlannerInfo *root,
14041404
pathnode->path.total_cost = child->total_cost;
14051405
}
14061406
else
1407-
cost_append(pathnode);
1407+
cost_append(pathnode, root);
14081408
/* Must do this last, else cost_append complains */
14091409
pathnode->path.pathkeys = child->pathkeys;
14101410
}
14111411
else
1412-
cost_append(pathnode);
1412+
cost_append(pathnode, root);
14131413

14141414
/* If the caller provided a row estimate, override the computed value. */
14151415
if (rows >= 0)
@@ -1515,6 +1515,9 @@ create_merge_append_path(PlannerInfo *root,
15151515
foreach(l, subpaths)
15161516
{
15171517
Path *subpath = (Path *) lfirst(l);
1518+
int presorted_keys;
1519+
Path sort_path; /* dummy for result of
1520+
* cost_sort/cost_incremental_sort */
15181521

15191522
/* All child paths should be unparameterized */
15201523
Assert(bms_is_empty(PATH_REQ_OUTER(subpath)));
@@ -1523,32 +1526,52 @@ create_merge_append_path(PlannerInfo *root,
15231526
pathnode->path.parallel_safe = pathnode->path.parallel_safe &&
15241527
subpath->parallel_safe;
15251528

1526-
if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
1529+
if (!pathkeys_count_contained_in(pathkeys, subpath->pathkeys,
1530+
&presorted_keys))
15271531
{
1528-
/* Subpath is adequately ordered, we won't need to sort it */
1529-
input_disabled_nodes += subpath->disabled_nodes;
1530-
input_startup_cost += subpath->startup_cost;
1531-
input_total_cost += subpath->total_cost;
1532-
}
1533-
else
1534-
{
1535-
/* We'll need to insert a Sort node, so include cost for that */
1536-
Path sort_path; /* dummy for result of cost_sort */
1532+
/*
1533+
* We'll need to insert a Sort node, so include costs for that. We
1534+
* choose to use incremental sort if it is enabled and there are
1535+
* presorted keys; otherwise we use full sort.
1536+
*
1537+
* We can use the parent's LIMIT if any, since we certainly won't
1538+
* pull more than that many tuples from any child.
1539+
*/
1540+
if (enable_incremental_sort && presorted_keys > 0)
1541+
{
1542+
cost_incremental_sort(&sort_path,
1543+
root,
1544+
pathkeys,
1545+
presorted_keys,
1546+
subpath->disabled_nodes,
1547+
subpath->startup_cost,
1548+
subpath->total_cost,
1549+
subpath->rows,
1550+
subpath->pathtarget->width,
1551+
0.0,
1552+
work_mem,
1553+
pathnode->limit_tuples);
1554+
}
1555+
else
1556+
{
1557+
cost_sort(&sort_path,
1558+
root,
1559+
pathkeys,
1560+
subpath->disabled_nodes,
1561+
subpath->total_cost,
1562+
subpath->rows,
1563+
subpath->pathtarget->width,
1564+
0.0,
1565+
work_mem,
1566+
pathnode->limit_tuples);
1567+
}
15371568

1538-
cost_sort(&sort_path,
1539-
root,
1540-
pathkeys,
1541-
subpath->disabled_nodes,
1542-
subpath->total_cost,
1543-
subpath->rows,
1544-
subpath->pathtarget->width,
1545-
0.0,
1546-
work_mem,
1547-
pathnode->limit_tuples);
1548-
input_disabled_nodes += sort_path.disabled_nodes;
1549-
input_startup_cost += sort_path.startup_cost;
1550-
input_total_cost += sort_path.total_cost;
1569+
subpath = &sort_path;
15511570
}
1571+
1572+
input_disabled_nodes += subpath->disabled_nodes;
1573+
input_startup_cost += subpath->startup_cost;
1574+
input_total_cost += subpath->total_cost;
15521575
}
15531576

15541577
/*

src/include/optimizer/cost.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ extern void cost_incremental_sort(Path *path,
118118
Cost input_startup_cost, Cost input_total_cost,
119119
double input_tuples, int width, Cost comparison_cost, int sort_mem,
120120
double limit_tuples);
121-
extern void cost_append(AppendPath *apath);
121+
extern void cost_append(AppendPath *apath, PlannerInfo *root);
122122
extern void cost_merge_append(Path *path, PlannerInfo *root,
123123
List *pathkeys, int n_streams,
124124
int input_disabled_nodes,

src/test/regress/expected/incremental_sort.out

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,3 +1722,43 @@ order by t1.four, t1.two limit 1;
17221722
-> Seq Scan on tenk1 t2
17231723
(12 rows)
17241724

1725+
--
1726+
-- Test incremental sort for Append/MergeAppend
1727+
--
1728+
create table prt_tbl (a int, b int) partition by range (a);
1729+
create table prt_tbl_1 partition of prt_tbl for values from (0) to (100);
1730+
create table prt_tbl_2 partition of prt_tbl for values from (100) to (200);
1731+
insert into prt_tbl select i%200, i from generate_series(1,1000)i;
1732+
create index on prt_tbl_1(a);
1733+
create index on prt_tbl_2(a, b);
1734+
analyze prt_tbl;
1735+
set enable_seqscan to off;
1736+
set enable_bitmapscan to off;
1737+
-- Ensure we get an incremental sort for the subpath of Append
1738+
explain (costs off) select * from prt_tbl order by a, b;
1739+
QUERY PLAN
1740+
------------------------------------------------------------
1741+
Append
1742+
-> Incremental Sort
1743+
Sort Key: prt_tbl_1.a, prt_tbl_1.b
1744+
Presorted Key: prt_tbl_1.a
1745+
-> Index Scan using prt_tbl_1_a_idx on prt_tbl_1
1746+
-> Index Only Scan using prt_tbl_2_a_b_idx on prt_tbl_2
1747+
(6 rows)
1748+
1749+
-- Ensure we get an incremental sort for the subpath of MergeAppend
1750+
explain (costs off) select * from prt_tbl_1 union all select * from prt_tbl_2 order by a, b;
1751+
QUERY PLAN
1752+
------------------------------------------------------------
1753+
Merge Append
1754+
Sort Key: prt_tbl_1.a, prt_tbl_1.b
1755+
-> Incremental Sort
1756+
Sort Key: prt_tbl_1.a, prt_tbl_1.b
1757+
Presorted Key: prt_tbl_1.a
1758+
-> Index Scan using prt_tbl_1_a_idx on prt_tbl_1
1759+
-> Index Only Scan using prt_tbl_2_a_b_idx on prt_tbl_2
1760+
(7 rows)
1761+
1762+
reset enable_bitmapscan;
1763+
reset enable_seqscan;
1764+
drop table prt_tbl;

src/test/regress/expected/inherit.out

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,10 +1898,11 @@ ORDER BY thousand, tenthous;
18981898
Merge Append
18991899
Sort Key: tenk1.thousand, tenk1.tenthous
19001900
-> Index Only Scan using tenk1_thous_tenthous on tenk1
1901-
-> Sort
1901+
-> Incremental Sort
19021902
Sort Key: tenk1_1.thousand, tenk1_1.thousand
1903+
Presorted Key: tenk1_1.thousand
19031904
-> Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
1904-
(6 rows)
1905+
(7 rows)
19051906

19061907
explain (costs off)
19071908
SELECT thousand, tenthous, thousand+tenthous AS x FROM tenk1
@@ -1982,10 +1983,11 @@ ORDER BY x, y;
19821983
Merge Append
19831984
Sort Key: a.thousand, a.tenthous
19841985
-> Index Only Scan using tenk1_thous_tenthous on tenk1 a
1985-
-> Sort
1986+
-> Incremental Sort
19861987
Sort Key: b.unique2, b.unique2
1988+
Presorted Key: b.unique2
19871989
-> Index Only Scan using tenk1_unique2 on tenk1 b
1988-
(6 rows)
1990+
(7 rows)
19891991

19901992
-- exercise rescan code path via a repeatedly-evaluated subquery
19911993
explain (costs off)

src/test/regress/sql/incremental_sort.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,27 @@ explain (costs off)
298298
select * from
299299
(select * from tenk1 order by four) t1 join tenk1 t2 on t1.four = t2.four and t1.two = t2.two
300300
order by t1.four, t1.two limit 1;
301+
302+
--
303+
-- Test incremental sort for Append/MergeAppend
304+
--
305+
create table prt_tbl (a int, b int) partition by range (a);
306+
create table prt_tbl_1 partition of prt_tbl for values from (0) to (100);
307+
create table prt_tbl_2 partition of prt_tbl for values from (100) to (200);
308+
insert into prt_tbl select i%200, i from generate_series(1,1000)i;
309+
create index on prt_tbl_1(a);
310+
create index on prt_tbl_2(a, b);
311+
analyze prt_tbl;
312+
313+
set enable_seqscan to off;
314+
set enable_bitmapscan to off;
315+
316+
-- Ensure we get an incremental sort for the subpath of Append
317+
explain (costs off) select * from prt_tbl order by a, b;
318+
319+
-- Ensure we get an incremental sort for the subpath of MergeAppend
320+
explain (costs off) select * from prt_tbl_1 union all select * from prt_tbl_2 order by a, b;
321+
322+
reset enable_bitmapscan;
323+
reset enable_seqscan;
324+
drop table prt_tbl;

0 commit comments

Comments
 (0)