use crate::aggregates::group_values::multi_group_by::GroupColumn;
use crate::aggregates::group_values::row::encode_array_if_necessary;
use arrow::array::{Array, ArrayRef, BooleanBufferBuilder};
use arrow::datatypes::DataType;
use arrow::row::{RowConverter, Rows, SortField};
use datafusion_common::{DataFusionError, Result};
pub struct RowsGroupColumn {
row_converter: RowConverter,
group_values: Rows,
output_type: DataType,
}
fn contains_fsl_with_dictionary(data_type: &DataType) -> bool {
fn walk(dt: &DataType, inside_fsl: bool) -> bool {
match dt {
DataType::Dictionary(_, _) => inside_fsl,
DataType::FixedSizeList(f, _) => walk(f.data_type(), true),
DataType::List(f)
| DataType::LargeList(f)
| DataType::ListView(f)
| DataType::LargeListView(f) => walk(f.data_type(), inside_fsl),
DataType::Map(f, _) => walk(f.data_type(), inside_fsl),
DataType::Struct(fs) => fs.iter().any(|f| walk(f.data_type(), inside_fsl)),
DataType::RunEndEncoded(_, values) => walk(values.data_type(), inside_fsl),
DataType::Union(fs, _) => {
fs.iter().any(|(_, f)| walk(f.data_type(), inside_fsl))
}
_ => false,
}
}
walk(data_type, false)
}
fn contains_union_or_run_end_encoded(data_type: &DataType) -> bool {
match data_type {
DataType::Union(_, _) | DataType::RunEndEncoded(_, _) => true,
DataType::List(f)
| DataType::LargeList(f)
| DataType::ListView(f)
| DataType::LargeListView(f)
| DataType::FixedSizeList(f, _) => {
contains_union_or_run_end_encoded(f.data_type())
}
DataType::Map(f, _) => contains_union_or_run_end_encoded(f.data_type()),
DataType::Struct(fs) => fs
.iter()
.any(|f| contains_union_or_run_end_encoded(f.data_type())),
_ => false,
}
}
impl RowsGroupColumn {
pub fn supports_type(data_type: &DataType) -> bool {
if contains_fsl_with_dictionary(data_type) {
return false;
}
if contains_union_or_run_end_encoded(data_type) {
return false;
}
RowConverter::supports_fields(&[SortField::new(data_type.clone())])
}
pub fn try_new(data_type: DataType) -> Result<Self> {
let row_converter = RowConverter::new(vec![SortField::new(data_type.clone())])?;
let group_values = row_converter.empty_rows(0, 0);
Ok(Self {
row_converter,
group_values,
output_type: data_type,
})
}
fn rows_to_array<'a>(
&self,
rows: impl IntoIterator<Item = arrow::row::Row<'a>>,
) -> ArrayRef {
let mut arrays = self
.row_converter
.convert_rows(rows)
.expect("row conversion during emit");
assert_eq!(
arrays.len(),
1,
"Single field row converter must produce exactly one array, actual length is {}",
arrays.len()
);
let array = arrays.pop().unwrap();
encode_array_if_necessary(&array, &self.output_type)
.expect("dictionary re-encode during emit")
}
fn convert(&self, array: &ArrayRef) -> Result<Rows> {
self.row_converter
.convert_columns(std::slice::from_ref(array))
.map_err(DataFusionError::from)
}
}
impl GroupColumn for RowsGroupColumn {
fn equal_to(&self, lhs_row: usize, array: &ArrayRef, rhs_row: usize) -> bool {
let incoming = self
.convert(&array.slice(rhs_row, 1))
.expect("row conversion during equal_to");
self.group_values.row(lhs_row) == incoming.row(0)
}
fn append_val(&mut self, array: &ArrayRef, row: usize) -> Result<()> {
let incoming = self.convert(&array.slice(row, 1))?;
self.group_values.push(incoming.row(0));
Ok(())
}
fn vectorized_equal_to(
&self,
lhs_rows: &[usize],
array: &ArrayRef,
rhs_rows: &[usize],
equal_to_results: &mut BooleanBufferBuilder,
) {
let incoming = self
.convert(array)
.expect("row conversion during vectorized_equal_to");
for (idx, (&lhs_row, &rhs_row)) in
lhs_rows.iter().zip(rhs_rows.iter()).enumerate()
{
if !equal_to_results.get_bit(idx) {
continue;
}
if self.group_values.row(lhs_row) != incoming.row(rhs_row) {
equal_to_results.set_bit(idx, false);
}
}
}
fn vectorized_append(&mut self, array: &ArrayRef, rows: &[usize]) -> Result<()> {
let incoming = self.convert(array)?;
for &row in rows {
self.group_values.push(incoming.row(row));
}
Ok(())
}
fn len(&self) -> usize {
self.group_values.num_rows()
}
fn size(&self) -> usize {
self.row_converter.size() + self.group_values.size()
}
fn build(self: Box<Self>) -> ArrayRef {
self.rows_to_array(&self.group_values)
}
fn take_n(&mut self, n: usize) -> ArrayRef {
debug_assert!(n <= self.group_values.num_rows());
let output = self.rows_to_array(self.group_values.iter().take(n));
let remaining_rows = self.group_values.num_rows() - n;
let remaining_bytes = self.group_values.lengths().skip(n).sum();
let mut remaining = self
.row_converter
.empty_rows(remaining_rows, remaining_bytes);
for row in self.group_values.iter().skip(n) {
remaining.push(row);
}
self.group_values = remaining;
output
}
}
#[cfg(test)]
mod tests {
use super::*;
use arrow::array::{
Array, ArrayRef, FixedSizeListArray, Int32Array, StringArray, StructArray,
};
use arrow::datatypes::{DataType, Field, Int32Type};
use std::sync::Arc;
fn fsl_i32(data: Vec<Option<Vec<Option<i32>>>>, list_len: i32) -> ArrayRef {
Arc::new(FixedSizeListArray::from_iter_primitive::<Int32Type, _, _>(
data, list_len,
))
}
fn fsl_utf8(rows: Vec<Option<Option<&str>>>) -> ArrayRef {
let child = StringArray::from(
rows.iter()
.map(|row| row.and_then(|inner| inner))
.collect::<Vec<_>>(),
);
let outer_nulls = arrow::buffer::NullBuffer::from(
rows.iter().map(|row| row.is_some()).collect::<Vec<_>>(),
);
Arc::new(FixedSizeListArray::new(
Arc::new(Field::new("item", DataType::Utf8, true)),
1,
Arc::new(child),
Some(outer_nulls),
))
}
#[test]
fn fsl_append_equal_to_build_roundtrip() {
let dt = DataType::FixedSizeList(
Arc::new(Field::new("item", DataType::Int32, true)),
2,
);
let mut col = Box::new(RowsGroupColumn::try_new(dt).unwrap());
let input = fsl_i32(
vec![
Some(vec![Some(1), Some(2)]),
None,
Some(vec![Some(3), None]),
],
2,
);
col.vectorized_append(&input, &[0, 1, 2]).unwrap();
assert_eq!(col.len(), 3);
let probe = fsl_i32(
vec![
Some(vec![Some(1), Some(2)]), None, Some(vec![Some(9), Some(9)]), Some(vec![Some(3), None]), ],
2,
);
assert!(col.equal_to(0, &probe, 0));
assert!(col.equal_to(1, &probe, 1));
assert!(!col.equal_to(0, &probe, 2));
assert!(col.equal_to(2, &probe, 3));
let mut results = BooleanBufferBuilder::new(3);
results.append_n(3, true);
col.vectorized_equal_to(&[0, 1, 2], &probe, &[0, 1, 3], &mut results);
assert!(results.get_bit(0));
assert!(results.get_bit(1));
assert!(results.get_bit(2));
let out = col.build();
let out = out.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
assert_eq!(out.len(), 3);
assert!(out.is_null(1));
assert!(!out.is_null(0));
}
#[test]
fn fsl_take_n_shifts_remaining() {
let dt = DataType::FixedSizeList(
Arc::new(Field::new("item", DataType::Int32, true)),
1,
);
let mut col = RowsGroupColumn::try_new(dt).unwrap();
let input = fsl_i32(
vec![
Some(vec![Some(10)]),
Some(vec![Some(20)]),
Some(vec![Some(30)]),
],
1,
);
col.vectorized_append(&input, &[0, 1, 2]).unwrap();
let first = col.take_n(1);
let first = first.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
let first_vals = first
.value(0)
.as_any()
.downcast_ref::<Int32Array>()
.unwrap()
.clone();
assert_eq!(first_vals.value(0), 10);
assert_eq!(col.len(), 2);
let rest = Box::new(col).build();
let rest = rest.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
assert_eq!(rest.len(), 2);
let g0 = rest
.value(0)
.as_any()
.downcast_ref::<Int32Array>()
.unwrap()
.value(0);
assert_eq!(g0, 20);
}
#[test]
fn take_n_preallocated_rebuild_preserves_variable_length_rows() {
let dt = DataType::FixedSizeList(
Arc::new(Field::new("item", DataType::Utf8, true)),
1,
);
let mut col = RowsGroupColumn::try_new(dt).unwrap();
let input = fsl_utf8(vec![
Some(Some("emit_a")), Some(None), None, Some(Some("")), Some(Some("xyz")), Some(None), Some(Some("a_much_longer_payload_string")), ]);
col.vectorized_append(&input, &[0, 1, 2, 3, 4, 5, 6])
.unwrap();
assert_eq!(col.len(), 7);
let emitted = col.take_n(3);
let emitted = emitted
.as_any()
.downcast_ref::<FixedSizeListArray>()
.unwrap();
assert_eq!(emitted.len(), 3);
assert_eq!(
emitted
.value(0)
.as_any()
.downcast_ref::<StringArray>()
.unwrap()
.value(0),
"emit_a"
);
assert!(
emitted
.value(1)
.as_any()
.downcast_ref::<StringArray>()
.unwrap()
.is_null(0)
);
assert!(emitted.is_null(2));
assert_eq!(col.len(), 4);
let rest = Box::new(col).build();
let rest = rest.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
assert_eq!(rest.len(), 4);
let value_at = |idx: usize| {
rest.value(idx)
.as_any()
.downcast_ref::<StringArray>()
.unwrap()
.clone()
};
assert_eq!(value_at(0).value(0), "");
assert_eq!(value_at(1).value(0), "xyz");
assert!(
value_at(2).is_null(0),
"retained inner-null row must be preserved"
);
assert_eq!(value_at(3).value(0), "a_much_longer_payload_string");
}
#[test]
fn struct_roundtrip() {
let dt = DataType::Struct(vec![Field::new("a", DataType::Int32, true)].into());
let mut col = RowsGroupColumn::try_new(dt).unwrap();
let a: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(2)]));
let input: ArrayRef = Arc::new(StructArray::new(
vec![Field::new("a", DataType::Int32, true)].into(),
vec![a],
None,
));
col.vectorized_append(&input, &[0, 1]).unwrap();
assert_eq!(col.len(), 2);
assert!(col.equal_to(0, &input, 0));
assert!(!col.equal_to(0, &input, 1));
}
#[test]
fn supports_type_matches_row_converter_impl() {
assert!(RowsGroupColumn::supports_type(&DataType::FixedSizeList(
Arc::new(Field::new("item", DataType::Int32, true)),
3
)));
assert!(RowsGroupColumn::supports_type(&DataType::Struct(
vec![Field::new("a", DataType::Int32, true)].into()
)));
let map_field = Arc::new(Field::new(
"entries",
DataType::Struct(
vec![
Field::new("keys", DataType::Int32, false),
Field::new("values", DataType::Int32, true),
]
.into(),
),
false,
));
let map_dt = DataType::Map(map_field, false);
let arrow_supports =
RowConverter::supports_fields(&[SortField::new(map_dt.clone())]);
assert_eq!(RowsGroupColumn::supports_type(&map_dt), arrow_supports);
}
#[test]
fn build_preserves_list_of_dictionary_schema() {
use arrow::array::{DictionaryArray, ListArray, StringArray};
use arrow::buffer::OffsetBuffer;
use arrow::datatypes::Int32Type;
let dict_dt =
DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8));
let item_field = Arc::new(Field::new("item", dict_dt.clone(), true));
let outer_dt = DataType::List(Arc::clone(&item_field));
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
let values = Arc::new(StringArray::from(vec!["a", "b"]));
let keys = Int32Array::from(vec![0, 1]);
let dict = DictionaryArray::<Int32Type>::try_new(keys, values).unwrap();
let offsets = OffsetBuffer::from_lengths([2]);
let list =
ListArray::try_new(Arc::clone(&item_field), offsets, Arc::new(dict), None)
.unwrap();
let input: ArrayRef = Arc::new(list);
col.vectorized_append(&input, &[0]).unwrap();
let built = col.build();
assert_eq!(
built.data_type(),
&outer_dt,
"build() must return the declared List<Dict> data type, \
not the RowConverter-flattened List<Utf8>",
);
}
fn dict_utf8() -> DataType {
DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8))
}
fn fsl_of(inner: DataType) -> DataType {
DataType::FixedSizeList(Arc::new(Field::new("item", inner, true)), 2)
}
#[test]
fn supports_type_rejects_fixed_size_list_of_dict() {
assert!(!RowsGroupColumn::supports_type(&fsl_of(dict_utf8())));
}
#[test]
fn supports_type_rejects_fsl_with_dict_nested_in_struct() {
let struct_dt = DataType::Struct(vec![Field::new("d", dict_utf8(), true)].into());
assert!(!RowsGroupColumn::supports_type(&fsl_of(struct_dt)));
}
#[test]
fn supports_type_rejects_fsl_with_dict_nested_in_list() {
let list_of_dict =
DataType::List(Arc::new(Field::new("item", dict_utf8(), true)));
assert!(!RowsGroupColumn::supports_type(&fsl_of(list_of_dict)));
}
#[test]
fn supports_type_rejects_fsl_hidden_under_outer_list() {
let outer =
DataType::List(Arc::new(Field::new("item", fsl_of(dict_utf8()), true)));
assert!(!RowsGroupColumn::supports_type(&outer));
}
#[test]
fn supports_type_rejects_fsl_hidden_under_outer_struct() {
let outer =
DataType::Struct(vec![Field::new("f", fsl_of(dict_utf8()), true)].into());
assert!(!RowsGroupColumn::supports_type(&outer));
}
#[test]
fn supports_type_accepts_fsl_of_primitive() {
assert!(RowsGroupColumn::supports_type(&fsl_of(DataType::Int32)));
}
#[test]
fn supports_type_accepts_fsl_of_struct_without_dict() {
let struct_dt =
DataType::Struct(vec![Field::new("a", DataType::Int32, true)].into());
assert!(RowsGroupColumn::supports_type(&fsl_of(struct_dt)));
}
#[test]
fn supports_type_accepts_large_list_of_dict() {
let dt = DataType::LargeList(Arc::new(Field::new("item", dict_utf8(), true)));
assert!(RowsGroupColumn::supports_type(&dt));
}
#[test]
fn supports_type_accepts_list_view_of_dict() {
let dt = DataType::ListView(Arc::new(Field::new("item", dict_utf8(), true)));
assert!(RowsGroupColumn::supports_type(&dt));
}
#[test]
fn supports_type_accepts_large_list_view_of_dict() {
let dt = DataType::LargeListView(Arc::new(Field::new("item", dict_utf8(), true)));
assert!(RowsGroupColumn::supports_type(&dt));
}
#[test]
fn supports_type_map_agrees_with_row_converter() {
let entries = Arc::new(Field::new(
"entries",
DataType::Struct(
vec![
Field::new("keys", DataType::Int32, false),
Field::new("values", dict_utf8(), true),
]
.into(),
),
false,
));
let map_dt = DataType::Map(entries, false);
let arrow_supports =
RowConverter::supports_fields(&[SortField::new(map_dt.clone())]);
assert_eq!(RowsGroupColumn::supports_type(&map_dt), arrow_supports);
}
#[test]
fn build_preserves_large_list_of_dictionary_schema() {
use arrow::array::{DictionaryArray, LargeListArray, StringArray};
use arrow::buffer::OffsetBuffer;
let item_field = Arc::new(Field::new("item", dict_utf8(), true));
let outer_dt = DataType::LargeList(Arc::clone(&item_field));
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
let values = Arc::new(StringArray::from(vec!["a", "b"]));
let keys = Int32Array::from(vec![0, 1]);
let dict = DictionaryArray::<Int32Type>::try_new(keys, values).unwrap();
let offsets = OffsetBuffer::<i64>::from_lengths([2]);
let list = LargeListArray::try_new(
Arc::clone(&item_field),
offsets,
Arc::new(dict),
None,
)
.unwrap();
col.vectorized_append(&(Arc::new(list) as ArrayRef), &[0])
.unwrap();
let built = col.build();
assert_eq!(
built.data_type(),
&outer_dt,
"LargeList<Dict>: build() must preserve the declared type",
);
}
fn list_view_of_dict_input() -> (DataType, ArrayRef) {
use arrow::array::{DictionaryArray, ListViewArray, StringArray};
use arrow::buffer::ScalarBuffer;
let item_field = Arc::new(Field::new("item", dict_utf8(), true));
let outer_dt = DataType::ListView(Arc::clone(&item_field));
let values = Arc::new(StringArray::from(vec!["a", "b", "c"]));
let keys = Int32Array::from(vec![0, 1, 2]);
let dict = DictionaryArray::<Int32Type>::try_new(keys, values).unwrap();
let offsets = ScalarBuffer::<i32>::from(vec![0, 2]);
let sizes = ScalarBuffer::<i32>::from(vec![2, 1]);
let list = ListViewArray::try_new(
Arc::clone(&item_field),
offsets,
sizes,
Arc::new(dict),
None,
)
.unwrap();
(outer_dt, Arc::new(list) as ArrayRef)
}
#[test]
fn build_preserves_list_view_of_dictionary_schema() {
let (outer_dt, input) = list_view_of_dict_input();
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
col.vectorized_append(&input, &[0, 1]).unwrap();
assert_eq!(col.len(), 2);
let built = col.build();
assert_eq!(
built.data_type(),
&outer_dt,
"ListView<Dict>: build() must return the declared type, \
not the RowConverter-flattened ListView<Utf8>",
);
assert_eq!(built.len(), 2);
}
#[test]
fn take_n_preserves_list_view_of_dictionary_schema() {
let (outer_dt, input) = list_view_of_dict_input();
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
col.vectorized_append(&input, &[0, 1]).unwrap();
let taken = col.take_n(1);
assert_eq!(
taken.data_type(),
&outer_dt,
"ListView<Dict>: take_n() must return the declared type",
);
assert_eq!(taken.len(), 1);
let rest = col.build();
assert_eq!(
rest.data_type(),
&outer_dt,
"ListView<Dict>: build() after take_n must also preserve the type",
);
assert_eq!(rest.len(), 1);
}
#[test]
fn build_and_take_n_preserve_large_list_view_of_dictionary_schema() {
use arrow::array::{DictionaryArray, LargeListViewArray, StringArray};
use arrow::buffer::ScalarBuffer;
let item_field = Arc::new(Field::new("item", dict_utf8(), true));
let outer_dt = DataType::LargeListView(Arc::clone(&item_field));
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let values = Arc::new(StringArray::from(vec!["a", "b", "c"]));
let keys = Int32Array::from(vec![0, 1, 2]);
let dict = DictionaryArray::<Int32Type>::try_new(keys, values).unwrap();
let offsets = ScalarBuffer::<i64>::from(vec![0, 2]);
let sizes = ScalarBuffer::<i64>::from(vec![2, 1]);
let list = LargeListViewArray::try_new(
Arc::clone(&item_field),
offsets,
sizes,
Arc::new(dict),
None,
)
.unwrap();
let input: ArrayRef = Arc::new(list);
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
col.vectorized_append(&input, &[0, 1]).unwrap();
let taken = col.take_n(1);
assert_eq!(
taken.data_type(),
&outer_dt,
"LargeListView<Dict>: take_n() must return the declared type",
);
let rest = col.build();
assert_eq!(
rest.data_type(),
&outer_dt,
"LargeListView<Dict>: build() must return the declared type",
);
assert_eq!(rest.len(), 1);
}
#[test]
fn list_view_of_dict_groups_by_logical_value() {
use arrow::array::{DictionaryArray, ListViewArray, StringArray};
use arrow::buffer::ScalarBuffer;
let item_field = Arc::new(Field::new("item", dict_utf8(), true));
let outer_dt = DataType::ListView(Arc::clone(&item_field));
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let values = Arc::new(StringArray::from(vec!["a", "b", "a", "b", "c"]));
let keys = Int32Array::from(vec![0, 1, 2, 3, 4]);
let dict = DictionaryArray::<Int32Type>::try_new(keys, values).unwrap();
let offsets = ScalarBuffer::<i32>::from(vec![0, 2, 4]);
let sizes = ScalarBuffer::<i32>::from(vec![2, 2, 1]);
let list = ListViewArray::try_new(
Arc::clone(&item_field),
offsets,
sizes,
Arc::new(dict),
None,
)
.unwrap();
let input: ArrayRef = Arc::new(list);
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
col.vectorized_append(&input, &[0]).unwrap();
assert!(
col.equal_to(0, &input, 1),
"identical logical lists must be equal regardless of dict keys",
);
assert!(
!col.equal_to(0, &input, 2),
"different logical lists must not be equal",
);
col.vectorized_append(&input, &[2]).unwrap();
assert_eq!(col.len(), 2, "3 input rows → 2 distinct groups");
let built = col.build();
assert_eq!(built.data_type(), &outer_dt);
assert_eq!(built.len(), 2);
}
#[test]
fn build_preserves_map_of_dictionary_schema() {
use arrow::array::{
DictionaryArray, Int32Array, MapArray, StringArray, StructArray,
};
use arrow::buffer::OffsetBuffer;
let key_field = Arc::new(Field::new("keys", DataType::Int32, false));
let value_field = Arc::new(Field::new("values", dict_utf8(), true));
let entries_field = Arc::new(Field::new(
"entries",
DataType::Struct(vec![(*key_field).clone(), (*value_field).clone()].into()),
false,
));
let outer_dt = DataType::Map(Arc::clone(&entries_field), false);
if !RowsGroupColumn::supports_type(&outer_dt) {
return;
}
let mut col = Box::new(RowsGroupColumn::try_new(outer_dt.clone()).unwrap());
let keys = Arc::new(Int32Array::from(vec![1])) as ArrayRef;
let values_arr = Arc::new(StringArray::from(vec!["a"]));
let value_keys = Int32Array::from(vec![0]);
let value_dict =
DictionaryArray::<Int32Type>::try_new(value_keys, values_arr).unwrap();
let entries = StructArray::try_new(
vec![(*key_field).clone(), (*value_field).clone()].into(),
vec![keys, Arc::new(value_dict)],
None,
)
.unwrap();
let offsets = OffsetBuffer::<i32>::from_lengths([1]);
let map =
MapArray::try_new(Arc::clone(&entries_field), offsets, entries, None, false)
.unwrap();
col.vectorized_append(&(Arc::new(map) as ArrayRef), &[0])
.unwrap();
let built = col.build();
assert_eq!(
built.data_type(),
&outer_dt,
"Map<..., Dict>: build() must preserve the declared type",
);
}
#[test]
fn supports_type_rejects_union() {
use arrow::datatypes::UnionFields;
let fields = UnionFields::try_new(
vec![0_i8, 1_i8],
vec![
Field::new("a", DataType::Int32, true),
Field::new("b", DataType::Utf8, true),
],
)
.unwrap();
let dt = DataType::Union(fields, arrow::datatypes::UnionMode::Dense);
assert!(
!RowsGroupColumn::supports_type(&dt),
"Union must fall back to GroupValuesRows until arrow-row \
round-trip is covered by our tests",
);
}
#[test]
fn supports_type_rejects_run_end_encoded_with_nested_values() {
let list_of_i32 =
DataType::List(Arc::new(Field::new("item", DataType::Int32, true)));
let dt = DataType::RunEndEncoded(
Arc::new(Field::new("run_ends", DataType::Int32, false)),
Arc::new(Field::new("values", list_of_i32, true)),
);
assert!(!RowsGroupColumn::supports_type(&dt));
}
#[test]
fn supports_type_rejects_run_end_encoded_with_scalar_values() {
let dt = DataType::RunEndEncoded(
Arc::new(Field::new("run_ends", DataType::Int32, false)),
Arc::new(Field::new("values", DataType::Utf8, true)),
);
assert!(!RowsGroupColumn::supports_type(&dt));
}
#[test]
fn supports_type_rejects_ree_hidden_under_outer_wrapper() {
let ree = DataType::RunEndEncoded(
Arc::new(Field::new("run_ends", DataType::Int32, false)),
Arc::new(Field::new("values", DataType::Utf8, true)),
);
let outer = DataType::Struct(vec![Field::new("f", ree, true)].into());
assert!(!RowsGroupColumn::supports_type(&outer));
}
#[test]
fn supports_type_accepts_plain_list_and_struct_still() {
let list_of_int =
DataType::List(Arc::new(Field::new("item", DataType::Int32, true)));
assert!(RowsGroupColumn::supports_type(&list_of_int));
let struct_of_prims = DataType::Struct(
vec![
Field::new("a", DataType::Int32, true),
Field::new("b", DataType::Utf8, true),
]
.into(),
);
assert!(RowsGroupColumn::supports_type(&struct_of_prims));
}
}