1 /*
2    Copyright (c) 2014, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software Foundation,
22    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
23 
24 // First include (the generated) my_config.h, to get correct platform defines.
25 #include "my_config.h"
26 #include "handler.h"                            // handlerton
27 #include "m_ctype.h"                            // my_charset_utf8_general_ci
28 #include "my_dbug.h"
29 #include "opt_costconstants.h"
30 #include "sql_plugin.h"                         // plugin_ref
31 #include "table.h"                              // TABLE
32 
33 /**
34   The default value for storage device type. If device type information
35   is added to the data dictionary or the storage engines start to
36   provide this information, this default can be replaced.
37 */
38 const unsigned int DEFAULT_STORAGE_CLASS= 0;
39 
40 /*
41   Values for cost constants defined as static const variables in the
42   Server_cost_constants class.
43   These are the default cost constant values that will be use if
44   the server administrator has not added new values in the server_cost
45   table.
46 */
47 
48 // Default cost for evaluation of the query condition for a row.
49 const double Server_cost_constants::ROW_EVALUATE_COST= 0.2;
50 
51 // Default cost for comparing row ids.
52 const double Server_cost_constants::KEY_COMPARE_COST= 0.1;
53 
54 /*
55   Creating a Memory temporary table is by benchmark found to be as
56   costly as writing 10 rows into the table.
57 */
58 const double Server_cost_constants::MEMORY_TEMPTABLE_CREATE_COST= 2.0;
59 
60 /*
61   Writing a row to or reading a row from a Memory temporary table is
62   equivalent to evaluating a row in the join engine.
63 */
64 const double Server_cost_constants::MEMORY_TEMPTABLE_ROW_COST= 0.2;
65 
66 /*
67   Creating a MyISAM table is 20 times slower than creating a Memory table.
68 */
69 const double Server_cost_constants::DISK_TEMPTABLE_CREATE_COST= 40.0;
70 
71 /*
72   Generating MyISAM rows sequentially is 2 times slower than
73   generating Memory rows, when number of rows is greater than
74   1000. However, we do not have benchmarks for very large tables, so
75   setting this factor conservatively to be 5 times slower (ie the cost
76   is 1.0).
77 */
78 const double Server_cost_constants::DISK_TEMPTABLE_ROW_COST= 1.0;
79 
80 
set(const LEX_CSTRING & name,double value)81 cost_constant_error Server_cost_constants::set(const LEX_CSTRING &name,
82                                                double value)
83 {
84   assert(name.str != NULL);
85   assert(name.length > 0);
86 
87   if (name.str == NULL || name.length == 0)
88     return UNKNOWN_COST_NAME;                   /* purecov: inspected */
89 
90   /*
91     The cost constant value must be a positive and non-zero.
92   */
93   if (value <= 0.0)
94     return INVALID_COST_VALUE;
95 
96   // ROW_EVALUATE_COST
97   if (my_strcasecmp(&my_charset_utf8_general_ci,
98                     "ROW_EVALUATE_COST", name.str) == 0)
99   {
100     m_row_evaluate_cost= value;
101     return COST_CONSTANT_OK;
102   }
103   // KEY_COMPARE_COST
104   if (my_strcasecmp(&my_charset_utf8_general_ci,
105                     "KEY_COMPARE_COST", name.str) == 0)
106   {
107     m_key_compare_cost= value;
108     return COST_CONSTANT_OK;
109   }
110   // MEMORY_TEMPTABLE_CREATE_COST
111   if (my_strcasecmp(&my_charset_utf8_general_ci,
112                     "MEMORY_TEMPTABLE_CREATE_COST", name.str) == 0)
113   {
114     m_memory_temptable_create_cost= value;
115     return COST_CONSTANT_OK;
116   }
117   // MEMORY_TEMPTABLE_ROW_COST
118   if (my_strcasecmp(&my_charset_utf8_general_ci,
119                     "MEMORY_TEMPTABLE_ROW_COST", name.str) == 0)
120   {
121     m_memory_temptable_row_cost= value;
122     return COST_CONSTANT_OK;
123   }
124   // DISK_TEMPTABLE_CREATE_COST
125   if (my_strcasecmp(&my_charset_utf8_general_ci,
126                     "DISK_TEMPTABLE_CREATE_COST", name.str) == 0)
127   {
128     m_disk_temptable_create_cost= value;
129     return COST_CONSTANT_OK;
130   }
131   // DISK_TEMPTABLE_ROW_COST
132   if (my_strcasecmp(&my_charset_utf8_general_ci,
133                     "DISK_TEMPTABLE_ROW_COST", name.str) == 0)
134   {
135     m_disk_temptable_row_cost= value;
136     return COST_CONSTANT_OK;
137   }
138 
139   return UNKNOWN_COST_NAME;                     // Cost constant does not exist
140 }
141 
142 
143 /*
144   Values for cost constants defined as static const variables in the
145   SE_cost_constants class.
146   These are the default cost constant values that will be used if
147   the server administrator has not added new values in the engine_cost
148   table.
149 */
150 
151 // The cost of reading a block from a main memory buffer pool
152 const double SE_cost_constants::MEMORY_BLOCK_READ_COST= 1.0;
153 
154 // The cost of reading a block from an IO device (disk)
155 const double SE_cost_constants::IO_BLOCK_READ_COST= 1.0;
156 
157 
set(const LEX_CSTRING & name,const double value,bool default_value)158 cost_constant_error SE_cost_constants::set(const LEX_CSTRING &name,
159                                            const double value,
160                                            bool default_value)
161 {
162   assert(name.str != NULL);
163   assert(name.length > 0);
164 
165   if (name.str == NULL || name.length == 0)
166     return UNKNOWN_COST_NAME;                   /* purecov: inspected */
167 
168   /*
169     The cost constant value must be a positive and non-zero number.
170   */
171   if (value <= 0.0)
172     return INVALID_COST_VALUE;
173 
174   // MEMORY_BLOCK_READ_COST
175   if (my_strcasecmp(&my_charset_utf8_general_ci, "MEMORY_BLOCK_READ_COST",
176                     name.str) == 0)
177   {
178     update_cost_value(&m_memory_block_read_cost,
179                       &m_memory_block_read_cost_default, value, default_value);
180     return COST_CONSTANT_OK;
181   }
182   // IO_BLOCK_READ_COST
183   if (my_strcasecmp(&my_charset_utf8_general_ci, "IO_BLOCK_READ_COST",
184                     name.str) == 0)
185   {
186     update_cost_value(&m_io_block_read_cost, &m_io_block_read_cost_default,
187                       value, default_value);
188     return COST_CONSTANT_OK;
189   }
190 
191   return UNKNOWN_COST_NAME;                     // Cost constant does not exist
192 }
193 
194 
update(const LEX_CSTRING & name,const double value)195 cost_constant_error SE_cost_constants::update(const LEX_CSTRING &name,
196                                               const double value)
197 {
198   return set(name, value, false);
199 }
200 
201 
update_default(const LEX_CSTRING & name,const double value)202 cost_constant_error SE_cost_constants::update_default(const LEX_CSTRING &name,
203                                        const double value)
204 {
205   return set(name, value, true);
206 }
207 
208 
update_cost_value(double * cost_constant,bool * cost_constant_is_default,double new_value,bool new_value_is_default)209 void SE_cost_constants::update_cost_value(double *cost_constant,
210                                           bool *cost_constant_is_default,
211                                           double new_value,
212                                           bool new_value_is_default)
213 {
214   // If this is not a new default value then do the update unconditionally
215   if (!new_value_is_default)
216   {
217     *cost_constant= new_value;
218     *cost_constant_is_default= false;           // No longer default value
219   }
220   else
221   {
222     /*
223       This new value is a default value. Only update the cost constant if it
224       currently has the default value.
225     */
226     if (*cost_constant_is_default)
227       *cost_constant= new_value;
228   }
229 }
230 
231 
Cost_model_se_info()232 Cost_model_se_info::Cost_model_se_info()
233 {
234   for (uint i= 0; i < MAX_STORAGE_CLASSES; ++i)
235     m_se_cost_constants[i]= NULL;
236 }
237 
238 
~Cost_model_se_info()239 Cost_model_se_info::~Cost_model_se_info()
240 {
241   for (uint i= 0; i < MAX_STORAGE_CLASSES; ++i)
242   {
243     delete m_se_cost_constants[i];
244     m_se_cost_constants[i]= NULL;
245   }
246 }
247 
248 
Cost_model_constants()249 Cost_model_constants::Cost_model_constants()
250   : m_ref_counter(0)
251 {
252   /**
253     Create default cost constants for each storage engine.
254   */
255   for (uint engine= 0; engine < MAX_HA; ++engine)
256   {
257     const handlerton *ht= NULL;
258 
259     // Check if the storage engine has been installed
260     if (hton2plugin[engine])
261     {
262       // Find the handlerton for the storage engine
263       ht= static_cast<handlerton*>(hton2plugin[engine]->data);
264     }
265 
266     for (uint storage= 0; storage < MAX_STORAGE_CLASSES; ++storage)
267     {
268       SE_cost_constants *se_cost= NULL;
269 
270       /*
271         If the storage engine has provided a function for creating
272         storage engine specific cost constants, then ask the
273         storage engine to create the cost constants.
274       */
275       if (ht && ht->get_cost_constants)
276         se_cost= ht->get_cost_constants(storage); /* purecov: tested */
277 
278       /*
279         If the storage engine did not provide cost constants, then the
280         default cost constants will be used.
281       */
282       if (se_cost == NULL)
283         se_cost= new SE_cost_constants();
284 
285       m_engines[engine].set_cost_constants(se_cost, storage);
286     }
287   }
288 }
289 
290 
~Cost_model_constants()291 Cost_model_constants::~Cost_model_constants()
292 {
293   assert(m_ref_counter == 0);
294 }
295 
296 
297 const SE_cost_constants
get_se_cost_constants(const TABLE * table) const298 *Cost_model_constants::get_se_cost_constants(const TABLE *table) const
299 {
300   assert(table->file != NULL);
301   assert(table->file->ht != NULL);
302 
303   const SE_cost_constants *se_cc=
304     m_engines[table->file->ht->slot].get_cost_constants(DEFAULT_STORAGE_CLASS);
305   assert(se_cc != NULL);
306 
307   return se_cc;
308 }
309 
310 
311 cost_constant_error
update_server_cost_constant(const LEX_CSTRING & name,double value)312 Cost_model_constants::update_server_cost_constant(const LEX_CSTRING &name,
313                                                   double value)
314 {
315   return m_server_constants.set(name, value);
316 }
317 
318 
319 cost_constant_error
update_engine_cost_constant(THD * thd,const LEX_CSTRING & se_name,uint storage_category,const LEX_CSTRING & name,double value)320 Cost_model_constants::update_engine_cost_constant(THD *thd,
321                                                   const LEX_CSTRING &se_name,
322                                                   uint storage_category,
323                                                   const LEX_CSTRING &name,
324                                                   double value)
325 {
326   cost_constant_error retval= COST_CONSTANT_OK;
327 
328   // Validate the storage category.
329   if (storage_category >= MAX_STORAGE_CLASSES)
330     return INVALID_DEVICE_TYPE;
331 
332   // Check if this is a default value
333   if (my_strcasecmp(&my_charset_utf8_general_ci, "default", se_name.str) == 0)
334   {
335     retval= update_engine_default_cost(name, storage_category, value);
336   }
337   else
338   {
339     // Look up the handler's slot id by using the storage engine name
340     const uint ht_slot_id= find_handler_slot_from_name(thd, se_name);
341     if (ht_slot_id == HA_SLOT_UNDEF)
342       return UNKNOWN_ENGINE_NAME;
343 
344     SE_cost_constants *se_cc=
345       m_engines[ht_slot_id].get_cost_constants(storage_category);
346     assert(se_cc != NULL);
347 
348     retval= se_cc->update(name, value);
349   }
350 
351   return retval;
352 }
353 
354 
find_handler_slot_from_name(THD * thd,const LEX_CSTRING & name) const355 uint Cost_model_constants::find_handler_slot_from_name(THD *thd,
356                                                        const LEX_CSTRING &name)
357   const
358 {
359   // Convert storage engine name from const to non-const lex string
360   const LEX_STRING name_non_const=
361     { const_cast<char*>(name.str), name.length };
362 
363   // Look up the storage engine
364   const plugin_ref plugin= ha_resolve_by_name(thd, &name_non_const, false);
365   if (!plugin)
366     return HA_SLOT_UNDEF;
367 
368   // Find the handlerton for this storage engine
369   handlerton *ht= plugin_data<handlerton*>(plugin);
370   assert(ht != NULL);
371   if (!ht)
372   {
373     assert(false);                         /* purecov: inspected */
374     return HA_SLOT_UNDEF;
375   }
376 
377   return ht->slot;
378 }
379 
380 
381 cost_constant_error
update_engine_default_cost(const LEX_CSTRING & name,uint storage_category,double value)382 Cost_model_constants::update_engine_default_cost(const LEX_CSTRING &name,
383                                                  uint storage_category,
384                                                  double value)
385 {
386   assert(storage_category < MAX_STORAGE_CLASSES);
387 
388   /*
389     Return value: if at least one of the storage engines recognizes the
390     cost constants name, then success is returned.
391   */
392   cost_constant_error retval= UNKNOWN_COST_NAME;
393 
394   /*
395     Update all constants for engines that have their own cost constants
396   */
397   for (size_t i= 0; i < MAX_HA; ++i)
398   {
399     SE_cost_constants *se_cc= m_engines[i].get_cost_constants(storage_category);
400     if (se_cc)
401     {
402       const cost_constant_error err= se_cc->update_default(name, value);
403       if (err != UNKNOWN_COST_NAME)
404         retval= err;
405     }
406   }
407 
408   return retval;
409 }
410