Skip to content

Commit be9c3cd

Browse files
committed
Check parallel safety in generate_useful_gather_paths
Commit ebb7ae839d 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 ea190ed commit be9c3cd

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
@@ -2742,14 +2742,18 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows)
27422742
* This allows us to do incremental sort on top of an index scan under a gather
27432743
* merge node, i.e. parallelized.
27442744
*
2745+
* If the require_parallel_safe is true, we also require the expressions to
2746+
* be parallel safe (which allows pushing the sort below Gather Merge).
2747+
*
27452748
* XXX At the moment this can only ever return a list with a single element,
27462749
* because it looks at query_pathkeys only. So we might return the pathkeys
27472750
* directly, but it seems plausible we'll want to consider other orderings
27482751
* in the future. For example, we might want to consider pathkeys useful for
27492752
* merge joins.
27502753
*/
27512754
static List *
2752-
get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
2755+
get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel,
2756+
bool require_parallel_safe)
27532757
{
27542758
List *useful_pathkeys_list = NIL;
27552759

@@ -2779,8 +2783,11 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
27792783
* meet criteria of EC membership in the current relation, we
27802784
* enable not just an incremental sort on the entirety of
27812785
* query_pathkeys but also incremental sort below a JOIN.
2786+
*
2787+
* If requested, ensure the expression is parallel safe too.
27822788
*/
2783-
if (!find_em_expr_usable_for_sorting_rel(pathkey_ec, rel))
2789+
if (!find_em_expr_usable_for_sorting_rel(root, pathkey_ec, rel,
2790+
require_parallel_safe))
27842791
break;
27852792

27862793
npathkeys++;
@@ -2834,7 +2841,7 @@ generate_useful_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_r
28342841
generate_gather_paths(root, rel, override_rows);
28352842

28362843
/* consider incremental sort for interesting orderings */
2837-
useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
2844+
useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel, true);
28382845

28392846
/* used for explicit (full) sort paths */
28402847
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
@@ -800,7 +800,8 @@ find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel)
800800
* applied in prepare_sort_from_pathkeys.
801801
*/
802802
Expr *
803-
find_em_expr_usable_for_sorting_rel(EquivalenceClass *ec, RelOptInfo *rel)
803+
find_em_expr_usable_for_sorting_rel(PlannerInfo *root, EquivalenceClass *ec,
804+
RelOptInfo *rel, bool require_parallel_safe)
804805
{
805806
ListCell *lc_em;
806807

@@ -830,6 +831,12 @@ find_em_expr_usable_for_sorting_rel(EquivalenceClass *ec, RelOptInfo *rel)
830831
if (!bms_is_subset(em->em_relids, rel->relids))
831832
continue;
832833

834+
/*
835+
* If requested, reject expressions that are not parallel-safe.
836+
*/
837+
if (require_parallel_safe && !is_parallel_safe(root, (Node *) em_expr))
838+
continue;
839+
833840
/*
834841
* As long as the expression isn't volatile then
835842
* 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