| 1 | /* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. |
| 2 | Copyright (c) 2016, MariaDB Corporation |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | #include "mariadb.h" |
| 18 | #include "sql_parse.h" // check_access |
| 19 | #include "sql_table.h" // mysql_alter_table, |
| 20 | // mysql_exchange_partition |
| 21 | #include "sql_alter.h" |
| 22 | #include "wsrep_mysqld.h" |
| 23 | |
| 24 | Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) |
| 25 | :drop_list(rhs.drop_list, mem_root), |
| 26 | alter_list(rhs.alter_list, mem_root), |
| 27 | key_list(rhs.key_list, mem_root), |
| 28 | create_list(rhs.create_list, mem_root), |
| 29 | check_constraint_list(rhs.check_constraint_list, mem_root), |
| 30 | flags(rhs.flags), partition_flags(rhs.partition_flags), |
| 31 | keys_onoff(rhs.keys_onoff), |
| 32 | partition_names(rhs.partition_names, mem_root), |
| 33 | num_parts(rhs.num_parts), |
| 34 | requested_algorithm(rhs.requested_algorithm), |
| 35 | requested_lock(rhs.requested_lock) |
| 36 | { |
| 37 | /* |
| 38 | Make deep copies of used objects. |
| 39 | This is not a fully deep copy - clone() implementations |
| 40 | of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec |
| 41 | do not copy string constants. At the same length the only |
| 42 | reason we make a copy currently is that ALTER/CREATE TABLE |
| 43 | code changes input Alter_info definitions, but string |
| 44 | constants never change. |
| 45 | */ |
| 46 | list_copy_and_replace_each_value(drop_list, mem_root); |
| 47 | list_copy_and_replace_each_value(alter_list, mem_root); |
| 48 | list_copy_and_replace_each_value(key_list, mem_root); |
| 49 | list_copy_and_replace_each_value(create_list, mem_root); |
| 50 | /* partition_names are not deeply copied currently */ |
| 51 | } |
| 52 | |
| 53 | |
| 54 | bool Alter_info::set_requested_algorithm(const LEX_CSTRING *str) |
| 55 | { |
| 56 | // To avoid adding new keywords to the grammar, we match strings here. |
| 57 | if (lex_string_eq(str, STRING_WITH_LEN("INPLACE" ))) |
| 58 | requested_algorithm= ALTER_TABLE_ALGORITHM_INPLACE; |
| 59 | else if (lex_string_eq(str, STRING_WITH_LEN("COPY" ))) |
| 60 | requested_algorithm= ALTER_TABLE_ALGORITHM_COPY; |
| 61 | else if (lex_string_eq(str, STRING_WITH_LEN("DEFAULT" ))) |
| 62 | requested_algorithm= ALTER_TABLE_ALGORITHM_DEFAULT; |
| 63 | else if (lex_string_eq(str, STRING_WITH_LEN("NOCOPY" ))) |
| 64 | requested_algorithm= ALTER_TABLE_ALGORITHM_NOCOPY; |
| 65 | else if (lex_string_eq(str, STRING_WITH_LEN("INSTANT" ))) |
| 66 | requested_algorithm= ALTER_TABLE_ALGORITHM_INSTANT; |
| 67 | else |
| 68 | return true; |
| 69 | return false; |
| 70 | } |
| 71 | |
| 72 | |
| 73 | bool Alter_info::set_requested_lock(const LEX_CSTRING *str) |
| 74 | { |
| 75 | // To avoid adding new keywords to the grammar, we match strings here. |
| 76 | if (lex_string_eq(str, STRING_WITH_LEN("NONE" ))) |
| 77 | requested_lock= ALTER_TABLE_LOCK_NONE; |
| 78 | else if (lex_string_eq(str, STRING_WITH_LEN("SHARED" ))) |
| 79 | requested_lock= ALTER_TABLE_LOCK_SHARED; |
| 80 | else if (lex_string_eq(str, STRING_WITH_LEN("EXCLUSIVE" ))) |
| 81 | requested_lock= ALTER_TABLE_LOCK_EXCLUSIVE; |
| 82 | else if (lex_string_eq(str, STRING_WITH_LEN("DEFAULT" ))) |
| 83 | requested_lock= ALTER_TABLE_LOCK_DEFAULT; |
| 84 | else |
| 85 | return true; |
| 86 | return false; |
| 87 | } |
| 88 | |
| 89 | const char* Alter_info::algorithm() const |
| 90 | { |
| 91 | switch (requested_algorithm) { |
| 92 | case ALTER_TABLE_ALGORITHM_INPLACE: |
| 93 | return "ALGORITHM=INPLACE" ; |
| 94 | case ALTER_TABLE_ALGORITHM_COPY: |
| 95 | return "ALGORITHM=COPY" ; |
| 96 | case ALTER_TABLE_ALGORITHM_DEFAULT: |
| 97 | return "ALGORITHM=DEFAULT" ; |
| 98 | case ALTER_TABLE_ALGORITHM_NOCOPY: |
| 99 | return "ALGORITHM=NOCOPY" ; |
| 100 | case ALTER_TABLE_ALGORITHM_INSTANT: |
| 101 | return "ALGORITHM=INSTANT" ; |
| 102 | } |
| 103 | |
| 104 | return NULL; /* purecov: begin deadcode */ |
| 105 | } |
| 106 | |
| 107 | const char* Alter_info::lock() const |
| 108 | { |
| 109 | switch (requested_lock) { |
| 110 | case ALTER_TABLE_LOCK_SHARED: |
| 111 | return "LOCK=SHARED" ; |
| 112 | case ALTER_TABLE_LOCK_NONE: |
| 113 | return "LOCK=NONE" ; |
| 114 | case ALTER_TABLE_LOCK_DEFAULT: |
| 115 | return "LOCK=DEFAULT" ; |
| 116 | case ALTER_TABLE_LOCK_EXCLUSIVE: |
| 117 | return "LOCK=EXCLUSIVE" ; |
| 118 | } |
| 119 | return NULL; /* purecov: begin deadcode */ |
| 120 | } |
| 121 | |
| 122 | |
| 123 | bool Alter_info::supports_algorithm(THD *thd, enum_alter_inplace_result result, |
| 124 | const Alter_inplace_info *ha_alter_info) |
| 125 | { |
| 126 | if (requested_algorithm == Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) |
| 127 | requested_algorithm = (Alter_info::enum_alter_table_algorithm) thd->variables.alter_algorithm; |
| 128 | |
| 129 | switch (result) { |
| 130 | case HA_ALTER_INPLACE_EXCLUSIVE_LOCK: |
| 131 | case HA_ALTER_INPLACE_SHARED_LOCK: |
| 132 | case HA_ALTER_INPLACE_NO_LOCK: |
| 133 | case HA_ALTER_INPLACE_INSTANT: |
| 134 | return false; |
| 135 | case HA_ALTER_INPLACE_COPY_NO_LOCK: |
| 136 | case HA_ALTER_INPLACE_COPY_LOCK: |
| 137 | if (requested_algorithm >= Alter_info::ALTER_TABLE_ALGORITHM_NOCOPY) |
| 138 | { |
| 139 | ha_alter_info->report_unsupported_error(algorithm(), |
| 140 | "ALGORITHM=INPLACE" ); |
| 141 | return true; |
| 142 | } |
| 143 | return false; |
| 144 | case HA_ALTER_INPLACE_NOCOPY_NO_LOCK: |
| 145 | case HA_ALTER_INPLACE_NOCOPY_LOCK: |
| 146 | if (requested_algorithm == Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) |
| 147 | { |
| 148 | ha_alter_info->report_unsupported_error("ALGORITHM=INSTANT" , |
| 149 | "ALGORITHM=NOCOPY" ); |
| 150 | return true; |
| 151 | } |
| 152 | return false; |
| 153 | case HA_ALTER_INPLACE_NOT_SUPPORTED: |
| 154 | if (requested_algorithm >= Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) |
| 155 | { |
| 156 | ha_alter_info->report_unsupported_error(algorithm(), |
| 157 | "ALGORITHM=COPY" ); |
| 158 | return true; |
| 159 | } |
| 160 | return false; |
| 161 | case HA_ALTER_ERROR: |
| 162 | return true; |
| 163 | } |
| 164 | /* purecov: begin deadcode */ |
| 165 | DBUG_ASSERT(0); |
| 166 | return false; |
| 167 | } |
| 168 | |
| 169 | |
| 170 | bool Alter_info::supports_lock(THD *thd, enum_alter_inplace_result result, |
| 171 | const Alter_inplace_info *ha_alter_info) |
| 172 | { |
| 173 | switch (result) { |
| 174 | case HA_ALTER_INPLACE_EXCLUSIVE_LOCK: |
| 175 | // If SHARED lock and no particular algorithm was requested, use COPY. |
| 176 | if (requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED && |
| 177 | requested_algorithm == Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT && |
| 178 | thd->variables.alter_algorithm == |
| 179 | Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) |
| 180 | return false; |
| 181 | |
| 182 | if (requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED || |
| 183 | requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE) |
| 184 | { |
| 185 | ha_alter_info->report_unsupported_error(lock(), "LOCK=EXCLUSIVE" ); |
| 186 | return true; |
| 187 | } |
| 188 | return false; |
| 189 | case HA_ALTER_INPLACE_NO_LOCK: |
| 190 | case HA_ALTER_INPLACE_INSTANT: |
| 191 | case HA_ALTER_INPLACE_COPY_NO_LOCK: |
| 192 | case HA_ALTER_INPLACE_NOCOPY_NO_LOCK: |
| 193 | return false; |
| 194 | case HA_ALTER_INPLACE_COPY_LOCK: |
| 195 | case HA_ALTER_INPLACE_NOCOPY_LOCK: |
| 196 | case HA_ALTER_INPLACE_NOT_SUPPORTED: |
| 197 | case HA_ALTER_INPLACE_SHARED_LOCK: |
| 198 | if (requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE) |
| 199 | { |
| 200 | ha_alter_info->report_unsupported_error("LOCK=NONE" , "LOCK=SHARED" ); |
| 201 | return true; |
| 202 | } |
| 203 | return false; |
| 204 | case HA_ALTER_ERROR: |
| 205 | return true; |
| 206 | } |
| 207 | /* purecov: begin deadcode */ |
| 208 | DBUG_ASSERT(0); |
| 209 | return false; |
| 210 | } |
| 211 | |
| 212 | Alter_table_ctx::Alter_table_ctx() |
| 213 | : datetime_field(NULL), error_if_not_empty(false), |
| 214 | tables_opened(0), |
| 215 | db(null_clex_str), table_name(null_clex_str), alias(null_clex_str), |
| 216 | new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str), |
| 217 | fk_error_if_delete_row(false), fk_error_id(NULL), |
| 218 | fk_error_table(NULL) |
| 219 | #ifdef DBUG_ASSERT_EXISTS |
| 220 | , tmp_table(false) |
| 221 | #endif |
| 222 | { |
| 223 | } |
| 224 | |
| 225 | /* |
| 226 | TODO: new_name_arg changes if lower case table names. |
| 227 | Should be copied or converted before call |
| 228 | */ |
| 229 | |
| 230 | Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, |
| 231 | uint tables_opened_arg, |
| 232 | const LEX_CSTRING *new_db_arg, |
| 233 | const LEX_CSTRING *new_name_arg) |
| 234 | : datetime_field(NULL), error_if_not_empty(false), |
| 235 | tables_opened(tables_opened_arg), |
| 236 | new_db(*new_db_arg), new_name(*new_name_arg), |
| 237 | fk_error_if_delete_row(false), fk_error_id(NULL), |
| 238 | fk_error_table(NULL) |
| 239 | #ifdef DBUG_ASSERT_EXISTS |
| 240 | , tmp_table(false) |
| 241 | #endif |
| 242 | { |
| 243 | /* |
| 244 | Assign members db, table_name, new_db and new_name |
| 245 | to simplify further comparisions: we want to see if it's a RENAME |
| 246 | later just by comparing the pointers, avoiding the need for strcmp. |
| 247 | */ |
| 248 | db= table_list->db; |
| 249 | table_name= table_list->table_name; |
| 250 | alias= (lower_case_table_names == 2) ? table_list->alias : table_name; |
| 251 | |
| 252 | if (!new_db.str || !my_strcasecmp(table_alias_charset, new_db.str, db.str)) |
| 253 | new_db= db; |
| 254 | |
| 255 | if (new_name.str) |
| 256 | { |
| 257 | DBUG_PRINT("info" , ("new_db.new_name: '%s'.'%s'" , new_db.str, new_name.str)); |
| 258 | |
| 259 | if (lower_case_table_names == 1) // Convert new_name/new_alias to lower |
| 260 | { |
| 261 | new_name.length= my_casedn_str(files_charset_info, (char*) new_name.str); |
| 262 | new_alias= new_name; |
| 263 | } |
| 264 | else if (lower_case_table_names == 2) // Convert new_name to lower case |
| 265 | { |
| 266 | new_alias.str= new_alias_buff; |
| 267 | new_alias.length= new_name.length; |
| 268 | strmov(new_alias_buff, new_name.str); |
| 269 | new_name.length= my_casedn_str(files_charset_info, (char*) new_name.str); |
| 270 | |
| 271 | } |
| 272 | else |
| 273 | new_alias= new_name; // LCTN=0 => case sensitive + case preserving |
| 274 | |
| 275 | if (!is_database_changed() && |
| 276 | !my_strcasecmp(table_alias_charset, new_name.str, table_name.str)) |
| 277 | { |
| 278 | /* |
| 279 | Source and destination table names are equal: |
| 280 | make is_table_renamed() more efficient. |
| 281 | */ |
| 282 | new_alias= table_name; |
| 283 | new_name= table_name; |
| 284 | } |
| 285 | } |
| 286 | else |
| 287 | { |
| 288 | new_alias= alias; |
| 289 | new_name= table_name; |
| 290 | } |
| 291 | |
| 292 | tmp_name.str= tmp_name_buff; |
| 293 | tmp_name.length= my_snprintf(tmp_name_buff, sizeof(tmp_name_buff), "%s-%lx_%llx" , |
| 294 | tmp_file_prefix, current_pid, thd->thread_id); |
| 295 | /* Safety fix for InnoDB */ |
| 296 | if (lower_case_table_names) |
| 297 | tmp_name.length= my_casedn_str(files_charset_info, tmp_name_buff); |
| 298 | |
| 299 | if (table_list->table->s->tmp_table == NO_TMP_TABLE) |
| 300 | { |
| 301 | build_table_filename(path, sizeof(path) - 1, db.str, table_name.str, "" , 0); |
| 302 | |
| 303 | build_table_filename(new_path, sizeof(new_path) - 1, new_db.str, new_name.str, "" , 0); |
| 304 | |
| 305 | build_table_filename(new_filename, sizeof(new_filename) - 1, |
| 306 | new_db.str, new_name.str, reg_ext, 0); |
| 307 | |
| 308 | build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db.str, tmp_name.str, "" , |
| 309 | FN_IS_TMP); |
| 310 | } |
| 311 | else |
| 312 | { |
| 313 | /* |
| 314 | We are not filling path, new_path and new_filename members if |
| 315 | we are altering temporary table as these members are not used in |
| 316 | this case. This fact is enforced with assert. |
| 317 | */ |
| 318 | build_tmptable_filename(thd, tmp_path, sizeof(tmp_path)); |
| 319 | #ifdef DBUG_ASSERT_EXISTS |
| 320 | tmp_table= true; |
| 321 | #endif |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | |
| 326 | bool Sql_cmd_alter_table::execute(THD *thd) |
| 327 | { |
| 328 | LEX *lex= thd->lex; |
| 329 | /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ |
| 330 | SELECT_LEX *select_lex= &lex->select_lex; |
| 331 | /* first table of first SELECT_LEX */ |
| 332 | TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; |
| 333 | /* |
| 334 | Code in mysql_alter_table() may modify its HA_CREATE_INFO argument, |
| 335 | so we have to use a copy of this structure to make execution |
| 336 | prepared statement- safe. A shallow copy is enough as no memory |
| 337 | referenced from this structure will be modified. |
| 338 | @todo move these into constructor... |
| 339 | */ |
| 340 | HA_CREATE_INFO create_info(lex->create_info); |
| 341 | Alter_info alter_info(lex->alter_info, thd->mem_root); |
| 342 | ulong priv=0; |
| 343 | ulong priv_needed= ALTER_ACL; |
| 344 | bool result; |
| 345 | |
| 346 | DBUG_ENTER("Sql_cmd_alter_table::execute" ); |
| 347 | |
| 348 | if (unlikely(thd->is_fatal_error)) |
| 349 | { |
| 350 | /* out of memory creating a copy of alter_info */ |
| 351 | DBUG_RETURN(TRUE); |
| 352 | } |
| 353 | /* |
| 354 | We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well |
| 355 | as for RENAME TO, as being done by SQLCOM_RENAME_TABLE |
| 356 | */ |
| 357 | if ((alter_info.partition_flags & ALTER_PARTITION_DROP) || |
| 358 | (alter_info.flags & ALTER_RENAME)) |
| 359 | priv_needed|= DROP_ACL; |
| 360 | |
| 361 | /* Must be set in the parser */ |
| 362 | DBUG_ASSERT(select_lex->db.str); |
| 363 | DBUG_ASSERT(!(alter_info.partition_flags & ALTER_PARTITION_EXCHANGE)); |
| 364 | DBUG_ASSERT(!(alter_info.partition_flags & ALTER_PARTITION_ADMIN)); |
| 365 | if (check_access(thd, priv_needed, first_table->db.str, |
| 366 | &first_table->grant.privilege, |
| 367 | &first_table->grant.m_internal, |
| 368 | 0, 0) || |
| 369 | check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db.str, |
| 370 | &priv, |
| 371 | NULL, /* Don't use first_tab->grant with sel_lex->db */ |
| 372 | 0, 0)) |
| 373 | DBUG_RETURN(TRUE); /* purecov: inspected */ |
| 374 | |
| 375 | /* If it is a merge table, check privileges for merge children. */ |
| 376 | if (create_info.merge_list.first) |
| 377 | { |
| 378 | /* |
| 379 | The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the |
| 380 | underlying base tables, even if there are temporary tables with the same |
| 381 | names. |
| 382 | |
| 383 | From user's point of view, it might look as if the user must have these |
| 384 | privileges on temporary tables to create a merge table over them. This is |
| 385 | one of two cases when a set of privileges is required for operations on |
| 386 | temporary tables (see also CREATE TABLE). |
| 387 | |
| 388 | The reason for this behavior stems from the following facts: |
| 389 | |
| 390 | - For merge tables, the underlying table privileges are checked only |
| 391 | at CREATE TABLE / ALTER TABLE time. |
| 392 | |
| 393 | In other words, once a merge table is created, the privileges of |
| 394 | the underlying tables can be revoked, but the user will still have |
| 395 | access to the merge table (provided that the user has privileges on |
| 396 | the merge table itself). |
| 397 | |
| 398 | - Temporary tables shadow base tables. |
| 399 | |
| 400 | I.e. there might be temporary and base tables with the same name, and |
| 401 | the temporary table takes the precedence in all operations. |
| 402 | |
| 403 | - For temporary MERGE tables we do not track if their child tables are |
| 404 | base or temporary. As result we can't guarantee that privilege check |
| 405 | which was done in presence of temporary child will stay relevant later |
| 406 | as this temporary table might be removed. |
| 407 | |
| 408 | If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for |
| 409 | the underlying *base* tables, it would create a security breach as in |
| 410 | Bug#12771903. |
| 411 | */ |
| 412 | |
| 413 | if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, |
| 414 | create_info.merge_list.first, FALSE, UINT_MAX, FALSE)) |
| 415 | DBUG_RETURN(TRUE); |
| 416 | } |
| 417 | |
| 418 | if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) |
| 419 | DBUG_RETURN(TRUE); /* purecov: inspected */ |
| 420 | |
| 421 | if (lex->name.str && !test_all_bits(priv, INSERT_ACL | CREATE_ACL)) |
| 422 | { |
| 423 | // Rename of table |
| 424 | TABLE_LIST tmp_table; |
| 425 | memset(&tmp_table, 0, sizeof(tmp_table)); |
| 426 | tmp_table.table_name= lex->name; |
| 427 | tmp_table.db= select_lex->db; |
| 428 | tmp_table.grant.privilege= priv; |
| 429 | if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE, |
| 430 | UINT_MAX, FALSE)) |
| 431 | DBUG_RETURN(TRUE); /* purecov: inspected */ |
| 432 | } |
| 433 | |
| 434 | /* Don't yet allow changing of symlinks with ALTER TABLE */ |
| 435 | if (create_info.data_file_name) |
| 436 | push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, |
| 437 | WARN_OPTION_IGNORED, ER_THD(thd, WARN_OPTION_IGNORED), |
| 438 | "DATA DIRECTORY" ); |
| 439 | if (create_info.index_file_name) |
| 440 | push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, |
| 441 | WARN_OPTION_IGNORED, ER_THD(thd, WARN_OPTION_IGNORED), |
| 442 | "INDEX DIRECTORY" ); |
| 443 | create_info.data_file_name= create_info.index_file_name= NULL; |
| 444 | |
| 445 | thd->prepare_logs_for_admin_command(); |
| 446 | |
| 447 | #ifdef WITH_WSREP |
| 448 | if ((!thd->is_current_stmt_binlog_format_row() || |
| 449 | !thd->find_temporary_table(first_table))) |
| 450 | { |
| 451 | WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db.str : NULL), |
| 452 | ((lex->name.str) ? lex->name.str : NULL), |
| 453 | first_table); |
| 454 | } |
| 455 | #endif /* WITH_WSREP */ |
| 456 | |
| 457 | result= mysql_alter_table(thd, &select_lex->db, &lex->name, |
| 458 | &create_info, |
| 459 | first_table, |
| 460 | &alter_info, |
| 461 | select_lex->order_list.elements, |
| 462 | select_lex->order_list.first, |
| 463 | lex->ignore); |
| 464 | |
| 465 | DBUG_RETURN(result); |
| 466 | |
| 467 | #ifdef WITH_WSREP |
| 468 | error: |
| 469 | WSREP_WARN("ALTER TABLE isolation failure" ); |
| 470 | DBUG_RETURN(TRUE); |
| 471 | #endif /* WITH_WSREP */ |
| 472 | } |
| 473 | |
| 474 | bool Sql_cmd_discard_import_tablespace::execute(THD *thd) |
| 475 | { |
| 476 | /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ |
| 477 | SELECT_LEX *select_lex= &thd->lex->select_lex; |
| 478 | /* first table of first SELECT_LEX */ |
| 479 | TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; |
| 480 | |
| 481 | if (check_access(thd, ALTER_ACL, table_list->db.str, |
| 482 | &table_list->grant.privilege, |
| 483 | &table_list->grant.m_internal, |
| 484 | 0, 0)) |
| 485 | return true; |
| 486 | |
| 487 | if (check_grant(thd, ALTER_ACL, table_list, false, UINT_MAX, false)) |
| 488 | return true; |
| 489 | |
| 490 | thd->prepare_logs_for_admin_command(); |
| 491 | |
| 492 | /* |
| 493 | Check if we attempt to alter mysql.slow_log or |
| 494 | mysql.general_log table and return an error if |
| 495 | it is the case. |
| 496 | TODO: this design is obsolete and will be removed. |
| 497 | */ |
| 498 | if (check_if_log_table(table_list, TRUE, "ALTER" )) |
| 499 | return true; |
| 500 | |
| 501 | return |
| 502 | mysql_discard_or_import_tablespace(thd, table_list, |
| 503 | m_tablespace_op == DISCARD_TABLESPACE); |
| 504 | } |
| 505 | |