SIGN IN SIGN UP

Make the result nullability of `arrayIntersect` follow its declared return type

`getReturnTypeImpl` declares `Array(getMostSubtype(nested_types, true))`, which is
`Nullable` only when every argument is. `executeImpl` separately computes the cast
target as `getMostSubtype(data_types, true, force_support_conversion = true)`, and
that third flag makes the common type `Nullable` as soon as one argument is.
`castColumns` preserves each argument's own nullability only for the types in a
hand-written list (`isNumber`, `isDate`, `isDateTime`, `isDateTime64`,
`isStringOrFixedString`); every other nested type is cast to that common type, so
all arguments become `Nullable`. `execute` then derived `all_nullable` from those
cast arguments and used it for the `ColumnNullable` wrap, the `use_null_map` flag
and the `NULL`-emission gate. For an intersection with some-but-not-all `Nullable`
arguments that produced a `Nullable` column against a not `Nullable` declaration,
and emitted a `NULL` that an intersection with a not `Nullable` argument cannot
contain.

Record the declared nullability once on `UnpackedArrays` and use it at those sites
instead. No cast is changed, no type list extended and no type check loosened, so
this covers every affected nested type at once rather than only the one that was
reported.

There are two symptoms, because `castRemoveNullable` discards the null map when it
strips a wrap. `Date32` has its own dispatch branch and never reaches that stripper,
so the mismatch surfaces as an `Unexpected return type from arrayIntersect`
exception, thrown from any of the six `columnMatchesType` guards
(`Analyzer/Utils.cpp`, `resolveFunction.cpp`, `ColumnFunction.cpp`, `ActionsDAG.cpp`
twice, `ExpressionActions.cpp`). `UUID`, `IPv4`, `IPv6`, `Time`, `Time64`,
`Interval`, `JSON` and `QBit` take the generic branch, where the stripper hides the
mismatch and the emitted `NULL` silently becomes a default-valued element; for
`Enum8` and `Enum16` that element is not a valid enum value, so it throws
`Unexpected value 0 in enum` instead.

`arrayUnion` and `arraySymmetricDifference` declare `getLeastSupertype`, which is
`Nullable` exactly when the cast makes all arguments `Nullable`, so their declared
nullability and the old derivation always agreed. They are unaffected, and measured
unchanged across the whole type matrix.

Introduced by 3ba2d114d5744384, which added the `isDate32` dispatch branch without
adding `isDate32` to the `castColumns` list.

Found by `FunctionsStress` in `Unit tests (tsan, function_prop_fuzzer)`. Validated
in both directions from a deterministic single-query repro and over 23 nested types
in both argument positions, with and without an actual `NULL`: exactly the 14
defective probes change, across 11 nested types, and every control is byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
G
Groene AI committed
c5e46b7aacffc0ed3f36d88e58453b689c3069d8
Parent: ce6e031