Skip to content

feat: Add support for explode_outer function for arrays#22100

Open
athlcode wants to merge 27 commits into
apache:mainfrom
athlcode:feat/explode_outer
Open

feat: Add support for explode_outer function for arrays#22100
athlcode wants to merge 27 commits into
apache:mainfrom
athlcode:feat/explode_outer

Conversation

@athlcode

@athlcode athlcode commented May 10, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

DataFusion's 'unnest' had no way to express Spark 'explode_outer' semantics, empty input lists were silently dropped, even with 'preserve_nulls' = true.

What changes are included in this PR?

Adds a third unnest behavior that produces a NULL row for empty input lists, by replacing UnnestOptions.preserve_nulls: bool with a NullHandling { Drop, Preserve, PreserveAndExpandEmpty } enum. The with_preserve_nulls(bool) builder is kept as a backward-compat shim.

Are these changes tested?

Yes, new unit test for the empty-list case, extended longest-length and DataFrame unnest_column_nulls tests, and existing proto round-trip coverage.

Are there any user-facing changes?

The preserve_nulls field on UnnestOptions is renamed to null_handling. The with_preserve_nulls(bool) builder is preserved, so most callers are unaffected. Add the api change label for the field rename.

@github-actions github-actions Bot added sql SQL Planner core Core DataFusion crate common Related to common crate proto Related to proto crate physical-plan Changes to the physical-plan crate labels May 10, 2026
@github-actions

github-actions Bot commented May 10, 2026

Copy link
Copy Markdown

Thank you for opening this pull request!

Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch).

Details
     Cloning apache/main
    Building datafusion v54.1.0 (current)
       Built [ 106.290s] (current)
     Parsing datafusion v54.1.0 (current)
      Parsed [   0.034s] (current)
    Building datafusion v54.1.0 (baseline)
       Built [ 103.517s] (baseline)
     Parsing datafusion v54.1.0 (baseline)
      Parsed [   0.034s] (baseline)
    Checking datafusion v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   0.635s] 223 checks: 223 pass, 30 skip
     Summary no semver update required
    Finished [ 212.166s] datafusion
    Building datafusion-common v54.1.0 (current)
       Built [  33.679s] (current)
     Parsing datafusion-common v54.1.0 (current)
      Parsed [   0.059s] (current)
    Building datafusion-common v54.1.0 (baseline)
       Built [  33.751s] (baseline)
     Parsing datafusion-common v54.1.0 (baseline)
      Parsed [   0.062s] (baseline)
    Checking datafusion-common v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   0.581s] 223 checks: 221 pass, 2 fail, 0 warn, 30 skip

--- failure constructible_struct_adds_field: externally-constructible struct adds field ---

Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.49.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field UnnestOptions.null_handling in /home/runner/work/datafusion/datafusion/datafusion/common/src/unnest.rs:104

--- failure struct_pub_field_missing: pub struct's pub field removed or renamed ---

Description:
A publicly-visible struct has at least one public field that is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.49.0/src/lints/struct_pub_field_missing.ron

Failed in:
  field preserve_nulls of struct UnnestOptions, previously in file /home/runner/work/datafusion/datafusion/target/semver-checks/git-apache_main/69c5de70548ef1bd1da367af2b4867d13b8cd977/datafusion/common/src/unnest.rs:72

     Summary semver requires new major version: 2 major and 0 minor checks failed
    Finished [  69.519s] datafusion-common
    Building datafusion-expr v54.1.0 (current)
       Built [  26.722s] (current)
     Parsing datafusion-expr v54.1.0 (current)
      Parsed [   0.076s] (current)
    Building datafusion-expr v54.1.0 (baseline)
       Built [  26.944s] (baseline)
     Parsing datafusion-expr v54.1.0 (baseline)
      Parsed [   0.078s] (baseline)
    Checking datafusion-expr v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   1.282s] 223 checks: 222 pass, 1 fail, 0 warn, 30 skip

