Skip to content

adding support for Min/Max over LargeList and FixedSizeList #16071

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

Merged
merged 9 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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
34 changes: 34 additions & 0 deletions datafusion/common/src/scalar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4941,6 +4941,40 @@ mod tests {
])]),
));
assert_eq!(a.partial_cmp(&b), Some(Ordering::Greater));

let a = ScalarValue::LargeList(Arc::new(LargeListArray::from_iter_primitive::<
Int64Type,
_,
_,
>(vec![Some(vec![
None,
Some(2),
Some(3),
])])));
let b = ScalarValue::LargeList(Arc::new(LargeListArray::from_iter_primitive::<
Int64Type,
_,
_,
>(vec![Some(vec![
Some(1),
Some(2),
Some(3),
])])));
assert_eq!(a.partial_cmp(&b), Some(Ordering::Greater));

let a = ScalarValue::FixedSizeList(Arc::new(
FixedSizeListArray::from_iter_primitive::<Int64Type, _, _>(
vec![Some(vec![None, Some(2), Some(3)])],
3,
),
));
let b = ScalarValue::FixedSizeList(Arc::new(
FixedSizeListArray::from_iter_primitive::<Int64Type, _, _>(
vec![Some(vec![Some(1), Some(2), Some(3)])],
3,
),
));
assert_eq!(a.partial_cmp(&b), Some(Ordering::Greater));
}

