1use std::collections::HashMap;
2use std::sync::{Arc, Mutex, OnceLock};
3
4use crate::ast::{DataType, Expr, QuoteStyle, Statement};
5
6pub trait DialectPlugin: Send + Sync {
44 fn name(&self) -> &str;
46
47 fn quote_style(&self) -> Option<QuoteStyle> {
51 None
52 }
53
54 fn supports_ilike(&self) -> Option<bool> {
58 None
59 }
60
61 fn map_function_name(&self, name: &str) -> Option<String> {
67 let _ = name;
68 None
69 }
70
71 fn map_data_type(&self, data_type: &DataType) -> Option<DataType> {
75 let _ = data_type;
76 None
77 }
78
79 fn transform_expr(&self, expr: &Expr) -> Option<Expr> {
84 let _ = expr;
85 None
86 }
87
88 fn transform_statement(&self, statement: &Statement) -> Option<Statement> {
93 let _ = statement;
94 None
95 }
96}
97
98pub struct DialectRegistry {
106 dialects: Mutex<HashMap<String, Arc<dyn DialectPlugin>>>,
107}
108
109impl DialectRegistry {
110 pub fn global() -> &'static DialectRegistry {
112 static INSTANCE: OnceLock<DialectRegistry> = OnceLock::new();
113 INSTANCE.get_or_init(|| DialectRegistry {
114 dialects: Mutex::new(HashMap::new()),
115 })
116 }
117
118 pub fn register<P: DialectPlugin + 'static>(&self, plugin: P) {
122 let name = plugin.name().to_lowercase();
123 let mut map = self
124 .dialects
125 .lock()
126 .expect("dialect registry lock poisoned");
127 map.insert(name, Arc::new(plugin));
128 }
129
130 #[must_use]
132 pub fn get(&self, name: &str) -> Option<Arc<dyn DialectPlugin>> {
133 let map = self
134 .dialects
135 .lock()
136 .expect("dialect registry lock poisoned");
137 map.get(&name.to_lowercase()).cloned()
138 }
139
140 pub fn unregister(&self, name: &str) -> bool {
144 let mut map = self
145 .dialects
146 .lock()
147 .expect("dialect registry lock poisoned");
148 map.remove(&name.to_lowercase()).is_some()
149 }
150
151 #[must_use]
153 pub fn registered_names(&self) -> Vec<String> {
154 let map = self
155 .dialects
156 .lock()
157 .expect("dialect registry lock poisoned");
158 map.keys().cloned().collect()
159 }
160}
161
162use crate::dialects::Dialect;
167
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
183pub enum DialectRef {
184 BuiltIn(Dialect),
186 Custom(String),
188}
189
190impl DialectRef {
191 #[must_use]
193 pub fn custom(name: &str) -> Self {
194 DialectRef::Custom(name.to_lowercase())
195 }
196
197 #[must_use]
199 pub fn as_builtin(&self) -> Option<Dialect> {
200 match self {
201 DialectRef::BuiltIn(d) => Some(*d),
202 DialectRef::Custom(_) => None,
203 }
204 }
205
206 #[must_use]
208 pub fn as_plugin(&self) -> Option<Arc<dyn DialectPlugin>> {
209 match self {
210 DialectRef::Custom(name) => DialectRegistry::global().get(name),
211 DialectRef::BuiltIn(_) => None,
212 }
213 }
214
215 #[must_use]
217 pub fn quote_style(&self) -> QuoteStyle {
218 match self {
219 DialectRef::BuiltIn(d) => QuoteStyle::for_dialect(*d),
220 DialectRef::Custom(name) => DialectRegistry::global()
221 .get(name)
222 .and_then(|p| p.quote_style())
223 .unwrap_or(QuoteStyle::DoubleQuote),
224 }
225 }
226
227 #[must_use]
229 pub fn supports_ilike(&self) -> bool {
230 match self {
231 DialectRef::BuiltIn(d) => super::supports_ilike_builtin(*d),
232 DialectRef::Custom(name) => DialectRegistry::global()
233 .get(name)
234 .and_then(|p| p.supports_ilike())
235 .unwrap_or(false),
236 }
237 }
238
239 #[must_use]
241 pub fn map_function_name(&self, name: &str) -> String {
242 match self {
243 DialectRef::BuiltIn(d) => super::map_function_name(name, *d),
244 DialectRef::Custom(cname) => DialectRegistry::global()
245 .get(cname)
246 .and_then(|p| p.map_function_name(name))
247 .unwrap_or_else(|| name.to_string()),
248 }
249 }
250
251 #[must_use]
253 pub fn map_data_type(&self, dt: &DataType) -> DataType {
254 match self {
255 DialectRef::BuiltIn(d) => super::map_data_type(dt.clone(), *d),
256 DialectRef::Custom(name) => DialectRegistry::global()
257 .get(name)
258 .and_then(|p| p.map_data_type(dt))
259 .unwrap_or_else(|| dt.clone()),
260 }
261 }
262}
263
264impl From<Dialect> for DialectRef {
265 fn from(d: Dialect) -> Self {
266 DialectRef::BuiltIn(d)
267 }
268}
269
270impl std::fmt::Display for DialectRef {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 match self {
273 DialectRef::BuiltIn(d) => write!(f, "{d}"),
274 DialectRef::Custom(name) => write!(f, "Custom({name})"),
275 }
276 }
277}
278
279use crate::ast::TypedFunction;
288
289fn typed_function_canonical_name(func: &TypedFunction) -> &'static str {
291 match func {
292 TypedFunction::DateAdd { .. } => "DATE_ADD",
293 TypedFunction::DateDiff { .. } => "DATE_DIFF",
294 TypedFunction::DateTrunc { .. } => "DATE_TRUNC",
295 TypedFunction::DateSub { .. } => "DATE_SUB",
296 TypedFunction::CurrentDate => "CURRENT_DATE",
297 TypedFunction::CurrentTime => "CURRENT_TIME",
298 TypedFunction::CurrentTimestamp => "NOW",
299 TypedFunction::StrToTime { .. } => "STR_TO_TIME",
300 TypedFunction::TimeToStr { .. } => "TIME_TO_STR",
301 TypedFunction::TsOrDsToDate { .. } => "TS_OR_DS_TO_DATE",
302 TypedFunction::Year { .. } => "YEAR",
303 TypedFunction::Month { .. } => "MONTH",
304 TypedFunction::Day { .. } => "DAY",
305 TypedFunction::Trim { .. } => "TRIM",
306 TypedFunction::Substring { .. } => "SUBSTRING",
307 TypedFunction::Upper { .. } => "UPPER",
308 TypedFunction::Lower { .. } => "LOWER",
309 TypedFunction::RegexpLike { .. } => "REGEXP_LIKE",
310 TypedFunction::RegexpExtract { .. } => "REGEXP_EXTRACT",
311 TypedFunction::RegexpReplace { .. } => "REGEXP_REPLACE",
312 TypedFunction::ConcatWs { .. } => "CONCAT_WS",
313 TypedFunction::Split { .. } => "SPLIT",
314 TypedFunction::Initcap { .. } => "INITCAP",
315 TypedFunction::Length { .. } => "LENGTH",
316 TypedFunction::Replace { .. } => "REPLACE",
317 TypedFunction::Reverse { .. } => "REVERSE",
318 TypedFunction::Left { .. } => "LEFT",
319 TypedFunction::Right { .. } => "RIGHT",
320 TypedFunction::Lpad { .. } => "LPAD",
321 TypedFunction::Rpad { .. } => "RPAD",
322 TypedFunction::Count { .. } => "COUNT",
323 TypedFunction::Sum { .. } => "SUM",
324 TypedFunction::Avg { .. } => "AVG",
325 TypedFunction::Min { .. } => "MIN",
326 TypedFunction::Max { .. } => "MAX",
327 TypedFunction::ArrayAgg { .. } => "ARRAY_AGG",
328 TypedFunction::ApproxDistinct { .. } => "APPROX_DISTINCT",
329 TypedFunction::Variance { .. } => "VARIANCE",
330 TypedFunction::VariancePop { .. } => "VAR_POP",
331 TypedFunction::Stddev { .. } => "STDDEV",
332 TypedFunction::StddevPop { .. } => "STDDEV_POP",
333 TypedFunction::GroupConcat { .. } => "GROUP_CONCAT",
334 TypedFunction::ArrayConcat { .. } => "ARRAY_CONCAT",
335 TypedFunction::ArrayContains { .. } => "ARRAY_CONTAINS",
336 TypedFunction::ArraySize { .. } => "ARRAY_SIZE",
337 TypedFunction::Explode { .. } => "EXPLODE",
338 TypedFunction::GenerateSeries { .. } => "GENERATE_SERIES",
339 TypedFunction::Flatten { .. } => "FLATTEN",
340 TypedFunction::JSONExtract { .. } => "JSON_EXTRACT",
341 TypedFunction::JSONExtractScalar { .. } => "JSON_EXTRACT_SCALAR",
342 TypedFunction::ParseJSON { .. } => "PARSE_JSON",
343 TypedFunction::JSONFormat { .. } => "JSON_FORMAT",
344 TypedFunction::RowNumber => "ROW_NUMBER",
345 TypedFunction::Rank => "RANK",
346 TypedFunction::DenseRank => "DENSE_RANK",
347 TypedFunction::NTile { .. } => "NTILE",
348 TypedFunction::Lead { .. } => "LEAD",
349 TypedFunction::Lag { .. } => "LAG",
350 TypedFunction::FirstValue { .. } => "FIRST_VALUE",
351 TypedFunction::LastValue { .. } => "LAST_VALUE",
352 TypedFunction::Abs { .. } => "ABS",
353 TypedFunction::Ceil { .. } => "CEIL",
354 TypedFunction::Floor { .. } => "FLOOR",
355 TypedFunction::Round { .. } => "ROUND",
356 TypedFunction::Log { .. } => "LOG",
357 TypedFunction::Ln { .. } => "LN",
358 TypedFunction::Pow { .. } => "POW",
359 TypedFunction::Sqrt { .. } => "SQRT",
360 TypedFunction::Greatest { .. } => "GREATEST",
361 TypedFunction::Least { .. } => "LEAST",
362 TypedFunction::Mod { .. } => "MOD",
363 TypedFunction::Hex { .. } => "HEX",
364 TypedFunction::Unhex { .. } => "UNHEX",
365 TypedFunction::Md5 { .. } => "MD5",
366 TypedFunction::Sha { .. } => "SHA",
367 TypedFunction::Sha2 { .. } => "SHA2",
368 }
369}
370
371fn typed_function_args(func: &TypedFunction) -> Vec<Expr> {
373 match func {
374 TypedFunction::CurrentDate
375 | TypedFunction::CurrentTime
376 | TypedFunction::CurrentTimestamp => vec![],
377 TypedFunction::RowNumber | TypedFunction::Rank | TypedFunction::DenseRank => vec![],
378 TypedFunction::Length { expr }
379 | TypedFunction::Upper { expr }
380 | TypedFunction::Lower { expr }
381 | TypedFunction::Initcap { expr }
382 | TypedFunction::Reverse { expr }
383 | TypedFunction::Abs { expr }
384 | TypedFunction::Ceil { expr }
385 | TypedFunction::Floor { expr }
386 | TypedFunction::Ln { expr }
387 | TypedFunction::Sqrt { expr }
388 | TypedFunction::Explode { expr }
389 | TypedFunction::Flatten { expr }
390 | TypedFunction::ArraySize { expr }
391 | TypedFunction::ParseJSON { expr }
392 | TypedFunction::JSONFormat { expr }
393 | TypedFunction::Hex { expr }
394 | TypedFunction::Unhex { expr }
395 | TypedFunction::Md5 { expr }
396 | TypedFunction::Sha { expr }
397 | TypedFunction::TsOrDsToDate { expr }
398 | TypedFunction::Year { expr }
399 | TypedFunction::Month { expr }
400 | TypedFunction::Day { expr }
401 | TypedFunction::ApproxDistinct { expr }
402 | TypedFunction::Variance { expr }
403 | TypedFunction::VariancePop { expr }
404 | TypedFunction::Stddev { expr }
405 | TypedFunction::StddevPop { expr }
406 | TypedFunction::FirstValue { expr }
407 | TypedFunction::LastValue { expr } => vec![*expr.clone()],
408 TypedFunction::DateTrunc { unit, expr } => {
409 vec![Expr::StringLiteral(format!("{unit:?}")), *expr.clone()]
410 }
411 TypedFunction::DateAdd { expr, interval, .. }
412 | TypedFunction::DateSub { expr, interval, .. } => {
413 vec![*expr.clone(), *interval.clone()]
414 }
415 TypedFunction::DateDiff { start, end, .. } => vec![*start.clone(), *end.clone()],
416 TypedFunction::StrToTime { expr, format } | TypedFunction::TimeToStr { expr, format } => {
417 vec![*expr.clone(), *format.clone()]
418 }
419 TypedFunction::Trim { expr, .. } => vec![*expr.clone()],
420 TypedFunction::Substring {
421 expr,
422 start,
423 length,
424 } => {
425 let mut args = vec![*expr.clone(), *start.clone()];
426 if let Some(len) = length {
427 args.push(*len.clone());
428 }
429 args
430 }
431 TypedFunction::RegexpLike {
432 expr,
433 pattern,
434 flags,
435 } => {
436 let mut args = vec![*expr.clone(), *pattern.clone()];
437 if let Some(f) = flags {
438 args.push(*f.clone());
439 }
440 args
441 }
442 TypedFunction::RegexpExtract {
443 expr,
444 pattern,
445 group_index,
446 } => {
447 let mut args = vec![*expr.clone(), *pattern.clone()];
448 if let Some(g) = group_index {
449 args.push(*g.clone());
450 }
451 args
452 }
453 TypedFunction::RegexpReplace {
454 expr,
455 pattern,
456 replacement,
457 flags,
458 } => {
459 let mut args = vec![*expr.clone(), *pattern.clone(), *replacement.clone()];
460 if let Some(f) = flags {
461 args.push(*f.clone());
462 }
463 args
464 }
465 TypedFunction::ConcatWs { separator, exprs } => {
466 let mut args = vec![*separator.clone()];
467 args.extend(exprs.iter().cloned());
468 args
469 }
470 TypedFunction::Split { expr, delimiter } => vec![*expr.clone(), *delimiter.clone()],
471 TypedFunction::Replace { expr, from, to } => {
472 vec![*expr.clone(), *from.clone(), *to.clone()]
473 }
474 TypedFunction::Left { expr, n } | TypedFunction::Right { expr, n } => {
475 vec![*expr.clone(), *n.clone()]
476 }
477 TypedFunction::Lpad { expr, length, pad } | TypedFunction::Rpad { expr, length, pad } => {
478 let mut args = vec![*expr.clone(), *length.clone()];
479 if let Some(p) = pad {
480 args.push(*p.clone());
481 }
482 args
483 }
484 TypedFunction::Count { expr, .. }
485 | TypedFunction::Sum { expr, .. }
486 | TypedFunction::Avg { expr, .. }
487 | TypedFunction::Min { expr }
488 | TypedFunction::Max { expr }
489 | TypedFunction::ArrayAgg { expr, .. } => vec![*expr.clone()],
490 TypedFunction::ArrayConcat { arrays } => arrays.clone(),
491 TypedFunction::ArrayContains { array, element } => {
492 vec![*array.clone(), *element.clone()]
493 }
494 TypedFunction::GenerateSeries { start, stop, step } => {
495 let mut args = vec![*start.clone(), *stop.clone()];
496 if let Some(s) = step {
497 args.push(*s.clone());
498 }
499 args
500 }
501 TypedFunction::JSONExtract { expr, path }
502 | TypedFunction::JSONExtractScalar { expr, path } => {
503 vec![*expr.clone(), *path.clone()]
504 }
505 TypedFunction::NTile { n } => vec![*n.clone()],
506 TypedFunction::Lead {
507 expr,
508 offset,
509 default,
510 }
511 | TypedFunction::Lag {
512 expr,
513 offset,
514 default,
515 } => {
516 let mut args = vec![*expr.clone()];
517 if let Some(o) = offset {
518 args.push(*o.clone());
519 }
520 if let Some(d) = default {
521 args.push(*d.clone());
522 }
523 args
524 }
525 TypedFunction::Round { expr, decimals } => {
526 let mut args = vec![*expr.clone()];
527 if let Some(d) = decimals {
528 args.push(*d.clone());
529 }
530 args
531 }
532 TypedFunction::Log { expr, base } => {
533 let mut args = vec![*expr.clone()];
534 if let Some(b) = base {
535 args.push(*b.clone());
536 }
537 args
538 }
539 TypedFunction::Pow { base, exponent } => vec![*base.clone(), *exponent.clone()],
540 TypedFunction::Greatest { exprs } | TypedFunction::Least { exprs } => exprs.clone(),
541 TypedFunction::Mod { left, right } => vec![*left.clone(), *right.clone()],
542 TypedFunction::Sha2 { expr, bit_length } => vec![*expr.clone(), *bit_length.clone()],
543 TypedFunction::GroupConcat {
544 exprs, separator, ..
545 } => {
546 let mut args = exprs.clone();
547 if let Some(s) = separator {
548 args.push(*s.clone());
549 }
550 args
551 }
552 }
553}
554
555#[must_use]
562pub fn transform(statement: &Statement, from: &DialectRef, to: &DialectRef) -> Statement {
563 if let (DialectRef::BuiltIn(f), DialectRef::BuiltIn(t)) = (from, to) {
565 return super::transform(statement, *f, *t);
566 }
567
568 if let Some(plugin) = to.as_plugin()
570 && let Some(transformed) = plugin.transform_statement(statement)
571 {
572 return transformed;
573 }
574
575 let mut stmt = statement.clone();
577 transform_statement_plugin(&mut stmt, to);
578 stmt
579}
580
581fn transform_statement_plugin(statement: &mut Statement, target: &DialectRef) {
583 match statement {
584 Statement::Select(sel) => {
585 for item in &mut sel.columns {
586 if let crate::ast::SelectItem::Expr { expr, .. } = item {
587 *expr = transform_expr_plugin(expr.clone(), target);
588 }
589 }
590 if let Some(wh) = &mut sel.where_clause {
591 *wh = transform_expr_plugin(wh.clone(), target);
592 }
593 for gb in &mut sel.group_by {
594 *gb = transform_expr_plugin(gb.clone(), target);
595 }
596 if let Some(having) = &mut sel.having {
597 *having = transform_expr_plugin(having.clone(), target);
598 }
599 }
600 Statement::CreateTable(ct) => {
601 for col in &mut ct.columns {
602 col.data_type = target.map_data_type(&col.data_type);
603 if let Some(default) = &mut col.default {
604 *default = transform_expr_plugin(default.clone(), target);
605 }
606 }
607 }
608 _ => {}
609 }
610}
611
612fn transform_expr_plugin(expr: Expr, target: &DialectRef) -> Expr {
614 if let Some(plugin) = target.as_plugin()
616 && let Some(transformed) = plugin.transform_expr(&expr)
617 {
618 return transformed;
619 }
620
621 match expr {
622 Expr::TypedFunction { func, filter, over } => {
625 if let DialectRef::Custom(_) = target {
626 let canonical = typed_function_canonical_name(&func);
627 let new_name = target.map_function_name(canonical);
628 if new_name != canonical {
629 let args = typed_function_args(&func)
631 .into_iter()
632 .map(|a| transform_expr_plugin(a, target))
633 .collect();
634 return Expr::Function {
635 name: new_name,
636 args,
637 distinct: false,
638 filter: filter.map(|f| Box::new(transform_expr_plugin(*f, target))),
639 over,
640 order_by: vec![],
641 within_group: false,
642 };
643 }
644 }
645 Expr::TypedFunction {
647 func: func.transform_children(&|e| transform_expr_plugin(e, target)),
648 filter: filter.map(|f| Box::new(transform_expr_plugin(*f, target))),
649 over,
650 }
651 }
652 Expr::Function {
653 name,
654 args,
655 distinct,
656 filter,
657 over,
658 order_by,
659 within_group,
660 } => {
661 let new_name = target.map_function_name(&name);
662 let new_args: Vec<Expr> = args
663 .into_iter()
664 .map(|a| transform_expr_plugin(a, target))
665 .collect();
666 Expr::Function {
667 name: new_name,
668 args: new_args,
669 distinct,
670 filter: filter.map(|f| Box::new(transform_expr_plugin(*f, target))),
671 over,
672 order_by,
673 within_group,
674 }
675 }
676 Expr::Cast { expr, data_type } => Expr::Cast {
677 expr: Box::new(transform_expr_plugin(*expr, target)),
678 data_type: target.map_data_type(&data_type),
679 },
680 Expr::ILike {
681 expr,
682 pattern,
683 negated,
684 escape,
685 } if !target.supports_ilike() => Expr::Like {
686 expr: Box::new(Expr::TypedFunction {
687 func: crate::ast::TypedFunction::Lower {
688 expr: Box::new(transform_expr_plugin(*expr, target)),
689 },
690 filter: None,
691 over: None,
692 }),
693 pattern: Box::new(Expr::TypedFunction {
694 func: crate::ast::TypedFunction::Lower {
695 expr: Box::new(transform_expr_plugin(*pattern, target)),
696 },
697 filter: None,
698 over: None,
699 }),
700 negated,
701 escape,
702 },
703 Expr::BinaryOp { left, op, right } => Expr::BinaryOp {
704 left: Box::new(transform_expr_plugin(*left, target)),
705 op,
706 right: Box::new(transform_expr_plugin(*right, target)),
707 },
708 Expr::UnaryOp { op, expr } => Expr::UnaryOp {
709 op,
710 expr: Box::new(transform_expr_plugin(*expr, target)),
711 },
712 Expr::Nested(inner) => Expr::Nested(Box::new(transform_expr_plugin(*inner, target))),
713 Expr::Column {
714 table,
715 name,
716 quote_style,
717 table_quote_style,
718 } => {
719 let new_qs = if quote_style.is_quoted() {
720 target.quote_style()
721 } else {
722 QuoteStyle::None
723 };
724 let new_tqs = if table_quote_style.is_quoted() {
725 target.quote_style()
726 } else {
727 QuoteStyle::None
728 };
729 Expr::Column {
730 table,
731 name,
732 quote_style: new_qs,
733 table_quote_style: new_tqs,
734 }
735 }
736 other => other,
737 }
738}
739
740use crate::errors;
745
746pub fn transpile_ext(
765 sql: &str,
766 read_dialect: &DialectRef,
767 write_dialect: &DialectRef,
768) -> errors::Result<String> {
769 let parse_dialect = read_dialect.as_builtin().unwrap_or(Dialect::Ansi);
771 let ast = crate::parser::parse(sql, parse_dialect)?;
772 let transformed = transform(&ast, read_dialect, write_dialect);
773 let gen_dialect = write_dialect.as_builtin().unwrap_or(Dialect::Ansi);
774 Ok(crate::generator::generate(&transformed, gen_dialect))
775}
776
777pub fn transpile_statements_ext(
783 sql: &str,
784 read_dialect: &DialectRef,
785 write_dialect: &DialectRef,
786) -> errors::Result<Vec<String>> {
787 let parse_dialect = read_dialect.as_builtin().unwrap_or(Dialect::Ansi);
788 let stmts = crate::parser::parse_statements(sql, parse_dialect)?;
789 let gen_dialect = write_dialect.as_builtin().unwrap_or(Dialect::Ansi);
790 let mut results = Vec::with_capacity(stmts.len());
791 for stmt in &stmts {
792 let transformed = transform(stmt, read_dialect, write_dialect);
793 results.push(crate::generator::generate(&transformed, gen_dialect));
794 }
795 Ok(results)
796}
797
798pub fn register_dialect<P: DialectPlugin + 'static>(plugin: P) {
806 DialectRegistry::global().register(plugin);
807}
808
809#[must_use]
813pub fn resolve_dialect(name: &str) -> Option<DialectRef> {
814 if let Some(d) = Dialect::from_str(name) {
816 return Some(DialectRef::BuiltIn(d));
817 }
818 if DialectRegistry::global().get(name).is_some() {
820 return Some(DialectRef::Custom(name.to_lowercase()));
821 }
822 None
823}