diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-04-07 18:01:13 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-04-07 18:01:13 +0300 |
commit | cf552f5886968fc022122960d3a9274ce9f27819 (patch) | |
tree | 07e6d40bdd128c9c12ccd9f21a382371a65eb7c4 /storage/innobase/fsp/fsp0file.cc | |
parent | c2a63ac526bf4cd269def30a3d55ff29fdba8f86 (diff) | |
download | mariadb-git-cf552f5886968fc022122960d3a9274ce9f27819.tar.gz |
MDEV-25312 Replace fil_space_t::name with fil_space_t::name()bb-10.6-MDEV-25312
A consistency check for fil_space_t::name is causing recovery failures
in MDEV-25180 (Atomic ALTER TABLE). So, we'd better remove that field
altogether.
fil_space_t::name was more or less a copy of dict_table_t::name
(except for some special cases), and it was not being used for
anything useful.
There used to be a name_hash, but it had been removed already in
commit a75dbfd7183cc96680f3e3e684fd36500dac8158 (MDEV-12266).
We will also remove os_normalize_path(), OS_PATH_SEPARATOR,
OS_PATH_SEPATOR_ALT. On Microsoft Windows, we will treat \ and /
roughly in the same way. The intention is that for per-table
tablespaces, the filenames will always follow the pattern
prefix/databasename/tablename.ibd. (Any \ in the prefix must not
be converted.)
ut_basename_noext(): Remove (unused function).
read_link_file(): Replaces RemoteDatafile::read_link_file().
We will ensure that the last two path component separators are
forward slashes (converting up to 2 trailing backslashes on
Microsoft Windows), so that everywhere else we can
assume that data file names end in "/databasename/tablename.ibd".
Note: On Microsoft Windows, path names that start with \\?\ must
not contain / as path component separators. Previously, such paths
did work in the DATA DIRECTORY argument of InnoDB tables.
Reviewed by: Vladislav Vaintroub
Diffstat (limited to 'storage/innobase/fsp/fsp0file.cc')
-rw-r--r-- | storage/innobase/fsp/fsp0file.cc | 295 |
1 files changed, 100 insertions, 195 deletions
diff --git a/storage/innobase/fsp/fsp0file.cc b/storage/innobase/fsp/fsp0file.cc index 57164113647..77faf58edcf 100644 --- a/storage/innobase/fsp/fsp0file.cc +++ b/storage/innobase/fsp/fsp0file.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2013, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2020, MariaDB Corporation. +Copyright (c) 2017, 2021, 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 @@ -30,29 +30,12 @@ Created 2013-7-26 by Kevin Lewis #include "page0page.h" #include "srv0start.h" -/** Initialize the name, size and order of this datafile -@param[in] name tablespace name, will be copied -@param[in] flags tablespace flags */ -void -Datafile::init( - const char* name, - ulint flags) -{ - ut_ad(m_name == NULL); - ut_ad(name != NULL); - - m_name = mem_strdup(name); - m_flags = flags; -} - /** Release the resources. */ void Datafile::shutdown() { close(); - ut_free(m_name); - m_name = NULL; free_filepath(); free_first_page(); } @@ -119,13 +102,12 @@ Datafile::open_read_only(bool strict) /** Open a data file in read-write mode during start-up so that doublewrite pages can be restored and then it can be validated.* -@param[in] read_only_mode if true, then readonly mode checks are enforced. @return DB_SUCCESS or error code */ -dberr_t -Datafile::open_read_write(bool read_only_mode) +inline dberr_t Datafile::open_read_write() { bool success = false; ut_ad(m_handle == OS_FILE_CLOSED); + ut_ad(!srv_read_only_mode); /* This function can be called for file objects that do not need to be opened, which is the case when the m_filepath is NULL */ @@ -136,7 +118,7 @@ Datafile::open_read_write(bool read_only_mode) set_open_flags(OS_FILE_OPEN); m_handle = os_file_create_simple_no_error_handling( innodb_data_file_key, m_filepath, m_open_flags, - OS_FILE_READ_WRITE, read_only_mode, &success); + OS_FILE_READ_WRITE, false, &success); if (!success) { m_last_os_error = os_file_get_last_error(true); @@ -182,24 +164,17 @@ Datafile::close() Prepend the dirpath to filename using the extension given. If dirpath is NULL, prepend the default datadir to filepath. Store the result in m_filepath. -@param[in] dirpath directory path -@param[in] filename filename or filepath -@param[in] ext filename extension */ -void -Datafile::make_filepath( - const char* dirpath, - const char* filename, - ib_extention ext) +@param dirpath directory path +@param name tablespace (table) name +@param ext filename extension */ +void Datafile::make_filepath(const char *dirpath, fil_space_t::name_type name, + ib_extention ext) { - ut_ad(dirpath != NULL || filename != NULL); - - free_filepath(); - - m_filepath = fil_make_filepath(dirpath, filename, ext, false); - - ut_ad(m_filepath != NULL); - - set_filename(); + ut_ad(dirpath || name.size()); + free_filepath(); + m_filepath= fil_make_filepath(dirpath, name, ext, false); + ut_ad(m_filepath); + set_filename(); } /** Set the filepath by duplicating the filepath sent in. This is the @@ -258,23 +233,6 @@ Datafile::same_as( #endif /* WIN32 */ } -/** Allocate and set the datafile or tablespace name in m_name. -If a name is provided, use it; else extract a file-per-table -tablespace name from m_filepath. The value of m_name -will be freed in the destructor. -@param[in] name tablespace name if known, NULL if not */ -void -Datafile::set_name(const char* name) -{ - ut_free(m_name); - - if (name != NULL) { - m_name = mem_strdup(name); - } else { - m_name = fil_path_to_space_name(m_filepath); - } -} - /** Reads a few significant fields from the first page of the first datafile. The Datafile must already be open. @param[in] read_only_mode If true, then readonly mode checks are enforced. @@ -454,7 +412,7 @@ Datafile::validate_for_recovery() page 0 from doublewrite and read the space ID from a survey of the first few pages. */ close(); - err = open_read_write(srv_read_only_mode); + err = open_read_write(); if (err != DB_SUCCESS) { return(err); } @@ -476,10 +434,6 @@ Datafile::validate_for_recovery() err = validate_first_page(0); } - if (err == DB_SUCCESS) { - set_name(NULL); - } - return(err); } @@ -491,11 +445,8 @@ m_is_valid is set true on success, else false. @retval DB_SUCCESS on if the datafile is valid @retval DB_CORRUPTION if the datafile is not readable @retval DB_TABLESPACE_EXISTS if there is a duplicate space_id */ -dberr_t -Datafile::validate_first_page(lsn_t* flush_lsn) +dberr_t Datafile::validate_first_page(lsn_t *flush_lsn) { - char* prev_name; - char* prev_filepath; const char* error_txt = NULL; m_is_valid = true; @@ -576,26 +527,30 @@ err_exit: goto err_exit; } - if (fil_space_read_name_and_filepath( - m_space_id, &prev_name, &prev_filepath)) { + mysql_mutex_lock(&fil_system.mutex); - if (0 == strcmp(m_filepath, prev_filepath)) { - ut_free(prev_name); - ut_free(prev_filepath); - return(DB_SUCCESS); + fil_space_t* space = fil_space_get_by_id(m_space_id); + + if (space) { + fil_node_t* node = UT_LIST_GET_FIRST(space->chain); + + if (node && !strcmp(m_filepath, node->name)) { + mysql_mutex_unlock(&fil_system.mutex); + return DB_SUCCESS; } /* Make sure the space_id has not already been opened. */ ib::error() << "Attempted to open a previously opened" - " tablespace. Previous tablespace " << prev_name - << " at filepath: " << prev_filepath - << " uses space ID: " << m_space_id - << ". Cannot open filepath: " << m_filepath - << " which uses the same space ID."; + " tablespace. Previous tablespace: " + << (node ? node->name : "(unknown)") + << " uses space ID: " << m_space_id + << ". Cannot open filepath: " << m_filepath + << " which uses the same space ID."; + } - ut_free(prev_name); - ut_free(prev_filepath); + mysql_mutex_unlock(&fil_system.mutex); + if (space) { m_is_valid = false; free_first_page(); @@ -809,68 +764,61 @@ Datafile::restore_from_doublewrite() != DB_SUCCESS); } -/** Create a link filename based on the contents of m_name, -open that file, and read the contents into m_filepath. -@retval DB_SUCCESS if remote linked tablespace file is opened and read. -@retval DB_CANNOT_OPEN_FILE if the link file does not exist. */ -dberr_t -RemoteDatafile::open_link_file() -{ - if (m_link_filepath == NULL) { - m_link_filepath = fil_make_filepath(NULL, name(), ISL, false); - } - - m_filepath = read_link_file(m_link_filepath); - - return(m_filepath == NULL ? DB_CANNOT_OPEN_FILE : DB_SUCCESS); -} - -/** Opens a handle to the file linked to in an InnoDB Symbolic Link file -in read-only mode so that it can be validated. -@param[in] strict whether to issue error messages -@return DB_SUCCESS if remote linked tablespace file is found and opened. */ -dberr_t -RemoteDatafile::open_read_only(bool strict) +/** Read an InnoDB Symbolic Link (ISL) file by name. +@param link_filepath filepath of the ISL file +@return data file name (must be freed by the caller) +@retval nullptr on error */ +static char *read_link_file(const char *link_filepath) { - if (m_filepath == NULL && open_link_file() == DB_CANNOT_OPEN_FILE) { - return(DB_ERROR); - } - - dberr_t err = Datafile::open_read_only(strict); - - if (err != DB_SUCCESS && strict) { - /* The following call prints an error message */ - os_file_get_last_error(true); - ib::error() << "A link file was found named '" - << m_link_filepath << "' but the linked tablespace '" - << m_filepath << "' could not be opened read-only."; - } - - return(err); + if (FILE* file= fopen(link_filepath, "r+b" STR_O_CLOEXEC)) + { + char *filepath= static_cast<char*>(ut_malloc_nokey(OS_FILE_MAX_PATH)); + + os_file_read_string(file, filepath, OS_FILE_MAX_PATH); + fclose(file); + + if (size_t len= strlen(filepath)) + { + /* Trim whitespace from end of filepath */ + len--; + while (filepath[len] >= 0 && filepath[len] <= 0x20) + filepath[len--]= 0; + if (!*filepath) + return nullptr; + /* Ensure that the last 2 path separators are forward slashes, + because elsewhere we are assuming that tablespace file names end + in "/databasename/tablename.ibd". */ + unsigned trailing_slashes= 0; + for (; len; len--) + { + switch (filepath[len]) { +#ifdef _WIN32 + case '\\': + filepath[len]= '/'; + /* fall through */ +#endif + case '/': + if (++trailing_slashes >= 2) + return filepath; + } + } + } + } + + return nullptr; } -/** Opens a handle to the file linked to in an InnoDB Symbolic Link file -in read-write mode so that it can be restored from doublewrite and validated. -@param[in] read_only_mode If true, then readonly mode checks are enforced. -@return DB_SUCCESS if remote linked tablespace file is found and opened. */ -dberr_t -RemoteDatafile::open_read_write(bool read_only_mode) +/** Create a link filename, +open that file, and read the contents into m_filepath. +@param name table name +@return filepath() +@retval nullptr if the .isl file does not exist or cannot be read */ +const char *RemoteDatafile::open_link_file(const table_name_t &name) { - if (m_filepath == NULL && open_link_file() == DB_CANNOT_OPEN_FILE) { - return(DB_ERROR); - } - - dberr_t err = Datafile::open_read_write(read_only_mode); - - if (err != DB_SUCCESS) { - /* The following call prints an error message */ - m_last_os_error = os_file_get_last_error(true); - ib::error() << "A link file was found named '" - << m_link_filepath << "' but the linked data file '" - << m_filepath << "' could not be opened for writing."; - } - - return(err); + if (!m_link_filepath) + m_link_filepath= fil_make_filepath(nullptr, name, ISL, false); + m_filepath= read_link_file(m_link_filepath); + return m_filepath; } /** Release the resources. */ @@ -885,16 +833,12 @@ RemoteDatafile::shutdown() } } -/** Creates a new InnoDB Symbolic Link (ISL) file. It is always created -under the 'datadir' of MySQL. The datadir is the directory of a -running mysqld program. We can refer to it by simply using the path ".". -@param[in] name tablespace name -@param[in] filepath remote filepath of tablespace datafile +/** Create InnoDB Symbolic Link (ISL) file. +@param name tablespace name +@param filepath full file name @return DB_SUCCESS or error code */ -dberr_t -RemoteDatafile::create_link_file( - const char* name, - const char* filepath) +dberr_t RemoteDatafile::create_link_file(fil_space_t::name_type name, + const char *filepath) { bool success; dberr_t err = DB_SUCCESS; @@ -902,7 +846,6 @@ RemoteDatafile::create_link_file( char* prev_filepath = NULL; ut_ad(!srv_read_only_mode); - ut_ad(0 == strcmp(&filepath[strlen(filepath) - 4], DOT_IBD)); link_filepath = fil_make_filepath(NULL, name, ISL, false); @@ -915,7 +858,8 @@ RemoteDatafile::create_link_file( /* Truncate (starting with MySQL 5.6, probably no longer since MariaDB Server 10.2.19) used to call this with an existing link file which contains the same filepath. */ - bool same = !strcmp(prev_filepath, filepath); + bool same = !strncmp(prev_filepath, name.data(), name.size()) + && !strcmp(prev_filepath + name.size(), DOT_IBD); ut_free(prev_filepath); if (same) { ut_free(link_filepath); @@ -963,9 +907,8 @@ RemoteDatafile::create_link_file( return(err); } - ulint rbytes = fwrite(filepath, 1, strlen(filepath), file); - - if (rbytes != strlen(filepath)) { + const size_t len = strlen(filepath); + if (fwrite(filepath, 1, len, file) != len) { error = os_file_get_last_error(true); ib::error() << "Cannot write link file: " @@ -994,50 +937,12 @@ RemoteDatafile::delete_link_file(void) } /** Delete an InnoDB Symbolic Link (ISL) file by name. -@param[in] name tablespace name */ -void -RemoteDatafile::delete_link_file( - const char* name) -{ - char* link_filepath = fil_make_filepath(NULL, name, ISL, false); - - if (link_filepath != NULL) { - os_file_delete_if_exists( - innodb_data_file_key, link_filepath, NULL); - - ut_free(link_filepath); - } -} - -/** Read an InnoDB Symbolic Link (ISL) file by name. -It is always created under the datadir of MySQL. -For file-per-table tablespaces, the isl file is expected to be -in a 'database' directory and called 'tablename.isl'. -The caller must free the memory returned if it is not null. -@param[in] link_filepath filepath of the ISL file -@return Filepath of the IBD file read from the ISL file */ -char* -RemoteDatafile::read_link_file( - const char* link_filepath) +@param name tablespace name */ +void RemoteDatafile::delete_link_file(fil_space_t::name_type name) { - FILE* file = fopen(link_filepath, "r+b" STR_O_CLOEXEC); - if (file == NULL) { - return(NULL); - } - - char* filepath = static_cast<char*>(ut_malloc_nokey(OS_FILE_MAX_PATH)); - - os_file_read_string(file, filepath, OS_FILE_MAX_PATH); - fclose(file); - - if (filepath[0] != '\0') { - /* Trim whitespace from end of filepath */ - ulint last_ch = strlen(filepath) - 1; - while (last_ch > 4 && filepath[last_ch] <= 0x20) { - filepath[last_ch--] = 0x00; - } - os_normalize_path(filepath); - } - - return(filepath); + if (char *link_filepath= fil_make_filepath(NULL, name, ISL, false)) + { + os_file_delete_if_exists(innodb_data_file_key, link_filepath, nullptr); + ut_free(link_filepath); + } } |