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