1use super::{DialectImpl, DialectType};
11use crate::error::Result;
12use crate::expressions::{
13 BinaryFunc, BinaryOp, Cast, DataType, Expression, Function, Interval, IntervalUnit,
14 IntervalUnitSpec, JsonExtractFunc, LikeOp, Literal, MakeInterval, Paren, UnaryFunc,
15};
16#[cfg(feature = "generate")]
17use crate::generator::GeneratorConfig;
18use crate::tokens::TokenizerConfig;
19
20fn wrap_if_json_arrow(expr: Expression) -> Expression {
24 match &expr {
25 Expression::JsonExtract(f) if f.arrow_syntax => Expression::Paren(Box::new(Paren {
26 this: expr,
27 trailing_comments: Vec::new(),
28 })),
29 Expression::JsonExtractScalar(f) if f.arrow_syntax => Expression::Paren(Box::new(Paren {
30 this: expr,
31 trailing_comments: Vec::new(),
32 })),
33 _ => expr,
34 }
35}
36
37fn json_arrow_to_function(expr: Expression) -> Expression {
40 match expr {
41 Expression::JsonExtract(f) if f.arrow_syntax => Expression::Function(Box::new(
42 Function::new("JSON_EXTRACT".to_string(), vec![f.this, f.path]),
43 )),
44 Expression::JsonExtractScalar(f) if f.arrow_syntax => {
45 let json_extract = Expression::Function(Box::new(Function::new(
48 "JSON_EXTRACT".to_string(),
49 vec![f.this, f.path],
50 )));
51 Expression::Function(Box::new(Function::new(
52 "JSON_UNQUOTE".to_string(),
53 vec![json_extract],
54 )))
55 }
56 other => other,
57 }
58}
59
60pub struct MySQLDialect;
62
63impl DialectImpl for MySQLDialect {
64 fn dialect_type(&self) -> DialectType {
65 DialectType::MySQL
66 }
67
68 fn tokenizer_config(&self) -> TokenizerConfig {
69 use crate::tokens::TokenType;
70 let mut config = TokenizerConfig::default();
71 config.identifiers.insert('`', '`');
73 config.identifiers.remove(&'"');
76 config.quotes.insert("\"".to_string(), "\"".to_string());
78 config.string_escapes.push('\\');
80 config.keywords.insert("XOR".to_string(), TokenType::Xor);
82 config.escape_follow_chars = vec!['0', 'b', 'n', 'r', 't', 'Z', '%', '_'];
85 config.identifiers_can_start_with_digit = true;
87 config.hex_number_strings = true;
88 config
89 }
90
91 #[cfg(feature = "generate")]
92
93 fn generator_config(&self) -> GeneratorConfig {
94 use crate::generator::IdentifierQuoteStyle;
95 GeneratorConfig {
96 identifier_quote: '`',
97 identifier_quote_style: IdentifierQuoteStyle::BACKTICK,
98 dialect: Some(DialectType::MySQL),
99 null_ordering_supported: false,
101 limit_only_literals: true,
103 semi_anti_join_with_side: false,
105 supports_table_alias_columns: false,
107 values_as_table: false,
109 tablesample_requires_parens: false,
111 tablesample_with_method: false,
112 aggregate_filter_supported: false,
114 try_supported: false,
116 supports_convert_timezone: false,
118 supports_uescape: false,
120 supports_between_flags: false,
122 query_hints: false,
124 parameter_token: "?",
126 supports_window_exclude: false,
128 supports_exploding_projections: false,
130 identifiers_can_start_with_digit: true,
131 locking_reads_supported: true,
133 ..Default::default()
134 }
135 }
136
137 #[cfg(feature = "transpile")]
138
139 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
140 match expr {
141 Expression::DataType(dt) => self.transform_data_type(dt),
143
144 Expression::Nvl(f) => Ok(Expression::IfNull(f)),
146
147 Expression::TryCast(c) => self.transform_cast(*c),
153
154 Expression::SafeCast(c) => self.transform_cast(*c),
156
157 Expression::Cast(c) => self.transform_cast(*c),
160
161 Expression::ILike(op) => {
163 let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
165 let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
166 Ok(Expression::Like(Box::new(LikeOp {
167 left: lower_left,
168 right: lower_right,
169 escape: op.escape,
170 quantifier: op.quantifier,
171 inferred_type: None,
172 })))
173 }
174
175 Expression::Concat(op) => Ok(Expression::Concat(op)),
178
179 Expression::Add(op) => self.transform_make_interval_binary(op, false),
181 Expression::Sub(op) => self.transform_make_interval_binary(op, true),
182
183 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
185 seed: None,
186 lower: None,
187 upper: None,
188 }))),
189
190 Expression::ArrayAgg(f) => Ok(Expression::Function(Box::new(Function::new(
192 "GROUP_CONCAT".to_string(),
193 vec![f.this],
194 )))),
195
196 Expression::StringAgg(f) => {
198 let mut args = vec![f.this.clone()];
199 if let Some(separator) = &f.separator {
200 args.push(separator.clone());
201 }
202 Ok(Expression::Function(Box::new(Function::new(
203 "GROUP_CONCAT".to_string(),
204 args,
205 ))))
206 }
207
208 Expression::Unnest(f) => {
211 Ok(Expression::Function(Box::new(Function::new(
214 "JSON_TABLE".to_string(),
215 vec![f.this],
216 ))))
217 }
218
219 Expression::Substring(mut f) => {
221 f.from_for_syntax = false;
222 Ok(Expression::Substring(f))
223 }
224
225 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
228 "BIT_AND".to_string(),
229 vec![f.this],
230 )))),
231
232 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
234 "BIT_OR".to_string(),
235 vec![f.this],
236 )))),
237
238 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
240 "BIT_XOR".to_string(),
241 vec![f.this],
242 )))),
243
244 Expression::BitwiseCount(f) => Ok(Expression::Function(Box::new(Function::new(
246 "BIT_COUNT".to_string(),
247 vec![f.this],
248 )))),
249
250 Expression::TimeFromParts(f) => {
252 let mut args = Vec::new();
253 if let Some(h) = f.hour {
254 args.push(*h);
255 }
256 if let Some(m) = f.min {
257 args.push(*m);
258 }
259 if let Some(s) = f.sec {
260 args.push(*s);
261 }
262 Ok(Expression::Function(Box::new(Function::new(
263 "MAKETIME".to_string(),
264 args,
265 ))))
266 }
267
268 Expression::LogicalAnd(f) => Ok(Expression::Function(Box::new(Function::new(
272 "MIN".to_string(),
273 vec![f.this],
274 )))),
275
276 Expression::LogicalOr(f) => Ok(Expression::Function(Box::new(Function::new(
278 "MAX".to_string(),
279 vec![f.this],
280 )))),
281
282 Expression::DayOfMonth(f) => Ok(Expression::Function(Box::new(Function::new(
285 "DAYOFMONTH".to_string(),
286 vec![f.this],
287 )))),
288
289 Expression::DayOfWeek(f) => Ok(Expression::Function(Box::new(Function::new(
291 "DAYOFWEEK".to_string(),
292 vec![f.this],
293 )))),
294
295 Expression::DayOfYear(f) => Ok(Expression::Function(Box::new(Function::new(
297 "DAYOFYEAR".to_string(),
298 vec![f.this],
299 )))),
300
301 Expression::WeekOfYear(f) => Ok(Expression::Function(Box::new(Function::new(
303 "WEEKOFYEAR".to_string(),
304 vec![f.this],
305 )))),
306
307 Expression::DateDiff(f) => Ok(Expression::Function(Box::new(Function::new(
309 "DATEDIFF".to_string(),
310 vec![f.this, f.expression],
311 )))),
312
313 Expression::TimeStrToUnix(f) => Ok(Expression::Function(Box::new(Function::new(
315 "UNIX_TIMESTAMP".to_string(),
316 vec![f.this],
317 )))),
318
319 Expression::TimestampDiff(f) => Ok(Expression::Function(Box::new(Function::new(
321 "TIMESTAMPDIFF".to_string(),
322 vec![*f.this, *f.expression],
323 )))),
324
325 Expression::StrPosition(f) => {
329 let mut args = vec![];
330 if let Some(substr) = f.substr {
331 args.push(*substr);
332 }
333 args.push(*f.this);
334 if let Some(pos) = f.position {
335 args.push(*pos);
336 }
337 Ok(Expression::Function(Box::new(Function::new(
338 "LOCATE".to_string(),
339 args,
340 ))))
341 }
342
343 Expression::Stuff(f) => {
345 let mut args = vec![*f.this];
346 if let Some(start) = f.start {
347 args.push(*start);
348 }
349 if let Some(length) = f.length {
350 args.push(Expression::number(length));
351 }
352 args.push(*f.expression);
353 Ok(Expression::Function(Box::new(Function::new(
354 "INSERT".to_string(),
355 args,
356 ))))
357 }
358
359 Expression::SessionUser(_) => Ok(Expression::Function(Box::new(Function::new(
362 "SESSION_USER".to_string(),
363 vec![],
364 )))),
365
366 Expression::CurrentDate(_) => {
368 Ok(Expression::CurrentDate(crate::expressions::CurrentDate))
369 }
370
371 Expression::NullSafeNeq(op) => {
374 let null_safe_eq = Expression::NullSafeEq(Box::new(crate::expressions::BinaryOp {
376 left: op.left,
377 right: op.right,
378 left_comments: Vec::new(),
379 operator_comments: Vec::new(),
380 trailing_comments: Vec::new(),
381 inferred_type: None,
382 }));
383 Ok(Expression::Not(Box::new(crate::expressions::UnaryOp {
384 this: null_safe_eq,
385 inferred_type: None,
386 })))
387 }
388
389 Expression::JSONExtract(e) if e.variant_extract.is_some() => {
393 let path = match *e.expression {
394 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
395 let Literal::String(s) = lit.as_ref() else {
396 unreachable!()
397 };
398 let s = Self::convert_bracket_to_quoted_path(&s);
400 let normalized = if s.starts_with('$') {
401 s
402 } else if s.starts_with('[') {
403 format!("${}", s)
404 } else {
405 format!("$.{}", s)
406 };
407 Expression::Literal(Box::new(Literal::String(normalized)))
408 }
409 other => other,
410 };
411 Ok(Expression::Function(Box::new(Function::new(
412 "JSON_EXTRACT".to_string(),
413 vec![*e.this, path],
414 ))))
415 }
416
417 Expression::Function(f) => self.transform_function(*f),
419
420 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
422
423 Expression::Eq(op) => Ok(Expression::Eq(Box::new(BinaryOp {
430 left: wrap_if_json_arrow(op.left),
431 right: wrap_if_json_arrow(op.right),
432 ..*op
433 }))),
434 Expression::Neq(op) => Ok(Expression::Neq(Box::new(BinaryOp {
435 left: wrap_if_json_arrow(op.left),
436 right: wrap_if_json_arrow(op.right),
437 ..*op
438 }))),
439 Expression::Lt(op) => Ok(Expression::Lt(Box::new(BinaryOp {
440 left: wrap_if_json_arrow(op.left),
441 right: wrap_if_json_arrow(op.right),
442 ..*op
443 }))),
444 Expression::Lte(op) => Ok(Expression::Lte(Box::new(BinaryOp {
445 left: wrap_if_json_arrow(op.left),
446 right: wrap_if_json_arrow(op.right),
447 ..*op
448 }))),
449 Expression::Gt(op) => Ok(Expression::Gt(Box::new(BinaryOp {
450 left: wrap_if_json_arrow(op.left),
451 right: wrap_if_json_arrow(op.right),
452 ..*op
453 }))),
454 Expression::Gte(op) => Ok(Expression::Gte(Box::new(BinaryOp {
455 left: wrap_if_json_arrow(op.left),
456 right: wrap_if_json_arrow(op.right),
457 ..*op
458 }))),
459
460 Expression::In(mut i) => {
462 i.this = wrap_if_json_arrow(i.this);
463 Ok(Expression::In(i))
464 }
465
466 Expression::Not(mut n) => {
468 n.this = wrap_if_json_arrow(n.this);
469 Ok(Expression::Not(n))
470 }
471
472 Expression::ArrayOverlaps(op) => Ok(Expression::And(op)),
475
476 Expression::ModFunc(f) => Ok(Expression::Mod(Box::new(BinaryOp {
478 left: f.this,
479 right: f.expression,
480 left_comments: Vec::new(),
481 operator_comments: Vec::new(),
482 trailing_comments: Vec::new(),
483 inferred_type: None,
484 }))),
485
486 Expression::Show(mut s) => {
488 if s.this == "SLAVE STATUS" {
489 s.this = "REPLICA STATUS".to_string();
490 }
491 if matches!(s.this.as_str(), "INDEX" | "COLUMNS") && s.db.is_none() {
492 if let Some(Expression::Table(mut t)) = s.target.take() {
493 if let Some(db_ident) = t.schema.take().or(t.catalog.take()) {
494 s.db = Some(Expression::Identifier(db_ident));
495 s.target = Some(Expression::Identifier(t.name));
496 } else {
497 s.target = Some(Expression::Table(t));
498 }
499 }
500 }
501 Ok(Expression::Show(s))
502 }
503
504 Expression::AtTimeZone(atz) => {
507 let is_current = match &atz.this {
508 Expression::CurrentDate(_) | Expression::CurrentTimestamp(_) => true,
509 Expression::Function(f) => {
510 let n = f.name.to_uppercase();
511 (n == "CURRENT_DATE" || n == "CURRENT_TIMESTAMP") && f.no_parens
512 }
513 _ => false,
514 };
515 if is_current {
516 Ok(Expression::AtTimeZone(atz)) } else {
518 Ok(atz.this) }
520 }
521
522 Expression::MemberOf(mut op) => {
525 op.right = json_arrow_to_function(op.right);
526 Ok(Expression::MemberOf(op))
527 }
528
529 _ => Ok(expr),
531 }
532 }
533}
534
535#[cfg(feature = "transpile")]
536impl MySQLDialect {
537 fn normalize_mysql_date_format(fmt: &str) -> String {
538 fmt.replace("%H:%i:%s", "%T").replace("%H:%i:%S", "%T")
539 }
540
541 fn convert_bracket_to_quoted_path(path: &str) -> String {
544 let mut result = String::new();
545 let mut chars = path.chars().peekable();
546 while let Some(c) = chars.next() {
547 if c == '[' && chars.peek() == Some(&'"') {
548 chars.next(); let mut key = String::new();
550 while let Some(kc) = chars.next() {
551 if kc == '"' && chars.peek() == Some(&']') {
552 chars.next(); break;
554 }
555 key.push(kc);
556 }
557 if !result.is_empty() && !result.ends_with('.') {
558 result.push('.');
559 }
560 result.push('"');
561 result.push_str(&key);
562 result.push('"');
563 } else {
564 result.push(c);
565 }
566 }
567 result
568 }
569
570 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
574 use crate::expressions::DataType;
575 let transformed = match dt {
576 DataType::Timestamp {
578 precision,
579 timezone: _,
580 } => DataType::Timestamp {
581 precision,
582 timezone: false,
583 },
584 DataType::Custom { name }
586 if name.to_uppercase() == "TIMESTAMPTZ"
587 || name.to_uppercase() == "TIMESTAMPLTZ" =>
588 {
589 DataType::Timestamp {
590 precision: None,
591 timezone: false,
592 }
593 }
594 other => other,
597 };
598 Ok(Expression::DataType(transformed))
599 }
600
601 fn transform_cast(&self, cast: Cast) -> Result<Expression> {
605 match &cast.to {
607 DataType::Timestamp { .. } => Ok(Expression::Function(Box::new(Function::new(
608 "TIMESTAMP".to_string(),
609 vec![cast.this],
610 )))),
611 DataType::Custom { name }
612 if name.to_uppercase() == "TIMESTAMPTZ"
613 || name.to_uppercase() == "TIMESTAMPLTZ" =>
614 {
615 Ok(Expression::Function(Box::new(Function::new(
616 "TIMESTAMP".to_string(),
617 vec![cast.this],
618 ))))
619 }
620 _ => Ok(Expression::Cast(Box::new(self.transform_cast_type(cast)))),
622 }
623 }
624
625 fn transform_make_interval_binary(
626 &self,
627 op: Box<BinaryOp>,
628 subtract: bool,
629 ) -> Result<Expression> {
630 if let Expression::MakeInterval(make_interval) = &op.right {
631 if let Some(expr) =
632 Self::expand_make_interval_binary(op.left.clone(), make_interval, subtract)
633 {
634 return Ok(expr);
635 }
636 }
637 if let Expression::Function(function) = &op.right {
638 if function.name.eq_ignore_ascii_case("MAKE_INTERVAL") {
639 if let Some(expr) =
640 Self::expand_make_interval_function_binary(op.left.clone(), function, subtract)
641 {
642 return Ok(expr);
643 }
644 }
645 }
646
647 if subtract {
648 Ok(Expression::Sub(op))
649 } else {
650 Ok(Expression::Add(op))
651 }
652 }
653
654 fn expand_make_interval_binary(
655 left: Expression,
656 make_interval: &MakeInterval,
657 subtract: bool,
658 ) -> Option<Expression> {
659 let parts = Self::make_interval_parts(make_interval);
660 if parts.is_empty() {
661 return None;
662 }
663
664 let mut expr = left;
665 for (value, unit) in parts {
666 let interval = Expression::Interval(Box::new(Interval {
667 this: Some(value),
668 unit: Some(IntervalUnitSpec::Simple {
669 unit,
670 use_plural: false,
671 }),
672 }));
673 expr = if subtract {
674 Expression::Sub(Box::new(BinaryOp::new(expr, interval)))
675 } else {
676 Expression::Add(Box::new(BinaryOp::new(expr, interval)))
677 };
678 }
679
680 Some(expr)
681 }
682
683 fn expand_make_interval_function_binary(
684 left: Expression,
685 function: &Function,
686 subtract: bool,
687 ) -> Option<Expression> {
688 let parts = Self::make_interval_function_parts(function);
689 if parts.is_empty() {
690 return None;
691 }
692
693 let mut expr = left;
694 for (value, unit) in parts {
695 let interval = Expression::Interval(Box::new(Interval {
696 this: Some(value),
697 unit: Some(IntervalUnitSpec::Simple {
698 unit,
699 use_plural: false,
700 }),
701 }));
702 expr = if subtract {
703 Expression::Sub(Box::new(BinaryOp::new(expr, interval)))
704 } else {
705 Expression::Add(Box::new(BinaryOp::new(expr, interval)))
706 };
707 }
708
709 Some(expr)
710 }
711
712 fn make_interval_parts(make_interval: &MakeInterval) -> Vec<(Expression, IntervalUnit)> {
713 let mut parts = Vec::new();
714 if let Some(year) = &make_interval.year {
715 parts.push(((**year).clone(), IntervalUnit::Year));
716 }
717 if let Some(month) = &make_interval.month {
718 parts.push(((**month).clone(), IntervalUnit::Month));
719 }
720 if let Some(week) = &make_interval.week {
721 parts.push(((**week).clone(), IntervalUnit::Week));
722 }
723 if let Some(day) = &make_interval.day {
724 parts.push(((**day).clone(), IntervalUnit::Day));
725 }
726 if let Some(hour) = &make_interval.hour {
727 parts.push(((**hour).clone(), IntervalUnit::Hour));
728 }
729 if let Some(minute) = &make_interval.minute {
730 parts.push(((**minute).clone(), IntervalUnit::Minute));
731 }
732 if let Some(second) = &make_interval.second {
733 parts.push(((**second).clone(), IntervalUnit::Second));
734 }
735 parts
736 }
737
738 fn make_interval_function_parts(function: &Function) -> Vec<(Expression, IntervalUnit)> {
739 let positional_units = [
740 IntervalUnit::Year,
741 IntervalUnit::Month,
742 IntervalUnit::Week,
743 IntervalUnit::Day,
744 IntervalUnit::Hour,
745 IntervalUnit::Minute,
746 IntervalUnit::Second,
747 ];
748 let mut positional_index = 0;
749 let mut parts = Vec::new();
750
751 for arg in &function.args {
752 if let Expression::NamedArgument(named) = arg {
753 if let Some(unit) = Self::make_interval_unit_from_name(&named.name.name) {
754 parts.push((named.value.clone(), unit));
755 }
756 } else if let Some(unit) = positional_units.get(positional_index) {
757 parts.push((arg.clone(), *unit));
758 positional_index += 1;
759 }
760 }
761
762 parts
763 }
764
765 fn make_interval_unit_from_name(name: &str) -> Option<IntervalUnit> {
766 match name.to_ascii_lowercase().as_str() {
767 "year" | "years" => Some(IntervalUnit::Year),
768 "month" | "months" => Some(IntervalUnit::Month),
769 "week" | "weeks" => Some(IntervalUnit::Week),
770 "day" | "days" => Some(IntervalUnit::Day),
771 "hour" | "hours" => Some(IntervalUnit::Hour),
772 "min" | "mins" | "minute" | "minutes" => Some(IntervalUnit::Minute),
773 "sec" | "secs" | "second" | "seconds" => Some(IntervalUnit::Second),
774 _ => None,
775 }
776 }
777
778 fn transform_cast_type(&self, cast: Cast) -> Cast {
782 let new_type = match &cast.to {
783 DataType::VarChar { length, .. } => DataType::Char { length: *length },
785 DataType::Text => DataType::Char { length: None },
786
787 DataType::BigInt { .. } => DataType::Custom {
789 name: "SIGNED".to_string(),
790 },
791 DataType::Int { .. } => DataType::Custom {
792 name: "SIGNED".to_string(),
793 },
794 DataType::SmallInt { .. } => DataType::Custom {
795 name: "SIGNED".to_string(),
796 },
797 DataType::TinyInt { .. } => DataType::Custom {
798 name: "SIGNED".to_string(),
799 },
800 DataType::Boolean => DataType::Custom {
801 name: "SIGNED".to_string(),
802 },
803 DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64 => {
806 DataType::Custom {
807 name: "UNSIGNED".to_string(),
808 }
809 }
810
811 DataType::Custom { name } => {
813 let upper = name.to_uppercase();
814 match upper.as_str() {
815 "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" | "LONGBLOB" | "MEDIUMBLOB"
818 | "TINYBLOB" => DataType::Custom { name: upper },
819 "MEDIUMINT" => DataType::Custom {
821 name: "SIGNED".to_string(),
822 },
823 "UBIGINT" | "UINT" | "USMALLINT" | "UTINYINT" | "UMEDIUMINT" => {
825 DataType::Custom {
826 name: "UNSIGNED".to_string(),
827 }
828 }
829 _ => cast.to.clone(),
831 }
832 }
833
834 DataType::Binary { .. } => cast.to.clone(),
836 DataType::VarBinary { .. } => cast.to.clone(),
837 DataType::Date => cast.to.clone(),
838 DataType::Time { .. } => cast.to.clone(),
839 DataType::Decimal { .. } => cast.to.clone(),
840 DataType::Json => cast.to.clone(),
841 DataType::Float { .. } => cast.to.clone(),
842 DataType::Double { .. } => cast.to.clone(),
843 DataType::Char { .. } => cast.to.clone(),
844 DataType::CharacterSet { .. } => cast.to.clone(),
845 DataType::Enum { .. } => cast.to.clone(),
846 DataType::Set { .. } => cast.to.clone(),
847 DataType::Timestamp { .. } => cast.to.clone(),
848
849 _ => DataType::Char { length: None },
851 };
852
853 Cast {
854 this: cast.this,
855 to: new_type,
856 trailing_comments: cast.trailing_comments,
857 double_colon_syntax: cast.double_colon_syntax,
858 format: cast.format,
859 default: cast.default,
860 inferred_type: None,
861 }
862 }
863
864 fn transform_function(&self, f: Function) -> Result<Expression> {
865 let name_upper = f.name.to_uppercase();
866 match name_upper.as_str() {
867 "DATE_FORMAT" if f.args.len() >= 2 => {
869 let mut f = f;
870 if let Some(Expression::Literal(lit)) = f.args.get(1) {
871 if let Literal::String(fmt) = lit.as_ref() {
872 let normalized = Self::normalize_mysql_date_format(fmt);
873 if normalized != *fmt {
874 f.args[1] = Expression::Literal(Box::new(Literal::String(normalized)));
875 }
876 }
877 }
878 Ok(Expression::Function(Box::new(f)))
879 }
880
881 "NVL" if f.args.len() == 2 => {
883 let mut args = f.args;
884 let second = args.pop().unwrap();
885 let first = args.pop().unwrap();
886 Ok(Expression::IfNull(Box::new(BinaryFunc {
887 original_name: None,
888 this: first,
889 expression: second,
890 inferred_type: None,
891 })))
892 }
893
894 "ARRAY_AGG" if f.args.len() == 1 => {
899 let mut args = f.args;
900 Ok(Expression::Function(Box::new(Function::new(
901 "GROUP_CONCAT".to_string(),
902 vec![args.pop().unwrap()],
903 ))))
904 }
905
906 "STRING_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
908 Function::new("GROUP_CONCAT".to_string(), f.args),
909 ))),
910
911 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
913 seed: None,
914 lower: None,
915 upper: None,
916 }))),
917
918 "CURRENT_TIMESTAMP" => {
921 let precision = if let Some(Expression::Literal(lit)) = f.args.first() {
922 if let crate::expressions::Literal::Number(n) = lit.as_ref() {
923 n.parse::<u32>().ok()
924 } else {
925 None
926 }
927 } else {
928 None
929 };
930 Ok(Expression::CurrentTimestamp(
931 crate::expressions::CurrentTimestamp {
932 precision,
933 sysdate: false,
934 },
935 ))
936 }
937
938 "POSITION" if f.args.len() == 2 => Ok(Expression::Function(Box::new(Function::new(
941 "LOCATE".to_string(),
942 f.args,
943 )))),
944
945 "LENGTH" => Ok(Expression::Function(Box::new(f))),
948
949 "CEIL" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
951 "CEILING".to_string(),
952 f.args,
953 )))),
954
955 "STDDEV" => Ok(Expression::Function(Box::new(Function::new(
957 "STD".to_string(),
958 f.args,
959 )))),
960
961 "STDDEV_SAMP" => Ok(Expression::Function(Box::new(Function::new(
963 "STDDEV".to_string(),
964 f.args,
965 )))),
966
967 "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
969 "STR_TO_DATE".to_string(),
970 f.args,
971 )))),
972
973 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
975 "STR_TO_DATE".to_string(),
976 f.args,
977 )))),
978
979 "DATE_TRUNC" if f.args.len() >= 2 => {
982 let mut args = f.args;
985 let _unit = args.remove(0);
986 let date = args.remove(0);
987 Ok(Expression::Function(Box::new(Function::new(
988 "DATE".to_string(),
989 vec![date],
990 ))))
991 }
992
993 "COALESCE" if f.args.len() > 2 => Ok(Expression::Function(Box::new(f))),
997
998 "DAY" => Ok(Expression::Function(Box::new(Function::new(
1000 "DAYOFMONTH".to_string(),
1001 f.args,
1002 )))),
1003
1004 "DAYOFWEEK" => Ok(Expression::Function(Box::new(f))),
1006
1007 "DAYOFYEAR" => Ok(Expression::Function(Box::new(f))),
1009
1010 "WEEKOFYEAR" => Ok(Expression::Function(Box::new(f))),
1012
1013 "LAST_DAY" => Ok(Expression::Function(Box::new(f))),
1015
1016 "TIMESTAMPADD" => Ok(Expression::Function(Box::new(Function::new(
1018 "DATE_ADD".to_string(),
1019 f.args,
1020 )))),
1021
1022 "TIMESTAMPDIFF" => Ok(Expression::Function(Box::new(f))),
1024
1025 "CONVERT_TIMEZONE" if f.args.len() == 3 => {
1027 let mut args = f.args;
1028 let from_tz = args.remove(0);
1029 let to_tz = args.remove(0);
1030 let timestamp = args.remove(0);
1031 Ok(Expression::Function(Box::new(Function::new(
1032 "CONVERT_TZ".to_string(),
1033 vec![timestamp, from_tz, to_tz],
1034 ))))
1035 }
1036
1037 "UTC_TIMESTAMP" => Ok(Expression::Function(Box::new(f))),
1039
1040 "UTC_TIME" => Ok(Expression::Function(Box::new(f))),
1042
1043 "MAKETIME" => Ok(Expression::Function(Box::new(f))),
1045
1046 "TIME_FROM_PARTS" if f.args.len() == 3 => Ok(Expression::Function(Box::new(
1048 Function::new("MAKETIME".to_string(), f.args),
1049 ))),
1050
1051 "STUFF" if f.args.len() == 4 => Ok(Expression::Function(Box::new(Function::new(
1053 "INSERT".to_string(),
1054 f.args,
1055 )))),
1056
1057 "LOCATE" => Ok(Expression::Function(Box::new(f))),
1059
1060 "FIND_IN_SET" => Ok(Expression::Function(Box::new(f))),
1062
1063 "FORMAT" => Ok(Expression::Function(Box::new(f))),
1065
1066 "JSON_EXTRACT" => Ok(Expression::Function(Box::new(f))),
1068
1069 "JSON_UNQUOTE" => Ok(Expression::Function(Box::new(f))),
1071
1072 "JSON_EXTRACT_PATH_TEXT" if f.args.len() >= 2 => {
1074 let extract = Expression::Function(Box::new(Function::new(
1075 "JSON_EXTRACT".to_string(),
1076 f.args,
1077 )));
1078 Ok(Expression::Function(Box::new(Function::new(
1079 "JSON_UNQUOTE".to_string(),
1080 vec![extract],
1081 ))))
1082 }
1083
1084 "GEN_RANDOM_UUID" | "GENERATE_UUID" => Ok(Expression::Function(Box::new(
1086 Function::new("UUID".to_string(), vec![]),
1087 ))),
1088
1089 "DATABASE" => Ok(Expression::Function(Box::new(Function::new(
1091 "SCHEMA".to_string(),
1092 f.args,
1093 )))),
1094
1095 "INSTR" if f.args.len() == 2 => {
1098 let mut args = f.args;
1099 let str_arg = args.remove(0);
1100 let substr_arg = args.remove(0);
1101 Ok(Expression::Function(Box::new(Function::new(
1102 "LOCATE".to_string(),
1103 vec![substr_arg, str_arg],
1104 ))))
1105 }
1106
1107 "TIME_STR_TO_UNIX" => Ok(Expression::Function(Box::new(Function::new(
1109 "UNIX_TIMESTAMP".to_string(),
1110 f.args,
1111 )))),
1112
1113 "TIME_STR_TO_TIME" if f.args.len() >= 1 => {
1115 let mut args = f.args.into_iter();
1116 let arg = args.next().unwrap();
1117
1118 if args.next().is_some() {
1120 return Ok(Expression::Function(Box::new(Function::new(
1121 "TIMESTAMP".to_string(),
1122 vec![arg],
1123 ))));
1124 }
1125
1126 let precision = if let Expression::Literal(ref lit) = arg {
1128 if let crate::expressions::Literal::String(ref s) = lit.as_ref() {
1129 if let Some(dot_pos) = s.rfind('.') {
1131 let after_dot = &s[dot_pos + 1..];
1132 let frac_digits =
1134 after_dot.chars().take_while(|c| c.is_ascii_digit()).count();
1135 if frac_digits > 0 {
1136 if frac_digits <= 3 {
1138 Some(3)
1139 } else {
1140 Some(6)
1141 }
1142 } else {
1143 None
1144 }
1145 } else {
1146 None
1147 }
1148 } else {
1149 None
1150 }
1151 } else {
1152 None
1153 };
1154
1155 let type_name = match precision {
1156 Some(p) => format!("DATETIME({})", p),
1157 None => "DATETIME".to_string(),
1158 };
1159
1160 Ok(Expression::Cast(Box::new(Cast {
1161 this: arg,
1162 to: DataType::Custom { name: type_name },
1163 trailing_comments: Vec::new(),
1164 double_colon_syntax: false,
1165 format: None,
1166 default: None,
1167 inferred_type: None,
1168 })))
1169 }
1170
1171 "UCASE" => Ok(Expression::Function(Box::new(Function::new(
1173 "UPPER".to_string(),
1174 f.args,
1175 )))),
1176
1177 "LCASE" => Ok(Expression::Function(Box::new(Function::new(
1179 "LOWER".to_string(),
1180 f.args,
1181 )))),
1182
1183 "DAY_OF_MONTH" => Ok(Expression::Function(Box::new(Function::new(
1185 "DAYOFMONTH".to_string(),
1186 f.args,
1187 )))),
1188
1189 "DAY_OF_WEEK" => Ok(Expression::Function(Box::new(Function::new(
1191 "DAYOFWEEK".to_string(),
1192 f.args,
1193 )))),
1194
1195 "DAY_OF_YEAR" => Ok(Expression::Function(Box::new(Function::new(
1197 "DAYOFYEAR".to_string(),
1198 f.args,
1199 )))),
1200
1201 "WEEK_OF_YEAR" => Ok(Expression::Function(Box::new(Function::new(
1203 "WEEKOFYEAR".to_string(),
1204 f.args,
1205 )))),
1206
1207 "MOD" if f.args.len() == 2 => {
1209 let mut args = f.args;
1210 let left = args.remove(0);
1211 let right = args.remove(0);
1212 Ok(Expression::Mod(Box::new(BinaryOp {
1213 left,
1214 right,
1215 left_comments: Vec::new(),
1216 operator_comments: Vec::new(),
1217 trailing_comments: Vec::new(),
1218 inferred_type: None,
1219 })))
1220 }
1221
1222 "PARSE_JSON" if f.args.len() == 1 => Ok(f.args.into_iter().next().unwrap()),
1224
1225 "GET_PATH" if f.args.len() == 2 => {
1227 let mut args = f.args;
1228 let this = args.remove(0);
1229 let path = args.remove(0);
1230 let json_path = match &path {
1231 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
1232 let Literal::String(s) = lit.as_ref() else {
1233 unreachable!()
1234 };
1235 let s = Self::convert_bracket_to_quoted_path(s);
1237 let normalized = if s.starts_with('$') {
1238 s
1239 } else if s.starts_with('[') {
1240 format!("${}", s)
1241 } else {
1242 format!("$.{}", s)
1243 };
1244 Expression::Literal(Box::new(Literal::String(normalized)))
1245 }
1246 _ => path,
1247 };
1248 Ok(Expression::JsonExtract(Box::new(JsonExtractFunc {
1249 this,
1250 path: json_path,
1251 returning: None,
1252 arrow_syntax: false,
1253 hash_arrow_syntax: false,
1254 wrapper_option: None,
1255 quotes_option: None,
1256 on_scalar_string: false,
1257 on_error: None,
1258 })))
1259 }
1260
1261 "REGEXP" if f.args.len() >= 2 => Ok(Expression::Function(Box::new(Function::new(
1263 "REGEXP_LIKE".to_string(),
1264 f.args,
1265 )))),
1266
1267 "CURTIME" => Ok(Expression::CurrentTime(crate::expressions::CurrentTime {
1269 precision: None,
1270 })),
1271
1272 "TRUNC" => Ok(Expression::Function(Box::new(Function::new(
1274 "TRUNCATE".to_string(),
1275 f.args,
1276 )))),
1277
1278 _ => Ok(Expression::Function(Box::new(f))),
1280 }
1281 }
1282
1283 fn transform_aggregate_function(
1284 &self,
1285 f: Box<crate::expressions::AggregateFunction>,
1286 ) -> Result<Expression> {
1287 let name_upper = f.name.to_uppercase();
1288 match name_upper.as_str() {
1289 "STRING_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1291 Function::new("GROUP_CONCAT".to_string(), f.args),
1292 ))),
1293
1294 "ARRAY_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
1296 "GROUP_CONCAT".to_string(),
1297 f.args,
1298 )))),
1299
1300 _ => Ok(Expression::AggregateFunction(f)),
1302 }
1303 }
1304}
1305
1306#[cfg(test)]
1307mod tests {
1308 use super::*;
1309 use crate::dialects::Dialect;
1310
1311 fn transpile_to_mysql(sql: &str) -> String {
1312 let dialect = Dialect::get(DialectType::Generic);
1313 let result = dialect
1314 .transpile(sql, DialectType::MySQL)
1315 .expect("Transpile failed");
1316 result[0].clone()
1317 }
1318
1319 #[test]
1320 fn test_nvl_to_ifnull() {
1321 let result = transpile_to_mysql("SELECT NVL(a, b)");
1322 assert!(
1323 result.contains("IFNULL"),
1324 "Expected IFNULL, got: {}",
1325 result
1326 );
1327 }
1328
1329 #[test]
1330 fn test_coalesce_preserved() {
1331 let result = transpile_to_mysql("SELECT COALESCE(a, b)");
1333 assert!(
1334 result.contains("COALESCE"),
1335 "Expected COALESCE to be preserved, got: {}",
1336 result
1337 );
1338 }
1339
1340 #[test]
1341 fn test_random_to_rand() {
1342 let result = transpile_to_mysql("SELECT RANDOM()");
1343 assert!(result.contains("RAND"), "Expected RAND, got: {}", result);
1344 }
1345
1346 #[test]
1347 fn test_basic_select() {
1348 let result = transpile_to_mysql("SELECT a, b FROM users WHERE id = 1");
1349 assert!(result.contains("SELECT"));
1350 assert!(result.contains("FROM users"));
1351 }
1352
1353 #[test]
1354 fn test_string_agg_to_group_concat() {
1355 let result = transpile_to_mysql("SELECT STRING_AGG(name)");
1356 assert!(
1357 result.contains("GROUP_CONCAT"),
1358 "Expected GROUP_CONCAT, got: {}",
1359 result
1360 );
1361 }
1362
1363 #[test]
1364 fn test_array_agg_to_group_concat() {
1365 let result = transpile_to_mysql("SELECT ARRAY_AGG(name)");
1366 assert!(
1367 result.contains("GROUP_CONCAT"),
1368 "Expected GROUP_CONCAT, got: {}",
1369 result
1370 );
1371 }
1372
1373 #[test]
1374 fn test_to_date_to_str_to_date() {
1375 let result = transpile_to_mysql("SELECT TO_DATE('2023-01-01')");
1376 assert!(
1377 result.contains("STR_TO_DATE"),
1378 "Expected STR_TO_DATE, got: {}",
1379 result
1380 );
1381 }
1382
1383 #[test]
1384 fn test_backtick_identifiers() {
1385 let dialect = MySQLDialect;
1387 let config = dialect.generator_config();
1388 assert_eq!(config.identifier_quote, '`');
1389 }
1390
1391 #[test]
1392 fn test_create_procedure_signal_sqlstate_regression() {
1393 let sql = "CREATE PROCEDURE CheckSalary(IN p_salary DECIMAL(10,2))
1394BEGIN
1395 IF p_salary <= 0 THEN
1396 SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Salary must be positive';
1397 END IF;
1398 INSERT INTO employees (salary) VALUES (p_salary);
1399END;";
1400
1401 let dialect = Dialect::get(DialectType::MySQL);
1402 let ast = dialect.parse(sql).expect("Parse failed");
1403 let output = dialect.generate(&ast[0]).expect("Generate failed");
1404
1405 assert!(output.contains("CREATE PROCEDURE CheckSalary"));
1406 assert!(output.contains("IF p_salary <= 0 THEN"));
1407 assert!(
1408 output.contains("SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Salary must be positive'")
1409 );
1410 assert!(output.contains("END IF"));
1411 assert!(output.contains("INSERT INTO employees (salary) VALUES (p_salary)"));
1412 }
1413
1414 #[test]
1415 fn test_mysql_functional_index_key_parts() {
1416 let dialect = Dialect::get(DialectType::MySQL);
1417 let issue_sql = "CREATE TABLE `test_report` (
1418 `id` bigint NOT NULL AUTO_INCREMENT,
1419 `batch_no` varchar(64) NOT NULL,
1420 PRIMARY KEY (`id`),
1421 KEY `idx_clean_batch` ((trim(leading _utf8mb4'0' from `batch_no`)))
1422) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
1423 let ast = dialect.parse(issue_sql).expect("Issue #371 should parse");
1424 let output = dialect.generate(&ast[0]).expect("Generate failed");
1425 assert!(
1426 output.contains("INDEX idx_clean_batch ((TRIM(LEADING _utf8mb4 '0' FROM `batch_no`)))"),
1427 "Unexpected output: {output}"
1428 );
1429
1430 for (sql, expected) in [
1431 (
1432 "CREATE TABLE t (c VARCHAR(20), d INT, KEY idx (d, (LOWER(c)) DESC))",
1433 "INDEX idx (d, (LOWER(c)) DESC)",
1434 ),
1435 (
1436 "CREATE TABLE t (c VARCHAR(20), UNIQUE INDEX idx ((LOWER(c))))",
1437 "UNIQUE idx ((LOWER(c)))",
1438 ),
1439 (
1440 "ALTER TABLE t ADD INDEX idx ((LOWER(c)))",
1441 "ADD INDEX idx ((LOWER(c)))",
1442 ),
1443 (
1444 "CREATE INDEX idx ON t ((LOWER(c)))",
1445 "CREATE INDEX idx ON t((LOWER(c)))",
1446 ),
1447 (
1448 "CREATE INDEX idx ON t (c(10), d DESC)",
1449 "CREATE INDEX idx ON t(c(10), d DESC)",
1450 ),
1451 ] {
1452 let ast = dialect
1453 .parse(sql)
1454 .unwrap_or_else(|error| panic!("{sql}: {error}"));
1455 let output = dialect.generate(&ast[0]).expect("Generate failed");
1456 assert!(
1457 output.contains(expected),
1458 "SQL: {sql}\nExpected: {expected}\nActual: {output}"
1459 );
1460 assert!(!output.contains('?'), "Expression was lost: {output}");
1461 }
1462 }
1463
1464 fn mysql_identity(sql: &str, expected: &str) {
1465 let dialect = Dialect::get(DialectType::MySQL);
1466 let ast = dialect.parse(sql).expect("Parse failed");
1467 let transformed = dialect.transform(ast[0].clone()).expect("Transform failed");
1468 let result = dialect.generate(&transformed).expect("Generate failed");
1469 assert_eq!(result, expected, "SQL: {}", sql);
1470 }
1471
1472 #[test]
1473 fn test_ucase_to_upper() {
1474 mysql_identity("SELECT UCASE('foo')", "SELECT UPPER('foo')");
1475 }
1476
1477 #[test]
1478 fn test_lcase_to_lower() {
1479 mysql_identity("SELECT LCASE('foo')", "SELECT LOWER('foo')");
1480 }
1481
1482 #[test]
1483 fn test_day_of_month() {
1484 mysql_identity(
1485 "SELECT DAY_OF_MONTH('2023-01-01')",
1486 "SELECT DAYOFMONTH('2023-01-01')",
1487 );
1488 }
1489
1490 #[test]
1491 fn test_day_of_week() {
1492 mysql_identity(
1493 "SELECT DAY_OF_WEEK('2023-01-01')",
1494 "SELECT DAYOFWEEK('2023-01-01')",
1495 );
1496 }
1497
1498 #[test]
1499 fn test_day_of_year() {
1500 mysql_identity(
1501 "SELECT DAY_OF_YEAR('2023-01-01')",
1502 "SELECT DAYOFYEAR('2023-01-01')",
1503 );
1504 }
1505
1506 #[test]
1507 fn test_week_of_year() {
1508 mysql_identity(
1509 "SELECT WEEK_OF_YEAR('2023-01-01')",
1510 "SELECT WEEKOFYEAR('2023-01-01')",
1511 );
1512 }
1513
1514 #[test]
1515 fn test_mod_func_to_percent() {
1516 mysql_identity("MOD(x, y)", "x % y");
1518 }
1519
1520 #[test]
1521 fn test_database_to_schema() {
1522 mysql_identity("DATABASE()", "SCHEMA()");
1523 }
1524
1525 #[test]
1526 fn test_and_operator() {
1527 mysql_identity("SELECT 1 && 0", "SELECT 1 AND 0");
1528 }
1529
1530 #[test]
1531 fn test_or_operator() {
1532 mysql_identity("SELECT a || b", "SELECT a OR b");
1533 }
1534}