Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x7TxP-00000000PPi-0Dr7 for pgsql-bugs@arkaria.postgresql.org; Fri, 18 Sep 2026 08:28:31 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1x7TxO-00000008px0-1KKQ for pgsql-bugs@arkaria.postgresql.org; Fri, 18 Sep 2026 08:28:30 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x7Scl-00000008IcV-04Jf for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 07:03:07 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x7Scj-00000000Hdp-1qXJ for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 07:03:06 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Message-ID:Date:Reply-To:Cc:From:To:Subject: Content-Transfer-Encoding:MIME-Version:Content-Type:Sender:Content-ID: Content-Description:In-Reply-To:References; bh=iFXyVmoVCwQNaOn52xp1AlhW1tsDnh6FFZkSAe8GgN0=; b=TEA2SgQa2GXs7Em78s0mtcV77L hIbyjT3kWu7MJXDVrJ01WVDREpZcMFX7ZtsqL4auyEJLHhHtY+yZDXcVa31hfLJm0Z4ITkjiIGePQ XSU/RodvEBX5/7FTjBCv0UXK60f05Y1F2kYmiMMtkc07BADPO08+SMEY7t7b2Ze3Mkz2TgUUbihEM DwJ15PlD5pfJMdyAIvFscZ1f7qEw+z2cNOB7a+fdLPIc5kxBHSWxpZP6WRSTe2JytVUrfKdHjo8Oo 4VJ6gwz/ei5SVdrJf6nNGB0ew0ktsBtllZZTOP57UA94bYwcNn4EoPT4Fims3mDp/SSs+zkE6F0z2 KaGSGmPQ==; Received: from wrigleys.postgresql.org ([2a02:16a8:dc51::60]) by mahout.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x7Scj-001v78-07 for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 07:03:05 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1x7Sch-000000076fl-468D for pgsql-bugs@lists.postgresql.org; Fri, 18 Sep 2026 07:03:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19697: HAVING-to-WHERE transfer gives wrong count when scale(numeric) distinguishes equal grouping values To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: imchifan@163.com Reply-To: imchifan@163.com, pgsql-bugs@lists.postgresql.org Date: Fri, 18 Sep 2026 07:02:16 +0000 Message-ID: <19697-6e7ccba388bbe858@postgresql.org> X-Auto-Response-Suppress: All Auto-Submitted: auto-generated List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk The following bug has been logged on the website: Bug reference: 19697 Logged by: Qifan Liu Email address: imchifan@163.com PostgreSQL version: 18.6 Operating system: Linux/amd64 Description: =20 PostgreSQL version: PostgreSQL 20devel at a12600b762c36d91450ce085fa25ef75250bc1c2; PostgreSQL 18.6; PostgreSQL 17.11 Operating system: Linux/amd64 Description ----------- A HAVING predicate using scale(numeric) is transferred below grouping even though numeric equality considers 1.0 and 1.00 equal while scale() distinguishes them. The pushed predicate removes one member of the group, producing an incorrect aggregate count. Steps to reproduce ------------------ Run the following input with psql: \set ON_ERROR_STOP on BEGIN; SET LOCAL enable_hashagg =3D on; SET LOCAL enable_sort =3D off; CREATE TEMP TABLE bugseer_postgres_00010_numeric_scale (x numeric); INSERT INTO bugseer_postgres_00010_numeric_scale VALUES (1.0), (1.00); CREATE FUNCTION bugseer_postgres_00010_identity_numeric(numeric) RETURNS numeric LANGUAGE plpgsql VOLATILE STRICT AS 'BEGIN RETURN $1; END'; EXPLAIN (COSTS OFF) SELECT x, count(*) AS n FROM bugseer_postgres_00010_numeric_scale GROUP BY x HAVING scale(x) =3D 1; EXPLAIN (COSTS OFF) SELECT x, count(*) AS n FROM bugseer_postgres_00010_numeric_scale GROUP BY x HAVING scale(bugseer_postgres_00010_identity_numeric(x)) =3D 1; WITH optimized AS ( SELECT count(*) AS n FROM bugseer_postgres_00010_numeric_scale GROUP BY x HAVING scale(x) =3D 1 ), baseline AS ( SELECT count(*) AS n FROM bugseer_postgres_00010_numeric_scale GROUP BY x HAVING scale(bugseer_postgres_00010_identity_numeric(x)) =3D 1 ) SELECT optimized.n AS optimized_n, baseline.n AS baseline_n, optimized.n =3D baseline.n AS invariant_holds FROM optimized FULL JOIN baseline ON true; Actual result ------------- The first plan applies scale(x) as a sequential-scan filter, while the value-preserving volatile form retains its filter on HashAggregate: optimized_n | baseline_n | invariant_holds -------------+------------+----------------- 1 | 2 | f (1 row) Expected result --------------- Equivalent pre-group and post-group predicates must preserve the group membership and return aggregate count 2, so invariant_holds should be true. Additional information ---------------------- The issue was reproduced on PostgreSQL 20devel, PostgreSQL 18.6, and PostgreSQL 17.11. The reproducer sets enable_hashagg to on and enable_sort to off for the transaction.