#[test]
Expand Down
23 changes: 23 additions & 0 deletions datafusion/functions-aggregate/src/min_max.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,10 @@ fn min_batch(values: &ArrayRef) -> Result<ScalarValue> {
}
DataType::Struct(_) => min_max_batch_generic(values, Ordering::Greater)?,
DataType::List(_) => min_max_batch_generic(values, Ordering::Greater)?,
DataType::LargeList(_) => min_max_batch_generic(values, Ordering::Greater)?,
DataType::FixedSizeList(_, _) => {
min_max_batch_generic(values, Ordering::Greater)?
}
DataType::Dictionary(_, _) => {
let values = values.as_any_dictionary().values();
min_batch(values)?
Expand Down Expand Up @@ -716,6 +720,8 @@ pub fn max_batch(values: &ArrayRef) -> Result<ScalarValue> {
}
DataType::Struct(_) => min_max_batch_generic(values, Ordering::Less)?,
DataType::List(_) => min_max_batch_generic(values, Ordering::Less)?,
DataType::LargeList(_) => min_max_batch_generic(values, Ordering::Less)?,
DataType::FixedSizeList(_, _) => min_max_batch_generic(values, Ordering::Less)?,
DataType::Dictionary(_, _) => {
let values = values.as_any_dictionary().values();
max_batch(values)?
Expand Down Expand Up @@ -1004,6 +1010,23 @@ macro_rules! min_max {
) => {
min_max_generic!(lhs, rhs, $OP)
}


(
lhs @ ScalarValue::LargeList(_),
rhs @ ScalarValue::LargeList(_),
) => {
min_max_generic!(lhs, rhs, $OP)
}


(
lhs @ ScalarValue::FixedSizeList(_),
rhs @ ScalarValue::FixedSizeList(_),
) => {
min_max_generic!(lhs, rhs, $OP)
}

e => {
return internal_err!(
"MIN/MAX is not expected to receive scalars of incompatible types {:?}",
Expand Down
2 changes: 2 additions & 0 deletions datafusion/optimizer/src/analyzer/type_coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,8 @@ fn extract_window_frame_target_type(col_type: &DataType) -> Result<DataType> {
if col_type.is_numeric()
|| is_utf8_or_utf8view_or_large_utf8(col_type)
|| matches!(col_type, DataType::List(_))
|| matches!(col_type, DataType::LargeList(_))
|| matches!(col_type, DataType::FixedSizeList(_, _))
|| matches!(col_type, DataType::Null)
|| matches!(col_type, DataType::Boolean)
{
Expand Down
134 changes: 134 additions & 0 deletions datafusion/sqllogictest/test_files/fixed_size_list.slt
Copy link
Contributor

@gabotechs gabotechs May 17, 2025

Choose a reason for hiding this comment

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

Really nice to get this tested! maybe it's a bit overkill to add dedicated .slt files though. Note that the core logic of the Min/Max function over lists is already tested in

{a: 1, b: 2, c: 3} {a: 1, b: 2, c: 4}
# Min/Max with list over integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([1, 2, 3]),
([1, 2]);
----
[1, 2] [1, 2, 3]
# Min/Max with lists over strings
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(['a', 'b', 'c']),
(['a', 'b']);
----
[a, b] [a, b, c]
# Min/Max with list over booleans
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([true, true, false]),
([false, true]);
----
[false, true] [true, true, false]
# Min/Max with list over nullable integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([NULL, 1, 2]),
([1, 2]);
----
[1, 2] [NULL, 1, 2]
# Min/Max list with different lengths and nulls
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([1, NULL, 3]),
([1, 2, 3, 4]),
([1, 2]);
----
[1, 2] [1, NULL, 3]
# Min/Max list with only NULLs
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([NULL, NULL]),
([NULL]);
----
[NULL] [NULL, NULL]
# Min/Max list with empty lists
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([]),
([1]),
([]);
----
[] [1]
# Min/Max list of varying types (integers and NULLs)
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([1, 2, 3]),
([NULL, 2, 3]),
([1, 2, NULL]);
----
[1, 2, 3] [NULL, 2, 3]
# Min/Max list grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, [1, NULL, 3]),
(0, [1, 2, 3, 4]),
(1, [1, 2]),
(1, [NULL, 5]),
(1, [])
GROUP BY column1;
----
0 [1, 2, 3, 4] [1, NULL, 3]
1 [] [NULL, 5]
# Min/Max list grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, [NULL]),
(0, [NULL, NULL]),
(1, [NULL])
GROUP BY column1;
----
0 [NULL] [NULL, NULL]
1 [NULL] [NULL]
# Min/Max grouped list with empty and non-empty
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, []),
(0, [1]),
(0, []),
(1, [5, 6]),
(1, [])
GROUP BY column1;
----
0 [] [1]
1 [] [5, 6]
# Min/Max over lists with a window function
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
([1, 2, 3]),
([1, 2, 3]),
([2, 3])
----
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
# Min/Max over lists with a window function and nulls
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
(NULL),
([4, 5]),
([2, 3])
----
[2, 3]
[2, 3]
[2, 3]
# Min/Max over lists with a window function, nulls and ROWS BETWEEN statement
query ?
SELECT min(column1) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
(NULL),
([4, 5]),
([2, 3])
----
[2, 3]
[2, 3]
[4, 5]
# Min/Max over lists with a window function using a different column
query ?
SELECT max(column2) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
([1, 2, 3], [4, 5]),
([2, 3], [2, 3]),
([1, 2, 3], NULL)
----
[4, 5]
[4, 5]
[2, 3]

IMO it should be enough to add a small set of basic tests at the end of aggregate.slt proving that the new FixedSizeList and LargeList types are supported, and rely on the previous vanilla List tests for checking that the core logic works. Otherwise, it will be unclear if new tests to the Min/Max aggregations over lists logic should also be replicated for all list variants.

WDYT? would also be curious to know the stance of other contributors

Copy link
Contributor Author

@logan-keede logan-keede May 17, 2025

Choose a reason for hiding this comment

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

Very nice that this was shipped so fast, thanks!

I did not do anything, Everything was already done!

Really nice to get this tested! maybe it's a bit overkill to add dedicated .slt files though. Note that the core logic of the Min/Max function over lists is already tested in

{a: 1, b: 2, c: 3} {a: 1, b: 2, c: 4}
# Min/Max with list over integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([1, 2, 3]),
([1, 2]);
----
[1, 2] [1, 2, 3]
# Min/Max with lists over strings
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(['a', 'b', 'c']),
(['a', 'b']);
----
[a, b] [a, b, c]
# Min/Max with list over booleans
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([true, true, false]),
([false, true]);
----
[false, true] [true, true, false]
# Min/Max with list over nullable integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([NULL, 1, 2]),
([1, 2]);
----
[1, 2] [NULL, 1, 2]
# Min/Max list with different lengths and nulls
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([1, NULL, 3]),
([1, 2, 3, 4]),
([1, 2]);
----
[1, 2] [1, NULL, 3]
# Min/Max list with only NULLs
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([NULL, NULL]),
([NULL]);
----
[NULL] [NULL, NULL]
# Min/Max list with empty lists
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([]),
([1]),
([]);
----
[] [1]
# Min/Max list of varying types (integers and NULLs)
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
([1, 2, 3]),
([NULL, 2, 3]),
([1, 2, NULL]);
----
[1, 2, 3] [NULL, 2, 3]
# Min/Max list grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, [1, NULL, 3]),
(0, [1, 2, 3, 4]),
(1, [1, 2]),
(1, [NULL, 5]),
(1, [])
GROUP BY column1;
----
0 [1, 2, 3, 4] [1, NULL, 3]
1 [] [NULL, 5]
# Min/Max list grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, [NULL]),
(0, [NULL, NULL]),
(1, [NULL])
GROUP BY column1;
----
0 [NULL] [NULL, NULL]
1 [NULL] [NULL]
# Min/Max grouped list with empty and non-empty
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, []),
(0, [1]),
(0, []),
(1, [5, 6]),
(1, [])
GROUP BY column1;
----
0 [] [1]
1 [] [5, 6]
# Min/Max over lists with a window function
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
([1, 2, 3]),
([1, 2, 3]),
([2, 3])
----
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
# Min/Max over lists with a window function and nulls
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
(NULL),
([4, 5]),
([2, 3])
----
[2, 3]
[2, 3]
[2, 3]
# Min/Max over lists with a window function, nulls and ROWS BETWEEN statement
query ?
SELECT min(column1) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
(NULL),
([4, 5]),
([2, 3])
----
[2, 3]
[2, 3]
[4, 5]
# Min/Max over lists with a window function using a different column
query ?
SELECT max(column2) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
([1, 2, 3], [4, 5]),
([2, 3], [2, 3]),
([1, 2, 3], NULL)
----
[4, 5]
[4, 5]
[2, 3]

IMO it should be enough to add a small set of basic tests at the end of aggregate.slt proving that the new FixedSizeList and LargeList types are supported, and rely on the previous vanilla List tests for checking that the core logic works. Otherwise, it will be unclear if new tests to the Min/Max aggregations over lists logic should also be replicated for all list variants.

WDYT? would also be curious to know the stance of other contributors

If you consider that we are actually using a generic logic under the hood then yes, but if we think from the perspective of black-box testing then perhaps it is more prudent to have complete test suite.
(edit:- or we can just leave a note that code path is currently same, and developer/reviewer should account for that.)

on a side note, I would like to avoid adding any more test to aggregate.slt. see this #13723

it already gives 132 error on my laptop(configuration issues that can be fixed,not an actual problem) but yeah it is a mess to parse through.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 that's fair, I wonder if there's a way of having all these tests be executed on List, LargeList and FixedSizeList types without the need of duplicating them manually. I see that there do is some tooling in place that was used for Utf8, LargeUtf8 and Utf8View here #12525. Do you think it's worth a shot?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess so. I am currently travelling, I will try to do that tomorrow.

Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Min/Max with FixedSizeList over integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3, 4), 'FixedSizeList(4, Int64)')),
(arrow_cast(make_array(1, 2), 'FixedSizeList(2, Int64)'));
----
[1, 2] [1, 2, 3, 4]

# Min/Max with FixedSizeList over strings
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array('a', 'b', 'c'), 'FixedSizeList(3, Utf8)')),
(arrow_cast(make_array('a', 'b'), 'FixedSizeList(2, Utf8)'));
----
[a, b] [a, b, c]

