Skip to content

fix: array_concat panics or fails on non-default nested lists - #25970

Merged
adriangb merged 3 commits into
apache:mainfrom
1fanwang:1fanwang/fix-nested-array-concat
Oct 23, 2026
Merged

adriangb merged 3 commits into
apache:mainfrom
1fanwang:1fanwang/fix-nested-array-concat

Conversation

@1fanwang

@1fanwang 1fanwang commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

array_concat on nested lists crashes or fails in cases that work with default inner fields. Concatenating a list whose inner lists have non-nullable elements with a one-dimensional array panics the process with "Arrays with inconsistent types passed to MutableArrayData". Nested lists whose inner list is a LargeList or has a non-default field name, and all-NULL arguments of different nesting depths, fail with an internal error saying array_concat returned a different type than the one promised at planning time.

Planning built the result type from the leaf element type with default nullable inner fields, while execution kept the inner fields of the inputs, so the planned and executed types disagreed, and a non-nullable inner field met a nullable one in the same array builder.

What changes are included in this PR?

array_concat now plans its result from the argument types aligned to the deepest argument and unified with the same type union as before, so inner LargeList lists, field names and nullability carry through. Each argument is coerced to that type or to the matching lower-dimensional part of it, and execution builds the output, including an all-NULL result, with the planned type. Concatenating one-dimensional arrays is unchanged.

What is the testing strategy for this PR?

New cases in array/array_concat.slt cover each failure from the issue: a nested LargeList, an inner list with a non-default field name, all-NULL arguments of different depths, and non-nullable inner elements concatenated with a one-dimensional array. Another case pins the one-dimensional result type for inputs that share a named, non-nullable field.

Testing Done

The issue's queries in datafusion-cli, built with cargo build --profile ci -p datafusion-cli on main (416002a) and on this branch. The cargo registry path in the panic message is shortened.

target/ci/datafusion-cli -f repro.sql; echo "exit=$?"

On main, the first three queries fail with internal type errors and the fourth panics:

DataFusion CLI v55.1.0
0 row(s) fetched. 
Elapsed 0.025 seconds.

Internal error: Assertion failed: result_data_type == *expected_type: Function 'array_concat' returned value of type 'List(LargeList(Int64))' while the following type was promised at planning time and expected: 'List(List(Int64))'.
This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
Internal error: Assertion failed: result_data_type == *expected_type: Function 'array_concat' returned value of type 'List(List(Int64, field: 'element'))' while the following type was promised at planning time and expected: 'List(List(Int64))'.
This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
Internal error: Assertion failed: result_data_type == *expected_type: Function 'array_concat' returned value of type 'List(Int64)' while the following type was promised at planning time and expected: 'List(List(Int64))'.
This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues

thread 'main' (35983318) panicked at <cargo-registry>/arrow-data-60.0.0/src/transform/mod.rs:464:13:
assertion `left == right` failed: Arrays with inconsistent types passed to MutableArrayData
  left: List(Field { data_type: Int64 })
 right: List(Field { data_type: Int64, nullable: true })
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

On this branch, all four queries return results:

DataFusion CLI v55.1.0
0 row(s) fetched. 
Elapsed 0.017 seconds.

+-----------------------+
| array_concat(t.a,t.a) |
+-----------------------+
| [[1, 2], [1, 2]]      |
+-----------------------+
1 row(s) fetched. 
Elapsed 0.002 seconds.

+-----------------------+
| array_concat(t.b,t.b) |
+-----------------------+
| [[1], [1]]            |
+-----------------------+
1 row(s) fetched. 
Elapsed 0.001 seconds.

+-----------------------------------------------------------------------------------------------+
| array_concat(arrow_cast(NULL,Utf8("List(Int64)")),arrow_cast(NULL,Utf8("List(List(Int64))"))) |
+-----------------------------------------------------------------------------------------------+
| NULL                                                                                          |
+-----------------------------------------------------------------------------------------------+
1 row(s) fetched. 
Elapsed 0.001 seconds.

+----------------------------------------+
| array_concat(t.c,make_array(Int64(2))) |
+----------------------------------------+
| [[1], [2]]                             |
+----------------------------------------+
1 row(s) fetched. 
Elapsed 0.001 seconds.

exit=0

One-dimensional results keep the types they have on main, including inputs that share a named or non-nullable field:

target/ci/datafusion-cli -q -f one-dimensional.sql

On main:

+-------------+
| one_input   |
+-------------+
| List(Int64) |
+-------------+
+-------------+
| two_inputs  |
+-------------+
| List(Int64) |
+-------------+
+------------------+
| large_and_list   |
+------------------+
| LargeList(Int64) |
+------------------+
+----------------+
| fixed_and_list |
+----------------+
| List(Int64)    |
+----------------+

On this branch:

