Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions datafusion/functions-nested/src/make_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use arrow::array::{
use arrow::buffer::OffsetBuffer;
use arrow::datatypes::DataType;
use arrow::datatypes::{DataType::Null, Field};
use datafusion_common::utils::SingleRowListArrayBuilder;
use datafusion_common::{Result, plan_err};
use datafusion_expr::binary::{
try_type_union_resolution_with_struct, type_union_resolution,
Expand Down Expand Up @@ -137,17 +136,36 @@ pub(crate) fn make_array_inner(arrays: &[ArrayRef]) -> Result<ArrayRef> {

let data_type = data_type.unwrap_or(&Null);
if data_type.is_null() {
// Either an empty array or all nulls:
let length = arrays.iter().map(|a| a.len()).sum();
let array = new_null_array(&Null, length);
Ok(Arc::new(
SingleRowListArrayBuilder::new(array).build_list_array(),
))
Ok(Arc::new(null_list_array(
arrays,
Field::LIST_FIELD_DEFAULT_NAME,
)))
} else {
array_array::<i32>(arrays, data_type.clone(), Field::LIST_FIELD_DEFAULT_NAME)
}
}

/// Builds the result of `make_array` when there are no arguments or every
/// argument is `NULL`-typed: a `List(Null)` array with one row per input row,
/// each holding one null per argument. With no arguments there is a single
/// row holding an empty list.
///
/// The arguments have already been expanded to a common length, so the row
/// count is taken from the first one. Summing the argument lengths instead
/// produced a single row for column inputs, see
/// <https://github.com/apache/datafusion/issues/21841>.
pub fn null_list_array(arrays: &[ArrayRef], field_name: &str) -> GenericListArray<i32> {
let num_rows = arrays.first().map_or(1, |array| array.len());
let values = new_null_array(&Null, num_rows * arrays.len());
let offsets = OffsetBuffer::from_lengths(std::iter::repeat_n(arrays.len(), num_rows));
GenericListArray::new(
Arc::new(Field::new(field_name, Null, true)),
offsets,
values,
None,
)
}

/// Convert one or more [`ArrayRef`] of the same type into a
/// `ListArray` or 'LargeListArray' depending on the offset size.
///
Expand Down
19 changes: 5 additions & 14 deletions datafusion/spark/src/function/array/spark_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@

use std::sync::Arc;

use arrow::array::{Array, ArrayRef, new_null_array};
use arrow::array::{Array, ArrayRef};
use arrow::datatypes::{DataType, Field, FieldRef};
use datafusion_common::utils::SingleRowListArrayBuilder;
use datafusion_common::{Result, internal_err};
use datafusion_expr::{
ColumnarValue, ReturnFieldArgs, ScalarFunctionArgs, ScalarUDFImpl, Signature,
Volatility,
};
use datafusion_functions_nested::make_array::{array_array, coerce_types_inner};
use datafusion_functions_nested::make_array::{
array_array, coerce_types_inner, null_list_array,
};

use crate::function::functions_nested_utils::make_scalar_function;

Expand Down Expand Up @@ -121,17 +122,7 @@ pub fn make_array_inner(arrays: &[ArrayRef]) -> Result<ArrayRef> {

match data_type {
// Either an empty array or all nulls:
DataType::Null => {
let length = arrays.iter().map(|a| a.len()).sum();
// By default Int32
let array = new_null_array(&DataType::Null, length);
Ok(Arc::new(
SingleRowListArrayBuilder::new(array)
.with_nullable(true)
.with_field_name(Some(ARRAY_FIELD_DEFAULT_NAME.to_string()))
.build_list_array(),
))
}
DataType::Null => Ok(Arc::new(null_list_array(arrays, ARRAY_FIELD_DEFAULT_NAME))),
_ => array_array::<i32>(arrays, data_type, ARRAY_FIELD_DEFAULT_NAME),
}
}
27 changes: 27 additions & 0 deletions datafusion/sqllogictest/test_files/array/make_array.slt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,33 @@ select make_array(NULL), make_array(NULL, NULL, NULL), make_array(make_array(NUL
----
[NULL] [NULL, NULL, NULL] [[NULL, NULL], [NULL, NULL]]

# make_array over NULL-typed columns builds one list per row, not a single
# list of all the nulls (https://github.com/apache/datafusion/issues/21841)
query ?
select make_array(n) from (values (NULL), (NULL), (NULL)) as t(n);
----
[NULL]
[NULL]
[NULL]

query ?T
select [n, n], arrow_typeof([n, n]) from (values (NULL), (NULL)) as t(n);
----
[NULL, NULL] List(Null)
[NULL, NULL] List(Null)

query I
select count(*) from (select unnest([n, n]) from (values (NULL), (NULL), (NULL)) as t(n));
----
6

# the NULL-typed path of array_append goes through make_array as well
query ?
select array_append(n, n) from (values (NULL), (NULL)) as t(n);
----
[NULL]
[NULL]

# make_array with 1 columns
query ???
select make_array(a), make_array(d), make_array(e) from values;
Expand Down
7 changes: 7 additions & 0 deletions datafusion/sqllogictest/test_files/spark/array/array.slt
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,10 @@ query ?
SELECT array(arrow_cast(array(1,2), 'LargeList(Int64)'), array(3));
----
[[1, 2], [3]]

# NULL-typed columns build one list per row
query ?
SELECT array(n) FROM (VALUES (NULL), (NULL)) AS t(n);
----
[NULL]
[NULL]