| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | imchifan(at)163(dot)com |
| Subject: | BUG #19713: WindowAgg qual pushdown gives wrong partition count when scale(numeric) distinguishes equal values |
| Date: | 2026-09-22 16:01:34 |
| Message-ID: | 19713-635b8656d07464df@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19713
Logged by: Qifan Liu
Email address: imchifan(at)163(dot)com
PostgreSQL version: 18.6
Operating system: Linux/amd64
Description:
An outer scale(numeric) predicate is pushed below WindowAgg even though
numeric values 1.0 and 1.00 are equal under PARTITION BY semantics while
scale() distinguishes them. The pushed predicate removes one partition
member and produces an incorrect window count of 1 instead of 2.
Steps to reproduce
------------------
Run the following input with psql:
\set ON_ERROR_STOP on
BEGIN;
CREATE TEMP TABLE bugseer_postgres_00010_window_scale (x numeric);
INSERT INTO bugseer_postgres_00010_window_scale VALUES (1.0), (1.00);
CREATE FUNCTION bugseer_postgres_00010_identity_numeric_window(numeric)
RETURNS numeric
LANGUAGE plpgsql VOLATILE STRICT
AS 'BEGIN RETURN $1; END';
EXPLAIN (COSTS OFF)
SELECT n
FROM
(
SELECT x, count(*) OVER (PARTITION BY x) AS n
FROM bugseer_postgres_00010_window_scale
) s
WHERE scale(x) = 1;
EXPLAIN (COSTS OFF)
SELECT n
FROM
(
SELECT x, count(*) OVER (PARTITION BY x) AS n
FROM bugseer_postgres_00010_window_scale
) s
WHERE scale(bugseer_postgres_00010_identity_numeric_window(x)) = 1;
WITH optimized AS
(
SELECT n
FROM
(
SELECT x, count(*) OVER (PARTITION BY x) AS n
FROM bugseer_postgres_00010_window_scale
) s
WHERE scale(x) = 1
), baseline AS
(
SELECT n
FROM
(
SELECT x, count(*) OVER (PARTITION BY x) AS n
FROM bugseer_postgres_00010_window_scale
) s
WHERE scale(bugseer_postgres_00010_identity_numeric_window(x)) = 1
)
SELECT (SELECT min(n) FROM optimized) AS optimized_n,
(SELECT min(n) FROM baseline) AS baseline_n,
(SELECT min(n) FROM optimized) = (SELECT min(n) FROM baseline)
AS invariant_holds;
ROLLBACK;
Actual result
-------------
The plain predicate appears as a sequential-scan filter below WindowAgg,
while the value-preserving volatile form remains above WindowAgg:
optimized_n | baseline_n | invariant_holds
-------------+------------+-----------------
1 | 2 | f
(1 row)
Expected result
---------------
Filtering must preserve every member equal under the window partition
semantics. Both forms should report partition count 2, and invariant_holds
should be true.
Additional information
----------------------
The issue was reproduced on PostgreSQL 20devel, PostgreSQL 18.6, and
PostgreSQL 17.11.
This issue may be similar to the three reports below [1–2], but I am unsure
whether they share the same root cause.
[1] [Fujii Masao’s scale(numeric)
report](https://www.postgresql.org/message-id/CAHGQGwGnPNX=nK+nXL0ihh+8WObQnFWjnPi5uvxBFvbtqN9WmQ@mail.gmail.com)
[2] [Richard Guo’s explanation of the known
limitation](https://www.postgresql.org/message-id/CAMbWs4_=1kH=J2qDmdQm1NrM0oVPXO8dETOSaUfMUYatfbDbRA@mail.gmail.com)
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-09-22 16:05:30 | BUG #19714: pgcrypto pgp_sym_encrypt accepts nonnumeric s2k-mode as mode 0 |
| Previous Message | Andrey Borodin | 2026-09-22 12:37:03 | Re: BUG #19705: One NaN box makes a BRIN box_inclusion_ops index omit unrelated rows |