--- failure constructible_struct_adds_field: externally-constructible struct adds field ---

Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.49.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field Unnest.outer in /home/runner/work/datafusion/datafusion/datafusion/expr/src/expr.rs:684

     Summary semver requires new major version: 1 major and 0 minor checks failed
    Finished [  56.010s] datafusion-expr
    Building datafusion-physical-plan v54.1.0 (current)
       Built [  38.118s] (current)
     Parsing datafusion-physical-plan v54.1.0 (current)
      Parsed [   0.145s] (current)
    Building datafusion-physical-plan v54.1.0 (baseline)
       Built [  39.129s] (baseline)
     Parsing datafusion-physical-plan v54.1.0 (baseline)
      Parsed [   0.144s] (baseline)
    Checking datafusion-physical-plan v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   0.611s] 223 checks: 223 pass, 30 skip
     Summary no semver update required
    Finished [  79.277s] datafusion-physical-plan
    Building datafusion-proto v54.1.0 (current)
error: running cargo-doc on crate 'datafusion-proto' failed with output:
-----
   Compiling proc-macro2 v1.0.107
   Compiling unicode-ident v1.0.24
   Compiling quote v1.0.47
   Compiling libc v0.2.189
    Checking cfg-if v1.0.4
    Checking memchr v2.8.3
   Compiling libm v0.2.16
   Compiling autocfg v1.5.1
   Compiling syn v2.0.119
   Compiling num-traits v0.2.19
    Checking bytes v1.12.1
   Compiling zerocopy v0.8.55
   Compiling serde_core v1.0.229
    Checking once_cell v1.21.4
    Checking equivalent v1.0.2
    Checking itoa v1.0.18
    Checking allocator-api2 v0.2.21
   Compiling getrandom v0.3.4
    Checking foldhash v0.2.0
    Checking hashbrown v0.17.1
   Compiling zmij v1.0.23
   Compiling serde_json v1.0.151
    Checking indexmap v2.14.0
    Checking num-integer v0.1.46
    Checking iana-time-zone v0.1.65
    Checking siphasher v1.0.3
   Compiling version_check v0.9.5
   Compiling ahash v0.8.12
    Checking phf_shared v0.12.1
    Checking chrono v0.4.45
   Compiling jobserver v0.1.35
    Checking num-bigint v0.4.8
   Compiling chrono-tz v0.10.4
   Compiling synstructure v0.13.2
   Compiling shlex v2.0.1
   Compiling find-msvc-tools v0.1.9
    Checking stable_deref_trait v1.2.1
   Compiling cc v1.4.0
    Checking arrow-schema v59.1.0
    Checking phf v0.12.1
   Compiling pkg-config v0.3.33
    Checking num-complex v0.4.6
    Checking pin-project-lite v0.2.17
   Compiling syn v3.0.3
   Compiling zerocopy-derive v0.8.55
   Compiling zerofrom-derive v0.1.7
    Checking zerofrom v0.1.8
   Compiling yoke-derive v0.8.2
    Checking yoke v0.8.3
   Compiling zerovec-derive v0.11.3
    Checking zerovec v0.11.6
   Compiling displaydoc v0.2.6
   Compiling zstd-sys v2.0.16+zstd.1.5.7
    Checking lexical-util v1.0.7
    Checking futures-core v0.3.33
    Checking tinystr v0.8.3
    Checking writeable v0.6.3
    Checking futures-sink v0.3.33
    Checking litemap v0.8.2
    Checking smallvec v1.15.2
    Checking zerotrie v0.2.4
    Checking icu_locale_core v2.2.0
    Checking potential_utf v0.1.5
   Compiling icu_normalizer_data v2.2.0
    Checking utf8_iter v1.0.4
   Compiling icu_properties_data v2.2.0
    Checking half v2.7.1
    Checking icu_collections v2.2.0
    Checking arrow-buffer v59.1.0
    Checking icu_provider v2.2.0
   Compiling semver v1.0.28
   Compiling zstd-safe v7.2.4
   Compiling rustc_version v0.4.1
    Checking lexical-write-integer v1.0.6
    Checking arrow-data v59.1.0
    Checking lexical-parse-integer v1.0.6
    Checking futures-channel v0.3.33
   Compiling futures-macro v0.3.33
   Compiling tokio-macros v2.7.1
    Checking arrow-array v59.1.0
    Checking bitflags v2.13.1
   Compiling parking_lot_core v0.9.12
    Checking slab v0.4.12
    Checking base64 v0.22.1
    Checking futures-task v0.3.33
    Checking futures-io v0.3.33
    Checking futures-util v0.3.33
    Checking tokio v1.53.1
    Checking arrow-select v59.1.0
    Checking lexical-write-float v1.0.6
    Checking lexical-parse-float v1.0.6
   Compiling flatbuffers v25.12.19
    Checking icu_properties v2.2.0
    Checking icu_normalizer v2.2.0
    Checking aho-corasick v1.1.4
    Checking scopeguard v1.2.0
    Checking unicode-segmentation v1.13.3
   Compiling crc32fast v1.5.0
    Checking ryu v1.0.23
   Compiling getrandom v0.4.3
    Checking regex-syntax v0.8.11
    Checking unicode-width v0.2.2
    Checking idna_adapter v1.2.2
    Checking comfy-table v7.2.2
    Checking lock_api v0.4.14
    Checking lexical-core v1.0.6
    Checking arrow-ord v59.1.0
    Checking atoi v2.0.0
   Compiling snap v1.1.2
    Checking percent-encoding v2.3.2
    Checking adler2 v2.0.1
    Checking alloc-no-stdlib v2.0.4
    Checking either v1.17.0
   Compiling thiserror v2.0.19
    Checking twox-hash v2.1.3
    Checking simd-adler32 v0.3.10
    Checking lz4_flex v0.13.1
    Checking miniz_oxide v0.8.9
    Checking regex-automata v0.4.16
    Checking alloc-stdlib v0.2.4
    Checking arrow-cast v59.1.0
    Checking form_urlencoded v1.2.2
    Checking idna v1.1.0
    Checking futures-executor v0.3.33
   Compiling thiserror-impl v2.0.19
   Compiling tracing-attributes v0.1.31
    Checking regex v1.13.1
    Checking tracing-core v0.1.36
    Checking csv-core v0.1.13
   Compiling paste v1.0.15
    Checking simdutf8 v0.1.5
    Checking same-file v1.0.6
    Checking zlib-rs v0.6.6
    Checking walkdir v2.5.0
    Checking tracing v0.1.44
    Checking csv v1.4.0
    Checking futures v0.3.33
    Checking url v2.5.8
    Checking brotli-decompressor v5.0.3
    Checking itertools v0.14.0
    Checking flate2 v1.1.9
    Checking parking_lot v0.12.5
   Compiling async-trait v0.1.91
    Checking http v1.4.2
   Compiling anyhow v1.0.104
   Compiling serde v1.0.229
    Checking humantime v2.4.0
    Checking object_store v0.13.2
    Checking brotli v8.0.4
    Checking arrow-csv v59.1.0
    Checking arrow-json v59.1.0
    Checking arrow-string v59.1.0
    Checking zstd v0.13.3
    Checking uuid v1.24.0
    Checking arrow-ipc v59.1.0
    Checking arrow-arith v59.1.0
    Checking arrow-row v59.1.0
   Compiling serde_derive v1.0.229
    Checking log v0.4.33
   Compiling seq-macro v0.3.6
    Checking arrow v59.1.0
    Checking parquet v59.1.0
    Checking itertools v0.15.0
   Compiling prost-derive v0.14.4
   Compiling pin-project-internal v1.1.13
    Checking pin-project v1.1.13
    Checking ppv-lite86 v0.2.21
    Checking rand_core v0.9.5
    Checking prost v0.14.4
   Compiling rustix v1.1.4
    Checking pbjson v0.9.0
   Compiling crossbeam-utils v0.8.22
    Checking rand_chacha v0.9.0
    Checking datafusion-doc v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/doc)
    Checking linux-raw-sys v0.12.1
    Checking rand v0.9.5
    Checking tokio-util v0.7.19
    Checking hashbrown v0.14.5
    Checking fastrand v2.5.0
    Checking foldhash v0.1.5
    Checking hashbrown v0.15.5
    Checking dashmap v6.2.1
    Checking fixedbitset v0.5.7
    Checking petgraph v0.8.3
   Compiling datafusion-macros v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/macros)
    Checking tempfile v3.27.0
    Checking datafusion-common v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/common)
    Checking hex v0.4.3
    Checking datafusion-common-runtime v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/common-runtime)
    Checking glob v0.3.4
   Compiling liblzma-sys v0.4.7
   Compiling heck v0.5.0
    Checking crc-catalog v2.5.0
    Checking libbz2-rs-sys v0.2.5
    Checking bzip2 v0.6.1
    Checking crc v3.4.0
   Compiling strum_macros v0.28.0
    Checking tokio-stream v0.1.19
    Checking datafusion-proto-common v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/proto-common)
    Checking datafusion-expr-common v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/expr-common)
    Checking datafusion-proto-models v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/proto-models)
    Checking liblzma v0.4.7
    Checking arrow-avro v59.1.0
    Checking datafusion-physical-expr-common v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/physical-expr-common)
    Checking datafusion-functions-aggregate-common v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/functions-aggregate-common)
    Checking datafusion-functions-window-common v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/functions-window-common)
    Checking datafusion-expr v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/expr)
    Checking datafusion-execution v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/execution)
    Checking datafusion-physical-expr v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/physical-expr)
    Checking datafusion-functions v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/functions)
    Checking datafusion-physical-plan v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/physical-plan)
    Checking datafusion-physical-expr-adapter v54.1.0 (/home/runner/work/datafusion/datafusion/datafusion/physical-expr-adapter)
