Skip to content

Commit 86b7cca

Browse files
committed
Check parallel safety in generate_useful_gather_paths
Commit ebb7ae8 ensured we ignore pathkeys with volatile expressions when considering adding a sort below a Gather Merge. Turns out we need to care about parallel safety of the pathkeys too, otherwise we might try sorting e.g. on results of a correlated subquery (as demonstrated by a report from Luis Roberto). Initial investigation by Tom Lane, patch by James Coleman. Backpatch to 13, where the code was instroduced (as part of Incremental Sort). Reported-by: Luis Roberto Author: James Coleman Reviewed-by: Tomas Vondra Backpatch-through: 13 Discussion: https://postgr.es/m/622580997.37108180.1604080457319.JavaMail.zimbra%40siscobra.com.br Discussion: https://postgr.es/m/CAAaqYe8cK3g5CfLC4w7bs=hC0mSksZC=H5M8LSchj5e5OxpTAg@mail.gmail.com
1 parent f4a3c0b commit 86b7cca

File tree

5 files changed

+73
-5
lines changed

5 files changed

+73
-5
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,14 +2802,18 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows)
28022802
* This allows us to do incremental sort on top of an index scan under a gather
28032803
* merge node, i.e. parallelized.
28042804
*
2805+
* If the require_parallel_safe is true, we also require the expressions to
2806+
* be parallel safe (which allows pushing the sort below Gather Merge).
2807+
*
28052808
* XXX At the moment this can only ever return a list with a single element,
28062809
* because it looks at query_pathkeys only. So we might return the pathkeys
28072810
* directly, but it seems plausible we'll want to consider other orderings
28082811
* in the future. For example, we might want to consider pathkeys useful for
28092812
* merge joins.
28102813
*/
28112814
static List *
2812-
get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
2815+
get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel,
2816+
bool require_parallel_safe)
28132817
{
28142818
List *useful_pathkeys_list = NIL;
28152819

@@ -2839,8 +2843,11 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
28392843
* meet criteria of EC membership in the current relation, we
28402844
* enable not just an incremental sort on the entirety of
28412845
* query_pathkeys but also incremental sort below a JOIN.
2846+
*
2847+
* If requested, ensure the expression is parallel safe too.
28422848
*/
2843-
if (!find_em_expr_usable_for_sorting_rel(pathkey_ec, rel))
2849+
if (!find_em_expr_usable_for_sorting_rel(root, pathkey_ec, rel,
2850+
require_parallel_safe))
28442851
break;
28452852

28462853
npathkeys++;
@@ -2894,7 +2901,7 @@ generate_useful_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_r
28942901
generate_gather_paths(root, rel, override_rows);
28952902

28962903
/* consider incremental sort for interesting orderings */
2897-
useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
2904+
useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel, true);
28982905

28992906
/* used for explicit (full) sort paths */
29002907
cheapest_partial_path = linitial(rel->partial_pathlist);

src/backend/optimizer/path/equivclass.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,8 @@ find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel)
803803
* applied in prepare_sort_from_pathkeys.
804804
*/
805805
Expr *
806-
find_em_expr_usable_for_sorting_rel(EquivalenceClass *ec, RelOptInfo *rel)
806+
find_em_expr_usable_for_sorting_rel(PlannerInfo *root, EquivalenceClass *ec,
807+
RelOptInfo *rel, bool require_parallel_safe)
807808
{
808809
ListCell *lc_em;
809810

@@ -833,6 +834,12 @@ find_em_expr_usable_for_sorting_rel(EquivalenceClass *ec, RelOptInfo *rel)
833834
if (!bms_is_subset(em->em_relids, rel->relids))
834835
continue;
835836

837+
/*
838+
* If requested, reject expressions that are not parallel-safe.
839+
*/
840+
if (require_parallel_safe && !is_parallel_safe(root, (Node *) em_expr))
841+
continue;
842+
836843
/*
837844
* As long as the expression isn't volatile then
838845
* prepare_sort_from_pathkeys is able to generate a new target entry,

src/include/optimizer/paths.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
135135
Relids rel,
136136
bool create_it);
137137
extern Expr *find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel);
138-
extern Expr *find_em_expr_usable_for_sorting_rel(EquivalenceClass *ec, RelOptInfo *rel);
138+
extern Expr *find_em_expr_usable_for_sorting_rel(PlannerInfo *root,
139+
EquivalenceClass *ec,
140+
RelOptInfo *rel,
141+
bool require_parallel_safe);
139142
extern void generate_base_implied_equalities(PlannerInfo *root);
140143
extern List *generate_join_implied_equalities(PlannerInfo *root,
141144
Relids join_relids,

src/test/regress/expected/incremental_sort.out

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,46 @@ order by 1, 2;
15511551
-> Function Scan on generate_series
15521552
(7 rows)
15531553

1554+
-- Parallel sort but with expression (correlated subquery) that
1555+
-- is prohibited in parallel plans.
1556+
explain (costs off) select distinct
1557+
unique1,
1558+
(select t.unique1 from tenk1 where tenk1.unique1 = t.unique1)
1559+
from tenk1 t, generate_series(1, 1000);
1560+
QUERY PLAN
1561+
---------------------------------------------------------------------------------
1562+
Unique
1563+
-> Sort
1564+
Sort Key: t.unique1, ((SubPlan 1))
1565+
-> Gather
1566+
Workers Planned: 2
1567+
-> Nested Loop
1568+
-> Parallel Index Only Scan using tenk1_unique1 on tenk1 t
1569+
-> Function Scan on generate_series
1570+
SubPlan 1
1571+
-> Index Only Scan using tenk1_unique1 on tenk1
1572+
Index Cond: (unique1 = t.unique1)
1573+
(11 rows)
1574+
1575+
explain (costs off) select
1576+
unique1,
1577+
(select t.unique1 from tenk1 where tenk1.unique1 = t.unique1)
1578+
from tenk1 t, generate_series(1, 1000)
1579+
order by 1, 2;
1580+
QUERY PLAN
1581+
---------------------------------------------------------------------------
1582+
Sort
1583+
Sort Key: t.unique1, ((SubPlan 1))
1584+
-> Gather
1585+
Workers Planned: 2
1586+
-> Nested Loop
1587+
-> Parallel Index Only Scan using tenk1_unique1 on tenk1 t
1588+
-> Function Scan on generate_series
1589+
SubPlan 1
1590+
-> Index Only Scan using tenk1_unique1 on tenk1
1591+
Index Cond: (unique1 = t.unique1)
1592+
(10 rows)
1593+
15541594
-- Parallel sort but with expression not available until the upper rel.
15551595
explain (costs off) select distinct sub.unique1, stringu1 || random()::text
15561596
from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub;

src/test/regress/sql/incremental_sort.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,17 @@ from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub;
250250
explain (costs off) select sub.unique1, md5(stringu1)
251251
from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub
252252
order by 1, 2;
253+
-- Parallel sort but with expression (correlated subquery) that
254+
-- is prohibited in parallel plans.
255+
explain (costs off) select distinct
256+
unique1,
257+
(select t.unique1 from tenk1 where tenk1.unique1 = t.unique1)
258+
from tenk1 t, generate_series(1, 1000);
259+
explain (costs off) select
260+
unique1,
261+
(select t.unique1 from tenk1 where tenk1.unique1 = t.unique1)
262+
from tenk1 t, generate_series(1, 1000)
263+
order by 1, 2;
253264
-- Parallel sort but with expression not available until the upper rel.
254265
explain (costs off) select distinct sub.unique1, stringu1 || random()::text
255266
from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub;

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