redundant double negation prevents IN-subquery pull-up and causes a slower SubPlan

From: 陈列行 <2320415112(at)qq(dot)com>
To: pgsql-hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: redundant double negation prevents IN-subquery pull-up and causes a slower SubPlan
Date: 2026-08-17 08:16:39
Message-ID: tencent_0B49E6A81AE42711B80C20E563ED01765505@qq.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

## Description

This issue concerns the identity `NOT NOT P = 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 (
&nbsp; &nbsp;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 (
&nbsp; &nbsp;o.v IN (SELECT i.v FROM identity_inner AS i)
);
```

Both queries return 1,000. Characteristic plans and measured times are:

```text
P:
&nbsp;Hash Semi Join
&nbsp;Execution Time: 7.143 ms

NOT NOT P:
&nbsp;Seq Scan on identity_outer
&nbsp; &nbsp;Filter: ANY (... hashed SubPlan 1 ...)
&nbsp;Execution Time: 12.321 ms
```

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message 陈列行 2026-08-17 08:17:55 Semantically redundant OR FALSE prevents IN-subquery pull-up and causes a slower SubPlan
Previous Message 陈列行 2026-08-17 08:14:20 Redundant outer DISTINCT adds Sort and Unique above EXCEPT