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