x = ANY (subquery) and x IN (subquery) are reported non-null, but PostgreSQL returns null when the subquery yields a null row and no match is found. The generated field is non-nullable and the mapper receives null.
This is pre-existing and unrelated to GROUPING SETS. It was found while verifying #236 and is filed separately because the fix belongs to a different code path.
Repro
CREATE TABLE t (id INT PRIMARY KEY, a TEXT NOT NULL);
INSERT INTO t VALUES (1, 'x');
CREATE TABLE u (v TEXT);
INSERT INTO u VALUES ('q'), (NULL);
-- name: anyOverNullableSubquery :many
SELECT a = ANY (SELECT v FROM u) FROM t;
Norm reports notNull = true. PostgreSQL returns null:
?column?
----------
(1 row)
Three-valued logic: 'x' = 'q' is false, 'x' = NULL is unknown, so the ANY result is unknown rather than false.
Cause
NodeTreeNullabilityAnalyzer's SubLink branch treats ANY_SUBLINK and EXISTS_SUBLINK alike. EXISTS is genuinely never null; ANY/IN over a subquery is not. The subquery body is also absent from the parsed SubLink node (only subLinkType and outerOperand are retained), so the analyzer cannot see the inner column's nullability even in principle without extending the parser.
Note x = ANY (array) is a ScalarArrayOpExpr, a different node, and is not implicated here.
Affected versions
PostgreSQL 16, 17 and 18 — verified identical on all three.
Verified at
Commit 58f9029, before the #236 work. The SubLink branch is byte-identical at that commit, so this is not a regression from it.
x = ANY (subquery)andx IN (subquery)are reported non-null, but PostgreSQL returnsnullwhen the subquery yields anullrow and no match is found. The generated field is non-nullable and the mapper receivesnull.This is pre-existing and unrelated to GROUPING SETS. It was found while verifying #236 and is filed separately because the fix belongs to a different code path.
Repro
Norm reports
notNull = true. PostgreSQL returnsnull:Three-valued logic:
'x' = 'q'is false,'x' = NULLis unknown, so theANYresult is unknown rather than false.Cause
NodeTreeNullabilityAnalyzer'sSubLinkbranch treatsANY_SUBLINKandEXISTS_SUBLINKalike.EXISTSis genuinely nevernull;ANY/INover a subquery is not. The subquery body is also absent from the parsedSubLinknode (onlysubLinkTypeandouterOperandare retained), so the analyzer cannot see the inner column's nullability even in principle without extending the parser.Note
x = ANY (array)is aScalarArrayOpExpr, a different node, and is not implicated here.Affected versions
PostgreSQL 16, 17 and 18 — verified identical on all three.
Verified at
Commit
58f9029, before the #236 work. TheSubLinkbranch is byte-identical at that commit, so this is not a regression from it.