From 6960e9ed24dbdab587730cdceab1f29bcdd6d52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 Mar 2020 14:23:47 +0200 Subject: [PATCH] MDEV-21983: Crash on DROP/RENAME TABLE after DISCARD TABLESPACE fil_delete_tablespace(): Remove the unused parameter drop_ahi, and add the parameter if_exists=false. We want to suppress error messages if we know that the tablespace has been discarded. dict_table_rename_in_cache(): Pass the new parameter to fil_delete_tablespace(), that is, do not complain about missing tablespace if the tablespace has been discarded. row_make_new_pathname(): Declare as static. row_drop_table_for_mysql(): Tolerate !table->data_dir_path when the tablespace has been discarded. row_rename_table_for_mysql(): Skip part of the RENAME TABLE when fil_space_get_first_path() returns NULL. --- .../innodb/r/alter_missing_tablespace.result | 4 +++- .../innodb/t/alter_missing_tablespace.test | 4 +++- storage/innobase/dict/dict0dict.cc | 3 ++- storage/innobase/fil/fil0fil.cc | 18 +++++++----------- storage/innobase/include/fil0fil.h | 11 +++-------- storage/innobase/include/row0merge.h | 14 +------------- storage/innobase/row/row0merge.cc | 1 + storage/innobase/row/row0mysql.cc | 14 +++++++------- 8 files changed, 27 insertions(+), 42 deletions(-) diff --git a/mysql-test/suite/innodb/r/alter_missing_tablespace.result b/mysql-test/suite/innodb/r/alter_missing_tablespace.result index f9db353af3c..95415dd4e03 100644 --- a/mysql-test/suite/innodb/r/alter_missing_tablespace.result +++ b/mysql-test/suite/innodb/r/alter_missing_tablespace.result @@ -35,7 +35,9 @@ ALTER TABLE t DISCARD TABLESPACE; Warnings: Warning 1812 Tablespace is missing for table 'test/t' Warning 1812 Tablespace is missing for table 'test/t' -DROP TABLE t; +RENAME TABLE t TO u; +RENAME TABLE u TO v; +DROP TABLE v; SELECT * FROM `x..d`; ERROR HY000: Got error 194 "Tablespace is missing for a table" from storage engine InnoDB DROP TABLE `x..d`; diff --git a/mysql-test/suite/innodb/t/alter_missing_tablespace.test b/mysql-test/suite/innodb/t/alter_missing_tablespace.test index 0094db520ae..1051b79bddb 100644 --- a/mysql-test/suite/innodb/t/alter_missing_tablespace.test +++ b/mysql-test/suite/innodb/t/alter_missing_tablespace.test @@ -58,7 +58,9 @@ ALTER TABLE t ALGORITHM=COPY, DISCARD TABLESPACE; --error ER_PARSE_ERROR ALTER TABLE t ALGORITHM=DEFAULT, DISCARD TABLESPACE; ALTER TABLE t DISCARD TABLESPACE; -DROP TABLE t; +RENAME TABLE t TO u; +RENAME TABLE u TO v; +DROP TABLE v; --error ER_GET_ERRNO SELECT * FROM `x..d`; DROP TABLE `x..d`; diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index cf70047ab15..455d51af438 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1630,7 +1630,8 @@ dict_table_rename_in_cache( return(DB_OUT_OF_MEMORY); } - fil_delete_tablespace(table->space); + fil_delete_tablespace(table->space, + dict_table_is_discarded(table)); /* Delete any temp file hanging around. */ if (os_file_status(filepath, &exists, &ftype) diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 64c2834a635..4192008ecf0 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -2861,14 +2861,9 @@ bool fil_table_accessible(const dict_table_t* table) /** Delete a tablespace and associated .ibd file. @param[in] id tablespace identifier +@param[in] if_exists whether to ignore missing tablespace @return DB_SUCCESS or error */ -dberr_t -fil_delete_tablespace( - ulint id -#ifdef BTR_CUR_HASH_ADAPT - , bool drop_ahi /*!< whether to drop the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ - ) +dberr_t fil_delete_tablespace(ulint id, bool if_exists) { char* path = 0; fil_space_t* space = 0; @@ -2879,10 +2874,11 @@ fil_delete_tablespace( id, FIL_OPERATION_DELETE, &space, &path); if (err != DB_SUCCESS) { - - ib::error() << "Cannot delete tablespace " << id - << " because it is not found in the tablespace" - " memory cache."; + if (!if_exists) { + ib::error() << "Cannot delete tablespace " << id + << " because it is not found" + " in the tablespace memory cache."; + } return(err); } diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 5484477d913..9c722944665 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2019, MariaDB Corporation. +Copyright (c) 2013, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -964,14 +964,9 @@ bool fil_table_accessible(const dict_table_t* table) /** Delete a tablespace and associated .ibd file. @param[in] id tablespace identifier +@param[in] if_exists whether to ignore missing tablespace @return DB_SUCCESS or error */ -dberr_t -fil_delete_tablespace( - ulint id -#ifdef BTR_CUR_HASH_ADAPT - , bool drop_ahi = false /*!< whether to drop the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ - ); +dberr_t fil_delete_tablespace(ulint id, bool if_exists= false); /** Prepare to truncate an undo tablespace. @param[in] space_id undo tablespace id diff --git a/storage/innobase/include/row0merge.h b/storage/innobase/include/row0merge.h index a9b275c890e..8e7ca5de046 100644 --- a/storage/innobase/include/row0merge.h +++ b/storage/innobase/include/row0merge.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2005, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2019, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -202,18 +202,6 @@ row_merge_file_destroy_low( /*=======================*/ int fd); /*!< in: merge file descriptor */ -/*********************************************************************//** -Provide a new pathname for a table that is being renamed if it belongs to -a file-per-table tablespace. The caller is responsible for freeing the -memory allocated for the return value. -@return new pathname of tablespace file, or NULL if space = 0 */ -char* -row_make_new_pathname( -/*==================*/ - dict_table_t* table, /*!< in: table to be renamed */ - const char* new_name) /*!< in: new name */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); - /*********************************************************************//** Rename the tables in the data dictionary. The data dictionary must have been locked exclusively by the caller, because the transaction diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index d218c166a1b..29f69317a18 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -4149,6 +4149,7 @@ Provide a new pathname for a table that is being renamed if it belongs to a file-per-table tablespace. The caller is responsible for freeing the memory allocated for the return value. @return new pathname of tablespace file, or NULL if space = 0 */ +static char* row_make_new_pathname( /*==================*/ diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index a437c8d5ff1..9add75b3a67 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -3725,10 +3725,12 @@ do_drop: dict_table_t. Free this memory before returning. */ if (DICT_TF_HAS_DATA_DIR(table->flags)) { dict_get_and_save_data_dir_path(table, true); - ut_a(table->data_dir_path); + ut_ad(table->data_dir_path + || dict_table_is_discarded(table)); filepath = fil_make_filepath( table->data_dir_path, - table->name.m_name, IBD, true); + table->name.m_name, IBD, + table->data_dir_path != NULL); } else { filepath = fil_make_filepath( NULL, table->name.m_name, IBD, false); @@ -4304,11 +4306,9 @@ row_rename_table_for_mysql( /* SYS_TABLESPACES and SYS_DATAFILES need to be updated if the table is in a single-table tablespace. */ - if (err == DB_SUCCESS - && dict_table_is_file_per_table(table)) { - /* Make a new pathname to update SYS_DATAFILES. */ - char* new_path = row_make_new_pathname(table, new_name); - char* old_path = fil_space_get_first_path(table->space); + if (err != DB_SUCCESS || !dict_table_is_file_per_table(table)) { + } else if (char* old_path = fil_space_get_first_path(table->space)) { + char* new_path = os_file_make_new_pathname(old_path, new_name); /* If old path and new path are the same means tablename has not changed and only the database name holding the table