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