Skip to main content

qail_core/ast/
values.rs

1use crate::ast::Qail;
2use uuid::Uuid;
3
4/// Escape content placed inside a SQL single-quoted string literal.
5pub(crate) fn escape_sql_literal_body(value: &str) -> String {
6    value.replace('\0', "").replace('\'', "''")
7}
8
9/// Time interval unit for duration expressions
10#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
11pub enum IntervalUnit {
12    /// Seconds.
13    Second,
14    /// Minutes.
15    Minute,
16    /// Hours.
17    Hour,
18    /// Days.
19    Day,
20    /// Weeks.
21    Week,
22    /// Months.
23    Month,
24    /// Years.
25    Year,
26}
27
28impl std::fmt::Display for IntervalUnit {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            IntervalUnit::Second => write!(f, "seconds"),
32            IntervalUnit::Minute => write!(f, "minutes"),
33            IntervalUnit::Hour => write!(f, "hours"),
34            IntervalUnit::Day => write!(f, "days"),
35            IntervalUnit::Week => write!(f, "weeks"),
36            IntervalUnit::Month => write!(f, "months"),
37            IntervalUnit::Year => write!(f, "years"),
38        }
39    }
40}
41
42/// A value in a condition.
43#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
44pub enum Value {
45    /// SQL NULL.
46    Null,
47    /// Boolean.
48    Bool(bool),
49    /// 64-bit integer.
50    Int(i64),
51    /// 64-bit float.
52    Float(f64),
53    /// Text string.
54    String(String),
55    /// Positional parameter ($n).
56    Param(usize),
57    /// Named parameter reference (:name, :id, etc.)
58    NamedParam(String),
59    /// SQL function call.
60    Function(String),
61    /// Array of values.
62    Array(Vec<Value>),
63    /// Subquery.
64    Subquery(Box<Qail>),
65    /// Column reference.
66    Column(String),
67    /// UUID.
68    Uuid(Uuid),
69    /// NULL-typed UUID.
70    NullUuid,
71    /// Time interval (e.g., 24 hours, 7 days)
72    Interval {
73        /// Numeric amount.
74        amount: i64,
75        /// Unit of time.
76        unit: IntervalUnit,
77    },
78    /// Timestamp literal.
79    Timestamp(String),
80    /// Binary data (bytea)
81    Bytes(Vec<u8>),
82    /// AST Expression (for complex expression comparisons like col > NOW() - INTERVAL)
83    Expr(Box<crate::ast::Expr>),
84    /// Vector embedding for similarity search (Qdrant)
85    Vector(Vec<f32>),
86    /// JSON data.
87    Json(String),
88}
89
90impl std::fmt::Display for Value {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            Value::Null => write!(f, "NULL"),
94            Value::Bool(b) => write!(f, "{}", b),
95            Value::Int(n) => write!(f, "{}", n),
96            Value::Float(n) => write!(f, "{}", n),
97            Value::String(s) => write!(f, "'{}'", escape_sql_literal_body(s)),
98            Value::Param(n) => write!(f, "${}", n),
99            Value::NamedParam(name) => write!(f, ":{}", name),
100            Value::Function(s) => write!(f, "{}", s),
101            Value::Array(arr) => {
102                write!(f, "(")?;
103                for (i, v) in arr.iter().enumerate() {
104                    if i > 0 {
105                        write!(f, ", ")?;
106                    }
107                    write!(f, "{}", v)?;
108                }
109                write!(f, ")")
110            }
111            Value::Subquery(_) => write!(f, "(SUBQUERY)"),
112            Value::Column(s) => write!(f, "{}", s),
113            Value::Uuid(u) => write!(f, "'{}'", u),
114            Value::NullUuid => write!(f, "NULL"),
115            Value::Interval { amount, unit } => write!(f, "INTERVAL '{} {}'", amount, unit),
116            Value::Timestamp(ts) => write!(f, "'{}'", escape_sql_literal_body(ts)),
117            Value::Bytes(bytes) => {
118                write!(f, "'\\x")?;
119                for byte in bytes {
120                    write!(f, "{:02x}", byte)?;
121                }
122                write!(f, "'")
123            }
124            Value::Expr(expr) => write!(f, "{}", expr),
125            Value::Vector(v) => {
126                write!(f, "[")?;
127                for (i, val) in v.iter().enumerate() {
128                    if i > 0 {
129                        write!(f, ", ")?;
130                    }
131                    write!(f, "{}", val)?;
132                }
133                write!(f, "]")
134            }
135            Value::Json(json) => write!(f, "'{}'::jsonb", escape_sql_literal_body(json)),
136        }
137    }
138}
139
140impl From<bool> for Value {
141    fn from(b: bool) -> Self {
142        Value::Bool(b)
143    }
144}
145
146impl From<i32> for Value {
147    fn from(n: i32) -> Self {
148        Value::Int(n as i64)
149    }
150}
151
152impl From<i64> for Value {
153    fn from(n: i64) -> Self {
154        Value::Int(n)
155    }
156}
157
158impl From<f64> for Value {
159    fn from(n: f64) -> Self {
160        Value::Float(n)
161    }
162}
163
164impl From<&str> for Value {
165    fn from(s: &str) -> Self {
166        Value::String(s.to_string())
167    }
168}
169
170impl From<String> for Value {
171    fn from(s: String) -> Self {
172        Value::String(s)
173    }
174}
175
176impl From<Uuid> for Value {
177    fn from(u: Uuid) -> Self {
178        Value::Uuid(u)
179    }
180}
181
182impl From<Option<Uuid>> for Value {
183    fn from(opt: Option<Uuid>) -> Self {
184        match opt {
185            Some(u) => Value::Uuid(u),
186            None => Value::NullUuid,
187        }
188    }
189}
190
191impl From<Option<String>> for Value {
192    fn from(opt: Option<String>) -> Self {
193        match opt {
194            Some(s) => Value::String(s),
195            None => Value::Null,
196        }
197    }
198}
199
200impl<'a> From<Option<&'a str>> for Value {
201    fn from(opt: Option<&'a str>) -> Self {
202        match opt {
203            Some(s) => Value::String(s.to_string()),
204            None => Value::Null,
205        }
206    }
207}
208
209impl From<Option<i64>> for Value {
210    fn from(opt: Option<i64>) -> Self {
211        match opt {
212            Some(n) => Value::Int(n),
213            None => Value::Null,
214        }
215    }
216}
217
218impl From<Option<i32>> for Value {
219    fn from(opt: Option<i32>) -> Self {
220        match opt {
221            Some(n) => Value::Int(n as i64),
222            None => Value::Null,
223        }
224    }
225}
226
227impl From<Option<bool>> for Value {
228    fn from(opt: Option<bool>) -> Self {
229        match opt {
230            Some(b) => Value::Bool(b),
231            None => Value::Null,
232        }
233    }
234}
235
236/// Convert an Expr into a Value for use in correlated subquery filters.
237///
238/// # Example
239/// ```ignore
240/// use qail_core::ast::builders::col;
241///
242/// // WHERE session_id = parent_table.id
243/// .eq("session_id", col("parent_table.id"))
244/// ```
245impl From<crate::ast::Expr> for Value {
246    fn from(expr: crate::ast::Expr) -> Self {
247        Value::Expr(Box::new(expr))
248    }
249}