BUG #19713: WindowAgg qual pushdown gives wrong partition count when scale(numeric) distinguishes equal values

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)

Responses

Browse pgsql-bugs by date

  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