-
Notifications
You must be signed in to change notification settings - Fork 353
fix: support empty struct types #5414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ce25a30
5a5d4f3
d763c74
2fcee1c
34083c7
6775382
1950465
d168ea6
332d9c2
017266b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,8 +54,9 @@ trait DataTypeSupport { | |
| CalendarIntervalType => | ||
| true | ||
| case StructType(fields) => | ||
| fields.nonEmpty && fields.forall(f => | ||
| isTypeSupported(f.dataType, f.name, fallbackReasons)) | ||
| // A struct's `fields` can be empty -- e.g. Iceberg's `_partition` metadata column is | ||
| // exactly that on an unpartitioned table. It's still a value Comet can represent. | ||
| fields.forall(f => isTypeSupported(f.dataType, f.name, fallbackReasons)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Guard empty structs in FIRST_VALUE/LAST_VALUE windows This also admits empty-struct inputs to native windows that cannot handle them. Using the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. Also opened the actual fix upstream: apache/datafusion#24582.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Apply the empty-struct guard to collect_set too Could we also reject empty-struct-containing inputs in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Keep empty-struct grouping keys off native aggregation With
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. Rejects grouping keys containing an empty struct in both CometBaseAggregate.doConvert and the mirrored canAggregateBeConverted tag check (per its own WARNING comment). Added regression tests for GROUP BY and DISTINCT.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Guard nested-empty values in native scalar-ordering paths With native local-table scan/shuffle enabled, let
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed both. Same root cause, two spots: ScalarValue::partial_cmp_struct flattens a struct to its leaf columns to compare, so a zero-field struct loses its own validity bit and a NULL element ties with a non-null {} element. Guarded array_max/array_min on element type, and RANGE frame ordering on the ORDER BY key type (the existing offset checks only covered explicit-offset bounds UNBOUNDED/CURRENT ROW skipped them entirely). Also checked sort_array and plain ORDER BY both use arrow's row-format comparator instead of ScalarValue::partial_cmp, which encodes struct-level validity independent of field count, so they're not affected by this one |
||
| case ArrayType(elementType, _) => | ||
| isTypeSupported(elementType, ARRAY_ELEMENT, fallbackReasons) | ||
| case MapType(keyType, valueType, _) => | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -539,7 +539,9 @@ object QueryPlanSerde extends Logging with CometExprShim with CometTypeShim { | |
| case dt if isTimeType(dt) => | ||
| true | ||
| case s: StructType if allowComplex => | ||
| s.fields.nonEmpty && s.fields.map(_.dataType).forall(supportedDataType(_, allowComplex)) | ||
| // A struct's `fields` can be empty -- e.g. Iceberg's `_partition` metadata column is | ||
| // exactly that on an unpartitioned table. It's still a value Comet can represent. | ||
| s.fields.map(_.dataType).forall(supportedDataType(_, allowComplex)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Preserve fallback for nested-array empty-struct literals Could we keep literal fallback until these element types can be serialized? Over a Parquet-backed table,
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Guard typed NULL array defaults in LEAD/LAG This also admits
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. Declines LAG/LEAD when the default expression's own type carries an empty struct - keyed on that, not the input type, so omitted/plain-NULL defaults (which don't hit the cast) stay native. Added regression tests for both the failing and the still-native forms. |
||
| case a: ArrayType if allowComplex => | ||
| supportedDataType(a.elementType, allowComplex) | ||
| case m: MapType if allowComplex => | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -250,7 +250,9 @@ object CometJsonToStructs extends CometCodegenDispatch[JsonToStructs] with Nativ | |
|
|
||
| private def isSupportedSchema(dt: DataType): Boolean = dt match { | ||
| case StructType(fields) => | ||
| fields.nonEmpty && fields.forall(f => isSupportedSchema(f.dataType)) | ||
| // A struct's `fields` can be empty -- e.g. `from_json(col, 'struct<>')`'s target schema. | ||
| // With no fields to check, this holds vacuously. | ||
| fields.forall(f => isSupportedSchema(f.dataType)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Keep nested empty structs off the native path until they are constructed safely Because this predicate is recursive, it now admits
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the quick, thorough review @sunchao . Good catch, the recursive Scala predicate let |
||
| case DataTypes.IntegerType | DataTypes.LongType | DataTypes.FloatType | DataTypes.DoubleType | | ||
| DataTypes.BooleanType | DataTypes.StringType => | ||
| true | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Preserve Spark NULLs for blank JSON with an empty struct schema
With
spark.comet.expression.JsonToStructs.allowIncompatible=true, this branch now makesfrom_json(col, 'struct<>')execute natively whencolis''or whitespace. The native parser marks every parse error as a valid struct, so the exact-head expression produces a non-nullRow()andfrom_json(col, 'struct<>') IS NULLis false; Spark 3.5 and 4.0 intentionally return NULL for blank inputs (SPARK-19543). Before this change the empty schema took the Spark/codegen path, so this is a newly introduced wrong-result case. The added test uses only{}; please preserve the NULL validity bit for blank input and add empty/whitespace regression rows.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blank/whitespace input now short-circuits to NULL before parsing, matching SPARK-19543, separate from non-blank malformed input (still PERMISSIVE null-fields). New regression rows cover blank, whitespace, non-blank-malformed, and SQL NULL, checked against real Spark output.