sea_query/backend/mysql/
query.rs

1use super::*;
2use crate::extension::mysql::*;
3
4impl QueryBuilder for MysqlQueryBuilder {
5    fn values_list_tuple_prefix(&self) -> &str {
6        "ROW"
7    }
8
9    fn prepare_select_distinct(&self, select_distinct: &SelectDistinct, sql: &mut impl SqlWriter) {
10        match select_distinct {
11            SelectDistinct::All => sql.write_str("ALL").unwrap(),
12            SelectDistinct::Distinct => sql.write_str("DISTINCT").unwrap(),
13            SelectDistinct::DistinctRow => sql.write_str("DISTINCTROW").unwrap(),
14            _ => {}
15        };
16    }
17
18    fn prepare_index_hints(
19        &self,
20        table_ref: &TableRef,
21        select: &SelectStatement,
22        sql: &mut impl SqlWriter,
23    ) {
24        let Some(hints) = select.index_hints.get(&table_ref.into()) else {
25            return;
26        };
27        sql.write_str(" ").unwrap();
28
29        let mut hints = hints.iter();
30        join_io!(
31            hints,
32            hint,
33            join {
34                sql.write_str(" ").unwrap();
35            },
36            do {
37                match hint.r#type {
38                    IndexHintType::Use => {
39                        sql.write_str("USE INDEX ").unwrap();
40                        self.prepare_index_hint_scope(&hint.scope, sql);
41                        sql.write_str("(").unwrap();
42                        self.prepare_iden(&hint.index, sql);
43                    }
44                    IndexHintType::Ignore => {
45                        sql.write_str("IGNORE INDEX ").unwrap();
46                        self.prepare_index_hint_scope(&hint.scope, sql);
47                        sql.write_str("(").unwrap();
48                        self.prepare_iden(&hint.index, sql);
49                    }
50                    IndexHintType::Force => {
51                        sql.write_str("FORCE INDEX ").unwrap();
52                        self.prepare_index_hint_scope(&hint.scope, sql);
53                        sql.write_str("(").unwrap();
54                        self.prepare_iden(&hint.index, sql);
55                    }
56                }
57                sql.write_str(")").unwrap();
58            }
59        );
60    }
61
62    fn prepare_query_statement(&self, query: &SubQueryStatement, sql: &mut impl SqlWriter) {
63        query.prepare_statement(self, sql);
64    }
65
66    fn prepare_with_clause_recursive_options(&self, _: &WithClause, _: &mut impl SqlWriter) {
67        // MySQL doesn't support sql recursive with query 'SEARCH' and 'CYCLE' options.
68    }
69
70    fn prepare_with_query_clause_materialization(
71        &self,
72        _: &CommonTableExpression,
73        _: &mut impl SqlWriter,
74    ) {
75        // MySQL doesn't support declaring materialization in SQL for with query.
76    }
77
78    fn prepare_update_join(
79        &self,
80        from: &[TableRef],
81        condition: &ConditionHolder,
82        sql: &mut impl SqlWriter,
83    ) {
84        if from.is_empty() {
85            return;
86        }
87
88        sql.write_str(" JOIN ").unwrap();
89
90        // TODO what if we have multiple from?
91        self.prepare_table_ref(&from[0], sql);
92
93        self.prepare_condition(condition, "ON", sql);
94    }
95
96    fn prepare_update_from(&self, _: &[TableRef], _: &mut impl SqlWriter) {}
97
98    fn prepare_update_column(
99        &self,
100        table: &Option<Box<TableRef>>,
101        from: &[TableRef],
102        column: &DynIden,
103        sql: &mut impl SqlWriter,
104    ) {
105        use std::ops::Deref;
106
107        if !from.is_empty() {
108            if let Some(table) = table {
109                // Support only "naked" table names with no schema or alias.
110                if let TableRef::Table(TableName(None, table), None) = table.deref() {
111                    let column_name = ColumnName::from((table.clone(), column.clone()));
112                    self.prepare_column_ref(&ColumnRef::Column(column_name), sql);
113                    return;
114                }
115            }
116        }
117        self.prepare_iden(column, sql)
118    }
119
120    fn prepare_update_condition(
121        &self,
122        from: &[TableRef],
123        condition: &ConditionHolder,
124        sql: &mut impl SqlWriter,
125    ) {
126        if !from.is_empty() {
127            return;
128        }
129        self.prepare_condition(condition, "WHERE", sql);
130    }
131
132    fn prepare_join_type(&self, join_type: &JoinType, sql: &mut impl SqlWriter) {
133        match join_type {
134            JoinType::FullOuterJoin => panic!("Mysql does not support FULL OUTER JOIN"),
135            _ => self.prepare_join_type_common(join_type, sql),
136        }
137    }
138
139    fn prepare_order_expr(&self, order_expr: &OrderExpr, sql: &mut impl SqlWriter) {
140        match order_expr.nulls {
141            None => (),
142            Some(NullOrdering::Last) => {
143                self.prepare_expr(&order_expr.expr, sql);
144                sql.write_str(" IS NULL ASC, ").unwrap()
145            }
146            Some(NullOrdering::First) => {
147                self.prepare_expr(&order_expr.expr, sql);
148                sql.write_str(" IS NULL DESC, ").unwrap()
149            }
150        }
151        if !matches!(order_expr.order, Order::Field(_)) {
152            self.prepare_expr(&order_expr.expr, sql);
153        }
154        self.prepare_order(order_expr, sql);
155    }
156
157    fn prepare_value(&self, value: Value, sql: &mut impl SqlWriter) {
158        sql.push_param(value, self as _);
159    }
160
161    fn prepare_on_conflict_target(&self, _: &[OnConflictTarget], _: &mut impl SqlWriter) {
162        // MySQL doesn't support declaring ON CONFLICT target.
163    }
164
165    fn prepare_on_conflict_action(
166        &self,
167        on_conflict_action: &Option<OnConflictAction>,
168        sql: &mut impl SqlWriter,
169    ) {
170        match on_conflict_action {
171            Some(OnConflictAction::DoNothing(pk_cols)) => {
172                if !pk_cols.is_empty() {
173                    self.prepare_on_conflict_do_update_keywords(sql);
174                    let mut pk_cols_iter = pk_cols.iter();
175                    join_io!(
176                        pk_cols_iter,
177                        pk_col,
178                        join {
179                            sql.write_str(", ").unwrap();
180                        },
181                        do {
182                            self.prepare_iden(pk_col, sql);
183                            sql.write_str(" = ").unwrap();
184                            self.prepare_iden(pk_col, sql);
185                        }
186                    );
187                } else {
188                    sql.write_str(" IGNORE").unwrap();
189                }
190            }
191            _ => self.prepare_on_conflict_action_common(on_conflict_action, sql),
192        }
193    }
194
195    fn prepare_on_conflict_keywords(&self, sql: &mut impl SqlWriter) {
196        sql.write_str(" ON DUPLICATE KEY").unwrap();
197    }
198
199    fn prepare_on_conflict_do_update_keywords(&self, sql: &mut impl SqlWriter) {
200        sql.write_str(" UPDATE ").unwrap();
201    }
202
203    fn prepare_on_conflict_excluded_table(&self, col: &DynIden, sql: &mut impl SqlWriter) {
204        sql.write_str("VALUES(").unwrap();
205        self.prepare_iden(col, sql);
206        sql.write_str(")").unwrap();
207    }
208
209    fn prepare_on_conflict_condition(&self, _: &ConditionHolder, _: &mut impl SqlWriter) {}
210
211    fn prepare_returning(&self, _returning: &Option<ReturningClause>, _sql: &mut impl SqlWriter) {}
212
213    fn random_function(&self) -> &str {
214        "RAND"
215    }
216
217    fn lock_phrase(&self, lock_type: LockType) -> &'static str {
218        match lock_type {
219            LockType::Update => "FOR UPDATE",
220            LockType::NoKeyUpdate => "FOR NO KEY UPDATE",
221            LockType::Share => "LOCK IN SHARE MODE",
222            LockType::KeyShare => "FOR KEY SHARE",
223        }
224    }
225
226    fn insert_default_keyword(&self) -> &str {
227        "()"
228    }
229}
230
231impl MysqlQueryBuilder {
232    fn prepare_index_hint_scope(
233        &self,
234        index_hint_scope: &IndexHintScope,
235        sql: &mut impl SqlWriter,
236    ) {
237        match index_hint_scope {
238            IndexHintScope::Join => {
239                sql.write_str("FOR JOIN ").unwrap();
240            }
241            IndexHintScope::OrderBy => {
242                sql.write_str("FOR ORDER BY ").unwrap();
243            }
244            IndexHintScope::GroupBy => {
245                sql.write_str("FOR GROUP BY ").unwrap();
246            }
247            IndexHintScope::All => {}
248        }
249    }
250}