# Min/Max with FixedSizeList over booleans
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(true, false, true), 'FixedSizeList(3, Boolean)')),
(arrow_cast(make_array(true, false), 'FixedSizeList(2, Boolean)'));
----
[true, false] [true, false, true]

# Min/Max with FixedSizeList over nullable integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(NULL, 1, 2), 'FixedSizeList(3, Int64)')),
(arrow_cast(make_array(1, 2), 'FixedSizeList(2, Int64)'));
----
[1, 2] [NULL, 1, 2]

# Min/Max FixedSizeList with different lengths and nulls
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3, 4), 'FixedSizeList(4, Int64)')),
(arrow_cast(make_array(1, 2), 'FixedSizeList(2, Int64)')),
(arrow_cast(make_array(1, NULL, 3), 'FixedSizeList(3, Int64)'));
----
[1, 2] [1, NULL, 3]

# Min/Max FixedSizeList with only NULLs
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(NULL, NULL), 'FixedSizeList(2, Int64)')),
(arrow_cast(make_array(NULL), 'FixedSizeList(1, Int64)'));
----
[NULL] [NULL, NULL]


# Min/Max FixedSizeList of varying types (integers and NULLs)
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3), 'FixedSizeList(3, Int64)')),
(arrow_cast(make_array(NULL, 2, 3), 'FixedSizeList(3, Int64)')),
(arrow_cast(make_array(1, 2, NULL), 'FixedSizeList(3, Int64)'));
----
[1, 2, 3] [NULL, 2, 3]