error[E0560]: struct `datafusion_proto_models::protobuf::UnnestOptions` has no field named `preserve_nulls`
   --> /home/runner/work/datafusion/datafusion/datafusion/physical-plan/src/unnest.rs:310:13
    |
310 |             preserve_nulls: self.options().preserve_nulls,
    |             ^^^^^^^^^^^^^^ `datafusion_proto_models::protobuf::UnnestOptions` does not have this field
    |
    = note: available fields are: `null_handling`

error[E0615]: attempted to take value of method `preserve_nulls` on type `&datafusion_common::UnnestOptions`
   --> /home/runner/work/datafusion/datafusion/datafusion/physical-plan/src/unnest.rs:310:44
    |
310 |             preserve_nulls: self.options().preserve_nulls,
    |                                            ^^^^^^^^^^^^^^ method, not a field
    |
help: use parentheses to call the method
    |
310 |             preserve_nulls: self.options().preserve_nulls(),
    |                                                          ++

error[E0560]: struct `datafusion_common::UnnestOptions` has no field named `preserve_nulls`
   --> /home/runner/work/datafusion/datafusion/datafusion/physical-plan/src/unnest.rs:387:13
    |
387 |             preserve_nulls: options.preserve_nulls,
    |             ^^^^^^^^^^^^^^ `datafusion_common::UnnestOptions` does not have this field
    |
    = note: available fields are: `null_handling`

