1use crate::dialects::DialectType;
14use crate::expressions::{DataType, Expression, Literal, Null};
15use crate::helper::{is_iso_date, is_iso_datetime};
16
17pub fn canonicalize(expression: Expression, dialect: Option<DialectType>) -> Expression {
29 canonicalize_recursive(expression, dialect)
30}
31
32fn canonicalize_recursive(expression: Expression, dialect: Option<DialectType>) -> Expression {
34 let expr = match expression {
35 Expression::Select(mut select) => {
36 select.expressions = select
38 .expressions
39 .into_iter()
40 .map(|e| canonicalize_recursive(e, dialect))
41 .collect();
42
43 if let Some(mut from) = select.from {
45 from.expressions = from
46 .expressions
47 .into_iter()
48 .map(|e| canonicalize_recursive(e, dialect))
49 .collect();
50 select.from = Some(from);
51 }
52
53 if let Some(mut where_clause) = select.where_clause {
55 where_clause.this = canonicalize_recursive(where_clause.this, dialect);
56 where_clause.this = ensure_bools(where_clause.this);
57 select.where_clause = Some(where_clause);
58 }
59
60 if let Some(mut having) = select.having {
62 having.this = canonicalize_recursive(having.this, dialect);
63 having.this = ensure_bools(having.this);
64 select.having = Some(having);
65 }
66
67 if let Some(mut order_by) = select.order_by {
69 order_by.expressions = order_by
70 .expressions
71 .into_iter()
72 .map(|mut o| {
73 o.this = canonicalize_recursive(o.this, dialect);
74 o = remove_ascending_order(o);
75 o
76 })
77 .collect();
78 select.order_by = Some(order_by);
79 }
80
81 select.joins = select
83 .joins
84 .into_iter()
85 .map(|mut j| {
86 j.this = canonicalize_recursive(j.this, dialect);
87 if let Some(on) = j.on {
88 j.on = Some(canonicalize_recursive(on, dialect));
89 }
90 j
91 })
92 .collect();
93
94 Expression::Select(select)
95 }
96
97 Expression::Add(bin) => {
99 let left = canonicalize_recursive(bin.left, dialect);
100 let right = canonicalize_recursive(bin.right, dialect);
101 let result = Expression::Add(Box::new(crate::expressions::BinaryOp {
102 left,
103 right,
104 left_comments: bin.left_comments,
105 operator_comments: bin.operator_comments,
106 trailing_comments: bin.trailing_comments,
107 inferred_type: None,
108 }));
109 add_text_to_concat(result)
110 }
111
112 Expression::And(bin) => {
114 let left = ensure_bools(canonicalize_recursive(bin.left, dialect));
115 let right = ensure_bools(canonicalize_recursive(bin.right, dialect));
116 Expression::And(Box::new(crate::expressions::BinaryOp {
117 left,
118 right,
119 left_comments: bin.left_comments,
120 operator_comments: bin.operator_comments,
121 trailing_comments: bin.trailing_comments,
122 inferred_type: None,
123 }))
124 }
125 Expression::Or(bin) => {
126 let left = ensure_bools(canonicalize_recursive(bin.left, dialect));
127 let right = ensure_bools(canonicalize_recursive(bin.right, dialect));
128 Expression::Or(Box::new(crate::expressions::BinaryOp {
129 left,
130 right,
131 left_comments: bin.left_comments,
132 operator_comments: bin.operator_comments,
133 trailing_comments: bin.trailing_comments,
134 inferred_type: None,
135 }))
136 }
137
138 Expression::Not(un) => {
139 let inner = ensure_bools(canonicalize_recursive(un.this, dialect));
140 Expression::Not(Box::new(crate::expressions::UnaryOp {
141 this: inner,
142 inferred_type: None,
143 }))
144 }
145
146 Expression::Eq(bin) => canonicalize_comparison(Expression::Eq, *bin, dialect),
148 Expression::Neq(bin) => canonicalize_comparison(Expression::Neq, *bin, dialect),
149 Expression::Lt(bin) => canonicalize_comparison(Expression::Lt, *bin, dialect),
150 Expression::Lte(bin) => canonicalize_comparison(Expression::Lte, *bin, dialect),
151 Expression::Gt(bin) => canonicalize_comparison(Expression::Gt, *bin, dialect),
152 Expression::Gte(bin) => canonicalize_comparison(Expression::Gte, *bin, dialect),
153
154 Expression::Sub(bin) => canonicalize_comparison(Expression::Sub, *bin, dialect),
155 Expression::Mul(bin) => canonicalize_binary(Expression::Mul, *bin, dialect),
156 Expression::Div(bin) => canonicalize_binary(Expression::Div, *bin, dialect),
157
158 Expression::Cast(cast) => {
160 let inner = canonicalize_recursive(cast.this, dialect);
161 let result = Expression::Cast(Box::new(crate::expressions::Cast {
162 this: inner,
163 to: cast.to,
164 trailing_comments: cast.trailing_comments,
165 double_colon_syntax: cast.double_colon_syntax,
166 format: cast.format,
167 default: cast.default,
168 inferred_type: None,
169 }));
170 remove_redundant_casts(result)
171 }
172
173 Expression::Function(mut func) => {
175 func.args = func
176 .args
177 .into_iter()
178 .map(|e| canonicalize_recursive(e, dialect))
179 .collect();
180 if let Some(behavior) = &mut func.on_error {
181 behavior.value = behavior
182 .value
183 .take()
184 .map(|e| canonicalize_recursive(e, dialect));
185 }
186 func.span = None;
187 func.inferred_type = None;
188 Expression::Function(func)
189 }
190 Expression::AggregateFunction(mut agg) => {
191 agg.args = agg
192 .args
193 .into_iter()
194 .map(|e| canonicalize_recursive(e, dialect))
195 .collect();
196 agg.filter = agg.filter.map(|e| canonicalize_recursive(e, dialect));
197 agg.inferred_type = None;
198 Expression::AggregateFunction(agg)
199 }
200
201 Expression::Alias(alias) => {
203 let inner = canonicalize_recursive(alias.this, dialect);
204 Expression::Alias(Box::new(crate::expressions::Alias {
205 this: inner,
206 alias: alias.alias,
207 column_aliases: alias.column_aliases,
208 alias_explicit_as: false,
209 alias_keyword: None,
210 pre_alias_comments: alias.pre_alias_comments,
211 trailing_comments: alias.trailing_comments,
212 inferred_type: None,
213 }))
214 }
215
216 Expression::Paren(paren) => {
218 let inner = canonicalize_recursive(paren.this, dialect);
219 Expression::Paren(Box::new(crate::expressions::Paren {
220 this: inner,
221 trailing_comments: paren.trailing_comments,
222 }))
223 }
224
225 Expression::Case(case) => {
227 let operand = case.operand.map(|e| canonicalize_recursive(e, dialect));
228 let whens = case
229 .whens
230 .into_iter()
231 .map(|(w, t)| {
232 (
233 canonicalize_recursive(w, dialect),
234 canonicalize_recursive(t, dialect),
235 )
236 })
237 .collect();
238 let else_ = case.else_.map(|e| canonicalize_recursive(e, dialect));
239 Expression::Case(Box::new(crate::expressions::Case {
240 operand,
241 whens,
242 else_,
243 comments: Vec::new(),
244 inferred_type: None,
245 }))
246 }
247
248 Expression::Between(between) => {
250 let this = canonicalize_recursive(between.this, dialect);
251 let low = canonicalize_recursive(between.low, dialect);
252 let high = canonicalize_recursive(between.high, dialect);
253 Expression::Between(Box::new(crate::expressions::Between {
254 this,
255 low,
256 high,
257 not: between.not,
258 symmetric: between.symmetric,
259 }))
260 }
261
262 Expression::In(in_expr) => {
264 let this = canonicalize_recursive(in_expr.this, dialect);
265 let expressions = in_expr
266 .expressions
267 .into_iter()
268 .map(|e| canonicalize_recursive(e, dialect))
269 .collect();
270 let query = in_expr.query.map(|q| canonicalize_recursive(q, dialect));
271 Expression::In(Box::new(crate::expressions::In {
272 this,
273 expressions,
274 query,
275 not: in_expr.not,
276 global: in_expr.global,
277 unnest: in_expr.unnest,
278 is_field: in_expr.is_field,
279 }))
280 }
281
282 Expression::Subquery(subquery) => {
284 let this = canonicalize_recursive(subquery.this, dialect);
285 Expression::Subquery(Box::new(crate::expressions::Subquery {
286 this,
287 alias: subquery.alias,
288 column_aliases: subquery.column_aliases,
289 alias_explicit_as: subquery.alias_explicit_as,
290 alias_keyword: subquery.alias_keyword,
291 order_by: subquery.order_by,
292 limit: subquery.limit,
293 offset: subquery.offset,
294 distribute_by: subquery.distribute_by,
295 sort_by: subquery.sort_by,
296 cluster_by: subquery.cluster_by,
297 lateral: subquery.lateral,
298 modifiers_inside: subquery.modifiers_inside,
299 trailing_comments: subquery.trailing_comments,
300 inferred_type: None,
301 }))
302 }
303
304 Expression::Union(union) => {
306 let mut u = *union;
307 let left = std::mem::replace(&mut u.left, Expression::Null(Null));
308 u.left = canonicalize_recursive(left, dialect);
309 let right = std::mem::replace(&mut u.right, Expression::Null(Null));
310 u.right = canonicalize_recursive(right, dialect);
311 Expression::Union(Box::new(u))
312 }
313 Expression::Intersect(intersect) => {
314 let mut i = *intersect;
315 let left = std::mem::replace(&mut i.left, Expression::Null(Null));
316 i.left = canonicalize_recursive(left, dialect);
317 let right = std::mem::replace(&mut i.right, Expression::Null(Null));
318 i.right = canonicalize_recursive(right, dialect);
319 Expression::Intersect(Box::new(i))
320 }
321 Expression::Except(except) => {
322 let mut e = *except;
323 let left = std::mem::replace(&mut e.left, Expression::Null(Null));
324 e.left = canonicalize_recursive(left, dialect);
325 let right = std::mem::replace(&mut e.right, Expression::Null(Null));
326 e.right = canonicalize_recursive(right, dialect);
327 Expression::Except(Box::new(e))
328 }
329
330 other => other,
332 };
333
334 expr
335}
336
337fn add_text_to_concat(expression: Expression) -> Expression {
342 expression
345}
346
347fn remove_redundant_casts(expression: Expression) -> Expression {
351 if let Expression::Cast(cast) = &expression {
352 if let Expression::Literal(lit) = &cast.this {
358 if let Literal::String(_) = lit.as_ref() {
359 if matches!(&cast.to, DataType::VarChar { .. } | DataType::Text) {
360 return cast.this.clone();
361 }
362 }
363 }
364 if let Expression::Literal(lit) = &cast.this {
365 if let Literal::Number(_) = lit.as_ref() {
366 if matches!(
367 &cast.to,
368 DataType::Int { .. }
369 | DataType::BigInt { .. }
370 | DataType::Decimal { .. }
371 | DataType::Float { .. }
372 ) {
373 }
375 }
376 }
377 }
378 expression
379}
380
381fn ensure_bools(expression: Expression) -> Expression {
386 expression
390}
391
392fn remove_ascending_order(mut ordered: crate::expressions::Ordered) -> crate::expressions::Ordered {
396 if !ordered.desc && ordered.explicit_asc {
399 ordered.explicit_asc = false;
400 }
401 ordered
402}
403
404fn canonicalize_comparison<F>(
406 constructor: F,
407 bin: crate::expressions::BinaryOp,
408 dialect: Option<DialectType>,
409) -> Expression
410where
411 F: FnOnce(Box<crate::expressions::BinaryOp>) -> Expression,
412{
413 let left = canonicalize_recursive(bin.left, dialect);
414 let right = canonicalize_recursive(bin.right, dialect);
415
416 let (left, right) = coerce_date_operands(left, right);
418
419 constructor(Box::new(crate::expressions::BinaryOp {
420 left,
421 right,
422 left_comments: bin.left_comments,
423 operator_comments: bin.operator_comments,
424 trailing_comments: bin.trailing_comments,
425 inferred_type: None,
426 }))
427}
428
429fn canonicalize_binary<F>(
431 constructor: F,
432 bin: crate::expressions::BinaryOp,
433 dialect: Option<DialectType>,
434) -> Expression
435where
436 F: FnOnce(Box<crate::expressions::BinaryOp>) -> Expression,
437{
438 let left = canonicalize_recursive(bin.left, dialect);
439 let right = canonicalize_recursive(bin.right, dialect);
440
441 constructor(Box::new(crate::expressions::BinaryOp {
442 left,
443 right,
444 left_comments: bin.left_comments,
445 operator_comments: bin.operator_comments,
446 trailing_comments: bin.trailing_comments,
447 inferred_type: None,
448 }))
449}
450
451fn coerce_date_operands(left: Expression, right: Expression) -> (Expression, Expression) {
456 let left = coerce_date_string(left, &right);
458 let right = coerce_date_string(right, &left);
459 (left, right)
460}
461
462fn coerce_date_string(expr: Expression, _other: &Expression) -> Expression {
464 if let Expression::Literal(ref lit) = expr {
465 if let Literal::String(ref s) = lit.as_ref() {
466 if is_iso_date(s) {
468 } else if is_iso_datetime(s) {
471 }
474 }
475 }
476 expr
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use crate::generator::Generator;
483 use crate::parser::Parser;
484
485 fn gen(expr: &Expression) -> String {
486 Generator::new().generate(expr).unwrap()
487 }
488
489 fn parse(sql: &str) -> Expression {
490 Parser::parse_sql(sql).expect("Failed to parse")[0].clone()
491 }
492
493 #[test]
494 fn test_canonicalize_simple() {
495 let expr = parse("SELECT a FROM t");
496 let result = canonicalize(expr, None);
497 let sql = gen(&result);
498 assert!(sql.contains("SELECT"));
499 }
500
501 #[test]
502 fn test_canonicalize_preserves_structure() {
503 let expr = parse("SELECT a, b FROM t WHERE c = 1");
504 let result = canonicalize(expr, None);
505 let sql = gen(&result);
506 assert!(sql.contains("WHERE"));
507 }
508
509 #[test]
510 fn test_canonicalize_and_or() {
511 let expr = parse("SELECT 1 WHERE a AND b OR c");
512 let result = canonicalize(expr, None);
513 let sql = gen(&result);
514 assert!(sql.contains("AND") || sql.contains("OR"));
515 }
516
517 #[test]
518 fn test_canonicalize_comparison() {
519 let expr = parse("SELECT 1 WHERE a = 1 AND b > 2");
520 let result = canonicalize(expr, None);
521 let sql = gen(&result);
522 assert!(sql.contains("=") && sql.contains(">"));
523 }
524
525 #[test]
526 fn test_canonicalize_case() {
527 let expr = parse("SELECT CASE WHEN a = 1 THEN 'yes' ELSE 'no' END FROM t");
528 let result = canonicalize(expr, None);
529 let sql = gen(&result);
530 assert!(sql.contains("CASE") && sql.contains("WHEN"));
531 }
532
533 #[test]
534 fn test_canonicalize_subquery() {
535 let expr = parse("SELECT a FROM (SELECT b FROM t) AS sub");
536 let result = canonicalize(expr, None);
537 let sql = gen(&result);
538 assert!(sql.contains("SELECT") && sql.contains("sub"));
539 }
540
541 #[test]
542 fn test_canonicalize_order_by() {
543 let expr = parse("SELECT a FROM t ORDER BY a");
544 let result = canonicalize(expr, None);
545 let sql = gen(&result);
546 assert!(sql.contains("ORDER BY"));
547 }
548
549 #[test]
550 fn test_canonicalize_union() {
551 let expr = parse("SELECT a FROM t UNION SELECT b FROM s");
552 let result = canonicalize(expr, None);
553 let sql = gen(&result);
554 assert!(sql.contains("UNION"));
555 }
556
557 #[test]
558 fn test_add_text_to_concat_passthrough() {
559 let expr = parse("SELECT 1 + 2");
561 let result = canonicalize(expr, None);
562 let sql = gen(&result);
563 assert!(sql.contains("+"));
564 }
565
566 #[test]
567 fn test_canonicalize_function() {
568 let expr = parse("SELECT MAX(a) FROM t");
569 let result = canonicalize(expr, None);
570 let sql = gen(&result);
571 assert!(sql.contains("MAX"));
572 }
573
574 #[test]
575 fn test_canonicalize_between() {
576 let expr = parse("SELECT 1 WHERE a BETWEEN 1 AND 10");
577 let result = canonicalize(expr, None);
578 let sql = gen(&result);
579 assert!(sql.contains("BETWEEN"));
580 }
581}