# Min/Max FixedSizeList grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, arrow_cast(make_array(1, NULL, 3), 'FixedSizeList(3, Int64)')),
(0, arrow_cast(make_array(1, 2, 3, 4), 'FixedSizeList(4, Int64)')),
(1, arrow_cast(make_array(1, 2), 'FixedSizeList(2, Int64)')),
(1, arrow_cast(make_array(NULL, 5), 'FixedSizeList(2, Int64)'))
GROUP BY column1;
----
0 [1, 2, 3, 4] [1, NULL, 3]
1 [1, 2] [NULL, 5]

# Min/Max FixedSizeList grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, arrow_cast(make_array(NULL), 'FixedSizeList(1, Int64)')),
(0, arrow_cast(make_array(NULL, NULL), 'FixedSizeList(2, Int64)')),
(1, arrow_cast(make_array(NULL), 'FixedSizeList(1, Int64)'))
GROUP BY column1;
----
0 [NULL] [NULL, NULL]
1 [NULL] [NULL]

# Min/Max grouped FixedSizeList with empty and non-empty
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, arrow_cast(make_array(1), 'FixedSizeList(1, Int64)')),
(1, arrow_cast(make_array(5, 6), 'FixedSizeList(2, Int64)'))
GROUP BY column1;
----
0 [1] [1]
1 [5, 6] [5, 6]

# Min/Max over FixedSizeList with a window function
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3), 'FixedSizeList(3, Int64)')),
(arrow_cast(make_array(1, 2, 3), 'FixedSizeList(3, Int64)')),
(arrow_cast(make_array(2, 3), 'FixedSizeList(2, Int64)'))
----
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]

# Min/Max over FixedSizeList with a window function and nulls
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
(arrow_cast(make_array(NULL), 'FixedSizeList(1, Int64)')),
(arrow_cast(make_array(4, 5), 'FixedSizeList(2, Int64)')),
(arrow_cast(make_array(2, 3), 'FixedSizeList(2, Int64)'))
----
[2, 3]
[2, 3]
[2, 3]

# Min/Max over FixedSizeList with a window function, nulls and ROWS BETWEEN statement
query ?
SELECT min(column1) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
(arrow_cast(make_array(NULL), 'FixedSizeList(1, Int64)')),
(arrow_cast(make_array(4, 5), 'FixedSizeList(2, Int64)')),
(arrow_cast(make_array(2, 3), 'FixedSizeList(2, Int64)'))
----
[2, 3]
[2, 3]
[4, 5]

# Min/Max over FixedSizeList with a window function using a different column
query ?
SELECT max(column2) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
(arrow_cast(make_array(1, 2, 3), 'FixedSizeList(3, Int64)'), arrow_cast(make_array(4, 5), 'FixedSizeList(2, Int64)')),
(arrow_cast(make_array(2, 3), 'FixedSizeList(2, Int64)'), arrow_cast(make_array(2, 3), 'FixedSizeList(2, Int64)'))
----
[4, 5]
[4, 5]

148 changes: 148 additions & 0 deletions datafusion/sqllogictest/test_files/large_list.slt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Min/Max with LargeList over integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3, 4), 'LargeList(Int64)')),
(arrow_cast(make_array(1, 2), 'LargeList(Int64)'));
----
[1, 2] [1, 2, 3, 4]

# Min/Max with LargeList over strings
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array('a', 'b', 'c'), 'LargeList(Utf8)')),
(arrow_cast(make_array('a', 'b'), 'LargeList(Utf8)'));
----
[a, b] [a, b, c]

# Min/Max with LargeList over booleans
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(true, false, true), 'LargeList(Boolean)')),
(arrow_cast(make_array(true, false), 'LargeList(Boolean)'));
----
[true, false] [true, false, true]

# Min/Max with LargeList over nullable integers
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(NULL, 1, 2), 'LargeList(Int64)')),
(arrow_cast(make_array(1, 2), 'LargeList(Int64)'));
----
[1, 2] [NULL, 1, 2]

