What is the problem the feature request solves?
Any expression whose output type contains NullType is rejected by the codegen dispatch gate and the whole operator falls back to Spark, even though a NullType column can only ever hold nulls.
Spark's untyped constructors leave NullType children behind, so this hits common literals:
map() → MapType(NullType, NullType)
map('a', NULL) → MapType(StringType, NullType)
array() → ArrayType(NullType)
map_from_arrays(array(), array()), struct(map()), and anything nested around them
CreateMap and friends are routed through codegen dispatch (spark/src/main/scala/org/apache/comet/serde/maps.scala:214), so a projection containing one of these literals is currently kicked out of the native plan with codegen dispatch: unsupported output type ....
Why the gate rejects it today:
isSupportedDataType (spark/src/main/scala/org/apache/comet/codegen/CometBatchKernelCodegen.scala:85) has no NullType case, so it falls through to false, recursively for ArrayType / StructType / MapType children.
canHandle applies that same predicate to both the output type (CometBatchKernelCodegen.scala:120) and every BoundReference input (CometBatchKernelCodegen.scala:172).
The rejection is only needed on the input side: CometScalaUDFCodegen.specFor has no way to build an ArrowColumnSpec for a NullVector, so a NullType input must keep falling back. On the output side the kernel only needs to emit an all-null Arrow NullVector, which the rest of the pipeline already understands (serializeDataType maps NullType to its own type id and Utils.toArrowField maps it to ArrowType.Null).
Describe the potential solution
Make the type gate asymmetric:
- Accept
NullType (top-level or nested inside array / struct / map) for the output type in canHandle, and keep rejecting it for BoundReference inputs.
- Teach the output emitter (
CometBatchKernelCodegenOutput) to map NullType to NullVector and to write it with setNull only, without reading a source value.
- Keep the output emitter's type surface in sync with the gate, as the existing doc comments require, so plan-time acceptance never turns into an execute-time exception.
- Update the Scala/Java UDF user guide:
NullType arguments remain unsupported, NullType return types become supported.
Done when a CometSqlFileTestSuite fixture such as SELECT map(), SELECT map('a', NULL), SELECT array(map()), and a map() column carried through ORDER BY runs without a Spark fallback, and unit tests in CometCodegenSourceSuite lock in the output/input asymmetry and the gate/emitter agreement.
Additional context
No native changes are needed: the NullType support in serde and Arrow field conversion already exists; the missing piece is entirely in the JVM codegen gate and output emitter.
I have a patch for this ready and will open a PR referencing this issue.
Assisted-by: Claude Code (claude-fable-5)
What is the problem the feature request solves?
Any expression whose output type contains
NullTypeis rejected by the codegen dispatch gate and the whole operator falls back to Spark, even though aNullTypecolumn can only ever hold nulls.Spark's untyped constructors leave
NullTypechildren behind, so this hits common literals:map()→MapType(NullType, NullType)map('a', NULL)→MapType(StringType, NullType)array()→ArrayType(NullType)map_from_arrays(array(), array()),struct(map()), and anything nested around themCreateMapand friends are routed through codegen dispatch (spark/src/main/scala/org/apache/comet/serde/maps.scala:214), so a projection containing one of these literals is currently kicked out of the native plan withcodegen dispatch: unsupported output type ....Why the gate rejects it today:
isSupportedDataType(spark/src/main/scala/org/apache/comet/codegen/CometBatchKernelCodegen.scala:85) has noNullTypecase, so it falls through tofalse, recursively forArrayType/StructType/MapTypechildren.canHandleapplies that same predicate to both the output type (CometBatchKernelCodegen.scala:120) and everyBoundReferenceinput (CometBatchKernelCodegen.scala:172).The rejection is only needed on the input side:
CometScalaUDFCodegen.specForhas no way to build anArrowColumnSpecfor aNullVector, so aNullTypeinput must keep falling back. On the output side the kernel only needs to emit an all-null ArrowNullVector, which the rest of the pipeline already understands (serializeDataTypemapsNullTypeto its own type id andUtils.toArrowFieldmaps it toArrowType.Null).Describe the potential solution
Make the type gate asymmetric:
NullType(top-level or nested inside array / struct / map) for the output type incanHandle, and keep rejecting it forBoundReferenceinputs.CometBatchKernelCodegenOutput) to mapNullTypetoNullVectorand to write it withsetNullonly, without reading a source value.NullTypearguments remain unsupported,NullTypereturn types become supported.Done when a
CometSqlFileTestSuitefixture such asSELECT map(),SELECT map('a', NULL),SELECT array(map()), and amap()column carried throughORDER BYruns without a Spark fallback, and unit tests inCometCodegenSourceSuitelock in the output/input asymmetry and the gate/emitter agreement.Additional context
No native changes are needed: the
NullTypesupport in serde and Arrow field conversion already exists; the missing piece is entirely in the JVM codegen gate and output emitter.I have a patch for this ready and will open a PR referencing this issue.
Assisted-by: Claude Code (claude-fable-5)