Skip to main content

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_select_into(&self, _: &SelectInto, _: &mut impl SqlWriter) {
19        // MySQL doesn't support SELECT INOT table clause (only into variable but that is
20        // different).
21    }
22
23    fn prepare_index_hints(
24        &self,
25        table_ref: &TableRef,
26        select: &SelectStatement,
27        sql: &mut impl SqlWriter,
28    ) {
29        let Some(hints) = select.index_hints.get(&table_ref.into()) else {
30            return;
31        };
32        sql.write_str(" ").unwrap();
33
34        let mut hints = hints.iter();
35        join_io!(
36            hints,
37            hint,
38            join {
39                sql.write_str(" ").unwrap();
40            },
41            do {
42                match hint.r#type {
43                    IndexHintType::Use => {
44                        sql.write_str("USE INDEX ").unwrap();
45                        self.prepare_index_hint_scope(&hint.scope, sql);
46                        sql.write_str("(").unwrap();
47                        self.prepare_iden(&hint.index, sql);
48                    }
49                    IndexHintType::Ignore => {
50                        sql.write_str("IGNORE INDEX ").unwrap();
51                        self.prepare_index_hint_scope(&hint.scope, sql);
52                        sql.write_str("(").unwrap();
53                        self.prepare_iden(&hint.index, sql);
54                    }
55                    IndexHintType::Force => {
56                        sql.write_str("FORCE INDEX ").unwrap();
57                        self.prepare_index_hint_scope(&hint.scope, sql);
58                        sql.write_str("(").unwrap();
59                        self.prepare_iden(&hint.index, sql);
60                    }
61                }
62                sql.write_str(")").unwrap();
63            }
64        );
65    }
66
67    fn prepare_query_statement(&self, query: &SubQueryStatement, sql: &mut impl SqlWriter) {
68        query.prepare_statement(self, sql);
69    }
70
71    fn prepare_explain_statement(&self, explain: &ExplainStatement, sql: &mut impl SqlWriter) {
72        sql.write_str("EXPLAIN").unwrap();
73        if let Some(table) = &explain.mysql_opts.table {
74            sql.write_str(" ").unwrap();
75            self.prepare_table_ref(table, sql);
76            if let Some(target) = &explain.mysql_opts.target {
77                match target {
78                    ExplainTableTarget::Column(column) => {
79                        sql.write_str(" ").unwrap();
80                        self.prepare_iden(column, sql);
81                    }
82                    ExplainTableTarget::Wildcard(wildcard) => {
83                        sql.write_str(" '").unwrap();
84                        if self.needs_escape(wildcard) {
85                            sql.write_str(&self.escape_string(wildcard)).unwrap();
86                        } else {
87                            sql.write_str(wildcard).unwrap();
88                        }
89                        sql.write_str("'").unwrap();
90                    }
91                }
92            }
93            return;
94        }
95
96        if let Some(analyze) = explain.analyze {
97            if analyze {
98                sql.write_str(" ANALYZE").unwrap();
99            } else {
100                sql.write_str(" ANALYZE FALSE").unwrap();
101            }
102        }
103
104        if let Some(format) = explain.format {
105            sql.write_str(" FORMAT = ").unwrap();
106            sql.write_str(format.as_str()).unwrap();
107        }
108
109        if let Some(variable) = &explain.mysql_opts.into_variable {
110            sql.write_str(" INTO ").unwrap();
111            sql.write_str(variable).unwrap();
112        }
113
114        if let Some(schema) = &explain.mysql_opts.schema_spec {
115            match schema {
116                MySqlExplainSchemaSpec::Schema(schema) => {
117                    sql.write_str(" FOR SCHEMA ").unwrap();
118                    self.prepare_iden(schema, sql);
119                }
120                MySqlExplainSchemaSpec::Database(schema) => {
121                    sql.write_str(" FOR DATABASE ").unwrap();
122                    self.prepare_iden(schema, sql);
123                }
124            }
125        }
126
127        if let Some(connection_id) = explain.mysql_opts.for_connection {
128            sql.write_str(" FOR CONNECTION ").unwrap();
129            write_int(sql, connection_id);
130            return;
131        }
132
133        if let Some(statement) = &explain.statement {
134            sql.write_str(" ").unwrap();
135            statement.write_to(self, sql);
136        }
137    }
138
139    fn prepare_with_clause_recursive_options(&self, _: &WithClause, _: &mut impl SqlWriter) {
140        // MySQL doesn't support sql recursive with query 'SEARCH' and 'CYCLE' options.
141    }
142
143    fn prepare_with_query_clause_materialization(
144        &self,
145        _: &CommonTableExpression,
146        _: &mut impl SqlWriter,
147    ) {
148        // MySQL doesn't support declaring materialization in SQL for with query.
149    }
150
151    fn prepare_update_join(
152        &self,
153        from: &[TableRef],
154        condition: &ConditionHolder,
155        sql: &mut impl SqlWriter,
156    ) {
157        if from.is_empty() {
158            return;
159        }
160
161        sql.write_str(" JOIN ").unwrap();
162
163        // TODO what if we have multiple from?
164        self.prepare_table_ref(&from[0], sql);
165
166        self.prepare_condition(condition, "ON", sql);
167    }
168
169    fn prepare_update_from(&self, _: &[TableRef], _: &mut impl SqlWriter) {}
170
171    fn prepare_update_column(
172        &self,
173        table: &Option<Box<TableRef>>,
174        from: &[TableRef],
175        column: &DynIden,
176        sql: &mut impl SqlWriter,
177    ) {
178        use std::ops::Deref;
179
180        if !from.is_empty() {
181            if let Some(table) = table {
182                // Support only "naked" table names with no schema or alias.
183                if let TableRef::Table(TableName(None, table), None) = table.deref() {
184                    let column_name = ColumnName::from((table.clone(), column.clone()));
185                    self.prepare_column_ref(&ColumnRef::Column(column_name), sql);
186                    return;
187                }
188            }
189        }
190        self.prepare_iden(column, sql)
191    }
192
193    fn prepare_update_condition(
194        &self,
195        from: &[TableRef],
196        condition: &ConditionHolder,
197        sql: &mut impl SqlWriter,
198    ) {
199        if !from.is_empty() {
200            return;
201        }
202        self.prepare_condition(condition, "WHERE", sql);
203    }
204
205    fn prepare_join_type(&self, join_type: &JoinType, sql: &mut impl SqlWriter) {
206        match join_type {
207            JoinType::FullOuterJoin => panic!("Mysql does not support FULL OUTER JOIN"),
208            _ => self.prepare_join_type_common(join_type, sql),
209        }
210    }
211
212    fn prepare_order_expr(&self, order_expr: &OrderExpr, sql: &mut impl SqlWriter) {
213        match order_expr.nulls {
214            None => (),
215            Some(NullOrdering::Last) => {
216                self.prepare_expr(&order_expr.expr, sql);
217                sql.write_str(" IS NULL ASC, ").unwrap()
218            }
219            Some(NullOrdering::First) => {
220                self.prepare_expr(&order_expr.expr, sql);
221                sql.write_str(" IS NULL DESC, ").unwrap()
222            }
223        }
224        if !matches!(order_expr.order, Order::Field(_)) {
225            self.prepare_expr(&order_expr.expr, sql);
226        }
227        self.prepare_order(order_expr, sql);
228    }
229
230    fn prepare_value(&self, value: Value, sql: &mut impl SqlWriter) {
231        sql.push_param(value, self as _);
232    }
233
234    fn prepare_on_conflict_target(&self, _: &OnConflictTarget, _: &mut impl SqlWriter) {
235        // MySQL doesn't support declaring ON CONFLICT target.
236    }
237
238    fn prepare_on_conflict_action(
239        &self,
240        on_conflict_action: &Option<OnConflictAction>,
241        sql: &mut impl SqlWriter,
242    ) {
243        match on_conflict_action {
244            Some(OnConflictAction::DoNothing(pk_cols)) => {
245                if !pk_cols.is_empty() {
246                    self.prepare_on_conflict_do_update_keywords(sql);
247                    let mut pk_cols_iter = pk_cols.iter();
248                    join_io!(
249                        pk_cols_iter,
250                        pk_col,
251                        join {
252                            sql.write_str(", ").unwrap();
253                        },
254                        do {
255                            self.prepare_iden(pk_col, sql);
256                            sql.write_str(" = ").unwrap();
257                            self.prepare_iden(pk_col, sql);
258                        }
259                    );
260                } else {
261                    sql.write_str(" IGNORE").unwrap();
262                }
263            }
264            _ => self.prepare_on_conflict_action_common(on_conflict_action, sql),
265        }
266    }
267
268    fn prepare_on_conflict_keywords(&self, sql: &mut impl SqlWriter) {
269        sql.write_str(" ON DUPLICATE KEY").unwrap();
270    }
271
272    fn prepare_on_conflict_do_update_keywords(&self, sql: &mut impl SqlWriter) {
273        sql.write_str(" UPDATE ").unwrap();
274    }
275
276    fn prepare_on_conflict_excluded_table(&self, col: &DynIden, sql: &mut impl SqlWriter) {
277        sql.write_str("VALUES(").unwrap();
278        self.prepare_iden(col, sql);
279        sql.write_str(")").unwrap();
280    }
281
282    fn prepare_on_conflict_condition(&self, _: &ConditionHolder, _: &mut impl SqlWriter) {}
283
284    fn prepare_returning(&self, _returning: &Option<ReturningClause>, _sql: &mut impl SqlWriter) {}
285
286    fn random_function(&self) -> &str {
287        "RAND"
288    }
289
290    fn lock_phrase(&self, lock_type: LockType) -> &'static str {
291        match lock_type {
292            LockType::Update => "FOR UPDATE",
293            LockType::NoKeyUpdate => "FOR NO KEY UPDATE",
294            LockType::Share => "LOCK IN SHARE MODE",
295            LockType::KeyShare => "FOR KEY SHARE",
296        }
297    }
298
299    fn insert_default_keyword(&self) -> &str {
300        "()"
301    }
302}
303
304impl MysqlQueryBuilder {
305    fn prepare_index_hint_scope(
306        &self,
307        index_hint_scope: &IndexHintScope,
308        sql: &mut impl SqlWriter,
309    ) {
310        match index_hint_scope {
311            IndexHintScope::Join => {
312                sql.write_str("FOR JOIN ").unwrap();
313            }
314            IndexHintScope::OrderBy => {
315                sql.write_str("FOR ORDER BY ").unwrap();
316            }
317            IndexHintScope::GroupBy => {
318                sql.write_str("FOR GROUP BY ").unwrap();
319            }
320            IndexHintScope::All => {}
321        }
322    }
323}