1use crate::dialects::DialectType;
9use crate::expressions::{Column, Expression, Identifier, Null};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum NormalizationStrategy {
14 Lowercase,
16 Uppercase,
18 CaseSensitive,
20 CaseInsensitive,
22 CaseInsensitiveUppercase,
24}
25
26impl Default for NormalizationStrategy {
27 fn default() -> Self {
28 Self::Lowercase
29 }
30}
31
32pub fn get_normalization_strategy(dialect: Option<DialectType>) -> NormalizationStrategy {
34 match dialect {
35 Some(DialectType::Oracle)
37 | Some(DialectType::Snowflake)
38 | Some(DialectType::Exasol)
39 | Some(DialectType::HANA) => NormalizationStrategy::Uppercase,
40 Some(DialectType::MySQL) | Some(DialectType::ClickHouse) => {
42 NormalizationStrategy::CaseSensitive
43 }
44 Some(DialectType::DuckDB)
46 | Some(DialectType::SQLite)
47 | Some(DialectType::BigQuery)
48 | Some(DialectType::Presto)
49 | Some(DialectType::Trino)
50 | Some(DialectType::Hive)
51 | Some(DialectType::Spark)
52 | Some(DialectType::Databricks)
53 | Some(DialectType::Redshift) => NormalizationStrategy::CaseInsensitive,
54 _ => NormalizationStrategy::Lowercase,
56 }
57}
58
59pub fn normalize_identifiers(expression: Expression, dialect: Option<DialectType>) -> Expression {
74 let strategy = get_normalization_strategy(dialect);
75 normalize_expression(expression, strategy)
76}
77
78pub fn normalize_identifier(identifier: Identifier, strategy: NormalizationStrategy) -> Identifier {
87 if strategy == NormalizationStrategy::CaseSensitive {
89 return identifier;
90 }
91
92 if identifier.quoted
94 && strategy != NormalizationStrategy::CaseInsensitive
95 && strategy != NormalizationStrategy::CaseInsensitiveUppercase
96 {
97 return identifier;
98 }
99
100 let normalized_name = match strategy {
102 NormalizationStrategy::Uppercase | NormalizationStrategy::CaseInsensitiveUppercase => {
103 identifier.name.to_uppercase()
104 }
105 NormalizationStrategy::Lowercase | NormalizationStrategy::CaseInsensitive => {
106 identifier.name.to_lowercase()
107 }
108 NormalizationStrategy::CaseSensitive => identifier.name, };
110
111 Identifier {
112 name: normalized_name,
113 quoted: identifier.quoted,
114 trailing_comments: identifier.trailing_comments,
115 span: None,
116 }
117}
118
119fn normalize_expression(expression: Expression, strategy: NormalizationStrategy) -> Expression {
121 match expression {
122 Expression::Identifier(id) => Expression::Identifier(normalize_identifier(id, strategy)),
123 Expression::Column(col) => Expression::boxed_column(Column {
124 name: normalize_identifier(col.name, strategy),
125 table: col.table.map(|t| normalize_identifier(t, strategy)),
126 join_mark: col.join_mark,
127 trailing_comments: col.trailing_comments,
128 span: None,
129 inferred_type: None,
130 }),
131 Expression::Table(mut table) => {
132 table.name = normalize_identifier(table.name, strategy);
133 if let Some(schema) = table.schema {
134 table.schema = Some(normalize_identifier(schema, strategy));
135 }
136 if let Some(catalog) = table.catalog {
137 table.catalog = Some(normalize_identifier(catalog, strategy));
138 }
139 if let Some(alias) = table.alias {
140 table.alias = Some(normalize_identifier(alias, strategy));
141 }
142 table.column_aliases = table
143 .column_aliases
144 .into_iter()
145 .map(|a| normalize_identifier(a, strategy))
146 .collect();
147 Expression::Table(table)
148 }
149 Expression::Select(select) => {
150 let mut select = *select;
151 select.expressions = select
153 .expressions
154 .into_iter()
155 .map(|e| normalize_expression(e, strategy))
156 .collect();
157 if let Some(mut from) = select.from {
159 from.expressions = from
160 .expressions
161 .into_iter()
162 .map(|e| normalize_expression(e, strategy))
163 .collect();
164 select.from = Some(from);
165 }
166 select.joins = select
168 .joins
169 .into_iter()
170 .map(|mut j| {
171 j.this = normalize_expression(j.this, strategy);
172 if let Some(on) = j.on {
173 j.on = Some(normalize_expression(on, strategy));
174 }
175 j
176 })
177 .collect();
178 if let Some(mut where_clause) = select.where_clause {
180 where_clause.this = normalize_expression(where_clause.this, strategy);
181 select.where_clause = Some(where_clause);
182 }
183 if let Some(mut group_by) = select.group_by {
185 group_by.expressions = group_by
186 .expressions
187 .into_iter()
188 .map(|e| normalize_expression(e, strategy))
189 .collect();
190 select.group_by = Some(group_by);
191 }
192 if let Some(mut having) = select.having {
194 having.this = normalize_expression(having.this, strategy);
195 select.having = Some(having);
196 }
197 if let Some(mut order_by) = select.order_by {
199 order_by.expressions = order_by
200 .expressions
201 .into_iter()
202 .map(|mut o| {
203 o.this = normalize_expression(o.this, strategy);
204 o
205 })
206 .collect();
207 select.order_by = Some(order_by);
208 }
209 Expression::Select(Box::new(select))
210 }
211 Expression::Alias(alias) => {
212 let mut alias = *alias;
213 alias.this = normalize_expression(alias.this, strategy);
214 alias.alias = normalize_identifier(alias.alias, strategy);
215 Expression::Alias(Box::new(alias))
216 }
217 Expression::And(bin) => normalize_binary(Expression::And, *bin, strategy),
219 Expression::Or(bin) => normalize_binary(Expression::Or, *bin, strategy),
220 Expression::Add(bin) => normalize_binary(Expression::Add, *bin, strategy),
221 Expression::Sub(bin) => normalize_binary(Expression::Sub, *bin, strategy),
222 Expression::Mul(bin) => normalize_binary(Expression::Mul, *bin, strategy),
223 Expression::Div(bin) => normalize_binary(Expression::Div, *bin, strategy),
224 Expression::Mod(bin) => normalize_binary(Expression::Mod, *bin, strategy),
225 Expression::Eq(bin) => normalize_binary(Expression::Eq, *bin, strategy),
226 Expression::Neq(bin) => normalize_binary(Expression::Neq, *bin, strategy),
227 Expression::Lt(bin) => normalize_binary(Expression::Lt, *bin, strategy),
228 Expression::Lte(bin) => normalize_binary(Expression::Lte, *bin, strategy),
229 Expression::Gt(bin) => normalize_binary(Expression::Gt, *bin, strategy),
230 Expression::Gte(bin) => normalize_binary(Expression::Gte, *bin, strategy),
231 Expression::Concat(bin) => normalize_binary(Expression::Concat, *bin, strategy),
232 Expression::Not(un) => {
234 let mut un = *un;
235 un.this = normalize_expression(un.this, strategy);
236 Expression::Not(Box::new(un))
237 }
238 Expression::Neg(un) => {
239 let mut un = *un;
240 un.this = normalize_expression(un.this, strategy);
241 Expression::Neg(Box::new(un))
242 }
243 Expression::Function(func) => {
245 let mut func = *func;
246 func.args = func
247 .args
248 .into_iter()
249 .map(|e| normalize_expression(e, strategy))
250 .collect();
251 func.qualified_name = func
252 .qualified_name
253 .into_iter()
254 .map(|i| normalize_identifier(i, strategy))
255 .collect();
256 if let Some(behavior) = &mut func.on_error {
257 behavior.value = behavior
258 .value
259 .take()
260 .map(|e| normalize_expression(e, strategy));
261 }
262 Expression::Function(Box::new(func))
263 }
264 Expression::AggregateFunction(agg) => {
265 let mut agg = *agg;
266 agg.args = agg
267 .args
268 .into_iter()
269 .map(|e| normalize_expression(e, strategy))
270 .collect();
271 Expression::AggregateFunction(Box::new(agg))
272 }
273 Expression::Paren(paren) => {
275 let mut paren = *paren;
276 paren.this = normalize_expression(paren.this, strategy);
277 Expression::Paren(Box::new(paren))
278 }
279 Expression::Case(case) => {
280 let mut case = *case;
281 case.operand = case.operand.map(|e| normalize_expression(e, strategy));
282 case.whens = case
283 .whens
284 .into_iter()
285 .map(|(w, t)| {
286 (
287 normalize_expression(w, strategy),
288 normalize_expression(t, strategy),
289 )
290 })
291 .collect();
292 case.else_ = case.else_.map(|e| normalize_expression(e, strategy));
293 Expression::Case(Box::new(case))
294 }
295 Expression::Cast(cast) => {
296 let mut cast = *cast;
297 cast.this = normalize_expression(cast.this, strategy);
298 Expression::Cast(Box::new(cast))
299 }
300 Expression::In(in_expr) => {
301 let mut in_expr = *in_expr;
302 in_expr.this = normalize_expression(in_expr.this, strategy);
303 in_expr.expressions = in_expr
304 .expressions
305 .into_iter()
306 .map(|e| normalize_expression(e, strategy))
307 .collect();
308 if let Some(q) = in_expr.query {
309 in_expr.query = Some(normalize_expression(q, strategy));
310 }
311 Expression::In(Box::new(in_expr))
312 }
313 Expression::Between(between) => {
314 let mut between = *between;
315 between.this = normalize_expression(between.this, strategy);
316 between.low = normalize_expression(between.low, strategy);
317 between.high = normalize_expression(between.high, strategy);
318 Expression::Between(Box::new(between))
319 }
320 Expression::Subquery(subquery) => {
321 let mut subquery = *subquery;
322 subquery.this = normalize_expression(subquery.this, strategy);
323 if let Some(alias) = subquery.alias {
324 subquery.alias = Some(normalize_identifier(alias, strategy));
325 }
326 Expression::Subquery(Box::new(subquery))
327 }
328 Expression::Union(mut union) => {
330 let left = std::mem::replace(&mut union.left, Expression::Null(Null));
331 union.left = normalize_expression(left, strategy);
332 let right = std::mem::replace(&mut union.right, Expression::Null(Null));
333 union.right = normalize_expression(right, strategy);
334 Expression::Union(union)
335 }
336 Expression::Intersect(mut intersect) => {
337 let left = std::mem::replace(&mut intersect.left, Expression::Null(Null));
338 intersect.left = normalize_expression(left, strategy);
339 let right = std::mem::replace(&mut intersect.right, Expression::Null(Null));
340 intersect.right = normalize_expression(right, strategy);
341 Expression::Intersect(intersect)
342 }
343 Expression::Except(mut except) => {
344 let left = std::mem::replace(&mut except.left, Expression::Null(Null));
345 except.left = normalize_expression(left, strategy);
346 let right = std::mem::replace(&mut except.right, Expression::Null(Null));
347 except.right = normalize_expression(right, strategy);
348 Expression::Except(except)
349 }
350 _ => expression,
352 }
353}
354
355fn normalize_binary<F>(
357 constructor: F,
358 mut bin: crate::expressions::BinaryOp,
359 strategy: NormalizationStrategy,
360) -> Expression
361where
362 F: FnOnce(Box<crate::expressions::BinaryOp>) -> Expression,
363{
364 bin.left = normalize_expression(bin.left, strategy);
365 bin.right = normalize_expression(bin.right, strategy);
366 constructor(Box::new(bin))
367}
368
369pub fn is_case_sensitive(text: &str, strategy: NormalizationStrategy) -> bool {
371 match strategy {
372 NormalizationStrategy::CaseInsensitive
373 | NormalizationStrategy::CaseInsensitiveUppercase => false,
374 NormalizationStrategy::Uppercase => text.chars().any(|c| c.is_lowercase()),
375 NormalizationStrategy::Lowercase => text.chars().any(|c| c.is_uppercase()),
376 NormalizationStrategy::CaseSensitive => true,
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use crate::generator::Generator;
384 use crate::parser::Parser;
385
386 fn gen(expr: &Expression) -> String {
387 Generator::new().generate(expr).unwrap()
388 }
389
390 fn parse_and_normalize(sql: &str, dialect: Option<DialectType>) -> String {
391 let ast = Parser::parse_sql(sql).expect("Failed to parse");
392 let normalized = normalize_identifiers(ast[0].clone(), dialect);
393 gen(&normalized)
394 }
395
396 #[test]
397 fn test_normalize_lowercase() {
398 let result = parse_and_normalize("SELECT FoO FROM Bar", None);
400 assert!(result.contains("foo") || result.contains("FOO")); }
402
403 #[test]
404 fn test_normalize_uppercase() {
405 let result = parse_and_normalize("SELECT foo FROM bar", Some(DialectType::Snowflake));
407 assert!(result.to_uppercase().contains("FOO"));
409 }
410
411 #[test]
412 fn test_normalize_preserves_quoted() {
413 let id = Identifier {
415 name: "FoO".to_string(),
416 quoted: true,
417 trailing_comments: vec![],
418 span: None,
419 };
420 let normalized = normalize_identifier(id, NormalizationStrategy::Lowercase);
421 assert_eq!(normalized.name, "FoO"); }
423
424 #[test]
425 fn test_case_insensitive_normalizes_quoted() {
426 let id = Identifier {
428 name: "FoO".to_string(),
429 quoted: true,
430 trailing_comments: vec![],
431 span: None,
432 };
433 let normalized = normalize_identifier(id, NormalizationStrategy::CaseInsensitive);
434 assert_eq!(normalized.name, "foo"); }
436
437 #[test]
438 fn test_case_sensitive_no_normalization() {
439 let id = Identifier {
441 name: "FoO".to_string(),
442 quoted: false,
443 trailing_comments: vec![],
444 span: None,
445 };
446 let normalized = normalize_identifier(id, NormalizationStrategy::CaseSensitive);
447 assert_eq!(normalized.name, "FoO"); }
449
450 #[test]
451 fn test_normalize_column() {
452 let col = Expression::boxed_column(Column {
453 name: Identifier::new("MyColumn"),
454 table: Some(Identifier::new("MyTable")),
455 join_mark: false,
456 trailing_comments: vec![],
457 span: None,
458 inferred_type: None,
459 });
460
461 let normalized = normalize_expression(col, NormalizationStrategy::Lowercase);
462 let sql = gen(&normalized);
463 assert!(sql.contains("mycolumn") || sql.contains("mytable"));
464 }
465
466 #[test]
467 fn test_get_normalization_strategy() {
468 assert_eq!(
469 get_normalization_strategy(Some(DialectType::Snowflake)),
470 NormalizationStrategy::Uppercase
471 );
472 assert_eq!(
473 get_normalization_strategy(Some(DialectType::PostgreSQL)),
474 NormalizationStrategy::Lowercase
475 );
476 assert_eq!(
477 get_normalization_strategy(Some(DialectType::MySQL)),
478 NormalizationStrategy::CaseSensitive
479 );
480 assert_eq!(
481 get_normalization_strategy(Some(DialectType::DuckDB)),
482 NormalizationStrategy::CaseInsensitive
483 );
484 }
485}