mirror of
https://github.com/postgres/postgres.git
synced 2026-07-09 17:51:00 -04:00
Fix COUNT's logic for window run condition support
9d9c02ccd added code to allow the executor to stop early when processing
WindowAgg nodes where a monotonic window function starts producing
values that result in a pushed-down qual no longer matching, and will
never match again due to the window function's monotonic properties.
That commit requires a SupportRequestWFuncMonotonic to exist on the
window function and for it to detect when the function is monotonic. For
COUNT(ANY) and COUNT(*), the support function failed to consider some
cases where the WindowClause used EXCLUDE to exclude certain rows from
being aggregated. Some WindowClause definitions mean we aggregate rows
that come after the current row, and when processing those rows later,
if we EXCLUDE certain rows, the monotonic property can be broken.
Wrongly treating the COUNT(*) or COUNT(ANY) aggregate as monotonic could
lead to rows being filtered that should not be filtered from the result
set.
Another issue was that the support function for the COUNT aggregate
mistakenly thought that a WindowClause without an ORDER BY meant that
the results would be both monotonically increasing and decreasing, but
that's only true when in RANGE mode, where all rows are peers.
It is possible to support various cases that do have an EXCLUDE clause,
but getting the logic correct for the exact set of cases that are valid
is quite complex and would likely better be left for a future project.
Here, we mostly disable run condition pushdown when there is an EXCLUDE
clause unless the clause is for EXCLUDE CURRENT ROW, uses COUNT(*)
(rather than COUNT(ANY)), and the window aggregate has no FILTER clause.
Bug: #19533
Reported-by: Qifan Liu <imchifan@163.com>
Author: Chengpeng Yan <chengpeng_yan@outlook.com>
Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: John Naylor <johncnaylorls@gmail.com>
Discussion: https://postgr.es/m/19533-413a1014e5d0e766@postgresql.org
Backpatch-through: 15
This commit is contained in:
parent
441e4c8d69
commit
cf184ec77a
3 changed files with 362 additions and 10 deletions
|
|
@ -24,7 +24,7 @@
|
|||
#include "nodes/supportnodes.h"
|
||||
#include "optimizer/optimizer.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
#include "utils/fmgroids.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
@ -833,8 +833,38 @@ int8inc_support(PG_FUNCTION_ARGS)
|
|||
MonotonicFunction monotonic = MONOTONICFUNC_NONE;
|
||||
int frameOptions = req->window_clause->frameOptions;
|
||||
|
||||
/* No ORDER BY clause then all rows are peers */
|
||||
if (req->window_clause->orderClause == NIL)
|
||||
/*
|
||||
* Because an EXCLUDE clauses in the window definition can exclude
|
||||
* rows that have previously been included in the aggregate result for
|
||||
* prior rows, this can break the monotonic properties that might
|
||||
* otherwise be guaranteed. There's a narrow set of circumstances
|
||||
* that can be guaranteed, which we check for below.
|
||||
*/
|
||||
if (frameOptions & FRAMEOPTION_EXCLUSION)
|
||||
{
|
||||
WindowFunc *wfunc = req->window_func;
|
||||
|
||||
/*
|
||||
* To add handling for all valid monotonic cases with an EXCLUDE
|
||||
* clause is complex and likely not worth troubling over. For
|
||||
* now, just bail unless we see EXCLUDE CURRENT ROW with COUNT(*)
|
||||
* and no FILTER. Excluding the current row is fine when using
|
||||
* COUNT(*) as this always reduces the count by 1. The same isn't
|
||||
* true for COUNY(ANY) as a NULL won't be counted, and a
|
||||
* subsequent non-NULL could make the count decrease.
|
||||
*/
|
||||
if ((frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) == 0 ||
|
||||
wfunc->winfnoid != F_COUNT_ ||
|
||||
wfunc->aggfilter != NULL)
|
||||
{
|
||||
req->monotonic = MONOTONICFUNC_NONE;
|
||||
PG_RETURN_POINTER(req);
|
||||
}
|
||||
}
|
||||
|
||||
/* No ORDER BY clause and RANGE mode means all rows are peers. */
|
||||
if (req->window_clause->orderClause == NIL &&
|
||||
(frameOptions & FRAMEOPTION_RANGE))
|
||||
monotonic = MONOTONICFUNC_BOTH;
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4232,23 +4232,59 @@ WHERE c <= 3;
|
|||
(8 rows)
|
||||
|
||||
-- Ensure we get the correct run condition when the window function is both
|
||||
-- monotonically increasing and decreasing.
|
||||
-- monotonically increasing and decreasing in RANGE mode without an ORDER BY
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
depname,
|
||||
salary,
|
||||
count(empno) OVER () c
|
||||
count(empno) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c = 1;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------
|
||||
WindowAgg
|
||||
Window: w1 AS ()
|
||||
Window: w1 AS (RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
|
||||
Run Condition: (count(empsalary.empno) OVER w1 = 1)
|
||||
-> Seq Scan on empsalary
|
||||
(4 rows)
|
||||
|
||||
-- As above, but check we detect it's monotonically increasing
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
depname,
|
||||
salary,
|
||||
count(empno) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------
|
||||
WindowAgg
|
||||
Window: w1 AS (RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
|
||||
Run Condition: (count(empsalary.empno) OVER w1 <= 3)
|
||||
-> Seq Scan on empsalary
|
||||
(4 rows)
|
||||
|
||||
-- Ensure that ROWS mode without an ORDER BY doesn't think it's monotonically
|
||||
-- decreasing, i.e. don't push down the run condition.
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
depname,
|
||||
salary,
|
||||
count(empno) OVER (ROWS BETWEEN CURRENT ROW AND CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c > 1;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c > 1)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ROWS BETWEEN CURRENT ROW AND CURRENT ROW)
|
||||
-> Seq Scan on empsalary
|
||||
(5 rows)
|
||||
|
||||
-- Try another case with a WindowFunc with a byref return type
|
||||
SELECT * FROM
|
||||
(SELECT row_number() OVER (PARTITION BY salary) AS rn,
|
||||
|
|
@ -4436,6 +4472,181 @@ WHERE c = 1;
|
|||
-> Seq Scan on empsalary
|
||||
(9 rows)
|
||||
|
||||
--
|
||||
-- Ensure we get the correct behavior for run condition pushdown when the
|
||||
-- frame option has an EXCLUDE clause
|
||||
--
|
||||
-- Ensure pushdown occurs for ROWS BETWEEN UNBOUNDED PRECEDING with EXCLUDE
|
||||
-- CURRENT ROW with COUNT(*)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------------------------------------------------
|
||||
WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE CURRENT ROW)
|
||||
Run Condition: (count(*) OVER w1 <= 3)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(6 rows)
|
||||
|
||||
-- Ensure pushdown occurs for GROUPS BETWEEN UNBOUNDED PRECEDING with EXCLUDE
|
||||
-- CURRENT ROW with COUNT(*)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
|
||||
Run Condition: (count(*) OVER w1 <= 3)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(6 rows)
|
||||
|
||||
-- Ensure pushdown occurs for RANGE BETWEEN UNBOUNDED PRECEDING with EXCLUDE
|
||||
-- CURRENT ROW with COUNT(*)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------------------------------------------------
|
||||
WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
|
||||
Run Condition: (count(*) OVER w1 <= 3)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(6 rows)
|
||||
|
||||
-- Ensure we don't get pushdown when a FILTER clause is present
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) FILTER (WHERE salary > 4000) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c <= 3)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE CURRENT ROW)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(7 rows)
|
||||
|
||||
-- Ensure we don't get pushdown with COUNT(ANY)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(salary) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c <= 3)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE CURRENT ROW)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(7 rows)
|
||||
|
||||
-- Ensure we don't get pushdown with EXCLUDE GROUP
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE GROUP) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c <= 3)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE GROUP)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(7 rows)
|
||||
|
||||
-- Ensure we don't get pushdown with EXCLUDE TIES
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE TIES) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c <= 3)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary ROWS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE TIES)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(7 rows)
|
||||
|
||||
-- Ensure we don't get pushdown with GROUPS mode and EXCLUDE GROUP
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary GROUPS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE GROUP) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c <= 3)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary GROUPS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE GROUP)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(7 rows)
|
||||
|
||||
-- Ensure we don't get pushdown with GROUPS mode and EXCLUDE TIES
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary GROUPS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE TIES) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
QUERY PLAN
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
Subquery Scan on emp
|
||||
Filter: (emp.c <= 3)
|
||||
-> WindowAgg
|
||||
Window: w1 AS (ORDER BY empsalary.salary GROUPS BETWEEN UNBOUNDED PRECEDING AND '0'::bigint PRECEDING EXCLUDE TIES)
|
||||
-> Sort
|
||||
Sort Key: empsalary.salary
|
||||
-> Seq Scan on empsalary
|
||||
(7 rows)
|
||||
|
||||
-- Test Sort node collapsing
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
|
|
|
|||
|
|
@ -1361,16 +1361,37 @@ SELECT * FROM
|
|||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we get the correct run condition when the window function is both
|
||||
-- monotonically increasing and decreasing.
|
||||
-- monotonically increasing and decreasing in RANGE mode without an ORDER BY
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
depname,
|
||||
salary,
|
||||
count(empno) OVER () c
|
||||
count(empno) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c = 1;
|
||||
|
||||
-- As above, but check we detect it's monotonically increasing
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
depname,
|
||||
salary,
|
||||
count(empno) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure that ROWS mode without an ORDER BY doesn't think it's monotonically
|
||||
-- decreasing, i.e. don't push down the run condition.
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
depname,
|
||||
salary,
|
||||
count(empno) OVER (ROWS BETWEEN CURRENT ROW AND CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c > 1;
|
||||
|
||||
-- Try another case with a WindowFunc with a byref return type
|
||||
SELECT * FROM
|
||||
(SELECT row_number() OVER (PARTITION BY salary) AS rn,
|
||||
|
|
@ -1460,6 +1481,96 @@ SELECT * FROM
|
|||
FROM empsalary) emp
|
||||
WHERE c = 1;
|
||||
|
||||
--
|
||||
-- Ensure we get the correct behavior for run condition pushdown when the
|
||||
-- frame option has an EXCLUDE clause
|
||||
--
|
||||
|
||||
-- Ensure pushdown occurs for ROWS BETWEEN UNBOUNDED PRECEDING with EXCLUDE
|
||||
-- CURRENT ROW with COUNT(*)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure pushdown occurs for GROUPS BETWEEN UNBOUNDED PRECEDING with EXCLUDE
|
||||
-- CURRENT ROW with COUNT(*)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure pushdown occurs for RANGE BETWEEN UNBOUNDED PRECEDING with EXCLUDE
|
||||
-- CURRENT ROW with COUNT(*)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we don't get pushdown when a FILTER clause is present
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) FILTER (WHERE salary > 4000) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we don't get pushdown with COUNT(ANY)
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(salary) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we don't get pushdown with EXCLUDE GROUP
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE GROUP) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we don't get pushdown with EXCLUDE TIES
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE TIES) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we don't get pushdown with GROUPS mode and EXCLUDE GROUP
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary GROUPS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE GROUP) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
-- Ensure we don't get pushdown with GROUPS mode and EXCLUDE TIES
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
(SELECT empno,
|
||||
salary,
|
||||
count(*) OVER (ORDER BY salary GROUPS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING EXCLUDE TIES) c
|
||||
FROM empsalary) emp
|
||||
WHERE c <= 3;
|
||||
|
||||
|
||||
-- Test Sort node collapsing
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM
|
||||
|
|
|
|||
Loading…
Reference in a new issue