proof_of_sql_parser/
utility.rs

1use crate::{
2    intermediate_ast::{
3        AggregationOperator, AliasedResultExpr, BinaryOperator, Expression, Literal, OrderBy,
4        OrderByDirection, SelectResultExpr, SetExpression, Slice, TableExpression, UnaryOperator,
5    },
6    Identifier, SelectStatement,
7};
8use alloc::{boxed::Box, vec, vec::Vec};
9
10///
11/// # Panics
12///
13/// This function will panic if `name` (if provided) cannot be parsed.
14/// Construct an identifier from a str
15#[must_use]
16pub fn ident(name: &str) -> Identifier {
17    name.parse().unwrap()
18}
19
20/// Construct a new boxed `Expression` A == B
21#[must_use]
22pub fn equal(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
23    Box::new(Expression::Binary {
24        op: BinaryOperator::Equal,
25        left,
26        right,
27    })
28}
29
30/// Construct a new boxed `Expression` A >= B
31#[must_use]
32pub fn ge(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
33    not(Box::new(Expression::Binary {
34        op: BinaryOperator::LessThan,
35        left,
36        right,
37    }))
38}
39
40/// Construct a new boxed `Expression` A > B
41#[must_use]
42pub fn gt(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
43    Box::new(Expression::Binary {
44        op: BinaryOperator::GreaterThan,
45        left,
46        right,
47    })
48}
49
50/// Construct a new boxed `Expression` A <= B
51#[must_use]
52pub fn le(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
53    not(Box::new(Expression::Binary {
54        op: BinaryOperator::GreaterThan,
55        left,
56        right,
57    }))
58}
59
60/// Construct a new boxed `Expression` A < B
61#[must_use]
62pub fn lt(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
63    Box::new(Expression::Binary {
64        op: BinaryOperator::LessThan,
65        left,
66        right,
67    })
68}
69
70/// Construct a new boxed `Expression` NOT P
71#[must_use]
72pub fn not(expr: Box<Expression>) -> Box<Expression> {
73    Box::new(Expression::Unary {
74        op: UnaryOperator::Not,
75        expr,
76    })
77}
78
79/// Construct a new boxed `Expression` P AND Q
80#[must_use]
81pub fn and(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
82    Box::new(Expression::Binary {
83        op: BinaryOperator::And,
84        left,
85        right,
86    })
87}
88
89/// Construct a new boxed `Expression` P OR Q
90#[must_use]
91pub fn or(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
92    Box::new(Expression::Binary {
93        op: BinaryOperator::Or,
94        left,
95        right,
96    })
97}
98
99/// Construct a new boxed `Expression` A + B
100#[must_use]
101pub fn add(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
102    Box::new(Expression::Binary {
103        op: BinaryOperator::Add,
104        left,
105        right,
106    })
107}
108
109/// Construct a new boxed `Expression` A - B
110#[must_use]
111pub fn sub(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
112    Box::new(Expression::Binary {
113        op: BinaryOperator::Subtract,
114        left,
115        right,
116    })
117}
118
119/// Construct a new boxed `Expression` A * B
120#[must_use]
121pub fn mul(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
122    Box::new(Expression::Binary {
123        op: BinaryOperator::Multiply,
124        left,
125        right,
126    })
127}
128
129/// Construct a new boxed `Expression` A / B
130#[must_use]
131pub fn div(left: Box<Expression>, right: Box<Expression>) -> Box<Expression> {
132    Box::new(Expression::Binary {
133        op: BinaryOperator::Division,
134        left,
135        right,
136    })
137}
138
139/// Get table from schema and name.
140///
141/// If the schema is `None`, the table is assumed to be in the default schema.
142/// # Panics
143///
144/// This function will panic if either the `name` or the `schema` (if provided) cannot be parsed as valid [Identifier]s.
145#[must_use]
146pub fn tab(schema: Option<&str>, name: &str) -> Box<TableExpression> {
147    Box::new(TableExpression::Named {
148        table: name.parse().unwrap(),
149        schema: schema.map(|schema| schema.parse().unwrap()),
150    })
151}
152
153/// Get column from name
154///
155/// # Panics
156///
157/// This function will panic if the `name` cannot be parsed into a valid column expression as valid [Identifier]s.
158#[must_use]
159pub fn col(name: &str) -> Box<Expression> {
160    Box::new(Expression::Column(name.parse().unwrap()))
161}
162
163/// Get literal from value
164pub fn lit<L: Into<Literal>>(literal: L) -> Box<Expression> {
165    Box::new(Expression::Literal(literal.into()))
166}
167
168/// Compute the sum of an expression
169#[must_use]
170pub fn sum(expr: Box<Expression>) -> Box<Expression> {
171    Box::new(Expression::Aggregation {
172        op: AggregationOperator::Sum,
173        expr,
174    })
175}
176
177/// Compute the minimum of an expression
178#[must_use]
179pub fn min(expr: Box<Expression>) -> Box<Expression> {
180    Box::new(Expression::Aggregation {
181        op: AggregationOperator::Min,
182        expr,
183    })
184}
185
186/// Compute the maximum of an expression
187#[must_use]
188pub fn max(expr: Box<Expression>) -> Box<Expression> {
189    Box::new(Expression::Aggregation {
190        op: AggregationOperator::Max,
191        expr,
192    })
193}
194
195/// Count the amount of non-null entries of expression
196#[must_use]
197pub fn count(expr: Box<Expression>) -> Box<Expression> {
198    Box::new(Expression::Aggregation {
199        op: AggregationOperator::Count,
200        expr,
201    })
202}
203
204/// Count the rows
205#[must_use]
206pub fn count_all() -> Box<Expression> {
207    count(Box::new(Expression::Wildcard))
208}
209
210/// An expression with an alias i.e. EXPR AS ALIAS
211///
212/// # Panics
213///
214/// This function will panic if the `alias` cannot be parsed as valid [Identifier]s.
215#[must_use]
216pub fn aliased_expr(expr: Box<Expression>, alias: &str) -> AliasedResultExpr {
217    AliasedResultExpr {
218        expr,
219        alias: alias.parse().unwrap(),
220    }
221}
222
223/// Select all columns from a table i.e. SELECT *
224#[must_use]
225pub fn col_res_all() -> SelectResultExpr {
226    SelectResultExpr::ALL
227}
228
229/// Select one column from a table and give it an alias i.e. SELECT COL AS ALIAS
230///
231/// # Panics
232///
233/// This function will panic if the `alias` cannot be parsed as valid [Identifier]s.
234#[must_use]
235pub fn col_res(col_val: Box<Expression>, alias: &str) -> SelectResultExpr {
236    SelectResultExpr::AliasedResultExpr(AliasedResultExpr {
237        expr: col_val,
238        alias: alias.parse().unwrap(),
239    })
240}
241
242/// Select multiple columns from a table i.e. SELECT COL1, COL2, ...
243#[must_use]
244pub fn cols_res(names: &[&str]) -> Vec<SelectResultExpr> {
245    names.iter().map(|name| col_res(col(name), name)).collect()
246}
247
248/// Compute the minimum of an expression and give it an alias i.e. SELECT MIN(EXPR) AS ALIAS
249///
250/// # Panics
251///
252/// This function will panic if the `alias` cannot be parsed.
253#[must_use]
254pub fn min_res(expr: Box<Expression>, alias: &str) -> SelectResultExpr {
255    SelectResultExpr::AliasedResultExpr(AliasedResultExpr {
256        expr: min(expr),
257        alias: alias.parse().unwrap(),
258    })
259}
260
261/// Compute the maximum of an expression and give it an alias i.e. SELECT MAX(EXPR) AS ALIAS
262///
263/// # Panics
264///
265/// This function will panic if the `alias` cannot be parsed.
266#[must_use]
267pub fn max_res(expr: Box<Expression>, alias: &str) -> SelectResultExpr {
268    SelectResultExpr::AliasedResultExpr(AliasedResultExpr {
269        expr: max(expr),
270        alias: alias.parse().unwrap(),
271    })
272}
273
274/// Compute the sum of an expression and give it an alias i.e. SELECT SUM(EXPR) AS ALIAS
275///
276/// # Panics
277///
278/// This function will panic if the `alias` cannot be parsed.
279#[must_use]
280pub fn sum_res(expr: Box<Expression>, alias: &str) -> SelectResultExpr {
281    SelectResultExpr::AliasedResultExpr(AliasedResultExpr {
282        expr: sum(expr),
283        alias: alias.parse().unwrap(),
284    })
285}
286
287/// Count the amount of non-null entries of expression and give it an alias i.e. SELECT COUNT(EXPR) AS ALIAS
288///
289/// # Panics
290///
291/// This function will panic if the `alias` cannot be parsed.
292#[must_use]
293pub fn count_res(expr: Box<Expression>, alias: &str) -> SelectResultExpr {
294    SelectResultExpr::AliasedResultExpr(AliasedResultExpr {
295        expr: count(expr),
296        alias: alias.parse().unwrap(),
297    })
298}
299
300/// Count rows and give the result an alias i.e. SELECT COUNT(*) AS ALIAS
301///
302/// # Panics
303///
304/// This function will panic if the `alias` cannot be parsed.
305#[must_use]
306pub fn count_all_res(alias: &str) -> SelectResultExpr {
307    SelectResultExpr::AliasedResultExpr(AliasedResultExpr {
308        expr: Box::new(Expression::Aggregation {
309            op: AggregationOperator::Count,
310            expr: Box::new(Expression::Wildcard),
311        }),
312        alias: alias.parse().unwrap(),
313    })
314}
315
316/// Generate a `SetExpression` of the kind SELECT COL1, COL2, ... FROM TAB WHERE EXPR GROUP BY ...
317#[must_use]
318pub fn query(
319    result_exprs: Vec<SelectResultExpr>,
320    tab: Box<TableExpression>,
321    where_expr: Box<Expression>,
322    group_by: Vec<Identifier>,
323) -> Box<SetExpression> {
324    Box::new(SetExpression::Query {
325        result_exprs,
326        from: vec![tab],
327        where_expr: Some(where_expr),
328        group_by,
329    })
330}
331
332/// Generate a `SetExpression` of the kind SELECT COL1, COL2, ... FROM TAB GROUP BY ...
333///
334/// Note that there is no WHERE clause.
335#[must_use]
336pub fn query_all(
337    result_exprs: Vec<SelectResultExpr>,
338    tab: Box<TableExpression>,
339    group_by: Vec<Identifier>,
340) -> Box<SetExpression> {
341    Box::new(SetExpression::Query {
342        result_exprs,
343        from: vec![tab],
344        where_expr: None,
345        group_by,
346    })
347}
348
349/// Generate a query of the kind SELECT ... ORDER BY ... [LIMIT ... OFFSET ...]
350///
351/// Note that `expr` is a boxed `SetExpression`
352#[must_use]
353pub fn select(
354    expr: Box<SetExpression>,
355    order_by: Vec<OrderBy>,
356    slice: Option<Slice>,
357) -> SelectStatement {
358    SelectStatement {
359        expr,
360        order_by,
361        slice,
362    }
363}
364
365/// Order by one column i.e. ORDER BY ID [ASC|DESC]
366///
367/// # Panics
368///
369/// This function will panic if the `id` cannot be parsed into an identifier.
370#[must_use]
371pub fn order(id: &str, direction: OrderByDirection) -> Vec<OrderBy> {
372    vec![OrderBy {
373        expr: id.parse().unwrap(),
374        direction,
375    }]
376}
377
378/// Order by multiple columns i.e. ORDER BY ID0 [ASC|DESC], ID1 [ASC|DESC], ...
379///
380/// # Panics
381///
382/// This function will panic if any of the `ids` cannot be parsed
383/// into an identifier.
384#[must_use]
385pub fn orders(ids: &[&str], directions: &[OrderByDirection]) -> Vec<OrderBy> {
386    ids.iter()
387        .zip(directions.iter())
388        .map(|(id, dir)| OrderBy {
389            expr: id.parse().unwrap(),
390            direction: *dir,
391        })
392        .collect::<Vec<_>>()
393}
394
395/// Slice a query result using `LIMIT` and `OFFSET` clauses i.e. LIMIT N OFFSET M
396#[must_use]
397pub fn slice(number_rows: u64, offset_value: i64) -> Option<Slice> {
398    Some(Slice {
399        number_rows,
400        offset_value,
401    })
402}
403
404/// Group by clause with multiple columns i.e. GROUP BY ID0, ID1, ...
405///
406/// # Panics
407///
408/// This function will panic if any of the `ids` cannot be parsed
409/// into an identifier.
410#[must_use]
411pub fn group_by(ids: &[&str]) -> Vec<Identifier> {
412    ids.iter().map(|id| id.parse().unwrap()).collect()
413}