1 /* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "parse_tree_hints.h"
24 #include "sql_class.h"
25 #include "mysqld.h" // table_alias_charset
26 #include "sql_lex.h"
27
28
29 /**
30 Information about hints. Sould be
31 synchronized with opt_hints_enum enum.
32
33 Note: Hint name depends on hint state. 'NO_' prefix is added
34 if appropriate hint state bit(see Opt_hints_map::hints) is not
35 set. Depending on 'switch_state_arg' argument in 'parse tree
36 object' constructors(see parse_tree_hints.[h,cc]) implementor
37 can control wishful form of the hint name.
38 */
39
40 struct st_opt_hint_info opt_hint_info[]=
41 {
42 {"BKA", true, true},
43 {"BNL", true, true},
44 {"ICP", true, true},
45 {"MRR", true, true},
46 {"NO_RANGE_OPTIMIZATION", true, true},
47 {"MAX_EXECUTION_TIME", false, false},
48 {"QB_NAME", false, false},
49 {"SEMIJOIN", false, false},
50 {"SUBQUERY", false, false},
51 {0, 0, 0}
52 };
53
54
55 /**
56 Prefix for system generated query block name.
57 Used in information warning in EXPLAIN oputput.
58 */
59
60 const LEX_CSTRING sys_qb_prefix= {"select#", 7};
61
62
63 /*
64 Compare LEX_CSTRING objects.
65
66 @param s Pointer to LEX_CSTRING
67 @param t Pointer to LEX_CSTRING
68 @param cs Pointer to character set
69
70 @return 0 if strings are equal
71 1 if s is greater
72 -1 if t is greater
73 */
74
cmp_lex_string(const LEX_CSTRING * s,const LEX_CSTRING * t,const CHARSET_INFO * cs)75 static int cmp_lex_string(const LEX_CSTRING *s,
76 const LEX_CSTRING *t,
77 const CHARSET_INFO *cs)
78 {
79 return cs->coll->strnncollsp(cs,
80 (uchar *) s->str, s->length,
81 (uchar *) t->str, t->length, 0);
82 }
83
84
get_switch(opt_hints_enum type_arg) const85 bool Opt_hints::get_switch(opt_hints_enum type_arg) const
86 {
87 if (is_specified(type_arg))
88 return hints_map.switch_on(type_arg);
89
90 if (opt_hint_info[type_arg].check_upper_lvl)
91 return parent->get_switch(type_arg);
92
93 return false;
94 }
95
96
find_by_name(const LEX_CSTRING * name_arg,const CHARSET_INFO * cs) const97 Opt_hints* Opt_hints::find_by_name(const LEX_CSTRING *name_arg,
98 const CHARSET_INFO *cs) const
99 {
100 for (uint i= 0; i < child_array.size(); i++)
101 {
102 const LEX_CSTRING *name= child_array[i]->get_name();
103 if (name && !cmp_lex_string(name, name_arg, cs))
104 return child_array[i];
105 }
106 return NULL;
107 }
108
109
print(THD * thd,String * str,enum_query_type query_type)110 void Opt_hints::print(THD *thd, String *str, enum_query_type query_type)
111 {
112 for (uint i= 0; i < MAX_HINT_ENUM; i++)
113 {
114 opt_hints_enum hint= static_cast<opt_hints_enum>(i);
115 /*
116 If printing a normalized query, also unresolved hints will be printed.
117 (This is needed by query rewrite plugins which request
118 normalized form before resolving has been performed.)
119 */
120 if (is_specified(hint) &&
121 (is_resolved() || query_type == QT_NORMALIZED_FORMAT))
122 {
123 append_hint_type(str, hint);
124 str->append(STRING_WITH_LEN("("));
125 append_name(thd, str);
126 if (!opt_hint_info[i].switch_hint)
127 get_complex_hints(hint)->append_args(thd, str);
128 str->append(STRING_WITH_LEN(") "));
129 }
130 }
131
132 for (uint i= 0; i < child_array.size(); i++)
133 child_array[i]->print(thd, str, query_type);
134 }
135
136
append_hint_type(String * str,opt_hints_enum type)137 void Opt_hints::append_hint_type(String *str, opt_hints_enum type)
138 {
139 const char* hint_name= opt_hint_info[type].hint_name;
140 if(!hints_map.switch_on(type))
141 str->append(STRING_WITH_LEN("NO_"));
142 str->append(hint_name);
143 }
144
145
print_warn_unresolved(THD * thd)146 void Opt_hints::print_warn_unresolved(THD *thd)
147 {
148 String hint_name_str, hint_type_str;
149 append_name(thd, &hint_name_str);
150
151 for (uint i= 0; i < MAX_HINT_ENUM; i++)
152 {
153 if (is_specified(static_cast<opt_hints_enum>(i)))
154 {
155 hint_type_str.length(0);
156 append_hint_type(&hint_type_str, static_cast<opt_hints_enum>(i));
157 push_warning_printf(thd, Sql_condition::SL_WARNING,
158 ER_UNRESOLVED_HINT_NAME,
159 ER_THD(thd, ER_UNRESOLVED_HINT_NAME),
160 hint_name_str.c_ptr_safe(),
161 hint_type_str.c_ptr_safe());
162 }
163 }
164 }
165
166
check_unresolved(THD * thd)167 void Opt_hints::check_unresolved(THD *thd)
168 {
169 if (!is_resolved())
170 print_warn_unresolved(thd);
171
172 if (!is_all_resolved())
173 {
174 for (uint i= 0; i < child_array.size(); i++)
175 child_array[i]->check_unresolved(thd);
176 }
177 }
178
179
get_complex_hints(opt_hints_enum type)180 PT_hint *Opt_hints_global::get_complex_hints(opt_hints_enum type)
181 {
182 if (type == MAX_EXEC_TIME_HINT_ENUM)
183 return max_exec_time;
184
185 assert(0);
186 return NULL;
187 }
188
189
Opt_hints_qb(Opt_hints * opt_hints_arg,MEM_ROOT * mem_root_arg,uint select_number_arg)190 Opt_hints_qb::Opt_hints_qb(Opt_hints *opt_hints_arg,
191 MEM_ROOT *mem_root_arg,
192 uint select_number_arg)
193 : Opt_hints(NULL, opt_hints_arg, mem_root_arg),
194 select_number(select_number_arg), subquery_hint(NULL), semijoin_hint(NULL)
195 {
196 sys_name.str= buff;
197 sys_name.length= my_snprintf(buff, sizeof(buff), "%s%lx",
198 sys_qb_prefix.str, select_number);
199 }
200
201
get_complex_hints(opt_hints_enum type)202 PT_hint *Opt_hints_qb::get_complex_hints(opt_hints_enum type)
203 {
204 if (type == SEMIJOIN_HINT_ENUM)
205 return semijoin_hint;
206
207 if (type == SUBQUERY_HINT_ENUM)
208 return subquery_hint;
209
210 assert(0);
211 return NULL;
212 }
213
214
adjust_table_hints(TABLE * table,const char * alias)215 Opt_hints_table *Opt_hints_qb::adjust_table_hints(TABLE *table,
216 const char *alias)
217 {
218 const LEX_CSTRING str= { alias, strlen(alias) };
219 Opt_hints_table *tab=
220 static_cast<Opt_hints_table *>(find_by_name(&str, table_alias_charset));
221
222 table->pos_in_table_list->opt_hints_qb= this;
223
224 if (!tab) // Tables not found
225 return NULL;
226
227 tab->adjust_key_hints(table);
228 return tab;
229 }
230
231
semijoin_enabled(THD * thd) const232 bool Opt_hints_qb::semijoin_enabled(THD *thd) const
233 {
234 if (subquery_hint) // SUBQUERY hint disables semi-join
235 return false;
236
237 if (semijoin_hint)
238 {
239 // SEMIJOIN hint will always force semijoin regardless of optimizer_switch
240 if (semijoin_hint->switch_on())
241 return true;
242
243 // NO_SEMIJOIN hint. If strategy list is empty, do not use SEMIJOIN
244 if (semijoin_hint->get_args() == 0)
245 return false;
246
247 // Fall through: NO_SEMIJOIN w/ strategies neither turns SEMIJOIN off nor on
248 }
249
250 return thd->optimizer_switch_flag(OPTIMIZER_SWITCH_SEMIJOIN);
251 }
252
253
sj_enabled_strategies(uint opt_switches) const254 uint Opt_hints_qb::sj_enabled_strategies(uint opt_switches) const
255 {
256 // Hints override switches
257 if (semijoin_hint)
258 {
259 const uint strategies= semijoin_hint->get_args();
260 if (semijoin_hint->switch_on()) // SEMIJOIN hint
261 return (strategies == 0) ? opt_switches : strategies;
262
263 // NO_SEMIJOIN hint. Hints and optimizer_switch both affect strategies
264 return ~strategies & opt_switches;
265 }
266
267 return opt_switches;
268 }
269
270
271 Item_exists_subselect::enum_exec_method
subquery_strategy() const272 Opt_hints_qb::subquery_strategy() const
273 {
274 if (subquery_hint)
275 return static_cast<Item_exists_subselect::enum_exec_method>
276 (subquery_hint->get_args());
277
278 return Item_exists_subselect::EXEC_UNSPECIFIED;
279 }
280
281
adjust_key_hints(TABLE * table)282 void Opt_hints_table::adjust_key_hints(TABLE *table)
283 {
284 set_resolved();
285 if (child_array_ptr()->size() == 0) // No key level hints
286 {
287 get_parent()->incr_resolved_children();
288 return;
289 }
290
291 /*
292 Make sure that adjustement is done only once.
293 Table has already been processed if keyinfo_array is not empty.
294 */
295 if (keyinfo_array.size())
296 return;
297
298 keyinfo_array.resize(table->s->keys, NULL);
299
300 for (Opt_hints** hint= child_array_ptr()->begin();
301 hint < child_array_ptr()->end(); ++hint)
302 {
303 KEY *key_info= table->key_info;
304 for (uint j= 0 ; j < table->s->keys ; j++, key_info++)
305 {
306 const LEX_CSTRING key_name= { key_info->name, strlen(key_info->name) };
307 if (!cmp_lex_string((*hint)->get_name(), &key_name, system_charset_info))
308 {
309 (*hint)->set_resolved();
310 keyinfo_array[j]= static_cast<Opt_hints_key *>(*hint);
311 incr_resolved_children();
312 }
313 }
314 }
315
316 /*
317 Do not increase number of resolved tables
318 if there are unresolved key objects. It's
319 important for check_unresolved() function.
320 */
321 if (is_all_resolved())
322 get_parent()->incr_resolved_children();
323 }
324
325
326 /**
327 Function returns hint value depending on
328 the specfied hint level. If hint is specified
329 on current level, current level hint value is
330 returned, otherwise parent level hint is checked.
331
332 @param hint Pointer to the hint object
333 @param parent_hint Pointer to the parent hint object,
334 should never be NULL
335 @param type_arg hint type
336 @param OUT ret_val hint value depending on
337 what hint level is used
338
339 @return true if hint is specified, false otherwise
340 */
341
get_hint_state(Opt_hints * hint,Opt_hints * parent_hint,opt_hints_enum type_arg,bool * ret_val)342 static bool get_hint_state(Opt_hints *hint,
343 Opt_hints *parent_hint,
344 opt_hints_enum type_arg,
345 bool *ret_val)
346 {
347 assert(parent_hint);
348
349 if (opt_hint_info[type_arg].switch_hint)
350 {
351 if (hint && hint->is_specified(type_arg))
352 {
353 *ret_val= hint->get_switch(type_arg);
354 return true;
355 }
356 else if (opt_hint_info[type_arg].check_upper_lvl &&
357 parent_hint->is_specified(type_arg))
358 {
359 *ret_val= parent_hint->get_switch(type_arg);
360 return true;
361 }
362 }
363 else
364 {
365 /* Complex hint, not implemented atm */
366 assert(0);
367 }
368 return false;
369 }
370
371
hint_key_state(const THD * thd,const TABLE * table,uint keyno,opt_hints_enum type_arg,uint optimizer_switch)372 bool hint_key_state(const THD *thd, const TABLE *table,
373 uint keyno, opt_hints_enum type_arg,
374 uint optimizer_switch)
375 {
376 Opt_hints_table *table_hints= table->pos_in_table_list->opt_hints_table;
377
378 /* Parent should always be initialized */
379 if (table_hints && keyno != MAX_KEY)
380 {
381 Opt_hints_key *key_hints= table_hints->keyinfo_array.size() > 0 ?
382 table_hints->keyinfo_array[keyno] : NULL;
383 bool ret_val= false;
384 if (get_hint_state(key_hints, table_hints, type_arg, &ret_val))
385 return ret_val;
386 }
387
388 return thd->optimizer_switch_flag(optimizer_switch);
389 }
390
391
hint_table_state(const THD * thd,const TABLE * table,opt_hints_enum type_arg,uint optimizer_switch)392 bool hint_table_state(const THD *thd, const TABLE *table,
393 opt_hints_enum type_arg,
394 uint optimizer_switch)
395 {
396 TABLE_LIST *table_list= table->pos_in_table_list;
397 if (table_list->opt_hints_qb)
398 {
399 bool ret_val= false;
400 if (get_hint_state(table_list->opt_hints_table,
401 table_list->opt_hints_qb,
402 type_arg, &ret_val))
403 return ret_val;
404 }
405
406 return thd->optimizer_switch_flag(optimizer_switch);
407 }
408