error[E0609]: no field `preserve_nulls` on type `&datafusion_proto_models::protobuf::UnnestOptions`
   --> /home/runner/work/datafusion/datafusion/datafusion/physical-plan/src/unnest.rs:387:37
    |
387 |             preserve_nulls: options.preserve_nulls,
    |                                     ^^^^^^^^^^^^^^ unknown field
    |
    = note: available fields are: `null_handling`, `recursions`

Some errors have detailed explanations: E0560, E0609, E0615.
For more information about an error, try `rustc --explain E0560`.
error: could not compile `datafusion-physical-plan` (lib) due to 4 previous errors

-----

error: failed to build rustdoc for crate datafusion-proto v54.1.0
note: this is usually due to a compilation error in the crate,
      and is unlikely to be a bug in cargo-semver-checks
note: the following command can be used to reproduce the error:
      cargo new --lib example &&
          cd example &&
          echo '[workspace]' >> Cargo.toml &&
          cargo add --path /home/runner/work/datafusion/datafusion/datafusion/proto --features avro,datafusion-datasource-avro,datafusion-datasource-parquet,default,json,parquet,serde_json &&
          cargo check &&
          cargo doc

    Building datafusion-proto-models v54.1.0 (current)
       Built [  25.422s] (current)
     Parsing datafusion-proto-models v54.1.0 (current)
      Parsed [   0.130s] (current)
    Building datafusion-proto-models v54.1.0 (baseline)
       Built [  25.598s] (baseline)
     Parsing datafusion-proto-models v54.1.0 (baseline)
      Parsed [   0.132s] (baseline)
    Checking datafusion-proto-models v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   1.710s] 223 checks: 221 pass, 2 fail, 0 warn, 30 skip

