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.