Skip to main content

corium_sql/
value.rs

1use std::fmt;
2
3use arrow::array::Array as _;
4use arrow::datatypes::{DataType, Field, TimeUnit};
5use datafusion::common::ScalarValue;
6
7use crate::SqlError;
8
9/// SQL result type independent of `DataFusion` and Arrow versions.
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub enum SqlType {
12    /// SQL NULL.
13    Null,
14    /// Boolean.
15    Boolean,
16    /// Signed integer, with its bit width.
17    SignedInteger(u8),
18    /// Unsigned integer, with its bit width.
19    UnsignedInteger(u8),
20    /// Floating-point value, with its bit width.
21    Float(u8),
22    /// Milliseconds since the Unix epoch, with an optional timezone.
23    TimestampMillis(Option<String>),
24    /// UTF-8 text.
25    Text,
26    /// Arbitrary bytes.
27    Bytes,
28    /// Variable-length list.
29    List(Box<Self>),
30    /// An engine type not yet represented structurally by this API.
31    Other(String),
32}
33
34impl SqlType {
35    pub(crate) fn from_arrow(data_type: &DataType) -> Self {
36        match data_type {
37            DataType::Null => Self::Null,
38            DataType::Boolean => Self::Boolean,
39            DataType::Int8 => Self::SignedInteger(8),
40            DataType::Int16 => Self::SignedInteger(16),
41            DataType::Int32 => Self::SignedInteger(32),
42            DataType::Int64 => Self::SignedInteger(64),
43            DataType::UInt8 => Self::UnsignedInteger(8),
44            DataType::UInt16 => Self::UnsignedInteger(16),
45            DataType::UInt32 => Self::UnsignedInteger(32),
46            DataType::UInt64 => Self::UnsignedInteger(64),
47            DataType::Float16 => Self::Float(16),
48            DataType::Float32 => Self::Float(32),
49            DataType::Float64 => Self::Float(64),
50            DataType::Timestamp(TimeUnit::Millisecond, timezone) => {
51                Self::TimestampMillis(timezone.as_ref().map(ToString::to_string))
52            }
53            DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => Self::Text,
54            DataType::Binary
55            | DataType::LargeBinary
56            | DataType::BinaryView
57            | DataType::FixedSizeBinary(_) => Self::Bytes,
58            DataType::List(field) | DataType::LargeList(field) => {
59                Self::List(Box::new(Self::from_arrow(field.data_type())))
60            }
61            other => Self::Other(other.to_string()),
62        }
63    }
64}
65
66/// One result column.
67#[derive(Clone, Debug, Eq, PartialEq)]
68pub struct SqlColumn {
69    /// Projected column name.
70    pub name: String,
71    /// SQL result type.
72    pub data_type: SqlType,
73    /// Whether the result may contain NULL.
74    pub nullable: bool,
75}
76
77impl SqlColumn {
78    pub(crate) fn from_arrow(field: &Field) -> Self {
79        Self {
80            name: field.name().clone(),
81            data_type: SqlType::from_arrow(field.data_type()),
82            nullable: field.is_nullable(),
83        }
84    }
85}
86
87/// One SQL value independent of `DataFusion` and Arrow versions.
88#[derive(Clone, Debug, PartialEq)]
89pub enum SqlValue {
90    /// SQL NULL.
91    Null,
92    /// Boolean.
93    Boolean(bool),
94    /// Signed integer normalized to 64 bits.
95    Integer(i64),
96    /// Unsigned integer normalized to 64 bits.
97    Unsigned(u64),
98    /// Floating-point value normalized to 64 bits.
99    Float(f64),
100    /// Milliseconds since the Unix epoch.
101    TimestampMillis(i64),
102    /// UTF-8 text.
103    Text(String),
104    /// Arbitrary bytes.
105    Bytes(Vec<u8>),
106    /// Variable-length list. Corium cardinality-many columns use unique,
107    /// deterministically ordered lists with set semantics.
108    List(Vec<Self>),
109    /// Text rendering of an engine value not yet represented structurally.
110    Other(String),
111}
112
113/// One SQL result row.
114pub type SqlRow = Vec<SqlValue>;
115
116impl SqlValue {
117    pub(crate) fn from_scalar(value: ScalarValue) -> Result<Self, SqlError> {
118        Ok(match value {
119            ScalarValue::Null
120            | ScalarValue::Boolean(None)
121            | ScalarValue::Int8(None)
122            | ScalarValue::Int16(None)
123            | ScalarValue::Int32(None)
124            | ScalarValue::Int64(None)
125            | ScalarValue::UInt8(None)
126            | ScalarValue::UInt16(None)
127            | ScalarValue::UInt32(None)
128            | ScalarValue::UInt64(None)
129            | ScalarValue::Float16(None)
130            | ScalarValue::Float32(None)
131            | ScalarValue::Float64(None)
132            | ScalarValue::Utf8(None)
133            | ScalarValue::Utf8View(None)
134            | ScalarValue::LargeUtf8(None)
135            | ScalarValue::Binary(None)
136            | ScalarValue::BinaryView(None)
137            | ScalarValue::LargeBinary(None)
138            | ScalarValue::TimestampMillisecond(None, _)
139            | ScalarValue::FixedSizeBinary(_, None) => Self::Null,
140            ScalarValue::Boolean(Some(value)) => Self::Boolean(value),
141            ScalarValue::Int8(Some(value)) => Self::Integer(i64::from(value)),
142            ScalarValue::Int16(Some(value)) => Self::Integer(i64::from(value)),
143            ScalarValue::Int32(Some(value)) => Self::Integer(i64::from(value)),
144            ScalarValue::Int64(Some(value)) => Self::Integer(value),
145            ScalarValue::UInt8(Some(value)) => Self::Unsigned(u64::from(value)),
146            ScalarValue::UInt16(Some(value)) => Self::Unsigned(u64::from(value)),
147            ScalarValue::UInt32(Some(value)) => Self::Unsigned(u64::from(value)),
148            ScalarValue::UInt64(Some(value)) => Self::Unsigned(value),
149            ScalarValue::Float16(Some(value)) => Self::Float(f64::from(value)),
150            ScalarValue::Float32(Some(value)) => Self::Float(f64::from(value)),
151            ScalarValue::Float64(Some(value)) => Self::Float(value),
152            ScalarValue::TimestampMillisecond(Some(value), _) => Self::TimestampMillis(value),
153            ScalarValue::Utf8(Some(value))
154            | ScalarValue::Utf8View(Some(value))
155            | ScalarValue::LargeUtf8(Some(value)) => Self::Text(value),
156            ScalarValue::Binary(Some(value))
157            | ScalarValue::BinaryView(Some(value))
158            | ScalarValue::LargeBinary(Some(value))
159            | ScalarValue::FixedSizeBinary(_, Some(value)) => Self::Bytes(value),
160            ScalarValue::List(array) => {
161                if array.is_null(0) {
162                    Self::Null
163                } else {
164                    let values = array.value(0);
165                    let mut result = Vec::with_capacity(values.len());
166                    for index in 0..values.len() {
167                        result.push(Self::from_scalar(ScalarValue::try_from_array(
168                            values.as_ref(),
169                            index,
170                        )?)?);
171                    }
172                    Self::List(result)
173                }
174            }
175            other => Self::Other(other.to_string()),
176        })
177    }
178}
179
180impl fmt::Display for SqlValue {
181    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
182        match self {
183            Self::Null => formatter.write_str("NULL"),
184            Self::Boolean(value) => write!(formatter, "{value}"),
185            Self::Integer(value) | Self::TimestampMillis(value) => write!(formatter, "{value}"),
186            Self::Unsigned(value) => write!(formatter, "{value}"),
187            Self::Float(value) => write!(formatter, "{value}"),
188            Self::Text(value) | Self::Other(value) => formatter.write_str(value),
189            Self::Bytes(value) => {
190                for byte in value {
191                    write!(formatter, "{byte:02x}")?;
192                }
193                Ok(())
194            }
195            Self::List(values) => {
196                formatter.write_str("[")?;
197                for (index, value) in values.iter().enumerate() {
198                    if index > 0 {
199                        formatter.write_str(", ")?;
200                    }
201                    write!(formatter, "{value}")?;
202                }
203                formatter.write_str("]")
204            }
205        }
206    }
207}