sea_query/extension/mysql/
select.rs

1use super::{IndexHint, IndexHintScope, IndexHintType};
2use crate::{IntoIden, IntoTableRef, SelectStatement};
3
4macro_rules! get_or_insert_index {
5    ($self:ident, $table:expr, $index:expr, $ty:expr, $scope:expr) => {
6        let key = $table.into();
7        match $self.index_hints.get_mut(&key) {
8            Some(value) => value.push(IndexHint {
9                index: $index,
10                r#type: $ty,
11                scope: $scope,
12            }),
13            None => {
14                $self.index_hints.insert(
15                    key,
16                    vec![IndexHint {
17                        index: $index,
18                        r#type: $ty,
19                        scope: $scope,
20                    }],
21                );
22            }
23        };
24    };
25}
26
27pub trait MySqlSelectStatementExt {
28    fn use_index<I>(&mut self, index: I, scope: IndexHintScope) -> &mut Self
29    where
30        I: IntoIden;
31
32    fn use_index_on<T, I>(&mut self, table: T, index: I, scope: IndexHintScope) -> &mut Self
33    where
34        T: IntoTableRef,
35        I: IntoIden;
36
37    fn force_index<I>(&mut self, index: I, scope: IndexHintScope) -> &mut Self
38    where
39        I: IntoIden;
40
41    fn force_index_on<T, I>(&mut self, table: T, index: I, scope: IndexHintScope) -> &mut Self
42    where
43        T: IntoTableRef,
44        I: IntoIden;
45
46    fn ignore_index<I>(&mut self, index: I, scope: IndexHintScope) -> &mut Self
47    where
48        I: IntoIden;
49
50    fn ignore_index_on<T, I>(&mut self, table: T, index: I, scope: IndexHintScope) -> &mut Self
51    where
52        T: IntoTableRef,
53        I: IntoIden;
54}
55
56const PANIC_MSG: &str =
57    "No table in from clause, you should specify the table before using index hint";
58
59impl MySqlSelectStatementExt for SelectStatement {
60    /// USE INDEX hint on the last table in the from clause for MySQL
61    ///
62    /// Give the optimizer information about how to choose indexes during query processing.
63    /// See [MySQL reference manual for Index Hints](https://dev.mysql.com/doc/refman/8.0/en/index-hints.html)
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use sea_query::{extension::mysql::*, tests_cfg::*, *};
69    ///
70    /// let query = Query::select()
71    ///     .from(Char::Table)
72    ///     .use_index(IndexName::new("IDX_123456"), IndexHintScope::All)
73    ///     .column(Char::SizeW)
74    ///     .to_owned();
75    ///
76    /// assert_eq!(
77    ///     query.to_string(MysqlQueryBuilder),
78    ///     r#"SELECT `size_w` FROM `character` USE INDEX (`IDX_123456`)"#
79    /// );
80    /// ```
81    fn use_index<I>(&mut self, index: I, scope: IndexHintScope) -> &mut Self
82    where
83        I: IntoIden,
84    {
85        let table_ref = self.from.last().expect(PANIC_MSG);
86
87        get_or_insert_index!(
88            self,
89            table_ref,
90            index.into_iden(),
91            IndexHintType::Use,
92            scope
93        );
94
95        self
96    }
97
98    /// USE INDEX hint on the specified in the from clause for MySQL
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use sea_query::{extension::mysql::*, tests_cfg::*, *};
104    ///
105    /// let query = Query::select()
106    ///     .from(Font::Table)
107    ///     .from(Char::Table)
108    ///     .use_index_on(Char::Table, IndexName::new("IDX_123456"), IndexHintScope::All)
109    ///     .use_index_on(Font::Table, IndexName::new("IDX_654321"), IndexHintScope::All)
110    ///     .column(Char::SizeW)
111    ///     .to_owned();
112    ///
113    /// assert_eq!(
114    ///     query.to_string(MysqlQueryBuilder),
115    ///     r#"SELECT `size_w` FROM `font` USE INDEX (`IDX_654321`), `character` USE INDEX (`IDX_123456`)"#
116    /// );
117    /// ```
118    fn use_index_on<T, I>(&mut self, table: T, index: I, scope: IndexHintScope) -> &mut Self
119    where
120        T: IntoTableRef,
121        I: IntoIden,
122    {
123        get_or_insert_index!(
124            self,
125            table.into_table_ref(),
126            index.into_iden(),
127            IndexHintType::Use,
128            scope
129        );
130
131        self
132    }
133
134    /// FORCE INDEX hint on the last table in from clause for MySQL
135    ///
136    /// Give the optimizer information about how to choose indexes during query processing.
137    /// See [MySQL reference manual for Index Hints](https://dev.mysql.com/doc/refman/8.0/en/index-hints.html)
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// use sea_query::{extension::mysql::*, tests_cfg::*, *};
143    ///
144    /// let query = Query::select()
145    ///     .from(Char::Table)
146    ///     .force_index(IndexName::new("IDX_123456"), IndexHintScope::All)
147    ///     .column(Char::SizeW)
148    ///     .to_owned();
149    ///
150    /// assert_eq!(
151    ///     query.to_string(MysqlQueryBuilder),
152    ///     r#"SELECT `size_w` FROM `character` FORCE INDEX (`IDX_123456`)"#
153    /// );
154    /// ```
155    fn force_index<I>(&mut self, index: I, scope: IndexHintScope) -> &mut Self
156    where
157        I: IntoIden,
158    {
159        let table_ref = self.from.last().expect(PANIC_MSG);
160
161        get_or_insert_index!(
162            self,
163            table_ref,
164            index.into_iden(),
165            IndexHintType::Force,
166            scope
167        );
168
169        self
170    }
171
172    /// FORCE INDEX hint on the specified in the from clause for MySQL
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use sea_query::{extension::mysql::*, tests_cfg::*, *};
178    ///
179    /// let query = Query::select()
180    ///     .from(Font::Table)
181    ///     .from(Char::Table)
182    ///     .force_index_on(Char::Table, IndexName::new("IDX_123456"), IndexHintScope::All)
183    ///     .force_index_on(Font::Table, IndexName::new("IDX_654321"), IndexHintScope::All)
184    ///     .column(Char::SizeW)
185    ///     .to_owned();
186    ///
187    /// assert_eq!(
188    ///     query.to_string(MysqlQueryBuilder),
189    ///     r#"SELECT `size_w` FROM `font` FORCE INDEX (`IDX_654321`), `character` FORCE INDEX (`IDX_123456`)"#
190    /// );
191    /// ```
192    fn force_index_on<T, I>(&mut self, table: T, index: I, scope: IndexHintScope) -> &mut Self
193    where
194        T: IntoTableRef,
195        I: IntoIden,
196    {
197        get_or_insert_index!(
198            self,
199            table.into_table_ref(),
200            index.into_iden(),
201            IndexHintType::Force,
202            scope
203        );
204
205        self
206    }
207
208    /// Ignore index hint on the last table in the from clause for MySQL
209    ///
210    /// Give the optimizer information about how to choose indexes during query processing.
211    /// See [MySQL reference manual for Index Hints](https://dev.mysql.com/doc/refman/8.0/en/index-hints.html)
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// use sea_query::{extension::mysql::*, tests_cfg::*, *};
217    ///
218    /// let query = Query::select()
219    ///     .from(Char::Table)
220    ///     .ignore_index(IndexName::new("IDX_123456"), IndexHintScope::All)
221    ///     .column(Char::SizeW)
222    ///     .to_owned();
223    ///
224    /// assert_eq!(
225    ///     query.to_string(MysqlQueryBuilder),
226    ///     r#"SELECT `size_w` FROM `character` IGNORE INDEX (`IDX_123456`)"#
227    /// )
228    /// ```
229    fn ignore_index<I>(&mut self, index: I, scope: IndexHintScope) -> &mut Self
230    where
231        I: IntoIden,
232    {
233        let table_ref = self.from.last().expect(PANIC_MSG);
234
235        get_or_insert_index!(
236            self,
237            table_ref,
238            index.into_iden(),
239            IndexHintType::Ignore,
240            scope
241        );
242
243        self
244    }
245
246    /// IGNORE INDEX hint on the specified in the from clause for MySQL
247    ///
248    /// # Examples
249    ///
250    /// ```
251    /// use sea_query::{extension::mysql::*, tests_cfg::*, *};
252    ///
253    /// let query = Query::select()
254    ///     .from(Font::Table)
255    ///     .from(Char::Table)
256    ///     .ignore_index_on(Char::Table, IndexName::new("IDX_123456"), IndexHintScope::All)
257    ///     .ignore_index_on(Font::Table, IndexName::new("IDX_654321"), IndexHintScope::All)
258    ///     .column(Char::SizeW)
259    ///     .to_owned();
260    ///
261    /// assert_eq!(
262    ///     query.to_string(MysqlQueryBuilder),
263    ///     r#"SELECT `size_w` FROM `font` IGNORE INDEX (`IDX_654321`), `character` IGNORE INDEX (`IDX_123456`)"#
264    /// );
265    /// ```
266    fn ignore_index_on<T, I>(&mut self, table: T, index: I, scope: IndexHintScope) -> &mut Self
267    where
268        T: IntoTableRef,
269        I: IntoIden,
270    {
271        get_or_insert_index!(
272            self,
273            table.into_table_ref(),
274            index.into_iden(),
275            IndexHintType::Ignore,
276            scope
277        );
278
279        self
280    }
281}