--- failure constructible_struct_adds_field: externally-constructible struct adds field ---

Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.49.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field UnnestOptions.null_handling in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:674
  field UnnestOptions.null_handling in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:674
  field Unnest.outer in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:1015
  field Unnest.outer in /home/runner/work/datafusion/datafusion/datafusion/proto-models/src/generated/prost.rs:1015

--- failure struct_pub_field_missing: pub struct's pub field removed or renamed ---

Description:
A publicly-visible struct has at least one public field that is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.49.0/src/lints/struct_pub_field_missing.ron

Failed in:
  field preserve_nulls of struct UnnestOptions, previously in file /home/runner/work/datafusion/datafusion/target/semver-checks/git-apache_main/69c5de70548ef1bd1da367af2b4867d13b8cd977/datafusion/proto-models/src/generated/prost.rs:674
  field preserve_nulls of struct UnnestOptions, previously in file /home/runner/work/datafusion/datafusion/target/semver-checks/git-apache_main/69c5de70548ef1bd1da367af2b4867d13b8cd977/datafusion/proto-models/src/generated/prost.rs:674

     Summary semver requires new major version: 2 major and 0 minor checks failed
    Finished [  54.019s] datafusion-proto-models
    Building datafusion-sql v54.1.0 (current)
       Built [  42.380s] (current)
     Parsing datafusion-sql v54.1.0 (current)
      Parsed [   0.031s] (current)
    Building datafusion-sql v54.1.0 (baseline)
       Built [  41.348s] (baseline)
     Parsing datafusion-sql v54.1.0 (baseline)
      Parsed [   0.032s] (baseline)
    Checking datafusion-sql v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   0.221s] 223 checks: 223 pass, 30 skip
     Summary no semver update required
    Finished [  85.014s] datafusion-sql
    Building datafusion-sqllogictest v54.1.0 (current)
       Built [ 180.506s] (current)
     Parsing datafusion-sqllogictest v54.1.0 (current)
      Parsed [   0.021s] (current)
    Building datafusion-sqllogictest v54.1.0 (baseline)
       Built [ 182.238s] (baseline)
     Parsing datafusion-sqllogictest v54.1.0 (baseline)
      Parsed [   0.024s] (baseline)
    Checking datafusion-sqllogictest v54.1.0 -> v54.1.0 (no change; assume patch)
     Checked [   0.093s] 223 checks: 223 pass, 30 skip
     Summary no semver update required
    Finished [ 365.855s] datafusion-sqllogictest
error: aborting due to failure to build rustdoc for crate datafusion-proto v54.1.0

@github-actions github-actions Bot added the auto detected api change Auto detected API change label May 10, 2026

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @athlcode explode_outer is not like other builtin function so it has to be in the core.

