From f658235a448aa9462249f9d3448168be1e63a55f Mon Sep 17 00:00:00 2001 From: Amit Kapila Date: Tue, 4 Sep 2018 11:01:25 +0530 Subject: [PATCH] Prohibit pushing subqueries containing window function calculation to workers. Allowing window function calculation in workers leads to inconsistent results because if the input row ordering is not fully deterministic, the output of window functions might vary across workers. The fix is to treat them as parallel-restricted. In the passing, improve the coding pattern in max_parallel_hazard_walker so that it has a chain of mutually-exclusive if ... else if ... else if ... else if ... IsA tests. Reported-by: Marko Tiikkaja Bug: 15324 Author: Amit Kapila Reviewed-by: Tom Lane Backpatch-through: 9.6 Discussion: https://postgr.es/m/CAL9smLAnfPJCDUUG4ckX2iznj53V7VSMsYefzZieN93YxTNOcw@mail.gmail.com --- src/backend/optimizer/util/clauses.c | 14 +++++++++++ src/test/regress/expected/select_parallel.out | 23 +++++++++++++++++++ src/test/regress/sql/select_parallel.sql | 5 ++++ 3 files changed, 42 insertions(+) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8cdee705d15..9f2ead73cb5 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -1161,6 +1161,20 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context) return true; } + /* + * Treat window functions as parallel-restricted because we aren't sure + * whether the input row ordering is fully deterministic, and the output + * of window functions might vary across workers if not. (In some cases, + * like where the window frame orders by a primary key, we could relax + * this restriction. But it doesn't currently seem worth expending extra + * effort to do so.) + */ + else if (IsA(node, WindowFunc)) + { + if (!context->allow_restricted) + return true; + } + /* * As a notational convenience for callers, look through RestrictInfo. */ diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 43801e0f8a2..926202192b3 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -182,6 +182,29 @@ select count(*) from tenk1; (1 row) reset role; +-- Window function calculation can't be pushed to workers. +explain (costs off, verbose) + select count(*) from tenk1 a where (unique1, two) in + (select unique1, row_number() over() from tenk1 b); + QUERY PLAN +------------------------------------------------------------------------------------ + Aggregate + Output: count(*) + -> Hash Semi Join + Hash Cond: ((a.unique1 = b.unique1) AND (a.two = (row_number() OVER (?)))) + -> Gather + Output: a.unique1, a.two + Workers Planned: 4 + -> Parallel Seq Scan on public.tenk1 a + Output: a.unique1, a.two + -> Hash + Output: b.unique1, (row_number() OVER (?)) + -> WindowAgg + Output: b.unique1, row_number() OVER (?) + -> Index Only Scan using tenk1_unique1 on public.tenk1 b + Output: b.unique1 +(15 rows) + explain (costs off) select stringu1::int2 from tenk1 where unique1 = 1; QUERY PLAN diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql index 7defc34cb78..266d0dd6464 100644 --- a/src/test/regress/sql/select_parallel.sql +++ b/src/test/regress/sql/select_parallel.sql @@ -83,6 +83,11 @@ drop role regress_parallel_worker; select count(*) from tenk1; reset role; +-- Window function calculation can't be pushed to workers. +explain (costs off, verbose) + select count(*) from tenk1 a where (unique1, two) in + (select unique1, row_number() over() from tenk1 b); + explain (costs off) select stringu1::int2 from tenk1 where unique1 = 1;