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, &current_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, &current_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