Please add tests, more tests the better, including mix with nulls, nested combinations with explode and explode_outer and supported datatypes

@athlcode

Copy link
Copy Markdown
Contributor Author

Thanks @athlcode explode_outer is not like other builtin function so it has to be in the core.

Please add tests, more tests the better, including mix with nulls, nested combinations with explode and explode_outer and supported datatypes

thank you, added more test cases

@comphead

Copy link
Copy Markdown
Contributor

Thanks @athlcode my bad, I just realized I haven't clarified tests needs to be added to slt file, lets remove rust tests unless they are not reproducible with slt. Ideally to create a separate .slt file for explode_outer to localize tests

@athlcode

athlcode commented May 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @athlcode my bad, I just realized I haven't clarified tests needs to be added to slt file, lets remove rust tests unless they are not reproducible with slt. Ideally to create a separate .slt file for explode_outer to localize tests

Thanks @comphead for the clarification. None of the test cases are currently reproducible via SLT because there's no SQL surface for NullHandling::PreserveAndExpandEmpty. To enable SLT coverage we need to add SQL recognition of explode_outer(col) (and probably explode(col) for symmetry) and map them to UNNEST with the right NullHandling variant.

@comphead

Copy link
Copy Markdown
Contributor

we would still need to have .slt tests in datafusion-spark crate to test explode_outer works properly with SQL dialect

@github-actions github-actions Bot added logical-expr Logical plan and expressions sqllogictest SQL Logic Tests (.slt) labels May 18, 2026
@athlcode

Copy link
Copy Markdown
Contributor Author

we would still need to have .slt tests in datafusion-spark crate to test explode_outer works properly with SQL dialect

Added slt tests at datafusion/sqllogictest/test_files/spark/generator/explode_outer.slt, exercises explode_outer via the Spark SQL dialect path

@kazuyukitanimura

Copy link
Copy Markdown
Contributor

Thanks @athlcode would you mind resolving the conflicts?

@comphead any further comments?

@athlcode

Copy link
Copy Markdown
Contributor Author

Thanks @athlcode would you mind resolving the conflicts?

@comphead any further comments?

resolved the conflicts, thank you

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@athlcode I'm planning to run the tests in Spark in see if there is any differences

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label May 30, 2026
pub format: FormatOptions,
/// Spark-compatibility options (functions under `datafusion/spark` and
/// Spark-specific planner behavior)
pub spark: SparkOptions,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not be adding spark into the core, it would inverse the dependencies

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are fine, thanks @athlcode
Please help me to understand why we need to keep anything spark related in the core?

@comphead comphead changed the title feat: Add support for Spark-compatible explode_outer function feat: Add support for Spark-compatible explode_outer function for arrays Jun 3, 2026
@comphead

comphead commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

we might also update the SELECT doc like in #22672

@github-actions github-actions Bot removed the documentation Improvements or additions to documentation label Jun 28, 2026
@athlcode

Copy link
Copy Markdown
Contributor Author

Tests are fine, thanks @athlcode Please help me to understand why we need to keep anything spark related in the core?

The mechanism has to live in core, but it isn't Spark-specific. NullHandling::PreserveAndExpandEmpty is just a new option on UnnestOptions in datafusion-common, where UnnestOptions already lives, and UnnestExec in datafusion-physical-plan is the only place the per-row logic can run. Expr::Unnest.outer is a new field on a core Expr variant, and downstream crates can't add fields to a pub enum variant in Rust, so the flag has to live where the variant is defined.

Neither references Spark by name. They're the generic mechanism for "unnest that preserves empty arrays as NULL rows," which Hive EXPLODE OUTER, Snowflake FLATTEN(OUTER => true), and BigQuery UNNEST ... WITH OFFSET all need too.

The Spark-flavored surface (the explode / explode_outer SQL aliases and the allow_multiple_generators config) is the only part that's truly dialect-specific. Happy to move those into datafusion-spark as a planner extension and a custom config registered via Extensions if you'd prefer the core diff to be purely the mechanism.

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jun 28, 2026
@comphead

