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 1wmTre-000o7h-2q 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-00BUZ7-1s for pgsql-bugs@arkaria.postgresql.org; Wed, 22 Jul 2026 10:07:45 +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 1wmQu8-00Apz5-1j for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:58:08 +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 1wmQu6-00000000Z88-1CuQ for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:58: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=9LG/Ul7mKP58MGiiGTRVzJQCpLYuFEdq79Z7Ixc5Ezc=; b=gGJcns54u9CbeM1wC2+9MjRYSP b43Pock/5agGJm6rygF1TDeRSlQccatjRKqqBqYECHn2ZIHdfb2PO8kk3ifYHoRXm00XnA4D5PH+I yFLGN2TGX25BfthoKav/aTvnjroGFgs8j7vhGVHgXUGDp36GqgNgldul5hgR9nQlIo+DuoTss4+K3 nGk0rHhV/GU/V+ZJx5e+ojhxHGwxxEp8nz//NljPkj3zPTG3mvtCyYsCr1i60xQaFow54VTqV846a cPoDfiZOJ71iM3Kzb/wdFWwhnVAeyTZFilWgKeZXgtBONblyTWVOsE0RN2xcJ2SIfxAyLoz0KoIng HyoaRQ8A==; 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 1wmQu5-001kv2-01 for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:58: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 1wmQu3-000000026LZ-44RC for pgsql-bugs@lists.postgresql.org; Wed, 22 Jul 2026 06:58:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19570: redundant double negation prevents IN-subquery pull-up and causes a slower 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: Wed, 22 Jul 2026 06:57:56 +0000 Message-ID: <19570-ce74757b8bb2eb9c@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: 19570 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 the identity `NOT NOT P =3D P`, which is valid under SQL three-valued logic because double negation preserves TRUE, FALSE, and NULL. When `P` is an `IN` subquery, PostgreSQL chooses different plans for the equivalent forms. ### Expected Behaviour PostgreSQL should remove double negation before subquery planning and produce the same semijoin plan as the unwrapped `IN` predicate. ### Actual Behaviour The plain predicate is pulled up into a `Hash Semi Join`. The double-negated form remains a hashed `SubPlan` evaluated by an outer sequential scan. In the standalone case, execution time increases from 7.143 ms to 12.321 ms, approximately 1.72x. Generated pair 3137 exhibits a larger order-stable instance: median execution time increases from 2.255 ms to 404.978 ms, approximately 178.6x. ## How to repeat ```sql DROP TABLE IF EXISTS identity_outer; DROP TABLE IF EXISTS identity_inner; CREATE TABLE identity_outer (v INTEGER NOT NULL); CREATE TABLE identity_inner (v INTEGER NOT NULL); INSERT INTO identity_outer SELECT g FROM generate_series(90001, 91000) AS g; INSERT INTO identity_inner SELECT g FROM generate_series(1, 100000) AS g; ANALYZE identity_outer; ANALYZE identity_inner; -- Original form P. EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT COUNT(*) FROM identity_outer AS o WHERE o.v IN ( SELECT i.v FROM identity_inner AS i ); -- Equivalent double-negated form NOT NOT P. EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, TIMING OFF) SELECT COUNT(*) FROM identity_outer AS o WHERE NOT NOT ( o.v IN (SELECT i.v FROM identity_inner AS i) ); ``` Both queries return 1,000. Characteristic plans and measured times are: ```text P: Hash Semi Join Execution Time: 7.143 ms NOT NOT P: Seq Scan on identity_outer Filter: ANY (... hashed SubPlan 1 ...) Execution Time: 12.321 ms ``` ## Assessment