+-------------+
| one_input   |
+-------------+
| List(Int64) |
+-------------+
+-------------+
| two_inputs  |
+-------------+
| List(Int64) |
+-------------+
+------------------+
| large_and_list   |
+------------------+
| LargeList(Int64) |
+------------------+
+----------------+
| fixed_and_list |
+----------------+
| List(Int64)    |
+----------------+
Reproducer source: repro.sql
CREATE TABLE t AS SELECT
  make_array(arrow_cast([1, 2], 'LargeList(Int64)')) AS a,
  arrow_cast([[1]], 'List(List(Int64, field: ''element''), field: ''element'')') AS b,
  arrow_cast(make_array(make_array(1)), 'List(List(non-null Int64))') AS c;

SELECT array_concat(a, a) FROM t;
SELECT array_concat(b, b) FROM t;
SELECT array_concat(arrow_cast(NULL, 'List(Int64)'), arrow_cast(NULL, 'List(List(Int64))'));
SELECT array_concat(c, make_array(2)) FROM t;
Reproducer source: one-dimensional.sql
SELECT arrow_typeof(array_concat(arrow_cast([1], 'List(non-null Int64, field: ''element'')'))) AS one_input;
SELECT arrow_typeof(array_concat(arrow_cast([1], 'List(non-null Int64, field: ''element'')'), arrow_cast([2], 'List(non-null Int64, field: ''element'')'))) AS two_inputs;
SELECT arrow_typeof(array_concat(arrow_cast([1], 'LargeList(Int64)'), [2])) AS large_and_list;
SELECT arrow_typeof(array_concat(arrow_cast([1, 2], 'FixedSizeList(2, Int64)'), [3])) AS fixed_and_list;

Are there any user-facing changes?

Yes. These array_concat calls now return results instead of panicking or failing with internal errors, and nested results keep the inner list types of their inputs. No API changes.

array_concat planned nested list types from the leaf element type with
default inner fields, while execution kept the input fields. LargeList or
renamed inner lists failed a type assertion, all-NULL arguments of
different depths returned the wrong depth, and non-nullable inner
elements panicked. Plan from the dimension-aligned argument types and
build the output with the planned type.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
@github-actions github-actions Bot added sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation labels Oct 2, 2026
@codecov-commenter

codecov-commenter commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.61%. Comparing base (fca52aa) to head (07cf61a).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/functions-nested/src/concat.rs 84.61% 11 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25970      +/-   ##
==========================================
+ Coverage   82.59%   82.61%   +0.01%     
==========================================
  Files        1145     1147       +2     
  Lines      444194   444775     +581     
  Branches   444194   444775     +581     
==========================================
+ Hits       366897   367445     +548     
+ Misses      55029    55006      -23     
- Partials    22268    22324      +56     

☔ 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.

@Hasnaathussain Hasnaathussain 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, 1fanwang. I compared fourteen SQL cases on fca52aa59 and c3fcddbc2 under WSL/Rust 1.98.1: nine fail on the parent and all pass here, including mixed-depth List/LargeList, nested nulls and FixedSizeList inputs. The probe checks actual/planned type agreement and values against typed SQL literals; this isn't a full-suite result.

One contract detail: the one-dimensional output type changes too when the input fields already match. This query returns List(Int64) on the parent, but List(non-null Int64, field: 'element') here; the value remains [1]:

SELECT arrow_typeof(array_concat(
  arrow_cast([1], 'List(non-null Int64, field: ''element'')')
));

The same happens with two inputs carrying that field. The description says one-dimensional concatenation is unchanged, and the SLT comment still describes a default nullable item field for all one-dimensional inputs. If retaining this metadata is intentional, could you call it out in the description and add a small SLT case for identical named/non-nullable inputs? This is a contract/test clarification, not a value regression.

One-dimensional inputs that share a named or non-nullable field kept that
field in the result, which changed the result type from main. Plan
one-dimensional results from the unified element type with the default
nullable field again, and pin it in array_concat.slt.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
@1fanwang

1fanwang commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

If retaining this metadata is intentional, could you call it out in the description and add a small SLT case for identical named/non-nullable inputs?

@Hasnaathussain It wasn't intentional. 07cf61a restores main's default item field for one-dimensional results, and array_concat.slt now pins List(Int64) for that case. Nested results still keep their inner fields, which the issue needs.

@adriangb adriangb 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.

Good fix @1fanwang, one small nit. Let me know when this is ready to merge.

Comment thread datafusion/functions-nested/src/concat.rs Outdated
Signed-off-by: 1fanwang <1fannnw@gmail.com>
@adriangb
adriangb added this pull request to the merge queue Oct 23, 2026
Merged via the queue into apache:main with commit 3c83711 Oct 23, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions Changes to functions implementation sqllogictest SQL Logic Tests (.slt) v56.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

array_concat on nested lists panics or fails with type mismatch errors

4 participants