Skip to content

Commit 75f9c4c

Browse files
author
Amit Kapila
committed
Don't allow LIMIT/OFFSET clause within sub-selects to be pushed to workers.
Allowing sub-select containing LIMIT/OFFSET in workers can lead to inconsistent results at the top-level as there is no guarantee that the row order will be fully deterministic. The fix is to prohibit pushing LIMIT/OFFSET within sub-selects to workers. Reported-by: Andrew Fletcher Bug: 15324 Author: Amit Kapila Reviewed-by: Dilip Kumar Backpatch-through: 9.6 Discussion: https://postgr.es/m/153417684333.10284.11356259990921828616@wrigleys.postgresql.org
1 parent 0ba06e0 commit 75f9c4c

File tree

5 files changed

+40
-2
lines changed

5 files changed

+40
-2
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,20 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
620620
* the SubqueryScanPath as not parallel-safe. (Note that
621621
* set_subquery_pathlist() might push some of these quals down
622622
* into the subquery itself, but that doesn't change anything.)
623+
*
624+
* We can't push sub-select containing LIMIT/OFFSET to workers as
625+
* there is no guarantee that the row order will be fully
626+
* deterministic, and applying LIMIT/OFFSET will lead to
627+
* inconsistent results at the top-level. (In some cases, where
628+
* the result is ordered, we could relax this restriction. But it
629+
* doesn't currently seem worth expending extra effort to do so.)
623630
*/
631+
{
632+
Query *subquery = castNode(Query, rte->subquery);
633+
634+
if (limit_needed(subquery))
635+
return;
636+
}
624637
break;
625638

626639
case RTE_JOIN:

src/backend/optimizer/plan/planner.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ static void preprocess_rowmarks(PlannerInfo *root);
123123
static double preprocess_limit(PlannerInfo *root,
124124
double tuple_fraction,
125125
int64 *offset_est, int64 *count_est);
126-
static bool limit_needed(Query *parse);
127126
static void remove_useless_groupby_columns(PlannerInfo *root);
128127
static List *preprocess_groupclause(PlannerInfo *root, List *force);
129128
static List *extract_rollup_sets(List *groupingSets);
@@ -2870,7 +2869,7 @@ preprocess_limit(PlannerInfo *root, double tuple_fraction,
28702869
* a key distinction: here we need hard constants in OFFSET/LIMIT, whereas
28712870
* in preprocess_limit it's good enough to consider estimated values.
28722871
*/
2873-
static bool
2872+
bool
28742873
limit_needed(Query *parse)
28752874
{
28762875
Node *node;

src/include/optimizer/planner.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ extern bool is_dummy_plan(Plan *plan);
4747
extern RowMarkType select_rowmark_type(RangeTblEntry *rte,
4848
LockClauseStrength strength);
4949

50+
extern bool limit_needed(Query *parse);
51+
5052
extern void mark_partial_aggref(Aggref *agg, AggSplit aggsplit);
5153

5254
extern Path *get_cheapest_fractional_path(RelOptInfo *rel,

src/test/regress/expected/select_parallel.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,25 @@ explain (costs off, verbose)
984984
Output: b.unique1
985985
(18 rows)
986986

987+
-- LIMIT/OFFSET within sub-selects can't be pushed to workers.
988+
explain (costs off)
989+
select * from tenk1 a where two in
990+
(select two from tenk1 b where stringu1 like '%AAAA' limit 3);
991+
QUERY PLAN
992+
---------------------------------------------------------------
993+
Hash Semi Join
994+
Hash Cond: (a.two = b.two)
995+
-> Gather
996+
Workers Planned: 4
997+
-> Parallel Seq Scan on tenk1 a
998+
-> Hash
999+
-> Limit
1000+
-> Gather
1001+
Workers Planned: 4
1002+
-> Parallel Seq Scan on tenk1 b
1003+
Filter: (stringu1 ~~ '%AAAA'::text)
1004+
(11 rows)
1005+
9871006
-- to increase the parallel query test coverage
9881007
SAVEPOINT settings;
9891008
SET LOCAL force_parallel_mode = 1;

src/test/regress/sql/select_parallel.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,11 @@ explain (costs off, verbose)
368368
(select unique1, row_number() over() from tenk1 b);
369369

370370

371+
-- LIMIT/OFFSET within sub-selects can't be pushed to workers.
372+
explain (costs off)
373+
select * from tenk1 a where two in
374+
(select two from tenk1 b where stringu1 like '%AAAA' limit 3);
375+
371376
-- to increase the parallel query test coverage
372377
SAVEPOINT settings;
373378
SET LOCAL force_parallel_mode = 1;

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