fix: Fix array_to_string with columnar third arg (#20536)

## Which issue does this PR close?

- Closes #20535 

## Rationale for this change

The previous coding used the `null_string` value for the first row as
the value for the remainder of the rows. This is wrong if `null_string`
is columnar.

## What changes are included in this PR?

## Are these changes tested?

Yes; added new SLT test.

## Are there any user-facing changes?

No, other than fixing the behavior of `array_to_string` in this
scenario.
This commit is contained in:
Neil Conway
2026-02-27 11:03:32 -05:00
committed by GitHub
parent 7ef62b988d
commit 451c79fc00
2 changed files with 41 additions and 27 deletions
+22 -27
View File
@@ -347,21 +347,20 @@ fn array_to_string_inner(args: &[ArrayRef]) -> Result<ArrayRef> {
}
};
let mut null_string = String::from("");
let mut with_null_string = false;
if args.len() == 3 {
null_string = match args[2].data_type() {
Utf8 => args[2].as_string::<i32>().value(0).to_string(),
Utf8View => args[2].as_string_view().value(0).to_string(),
LargeUtf8 => args[2].as_string::<i64>().value(0).to_string(),
let null_strings = if args.len() == 3 {
Some(match args[2].data_type() {
Utf8 => args[2].as_string::<i32>().iter().collect(),
Utf8View => args[2].as_string_view().iter().collect(),
LargeUtf8 => args[2].as_string::<i64>().iter().collect(),
other => {
return exec_err!(
"unsupported type for second argument to array_to_string function as {other:?}"
"unsupported type for third argument to array_to_string function as {other:?}"
);
}
};
with_null_string = true;
}
})
} else {
None
};
/// Creates a single string from single element of a ListArray (which is
/// itself another Array)
@@ -469,18 +468,24 @@ fn array_to_string_inner(args: &[ArrayRef]) -> Result<ArrayRef> {
fn generate_string_array<O: OffsetSizeTrait>(
list_arr: &GenericListArray<O>,
delimiters: &[Option<&str>],
null_string: &str,
with_null_string: bool,
null_strings: &Option<Vec<Option<&str>>>,
) -> Result<StringArray> {
let mut res: Vec<Option<String>> = Vec::new();
for (arr, &delimiter) in list_arr.iter().zip(delimiters.iter()) {
for (i, (arr, &delimiter)) in list_arr.iter().zip(delimiters.iter()).enumerate() {
if let (Some(arr), Some(delimiter)) = (arr, delimiter) {
let (null_string, with_null_string) = match null_strings {
Some(ns) => match ns[i] {
Some(s) => (s.to_string(), true),
None => (String::new(), false),
},
None => (String::new(), false),
};
let mut arg = String::from("");
let s = compute_array_to_string(
&mut arg,
&arr,
delimiter.to_string(),
null_string.to_string(),
null_string,
with_null_string,
)?
.clone();
@@ -501,21 +506,11 @@ fn array_to_string_inner(args: &[ArrayRef]) -> Result<ArrayRef> {
let string_arr = match arr.data_type() {
List(_) => {
let list_array = as_list_array(&arr)?;
generate_string_array::<i32>(
list_array,
&delimiters,
&null_string,
with_null_string,
)?
generate_string_array::<i32>(list_array, &delimiters, &null_strings)?
}
LargeList(_) => {
let list_array = as_large_list_array(&arr)?;
generate_string_array::<i64>(
list_array,
&delimiters,
&null_string,
with_null_string,
)?
generate_string_array::<i64>(list_array, &delimiters, &null_strings)?
}
// Signature guards against this arm
_ => return exec_err!("array_to_string expects list as first argument"),
@@ -5248,6 +5248,25 @@ NULL 1.2.3
51_52_*_54_55_56_57_58_59_60 1.2.3
61_62_63_64_65_66_67_68_69_70 1.2.3
# array_to_string with per-row null_string column
statement ok
CREATE TABLE test_null_str_col AS VALUES
(make_array(1, NULL, 3), ',', 'N/A'),
(make_array(NULL, 5, NULL), ',', 'MISSING'),
(make_array(10, NULL, 12), '-', 'X'),
(make_array(20, NULL, 21), '-', NULL);
query T
SELECT array_to_string(column1, column2, column3) FROM test_null_str_col;
----
1,N/A,3
MISSING,5,MISSING
10-X-12
20-21
statement ok
DROP TABLE test_null_str_col;
## cardinality
# cardinality scalar function