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