use crate::DbkitError;
use crate::analytical::RecordBatch;
use crate::analytical::arrow::array::{
ArrayRef, BinaryBuilder, BooleanBuilder, Float32Builder, Float64Builder, Int16Builder,
Int32Builder, Int64Builder, NullArray, StringBuilder,
};
use crate::analytical::arrow::datatypes::{DataType, Field, Schema};
use sqlx::any::AnyRow;
use sqlx::{Column, Row, TypeInfo};
use std::sync::Arc;
pub(crate) fn rows_to_record_batch(rows: &[AnyRow]) -> Result<Option<RecordBatch>, DbkitError> {
let Some(first) = rows.first() else {
return Ok(None);
};
let columns = first.columns();
let mut arrays: Vec<ArrayRef> = Vec::with_capacity(columns.len());
let mut fields: Vec<Field> = Vec::with_capacity(columns.len());
for (idx, col) in columns.iter().enumerate() {
let (array, dtype) = build_column(rows, idx, col.type_info().name())?;
fields.push(Field::new(col.name(), dtype, true));
arrays.push(array);
}
let schema = Arc::new(Schema::new(fields));
let batch = RecordBatch::try_new(schema, arrays)
.map_err(|e| DbkitError::Conversion(e.to_string()))?;
Ok(Some(batch))
}
fn build_column(
rows: &[AnyRow],
idx: usize,
type_name: &str,
) -> Result<(ArrayRef, DataType), DbkitError> {
macro_rules! build {
($t:ty, $builder:ty, $dtype:expr) => {{
let mut b = <$builder>::with_capacity(rows.len());
for row in rows {
let v: Option<$t> = row
.try_get(idx)
.map_err(|e| DbkitError::Conversion(e.to_string()))?;
match v {
Some(x) => b.append_value(x),
None => b.append_null(),
}
}
Ok((Arc::new(b.finish()) as ArrayRef, $dtype))
}};
}
match type_name {
"BOOLEAN" => build!(bool, BooleanBuilder, DataType::Boolean),
"SMALLINT" => build!(i16, Int16Builder, DataType::Int16),
"INTEGER" => build!(i32, Int32Builder, DataType::Int32),
"BIGINT" => build!(i64, Int64Builder, DataType::Int64),
"REAL" => build!(f32, Float32Builder, DataType::Float32),
"DOUBLE" => build!(f64, Float64Builder, DataType::Float64),
"TEXT" => {
let mut b = StringBuilder::new();
for row in rows {
let v: Option<String> = row
.try_get(idx)
.map_err(|e| DbkitError::Conversion(e.to_string()))?;
match v {
Some(s) => b.append_value(s),
None => b.append_null(),
}
}
Ok((Arc::new(b.finish()) as ArrayRef, DataType::Utf8))
}
"BLOB" => {
let mut b = BinaryBuilder::new();
for row in rows {
let v: Option<Vec<u8>> = row
.try_get(idx)
.map_err(|e| DbkitError::Conversion(e.to_string()))?;
match v {
Some(bytes) => b.append_value(bytes),
None => b.append_null(),
}
}
Ok((Arc::new(b.finish()) as ArrayRef, DataType::Binary))
}
"NULL" => Ok((Arc::new(NullArray::new(rows.len())) as ArrayRef, DataType::Null)),
other => Err(DbkitError::Conversion(format!(
"unsupported column type for Arrow conversion: {other}"
))),
}
}