[PATCH] Fix hashed ScalarArrayOp semantics for NULL LHS with non-strict comparators
Chengpeng Yan <chengpeng_yan@outlook.com>
From: Chengpeng Yan <chengpeng_yan@outlook.com>
To: PostgreSQL-development <pgsql-hackers@postgresql.org>
Cc: David Rowley <dgrowleyml@gmail.com>
Date: 2026-04-16T13:01:38Z
Lists: pgsql-hackers
Attachments
- v1-0002-Factor-out-common-ScalarArrayOp-array-reduction-l.patch (application/octet-stream) patch v1-0002
- v1-0001-Fix-hashed-ScalarArrayOp-NULL-handling-for-non-st.patch (application/octet-stream) patch v1-0001
- repro.sql (application/octet-stream)
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
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Fix incorrect logic for hashed IN / NOT IN with non-strict operators
- 109de35b705c 14.23 landed
- 622f8b53014e 15.18 landed
- a2a0060d5d8f 16.14 landed
- 3fda3e12f41b 17.10 landed
- 035c520db866 18.4 landed
- 94219a73f79d 19 (unreleased) landed