use crate::error::Error::IllegalArgument;
use crate::error::Result;
use crate::row::Decimal;
use crate::row::column::ColumnarRow;
use crate::row::column_vector::TypedColumn;
use crate::row::datum::{Date, Time, TimestampLtz, TimestampNtz};
use crate::row::view::{ArrayView, MapView, RowView};
use crate::row::{DataGetters, InternalArray, InternalMap};
use std::sync::Arc;
#[derive(Debug, Clone, Copy)]
pub struct ColumnarArray<'a> {
element: &'a TypedColumn,
start: usize,
length: usize,
}
impl<'a> ColumnarArray<'a> {
pub(crate) fn new(element: &'a TypedColumn, start: usize, length: usize) -> Self {
Self {
element,
start,
length,
}
}
pub(crate) fn element(&self) -> &'a TypedColumn {
self.element
}
fn absolute_index(&self, pos: usize) -> Result<usize> {
if pos >= self.length {
return Err(IllegalArgument {
message: format!(
"columnar array index out of bounds: pos={pos}, length={}",
self.length
),
});
}
Ok(self.start + pos)
}
}
impl InternalArray for ColumnarArray<'_> {
fn size(&self) -> usize {
self.length
}
}
impl DataGetters for ColumnarArray<'_> {
fn is_null_at(&self, pos: usize) -> Result<bool> {
let abs = self.absolute_index(pos)?;
Ok(self.element.is_null_at(abs))
}
fn get_boolean(&self, pos: usize) -> Result<bool> {
self.element.get_boolean(self.absolute_index(pos)?)
}
fn get_byte(&self, pos: usize) -> Result<i8> {
self.element.get_byte(self.absolute_index(pos)?)
}
fn get_short(&self, pos: usize) -> Result<i16> {
self.element.get_short(self.absolute_index(pos)?)
}
fn get_int(&self, pos: usize) -> Result<i32> {
self.element.get_int(self.absolute_index(pos)?)
}
fn get_long(&self, pos: usize) -> Result<i64> {
self.element.get_long(self.absolute_index(pos)?)
}
fn get_float(&self, pos: usize) -> Result<f32> {
self.element.get_float(self.absolute_index(pos)?)
}
fn get_double(&self, pos: usize) -> Result<f64> {
self.element.get_double(self.absolute_index(pos)?)
}
fn get_char(&self, pos: usize, _length: usize) -> Result<&str> {
self.element.get_char(self.absolute_index(pos)?)
}
fn get_string(&self, pos: usize) -> Result<&str> {
self.element.get_string(self.absolute_index(pos)?)
}
fn get_decimal(&self, pos: usize, precision: usize, scale: usize) -> Result<Decimal> {
self.element
.get_decimal(self.absolute_index(pos)?, precision as u32, scale as u32)
}
fn get_date(&self, pos: usize) -> Result<Date> {
self.element.get_date(self.absolute_index(pos)?)
}
fn get_time(&self, pos: usize) -> Result<Time> {
self.element.get_time(self.absolute_index(pos)?)
}
fn get_timestamp_ntz(&self, pos: usize, _precision: u32) -> Result<TimestampNtz> {
self.element.get_timestamp_ntz(self.absolute_index(pos)?)
}
fn get_timestamp_ltz(&self, pos: usize, _precision: u32) -> Result<TimestampLtz> {
self.element.get_timestamp_ltz(self.absolute_index(pos)?)
}
fn get_binary(&self, pos: usize, _length: usize) -> Result<&[u8]> {
self.element.get_binary(self.absolute_index(pos)?)
}
fn get_bytes(&self, pos: usize) -> Result<&[u8]> {
self.element.get_bytes(self.absolute_index(pos)?)
}
fn get_array(&self, pos: usize) -> Result<ArrayView<'_>> {
let abs = self.absolute_index(pos)?;
match self.element {
TypedColumn::Array(list_arr, inner) => {
let start = list_arr.value_offsets()[abs] as usize;
let end = list_arr.value_offsets()[abs + 1] as usize;
Ok(ArrayView::Columnar(ColumnarArray::new(
inner.as_ref(),
start,
end - start,
)))
}
other => Err(IllegalArgument {
message: format!("expected ARRAY element at position {pos}, got {other:?}"),
}),
}
}
fn get_map(&self, pos: usize) -> Result<MapView<'_>> {
let abs = self.absolute_index(pos)?;
match self.element {
TypedColumn::Map(map_arr, keys, values) => {
let start = map_arr.value_offsets()[abs] as usize;
let end = map_arr.value_offsets()[abs + 1] as usize;
Ok(MapView::Columnar(ColumnarMap::new(
keys.as_ref(),
values.as_ref(),
start,
end - start,
)))
}
other => Err(IllegalArgument {
message: format!("expected MAP element at position {pos}, got {other:?}"),
}),
}
}
fn get_row(&self, pos: usize) -> Result<RowView<'_>> {
let abs = self.absolute_index(pos)?;
match self.element {
TypedColumn::Row(_, inner_batch) => Ok(RowView::Columnar(
ColumnarRow::from_typed_batch(Arc::clone(inner_batch), abs),
)),
other => Err(IllegalArgument {
message: format!("expected ROW element at position {pos}, got {other:?}"),
}),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ColumnarMap<'a> {
keys: ColumnarArray<'a>,
values: ColumnarArray<'a>,
}
impl<'a> ColumnarMap<'a> {
pub(crate) fn new(
keys_column: &'a TypedColumn,
values_column: &'a TypedColumn,
start: usize,
length: usize,
) -> Self {
Self {
keys: ColumnarArray::new(keys_column, start, length),
values: ColumnarArray::new(values_column, start, length),
}
}
pub(crate) fn keys_slice(&self) -> &ColumnarArray<'a> {
&self.keys
}
pub(crate) fn values_slice(&self) -> &ColumnarArray<'a> {
&self.values
}
}
impl InternalMap for ColumnarMap<'_> {
fn size(&self) -> usize {
self.keys.size()
}
fn key_array(&self) -> &dyn InternalArray {
&self.keys
}
fn value_array(&self) -> &dyn InternalArray {
&self.values
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::{DataField, DataTypes, RowType};
use crate::row::column_vector::TypedBatch;
use arrow::array::{
Array, ArrayRef, Int32Array, ListArray, MapArray, RecordBatch, StringArray, StructArray,
};
use arrow::buffer::OffsetBuffer;
use arrow::datatypes::{DataType as ArrowDataType, Field, Fields, Schema};
use std::sync::Arc;
fn list_int_batch() -> (ListArray, Arc<TypedBatch>) {
let values = Int32Array::from(vec![10, 20, 30, 40, 50]);
let offsets = OffsetBuffer::new(vec![0_i32, 2, 5].into());
let list = ListArray::new(
Arc::new(Field::new("item", ArrowDataType::Int32, true)),
offsets,
Arc::new(values),
None,
);
let rb = RecordBatch::try_new(
Arc::new(Schema::new(vec![Field::new(
"arr",
list.data_type().clone(),
true,
)])),
vec![Arc::new(list.clone()) as ArrayRef],
)
.expect("batch");
let rt = RowType::new(vec![DataField::new(
"arr",
DataTypes::array(DataTypes::int()),
None,
)]);
(list, Arc::new(TypedBatch::build(&rb, &rt).expect("build")))
}
#[test]
fn columnar_array_reads_a_slice_of_the_element_vector() {
let (list, tb) = list_int_batch();
let element = match &tb.columns[0] {
TypedColumn::Array(_, e) => e.as_ref(),
_ => unreachable!(),
};
let start = list.value_offsets()[1] as usize;
let end = list.value_offsets()[2] as usize;
let arr = ColumnarArray::new(element, start, end - start);
assert_eq!(arr.size(), 3);
assert!(!arr.is_null_at(0).unwrap());
assert_eq!(arr.get_int(0).unwrap(), 30);
assert_eq!(arr.get_int(1).unwrap(), 40);
assert_eq!(arr.get_int(2).unwrap(), 50);
}
#[test]
fn columnar_array_out_of_bounds_is_clear_error() {
let (list, tb) = list_int_batch();
let element = match &tb.columns[0] {
TypedColumn::Array(_, e) => e.as_ref(),
_ => unreachable!(),
};
let start = list.value_offsets()[0] as usize;
let end = list.value_offsets()[1] as usize;
let arr = ColumnarArray::new(element, start, end - start);
let err = arr.get_int(5).unwrap_err().to_string();
assert!(err.contains("out of bounds"), "{err}");
}
fn map_string_int_batch() -> (MapArray, Arc<TypedBatch>) {
let keys = StringArray::from(vec!["a", "b"]);
let values = Int32Array::from(vec![1, 2]);
let entries_fields = Fields::from(vec![
Field::new("keys", ArrowDataType::Utf8, false),
Field::new("values", ArrowDataType::Int32, true),
]);
let entries = StructArray::new(
entries_fields.clone(),
vec![Arc::new(keys) as ArrayRef, Arc::new(values) as ArrayRef],
None,
);
let offsets = OffsetBuffer::new(vec![0_i32, 2].into());
let map = MapArray::new(
Arc::new(Field::new(
"entries",
ArrowDataType::Struct(entries_fields),
false,
)),
offsets,
entries,
None,
false,
);
let rb = RecordBatch::try_new(
Arc::new(Schema::new(vec![Field::new(
"m",
map.data_type().clone(),
true,
)])),
vec![Arc::new(map.clone()) as ArrayRef],
)
.expect("batch");
let rt = RowType::new(vec![DataField::new(
"m",
DataTypes::map(DataTypes::string(), DataTypes::int()),
None,
)]);
(map, Arc::new(TypedBatch::build(&rb, &rt).expect("build")))
}
#[test]
fn columnar_map_exposes_key_and_value_slices() {
let (map, tb) = map_string_int_batch();
let (key_col, value_col) = match &tb.columns[0] {
TypedColumn::Map(_, k, v) => (k.as_ref(), v.as_ref()),
_ => unreachable!(),
};
let start = map.value_offsets()[0] as usize;
let end = map.value_offsets()[1] as usize;
let cm = ColumnarMap::new(key_col, value_col, start, end - start);
assert_eq!(cm.size(), 2);
let keys = cm.key_array();
let values = cm.value_array();
assert_eq!(keys.size(), 2);
assert_eq!(keys.get_string(0).unwrap(), "a");
assert_eq!(keys.get_string(1).unwrap(), "b");
assert_eq!(values.get_int(0).unwrap(), 1);
assert_eq!(values.get_int(1).unwrap(), 2);
}
}