Re: BUG #19852: NOT IN -> anti-join conversion gives wrong results when the equality operator returns NULL for non-n

From: Keyerror Smart <smartkeyerror(at)gmail(dot)com>
To: Tender Wang <tndrwang(at)gmail(dot)com>
Cc: 303677365(at)qq(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Re: BUG #19852: NOT IN -> anti-join conversion gives wrong results when the equality operator returns NULL for non-n
Date: 2026-11-09 02:36:12
Message-ID: CAD=-kXb5xhFbCf+_HdyWOYfOkY8deLyqTWjZ9PBzbK3Q-zSZHw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

On Mon, Nov 9, 2026 at 12:59 AM Tender Wang <tndrwang(at)gmail(dot)com> wrote:
> The ?= operator the reproducer created is not a member of any B-tree
> or hash operator family, so op_is_safe_index_member() should reject
> the conversion.

Yeah, the original script never gets to the conversion. But if you
put that kind of operator into an opclass, it does, and the result is
wrong. On current master:

CREATE FUNCTION eq_null(int4, int4) RETURNS bool AS $f$
SELECT CASE WHEN $1 = 7 AND $2 <> 7 THEN NULL ELSE $1 = $2 END
$f$ LANGUAGE sql IMMUTABLE;
CREATE OPERATOR ?= (PROCEDURE = eq_null, LEFTARG = int4, RIGHTARG = int4);
CREATE OPERATOR CLASS weird_hash FOR TYPE int4 USING hash AS
OPERATOR 1 ?=, FUNCTION 1 hashint4(int4);

CREATE TABLE onn (x int NOT NULL);
INSERT INTO onn VALUES (2), (3), (7);
CREATE TABLE inn (y int NOT NULL);
INSERT INTO inn VALUES (2), (4);

SELECT x FROM onn WHERE NOT (x ?= ANY (SELECT y FROM inn));
x
---
3
7

That's a Nested Loop Anti Join; forcing the SubPlan with "... IS TRUE"
gives just 3.

Whether this counts as a bug is debatable. A btree version of this is
plain user error, since btree.sgml requires "=" to be an equivalence
relation. The hash case is fuzzier: the operator above is reflexive
and consistent with hashint4(), and I couldn't find anything in the
docs saying an opfamily member must not return NULL for non-null
inputs. The planner just assumes it.

I don't think we can check this from the catalogs (strictness says
nothing about it), so the simplest fix is probably to document the
requirement rather than change the code. Restricting the conversion
to built-in operators would also work, but it seems like overkill for
something you can only hit with a deliberately weird opclass.

Thoughts?

--
Zhenglong Li

In response to

Browse pgsql-bugs by date

  From Date Subject
Previous Message Tender Wang 2026-11-09 00:59:23 Re: BUG #19852: NOT IN -> anti-join conversion gives wrong results when the equality operator returns NULL for non-n