fraiseql_db/dialect/
mysql.rs1use std::borrow::Cow;
4
5use super::trait_def::{RowViewColumnType, SqlDialect, UnsupportedOperator};
6
7pub struct MySqlDialect;
11
12impl SqlDialect for MySqlDialect {
13 fn name(&self) -> &'static str {
14 "MySQL"
15 }
16
17 fn quote_identifier(&self, name: &str) -> String {
18 format!("`{}`", name.replace('`', "``"))
19 }
20
21 fn json_extract_scalar(&self, column: &str, path: &[String]) -> String {
22 let json_path = crate::path_escape::escape_mysql_json_path(path);
23 format!("JSON_UNQUOTE(JSON_EXTRACT({column}, '{json_path}'))")
24 }
25
26 fn placeholder(&self, _n: usize) -> String {
27 "?".to_string()
28 }
29
30 fn cast_to_numeric<'a>(&self, expr: &'a str) -> Cow<'a, str> {
31 Cow::Owned(format!("CAST({expr} AS DECIMAL)"))
32 }
33
34 fn ilike_sql(&self, lhs: &str, rhs: &str) -> String {
35 format!("LOWER({lhs}) LIKE LOWER({rhs})")
38 }
39
40 fn concat_sql(&self, parts: &[&str]) -> String {
41 format!("CONCAT({})", parts.join(", "))
42 }
43
44 fn json_array_length(&self, expr: &str) -> String {
45 format!("JSON_LENGTH({expr})")
46 }
47
48 fn array_contains_sql(&self, lhs: &str, rhs: &str) -> Result<String, UnsupportedOperator> {
49 Ok(format!("JSON_CONTAINS({lhs}, {rhs})"))
50 }
51
52 fn array_overlaps_sql(&self, lhs: &str, rhs: &str) -> Result<String, UnsupportedOperator> {
53 Ok(format!("JSON_OVERLAPS({lhs}, {rhs})"))
54 }
55
56 fn row_view_column_expr(
57 &self,
58 json_column: &str,
59 field_name: &str,
60 col_type: &RowViewColumnType,
61 ) -> String {
62 let mysql_type = match col_type {
63 RowViewColumnType::Text | RowViewColumnType::Uuid => "CHAR",
64 RowViewColumnType::Int32 => "SIGNED",
65 RowViewColumnType::Int64 => "SIGNED",
66 RowViewColumnType::Float64 => "DOUBLE",
67 RowViewColumnType::Boolean => "UNSIGNED",
68 RowViewColumnType::Timestamptz => "DATETIME",
69 RowViewColumnType::Date => "DATE",
70 RowViewColumnType::Json => "JSON",
71 };
72 format!("CAST(JSON_UNQUOTE(JSON_EXTRACT({json_column}, '$.{field_name}')) AS {mysql_type})")
73 }
74
75 fn fts_matches_sql(&self, expr: &str, param: &str) -> Result<String, UnsupportedOperator> {
77 Ok(format!("MATCH({expr}) AGAINST({param} IN NATURAL LANGUAGE MODE)"))
78 }
79
80 fn fts_plain_query_sql(&self, expr: &str, param: &str) -> Result<String, UnsupportedOperator> {
81 Ok(format!("MATCH({expr}) AGAINST({param} IN BOOLEAN MODE)"))
82 }
83
84 fn fts_phrase_query_sql(&self, expr: &str, param: &str) -> Result<String, UnsupportedOperator> {
85 Ok(format!("MATCH({expr}) AGAINST({param} IN NATURAL LANGUAGE MODE)"))
86 }
87
88 fn regex_sql(
91 &self,
92 lhs: &str,
93 rhs: &str,
94 _case_insensitive: bool,
95 negate: bool,
96 ) -> Result<String, UnsupportedOperator> {
97 if negate {
100 Ok(format!("{lhs} NOT REGEXP {rhs}"))
101 } else {
102 Ok(format!("{lhs} REGEXP {rhs}"))
103 }
104 }
105}