1 /* Copyright (c) 2010, 2020, 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 "sql/sql_admin.h"
24
25 #include <limits.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29
30 #include <algorithm>
31 #include <string>
32 #include <utility>
33
34 #include "keycache.h"
35 #include "m_string.h"
36 #include "my_base.h"
37 #include "my_dbug.h"
38 #include "my_dir.h"
39 #include "my_inttypes.h"
40 #include "my_io.h"
41 #include "my_macros.h"
42 #include "my_sys.h"
43 #include "myisam.h" // TT_USEFRM
44 #include "mysql/components/services/log_builtins.h"
45 #include "mysql/psi/mysql_file.h"
46 #include "mysql/psi/mysql_mutex.h"
47 #include "mysql_com.h"
48 #include "mysqld_error.h"
49 #include "sql/auth/auth_acls.h"
50 #include "sql/auth/auth_common.h" // *_ACL
51 #include "sql/auth/sql_security_ctx.h"
52 #include "sql/clone_handler.h"
53 #include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client
54 #include "sql/dd/dd_table.h" // dd::recreate_table
55 #include "sql/dd/impl/sdi_utils.h" // mdl_lock
56 #include "sql/dd/info_schema/table_stats.h" // dd::info_schema::update_*
57 #include "sql/dd/string_type.h" // dd::String_type
58 #include "sql/dd/types/abstract_table.h" // dd::enum_table_type
59 #include "sql/dd/types/table.h" // dd::Table
60 #include "sql/debug_sync.h" // DEBUG_SYNC
61 #include "sql/derror.h" // ER_THD
62 #include "sql/handler.h"
63 #include "sql/histograms/histogram.h"
64 #include "sql/item.h"
65 #include "sql/key.h"
66 #include "sql/keycaches.h" // get_key_cache
67 #include "sql/lock.h" // acquire_shared_global_read_lock()
68 #include "sql/log.h"
69 #include "sql/log_event.h"
70 #include "sql/mdl.h"
71 #include "sql/mysqld.h" // key_file_misc
72 #include "sql/partition_element.h" // PART_ADMIN
73 #include "sql/protocol.h"
74 #include "sql/protocol_classic.h"
75 #include "sql/rpl_group_replication.h" // is_group_replication_running
76 #include "sql/rpl_gtid.h"
77 #include "sql/rpl_slave_commit_order_manager.h" // Commit_order_manager
78 #include "sql/sp.h" // Sroutine_hash_entry
79 #include "sql/sp_rcontext.h" // sp_rcontext
80 #include "sql/sql_alter.h"
81 #include "sql/sql_alter_instance.h" // Alter_instance
82 #include "sql/sql_backup_lock.h" // acquire_shared_backup_lock
83 #include "sql/sql_base.h" // Open_table_context
84 #include "sql/sql_class.h" // THD
85 #include "sql/sql_error.h"
86 #include "sql/sql_lex.h"
87 #include "sql/sql_list.h"
88 #include "sql/sql_parse.h" // check_table_access
89 #include "sql/sql_partition.h" // set_part_state
90 #include "sql/sql_prepare.h" // mysql_test_show
91 #include "sql/sql_table.h" // mysql_recreate_table
92 #include "sql/ssl_acceptor_context_operator.h"
93 #include "sql/ssl_init_callback.h"
94 #include "sql/system_variables.h"
95 #include "sql/table.h"
96 #include "sql/table_trigger_dispatcher.h" // Table_trigger_dispatcher
97 #include "sql/thd_raii.h"
98 #include "sql/transaction.h" // trans_rollback_stmt
99 #include "sql_string.h"
100 #include "thr_lock.h"
101 #include "violite.h"
102
operator ()(const String * lhs,const String * rhs) const103 bool Column_name_comparator::operator()(const String *lhs,
104 const String *rhs) const {
105 DBUG_ASSERT(lhs->charset()->number == rhs->charset()->number);
106 return sortcmp(lhs, rhs, lhs->charset()) < 0;
107 }
108
send_check_errmsg(THD * thd,TABLE_LIST * table,const char * operator_name,const char * errmsg)109 static int send_check_errmsg(THD *thd, TABLE_LIST *table,
110 const char *operator_name, const char *errmsg)
111
112 {
113 Protocol *protocol = thd->get_protocol();
114 protocol->start_row();
115 protocol->store(table->alias, system_charset_info);
116 protocol->store(operator_name, system_charset_info);
117 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
118 protocol->store(errmsg, system_charset_info);
119 thd->clear_error();
120 if (protocol->end_row()) return -1;
121 return 1;
122 }
123
prepare_for_repair(THD * thd,TABLE_LIST * table_list,HA_CHECK_OPT * check_opt)124 static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
125 HA_CHECK_OPT *check_opt) {
126 int error = 0;
127 TABLE tmp_table, *table;
128 TABLE_SHARE *share;
129 bool has_mdl_lock = false;
130 char from[FN_REFLEN], tmp[FN_REFLEN + 32];
131 const char **ext;
132 MY_STAT stat_info;
133 Open_table_context ot_ctx(thd,
134 (MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_HAS_MDL_LOCK |
135 MYSQL_LOCK_IGNORE_TIMEOUT));
136 DBUG_TRACE;
137
138 if (!(check_opt->sql_flags & TT_USEFRM)) return 0;
139
140 if (!(table = table_list->table)) {
141 const char *key;
142 size_t key_length;
143 /*
144 If the table didn't exist, we have a shared metadata lock
145 on it that is left from mysql_admin_table()'s attempt to
146 open it. Release the shared metadata lock before trying to
147 acquire the exclusive lock to satisfy MDL asserts and avoid
148 deadlocks.
149 */
150 thd->mdl_context.release_transactional_locks();
151 /*
152 Attempt to do full-blown table open in mysql_admin_table() has failed.
153 Let us try to open at least a .FRM for this table.
154 */
155 MDL_REQUEST_INIT(&table_list->mdl_request, MDL_key::TABLE, table_list->db,
156 table_list->table_name, MDL_EXCLUSIVE, MDL_TRANSACTION);
157
158 if (lock_table_names(thd, table_list, table_list->next_global,
159 thd->variables.lock_wait_timeout, 0))
160 return 0;
161 has_mdl_lock = true;
162
163 key_length = get_table_def_key(table_list, &key);
164
165 mysql_mutex_lock(&LOCK_open);
166 share = get_table_share(thd, table_list->db, table_list->table_name, key,
167 key_length, false);
168 mysql_mutex_unlock(&LOCK_open);
169 if (share == nullptr) return 0; // Can't open frm file
170
171 if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, false,
172 nullptr)) {
173 mysql_mutex_lock(&LOCK_open);
174 release_table_share(share);
175 mysql_mutex_unlock(&LOCK_open);
176 return 0; // Out of memory
177 }
178 table = &tmp_table;
179 }
180
181 /*
182 REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
183 */
184 if (table->s->tmp_table) {
185 error = send_check_errmsg(thd, table_list, "repair",
186 "Cannot repair temporary table from .frm file");
187 goto end;
188 }
189
190 /*
191 Check if this is a table type that stores index and data separately,
192 like ISAM or MyISAM. We assume fixed order of engine file name
193 extentions array. First element of engine file name extentions array
194 is meta/index file extention. Second element - data file extention.
195 */
196 ext = table->file->ht->file_extensions;
197 if (!ext || !ext[0] || !ext[1]) goto end; // No data file
198
199 /* A MERGE table must not come here. */
200 DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
201
202 /*
203 Storage engines supporting atomic DDL do not come here either.
204
205 If we are to have storage engine which supports atomic DDL on one
206 hand and REPAIR ... USE_FRM on another then the below code related
207 to table re-creation in SE needs to be adjusted to at least
208 commit the transaction.
209 */
210 DBUG_ASSERT(!(table->file->ht->flags & HTON_SUPPORTS_ATOMIC_DDL));
211
212 // Name of data file
213 strxmov(from, table->s->normalized_path.str, ext[1], NullS);
214 if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
215 goto end; // Can't use USE_FRM flag
216
217 snprintf(tmp, sizeof(tmp), "%s-%lx_%x", from, current_pid, thd->thread_id());
218
219 if (table_list->table) {
220 /*
221 Table was successfully open in mysql_admin_table(). Now we need
222 to close it, but leave it protected by exclusive metadata lock.
223 */
224 if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto end;
225 close_all_tables_for_name(thd, table_list->table->s, false, nullptr);
226 table_list->table = nullptr;
227 }
228 /*
229 After this point we have an exclusive metadata lock on our table
230 in both cases when table was successfully open in mysql_admin_table()
231 and when it was open in prepare_for_repair().
232 */
233
234 if (my_rename(from, tmp, MYF(MY_WME))) {
235 error = send_check_errmsg(thd, table_list, "repair",
236 "Failed renaming data file");
237 goto end;
238 }
239 if (dd::recreate_table(thd, table_list->db, table_list->table_name)) {
240 error = send_check_errmsg(thd, table_list, "repair",
241 "Failed generating table from .frm file");
242 goto end;
243 }
244 if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME))) {
245 error = send_check_errmsg(thd, table_list, "repair",
246 "Failed restoring .MYD file");
247 goto end;
248 }
249
250 if (thd->locked_tables_list.reopen_tables(thd)) goto end;
251
252 /*
253 Now we should be able to open the partially repaired table
254 to finish the repair in the handler later on.
255 */
256 if (open_table(thd, table_list, &ot_ctx)) {
257 error = send_check_errmsg(thd, table_list, "repair",
258 "Failed to open partially repaired table");
259 goto end;
260 }
261
262 end:
263 thd->locked_tables_list.unlink_all_closed_tables(thd, nullptr, 0);
264 if (table == &tmp_table) {
265 mysql_mutex_lock(&LOCK_open);
266 closefrm(table, true); // Free allocated memory
267 mysql_mutex_unlock(&LOCK_open);
268 }
269 /* In case of a temporary table there will be no metadata lock. */
270 if (error && has_mdl_lock) thd->mdl_context.release_transactional_locks();
271
272 return error;
273 }
274
275 /**
276 Check if a given error is something that could occur during
277 open_and_lock_tables() that does not indicate table corruption.
278
279 @param sql_errno Error number to check.
280
281 @retval true Error does not indicate table corruption.
282 @retval false Error could indicate table corruption.
283 */
284
table_not_corrupt_error(uint sql_errno)285 static inline bool table_not_corrupt_error(uint sql_errno) {
286 return (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_FILE_NOT_FOUND ||
287 sql_errno == ER_LOCK_WAIT_TIMEOUT || sql_errno == ER_LOCK_DEADLOCK ||
288 sql_errno == ER_CANT_LOCK_LOG_TABLE ||
289 sql_errno == ER_OPEN_AS_READONLY || sql_errno == ER_WRONG_OBJECT);
290 }
291
Sql_cmd_analyze_table(THD * thd,Alter_info * alter_info,Histogram_command histogram_command,int histogram_buckets)292 Sql_cmd_analyze_table::Sql_cmd_analyze_table(
293 THD *thd, Alter_info *alter_info, Histogram_command histogram_command,
294 int histogram_buckets)
295 : Sql_cmd_ddl_table(alter_info),
296 m_histogram_command(histogram_command),
297 m_histogram_fields(Column_name_comparator(),
298 Mem_root_allocator<String>(thd->mem_root)),
299 m_histogram_buckets(histogram_buckets) {}
300
drop_histogram(THD * thd,TABLE_LIST * table,histograms::results_map & results)301 bool Sql_cmd_analyze_table::drop_histogram(THD *thd, TABLE_LIST *table,
302 histograms::results_map &results) {
303 histograms::columns_set fields;
304
305 for (const auto column : get_histogram_fields())
306 fields.emplace(column->ptr(), column->length());
307 return histograms::drop_histograms(thd, *table, fields, results);
308 }
309
310 /**
311 Send any errors from the ANALYZE TABLE statement to the client.
312
313 This function sends any errors stored in the diagnostics area as a result set
314 to the client instead of a "normal" error. It will also clear the diagnostics
315 area before returning.
316
317 @param thd The thread handler.
318 @param operator_name The name of the ANALYZE TABLE operation that will be
319 printed in the column "Op" of the result set. This is usually either
320 "analyze" or "histogram".
321 @param table_name The name of the table that ANALYZE TABLE operated on.
322
323 @retval true An error occurred while sending the result set to the client.
324 @retval false The result set was sent to the client.
325 */
send_analyze_table_errors(THD * thd,const char * operator_name,const char * table_name)326 static bool send_analyze_table_errors(THD *thd, const char *operator_name,
327 const char *table_name) {
328 Diagnostics_area::Sql_condition_iterator it =
329 thd->get_stmt_da()->sql_conditions();
330 const Sql_condition *err;
331 Protocol *protocol = thd->get_protocol();
332 while ((err = it++)) {
333 protocol->start_row();
334 protocol->store(table_name, system_charset_info);
335 protocol->store(operator_name, system_charset_info);
336 protocol->store_string(warning_level_names[err->severity()].str,
337 warning_level_names[err->severity()].length,
338 system_charset_info);
339 protocol->store(err->message_text(), system_charset_info);
340 if (protocol->end_row()) return true;
341 }
342 thd->get_stmt_da()->reset_condition_info(thd);
343 return false;
344 }
345
send_histogram_results(THD * thd,const histograms::results_map & results,const TABLE_LIST * table)346 bool Sql_cmd_analyze_table::send_histogram_results(
347 THD *thd, const histograms::results_map &results, const TABLE_LIST *table) {
348 Item *item;
349 List<Item> field_list;
350
351 field_list.push_back(item =
352 new Item_empty_string("Table", NAME_CHAR_LEN * 2));
353 item->maybe_null = true;
354 field_list.push_back(item = new Item_empty_string("Op", 10));
355 item->maybe_null = true;
356 field_list.push_back(item = new Item_empty_string("Msg_type", 10));
357 item->maybe_null = true;
358 field_list.push_back(
359 item = new Item_empty_string("Msg_text", SQL_ADMIN_MSG_TEXT_SIZE));
360 item->maybe_null = true;
361 if (thd->send_result_metadata(&field_list,
362 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) {
363 return true; /* purecov: deadcode */
364 }
365
366 std::string combined_name(table->db, table->db_length);
367 combined_name.append(".");
368 combined_name.append(table->table_name, table->table_name_length);
369 if (send_analyze_table_errors(thd, "histogram", combined_name.c_str()))
370 return true;
371
372 Protocol *protocol = thd->get_protocol();
373 for (const auto &pair : results) {
374 const char *table_name = combined_name.c_str();
375
376 std::string message;
377 std::string message_type;
378 switch (pair.second) {
379 // Status messages
380 case histograms::Message::HISTOGRAM_CREATED:
381 message_type.assign("status");
382 message.assign("Histogram statistics created for column '");
383 message.append(pair.first);
384 message.append("'.");
385 break;
386 case histograms::Message::HISTOGRAM_DELETED:
387 message_type.assign("status");
388 message.assign("Histogram statistics removed for column '");
389 message.append(pair.first);
390 message.append("'.");
391 break;
392 // Errror messages
393 case histograms::Message::FIELD_NOT_FOUND:
394 message_type.assign("Error");
395 message.assign("The column '");
396 message.append(pair.first);
397 message.append("' does not exist.");
398 break;
399 case histograms::Message::UNSUPPORTED_DATA_TYPE:
400 message_type.assign("Error");
401 message.assign("The column '");
402 message.append(pair.first);
403 message.append("' has an unsupported data type.");
404 break;
405 case histograms::Message::TEMPORARY_TABLE:
406 message_type.assign("Error");
407 message.assign(
408 "Cannot create histogram statistics for a temporary table.");
409 break;
410 case histograms::Message::ENCRYPTED_TABLE:
411 message_type.assign("Error");
412 message.assign(
413 "Cannot create histogram statistics for an encrypted table.");
414 break;
415 case histograms::Message::VIEW:
416 message_type.assign("Error");
417 message.assign("Cannot create histogram statistics for a view.");
418 break;
419 case histograms::Message::MULTIPLE_TABLES_SPECIFIED:
420 message_type.assign("Error");
421 message.assign(
422 "Only one table can be specified while modifying histogram "
423 "statistics.");
424 table_name = "";
425 break;
426 case histograms::Message::COVERED_BY_SINGLE_PART_UNIQUE_INDEX:
427 message_type.assign("Error");
428 message.assign("The column '");
429 message.append(pair.first);
430 message.append("' is covered by a single-part unique index.");
431 break;
432 case histograms::Message::NO_HISTOGRAM_FOUND:
433 message_type.assign("Error");
434 message.assign("No histogram statistics found for column '");
435 message.append(pair.first);
436 message.append("'.");
437 break;
438 case histograms::Message::SERVER_READ_ONLY:
439 message_type.assign("Error");
440 message.assign("The server is in read-only mode.");
441 table_name = "";
442 break;
443 }
444
445 protocol->start_row();
446 if (protocol->store(table_name, system_charset_info) ||
447 protocol->store_string(STRING_WITH_LEN("histogram"),
448 system_charset_info) ||
449 protocol->store_string(message_type.c_str(), message_type.length(),
450 system_charset_info) ||
451 protocol->store_string(message.c_str(), message.size(),
452 system_charset_info) ||
453 protocol->end_row()) {
454 return true; /* purecov: deadcode */
455 }
456 }
457
458 return false;
459 }
460
update_histogram(THD * thd,TABLE_LIST * table,histograms::results_map & results)461 bool Sql_cmd_analyze_table::update_histogram(THD *thd, TABLE_LIST *table,
462 histograms::results_map &results) {
463 histograms::columns_set fields;
464
465 for (const auto column : get_histogram_fields())
466 fields.emplace(column->ptr(), column->length());
467
468 return histograms::update_histogram(thd, table, fields,
469 get_histogram_buckets(), results);
470 }
471
472 using Check_result = std::pair<bool, int>;
473 template <typename CHECK_FUNC>
check_for_upgrade(THD * thd,dd::String_type & sname,dd::String_type & tname,CHECK_FUNC && cf)474 static Check_result check_for_upgrade(THD *thd, dd::String_type &sname,
475 dd::String_type &tname, CHECK_FUNC &&cf) {
476 dd::cache::Dictionary_client *dc = thd->dd_client();
477
478 const dd::Table *t = nullptr;
479 if (dc->acquire(sname, tname, &t)) {
480 return {true, HA_ADMIN_FAILED};
481 }
482 DBUG_ASSERT(t != nullptr);
483
484 if (is_checked_for_upgrade(*t)) {
485 DBUG_PRINT("admin", ("Table %s (%llu) already checked for upgrade, "
486 "skipping",
487 t->name().c_str(), t->id()));
488 return {false, HA_ADMIN_ALREADY_DONE};
489 }
490 DBUG_PRINT("admin",
491 ("Table %s (%llu) needs checking", t->name().c_str(), t->id()));
492 int result_code = cf();
493
494 if (result_code != HA_ADMIN_OK && result_code != HA_ADMIN_ALREADY_DONE) {
495 DBUG_PRINT("admin", ("result_code: %d", result_code));
496 return {false, result_code};
497 }
498 Check_result error{true, result_code};
499
500 // Ok we have successfully checked table for upgrade. Record
501 // this fact in the DD.
502
503 if (acquire_shared_global_read_lock(thd, thd->variables.lock_wait_timeout)) {
504 return error;
505 }
506
507 // Need IX on schema for acquire_for_modification()
508 if (dd::sdi_utils::mdl_lock(thd, MDL_key::SCHEMA, sname, "",
509 MDL_INTENTION_EXCLUSIVE)) {
510 return error;
511 }
512
513 // Need X on table so that the last_checked version can be updated
514 if (dd::sdi_utils::mdl_lock(thd, MDL_key::TABLE, sname, tname)) {
515 return error;
516 }
517
518 dd::Table *c = nullptr;
519 if (dc->acquire_for_modification(t->id(), &c)) {
520 return error;
521 }
522 c->mark_as_checked_for_upgrade();
523 if (dc->update(c)) {
524 return error;
525 }
526 DBUG_PRINT("admin",
527 ("dd::Table %s marked as checked for upgrade", c->name().c_str()));
528
529 return {false, result_code};
530 }
531
532 /*
533 RETURN VALUES
534 false Message sent to net (admin operation went ok)
535 true Message should be sent by caller
536 (admin operation or network communication failed)
537 */
mysql_admin_table(THD * thd,TABLE_LIST * tables,HA_CHECK_OPT * check_opt,const char * operator_name,thr_lock_type lock_type,bool open_for_modify,bool repair_table_use_frm,uint extra_open_options,int (* prepare_func)(THD *,TABLE_LIST *,HA_CHECK_OPT *),int (handler::* operator_func)(THD *,HA_CHECK_OPT *),int check_view,Alter_info * alter_info,bool need_to_acquire_shared_backup_lock)538 static bool mysql_admin_table(
539 THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt,
540 const char *operator_name, thr_lock_type lock_type, bool open_for_modify,
541 bool repair_table_use_frm, uint extra_open_options,
542 int (*prepare_func)(THD *, TABLE_LIST *, HA_CHECK_OPT *),
543 int (handler::*operator_func)(THD *, HA_CHECK_OPT *), int check_view,
544 Alter_info *alter_info, bool need_to_acquire_shared_backup_lock) {
545 /*
546 Prevent InnoDB from automatically committing InnoDB
547 transaction each time data-dictionary tables are closed after
548 being updated.
549 */
550 Disable_autocommit_guard autocommit_guard(thd);
551
552 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
553
554 TABLE_LIST *table;
555 SELECT_LEX *select = thd->lex->select_lex;
556 List<Item> field_list;
557 Item *item;
558 Protocol *protocol = thd->get_protocol();
559 LEX *lex = thd->lex;
560 int result_code;
561 bool gtid_rollback_must_be_skipped =
562 ((thd->variables.gtid_next.type == ASSIGNED_GTID ||
563 thd->variables.gtid_next.type == ANONYMOUS_GTID) &&
564 (!thd->skip_gtid_rollback));
565 bool ignore_grl_on_analyze = operator_func == &handler::ha_analyze;
566 DBUG_TRACE;
567
568 field_list.push_back(item =
569 new Item_empty_string("Table", NAME_CHAR_LEN * 2));
570 item->maybe_null = true;
571 field_list.push_back(item = new Item_empty_string("Op", 10));
572 item->maybe_null = true;
573 field_list.push_back(item = new Item_empty_string("Msg_type", 10));
574 item->maybe_null = true;
575 field_list.push_back(
576 item = new Item_empty_string("Msg_text", SQL_ADMIN_MSG_TEXT_SIZE));
577 item->maybe_null = true;
578 if (thd->send_result_metadata(&field_list,
579 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
580 return true;
581
582 /*
583 Close all temporary tables which were pre-open to simplify
584 privilege checking. Clear all references to closed tables.
585 */
586 close_thread_tables(thd);
587 for (table = tables; table; table = table->next_local) table->table = nullptr;
588
589 /*
590 This statement will be written to the binary log even if it fails.
591 But a failing statement calls trans_rollback_stmt which calls
592 gtid_state->update_on_rollback, which releases GTID ownership.
593 And GTID ownership must be held when the statement is being
594 written to the binary log. Therefore, we set this flag before
595 executing the statement. The flag tells
596 gtid_state->update_on_rollback to skip releasing ownership.
597 */
598 if (gtid_rollback_must_be_skipped) thd->skip_gtid_rollback = true;
599
600 for (table = tables; table; table = table->next_local) {
601 char table_name[NAME_LEN * 2 + 2];
602 const char *db = table->db;
603 bool fatal_error = false;
604 bool open_error;
605
606 DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
607 DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
608 strxmov(table_name, db, ".", table->table_name, NullS);
609 thd->open_options |= extra_open_options;
610 table->set_lock({lock_type, THR_DEFAULT});
611 /*
612 To make code safe for re-execution we need to reset type of MDL
613 request as code below may change it.
614 To allow concurrent execution of read-only operations we acquire
615 weak metadata lock for them.
616 */
617 table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE)
618 ? MDL_SHARED_NO_READ_WRITE
619 : MDL_SHARED_READ);
620 /* open only one table from local list of command */
621 {
622 TABLE_LIST *save_next_global, *save_next_local;
623 save_next_global = table->next_global;
624 table->next_global = nullptr;
625 save_next_local = table->next_local;
626 table->next_local = nullptr;
627 select->table_list.first = table;
628 /*
629 Time zone tables and SP tables can be add to lex->query_tables list,
630 so it have to be prepared.
631 TODO: Investigate if we can put extra tables into argument instead of
632 using lex->query_tables
633 */
634 lex->query_tables = table;
635 lex->query_tables_last = &table->next_global;
636 lex->query_tables_own_last = nullptr;
637 /*
638 CHECK TABLE command is allowed for views as well. Check on alter flags
639 to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
640 allowed.
641 */
642 if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION ||
643 check_view != 1)
644 table->required_type = dd::enum_table_type::BASE_TABLE;
645
646 if (!thd->locked_tables_mode && repair_table_use_frm) {
647 /*
648 If we're not under LOCK TABLES and we're executing REPAIR TABLE
649 USE_FRM, we need to ignore errors from open_and_lock_tables().
650 REPAIR TABLE USE_FRM is a heavy weapon used when a table is
651 critically damaged, so open_and_lock_tables() will most likely
652 report errors. Those errors are not interesting for the user
653 because it's already known that the table is badly damaged.
654 */
655
656 Diagnostics_area tmp_da(false);
657 thd->push_diagnostics_area(&tmp_da);
658
659 open_error = open_temporary_tables(thd, table);
660
661 if (!open_error) {
662 open_error = open_and_lock_tables(thd, table, 0);
663
664 if (!open_error && need_to_acquire_shared_backup_lock &&
665 /*
666 Acquire backup lock explicitly since lock types used by
667 admin statements won't cause its automatic acquisition
668 in open_and_lock_tables().
669 */
670 acquire_shared_backup_lock(thd,
671 thd->variables.lock_wait_timeout)) {
672 result_code = HA_ADMIN_FAILED;
673 goto send_result;
674 }
675 }
676
677 thd->pop_diagnostics_area();
678 if (tmp_da.is_error()) {
679 // Copy the exception condition information.
680 thd->get_stmt_da()->set_error_status(tmp_da.mysql_errno(),
681 tmp_da.message_text(),
682 tmp_da.returned_sqlstate());
683 }
684 } else {
685 /*
686 It's assumed that even if it is REPAIR TABLE USE_FRM, the table
687 can be opened if we're under LOCK TABLES (otherwise LOCK TABLES
688 would fail). Thus, the only errors we could have from
689 open_and_lock_tables() are logical ones, like incorrect locking
690 mode. It does make sense for the user to see such errors.
691 */
692
693 open_error = open_temporary_tables(thd, table);
694
695 if (!open_error) {
696 open_error = open_and_lock_tables(thd, table, 0);
697
698 if (!open_error && need_to_acquire_shared_backup_lock &&
699 /*
700 Acquire backup lock explicitly since lock types used by
701 admin statements won't cause its automatic acquisition
702 in open_and_lock_tables().
703 */
704 acquire_shared_backup_lock(thd,
705 thd->variables.lock_wait_timeout)) {
706 result_code = HA_ADMIN_FAILED;
707 goto send_result;
708 }
709 }
710 }
711
712 /*
713 Views are always treated as materialized views, including creation
714 of temporary table descriptor.
715 */
716 if (!open_error && table->is_view()) {
717 open_error = table->resolve_derived(thd, false);
718 if (!open_error) open_error = table->setup_materialized_derived(thd);
719 }
720 table->next_global = save_next_global;
721 table->next_local = save_next_local;
722 thd->open_options &= ~extra_open_options;
723
724 /*
725 If open_and_lock_tables() failed, close_thread_tables() will close
726 the table and table->table can therefore be invalid.
727 */
728 if (open_error) table->table = nullptr;
729
730 /*
731 Under locked tables, we know that the table can be opened,
732 so any errors opening the table are logical errors.
733 In these cases it does not make sense to try to repair.
734 */
735 if (open_error && thd->locked_tables_mode) {
736 result_code = HA_ADMIN_FAILED;
737 goto send_result;
738 }
739 if (table->table) {
740 /*
741 Set up which partitions that should be processed
742 if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
743 CACHE INDEX/LOAD INDEX for specified partitions
744 */
745 if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) {
746 if (!table->table->part_info) {
747 my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
748 result_code = HA_ADMIN_FAILED;
749 goto send_result;
750 }
751
752 if (set_part_state(alter_info, table->table->part_info, PART_ADMIN,
753 true)) {
754 my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), table_name);
755 result_code = HA_ADMIN_FAILED;
756 goto send_result;
757 }
758 }
759 }
760 }
761 DBUG_PRINT("admin", ("table: %p", table->table));
762
763 if (prepare_func) {
764 DBUG_PRINT("admin", ("calling prepare_func"));
765 switch ((*prepare_func)(thd, table, check_opt)) {
766 case 1: // error, message written to net
767 trans_rollback_stmt(thd);
768 trans_rollback(thd);
769 /* Make sure this table instance is not reused after the operation. */
770 if (table->table) table->table->m_needs_reopen = true;
771 close_thread_tables(thd);
772 thd->mdl_context.release_transactional_locks();
773 DBUG_PRINT("admin", ("simple error, admin next table"));
774 continue;
775 case -1: // error, message could be written to net
776 /* purecov: begin inspected */
777 DBUG_PRINT("admin", ("severe error, stop"));
778 goto err;
779 /* purecov: end */
780 default: // should be 0 otherwise
781 DBUG_PRINT("admin", ("prepare_func succeeded"));
782 ;
783 }
784 }
785
786 /*
787 CHECK TABLE command is only command where VIEW allowed here and this
788 command use only temporary teble method for VIEWs resolving => there
789 can't be VIEW tree substitition of join view => if opening table
790 succeed then table->table will have real TABLE pointer as value (in
791 case of join view substitution table->table can be 0, but here it is
792 impossible)
793 */
794 if (!table->table) {
795 DBUG_PRINT("admin", ("open table failed"));
796 if (thd->get_stmt_da()->cond_count() == 0)
797 push_warning(thd, Sql_condition::SL_WARNING, ER_CHECK_NO_SUCH_TABLE,
798 ER_THD(thd, ER_CHECK_NO_SUCH_TABLE));
799 if (thd->get_stmt_da()->is_error() &&
800 table_not_corrupt_error(thd->get_stmt_da()->mysql_errno()))
801 result_code = HA_ADMIN_FAILED;
802 else
803 /* Default failure code is corrupt table */
804 result_code = HA_ADMIN_CORRUPT;
805 goto send_result;
806 }
807
808 if (table->is_view()) {
809 result_code = HA_ADMIN_OK;
810 goto send_result;
811 }
812
813 if (table->schema_table) {
814 result_code = HA_ADMIN_NOT_IMPLEMENTED;
815 goto send_result;
816 }
817
818 if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify) {
819 /* purecov: begin inspected */
820 char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
821 size_t length;
822 enum_sql_command save_sql_command = lex->sql_command;
823 DBUG_PRINT("admin", ("sending error message"));
824 protocol->start_row();
825 protocol->store(table_name, system_charset_info);
826 protocol->store(operator_name, system_charset_info);
827 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
828 length = snprintf(buff, sizeof(buff), ER_THD(thd, ER_OPEN_AS_READONLY),
829 table_name);
830 protocol->store_string(buff, length, system_charset_info);
831 {
832 /* Prevent intermediate commits to invoke commit order */
833 Implicit_substatement_state_guard substatement_guard(
834 thd, enum_implicit_substatement_guard_mode ::
835 DISABLE_GTID_AND_SPCO_IF_SPCO_ACTIVE);
836 trans_commit_stmt(thd, ignore_grl_on_analyze);
837 trans_commit(thd, ignore_grl_on_analyze);
838 }
839 /* Make sure this table instance is not reused after the operation. */
840 if (table->table) table->table->m_needs_reopen = true;
841 close_thread_tables(thd);
842 thd->mdl_context.release_transactional_locks();
843 lex->reset_query_tables_list(false);
844 /*
845 Restore Query_tables_list::sql_command value to make statement
846 safe for re-execution.
847 */
848 lex->sql_command = save_sql_command;
849 if (protocol->end_row()) goto err;
850 thd->get_stmt_da()->reset_diagnostics_area();
851 continue;
852 /* purecov: end */
853 }
854
855 /*
856 Close all instances of the table to allow MyISAM "repair"
857 to rename files.
858 @todo: This code does not close all instances of the table.
859 It only closes instances in other connections, but if this
860 connection has LOCK TABLE t1 a READ, t1 b WRITE,
861 both t1 instances will be kept open.
862 There is no need to execute this branch for InnoDB, which does
863 repair by recreate. There is no need to do it for OPTIMIZE,
864 which doesn't move files around.
865 Hence, this code should be moved to prepare_for_repair(),
866 and executed only for MyISAM engine.
867 */
868 if (lock_type == TL_WRITE && !table->table->s->tmp_table) {
869 if (wait_while_table_is_used(thd, table->table,
870 HA_EXTRA_PREPARE_FOR_RENAME))
871 goto err;
872 DEBUG_SYNC(thd, "after_admin_flush");
873 /*
874 XXX: hack: switch off open_for_modify to skip the
875 flush that is made later in the execution flow.
876 */
877 open_for_modify = false;
878 }
879
880 if (table->table->s->crashed && operator_func == &handler::ha_check) {
881 /* purecov: begin inspected */
882 DBUG_PRINT("admin", ("sending crashed warning"));
883 protocol->start_row();
884 protocol->store(table_name, system_charset_info);
885 protocol->store(operator_name, system_charset_info);
886 protocol->store_string(STRING_WITH_LEN("warning"), system_charset_info);
887 protocol->store_string(STRING_WITH_LEN("Table is marked as crashed"),
888 system_charset_info);
889 if (protocol->end_row()) goto err;
890 /* purecov: end */
891 }
892
893 if (operator_func == &handler::ha_repair &&
894 !(check_opt->sql_flags & TT_USEFRM)) {
895 // Check for old temporal format if avoid_temporal_upgrade is disabled.
896 mysql_mutex_lock(&LOCK_global_system_variables);
897 const bool check_temporal_upgrade = !avoid_temporal_upgrade;
898 mysql_mutex_unlock(&LOCK_global_system_variables);
899
900 if ((check_table_for_old_types(table->table, check_temporal_upgrade) ==
901 HA_ADMIN_NEEDS_ALTER) ||
902 (table->table->file->ha_check_for_upgrade(check_opt) ==
903 HA_ADMIN_NEEDS_ALTER)) {
904 DBUG_PRINT("admin", ("recreating table"));
905 /*
906 Temporary table are always created by current server so they never
907 require upgrade. So we don't need to pre-open them before calling
908 mysql_recreate_table().
909 */
910 DBUG_ASSERT(!table->table->s->tmp_table);
911
912 trans_rollback_stmt(thd);
913 trans_rollback(thd);
914 /* Make sure this table instance is not reused after the operation. */
915 if (table->table) table->table->m_needs_reopen = true;
916 close_thread_tables(thd);
917 thd->mdl_context.release_transactional_locks();
918
919 /*
920 table_list->table has been closed and freed. Do not reference
921 uninitialized data. open_tables() could fail.
922 */
923 table->table = nullptr;
924 /* Same applies to MDL ticket. */
925 table->mdl_request.ticket = nullptr;
926
927 {
928 // binlogging is done by caller if wanted
929 Disable_binlog_guard binlog_guard(thd);
930 result_code = mysql_recreate_table(thd, table, false);
931 }
932 /*
933 mysql_recreate_table() can push OK or ERROR.
934 Clear 'OK' status. If there is an error, keep it:
935 we will store the error message in a result set row
936 and then clear.
937 */
938 if (thd->get_stmt_da()->is_ok())
939 thd->get_stmt_da()->reset_diagnostics_area();
940 table->table = nullptr;
941 result_code = result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
942 goto send_result;
943 }
944 }
945
946 if (check_opt && (check_opt->sql_flags & TT_FOR_UPGRADE) != 0) {
947 if (table->table->s->tmp_table) {
948 result_code = HA_ADMIN_OK;
949 } else {
950 dd::String_type snam = dd::make_string_type(table->table->s->db);
951 dd::String_type tnam =
952 dd::make_string_type(table->table->s->table_name);
953
954 Check_result cr = check_for_upgrade(thd, snam, tnam, [&]() {
955 DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
956 return (table->table->file->*operator_func)(thd, check_opt);
957 });
958
959 result_code = cr.second;
960 if (cr.first) {
961 goto err;
962 }
963 }
964 }
965 // Some other admin COMMAND
966 else {
967 DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
968 result_code = (table->table->file->*operator_func)(thd, check_opt);
969 }
970 DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
971
972 /*
973 ANALYZE statement calculates values for dynamic fields of
974 I_S.TABLES and I_S.STATISTICS table in table_stats and index_stats
975 table. This table is joined with new dd table to provide results
976 when I_S table is queried.
977 To get latest statistics of table or index, user should use analyze
978 table statement before querying I_S.TABLES or I_S.STATISTICS
979 */
980
981 if (!read_only && ignore_grl_on_analyze) {
982 // Acquire the lock
983 if (dd::info_schema::update_table_stats(thd, table) ||
984 dd::info_schema::update_index_stats(thd, table)) {
985 // Play safe, rollback possible changes to the data-dictionary.
986 trans_rollback_stmt(thd);
987 trans_rollback_implicit(thd);
988 result_code = HA_ADMIN_STATS_UPD_ERR;
989 goto send_result;
990 }
991 }
992
993 /*
994 push_warning() if the table version is lesser than current
995 server version and there are triggers for this table.
996 */
997 if (operator_func == &handler::ha_check &&
998 (check_opt->sql_flags & TT_FOR_UPGRADE) && table->table->triggers) {
999 table->table->triggers->print_upgrade_warnings(thd);
1000 }
1001
1002 send_result:
1003
1004 lex->cleanup_after_one_table_open();
1005 thd->clear_error(); // these errors shouldn't get client
1006 if (send_analyze_table_errors(thd, operator_name, table_name)) goto err;
1007 protocol->start_row();
1008 protocol->store(table_name, system_charset_info);
1009 protocol->store(operator_name, system_charset_info);
1010
1011 send_result_message:
1012
1013 DBUG_PRINT("info", ("result_code: %d", result_code));
1014 switch (result_code) {
1015 case HA_ADMIN_NOT_IMPLEMENTED: {
1016 char buf[MYSQL_ERRMSG_SIZE];
1017 size_t length =
1018 snprintf(buf, sizeof(buf), ER_THD(thd, ER_CHECK_NOT_IMPLEMENTED),
1019 operator_name);
1020 protocol->store_string(STRING_WITH_LEN("note"), system_charset_info);
1021 protocol->store_string(buf, length, system_charset_info);
1022 } break;
1023
1024 case HA_ADMIN_NOT_BASE_TABLE: {
1025 char buf[MYSQL_ERRMSG_SIZE];
1026
1027 String tbl_name;
1028 tbl_name.append(String(db, system_charset_info));
1029 tbl_name.append('.');
1030 tbl_name.append(String(table_name, system_charset_info));
1031
1032 size_t length =
1033 snprintf(buf, sizeof(buf), ER_THD(thd, ER_BAD_TABLE_ERROR),
1034 tbl_name.c_ptr());
1035 protocol->store_string(STRING_WITH_LEN("note"), system_charset_info);
1036 protocol->store_string(buf, length, system_charset_info);
1037 } break;
1038
1039 case HA_ADMIN_OK:
1040 protocol->store_string(STRING_WITH_LEN("status"), system_charset_info);
1041 protocol->store_string(STRING_WITH_LEN("OK"), system_charset_info);
1042 break;
1043
1044 case HA_ADMIN_FAILED:
1045 protocol->store_string(STRING_WITH_LEN("status"), system_charset_info);
1046 protocol->store_string(STRING_WITH_LEN("Operation failed"),
1047 system_charset_info);
1048 break;
1049
1050 case HA_ADMIN_REJECT:
1051 protocol->store_string(STRING_WITH_LEN("status"), system_charset_info);
1052 protocol->store_string(
1053 STRING_WITH_LEN("Operation need committed state"),
1054 system_charset_info);
1055 open_for_modify = false;
1056 break;
1057
1058 case HA_ADMIN_ALREADY_DONE:
1059 protocol->store_string(STRING_WITH_LEN("status"), system_charset_info);
1060 protocol->store_string(STRING_WITH_LEN("Table is already up to date"),
1061 system_charset_info);
1062 break;
1063
1064 case HA_ADMIN_CORRUPT:
1065 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
1066 protocol->store_string(STRING_WITH_LEN("Corrupt"), system_charset_info);
1067 fatal_error = true;
1068 break;
1069
1070 case HA_ADMIN_INVALID:
1071 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
1072 protocol->store_string(STRING_WITH_LEN("Invalid argument"),
1073 system_charset_info);
1074 break;
1075
1076 case HA_ADMIN_TRY_ALTER: {
1077 uint save_flags;
1078
1079 /* Store the original value of alter_info->flags */
1080 save_flags = alter_info->flags;
1081 {
1082 /* Prevent intermediate commits to invoke commit order */
1083 Implicit_substatement_state_guard substatement_guard(
1084 thd, enum_implicit_substatement_guard_mode ::
1085 DISABLE_GTID_AND_SPCO_IF_SPCO_ACTIVE);
1086 /*
1087 This is currently used only by InnoDB. ha_innobase::optimize()
1088 answers "try with alter", so here we close the table, do an ALTER
1089 TABLE, reopen the table and do ha_innobase::analyze() on it. We have
1090 to end the row, so analyze could return more rows.
1091 */
1092 trans_commit_stmt(thd, ignore_grl_on_analyze);
1093 trans_commit(thd, ignore_grl_on_analyze);
1094 }
1095 close_thread_tables(thd);
1096 thd->mdl_context.release_transactional_locks();
1097
1098 /*
1099 table_list->table has been closed and freed. Do not reference
1100 uninitialized data. open_tables() could fail.
1101 */
1102 table->table = nullptr;
1103 /* Same applies to MDL ticket. */
1104 table->mdl_request.ticket = nullptr;
1105
1106 DEBUG_SYNC(thd, "ha_admin_try_alter");
1107 protocol->store_string(STRING_WITH_LEN("note"), system_charset_info);
1108 if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) {
1109 protocol->store_string(
1110 STRING_WITH_LEN("Table does not support optimize on "
1111 "partitions. All partitions "
1112 "will be rebuilt and analyzed."),
1113 system_charset_info);
1114 } else {
1115 protocol->store_string(
1116 STRING_WITH_LEN("Table does not support optimize, "
1117 "doing recreate + analyze instead"),
1118 system_charset_info);
1119 }
1120 if (protocol->end_row()) goto err;
1121 DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
1122 TABLE_LIST *save_next_local = table->next_local,
1123 *save_next_global = table->next_global;
1124 table->next_local = table->next_global = nullptr;
1125 {
1126 // binlogging is done by caller if wanted
1127 Disable_binlog_guard binlog_guard(thd);
1128 /* Don't forget to pre-open temporary tables. */
1129 result_code = (open_temporary_tables(thd, table) ||
1130 mysql_recreate_table(thd, table, false));
1131 }
1132 /*
1133 mysql_recreate_table() can push OK or ERROR.
1134 Clear 'OK' status. If there is an error, keep it:
1135 we will store the error message in a result set row
1136 and then clear.
1137 */
1138 if (thd->get_stmt_da()->is_ok())
1139 thd->get_stmt_da()->reset_diagnostics_area();
1140 {
1141 /* Prevent intermediate commits to invoke commit order */
1142 Implicit_substatement_state_guard substatement_guard(
1143 thd, enum_implicit_substatement_guard_mode ::
1144 DISABLE_GTID_AND_SPCO_IF_SPCO_ACTIVE);
1145 trans_commit_stmt(thd, ignore_grl_on_analyze);
1146 trans_commit(thd, ignore_grl_on_analyze);
1147 }
1148 close_thread_tables(thd);
1149 thd->mdl_context.release_transactional_locks();
1150 /* Clear references to TABLE and MDL_ticket after releasing them. */
1151 table->table = nullptr;
1152 table->mdl_request.ticket = nullptr;
1153 if (!result_code) // recreation went ok
1154 {
1155 DEBUG_SYNC(thd, "ha_admin_open_ltable");
1156 if (acquire_shared_backup_lock(thd,
1157 thd->variables.lock_wait_timeout)) {
1158 result_code = HA_ADMIN_FAILED;
1159 } else {
1160 table->mdl_request.set_type(MDL_SHARED_READ);
1161 if (!open_temporary_tables(thd, table) &&
1162 (table->table = open_n_lock_single_table(
1163 thd, table, TL_READ_NO_INSERT, 0))) {
1164 /*
1165 Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
1166 to force analyze on all partitions.
1167 */
1168 alter_info->flags &= ~(Alter_info::ALTER_ADMIN_PARTITION);
1169 result_code = table->table->file->ha_analyze(thd, check_opt);
1170 if (result_code == HA_ADMIN_ALREADY_DONE)
1171 result_code = HA_ADMIN_OK;
1172 else if (result_code) // analyze failed
1173 table->table->file->print_error(result_code, MYF(0));
1174 alter_info->flags = save_flags;
1175 } else
1176 result_code = -1; // open failed
1177 }
1178 }
1179 /* Start a new row for the final status row */
1180 protocol->start_row();
1181 protocol->store(table_name, system_charset_info);
1182 protocol->store(operator_name, system_charset_info);
1183 if (result_code) // either mysql_recreate_table or analyze failed
1184 {
1185 DBUG_ASSERT(thd->is_error() || thd->killed);
1186 if (thd->is_error()) {
1187 Diagnostics_area *da = thd->get_stmt_da();
1188 if (!thd->get_protocol()->connection_alive()) {
1189 LogEvent()
1190 .type(LOG_TYPE_ERROR)
1191 .subsys(LOG_SUBSYSTEM_TAG)
1192 .prio(ERROR_LEVEL)
1193 .source_file(MY_BASENAME)
1194 .lookup(ER_ERROR_INFO_FROM_DA, da->mysql_errno(),
1195 da->message_text())
1196 .sqlstate(da->returned_sqlstate());
1197 } else {
1198 /* Hijack the row already in-progress. */
1199 protocol->store_string(STRING_WITH_LEN("error"),
1200 system_charset_info);
1201 protocol->store(da->message_text(), system_charset_info);
1202 if (protocol->end_row()) goto err;
1203 /* Start off another row for HA_ADMIN_FAILED */
1204 protocol->start_row();
1205 protocol->store(table_name, system_charset_info);
1206 protocol->store(operator_name, system_charset_info);
1207 }
1208 thd->clear_error();
1209 }
1210 /* Make sure this table instance is not reused after the operation. */
1211 if (table->table) table->table->m_needs_reopen = true;
1212 }
1213 result_code = result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
1214 table->next_local = save_next_local;
1215 table->next_global = save_next_global;
1216 goto send_result_message;
1217 }
1218 case HA_ADMIN_WRONG_CHECKSUM: {
1219 protocol->store_string(STRING_WITH_LEN("note"), system_charset_info);
1220 protocol->store_string(ER_THD(thd, ER_VIEW_CHECKSUM),
1221 strlen(ER_THD(thd, ER_VIEW_CHECKSUM)),
1222 system_charset_info);
1223 break;
1224 }
1225
1226 case HA_ADMIN_NEEDS_UPGRADE:
1227 case HA_ADMIN_NEEDS_ALTER: {
1228 char buf[MYSQL_ERRMSG_SIZE];
1229 size_t length;
1230
1231 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
1232 if (table->table->file->ha_table_flags() & HA_CAN_REPAIR)
1233 length =
1234 snprintf(buf, sizeof(buf), ER_THD(thd, ER_TABLE_NEEDS_UPGRADE),
1235 table->table_name);
1236 else
1237 length =
1238 snprintf(buf, sizeof(buf), ER_THD(thd, ER_TABLE_NEEDS_REBUILD),
1239 table->table_name);
1240 protocol->store_string(buf, length, system_charset_info);
1241 fatal_error = true;
1242 break;
1243 }
1244
1245 case HA_ADMIN_STATS_UPD_ERR:
1246 protocol->store_string(STRING_WITH_LEN("status"), system_charset_info);
1247 protocol->store_string(
1248 STRING_WITH_LEN("Unable to write table statistics to DD tables"),
1249 system_charset_info);
1250 break;
1251
1252 case HA_ADMIN_NEEDS_DUMP_UPGRADE: {
1253 /*
1254 In-place upgrade does not allow pre 5.0 decimal to 8.0. Recreation of
1255 tables will not create pre 5.0 decimal types. Hence, control should
1256 never reach here.
1257 */
1258 DBUG_ASSERT(false);
1259
1260 char buf[MYSQL_ERRMSG_SIZE];
1261 size_t length;
1262
1263 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
1264 length = snprintf(buf, sizeof(buf),
1265 "Table upgrade required for "
1266 "`%-.64s`.`%-.64s`. Please dump/reload table to "
1267 "fix it!",
1268 table->db, table->table_name);
1269 protocol->store_string(buf, length, system_charset_info);
1270 fatal_error = true;
1271 break;
1272 }
1273
1274 default: // Probably HA_ADMIN_INTERNAL_ERROR
1275 {
1276 char buf[MYSQL_ERRMSG_SIZE];
1277 size_t length = snprintf(buf, sizeof(buf),
1278 "Unknown - internal error %d during operation",
1279 result_code);
1280 protocol->store_string(STRING_WITH_LEN("error"), system_charset_info);
1281 protocol->store_string(buf, length, system_charset_info);
1282 fatal_error = true;
1283 break;
1284 }
1285 }
1286 if (table->table) {
1287 if (table->table->s->tmp_table) {
1288 /*
1289 If the table was not opened successfully, do not try to get
1290 status information. (Bug#47633)
1291 */
1292 if (open_for_modify && !open_error)
1293 table->table->file->info(HA_STATUS_CONST);
1294 } else if (open_for_modify || fatal_error) {
1295 tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
1296 table->table_name, false);
1297 } else {
1298 /*
1299 Reset which partitions that should be processed
1300 if ALTER TABLE t ANALYZE/CHECK/.. PARTITION ..
1301 CACHE INDEX/LOAD INDEX for specified partitions
1302 */
1303 if (table->table->part_info &&
1304 alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) {
1305 set_all_part_state(table->table->part_info, PART_NORMAL);
1306 }
1307 }
1308 }
1309 /* Error path, a admin command failed. */
1310 if (thd->transaction_rollback_request) {
1311 /*
1312 Unlikely, but transaction rollback was requested by one of storage
1313 engines (e.g. due to deadlock). Perform it.
1314 */
1315 DBUG_PRINT("admin", ("rollback"));
1316
1317 if (trans_rollback_stmt(thd) || trans_rollback_implicit(thd)) goto err;
1318 } else {
1319 enum_implicit_substatement_guard_mode mode =
1320 enum_implicit_substatement_guard_mode ::
1321 DISABLE_GTID_AND_SPCO_IF_SPCO_ACTIVE;
1322
1323 if (strcmp(operator_name, "optimize") == 0 ||
1324 strcmp(operator_name, "analyze") == 0 ||
1325 strcmp(operator_name, "repair") == 0) {
1326 mode = enum_implicit_substatement_guard_mode ::
1327 ENABLE_GTID_AND_SPCO_IF_SPCO_ACTIVE;
1328 }
1329
1330 /*
1331 It allows saving GTID and invoking commit order i.e. set
1332 thd->is_operating_substatement_implicitly = false, when
1333 slave-preserve-commit-order is enabled and any of OPTIMIZE TABLE,
1334 ANALYZE TABLE and REPAIR TABLE command is getting executed,
1335 otherwise saving GTID and invoking commit order is disabled.
1336 */
1337 Implicit_substatement_state_guard guard(thd, mode);
1338
1339 if (trans_commit_stmt(thd, ignore_grl_on_analyze) ||
1340 trans_commit_implicit(thd, ignore_grl_on_analyze))
1341 goto err;
1342 DBUG_PRINT("admin", ("commit"));
1343 }
1344 close_thread_tables(thd);
1345 thd->mdl_context.release_transactional_locks();
1346
1347 if (protocol->end_row()) goto err;
1348 }
1349
1350 my_eof(thd);
1351
1352 if (gtid_rollback_must_be_skipped) thd->skip_gtid_rollback = false;
1353
1354 return false;
1355
1356 err:
1357 DBUG_PRINT("admin", ("err:"));
1358 if (gtid_rollback_must_be_skipped) thd->skip_gtid_rollback = false;
1359
1360 trans_rollback_stmt(thd);
1361 trans_rollback(thd);
1362
1363 if (thd->sp_runtime_ctx) thd->sp_runtime_ctx->end_partial_result_set = true;
1364
1365 /* Make sure this table instance is not reused after the operation. */
1366 if (table->table) table->table->m_needs_reopen = true;
1367 close_thread_tables(thd); // Shouldn't be needed
1368 thd->mdl_context.release_transactional_locks();
1369 return true;
1370 }
1371
1372 /*
1373 Assigned specified indexes for a table into key cache
1374
1375 SYNOPSIS
1376 assign_to_keycache()
1377 thd Thread object
1378 tables Table list (one table only)
1379
1380 RETURN VALUES
1381 false ok
1382 true error
1383 */
1384
assign_to_keycache(THD * thd,TABLE_LIST * tables)1385 bool Sql_cmd_cache_index::assign_to_keycache(THD *thd, TABLE_LIST *tables) {
1386 HA_CHECK_OPT check_opt;
1387 KEY_CACHE *key_cache;
1388 DBUG_TRACE;
1389
1390 mysql_mutex_lock(&LOCK_global_system_variables);
1391 if (!(key_cache = get_key_cache(&m_key_cache_name))) {
1392 mysql_mutex_unlock(&LOCK_global_system_variables);
1393 my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), m_key_cache_name.str);
1394 return true;
1395 }
1396 mysql_mutex_unlock(&LOCK_global_system_variables);
1397 if (!key_cache->key_cache_inited) {
1398 my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), m_key_cache_name.str);
1399 return true;
1400 }
1401 check_opt.key_cache = key_cache;
1402 // ret is needed since DBUG_RETURN isn't friendly to function call parameters:
1403 const bool ret = mysql_admin_table(
1404 thd, tables, &check_opt, "assign_to_keycache", TL_READ_NO_INSERT, false,
1405 false, 0, nullptr, &handler::assign_to_keycache, 0, m_alter_info, false);
1406 return ret;
1407 }
1408
1409 /*
1410 Preload specified indexes for a table into key cache
1411
1412 SYNOPSIS
1413 preload_keys()
1414 thd Thread object
1415 tables Table list (one table only)
1416
1417 RETURN VALUES
1418 false ok
1419 true error
1420 */
1421
preload_keys(THD * thd,TABLE_LIST * tables)1422 bool Sql_cmd_load_index::preload_keys(THD *thd, TABLE_LIST *tables) {
1423 DBUG_TRACE;
1424 /*
1425 We cannot allow concurrent inserts. The storage engine reads
1426 directly from the index file, bypassing the cache. It could read
1427 outdated information if parallel inserts into cache blocks happen.
1428 */
1429 // ret is needed since DBUG_RETURN isn't friendly to function call parameters:
1430 const bool ret = mysql_admin_table(
1431 thd, tables, nullptr, "preload_keys", TL_READ_NO_INSERT, false, false, 0,
1432 nullptr, &handler::preload_keys, 0, m_alter_info, false);
1433 return ret;
1434 }
1435
set_histogram_fields(List<String> * fields)1436 bool Sql_cmd_analyze_table::set_histogram_fields(List<String> *fields) {
1437 DBUG_ASSERT(m_histogram_fields.empty());
1438
1439 List_iterator<String> it(*fields);
1440 String *field;
1441 while ((field = it++)) {
1442 if (!m_histogram_fields.emplace(field).second) {
1443 my_error(ER_DUP_FIELDNAME, MYF(0), field->ptr());
1444 return true;
1445 }
1446 }
1447
1448 return false;
1449 }
1450
handle_histogram_command(THD * thd,TABLE_LIST * table)1451 bool Sql_cmd_analyze_table::handle_histogram_command(THD *thd,
1452 TABLE_LIST *table) {
1453 // This should not be empty here.
1454 DBUG_ASSERT(!get_histogram_fields().empty());
1455
1456 histograms::results_map results;
1457 bool res = false;
1458 if (table->next_local != nullptr) {
1459 /*
1460 Only one table can be specified for
1461 ANALYZE TABLE ... UPDATE/DROP HISTOGRAM
1462 */
1463 results.emplace("", histograms::Message::MULTIPLE_TABLES_SPECIFIED);
1464 res = true;
1465 } else {
1466 if (read_only || thd->tx_read_only) {
1467 // Do not try to update histograms when in read_only mode.
1468 results.emplace("", histograms::Message::SERVER_READ_ONLY);
1469 res = false;
1470 } else {
1471 Disable_autocommit_guard autocommit_guard(thd);
1472
1473 /* Prevent intermediate commits to invoke commit order */
1474 Implicit_substatement_state_guard substatement_guard(
1475 thd, enum_implicit_substatement_guard_mode ::
1476 DISABLE_GTID_AND_SPCO_IF_SPCO_ACTIVE);
1477
1478 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
1479 switch (get_histogram_command()) {
1480 case Histogram_command::UPDATE_HISTOGRAM:
1481 res = acquire_shared_backup_lock(thd,
1482 thd->variables.lock_wait_timeout) ||
1483 update_histogram(thd, table, results);
1484 break;
1485 case Histogram_command::DROP_HISTOGRAM:
1486 res = acquire_shared_backup_lock(thd,
1487 thd->variables.lock_wait_timeout) ||
1488 drop_histogram(thd, table, results);
1489
1490 if (res) {
1491 /*
1492 Do a rollback. We can end up here if query was interrupted
1493 during drop_histogram.
1494 */
1495 trans_rollback_stmt(thd);
1496 trans_rollback(thd);
1497 } else {
1498 res = trans_commit_stmt(thd) || trans_commit(thd);
1499 }
1500 break;
1501 case Histogram_command::NONE:
1502 DBUG_ASSERT(false); /* purecov: deadcode */
1503 break;
1504 }
1505
1506 if (!res) {
1507 /*
1508 If a histogram was added, updated or removed, we will request the old
1509 TABLE_SHARE to go away from the table definition cache. This is
1510 beacuse histogram data is cached in the TABLE_SHARE, so we want new
1511 transactions to fetch the updated data into the TABLE_SHARE before
1512 using it again.
1513 */
1514 tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
1515 table->table_name, false);
1516 }
1517 }
1518 }
1519
1520 thd->clear_error();
1521 send_histogram_results(thd, results, table);
1522 thd->get_stmt_da()->reset_condition_info(thd);
1523 my_eof(thd);
1524 return res;
1525 }
1526
execute(THD * thd)1527 bool Sql_cmd_analyze_table::execute(THD *thd) {
1528 TABLE_LIST *first_table = thd->lex->select_lex->get_table_list();
1529 bool res = true;
1530 thr_lock_type lock_type = TL_READ_NO_INSERT;
1531 DBUG_TRACE;
1532
1533 if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, false,
1534 UINT_MAX, false))
1535 goto error;
1536
1537 DBUG_EXECUTE_IF("simulate_analyze_table_lock_wait_timeout_error", {
1538 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
1539 return true;
1540 });
1541
1542 thd->enable_slow_log = opt_log_slow_admin_statements;
1543
1544 if (get_histogram_command() != Histogram_command::NONE) {
1545 res = handle_histogram_command(thd, first_table);
1546 } else {
1547 res = mysql_admin_table(thd, first_table, &thd->lex->check_opt, "analyze",
1548 lock_type, true, false, 0, nullptr,
1549 &handler::ha_analyze, 0, m_alter_info, true);
1550 }
1551
1552 /* ! we write after unlocking the table */
1553 if (!res && !thd->lex->no_write_to_binlog) {
1554 /*
1555 Presumably, ANALYZE and binlog writing doesn't require synchronization
1556 */
1557 res = write_bin_log(thd, true, thd->query().str, thd->query().length);
1558 }
1559 thd->lex->select_lex->table_list.first = first_table;
1560 thd->lex->query_tables = first_table;
1561
1562 error:
1563 return res;
1564 }
1565
execute(THD * thd)1566 bool Sql_cmd_check_table::execute(THD *thd) {
1567 TABLE_LIST *first_table = thd->lex->select_lex->get_table_list();
1568 thr_lock_type lock_type = TL_READ_NO_INSERT;
1569 bool res = true;
1570 DBUG_TRACE;
1571
1572 if (check_table_access(thd, SELECT_ACL, first_table, true, UINT_MAX, false))
1573 goto error; /* purecov: inspected */
1574 thd->enable_slow_log = opt_log_slow_admin_statements;
1575
1576 res = mysql_admin_table(thd, first_table, &thd->lex->check_opt, "check",
1577 lock_type, false, false, HA_OPEN_FOR_REPAIR, nullptr,
1578 &handler::ha_check, 1, m_alter_info, true);
1579
1580 thd->lex->select_lex->table_list.first = first_table;
1581 thd->lex->query_tables = first_table;
1582
1583 error:
1584 return res;
1585 }
1586
execute(THD * thd)1587 bool Sql_cmd_optimize_table::execute(THD *thd) {
1588 TABLE_LIST *first_table = thd->lex->select_lex->get_table_list();
1589 bool res = true;
1590 DBUG_TRACE;
1591
1592 if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, false,
1593 UINT_MAX, false))
1594 goto error; /* purecov: inspected */
1595 thd->enable_slow_log = opt_log_slow_admin_statements;
1596 res = (specialflag & SPECIAL_NO_NEW_FUNC)
1597 ? mysql_recreate_table(thd, first_table, true)
1598 : mysql_admin_table(thd, first_table, &thd->lex->check_opt,
1599 "optimize", TL_WRITE, true, false, 0, nullptr,
1600 &handler::ha_optimize, 0, m_alter_info, true);
1601 /* ! we write after unlocking the table */
1602 if (!res && !thd->lex->no_write_to_binlog) {
1603 /*
1604 Presumably, OPTIMIZE and binlog writing doesn't require synchronization
1605 */
1606 res = write_bin_log(thd, true, thd->query().str, thd->query().length);
1607 }
1608 thd->lex->select_lex->table_list.first = first_table;
1609 thd->lex->query_tables = first_table;
1610
1611 error:
1612 return res;
1613 }
1614
execute(THD * thd)1615 bool Sql_cmd_repair_table::execute(THD *thd) {
1616 TABLE_LIST *first_table = thd->lex->select_lex->get_table_list();
1617 bool res = true;
1618 DBUG_TRACE;
1619
1620 if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, false,
1621 UINT_MAX, false))
1622 goto error; /* purecov: inspected */
1623 thd->enable_slow_log = opt_log_slow_admin_statements;
1624 res = mysql_admin_table(
1625 thd, first_table, &thd->lex->check_opt, "repair", TL_WRITE, true,
1626 thd->lex->check_opt.sql_flags & TT_USEFRM, HA_OPEN_FOR_REPAIR,
1627 &prepare_for_repair, &handler::ha_repair, 0, m_alter_info, true);
1628
1629 /* ! we write after unlocking the table */
1630 if (!res && !thd->lex->no_write_to_binlog) {
1631 /*
1632 Presumably, REPAIR and binlog writing doesn't require synchronization
1633 */
1634 res = write_bin_log(thd, true, thd->query().str, thd->query().length);
1635 }
1636 thd->lex->select_lex->table_list.first = first_table;
1637 thd->lex->query_tables = first_table;
1638
1639 error:
1640 return res;
1641 }
1642
execute(THD * thd)1643 bool Sql_cmd_shutdown::execute(THD *thd) {
1644 DBUG_TRACE;
1645 bool res = true;
1646 res = !shutdown(thd, SHUTDOWN_DEFAULT);
1647
1648 return res;
1649 }
1650
1651 class Alter_instance_reload_tls : public Alter_instance {
1652 public:
Alter_instance_reload_tls(THD * thd,const LEX_CSTRING & channel_name,bool force=false)1653 explicit Alter_instance_reload_tls(THD *thd, const LEX_CSTRING &channel_name,
1654 bool force = false)
1655 : Alter_instance(thd), channel_name_(channel_name), force_(force) {}
1656
execute()1657 bool execute() {
1658 if (match_channel_name() == false) {
1659 my_error(ER_SYNTAX_ERROR, MYF(0));
1660 return true;
1661 }
1662
1663 Security_context *sctx = m_thd->security_context();
1664 if (!sctx->has_global_grant(STRING_WITH_LEN("CONNECTION_ADMIN")).first) {
1665 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "CONNECTION_ADMIN");
1666 return true;
1667 }
1668
1669 bool res = false;
1670 enum enum_ssl_init_error error = SSL_INITERR_NOERROR;
1671 switch (context_type_) {
1672 case Ssl_acceptor_context_type::context_server_main:
1673 TLS_channel::singleton_flush(mysql_main, mysql_main_channel,
1674 &server_main_callback, &error, force_);
1675 break;
1676 case Ssl_acceptor_context_type::context_server_admin:
1677 TLS_channel::singleton_flush(mysql_admin, mysql_admin_channel,
1678 &server_admin_callback, &error, force_);
1679 break;
1680 case Ssl_acceptor_context_type::context_last:
1681 // Fall through
1682 default:
1683 DBUG_ASSERT(false);
1684 return false;
1685 }
1686 if (error != SSL_INITERR_NOERROR) {
1687 const char *error_text = sslGetErrString(error);
1688 if (force_) {
1689 push_warning_printf(m_thd, Sql_condition::SL_WARNING,
1690 ER_DA_SSL_LIBRARY_ERROR,
1691 ER_THD(m_thd, ER_DA_SSL_LIBRARY_ERROR), error_text);
1692 LogErr(WARNING_LEVEL, ER_SSL_LIBRARY_ERROR, sslGetErrString(error));
1693 } else {
1694 my_error(ER_DA_SSL_LIBRARY_ERROR, MYF(0), error_text);
1695 res = true;
1696 }
1697 }
1698
1699 if (!res) my_ok(m_thd);
1700 return res;
1701 }
~Alter_instance_reload_tls()1702 ~Alter_instance_reload_tls() {}
1703
1704 protected:
match_channel_name()1705 bool match_channel_name() {
1706 String specified_channel(channel_name_.str, channel_name_.length,
1707 system_charset_info);
1708
1709 /* Compare now */
1710 if (!my_strcasecmp(system_charset_info, mysql_main_channel.c_str(),
1711 specified_channel.ptr())) {
1712 context_type_ = Ssl_acceptor_context_type::context_server_main;
1713 return true;
1714 }
1715 if (!my_strcasecmp(system_charset_info, mysql_admin_channel.c_str(),
1716 specified_channel.ptr())) {
1717 context_type_ = Ssl_acceptor_context_type::context_server_admin;
1718 return true;
1719 }
1720
1721 return false;
1722 }
1723
1724 protected:
1725 LEX_CSTRING channel_name_;
1726 bool force_;
1727 Ssl_acceptor_context_type context_type_;
1728 };
1729
execute(THD * thd)1730 bool Sql_cmd_alter_instance::execute(THD *thd) {
1731 bool res = true;
1732 DBUG_TRACE;
1733 switch (alter_instance_action) {
1734 case ROTATE_INNODB_MASTER_KEY:
1735 alter_instance = new Rotate_innodb_master_key(thd);
1736 break;
1737 case ALTER_INSTANCE_RELOAD_TLS:
1738 alter_instance = new Alter_instance_reload_tls(thd, channel_name_, true);
1739 break;
1740 case ALTER_INSTANCE_RELOAD_TLS_ROLLBACK_ON_ERROR:
1741 alter_instance = new Alter_instance_reload_tls(thd, channel_name_);
1742 break;
1743 case ROTATE_BINLOG_MASTER_KEY:
1744 alter_instance = new Rotate_binlog_master_key(thd);
1745 break;
1746 case ALTER_INSTANCE_ENABLE_INNODB_REDO:
1747 alter_instance = new Innodb_redo_log(thd, true);
1748 break;
1749 case ALTER_INSTANCE_DISABLE_INNODB_REDO:
1750 alter_instance = new Innodb_redo_log(thd, false);
1751 break;
1752 default:
1753 DBUG_ASSERT(false);
1754 my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER INSTANCE");
1755 return true;
1756 }
1757
1758 /*
1759 If we reach here, the only case when alter_instance
1760 is NULL is if we got out of memory error.
1761 In case of unsupported option, we should have returned
1762 from default case in switch() statement above.
1763 */
1764 if (!alter_instance) {
1765 my_error(ER_OUT_OF_RESOURCES, MYF(0));
1766 } else {
1767 res = alter_instance->execute();
1768 delete alter_instance;
1769 alter_instance = nullptr;
1770 }
1771
1772 return res;
1773 }
1774
Sql_cmd_clone(LEX_USER * user_info,ulong port,LEX_CSTRING data_dir)1775 Sql_cmd_clone::Sql_cmd_clone(LEX_USER *user_info, ulong port,
1776 LEX_CSTRING data_dir)
1777 : m_port(port), m_data_dir(data_dir), m_clone(), m_is_local(false) {
1778 m_host = user_info->host;
1779 m_user = user_info->user;
1780 m_passwd = user_info->auth;
1781 }
1782
execute(THD * thd)1783 bool Sql_cmd_clone::execute(THD *thd) {
1784 DBUG_TRACE;
1785
1786 bool is_replace = (m_data_dir.str == nullptr);
1787
1788 if (is_local()) {
1789 DBUG_PRINT("admin", ("CLONE type = local, DIR = %s", m_data_dir.str));
1790
1791 } else {
1792 DBUG_PRINT("admin", ("CLONE type = remote, DIR = %s",
1793 is_replace ? "" : m_data_dir.str));
1794 }
1795
1796 auto sctx = thd->security_context();
1797
1798 /* For replacing current data directory, needs clone_admin privilege. */
1799 if (is_replace) {
1800 if (!(sctx->has_global_grant(STRING_WITH_LEN("CLONE_ADMIN")).first)) {
1801 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "CLONE_ADMIN");
1802 return true;
1803 }
1804 } else if (!(sctx->has_global_grant(STRING_WITH_LEN("BACKUP_ADMIN")).first)) {
1805 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "BACKUP_ADMIN");
1806 return true;
1807 }
1808
1809 /* A user session cannot run clone that replaces data on a group member. */
1810 if (is_replace && is_group_replication_running() &&
1811 strcmp(thd->security_context()->priv_user().str, "mysql.session")) {
1812 my_error(ER_CLONE_DISALLOWED, MYF(0), "Group Replication is running");
1813 return true;
1814 }
1815
1816 DBUG_ASSERT(m_clone == nullptr);
1817 m_clone = clone_plugin_lock(thd, &m_plugin);
1818
1819 if (m_clone == nullptr) {
1820 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), "clone");
1821 return true;
1822 }
1823
1824 if (is_local()) {
1825 DBUG_ASSERT(!is_replace);
1826 auto err = m_clone->clone_local(thd, m_data_dir.str);
1827 clone_plugin_unlock(thd, m_plugin);
1828
1829 if (err != 0) {
1830 return true;
1831 }
1832
1833 my_ok(thd);
1834 return false;
1835 }
1836
1837 DBUG_ASSERT(!is_local());
1838
1839 enum mysql_ssl_mode ssl_mode = SSL_MODE_DISABLED;
1840
1841 if (thd->lex->ssl_type == SSL_TYPE_NONE) {
1842 ssl_mode = SSL_MODE_DISABLED;
1843 } else if (thd->lex->ssl_type == SSL_TYPE_SPECIFIED) {
1844 ssl_mode = SSL_MODE_REQUIRED;
1845 } else {
1846 DBUG_ASSERT(thd->lex->ssl_type == SSL_TYPE_NOT_SPECIFIED);
1847 ssl_mode = SSL_MODE_PREFERRED;
1848 }
1849
1850 auto err = m_clone->clone_remote_client(
1851 thd, m_host.str, static_cast<uint>(m_port), m_user.str, m_passwd.str,
1852 m_data_dir.str, ssl_mode);
1853 clone_plugin_unlock(thd, m_plugin);
1854 m_clone = nullptr;
1855
1856 /* Set active VIO as clone plugin might have reset it */
1857 if (thd->is_classic_protocol()) {
1858 NET *net = thd->get_protocol_classic()->get_net();
1859 thd->set_active_vio(net->vio);
1860 }
1861
1862 if (err != 0) {
1863 /* Log donor error number and message. */
1864 if (err == ER_CLONE_DONOR) {
1865 const char *donor_mesg = nullptr;
1866 int donor_error = 0;
1867 bool success =
1868 Clone_handler::get_donor_error(nullptr, donor_error, donor_mesg);
1869 if (success && donor_error != 0 && donor_mesg != nullptr) {
1870 char info_mesg[128];
1871 snprintf(info_mesg, 128, "Clone Donor error : %d : %s", donor_error,
1872 donor_mesg);
1873 LogErr(INFORMATION_LEVEL, ER_CLONE_CLIENT_TRACE, info_mesg);
1874 }
1875 }
1876 return true;
1877 }
1878
1879 /* Check for KILL after setting active VIO */
1880 if (!is_replace && thd->killed != THD::NOT_KILLED) {
1881 my_error(ER_QUERY_INTERRUPTED, MYF(0));
1882 return true;
1883 }
1884
1885 /* Restart server after successfully cloning to current data directory. */
1886 if (is_replace && signal_restart_server()) {
1887 /* Shutdown server if restart failed. */
1888 LogErr(ERROR_LEVEL, ER_CLONE_SHUTDOWN_TRACE);
1889 Diagnostics_area shutdown_da(false);
1890 thd->push_diagnostics_area(&shutdown_da);
1891 /* CLONE_ADMIN privilege allows us to shutdown/restart at end. */
1892 kill_mysql();
1893 thd->pop_diagnostics_area();
1894 return true;
1895 }
1896
1897 my_ok(thd);
1898 return false;
1899 }
1900
load(THD * thd)1901 bool Sql_cmd_clone::load(THD *thd) {
1902 DBUG_TRACE;
1903 DBUG_ASSERT(m_clone == nullptr);
1904 DBUG_ASSERT(!is_local());
1905
1906 auto sctx = thd->security_context();
1907
1908 if (!(sctx->has_global_grant(STRING_WITH_LEN("BACKUP_ADMIN")).first)) {
1909 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "BACKUP_ADMIN");
1910 return true;
1911 }
1912
1913 m_clone = clone_plugin_lock(thd, &m_plugin);
1914
1915 if (m_clone == nullptr) {
1916 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), "clone");
1917 return true;
1918 }
1919
1920 my_ok(thd);
1921 return false;
1922 }
1923
execute_server(THD * thd)1924 bool Sql_cmd_clone::execute_server(THD *thd) {
1925 DBUG_TRACE;
1926 DBUG_ASSERT(!is_local());
1927
1928 bool ret = false;
1929 auto net = thd->get_protocol_classic()->get_net();
1930 auto sock = net->vio->mysql_socket;
1931
1932 Diagnostics_area clone_da(false);
1933
1934 thd->push_diagnostics_area(&clone_da);
1935
1936 auto err = m_clone->clone_remote_server(thd, sock);
1937
1938 if (err == 0) {
1939 my_ok(thd);
1940 }
1941
1942 thd->pop_diagnostics_area();
1943
1944 if (err != 0) {
1945 auto da = thd->get_stmt_da();
1946
1947 da->set_overwrite_status(true);
1948
1949 da->set_error_status(clone_da.mysql_errno(), clone_da.message_text(),
1950 clone_da.returned_sqlstate());
1951 da->push_warning(thd, clone_da.mysql_errno(), clone_da.returned_sqlstate(),
1952 Sql_condition::SL_ERROR, clone_da.message_text());
1953 ret = true;
1954 }
1955
1956 clone_plugin_unlock(thd, m_plugin);
1957 m_clone = nullptr;
1958
1959 return ret;
1960 }
1961
rewrite(THD * thd,String & rlb)1962 bool Sql_cmd_clone::rewrite(THD *thd, String &rlb) {
1963 /* No password for local clone. */
1964 if (is_local()) {
1965 return false;
1966 }
1967
1968 rlb.append(STRING_WITH_LEN("CLONE INSTANCE FROM "));
1969
1970 /* Append user name. */
1971 String user(m_user.str, m_user.length, system_charset_info);
1972 append_query_string(thd, system_charset_info, &user, &rlb);
1973
1974 /* Append host name. */
1975 rlb.append(STRING_WITH_LEN("@"));
1976 String host(m_host.str, m_host.length, system_charset_info);
1977 append_query_string(thd, system_charset_info, &host, &rlb);
1978
1979 /* Append port number. */
1980 rlb.append(STRING_WITH_LEN(":"));
1981 String num_buffer(42);
1982 num_buffer.set((longlong)m_port, &my_charset_bin);
1983 rlb.append(num_buffer);
1984
1985 /* Append password clause. */
1986 rlb.append(STRING_WITH_LEN(" IDENTIFIED BY <secret>"));
1987
1988 /* Append data directory clause. */
1989 if (m_data_dir.str != nullptr) {
1990 rlb.append(STRING_WITH_LEN(" DATA DIRECTORY = "));
1991 String dir(m_data_dir.str, m_data_dir.length, system_charset_info);
1992 append_query_string(thd, system_charset_info, &dir, &rlb);
1993 }
1994
1995 /* Append SSL information. */
1996 if (thd->lex->ssl_type == SSL_TYPE_NONE) {
1997 rlb.append(STRING_WITH_LEN(" REQUIRE NO SSL"));
1998
1999 } else if (thd->lex->ssl_type == SSL_TYPE_SPECIFIED) {
2000 rlb.append(STRING_WITH_LEN(" REQUIRE SSL"));
2001 }
2002 return true;
2003 }
2004
execute(THD * thd)2005 bool Sql_cmd_create_role::execute(THD *thd) {
2006 DBUG_TRACE;
2007 // TODO: Execution-time processing of the CREATE ROLE statement
2008 if (check_global_access(thd, CREATE_ROLE_ACL | CREATE_USER_ACL)) return true;
2009 /* Conditionally writes to binlog */
2010 HA_CREATE_INFO create_info;
2011 /*
2012 Roles must be locked for authentication by default.
2013 The below is a hack to make mysql_create_user() behave
2014 correctly.
2015 */
2016 thd->lex->ssl_cipher = nullptr;
2017 thd->lex->x509_issuer = nullptr;
2018 thd->lex->x509_subject = nullptr;
2019 thd->lex->ssl_type = SSL_TYPE_NOT_SPECIFIED;
2020 thd->lex->alter_password.account_locked = true;
2021 thd->lex->alter_password.update_account_locked_column = true;
2022 thd->lex->alter_password.expire_after_days = 0;
2023 thd->lex->alter_password.update_password_expired_column = true;
2024 thd->lex->alter_password.use_default_password_lifetime = true;
2025 thd->lex->alter_password.update_password_expired_fields = true;
2026 thd->lex->alter_password.update_password_require_current =
2027 Lex_acl_attrib_udyn::UNCHANGED;
2028 thd->lex->alter_password.failed_login_attempts = 0;
2029 thd->lex->alter_password.password_lock_time = 0;
2030
2031 List_iterator<LEX_USER> it(*const_cast<List<LEX_USER> *>(roles));
2032 LEX_USER *role;
2033 while ((role = it++)) {
2034 role->uses_identified_by_clause = false;
2035 role->uses_identified_with_clause = false;
2036 role->uses_authentication_string_clause = false;
2037 role->alter_status.expire_after_days = 0;
2038 role->alter_status.account_locked = true;
2039 role->alter_status.update_account_locked_column = true;
2040 role->alter_status.update_password_expired_fields = true;
2041 role->alter_status.use_default_password_lifetime = true;
2042 role->alter_status.update_password_expired_column = true;
2043 role->auth.str = nullptr;
2044 role->auth.length = 0;
2045 role->has_password_generator = false;
2046 }
2047 if (!(mysql_create_user(thd, *const_cast<List<LEX_USER> *>(roles),
2048 if_not_exists, true))) {
2049 // Either my_ok() or my_eof() was called in mysql_create_user()
2050 return false;
2051 }
2052 // my_error() was called.
2053 return true;
2054 }
2055
execute(THD * thd)2056 bool Sql_cmd_drop_role::execute(THD *thd) {
2057 DBUG_TRACE;
2058 /*
2059 We want to do extra checks (if user login is disabled) when golding a
2060 using DROP_ROLE privilege.
2061 To do that we record if CREATE USER was granted.
2062 Then if one of DROP ROLE or CREATE USER was granted (the original
2063 requirement) and CREATE USER was not granted we know that it was DROP ROLE
2064 that caused the check to pass.
2065
2066 Thus we raise the flag (drop_role) in this case.
2067 */
2068 bool on_create_user_priv =
2069 thd->security_context()->check_access(CREATE_USER_ACL, "", true);
2070 if (check_global_access(thd, DROP_ROLE_ACL | CREATE_USER_ACL)) return true;
2071 if (mysql_drop_user(thd, const_cast<List<LEX_USER> &>(*roles), ignore_errors,
2072 !on_create_user_priv))
2073 return true;
2074 my_ok(thd);
2075 return false;
2076 }
2077
execute(THD * thd)2078 bool Sql_cmd_set_role::execute(THD *thd) {
2079 DBUG_TRACE;
2080 bool ret = false;
2081 switch (role_type) {
2082 case role_enum::ROLE_NONE:
2083 ret = mysql_set_active_role_none(thd);
2084 break;
2085 case role_enum::ROLE_DEFAULT:
2086 ret = mysql_set_role_default(thd);
2087 break;
2088 case role_enum::ROLE_ALL:
2089 ret = mysql_set_active_role_all(thd, except_roles);
2090 break;
2091 case role_enum::ROLE_NAME:
2092 ret = mysql_set_active_role(thd, role_list);
2093 break;
2094 }
2095
2096 /*
2097 1. In case of role_enum::ROLE_NONE -
2098 User might have SYSTEM_USER privilege granted explicitly using GRANT
2099 statement.
2100 2. For other cases -
2101 User may have got SYSTEM_USER privilege either through one of the roles
2102 OR, privilege may have been granted explicitly using GRANT statement.
2103 Therefore, update the THD accordingly.
2104
2105 Update the flag in THD if invoker has SYSTEM_USER privilege not if the
2106 definer user has that privilege.
2107 */
2108 if (!ret) set_system_user_flag(thd, true);
2109
2110 return ret;
2111 }
2112
execute(THD * thd)2113 bool Sql_cmd_grant_roles::execute(THD *thd) {
2114 DBUG_TRACE;
2115 if (!has_grant_role_privilege(thd, roles)) {
2116 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
2117 "WITH ADMIN, ROLE_ADMIN, SUPER");
2118 return true;
2119 }
2120 return mysql_grant_role(thd, users, roles, this->with_admin_option);
2121 }
2122
execute(THD * thd)2123 bool Sql_cmd_revoke_roles::execute(THD *thd) {
2124 DBUG_TRACE;
2125 if (!has_grant_role_privilege(thd, roles)) {
2126 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
2127 "WITH ADMIN, ROLE_ADMIN, SUPER");
2128 return true;
2129 }
2130 return mysql_revoke_role(thd, users, roles);
2131 }
2132
execute(THD * thd)2133 bool Sql_cmd_alter_user_default_role::execute(THD *thd) {
2134 DBUG_TRACE;
2135
2136 bool ret = mysql_alter_or_clear_default_roles(thd, role_type, users, roles);
2137 if (!ret) my_ok(thd);
2138
2139 return ret;
2140 }
2141
execute(THD * thd)2142 bool Sql_cmd_show_grants::execute(THD *thd) {
2143 DBUG_TRACE;
2144 bool show_mandatory_roles = (for_user == nullptr);
2145 bool have_using_clause =
2146 (using_users != nullptr && using_users->elements > 0);
2147
2148 if (for_user == nullptr || for_user->user.str == nullptr) {
2149 /* SHOW PRIVILEGE FOR CURRENT_USER */
2150 LEX_USER current_user;
2151 get_default_definer(thd, ¤t_user);
2152 if (!have_using_clause) {
2153 const List_of_auth_id_refs *active_list =
2154 thd->security_context()->get_active_roles();
2155 return mysql_show_grants(thd, ¤t_user, *active_list,
2156 show_mandatory_roles, have_using_clause);
2157 }
2158 } else if (strcmp(thd->security_context()->priv_user().str,
2159 for_user->user.str) != 0) {
2160 TABLE_LIST table("mysql", "user", nullptr, TL_READ);
2161 if (!is_granted_table_access(thd, SELECT_ACL, &table)) {
2162 char command[128];
2163 get_privilege_desc(command, sizeof(command), SELECT_ACL);
2164 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command,
2165 thd->security_context()->priv_user().str,
2166 thd->security_context()->host_or_ip().str, "user");
2167 return false;
2168 }
2169 }
2170 List_of_auth_id_refs authid_list;
2171 if (have_using_clause) {
2172 for (const LEX_USER &user : *using_users) {
2173 authid_list.emplace_back(user.user, user.host);
2174 }
2175 }
2176
2177 LEX_USER *tmp_user = const_cast<LEX_USER *>(for_user);
2178 tmp_user = get_current_user(thd, tmp_user);
2179 return mysql_show_grants(thd, tmp_user, authid_list, show_mandatory_roles,
2180 have_using_clause);
2181 }
2182
execute(THD * thd)2183 bool Sql_cmd_show::execute(THD *thd) {
2184 DBUG_TRACE;
2185
2186 thd->clear_current_query_costs();
2187 bool res = show_precheck(thd, thd->lex, true);
2188 if (!res) res = execute_show(thd, thd->lex->query_tables);
2189 thd->save_current_query_costs();
2190
2191 return res;
2192 }
2193
prepare(THD * thd)2194 bool Sql_cmd_show::prepare(THD *thd) {
2195 DBUG_TRACE;
2196
2197 if (Sql_cmd::prepare(thd)) return true;
2198
2199 bool rc = mysql_test_show(get_owner(), thd->lex->query_tables);
2200 return rc;
2201 }
2202