# Min/Max LargeList with different lengths and nulls
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3, 4), 'LargeList(Int64)')),
(arrow_cast(make_array(1, 2), 'LargeList(Int64)')),
(arrow_cast(make_array(1, NULL, 3), 'LargeList(Int64)'));
----
[1, 2] [1, NULL, 3]

# Min/Max LargeList with only NULLs
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(NULL, NULL), 'LargeList(Int64)')),
(arrow_cast(make_array(NULL), 'LargeList(Int64)'));
----
[NULL] [NULL, NULL]

# Min/Max LargeList with empty LargeLists
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(), 'LargeList(Int64)')),
(arrow_cast(make_array(), 'LargeList(Int64)')),
(arrow_cast(make_array(1), 'LargeList(Int64)'));
----
[] [1]

# Min/Max LargeList of varying types (integers and NULLs)
query ??
SELECT MIN(column1), MAX(column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3), 'LargeList(Int64)')),
(arrow_cast(make_array(NULL, 2, 3), 'LargeList(Int64)')),
(arrow_cast(make_array(1, 2, NULL), 'LargeList(Int64)'));
----
[1, 2, 3] [NULL, 2, 3]

# Min/Max LargeList grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, arrow_cast(make_array(1, NULL, 3), 'LargeList(Int64)')),
(0, arrow_cast(make_array(1, 2, 3, 4), 'LargeList(Int64)')),
(1, arrow_cast(make_array(1, 2), 'LargeList(Int64)')),
(1, arrow_cast(make_array(NULL, 5), 'LargeList(Int64)')),
(1, arrow_cast(make_array(), 'LargeList(Int64)'))
GROUP BY column1;
----
0 [1, 2, 3, 4] [1, NULL, 3]
1 [] [NULL, 5]

# Min/Max LargeList grouped by key with NULLs and differing lengths
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, arrow_cast(make_array(NULL), 'LargeList(Int64)')),
(0, arrow_cast(make_array(NULL, NULL), 'LargeList(Int64)')),
(1, arrow_cast(make_array(NULL), 'LargeList(Int64)'))
GROUP BY column1;
----
0 [NULL] [NULL, NULL]
1 [NULL] [NULL]

# Min/Max grouped LargeList with empty and non-empty
query I?? rowsort
SELECT column1, MIN(column2), MAX(column2) FROM VALUES
(0, arrow_cast(make_array(), 'LargeList(Int64)')),
(0, arrow_cast(make_array(1), 'LargeList(Int64)')),
(0, arrow_cast(make_array(), 'LargeList(Int64)')),
(1, arrow_cast(make_array(5, 6), 'LargeList(Int64)')),
(1, arrow_cast(make_array(), 'LargeList(Int64)'))
GROUP BY column1;
----
0 [] [1]
1 [] [5, 6]

# Min/Max over LargeLists with a window function
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
(arrow_cast(make_array(1, 2, 3), 'LargeList(Int64)')),
(arrow_cast(make_array(1, 2, 3), 'LargeList(Int64)')),
(arrow_cast(make_array(2, 3), 'LargeList(Int64)'))
----
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]

# Min/Max over LargeLists with a window function and nulls
query ?
SELECT min(column1) OVER (ORDER BY column1) FROM VALUES
(arrow_cast(make_array(NULL), 'LargeList(Int64)')),
(arrow_cast(make_array(4, 5), 'LargeList(Int64)')),
(arrow_cast(make_array(2, 3), 'LargeList(Int64)'))
----
[2, 3]
[2, 3]
[2, 3]

# Min/Max over LargeLists with a window function, nulls and ROWS BETWEEN statement
query ?
SELECT min(column1) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
(arrow_cast(make_array(NULL), 'LargeList(Int64)')),
(arrow_cast(make_array(4, 5), 'LargeList(Int64)')),
(arrow_cast(make_array(2, 3), 'LargeList(Int64)'))
----
[2, 3]
[2, 3]
[4, 5]

# Min/Max over LargeLists with a window function using a different column
query ?
SELECT max(column2) OVER (ORDER BY column1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES
(arrow_cast(make_array(1, 2, 3), 'LargeList(Int64)'), arrow_cast(make_array(4, 5), 'LargeList(Int64)')),
(arrow_cast(make_array(2, 3), 'LargeList(Int64)'), arrow_cast(make_array(2, 3), 'LargeList(Int64)')),
(arrow_cast(make_array(1, 2, 3), 'LargeList(Int64)'), NULL);
----
[4, 5]
[4, 5]
[2, 3]