use crate::value::Value;
use hamelin_lib::sql::expression::apply::FunctionCallApply;
use hamelin_lib::sql::expression::literal::{
ArrayLiteral, BinaryLiteral, BooleanLiteral, DecimalLiteral, IntegerLiteral, IntervalLiteral,
NullLiteral, RowLiteral, ScientificLiteral, StringLiteral, TimestampLiteral, TupleLiteral,
Unit,
};
use hamelin_lib::translation::ExpressionTranslation;
use hamelin_lib::types::array::Array;
use hamelin_lib::types::decimal_type::Decimal;
use hamelin_lib::types::map::Map;
use hamelin_lib::types::struct_type::Struct;
use hamelin_lib::types::tuple::Tuple;
use hamelin_lib::types::{
BINARY, BOOLEAN, DOUBLE, INT, INTERVAL, STRING, TIMESTAMP, UNKNOWN, VARIANT,
};
use vecmap::VecMap;
impl TryFrom<Value> for ExpressionTranslation {
type Error = anyhow::Error;
fn try_from(value: Value) -> anyhow::Result<Self> {
let (sql, typ) = match value {
Value::Boolean(b) => (BooleanLiteral { value: b }.into(), BOOLEAN),
Value::Int(i) => (
IntegerLiteral {
number: i.to_string(),
}
.into(),
INT,
),
Value::Double(d) => (ScientificLiteral::new(&d.to_string()).into(), DOUBLE),
Value::String(s) => (StringLiteral { value: s }.into(), STRING),
Value::Binary(bytes) => {
let hex_value = bytes
.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>();
(BinaryLiteral::new(&hex_value).into(), BINARY)
}
Value::Decimal(d) => {
let scale_factor = 10_i128.pow(d.scale as u32);
let integer_part = d.unscaled / scale_factor;
let fractional_part = (d.unscaled % scale_factor).abs();
let decimal_string = format!(
"{}.{:0width$}",
integer_part,
fractional_part,
width = d.scale as usize
);
(
DecimalLiteral::new(&decimal_string).into(),
Decimal::new(38, d.scale)?.into(),
)
}
Value::Timestamp(t) => {
let sql = TimestampLiteral::new(*t.instant()).into();
(sql, TIMESTAMP)
}
Value::Interval(duration) => {
let seconds = duration.num_seconds();
(IntervalLiteral::new(seconds, Unit::Second).into(), INTERVAL)
}
Value::CalendarInterval(months) => {
(
IntervalLiteral::new(months as i64, Unit::Month).into(),
INTERVAL,
)
}
Value::Array(elements) => {
let mut sql_elements = Vec::new();
let mut element_types = Vec::new();
for element in elements {
let translation: ExpressionTranslation = element.try_into()?;
element_types.push(translation.typ.clone());
sql_elements.push(translation.sql);
}
let element_type = if element_types.is_empty() {
UNKNOWN
} else {
let mut merged_type = element_types[0].clone();
for elt_type in element_types.iter().skip(1) {
merged_type = merged_type.merge(elt_type.clone()).unwrap_or(UNKNOWN);
}
merged_type
};
(
ArrayLiteral::new(sql_elements).into(),
Array::new(element_type).into(),
)
}
Value::Tuple(elements) => {
let mut sql_elements = Vec::new();
let mut element_types = Vec::new();
for element in elements {
let translation: ExpressionTranslation = element.try_into()?;
element_types.push(translation.typ.clone());
sql_elements.push(translation.sql);
}
(
TupleLiteral::new(sql_elements).into(),
Tuple::new(element_types).into(),
)
}
Value::Struct(fields) => {
let mut sql_values = Vec::new();
let mut field_types = VecMap::new();
for (name, value) in fields {
let translation: ExpressionTranslation = value.try_into()?;
field_types.insert(name.clone(), translation.typ.clone());
sql_values.push(translation.sql);
}
(
RowLiteral::new(sql_values).into(),
Struct::new(field_types).into(),
)
}
Value::Variant(json) => {
let json_string = json.to_string();
let sql = FunctionCallApply::with_two(
"json_extract",
StringLiteral::new(&json_string).into(),
StringLiteral::new("$").into(),
)
.into();
(sql, VARIANT)
}
Value::Map(map) => {
let mut key_elements = Vec::new();
let mut value_elements = Vec::new();
let mut key_type = UNKNOWN;
let mut value_type = UNKNOWN;
for (key, value) in map {
let key_translation: ExpressionTranslation = key.try_into()?;
let value_translation: ExpressionTranslation = value.try_into()?;
key_type = key_translation.typ.clone();
value_type = value_translation.typ.clone();
key_elements.push(key_translation.sql);
value_elements.push(value_translation.sql);
}
let keys_array = ArrayLiteral::new(key_elements).into();
let values_array = ArrayLiteral::new(value_elements).into();
let sql = FunctionCallApply::with_two("map", keys_array, values_array).into();
(sql, Map::new(key_type, value_type).into())
}
Value::Null => (NullLiteral::default().into(), UNKNOWN),
Value::Unknown => (NullLiteral::default().into(), UNKNOWN),
Value::Rows(_) => {
(NullLiteral::default().into(), UNKNOWN)
}
Value::Range(_) => {
(NullLiteral::default().into(), UNKNOWN)
}
Value::Closure(_) => {
(NullLiteral::default().into(), UNKNOWN)
}
};
Ok(ExpressionTranslation::with_defaults(typ, sql))
}
}