use crate::query::ColumnInfo;
use crate::schema::CqlType;
use crate::types::DataType;
use arrow::datatypes::{DataType as ArrowDataType, Field, Fields, TimeUnit};
use std::collections::HashMap;
use std::sync::Arc;
pub(crate) const DECIMAL_FIXED_SCALE: i32 = 9;
pub(crate) const DECIMAL_MAX_PRECISION: u8 = 38;
pub(crate) const ARROW_EXTENSION_NAME_KEY: &str = "ARROW:extension:name";
pub(crate) const ARROW_UUID_EXTENSION_NAME: &str = "arrow.uuid";
pub(crate) fn column_to_field(col: &ColumnInfo) -> Field {
if let Some(cql_type) = &col.cql_type {
if let Some(field) = cql_type_to_arrow_field(&col.name, cql_type, col.nullable) {
return field;
}
}
let arrow_type = data_type_to_arrow(&col.data_type);
Field::new(&col.name, arrow_type, col.nullable)
}
pub(crate) fn cql_type_to_arrow_field(
name: &str,
cql_type: &CqlType,
nullable: bool,
) -> Option<Field> {
match cql_type {
CqlType::Date => Some(Field::new(name, ArrowDataType::Date32, nullable)),
CqlType::Time => Some(Field::new(
name,
ArrowDataType::Time64(TimeUnit::Nanosecond),
nullable,
)),
CqlType::Decimal => Some(Field::new(
name,
ArrowDataType::Decimal128(DECIMAL_MAX_PRECISION, DECIMAL_FIXED_SCALE as i8),
nullable,
)),
CqlType::Varint => {
Some(Field::new(
name,
ArrowDataType::Decimal128(DECIMAL_MAX_PRECISION, 0),
nullable,
))
}
CqlType::Duration => {
Some(Field::new(name, ArrowDataType::Utf8, nullable))
}
CqlType::Uuid | CqlType::TimeUuid => {
let mut meta = HashMap::new();
meta.insert(
ARROW_EXTENSION_NAME_KEY.to_string(),
ARROW_UUID_EXTENSION_NAME.to_string(),
);
Some(Field::new(name, ArrowDataType::FixedSizeBinary(16), nullable).with_metadata(meta))
}
CqlType::Inet => Some(Field::new(name, ArrowDataType::Utf8, nullable)),
CqlType::Counter => Some(Field::new(name, ArrowDataType::Int64, nullable)),
CqlType::List(inner) | CqlType::Set(inner) | CqlType::Vector(inner, _) => {
let item_type = cql_type_to_arrow_data_type(inner);
let item_field = Arc::new(Field::new("item", item_type, true));
Some(Field::new(name, ArrowDataType::List(item_field), nullable))
}
CqlType::Frozen(inner) => cql_type_to_arrow_field(name, inner, nullable),
CqlType::Map(key_type, val_type) => {
let key_arrow = cql_type_to_arrow_data_type(key_type);
let val_arrow = cql_type_to_arrow_data_type(val_type);
let entries_field = Arc::new(Field::new(
"entries",
ArrowDataType::Struct(Fields::from(vec![
Field::new("key", key_arrow, false),
Field::new("value", val_arrow, true),
])),
false,
));
Some(Field::new(
name,
ArrowDataType::Map(entries_field, false),
nullable,
))
}
CqlType::Tuple(element_types) => {
if element_types.is_empty() {
return Some(Field::new(name, ArrowDataType::Utf8, nullable));
}
let struct_type = cql_type_to_arrow_data_type(cql_type);
Some(Field::new(name, struct_type, nullable))
}
CqlType::Udt(_udt_name, udt_fields) => {
if udt_fields.is_empty() {
return Some(Field::new(name, ArrowDataType::Utf8, nullable));
}
let struct_type = cql_type_to_arrow_data_type(cql_type);
Some(Field::new(name, struct_type, nullable))
}
_ => None,
}
}
pub(crate) fn cql_type_to_arrow_data_type(cql_type: &CqlType) -> ArrowDataType {
match cql_type {
CqlType::Boolean => ArrowDataType::Boolean,
CqlType::TinyInt => ArrowDataType::Int8,
CqlType::SmallInt => ArrowDataType::Int16,
CqlType::Int => ArrowDataType::Int32,
CqlType::BigInt => ArrowDataType::Int64,
CqlType::Counter => ArrowDataType::Int64,
CqlType::Float => ArrowDataType::Float32,
CqlType::Double => ArrowDataType::Float64,
CqlType::Text | CqlType::Ascii | CqlType::Varchar => ArrowDataType::Utf8,
CqlType::Blob => ArrowDataType::Binary,
CqlType::Timestamp => ArrowDataType::Timestamp(TimeUnit::Millisecond, Some("UTC".into())),
CqlType::Date => ArrowDataType::Date32,
CqlType::Time => ArrowDataType::Time64(TimeUnit::Nanosecond),
CqlType::Decimal => {
ArrowDataType::Decimal128(DECIMAL_MAX_PRECISION, DECIMAL_FIXED_SCALE as i8)
}
CqlType::Varint => ArrowDataType::Decimal128(DECIMAL_MAX_PRECISION, 0),
CqlType::Duration => ArrowDataType::Utf8,
CqlType::Uuid | CqlType::TimeUuid => ArrowDataType::FixedSizeBinary(16),
CqlType::Inet => ArrowDataType::Utf8,
CqlType::List(inner) | CqlType::Set(inner) | CqlType::Vector(inner, _) => {
let item_type = cql_type_to_arrow_data_type(inner);
ArrowDataType::List(Arc::new(Field::new("item", item_type, true)))
}
CqlType::Frozen(inner) => cql_type_to_arrow_data_type(inner),
CqlType::Map(key_type, val_type) => {
let key_arrow = cql_type_to_arrow_data_type(key_type);
let val_arrow = cql_type_to_arrow_data_type(val_type);
ArrowDataType::Map(
Arc::new(Field::new(
"entries",
ArrowDataType::Struct(Fields::from(vec![
Field::new("key", key_arrow, false),
Field::new("value", val_arrow, true),
])),
false,
)),
false,
)
}
CqlType::Tuple(element_types) => {
if element_types.is_empty() {
return ArrowDataType::Utf8;
}
let struct_fields: Vec<Field> = element_types
.iter()
.enumerate()
.map(|(i, t)| {
Field::new(
format!("field_{i}"),
cql_type_to_arrow_data_type(t),
true, )
})
.collect();
ArrowDataType::Struct(Fields::from(struct_fields))
}
CqlType::Udt(_udt_name, udt_fields) => {
if udt_fields.is_empty() {
return ArrowDataType::Utf8;
}
let struct_fields: Vec<Field> = udt_fields
.iter()
.map(|(field_name, field_type)| {
Field::new(
field_name.as_str(),
cql_type_to_arrow_data_type(field_type),
true, )
})
.collect();
ArrowDataType::Struct(Fields::from(struct_fields))
}
CqlType::Custom(_) => ArrowDataType::Utf8,
}
}
pub(crate) fn data_type_to_arrow(data_type: &DataType) -> ArrowDataType {
match data_type {
DataType::Null => ArrowDataType::Null,
DataType::Boolean => ArrowDataType::Boolean,
DataType::TinyInt => ArrowDataType::Int8,
DataType::SmallInt => ArrowDataType::Int16,
DataType::Integer => ArrowDataType::Int32,
DataType::BigInt => ArrowDataType::Int64,
DataType::Float32 => ArrowDataType::Float32,
DataType::Float => ArrowDataType::Float64,
DataType::Text => ArrowDataType::Utf8,
DataType::Blob => ArrowDataType::Binary,
DataType::Timestamp => ArrowDataType::Timestamp(TimeUnit::Millisecond, Some("UTC".into())),
DataType::Uuid => ArrowDataType::FixedSizeBinary(16),
DataType::Json => ArrowDataType::Utf8,
DataType::List => {
ArrowDataType::List(Arc::new(Field::new("item", ArrowDataType::Utf8, true)))
}
DataType::Set => {
ArrowDataType::List(Arc::new(Field::new("item", ArrowDataType::Utf8, true)))
}
DataType::Map => ArrowDataType::Map(
Arc::new(Field::new(
"entries",
ArrowDataType::Struct(Fields::from(vec![
Field::new("key", ArrowDataType::Utf8, false),
Field::new("value", ArrowDataType::Utf8, true),
])),
false,
)),
false,
),
DataType::Tuple => ArrowDataType::Utf8, DataType::Udt => ArrowDataType::Utf8, DataType::Frozen => ArrowDataType::Utf8,
DataType::Tombstone => ArrowDataType::Utf8,
}
}