Skip to main content

qcraft_core/ast/
expr.rs

1use super::common::{FieldRef, OrderByDef};
2use super::conditions::Conditions;
3use super::custom::{CustomBinaryOp, CustomExpr};
4use super::query::QueryStmt;
5use super::value::Value;
6
7/// An expression in a SQL statement.
8#[derive(Debug, Clone)]
9pub enum Expr {
10    /// Literal value.
11    Value(Value),
12
13    /// Column reference.
14    Field(FieldRef),
15
16    /// Binary operation: `left op right`.
17    Binary {
18        left: Box<Expr>,
19        op: BinaryOp,
20        right: Box<Expr>,
21    },
22
23    /// Unary operation: `-expr`, `NOT expr`.
24    Unary { op: UnaryOp, expr: Box<Expr> },
25
26    /// Function call: `name(args...)`.
27    Func { name: String, args: Vec<Expr> },
28
29    /// Aggregate function: `COUNT(expr)`, `SUM(DISTINCT expr) FILTER (WHERE ...)`.
30    Aggregate(AggregationDef),
31
32    /// Type cast: `expr::type` (PG) or `CAST(expr AS type)`.
33    Cast { expr: Box<Expr>, to_type: String },
34
35    /// CASE WHEN ... THEN ... ELSE ... END.
36    Case(CaseDef),
37
38    /// Window function: `expr OVER (PARTITION BY ... ORDER BY ... frame)`.
39    Window(WindowDef),
40
41    /// EXISTS (subquery).
42    Exists(Box<QueryStmt>),
43
44    /// Scalar subquery.
45    SubQuery(Box<QueryStmt>),
46
47    /// ARRAY(subquery).
48    ArraySubQuery(Box<QueryStmt>),
49
50    /// Collation override: `expr COLLATE "name"`.
51    Collate { expr: Box<Expr>, collation: String },
52
53    /// Build a JSON array: PG `jsonb_build_array(...)`, SQLite `json_array(...)`.
54    JsonArray(Vec<Expr>),
55
56    /// Build a JSON object: PG `jsonb_build_object(k, v, ...)`, SQLite `json_object(k, v, ...)`.
57    JsonObject(Vec<(String, Expr)>),
58
59    /// Aggregate into JSON array: PG `jsonb_agg(...)`, SQLite `json_group_array(...)`.
60    JsonAgg {
61        expr: Box<Expr>,
62        distinct: bool,
63        filter: Option<Conditions>,
64        order_by: Option<Vec<OrderByDef>>,
65    },
66
67    /// Concatenate strings: PG `string_agg(expr, delim)`, SQLite `group_concat(expr, delim)`.
68    StringAgg {
69        expr: Box<Expr>,
70        delimiter: String,
71        distinct: bool,
72        filter: Option<Conditions>,
73        order_by: Option<Vec<OrderByDef>>,
74    },
75
76    /// JSON text extraction: `expr->>'path'` on both PG and SQLite.
77    /// Unlike `->` (which returns JSON), `->>` returns the value as text.
78    JsonPathText { expr: Box<Expr>, path: String },
79
80    /// Current timestamp: PG `now()`, SQLite `datetime('now')`.
81    Now,
82
83    /// SQL CURRENT_TIMESTAMP keyword (rendered without parentheses).
84    CurrentTimestamp,
85
86    /// SQL CURRENT_DATE keyword (rendered without parentheses).
87    CurrentDate,
88
89    /// SQL CURRENT_TIME keyword (rendered without parentheses).
90    CurrentTime,
91
92    /// Row/tuple constructor: `(expr1, expr2, ...)`.
93    Tuple(Vec<Expr>),
94
95    /// Unbound parameter placeholder for executemany/batch operations.
96    /// Renders as `$N` (PG) or `?` (SQLite) without a concrete value.
97    /// Optional `type_hint` renders as `$N::type` in PG.
98    Param { type_hint: Option<String> },
99
100    /// Raw SQL with parameters (escape hatch).
101    Raw { sql: String, params: Vec<Value> },
102
103    /// User-defined expression (extension point).
104    Custom(Box<dyn CustomExpr>),
105}
106
107impl Expr {
108    /// Column reference: `table.field`.
109    pub fn field(table: &str, name: &str) -> Self {
110        Expr::Field(FieldRef::new(table, name))
111    }
112
113    /// Literal value.
114    pub fn value(val: impl Into<Value>) -> Self {
115        Expr::Value(val.into())
116    }
117
118    /// Raw SQL expression (no parameters).
119    pub fn raw(sql: impl Into<String>) -> Self {
120        Expr::Raw {
121            sql: sql.into(),
122            params: vec![],
123        }
124    }
125
126    /// Function call: `name(args...)`.
127    pub fn func(name: impl Into<String>, args: Vec<Expr>) -> Self {
128        Expr::Func {
129            name: name.into(),
130            args,
131        }
132    }
133
134    /// Type cast: `CAST(expr AS to_type)`.
135    pub fn cast(expr: Expr, to_type: impl Into<String>) -> Self {
136        Expr::Cast {
137            expr: Box::new(expr),
138            to_type: to_type.into(),
139        }
140    }
141
142    /// COUNT(expr).
143    pub fn count(expr: Expr) -> Self {
144        Expr::Aggregate(AggregationDef {
145            name: "COUNT".into(),
146            expression: Some(Box::new(expr)),
147            distinct: false,
148            filter: None,
149            args: None,
150            order_by: None,
151        })
152    }
153
154    /// COUNT(*).
155    pub fn count_all() -> Self {
156        Expr::Aggregate(AggregationDef {
157            name: "COUNT".into(),
158            expression: None,
159            distinct: false,
160            filter: None,
161            args: None,
162            order_by: None,
163        })
164    }
165
166    /// SUM(expr).
167    pub fn sum(expr: Expr) -> Self {
168        Expr::Aggregate(AggregationDef {
169            name: "SUM".into(),
170            expression: Some(Box::new(expr)),
171            distinct: false,
172            filter: None,
173            args: None,
174            order_by: None,
175        })
176    }
177
178    /// AVG(expr).
179    pub fn avg(expr: Expr) -> Self {
180        Expr::Aggregate(AggregationDef {
181            name: "AVG".into(),
182            expression: Some(Box::new(expr)),
183            distinct: false,
184            filter: None,
185            args: None,
186            order_by: None,
187        })
188    }
189
190    /// MIN(expr).
191    pub fn min(expr: Expr) -> Self {
192        Expr::Aggregate(AggregationDef {
193            name: "MIN".into(),
194            expression: Some(Box::new(expr)),
195            distinct: false,
196            filter: None,
197            args: None,
198            order_by: None,
199        })
200    }
201
202    /// MAX(expr).
203    pub fn max(expr: Expr) -> Self {
204        Expr::Aggregate(AggregationDef {
205            name: "MAX".into(),
206            expression: Some(Box::new(expr)),
207            distinct: false,
208            filter: None,
209            args: None,
210            order_by: None,
211        })
212    }
213
214    /// EXISTS (subquery).
215    pub fn exists(query: QueryStmt) -> Self {
216        Expr::Exists(Box::new(query))
217    }
218
219    /// Scalar subquery.
220    pub fn subquery(query: QueryStmt) -> Self {
221        Expr::SubQuery(Box::new(query))
222    }
223
224    /// Collation override: `expr COLLATE "name"`.
225    pub fn collate(self, collation: impl Into<String>) -> Self {
226        Expr::Collate {
227            expr: Box::new(self),
228            collation: collation.into(),
229        }
230    }
231
232    /// Build a JSON array from expressions.
233    pub fn json_array(items: Vec<Expr>) -> Self {
234        Expr::JsonArray(items)
235    }
236
237    /// Build a JSON object from key-value pairs.
238    pub fn json_object(pairs: Vec<(impl Into<String>, Expr)>) -> Self {
239        Expr::JsonObject(pairs.into_iter().map(|(k, v)| (k.into(), v)).collect())
240    }
241
242    /// Aggregate values into a JSON array.
243    pub fn json_agg(expr: Expr) -> Self {
244        Expr::JsonAgg {
245            expr: Box::new(expr),
246            distinct: false,
247            filter: None,
248            order_by: None,
249        }
250    }
251
252    /// Concatenate strings with a delimiter.
253    pub fn string_agg(expr: Expr, delimiter: impl Into<String>) -> Self {
254        Expr::StringAgg {
255            expr: Box::new(expr),
256            delimiter: delimiter.into(),
257            distinct: false,
258            filter: None,
259            order_by: None,
260        }
261    }
262
263    /// JSON text extraction: `expr->>'path'`.
264    pub fn json_path_text(expr: Expr, path: impl Into<String>) -> Self {
265        Expr::JsonPathText {
266            expr: Box::new(expr),
267            path: path.into(),
268        }
269    }
270
271    /// Current timestamp.
272    pub fn now() -> Self {
273        Expr::Now
274    }
275}
276
277impl From<Value> for Expr {
278    fn from(v: Value) -> Self {
279        Expr::Value(v)
280    }
281}
282
283impl From<FieldRef> for Expr {
284    fn from(f: FieldRef) -> Self {
285        Expr::Field(f)
286    }
287}
288
289/// Binary operators.
290#[derive(Debug, Clone)]
291pub enum BinaryOp {
292    Add,
293    Sub,
294    Mul,
295    Div,
296    Mod,
297    BitwiseAnd,
298    BitwiseOr,
299    ShiftLeft,
300    ShiftRight,
301    Concat,
302
303    /// User-defined binary operator (extension point).
304    Custom(Box<dyn CustomBinaryOp>),
305}
306
307/// Unary operators.
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub enum UnaryOp {
310    Neg,
311    Not,
312    BitwiseNot,
313}
314
315/// Aggregate function definition.
316#[derive(Debug, Clone)]
317pub struct AggregationDef {
318    pub name: String,
319    pub expression: Option<Box<Expr>>,
320    pub distinct: bool,
321    pub filter: Option<Conditions>,
322    pub args: Option<Vec<Expr>>,
323    pub order_by: Option<Vec<OrderByDef>>,
324}
325
326impl AggregationDef {
327    pub fn new(name: impl Into<String>, expr: Expr) -> Self {
328        Self {
329            name: name.into(),
330            expression: Some(Box::new(expr)),
331            distinct: false,
332            filter: None,
333            args: None,
334            order_by: None,
335        }
336    }
337
338    pub fn count_all() -> Self {
339        Self {
340            name: "COUNT".into(),
341            expression: None,
342            distinct: false,
343            filter: None,
344            args: None,
345            order_by: None,
346        }
347    }
348
349    pub fn distinct(mut self) -> Self {
350        self.distinct = true;
351        self
352    }
353
354    pub fn filter(mut self, cond: Conditions) -> Self {
355        self.filter = Some(cond);
356        self
357    }
358
359    pub fn order_by(mut self, order: Vec<OrderByDef>) -> Self {
360        self.order_by = Some(order);
361        self
362    }
363}
364
365/// CASE expression.
366#[derive(Debug, Clone)]
367pub struct CaseDef {
368    pub cases: Vec<WhenClause>,
369    pub default: Option<Box<Expr>>,
370}
371
372/// WHEN condition THEN result.
373#[derive(Debug, Clone)]
374pub struct WhenClause {
375    pub condition: Conditions,
376    pub result: Expr,
377}
378
379/// Window function definition.
380#[derive(Debug, Clone)]
381pub struct WindowDef {
382    pub expression: Box<Expr>,
383    pub partition_by: Option<Vec<Expr>>,
384    pub order_by: Option<Vec<OrderByDef>>,
385    pub frame: Option<WindowFrameDef>,
386}
387
388/// Window frame specification.
389#[derive(Debug, Clone)]
390pub struct WindowFrameDef {
391    pub frame_type: WindowFrameType,
392    pub start: WindowFrameBound,
393    pub end: Option<WindowFrameBound>,
394}
395
396/// Window frame type.
397#[derive(Debug, Clone, Copy, PartialEq, Eq)]
398pub enum WindowFrameType {
399    Rows,
400    Range,
401    Groups,
402}
403
404/// Window frame bound.
405#[derive(Debug, Clone, PartialEq, Eq)]
406pub enum WindowFrameBound {
407    CurrentRow,
408    Preceding(Option<u64>),
409    Following(Option<u64>),
410}