use std::fmt;
use arrow::array::Array as _;
use arrow::datatypes::{DataType, Field, TimeUnit};
use datafusion::common::ScalarValue;
use crate::SqlError;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SqlType {
Null,
Boolean,
SignedInteger(u8),
UnsignedInteger(u8),
Float(u8),
TimestampMillis(Option<String>),
Text,
Bytes,
List(Box<Self>),
Other(String),
}
impl SqlType {
pub(crate) fn from_arrow(data_type: &DataType) -> Self {
match data_type {
DataType::Null => Self::Null,
DataType::Boolean => Self::Boolean,
DataType::Int8 => Self::SignedInteger(8),
DataType::Int16 => Self::SignedInteger(16),
DataType::Int32 => Self::SignedInteger(32),
DataType::Int64 => Self::SignedInteger(64),
DataType::UInt8 => Self::UnsignedInteger(8),
DataType::UInt16 => Self::UnsignedInteger(16),
DataType::UInt32 => Self::UnsignedInteger(32),
DataType::UInt64 => Self::UnsignedInteger(64),
DataType::Float16 => Self::Float(16),
DataType::Float32 => Self::Float(32),
DataType::Float64 => Self::Float(64),
DataType::Timestamp(TimeUnit::Millisecond, timezone) => {
Self::TimestampMillis(timezone.as_ref().map(ToString::to_string))
}
DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => Self::Text,
DataType::Binary
| DataType::LargeBinary
| DataType::BinaryView
| DataType::FixedSizeBinary(_) => Self::Bytes,
DataType::List(field) | DataType::LargeList(field) => {
Self::List(Box::new(Self::from_arrow(field.data_type())))
}
other => Self::Other(other.to_string()),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SqlColumn {
pub name: String,
pub data_type: SqlType,
pub nullable: bool,
}
impl SqlColumn {
pub(crate) fn from_arrow(field: &Field) -> Self {
Self {
name: field.name().clone(),
data_type: SqlType::from_arrow(field.data_type()),
nullable: field.is_nullable(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SqlValue {
Null,
Boolean(bool),
Integer(i64),
Unsigned(u64),
Float(f64),
TimestampMillis(i64),
Text(String),
Bytes(Vec<u8>),
List(Vec<Self>),
Other(String),
}
pub type SqlRow = Vec<SqlValue>;
impl SqlValue {
pub(crate) fn from_scalar(value: ScalarValue) -> Result<Self, SqlError> {
Ok(match value {
ScalarValue::Null
| ScalarValue::Boolean(None)
| ScalarValue::Int8(None)
| ScalarValue::Int16(None)
| ScalarValue::Int32(None)
| ScalarValue::Int64(None)
| ScalarValue::UInt8(None)
| ScalarValue::UInt16(None)
| ScalarValue::UInt32(None)
| ScalarValue::UInt64(None)
| ScalarValue::Float16(None)
| ScalarValue::Float32(None)
| ScalarValue::Float64(None)
| ScalarValue::Utf8(None)
| ScalarValue::Utf8View(None)
| ScalarValue::LargeUtf8(None)
| ScalarValue::Binary(None)
| ScalarValue::BinaryView(None)
| ScalarValue::LargeBinary(None)
| ScalarValue::TimestampMillisecond(None, _)
| ScalarValue::FixedSizeBinary(_, None) => Self::Null,
ScalarValue::Boolean(Some(value)) => Self::Boolean(value),
ScalarValue::Int8(Some(value)) => Self::Integer(i64::from(value)),
ScalarValue::Int16(Some(value)) => Self::Integer(i64::from(value)),
ScalarValue::Int32(Some(value)) => Self::Integer(i64::from(value)),
ScalarValue::Int64(Some(value)) => Self::Integer(value),
ScalarValue::UInt8(Some(value)) => Self::Unsigned(u64::from(value)),
ScalarValue::UInt16(Some(value)) => Self::Unsigned(u64::from(value)),
ScalarValue::UInt32(Some(value)) => Self::Unsigned(u64::from(value)),
ScalarValue::UInt64(Some(value)) => Self::Unsigned(value),
ScalarValue::Float16(Some(value)) => Self::Float(f64::from(value)),
ScalarValue::Float32(Some(value)) => Self::Float(f64::from(value)),
ScalarValue::Float64(Some(value)) => Self::Float(value),
ScalarValue::TimestampMillisecond(Some(value), _) => Self::TimestampMillis(value),
ScalarValue::Utf8(Some(value))
| ScalarValue::Utf8View(Some(value))
| ScalarValue::LargeUtf8(Some(value)) => Self::Text(value),
ScalarValue::Binary(Some(value))
| ScalarValue::BinaryView(Some(value))
| ScalarValue::LargeBinary(Some(value))
| ScalarValue::FixedSizeBinary(_, Some(value)) => Self::Bytes(value),
ScalarValue::List(array) => {
if array.is_null(0) {
Self::Null
} else {
let values = array.value(0);
let mut result = Vec::with_capacity(values.len());
for index in 0..values.len() {
result.push(Self::from_scalar(ScalarValue::try_from_array(
values.as_ref(),
index,
)?)?);
}
Self::List(result)
}
}
other => Self::Other(other.to_string()),
})
}
}
impl fmt::Display for SqlValue {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => formatter.write_str("NULL"),
Self::Boolean(value) => write!(formatter, "{value}"),
Self::Integer(value) | Self::TimestampMillis(value) => write!(formatter, "{value}"),
Self::Unsigned(value) => write!(formatter, "{value}"),
Self::Float(value) => write!(formatter, "{value}"),
Self::Text(value) | Self::Other(value) => formatter.write_str(value),
Self::Bytes(value) => {
for byte in value {
write!(formatter, "{byte:02x}")?;
}
Ok(())
}
Self::List(values) => {
formatter.write_str("[")?;
for (index, value) in values.iter().enumerate() {
if index > 0 {
formatter.write_str(", ")?;
}
write!(formatter, "{value}")?;
}
formatter.write_str("]")
}
}
}
}