velesdb-core 1.15.0

High-performance vector database engine written in Rust
Documentation
//! Value types for VelesQL expressions.
//!
//! This module defines values, vectors, temporal expressions,
//! and subquery types used in VelesQL queries.

use serde::{Deserialize, Serialize};

/// Vector expression in a NEAR clause.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum VectorExpr {
    /// Literal vector: [0.1, 0.2, ...]
    Literal(Vec<f32>),
    /// Parameter reference: `$param_name`
    Parameter(String),
}

/// A value in VelesQL.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
    /// Integer value.
    Integer(i64),
    /// Unsigned integer value for values exceeding `i64::MAX` (issue #486).
    UnsignedInteger(u64),
    /// Float value.
    Float(f64),
    /// String value.
    String(String),
    /// Boolean value.
    Boolean(bool),
    /// Null value.
    Null,
    /// Parameter reference.
    Parameter(String),
    /// Temporal function (EPIC-038).
    Temporal(TemporalExpr),
    /// Scalar subquery (EPIC-039).
    Subquery(Box<Subquery>),
}

impl From<i64> for Value {
    fn from(v: i64) -> Self {
        Self::Integer(v)
    }
}

impl From<u64> for Value {
    fn from(v: u64) -> Self {
        Self::UnsignedInteger(v)
    }
}

impl From<f64> for Value {
    fn from(v: f64) -> Self {
        Self::Float(v)
    }
}

impl From<&str> for Value {
    fn from(v: &str) -> Self {
        Self::String(v.to_string())
    }
}

impl From<String> for Value {
    fn from(v: String) -> Self {
        Self::String(v)
    }
}

impl From<bool> for Value {
    fn from(v: bool) -> Self {
        Self::Boolean(v)
    }
}

/// Scalar subquery expression (EPIC-039).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Subquery {
    /// The SELECT statement of the subquery.
    pub select: super::select::SelectStatement,
    /// Correlated columns (references to outer query).
    #[serde(default)]
    pub correlations: Vec<CorrelatedColumn>,
}

/// A correlated column reference in a subquery (EPIC-039).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CorrelatedColumn {
    /// Outer query table/alias reference.
    pub outer_table: String,
    /// Column name in outer query.
    pub outer_column: String,
    /// Column in subquery that references it.
    pub inner_column: String,
}

/// Temporal expression for date/time operations (EPIC-038).
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TemporalExpr {
    /// Current timestamp: `NOW()`
    Now,
    /// Interval expression: `INTERVAL '7 days'`
    Interval(IntervalValue),
    /// Arithmetic: `NOW() - INTERVAL '7 days'`
    Subtract(Box<TemporalExpr>, Box<TemporalExpr>),
    /// Arithmetic: `NOW() + INTERVAL '1 hour'`
    Add(Box<TemporalExpr>, Box<TemporalExpr>),
}

impl TemporalExpr {
    /// Evaluates the temporal expression to epoch seconds.
    #[must_use]
    pub fn to_epoch_seconds(&self) -> i64 {
        use std::time::{SystemTime, UNIX_EPOCH};

        // Reason: Current Unix timestamps fit in i64 until year 292 billion.
        // Use saturating conversion for theoretical future-proofing.
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map_or(0, |d| i64::try_from(d.as_secs()).unwrap_or(i64::MAX));

        match self {
            Self::Now => now,
            Self::Interval(iv) => iv.to_seconds(),
            Self::Subtract(left, right) => left.to_epoch_seconds() - right.to_epoch_seconds(),
            Self::Add(left, right) => left.to_epoch_seconds() + right.to_epoch_seconds(),
        }
    }
}

/// Interval value with magnitude and unit (EPIC-038).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct IntervalValue {
    /// Numeric magnitude.
    pub magnitude: i64,
    /// Time unit.
    pub unit: IntervalUnit,
}

impl IntervalValue {
    /// Converts the interval to seconds.
    #[must_use]
    pub fn to_seconds(&self) -> i64 {
        match self.unit {
            IntervalUnit::Seconds => self.magnitude,
            IntervalUnit::Minutes => self.magnitude * 60,
            IntervalUnit::Hours => self.magnitude * 3600,
            IntervalUnit::Days => self.magnitude * 86400,
            IntervalUnit::Weeks => self.magnitude * 604_800,
            IntervalUnit::Months => self.magnitude * 2_592_000,
        }
    }
}

impl Value {
    /// Converts this VelesQL value to a JSON value.
    ///
    /// Literal values (integer, float, string, boolean, null) map directly
    /// to their JSON equivalents. Parameters serialize as `"$name"` strings.
    /// Temporal expressions evaluate to epoch-seconds integers.
    /// Subqueries are not serializable and produce `Value::Null`.
    #[must_use]
    pub fn to_json(&self) -> serde_json::Value {
        match self {
            Self::Integer(i) => serde_json::json!(i),
            Self::UnsignedInteger(u) => serde_json::json!(u),
            Self::Float(f) => serde_json::json!(f),
            Self::String(s) => serde_json::json!(s),
            Self::Boolean(b) => serde_json::json!(b),
            Self::Parameter(p) => serde_json::json!(format!("${p}")),
            Self::Temporal(t) => serde_json::json!(t.to_epoch_seconds()),
            Self::Null | Self::Subquery(_) => serde_json::Value::Null,
        }
    }
}

/// Time unit for INTERVAL expressions (EPIC-038).
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IntervalUnit {
    /// Seconds.
    Seconds,
    /// Minutes.
    Minutes,
    /// Hours.
    Hours,
    /// Days.
    Days,
    /// Weeks.
    Weeks,
    /// Months.
    Months,
}