Skip to main content

velesdb_core/velesql/ast/
values.rs

1//! Value types for VelesQL expressions.
2//!
3//! This module defines values, vectors, temporal expressions,
4//! and subquery types used in VelesQL queries.
5
6use serde::{Deserialize, Serialize};
7
8/// Vector expression in a NEAR clause.
9#[non_exhaustive]
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub enum VectorExpr {
12    /// Literal vector: [0.1, 0.2, ...]
13    Literal(Vec<f32>),
14    /// Parameter reference: `$param_name`
15    Parameter(String),
16}
17
18/// A value in VelesQL.
19#[non_exhaustive]
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub enum Value {
22    /// Integer value.
23    Integer(i64),
24    /// Unsigned integer value for values exceeding `i64::MAX` (issue #486).
25    UnsignedInteger(u64),
26    /// Float value.
27    Float(f64),
28    /// String value.
29    String(String),
30    /// Boolean value.
31    Boolean(bool),
32    /// Null value.
33    Null,
34    /// Parameter reference.
35    Parameter(String),
36    /// Temporal function (EPIC-038).
37    Temporal(TemporalExpr),
38    /// Scalar subquery (EPIC-039).
39    Subquery(Box<Subquery>),
40}
41
42impl From<i64> for Value {
43    fn from(v: i64) -> Self {
44        Self::Integer(v)
45    }
46}
47
48impl From<u64> for Value {
49    fn from(v: u64) -> Self {
50        Self::UnsignedInteger(v)
51    }
52}
53
54impl From<f64> for Value {
55    fn from(v: f64) -> Self {
56        Self::Float(v)
57    }
58}
59
60impl From<&str> for Value {
61    fn from(v: &str) -> Self {
62        Self::String(v.to_string())
63    }
64}
65
66impl From<String> for Value {
67    fn from(v: String) -> Self {
68        Self::String(v)
69    }
70}
71
72impl From<bool> for Value {
73    fn from(v: bool) -> Self {
74        Self::Boolean(v)
75    }
76}
77
78/// Scalar subquery expression (EPIC-039).
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct Subquery {
81    /// The SELECT statement of the subquery.
82    pub select: super::select::SelectStatement,
83    /// Correlated columns (references to outer query).
84    #[serde(default)]
85    pub correlations: Vec<CorrelatedColumn>,
86}
87
88/// A correlated column reference in a subquery (EPIC-039).
89#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
90pub struct CorrelatedColumn {
91    /// Outer query table/alias reference.
92    pub outer_table: String,
93    /// Column name in outer query.
94    pub outer_column: String,
95    /// Column in subquery that references it.
96    pub inner_column: String,
97}
98
99/// Temporal expression for date/time operations (EPIC-038).
100#[non_exhaustive]
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub enum TemporalExpr {
103    /// Current timestamp: `NOW()`
104    Now,
105    /// Interval expression: `INTERVAL '7 days'`
106    Interval(IntervalValue),
107    /// Arithmetic: `NOW() - INTERVAL '7 days'`
108    Subtract(Box<TemporalExpr>, Box<TemporalExpr>),
109    /// Arithmetic: `NOW() + INTERVAL '1 hour'`
110    Add(Box<TemporalExpr>, Box<TemporalExpr>),
111}
112
113impl TemporalExpr {
114    /// Evaluates the temporal expression to epoch seconds.
115    #[must_use]
116    pub fn to_epoch_seconds(&self) -> i64 {
117        use std::time::{SystemTime, UNIX_EPOCH};
118
119        // Reason: Current Unix timestamps fit in i64 until year 292 billion.
120        // Use saturating conversion for theoretical future-proofing.
121        let now = SystemTime::now()
122            .duration_since(UNIX_EPOCH)
123            .map_or(0, |d| i64::try_from(d.as_secs()).unwrap_or(i64::MAX));
124
125        match self {
126            Self::Now => now,
127            Self::Interval(iv) => iv.to_seconds(),
128            Self::Subtract(left, right) => left.to_epoch_seconds() - right.to_epoch_seconds(),
129            Self::Add(left, right) => left.to_epoch_seconds() + right.to_epoch_seconds(),
130        }
131    }
132}
133
134/// Interval value with magnitude and unit (EPIC-038).
135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136pub struct IntervalValue {
137    /// Numeric magnitude.
138    pub magnitude: i64,
139    /// Time unit.
140    pub unit: IntervalUnit,
141}
142
143impl IntervalValue {
144    /// Converts the interval to seconds.
145    #[must_use]
146    pub fn to_seconds(&self) -> i64 {
147        match self.unit {
148            IntervalUnit::Seconds => self.magnitude,
149            IntervalUnit::Minutes => self.magnitude * 60,
150            IntervalUnit::Hours => self.magnitude * 3600,
151            IntervalUnit::Days => self.magnitude * 86400,
152            IntervalUnit::Weeks => self.magnitude * 604_800,
153            IntervalUnit::Months => self.magnitude * 2_592_000,
154        }
155    }
156}
157
158impl Value {
159    /// Returns `true` if this value is a subquery.
160    ///
161    /// Subqueries parse but are not yet executable; callers use this to reject
162    /// them before they silently evaluate to `Value::Null`.
163    #[must_use]
164    pub fn is_subquery(&self) -> bool {
165        matches!(self, Self::Subquery(_))
166    }
167
168    /// Converts this VelesQL value to a JSON value.
169    ///
170    /// Literal values (integer, float, string, boolean, null) map directly
171    /// to their JSON equivalents. Parameters serialize as `"$name"` strings.
172    /// Temporal expressions evaluate to epoch-seconds integers.
173    /// Subqueries are not serializable and produce `Value::Null`.
174    #[must_use]
175    pub fn to_json(&self) -> serde_json::Value {
176        match self {
177            Self::Integer(i) => serde_json::json!(i),
178            Self::UnsignedInteger(u) => serde_json::json!(u),
179            Self::Float(f) => serde_json::json!(f),
180            Self::String(s) => serde_json::json!(s),
181            Self::Boolean(b) => serde_json::json!(b),
182            Self::Parameter(p) => serde_json::json!(format!("${p}")),
183            Self::Temporal(t) => serde_json::json!(t.to_epoch_seconds()),
184            Self::Null | Self::Subquery(_) => serde_json::Value::Null,
185        }
186    }
187}
188
189/// Time unit for INTERVAL expressions (EPIC-038).
190#[non_exhaustive]
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
192pub enum IntervalUnit {
193    /// Seconds.
194    Seconds,
195    /// Minutes.
196    Minutes,
197    /// Hours.
198    Hours,
199    /// Days.
200    Days,
201    /// Weeks.
202    Weeks,
203    /// Months.
204    Months,
205}