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 1wmTrf-000o7h-0D for pgsql-bugs@arkaria.postgresql.org; Wed, 22 Jul 2026 10:07:47 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wmTrd-00BUZ2-1s for pgsql-bugs@arkaria.postgresql.org; Wed, 22 Jul 2026 10:07:45 +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.96) (envelope-from ) id 1wmBfd-007tDz-0O for pgsql-bugs@lists.postgresql.org; Tue, 21 Jul 2026 14:42:08 +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 1wmBfZ-00000001HUh-0EKD for pgsql-bugs@lists.postgresql.org; Tue, 21 Jul 2026 14:42:07 +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=i38t6gBwZmGmR46JtKqxNgb5n1IKznGGUGVEJfHmzkg=; b=tWqPrxnzeqp0ma4vydFziSqFt6 /NVSMmyIhXylyX/9wCma2R722iMLY3G75FydWkuFgGifUCse1KcZj5OX3UcV+lsqq35Zb/cH87eDg t5CUXOowcEtgJQMplmyJKoCiVOKcMEv/6IvtRQD+24TGDG5oYpCFXaYJScWQn1Sg+poSvRBGZM0ec s5wvJafW109Eb0zZmYrjB82IdyJ43tO4ds23xSpm7SCCAh+VuMKxI+Gy1gIzmwdCK62FfHpV/simi Lw51+UkLuem7ZbmDfawR11z9W4Rkwmb0SH3yd983Y+AfDRZcG1WSVrWo4AKG4oZYkiEzlA+rL8GWn 5fs8Qvrw==; 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 1wmBfY-001QcV-2M for pgsql-bugs@lists.postgresql.org; Tue, 21 Jul 2026 14:42: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 1wmBfX-00000001MPB-1WdJ for pgsql-bugs@lists.postgresql.org; Tue, 21 Jul 2026 14:42:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19565: Duplicating an equivalent ANY predicate changes a semijoin into a slower per-row SubPlan To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: 2320415112@qq.com Reply-To: 2320415112@qq.com, pgsql-bugs@lists.postgresql.org Date: Tue, 21 Jul 2026 14:41:10 +0000 Message-ID: <19565-6010d3d795be6e38@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: 19565 Logged by: cl hl Email address: 2320415112@qq.com PostgreSQL version: 17.10 Operating system: Linux LAPTOP-2SQAVLB0 6.6.87.2-microsoft-standard- Description: =20 ## Description This issue concerns an idempotent Boolean rewrite such as `P OR P`, which is logically equivalent to `P`. PostgreSQL simplifies the displayed filter to a single predicate, but the redundant SQL form can still receive a different and slower execution strategy. Three independently generated cases exhibited the same order-stable slowdown. ### Expected Behaviour PostgreSQL should canonicalize an idempotent Boolean expression before selecting the access and join strategy. `P` and `P OR P` should therefore produce equivalent plans and comparable execution times, particularly when `P` contains an uncorrelated `ANY` subquery. ### Actual Behaviour In the standalone case below, the original predicate uses a `Nested Loop Semi Join`. After the same predicate is duplicated with `OR`, PostgreSQL uses a sequential scan with a per-row materialized `SubPlan`. Although the final plan displays only one copy of the predicate, execution time increases from 3,577.467 ms to 4,157.191 ms, approximately 16.2%. All three remained slower in both execution orders (`order_stable: True`). ## How to repeat Run the following complete SQL in a new PostgreSQL session: ```sql DROP TABLE IF EXISTS duplicate_operand_outer; DROP TABLE IF EXISTS duplicate_operand_inner; CREATE TABLE duplicate_operand_outer ( v INTEGER NOT NULL ); CREATE TABLE duplicate_operand_inner ( v INTEGER NOT NULL ); INSERT INTO duplicate_operand_outer (v) SELECT 1 FROM generate_series(1, 1000); INSERT INTO duplicate_operand_inner (v) SELECT 1 FROM generate_series(1, 100000); ANALYZE duplicate_operand_outer; ANALYZE duplicate_operand_inner; -- Original predicate P. The result is 0. EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT COUNT(*) FROM duplicate_operand_outer AS o WHERE o.v <> ANY ( SELECT i.v FROM duplicate_operand_inner AS i ); -- Idempotent rewrite P OR P. The result is also 0. EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT COUNT(*) FROM duplicate_operand_outer AS o WHERE o.v <> ANY ( SELECT i.v FROM duplicate_operand_inner AS i ) OR o.v <> ANY ( SELECT i.v FROM duplicate_operand_inner AS i ); ``` On the tested version, the characteristic plans and timings are: ```text P: Nested Loop Semi Join Execution Time: 3577.467 ms P OR P: Seq Scan on duplicate_operand_outer Filter: ANY (... SubPlan 1 ...) Execution Time: 4157.191 ms ```