[PATCH] Fix hashed ScalarArrayOp semantics for NULL LHS with non-strict comparators

From: Chengpeng Yan <chengpeng_yan(at)outlook(dot)com>
To: PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org>
Cc: David Rowley <dgrowleyml(at)gmail(dot)com>
Subject: [PATCH] Fix hashed ScalarArrayOp semantics for NULL LHS with non-strict comparators
Date: 2026-04-16 13:01:38
Message-ID: A16187AE-2359-4265-9F5E-71D015EC2B2D@outlook.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

ExecEvalHashedScalarArrayOp() produces results that are not semantically
equivalent to ExecEvalScalarArrayOp() when the LHS is NULL and the
comparison function is non-strict.

With the attached setup, the following two queries disagree:

select (a in
(0::myint,1::myint,2::myint,3::myint,4::myint,
5::myint,6::myint,7::myint,8::myint,9::myint)) is null
from inttest where a is null;

This takes the hashed SAOP path and returns false.

select (a in
(0::myint,1::myint,2::myint,3::myint,4::myint,5::myint)) is null
from inttest where a is null;

This stays on the linear path and returns true.

This only occurs when:
* the planner selects the hashed SAOP path,
* the LHS evaluates to NULL at execution time, and
* the comparison function is non-strict.

The root cause is that ExecEvalHashedScalarArrayOp() only special-cases
NULL LHS for strict functions, and otherwise proceeds to probe the hash
table. This is incorrect for non-strict functions, and can also result
in probing with an undefined Datum.

The first attached patch fixes this by bypassing hash probing when the
LHS is NULL and the comparator is non-strict, falling back to a linear
evaluation consistent with ExecEvalScalarArrayOp(). For NOT IN, only
non-NULL results are inverted.

The second patch is a cleanup that factors out the common array scan and
boolean reduction logic shared by ExecEvalScalarArrayOp() and the new
fallback path. No functional change intended.

The patches include regression tests using a custom type with a
non-strict, hashable “=” operator. A standalone SQL reproducer is also
attached.

--
Best regards,
Chengpeng Yan

Attachment Content-Type Size
v1-0002-Factor-out-common-ScalarArrayOp-array-reduction-l.patch application/octet-stream 6.9 KB
v1-0001-Fix-hashed-ScalarArrayOp-NULL-handling-for-non-st.patch application/octet-stream 6.5 KB
repro.sql application/octet-stream 2.3 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Daniel Gustafsson 2026-04-16 13:32:54 PostgreSQL and OpenSSL 4.0.0
Previous Message Amul Sul 2026-04-16 12:30:38 Re: Use XLogRecPtrIsValid() instead of negated XLogRecPtrIsInvalid