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 <stddef.h>
27 #include "sql_const.h" // MAX_FIELD_WIDTH
28 #include "field.h" // Field
29 #include "log.h" // sql_print_warning
30 #include "m_string.h" // LEX_CSTRING
31 #include "my_dbug.h" // assert
32 #include "opt_costconstants.h"
33 #include "opt_costconstantcache.h"
34 #include "template_utils.h" // pointer_cast
35 #include "records.h" // READ_RECORD
36 #include "sql_base.h" // open_and_lock_tables
37 #include "sql_class.h" // THD
38 #include "sql_lex.h" // lex_start/lex_end
39 #include "sql_string.h" // String
40 #include "table.h" // TABLE
41 #include "thr_lock.h" // TL_READ
42 #include "transaction.h"
43 #include "sql_tmp_table.h" // init_cache_tmp_engine_properties
44
45 Cost_constant_cache *cost_constant_cache= NULL;
46
47
48 static void read_cost_constants(Cost_model_constants* cost_constants);
49
50
51 /**
52 Minimal initialization of the object. The main initialization is done
53 by calling init().
54 */
55
Cost_constant_cache()56 Cost_constant_cache::Cost_constant_cache()
57 : current_cost_constants(NULL), m_inited(false)
58 {
59 }
60
61
~Cost_constant_cache()62 Cost_constant_cache::~Cost_constant_cache()
63 {
64 // Verify that close has been called
65 assert(current_cost_constants == NULL);
66 assert(m_inited == false);
67 }
68
69
init()70 void Cost_constant_cache::init()
71 {
72 DBUG_ENTER("Cost_constant_cache::init");
73
74 assert(m_inited == false);
75
76 // Initialize the mutex that is used for protecting the cost constants
77 mysql_mutex_init(key_LOCK_cost_const, &LOCK_cost_const,
78 MY_MUTEX_INIT_FAST);
79
80 // Create cost constants from constants found in the source code
81 Cost_model_constants *cost_constants= create_defaults();
82
83 // Set this to be the current set of cost constants
84 update_current_cost_constants(cost_constants);
85
86 m_inited= true;
87
88 DBUG_VOID_RETURN;
89 }
90
91
close()92 void Cost_constant_cache::close()
93 {
94 DBUG_ENTER("Cost_constant_cache::close");
95
96 assert(m_inited);
97
98 if (m_inited == false)
99 DBUG_VOID_RETURN; /* purecov: inspected */
100
101 // Release the current cost constant set
102 if (current_cost_constants)
103 {
104 release_cost_constants(current_cost_constants);
105 current_cost_constants= NULL;
106 }
107
108 // To ensure none is holding the mutex when deleting it, lock/unlock it.
109 mysql_mutex_lock(&LOCK_cost_const);
110 mysql_mutex_unlock(&LOCK_cost_const);
111
112 mysql_mutex_destroy(&LOCK_cost_const);
113
114 m_inited= false;
115
116 DBUG_VOID_RETURN;
117 }
118
119
reload()120 void Cost_constant_cache::reload()
121 {
122 DBUG_ENTER("Cost_constant_cache::reload");
123 assert(m_inited= true);
124
125 // Create cost constants from the constants defined in the source code
126 Cost_model_constants *cost_constants= create_defaults();
127
128 // Update the cost constants from the database tables
129 read_cost_constants(cost_constants);
130
131 // Set this to be the current set of cost constants
132 update_current_cost_constants(cost_constants);
133
134 DBUG_VOID_RETURN;
135 }
136
137
138
create_defaults() const139 Cost_model_constants *Cost_constant_cache::create_defaults() const
140 {
141 // Create default cost constants
142 Cost_model_constants *cost_constants= new Cost_model_constants();
143
144 return cost_constants;
145 }
146
147
148 void
update_current_cost_constants(Cost_model_constants * new_cost_constants)149 Cost_constant_cache::update_current_cost_constants(Cost_model_constants *new_cost_constants)
150 {
151 /*
152 Increase the ref counter to ensure that the new cost constants
153 are not deleted until next time we have a new set of cost constants.
154 */
155 new_cost_constants->inc_ref_count();
156
157 /*
158 The mutex needs to be held for the entire period for removing the
159 current cost constants and adding the new cost constants to ensure
160 that no user of this class can access the object when there is no
161 current cost constants.
162 */
163 mysql_mutex_lock(&LOCK_cost_const);
164
165 // Release the current cost constants by decrementing the ref counter
166 if (current_cost_constants)
167 {
168 const unsigned int ref_count= current_cost_constants->dec_ref_count();
169
170 // If there is none using the current cost constants then delete them
171 if (ref_count == 0)
172 delete current_cost_constants;
173 }
174
175 // Start to use the new cost constants
176 current_cost_constants= new_cost_constants;
177
178 mysql_mutex_unlock(&LOCK_cost_const);
179 }
180
181
182 /**
183 Write warnings about illegal entries in the server_cost table
184
185 The warnings are written to the MySQL error log.
186
187 @param cost_name name of the cost constant
188 @param value value it was attempted set to
189 @param err error status
190 */
191
report_server_cost_warnings(const LEX_CSTRING & cost_name,double value,cost_constant_error error)192 static void report_server_cost_warnings(const LEX_CSTRING &cost_name,
193 double value,
194 cost_constant_error error)
195 {
196 switch(error)
197 {
198 case UNKNOWN_COST_NAME:
199 sql_print_warning("Unknown cost constant \"%s\" in mysql.server_cost table\n",
200 cost_name.str);
201 break;
202 case INVALID_COST_VALUE:
203 sql_print_warning("Invalid value for cost constant \"%s\" in mysql.server_cost table: %.1f\n",
204 cost_name.str, value);
205 break;
206 default:
207 assert(false); /* purecov: inspected */
208 }
209 }
210
211
212 /**
213 Write warnings about illegal entries in the engine_cost table
214
215 The warnings are written to the MySQL error log.
216
217 @param se_name name of storage engine
218 @param storage_category device type
219 @param cost_name name of the cost constant
220 @param value value it was attempted set to
221 @param err error status
222 */
223
report_engine_cost_warnings(const LEX_CSTRING & se_name,int storage_category,const LEX_CSTRING & cost_name,double value,cost_constant_error error)224 static void report_engine_cost_warnings(const LEX_CSTRING &se_name,
225 int storage_category,
226 const LEX_CSTRING &cost_name,
227 double value,
228 cost_constant_error error)
229 {
230 switch(error)
231 {
232 case UNKNOWN_COST_NAME:
233 sql_print_warning("Unknown cost constant \"%s\" in mysql.engine_cost table\n",
234 cost_name.str);
235 break;
236 case UNKNOWN_ENGINE_NAME:
237 sql_print_warning("Unknown storage engine \"%s\" in mysql.engine_cost table\n",
238 se_name.str);
239 break;
240 case INVALID_DEVICE_TYPE:
241 sql_print_warning("Invalid device type %d for \"%s\" storage engine for cost constant \"%s\" in mysql.engine_cost table\n",
242 storage_category, se_name.str, cost_name.str);
243 break;
244 case INVALID_COST_VALUE:
245 sql_print_warning("Invalid value for cost constant \"%s\" for \"%s\" storage engine and device type %d in mysql.engine_cost table: %.1f\n",
246 cost_name.str, se_name.str, storage_category, value);
247 break;
248 default:
249 assert(false); /* purecov: inspected */
250 }
251 }
252
253
254 /**
255 Read the table that contains the cost constants for the server.
256
257 The table must already be opened. The cost constant object is updated
258 with cost constants found in the configuration table.
259
260 @param thd the THD
261 @param table the table to read from
262 @param cost_constants[in,out] cost constant object
263 */
264
read_server_cost_constants(THD * thd,TABLE * table,Cost_model_constants * cost_constants)265 static void read_server_cost_constants(THD *thd, TABLE *table,
266 Cost_model_constants* cost_constants)
267 {
268 DBUG_ENTER("read_server_cost_constants");
269
270 /*
271 The server constant table has the following columns:
272
273 cost_name VARCHAR(64) NOT NULL COLLATE utf8_general_ci
274 cost_value FLOAT DEFAULT NULL
275 last_update TIMESTAMP
276 comment VARCHAR(1024) DEFAULT NULL
277 */
278
279 READ_RECORD read_record_info;
280
281 // Prepare to read from the table
282 const bool ret= init_read_record(&read_record_info, thd, table, NULL, true,
283 true, false);
284 if (!ret)
285 {
286 table->use_all_columns();
287
288 // Read one record
289 while (!read_record_info.read_record(&read_record_info))
290 {
291 /*
292 Check if a non-default value has been configured for this cost
293 constant.
294 */
295 if (!table->field[1]->is_null())
296 {
297 char cost_name_buf[MAX_FIELD_WIDTH];
298 String cost_name(cost_name_buf, sizeof(cost_name_buf),
299 &my_charset_utf8_general_ci);
300
301 // Read the name of the cost constant
302 table->field[0]->val_str(&cost_name);
303 cost_name[cost_name.length()]= 0; // Null-terminate
304
305 // Read the value this cost constant should have
306 const float value= static_cast<float>(table->field[1]->val_real());
307
308 // Update the cost model with this cost constant
309 const LEX_CSTRING cost_constant= cost_name.lex_cstring();
310 const cost_constant_error err=
311 cost_constants->update_server_cost_constant(cost_constant, value);
312
313 if (err != COST_CONSTANT_OK)
314 report_server_cost_warnings(cost_constant, value, err);
315 }
316 }
317
318 end_read_record(&read_record_info);
319 }
320 else
321 {
322 sql_print_warning("init_read_record returned error when reading from mysql.server_cost table.\n");
323 }
324
325 DBUG_VOID_RETURN;
326 }
327
328
329 /**
330 Read the table that contains the cost constants for the storage engines.
331
332 The table must already be opened. The cost constant object is updated
333 with cost constants found in the configuration table.
334
335 @param thd the THD
336 @param table the table to read from
337 @param cost_constants[in,out] cost constant object
338 */
339
read_engine_cost_constants(THD * thd,TABLE * table,Cost_model_constants * cost_constants)340 static void read_engine_cost_constants(THD *thd, TABLE *table,
341 Cost_model_constants* cost_constants)
342 {
343 DBUG_ENTER("read_engine_cost_constants");
344
345 /*
346 The engine constant table has the following columns:
347
348 engine_name VARCHAR(64) NOT NULL COLLATE utf8_general_ci,
349 device_type INTEGER NOT NULL,
350 cost_name VARCHAR(64) NOT NULL COLLATE utf8_general_ci,
351 cost_value FLOAT DEFAULT NULL,
352 last_update TIMESTAMP
353 comment VARCHAR(1024) DEFAULT NULL,
354 */
355
356 READ_RECORD read_record_info;
357
358 // Prepare to read from the table
359 const bool ret= init_read_record(&read_record_info, thd, table, NULL, true,
360 true, false);
361 if (!ret)
362 {
363 table->use_all_columns();
364
365 // Read one record
366 while (!read_record_info.read_record(&read_record_info))
367 {
368 /*
369 Check if a non-default value has been configured for this cost
370 constant.
371 */
372 if (!table->field[3]->is_null())
373 {
374 char engine_name_buf[MAX_FIELD_WIDTH];
375 String engine_name(engine_name_buf, sizeof(engine_name_buf),
376 &my_charset_utf8_general_ci);
377 char cost_name_buf[MAX_FIELD_WIDTH];
378 String cost_name(cost_name_buf, sizeof(cost_name_buf),
379 &my_charset_utf8_general_ci);
380
381 // Read the name of the storage engine
382 table->field[0]->val_str(&engine_name);
383 engine_name[engine_name.length()]= 0; // Null-terminate
384
385 // Read the device type
386 const int device_type= static_cast<int>(table->field[1]->val_int());
387
388 // Read the name of the cost constant
389 table->field[2]->val_str(&cost_name);
390 cost_name[cost_name.length()]= 0; // Null-terminate
391
392 // Read the value this cost constant should have
393 const float value= static_cast<float>(table->field[3]->val_real());
394
395 // Update the cost model with this cost constant
396 const LEX_CSTRING engine= engine_name.lex_cstring();
397 const LEX_CSTRING cost_constant= cost_name.lex_cstring();
398 const cost_constant_error err=
399 cost_constants->update_engine_cost_constant(thd, engine,
400 device_type,
401 cost_constant, value);
402 if (err != COST_CONSTANT_OK)
403 report_engine_cost_warnings(engine, device_type, cost_constant,
404 value, err);
405 }
406 }
407
408 end_read_record(&read_record_info);
409 }
410 else
411 {
412 sql_print_warning("init_read_record returned error when reading from mysql.engine_cost table.\n");
413 }
414
415 DBUG_VOID_RETURN;
416 }
417
418
419 /**
420 Read the cost configuration tables and update the cost constant set.
421
422 The const constant set must be initialized with default values when
423 calling this function.
424
425 @param cost_constants set with cost constants
426 */
427
read_cost_constants(Cost_model_constants * cost_constants)428 static void read_cost_constants(Cost_model_constants* cost_constants)
429 {
430 DBUG_ENTER("read_cost_constants");
431
432 /*
433 This function creates its own THD. If there exists a current THD this needs
434 to be restored at the end of this function. The reason the current THD
435 can not be used is that this might already have opened and closed tables
436 and thus opening new tables will fail.
437 */
438 THD *orig_thd= current_thd;
439
440 // Create and initialize a new THD.
441 THD *thd= new THD;
442 assert(thd);
443 thd->thread_stack= pointer_cast<char*>(&thd);
444 thd->store_globals();
445 lex_start(thd);
446
447 TABLE_LIST tables[2];
448 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
449 C_STRING_WITH_LEN("server_cost"),
450 "server_cost", TL_READ);
451 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
452 C_STRING_WITH_LEN("engine_cost"),
453 "engine_cost", TL_READ);
454 tables[0].next_global= tables[0].next_local=
455 tables[0].next_name_resolution_table= &tables[1];
456
457 if (!open_and_lock_tables(thd, tables, MYSQL_LOCK_IGNORE_TIMEOUT))
458 {
459 assert(tables[0].table != NULL);
460 assert(tables[1].table != NULL);
461
462 // Read the server constants table
463 read_server_cost_constants(thd, tables[0].table, cost_constants);
464 // Read the storage engine table
465 read_engine_cost_constants(thd, tables[1].table, cost_constants);
466 }
467 else
468 {
469 sql_print_warning("Failed to open optimizer cost constant tables\n");
470 }
471
472 trans_commit_stmt(thd);
473 close_thread_tables(thd);
474 lex_end(thd->lex);
475
476 // Delete the locally created THD
477 delete thd;
478
479 // If the caller already had a THD, this must be restored
480 if(orig_thd)
481 orig_thd->store_globals();
482
483 DBUG_VOID_RETURN;
484 }
485
486
init_optimizer_cost_module(bool enable_plugins)487 void init_optimizer_cost_module(bool enable_plugins)
488 {
489 assert(cost_constant_cache == NULL);
490 cost_constant_cache= new Cost_constant_cache();
491 cost_constant_cache->init();
492 /*
493 Initialize max_key_length and max_key_part_length for internal temporary
494 table engines.
495 */
496 if (enable_plugins)
497 init_cache_tmp_engine_properties();
498 }
499
500
delete_optimizer_cost_module()501 void delete_optimizer_cost_module()
502 {
503 if (cost_constant_cache)
504 {
505 cost_constant_cache->close();
506 delete cost_constant_cache;
507 cost_constant_cache= NULL;
508 }
509 }
510
511
reload_optimizer_cost_constants()512 void reload_optimizer_cost_constants()
513 {
514 if (cost_constant_cache)
515 cost_constant_cache->reload();
516 }
517