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.96) (envelope-from ) id 1wXOoe-003F2Z-2Q for pgsql-bugs@arkaria.postgresql.org; Wed, 10 Jun 2026 19:42:20 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wXOod-00Cl4R-27 for pgsql-bugs@arkaria.postgresql.org; Wed, 10 Jun 2026 19:42:19 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wXL78-00CHJT-0H for pgsql-bugs@lists.postgresql.org; Wed, 10 Jun 2026 15:45:10 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wXL75-00000002MHR-1JBM for pgsql-bugs@lists.postgresql.org; Wed, 10 Jun 2026 15:45:09 +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=0MOZS23B7f/oSBMHKJLqX13K04mQLGGPCzEYQU3s+mE=; b=TjQqMhFiUeML0rim0rb98W95YO ASVsqusH6hY4g13SupL6OUBleCZawwkdL1VrtnI/TE2SSkPgS8IodLOe0DOE60HqOjQSJU+SUBP1p 8C1I6qCDI4gNR6gAZcd7tf22rLH+0GM45Sqr0gAk/Bx4LCpAZM1vnWX34rTpYGJvuDALXR55tPGW1 UzRN0WeqPnI7wm4ArzDwhTrMaF9b9D36XCU3SlYf7uz/SxFJJETIp82Zove6cjOJ+JsTiD3dulLgB 4wguW+EaJ8W8Yju+M6M3Cl+mXAftkUw/ZcLmjvtA/9JHUX4IOvYg5ffCaupG/+AOj//NOqgTS9jgm Q5wF5acg==; 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 1wXL73-006YuT-31 for pgsql-bugs@lists.postgresql.org; Wed, 10 Jun 2026 15:45:05 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wXL71-00EoYm-2R for pgsql-bugs@lists.postgresql.org; Wed, 10 Jun 2026 15:45:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19517: Eager Aggregation produces wrong count(*) when pushed into RHS of Hash Semi Join To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: ihalatci@gmail.com Reply-To: ihalatci@gmail.com, pgsql-bugs@lists.postgresql.org Date: Wed, 10 Jun 2026 15:44:31 +0000 Message-ID: <19517-4c53dcdcef98d263@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: 19517 Logged by: Ibrahim Halatci Email address: ihalatci@gmail.com PostgreSQL version: 19beta1 Operating system: Linux x86_64 Description: =20 Description ----------- The "Implement Eager Aggregation" optimization (commit by Richard Guo, 2025-10-08, master) can push a partial aggregate into the inner side of a semi-join. In that position the partial count(*) counts inner-side rows that match the semi-join condition, but the finalize aggregate above the join then sums those PARTIAL counts as if they were counts of outer rows. The query therefore returns inflated counts. Disabling hash aggregation (SET enable_hashagg =3D off) is a reliable workaround because it forces the planner away from the bad plan shape. Minimal self-contained reproducer --------------------------------- DROP TABLE IF EXISTS u, e; CREATE TABLE u(user_id int, value_2 int); CREATE TABLE e(user_id int, value_2 int); INSERT INTO u SELECT 5, s % 4 FROM generate_series(1, 26) s; INSERT INTO e SELECT s % 5, s % 4 FROM generate_series(1, 100) s; ANALYZE u; ANALYZE e; -- Wrong result on PG19beta1: SELECT u.user_id, count(*) FROM u WHERE u.value_2 =3D ANY (SELECT e.value_2 FROM e WHERE u.user_id > e.user_id) GROUP BY u.user_id; -- Observed on PG19beta1: (5, 130) -- Expected (and result on PG18, PG17, PG16): (5, 26) -- Workaround restores the correct result: SET enable_hashagg =3D off; SELECT u.user_id, count(*) FROM u WHERE u.value_2 =3D ANY (SELECT e.value_2 FROM e WHERE u.user_id > e.user_id) GROUP BY u.user_id; -- Returns: (5, 26) RESET enable_hashagg; EXPLAIN (VERBOSE, COSTS OFF) of the bad plan -------------------------------------------- The diagnostic indicator is a "Hash Semi Join" whose Output list contains "(PARTIAL count(*))", sourced from a "Partial HashAggregate" keyed on the inner relation's columns. The "Finalize HashAggregate" above the join then sums these inner-side partial counts instead of counting matched outer rows. Likely cause ------------ Eager Aggregation appears to consider the RHS of a SEMI/ANTI join a legal site to push a partial aggregate, but for SEMI joins each outer row matches at most one inner row by the join's own semantics, so partial counts taken on the inner side cannot be combined into a correct outer-side count(*). Either eager aggregation must be disabled for the RHS of SEMI/ANTI joins, or the partial state must be projected through the semi-join such that each surviving outer row contributes exactly one partial. Two follow-up commits to eager aggregation landed on 2026-04-06 ("Fix volatile function evaluation in eager aggregation" and "Fix collation handling for grouping keys in eager aggregation"), but neither addresses semi-join correctness. Search performed before reporting --------------------------------- - pgsql-bugs archives for 2026-04, 2026-05, and 2026-06: no matching report. - pgsql-hackers search "eager aggregation semi": 0 hits. - pgsql-hackers search "eager aggregation bug": only the original development thread by Richard Guo. - PostgreSQL 19 Open Items wiki (checked 2026-06-10): not listed under Open Issues, Resolved before 19beta1/19beta2, Non-bugs, or Won't Fix. Impact on downstream -------------------- This was discovered while bringing up Citus on PG19; the regression test multi_subquery_in_where_reference_clause exposes the same plan shape through a distributed query whose worker-side SQL reduces to the pattern above. We have applied SET enable_hashagg =3D off locally as a temporary workaround. Thank you,