comphead commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Tests are fine, thanks @athlcode Please help me to understand why we need to keep anything spark related in the core?

The mechanism has to live in core, but it isn't Spark-specific. NullHandling::PreserveAndExpandEmpty is just a new option on UnnestOptions in datafusion-common, where UnnestOptions already lives, and UnnestExec in datafusion-physical-plan is the only place the per-row logic can run. Expr::Unnest.outer is a new field on a core Expr variant, and downstream crates can't add fields to a pub enum variant in Rust, so the flag has to live where the variant is defined.

Neither references Spark by name. They're the generic mechanism for "unnest that preserves empty arrays as NULL rows," which Hive EXPLODE OUTER, Snowflake FLATTEN(OUTER => true), and BigQuery UNNEST ... WITH OFFSET all need too.

The Spark-flavored surface (the explode / explode_outer SQL aliases and the allow_multiple_generators config) is the only part that's truly dialect-specific. Happy to move those into datafusion-spark as a planner extension and a custom config registered via Extensions if you'd prefer the core diff to be purely the mechanism.

Thanks @athlcode sorry I really missed this comments, it makes sense to me, lets remove Spark from comments in the core, as this would be confusing, and other than that, the PR would be good to go

@athlcode

Copy link
Copy Markdown
Contributor Author

@comphead thanks, I have removed spark from the comments,

@comphead comphead changed the title feat: Add support for Spark-compatible explode_outer function for arrays feat: Add support for explode_outer function for arrays Jul 22, 2026
@comphead

Copy link
Copy Markdown
Contributor

Thanks @athlcode.
Since the code in the core it would actually benefit the DF to have outer unnest. For example unnest can be represented as a built in function or via joins #13824

I checked some of engines for unnest_outer as a built in function and the same functionality represented mostly via joins. However if we support unnest it would prob makes sense to support unnest_outer as a builtin function.

Lets move tests from explode.slt into unnest.slt referring it as unnest_outer

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.15931% with 119 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.75%. Comparing base (f7aef23) to head (d01c70f).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/proto-models/src/generated/pbjson.rs 14.28% 58 Missing and 2 partials ⚠️
datafusion/expr/src/expr.rs 25.00% 21 Missing ⚠️
datafusion/physical-plan/src/unnest.rs 93.73% 18 Missing and 3 partials ⚠️
datafusion/proto-models/src/generated/prost.rs 0.00% 13 Missing ⚠️
datafusion/proto/src/logical_plan/from_proto.rs 80.00% 2 Missing ⚠️
datafusion/proto/src/logical_plan/to_proto.rs 85.71% 1 Missing ⚠️
datafusion/sql/src/select.rs 96.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #22100      +/-   ##
==========================================
- Coverage   80.76%   80.75%   -0.01%     
==========================================
  Files        1089     1089              
  Lines      368797   369268     +471     
  Branches   368797   369268     +471     
==========================================
+ Hits       297850   298210     +360     
- Misses      53201    53309     +108     
- Partials    17746    17749       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@athlcode

Copy link
Copy Markdown
Contributor Author

Thanks @athlcode. Since the code in the core it would actually benefit the DF to have outer unnest. For example unnest can be represented as a built in function or via joins #13824

I checked some of engines for unnest_outer as a built in function and the same functionality represented mostly via joins. However if we support unnest it would prob makes sense to support unnest_outer as a builtin function.

Lets move tests from explode.slt into unnest.slt referring it as unnest_outer

thanks @comphead. moved tests into unnest.slt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto detected api change Auto detected API change common Related to common crate core Core DataFusion crate documentation Improvements or additions to documentation logical-expr Logical plan and expressions physical-plan Changes to the physical-plan crate proto Related to proto crate sql SQL Planner sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Spark-compatible explode_outer function for arrays

4 participants