Skip to content

Commit

Permalink
opt: don't fold sub-operators with null operands during type check
Browse files Browse the repository at this point in the history
This commit prevents type-checking from replacing expressions like
`NULL = ANY(...)` with `NULL`. This is necessary because in the case when
the right operand is an empty array, the result of the expression is
`False` instead of `NULL`. It is not always possible to know what the
right operand will evaluate to, and constant folding can be handled
during normalization anyway.

Fixes #37793

Release note (bug fix): Fixed a long-standing and rare bug in evaluation
of `ANY`, `SOME`, and `ALL` sub-operators that would cause expressions like
`NULL = ANY(ARRAY[]::INT[])` to return `NULL` instead of `False`.
  • Loading branch information
DrewKimball committed Mar 2, 2023
1 parent 5520c64 commit eec9590
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 4 deletions.
47 changes: 47 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/suboperators
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,50 @@ query B
SELECT 1 = ANY ROW()
----
false

# Regression test for #37793 - don't fold sub-operators with untyped NULL
# operands during type-checking.
query B
SELECT NULL = ANY(ARRAY []::INTEGER[]);
----
false

query B
SELECT NULL = SOME(ARRAY []::INTEGER[]);
----
false

query B
SELECT NULL = ALL(ARRAY []::INTEGER[]);
----
true

query B
SELECT NULL = ANY(ARRAY [1]::INTEGER[]);
----
NULL

query B
SELECT NULL = SOME(ARRAY [1]::INTEGER[]);
----
NULL

query B
SELECT NULL = ALL(ARRAY [1]::INTEGER[]);
----
NULL

query B
SELECT NULL = ANY(NULL::INTEGER[]);
----
NULL

query B
SELECT NULL = SOME(NULL::INTEGER[]);
----
NULL

query B
SELECT NULL = ALL(NULL::INTEGER[]);
----
NULL
45 changes: 45 additions & 0 deletions pkg/sql/sem/eval/testdata/eval/any_some_all
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,48 @@ eval
NULL::string LIKE ANY(ARRAY['bar', NULL])
----
NULL

eval
NULL = ANY(ARRAY []::INTEGER[]);
----
false

eval
NULL = SOME(ARRAY []::INTEGER[]);
----
false

eval
NULL = ALL(ARRAY []::INTEGER[]);
----
true

eval
NULL = ANY(ARRAY [1]::INTEGER[]);
----
NULL

eval
NULL = SOME(ARRAY [1]::INTEGER[]);
----
NULL

eval
NULL = ALL(ARRAY [1]::INTEGER[]);
----
NULL

eval
NULL = ANY(NULL::INTEGER[]);
----
NULL

eval
NULL = SOME(NULL::INTEGER[]);
----
NULL

eval
NULL = ALL(NULL::INTEGER[]);
----
NULL
4 changes: 0 additions & 4 deletions pkg/sql/sem/tree/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -2102,10 +2102,6 @@ func typeCheckComparisonOpWithSubOperator(
}

rightReturn := rightTyped.ResolvedType()
if cmpTypeLeft.Family() == types.UnknownFamily || rightReturn.Family() == types.UnknownFamily {
return leftTyped, rightTyped, nil, true /* alwaysNull */, nil
}

switch rightReturn.Family() {
case types.ArrayFamily:
cmpTypeRight = rightReturn.ArrayContents()
Expand Down

0 comments on commit eec9590

Please sign in to comment.