hamelin_eval 0.10.13

Expression evaluation for Hamelin query language
Documentation
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) => {
                // Convert Duration to seconds as a decimal
                let seconds = duration.num_seconds();
                (IntervalLiteral::new(seconds, Unit::Second).into(), INTERVAL)
            }
            Value::CalendarInterval(months) => {
                // CalendarInterval is stored as number of 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);
                }

                // Determine array element type by merging all element types
                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) {
                        // Attempt to merge types; fall back to UNKNOWN if incompatible
                        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) => {
                // Stringify JSON and translate to json_extract(json_string, '$')
                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) => {
                // Maps are represented as map(keys_array, values_array)
                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),
            // These types are not currently supported as default parameter values
            // and should likely never be used as such
            Value::Rows(_) => {
                // Rows is used for internal aggregate contexts, not as a literal value
                (NullLiteral::default().into(), UNKNOWN)
            }
            Value::Range(_) => {
                // Range literals are not supported as SQL literals
                (NullLiteral::default().into(), UNKNOWN)
            }
            Value::Closure(_) => {
                // Closures are runtime-only values, not SQL literals
                (NullLiteral::default().into(), UNKNOWN)
            }
        };

        Ok(ExpressionTranslation::with_defaults(typ, sql))
    }
}