You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1718 lines
55 KiB
1718 lines
55 KiB
/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include "sql/sql_tablespace.h"
|
|
|
|
#include <string.h>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "m_ctype.h"
|
|
#include "my_base.h"
|
|
#include "my_dbug.h"
|
|
#include "my_inttypes.h"
|
|
#include "my_io.h"
|
|
#include "my_sys.h"
|
|
#include "mysql_com.h"
|
|
#include "mysqld.h" // opt_table_encryption_privilege_check
|
|
#include "mysqld_error.h"
|
|
#include "sql/auth/auth_acls.h" // CREATE_TABLESPACE_ACL
|
|
#include "sql/auth/auth_common.h"
|
|
#include "sql/dd/cache/dictionary_client.h" // dd::Dictionary_client
|
|
#include "sql/dd/dd.h" // dd::create_object
|
|
#include "sql/dd/dd_kill_immunizer.h" // dd::DD_kill_immunizer
|
|
#include "sql/dd/dd_table.h" // dd::is_encrypted
|
|
#include "sql/dd/impl/sdi_utils.h" // dd::sdi_utils::make_guard
|
|
#include "sql/dd/properties.h"
|
|
#include "sql/dd/string_type.h"
|
|
#include "sql/dd/types/table.h" // dd::fetch_tablespace_table_refs
|
|
#include "sql/dd/types/tablespace.h" // dd::fetch_tablespace_table_refs
|
|
#include "sql/dd/types/tablespace_file.h" // dd::Tablespace_file
|
|
#include "sql/debug_sync.h" // DBUG_SYNC
|
|
#include "sql/derror.h" // ER_THD
|
|
#include "sql/handler.h" // st_alter_tablespace
|
|
#include "sql/item_strfunc.h" // mysql_generate_uuid
|
|
#include "sql/mdl.h"
|
|
#include "sql/parse_tree_helpers.h" // resolve_engine
|
|
#include "sql/sql_base.h" // TDC_RT_REMOVE_ALL
|
|
#include "sql/sql_class.h" // THD
|
|
#include "sql/sql_const.h"
|
|
#include "sql/sql_error.h"
|
|
#include "sql/sql_plugin_ref.h"
|
|
#include "sql/sql_table.h" // write_bin_log
|
|
#include "sql/strfunc.h" // lex_cstring_handle
|
|
#include "sql/system_variables.h"
|
|
#include "sql/thd_raii.h"
|
|
#include "sql/transaction.h" // trans_commit_stmt
|
|
|
|
namespace {
|
|
|
|
template <typename LEXSTR>
|
|
bool validate_tspnamelen(const LEXSTR &name) {
|
|
if (name.length == 0) {
|
|
// Empty name not allowed
|
|
my_error(ER_WRONG_TABLESPACE_NAME, MYF(0), name.str);
|
|
return true;
|
|
}
|
|
|
|
if (name.length > NAME_LEN ||
|
|
my_numchars_mb(system_charset_info, name.str, name.str + name.length) >
|
|
NAME_CHAR_LEN) {
|
|
// Byte length exceeding NAME_LEN, and character lenght exceeding
|
|
// NAME_CHAR_LEN not allowed
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), name.str);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} // namespace
|
|
|
|
st_alter_tablespace::st_alter_tablespace(
|
|
const char *tablespace, const char *logfile_group, ts_command_type cmd,
|
|
enum ts_alter_tablespace_type alter_tablespace_cmd, const char *datafile,
|
|
const char *undofile, const Tablespace_options &opts)
|
|
: tablespace_name{tablespace},
|
|
logfile_group_name{logfile_group},
|
|
ts_cmd_type{cmd},
|
|
ts_alter_tablespace_type{alter_tablespace_cmd},
|
|
data_file_name{datafile},
|
|
undo_file_name{undofile},
|
|
// Propagate Tablespace options from parser
|
|
extent_size{opts.extent_size},
|
|
undo_buffer_size{opts.undo_buffer_size},
|
|
redo_buffer_size{opts.redo_buffer_size},
|
|
initial_size{opts.initial_size},
|
|
autoextend_size{opts.autoextend_size},
|
|
max_size{opts.max_size},
|
|
file_block_size{opts.file_block_size},
|
|
nodegroup_id{opts.nodegroup_id},
|
|
wait_until_completed{opts.wait_until_completed},
|
|
ts_comment{opts.ts_comment.str} {}
|
|
|
|
bool validate_tablespace_name_length(const char *tablespace_name) {
|
|
DBUG_ASSERT(tablespace_name != nullptr);
|
|
LEX_CSTRING tspname = {tablespace_name, strlen(tablespace_name)};
|
|
return validate_tspnamelen(tspname);
|
|
}
|
|
|
|
bool validate_tablespace_name(ts_command_type ts_cmd,
|
|
const char *tablespace_name,
|
|
const handlerton *engine) {
|
|
DBUG_ASSERT(tablespace_name != nullptr);
|
|
DBUG_ASSERT(engine != nullptr);
|
|
|
|
// Length must be valid.
|
|
if (validate_tablespace_name_length(tablespace_name)) {
|
|
my_error(ER_WRONG_TABLESPACE_NAME, MYF(0), tablespace_name);
|
|
return true;
|
|
}
|
|
|
|
// Invoke SE specific validation of the name.
|
|
if (engine->is_valid_tablespace_name != nullptr &&
|
|
!engine->is_valid_tablespace_name(ts_cmd, tablespace_name)) {
|
|
my_error(ER_WRONG_TABLESPACE_NAME, MYF(0), tablespace_name);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
class Rollback_guard {
|
|
THD *m_thd;
|
|
bool m_disabled = false;
|
|
|
|
public:
|
|
handlerton *m_hton = nullptr;
|
|
|
|
explicit Rollback_guard(THD *thd) : m_thd{thd} {}
|
|
~Rollback_guard() {
|
|
if (m_disabled) {
|
|
return;
|
|
}
|
|
trans_rollback_stmt(m_thd);
|
|
// Full rollback in case we have THD::transaction_rollback_request.
|
|
trans_rollback(m_thd);
|
|
|
|
if (m_hton != nullptr && ddl_is_atomic(m_hton) &&
|
|
m_hton->post_ddl != nullptr) {
|
|
m_hton->post_ddl(m_thd);
|
|
}
|
|
}
|
|
void disable() { m_disabled = true; }
|
|
};
|
|
|
|
template <typename DISABLE_ROLLBACK>
|
|
bool complete_stmt(THD *thd, handlerton *hton, DISABLE_ROLLBACK &&dr,
|
|
bool using_trans = true, bool dont_write_to_binlog = false) {
|
|
if (!dont_write_to_binlog)
|
|
if (write_bin_log(thd, false, thd->query().str, thd->query().length,
|
|
using_trans && ddl_is_atomic(hton))) {
|
|
return true;
|
|
}
|
|
|
|
dr();
|
|
|
|
/* Commit the statement and call storage engine's post-DDL hook. */
|
|
if (trans_commit_stmt(thd) || trans_commit(thd)) {
|
|
return true;
|
|
}
|
|
|
|
if (hton && ddl_is_atomic(hton) && hton->post_ddl) {
|
|
hton->post_ddl(thd);
|
|
}
|
|
|
|
my_ok(thd);
|
|
return false;
|
|
}
|
|
|
|
bool lock_rec(THD *thd, MDL_request_list *rlst, const LEX_STRING &tsp) {
|
|
if (validate_tspnamelen(tsp)) {
|
|
return true;
|
|
}
|
|
MDL_request tail_request;
|
|
MDL_REQUEST_INIT(&tail_request, MDL_key::TABLESPACE, "", tsp.str,
|
|
MDL_EXCLUSIVE, MDL_TRANSACTION);
|
|
rlst->push_front(&tail_request);
|
|
|
|
MDL_request global_request;
|
|
MDL_REQUEST_INIT(&global_request, MDL_key::GLOBAL, "", "",
|
|
MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
|
|
rlst->push_front(&global_request);
|
|
|
|
MDL_request backup_lock_request;
|
|
|
|
MDL_REQUEST_INIT(&backup_lock_request, MDL_key::BACKUP_LOCK, "", "",
|
|
MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION);
|
|
rlst->push_front(&backup_lock_request);
|
|
|
|
return thd->mdl_context.acquire_locks(rlst, thd->variables.lock_wait_timeout);
|
|
}
|
|
|
|
template <typename... Names>
|
|
bool lock_rec(THD *thd, MDL_request_list *rlst, const LEX_STRING &tsp,
|
|
Names... names) {
|
|
if (validate_tspnamelen(tsp)) {
|
|
return true;
|
|
}
|
|
MDL_request request;
|
|
MDL_REQUEST_INIT(&request, MDL_key::TABLESPACE, "", tsp.str, MDL_EXCLUSIVE,
|
|
MDL_TRANSACTION);
|
|
rlst->push_front(&request);
|
|
return lock_rec(thd, rlst, names...);
|
|
}
|
|
|
|
template <typename... Names>
|
|
bool lock_tablespace_names(THD *thd, Names... names) {
|
|
MDL_request_list mdl_requests;
|
|
if (thd->locked_tables_mode) {
|
|
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (thd->global_read_lock.can_acquire_protection()) {
|
|
return true;
|
|
}
|
|
|
|
if (lock_rec(thd, &mdl_requests, names...)) {
|
|
return true;
|
|
}
|
|
|
|
size_t nts = sizeof...(names);
|
|
if (nts == 1) {
|
|
DEBUG_SYNC(thd, "after_wait_locked_tablespace_name");
|
|
} else {
|
|
DEBUG_SYNC(thd, "after_wait_locked_tablespace_name_for_table");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
using Mod_pair = std::pair<const T *, T *>;
|
|
typedef std::vector<Mod_pair<dd::Table>> Table_pair_list;
|
|
|
|
template <typename T>
|
|
Mod_pair<T> get_mod_pair(dd::cache::Dictionary_client *dcp,
|
|
const dd::String_type &name) {
|
|
Mod_pair<T> ret{nullptr, nullptr};
|
|
if (dcp->acquire(name, &ret.first)) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
if (ret.first == nullptr) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
if (dcp->acquire_for_modification(name, &ret.second)) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
DBUG_ASSERT(ret.second != nullptr);
|
|
return ret;
|
|
}
|
|
|
|
template <typename T>
|
|
Mod_pair<T> get_mod_pair(dd::cache::Dictionary_client *dcp,
|
|
const dd::String_type &sch_name,
|
|
const dd::String_type &name) {
|
|
Mod_pair<T> ret{nullptr, nullptr};
|
|
if (dcp->acquire(sch_name, name, &ret.first)) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
if (ret.first == nullptr) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
if (dcp->acquire_for_modification(sch_name, name, &ret.second)) {
|
|
return {nullptr, nullptr};
|
|
}
|
|
DBUG_ASSERT(ret.second != nullptr);
|
|
return ret;
|
|
}
|
|
|
|
const char *real_engine_name(THD *thd, const LEX_CSTRING &alias) {
|
|
plugin_ref pr = ha_resolve_by_name(thd, &alias, false);
|
|
handlerton *hton = (pr != nullptr ? plugin_data<handlerton *>(pr) : nullptr);
|
|
return hton != nullptr ? ha_resolve_storage_engine_name(hton) : "";
|
|
}
|
|
|
|
bool get_stmt_hton(THD *thd, const LEX_CSTRING &engine, const char *object_name,
|
|
const char *statement, handlerton **htonp) {
|
|
handlerton *hton = nullptr;
|
|
if (engine.str != nullptr &&
|
|
resolve_engine(thd, engine, false, false, &hton)) {
|
|
return true;
|
|
}
|
|
if (hton == nullptr || hton->state != SHOW_OPTION_YES) {
|
|
hton = ha_default_handlerton(thd);
|
|
if (engine.str != nullptr) {
|
|
push_warning_printf(thd, Sql_condition::SL_WARNING,
|
|
ER_WARN_USING_OTHER_HANDLER,
|
|
ER_THD(thd, ER_WARN_USING_OTHER_HANDLER),
|
|
ha_resolve_storage_engine_name(hton), object_name);
|
|
}
|
|
}
|
|
|
|
// Check if tablespace operation is disallowed by the storage engine.
|
|
if (ha_is_storage_engine_disabled(hton)) {
|
|
my_error(ER_DISABLED_STORAGE_ENGINE, MYF(0),
|
|
ha_resolve_storage_engine_name(hton));
|
|
return true;
|
|
}
|
|
if (hton->alter_tablespace == nullptr) {
|
|
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
|
|
ha_resolve_storage_engine_name(hton), statement);
|
|
return true;
|
|
}
|
|
*htonp = hton;
|
|
return false;
|
|
}
|
|
|
|
bool get_dd_hton(THD *thd, const dd::String_type &dd_engine,
|
|
const LEX_CSTRING &stmt_engine, const char *tblspc,
|
|
const char *stmt, handlerton **htonp) {
|
|
if (stmt_engine.str && dd_engine != real_engine_name(thd, stmt_engine)) {
|
|
my_error(ER_TABLESPACE_ENGINE_MISMATCH, MYF(0), stmt_engine.str,
|
|
dd_engine.c_str(), tblspc);
|
|
return true;
|
|
}
|
|
if (stmt_engine.str != nullptr) {
|
|
push_warning_printf(thd, Sql_condition::SL_WARNING,
|
|
ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
|
|
ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
|
|
"ENGINE tablespace option");
|
|
}
|
|
|
|
plugin_ref pr = ha_resolve_by_name_raw(thd, lex_cstring_handle(dd_engine));
|
|
handlerton *hton = (pr != nullptr ? plugin_data<handlerton *>(pr) : nullptr);
|
|
if (hton == nullptr) {
|
|
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), dd_engine.c_str());
|
|
return true;
|
|
}
|
|
|
|
DBUG_ASSERT(hton->alter_tablespace);
|
|
if (hton->alter_tablespace == nullptr) {
|
|
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), dd_engine.c_str(), stmt);
|
|
return true;
|
|
}
|
|
|
|
*htonp = hton;
|
|
return false;
|
|
}
|
|
|
|
bool intermediate_commit_unless_atomic_ddl(THD *thd, handlerton *hton) {
|
|
if (ddl_is_atomic(hton)) {
|
|
return false;
|
|
}
|
|
/* purecov: begin inspected */
|
|
Disable_gtid_state_update_guard disabler{thd};
|
|
return (trans_commit_stmt(thd) || trans_commit(thd));
|
|
/* purecov: end */
|
|
}
|
|
|
|
bool map_errors(int se_error, const char *statement_txt,
|
|
const st_alter_tablespace *ts_info) {
|
|
switch (se_error) {
|
|
case 0:
|
|
return false;
|
|
case 1:
|
|
return true;
|
|
case HA_ADMIN_NOT_IMPLEMENTED:
|
|
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), statement_txt);
|
|
break;
|
|
case HA_ERR_TABLESPACE_MISSING:
|
|
my_error(ER_TABLESPACE_MISSING, MYF(0), ts_info->tablespace_name);
|
|
break;
|
|
case HA_ERR_TABLESPACE_IS_NOT_EMPTY:
|
|
my_error(ER_TABLESPACE_IS_NOT_EMPTY, MYF(0), ts_info->tablespace_name);
|
|
break;
|
|
case HA_ERR_WRONG_FILE_NAME:
|
|
my_error(ER_WRONG_FILE_NAME, MYF(0), ts_info->data_file_name);
|
|
break;
|
|
case HA_ADMIN_FAILED:
|
|
my_error(ER_CANT_CREATE_FILE, MYF(0), ts_info->data_file_name);
|
|
break;
|
|
case HA_ERR_INNODB_READ_ONLY:
|
|
my_error(ER_INNODB_READ_ONLY, MYF(0));
|
|
break;
|
|
case HA_ERR_RECORD_FILE_FULL:
|
|
my_error(ER_RECORD_FILE_FULL, MYF(0), ts_info->tablespace_name);
|
|
break;
|
|
case HA_WRONG_CREATE_OPTION:
|
|
my_error(ER_ILLEGAL_HA, MYF(0), ts_info->tablespace_name);
|
|
break;
|
|
case HA_ERR_TABLESPACE_EXISTS:
|
|
my_error(ER_TABLESPACE_EXISTS, MYF(0), ts_info->tablespace_name);
|
|
break;
|
|
case HA_ERR_NOT_ALLOWED_COMMAND:
|
|
my_error(ER_DISALLOWED_OPERATION, MYF(0), statement_txt,
|
|
ts_info->tablespace_name);
|
|
break;
|
|
default:
|
|
char errbuf[MYSQL_ERRMSG_SIZE];
|
|
my_error(ER_GET_ERRNO, MYF(0), se_error,
|
|
my_strerror(errbuf, MYSQL_ERRMSG_SIZE, se_error));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Sql_cmd_tablespace::Sql_cmd_tablespace(const LEX_STRING &name,
|
|
const Tablespace_options *options)
|
|
: m_tablespace_name(name), m_options(options) {}
|
|
|
|
/* purecov: begin inspected */
|
|
enum_sql_command Sql_cmd_tablespace::sql_command_code() const {
|
|
DBUG_ASSERT(false);
|
|
return SQLCOM_ALTER_TABLESPACE;
|
|
}
|
|
/* purecov: end */
|
|
|
|
Sql_cmd_create_tablespace::Sql_cmd_create_tablespace(
|
|
const LEX_STRING &tsname, const LEX_STRING &dfname,
|
|
const LEX_STRING &lfgname, const Tablespace_options *options)
|
|
: Sql_cmd_tablespace{tsname, options},
|
|
m_datafile_name(dfname),
|
|
m_logfile_group_name(lfgname),
|
|
m_auto_generate_datafile_name(dfname.str == nullptr) {}
|
|
|
|
bool Sql_cmd_create_tablespace::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_stmt_hton(thd, m_options->engine_name, m_tablespace_name.str,
|
|
"CREATE TABLESPACE", &hton)) {
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton; // Allow rollback to call hton->post_ddl
|
|
|
|
// Check the tablespace name and acquire an MDL X lock.
|
|
if (validate_tablespace_name(CREATE_TABLESPACE, m_tablespace_name.str,
|
|
hton) ||
|
|
lock_tablespace_names(thd, m_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Check if user has permission to create tablespace, if encryption type
|
|
provided differ from global 'default_table_encryption' setting.
|
|
We use 'default_table_encryption' value if encryption is not supplied
|
|
by user.
|
|
*/
|
|
bool encrypt_tablespace = false;
|
|
dd::String_type encrypt_type;
|
|
if (m_options->encryption.str) {
|
|
encrypt_tablespace = dd::is_encrypted(m_options->encryption);
|
|
encrypt_type = dd::make_string_type(m_options->encryption);
|
|
} else {
|
|
encrypt_tablespace = thd->variables.default_table_encryption;
|
|
encrypt_type = encrypt_tablespace ? "Y" : "N";
|
|
}
|
|
|
|
if (opt_table_encryption_privilege_check &&
|
|
encrypt_tablespace != thd->variables.default_table_encryption &&
|
|
check_table_encryption_admin_access(thd)) {
|
|
my_error(ER_CANNOT_SET_TABLESPACE_ENCRYPTION, MYF(0));
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
// Check if same tablespace already exists.
|
|
auto tsn = dd::make_string_type(m_tablespace_name);
|
|
const dd::Tablespace *ts = nullptr;
|
|
if (dc.acquire(tsn, &ts)) {
|
|
return true;
|
|
}
|
|
|
|
if (ts != nullptr) {
|
|
my_error(ER_TABLESPACE_EXISTS, MYF(0), tsn.c_str());
|
|
return true;
|
|
}
|
|
|
|
// Create new tablespace.
|
|
std::unique_ptr<dd::Tablespace> tablespace(
|
|
dd::create_object<dd::Tablespace>());
|
|
|
|
// Set tablespace name
|
|
tablespace->set_name(tsn);
|
|
|
|
// Engine type
|
|
tablespace->set_engine(ha_resolve_storage_engine_name(hton));
|
|
|
|
// - Store ENCRYPTION if SE supports it.
|
|
// - Disallow encryption='y', if SE does not support it.
|
|
if (hton->flags & HTON_SUPPORTS_TABLE_ENCRYPTION) {
|
|
tablespace->options().set("encryption", encrypt_type);
|
|
} else if (encrypt_tablespace) {
|
|
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "ENCRYPTION");
|
|
return true;
|
|
}
|
|
|
|
size_t cl = m_options->ts_comment.length;
|
|
if (validate_comment_length(
|
|
thd, m_options->ts_comment.str, &cl, TABLESPACE_COMMENT_MAXLEN,
|
|
ER_TOO_LONG_TABLESPACE_COMMENT, m_tablespace_name.str)) {
|
|
return true;
|
|
}
|
|
|
|
tablespace->set_comment(dd::String_type{m_options->ts_comment.str, cl});
|
|
|
|
LEX_STRING tblspc_datafile_name = {m_datafile_name.str,
|
|
m_datafile_name.length};
|
|
if (m_auto_generate_datafile_name) {
|
|
char tmp_buf[40];
|
|
String str(tmp_buf, sizeof(tmp_buf), &my_charset_bin);
|
|
String *uuid = mysql_generate_uuid(&str);
|
|
if (hton->get_tablespace_filename_ext)
|
|
uuid->append(hton->get_tablespace_filename_ext());
|
|
|
|
tblspc_datafile_name.str =
|
|
thd->strmake(uuid->c_ptr_quick(), uuid->length());
|
|
tblspc_datafile_name.length = uuid->length();
|
|
}
|
|
|
|
if (tblspc_datafile_name.length > FN_REFLEN) {
|
|
my_error(ER_PATH_LENGTH, MYF(0), "DATAFILE");
|
|
return true;
|
|
}
|
|
|
|
// Add datafile
|
|
tablespace->add_file()->set_filename(
|
|
dd::make_string_type(tblspc_datafile_name));
|
|
|
|
// Write changes to dictionary.
|
|
if (dc.store(tablespace.get())) {
|
|
return true;
|
|
}
|
|
|
|
const bool atomic_ddl = ddl_is_atomic(hton);
|
|
/*
|
|
Commit after creation of tablespace in the data-dictionary for
|
|
storage engines which don't support atomic DDL. We do this to
|
|
avoid being left with tablespace in SE but not in data-dictionary
|
|
in case of crash. Indeed, in this case, we can end-up with tablespace
|
|
present in the data-dictionary and not present in SE. But this can be
|
|
easily fixed by doing DROP TABLESPACE.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true; /* purecov: inspected */
|
|
}
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(&dc, m_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
st_alter_tablespace ts_info{m_tablespace_name.str,
|
|
m_logfile_group_name.str,
|
|
CREATE_TABLESPACE,
|
|
TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED,
|
|
tblspc_datafile_name.str,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
if (map_errors(
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second),
|
|
"CREATE TABLESPACE", &ts_info)) {
|
|
if (!atomic_ddl) {
|
|
/*
|
|
For engines which don't support atomic DDL addition of tablespace to
|
|
data-dictionary has been committed already so we need to revert it.
|
|
*/
|
|
/* purecov: begin inspected */
|
|
if (dc.drop(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
Disable_gtid_state_update_guard disabler{thd};
|
|
(void)trans_commit_stmt(thd);
|
|
(void)trans_commit(thd);
|
|
/* purecov: end */
|
|
}
|
|
return true;
|
|
} // if (map_errors
|
|
|
|
/*
|
|
Per convention only engines supporting atomic DDL are allowed to
|
|
modify data-dictionary objects in handler::create() and other
|
|
similar calls.
|
|
*/
|
|
if (atomic_ddl && dc.update(tsmp.second)) {
|
|
return true; /* purecov: inspected */
|
|
}
|
|
|
|
if (complete_stmt(thd, hton, [&]() { rollback_on_return.disable(); })) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Sql_cmd_drop_tablespace::Sql_cmd_drop_tablespace(
|
|
const LEX_STRING &tsname, const Tablespace_options *options)
|
|
: Sql_cmd_tablespace{tsname, options} {}
|
|
|
|
bool Sql_cmd_drop_tablespace::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
if (lock_tablespace_names(thd, m_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
const dd::Tablespace *old_ts_def = nullptr;
|
|
if (dc.acquire(m_tablespace_name.str, &old_ts_def)) {
|
|
return true;
|
|
}
|
|
if (old_ts_def == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_dd_hton(thd, old_ts_def->engine(), m_options->engine_name,
|
|
m_tablespace_name.str, "DROP TABLESPACE", &hton)) {
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton;
|
|
|
|
/*
|
|
Even if the tablespace already exists in the DD we still need to
|
|
validate the name, since we are not allowed to modify
|
|
tablespaces created by the system.
|
|
*/
|
|
if (validate_tablespace_name(DROP_TABLESPACE, m_tablespace_name.str, hton)) {
|
|
return true;
|
|
}
|
|
|
|
bool is_empty;
|
|
if (old_ts_def->is_empty(thd, &is_empty)) {
|
|
return true;
|
|
}
|
|
if (!is_empty) {
|
|
my_error(ER_TABLESPACE_IS_NOT_EMPTY, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
|
|
st_alter_tablespace ts_info{m_tablespace_name.str,
|
|
nullptr,
|
|
DROP_TABLESPACE,
|
|
TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED,
|
|
nullptr,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
int ha_error =
|
|
hton->alter_tablespace(hton, thd, &ts_info, old_ts_def, nullptr);
|
|
if (map_errors(ha_error, "DROP TABLEPSPACE", &ts_info)) {
|
|
if (ha_error == HA_ERR_TABLESPACE_MISSING && !ddl_is_atomic(hton)) {
|
|
/*
|
|
For engines which don't support atomic DDL we might have
|
|
orphan tablespace entries in the data-dictionary which do not
|
|
correspond to tablespaces in SEs. To allow user to do manual
|
|
clean-up we drop tablespace from the dictionary even if SE
|
|
says it is missing (but still report error).
|
|
*/
|
|
/* purecov: begin inspected */
|
|
if (dc.drop(old_ts_def)) {
|
|
return true;
|
|
}
|
|
|
|
Disable_gtid_state_update_guard disabler{thd};
|
|
(void)trans_commit_stmt(thd);
|
|
(void)trans_commit(thd);
|
|
/* purecov: end */
|
|
}
|
|
return true;
|
|
} // if (map_errors
|
|
|
|
if (dc.drop(old_ts_def)) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
DROP for engines which don't support atomic DDL still needs to be
|
|
handled by doing commit right after updating data-dictionary.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true; /* purecov: inspected */
|
|
}
|
|
|
|
if (complete_stmt(thd, hton, [&]() { rollback_on_return.disable(); })) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Mark the tables in the tablespace with ENCRYPTION='y/n'. We only update
|
|
DD objects in memory and the real update of the data-dictionary happens
|
|
later.
|
|
|
|
We also check if the table encryption type conflicts with the schema
|
|
default encryption, and throw error if table_encryption_privilege_check
|
|
is enabled and the user does not own TABLE_ENCRYPTION_ADMIN privilege.
|
|
|
|
@param thd Thread.
|
|
@param ts Reference to tablespace object being altered.
|
|
@param trefs Reference to list of tables in tablespace.
|
|
@param tpl Reference to dd::Table objects to be updated.
|
|
@param requested_encryption Type of encryption requested in ALTER.
|
|
@param table_mdl_reqs[out] Pointer to MDL_request_list containing all MDL
|
|
lock requests.
|
|
|
|
@returns true on error, false on success
|
|
*/
|
|
static bool set_table_encryption_type(THD *thd, const dd::Tablespace &ts,
|
|
dd::Tablespace_table_ref_vec *trefs,
|
|
Table_pair_list *tpl,
|
|
const LEX_STRING &requested_encryption,
|
|
MDL_request_list *table_mdl_reqs) {
|
|
bool is_request_to_encrypt = dd::is_encrypted(requested_encryption);
|
|
|
|
// If the source tablespace encryption type is same as request type.
|
|
dd::String_type source_tablespace_encryption;
|
|
if (ts.options().exists("encryption"))
|
|
(void)ts.options().get("encryption", &source_tablespace_encryption);
|
|
else
|
|
source_tablespace_encryption = "N";
|
|
if (dd::is_encrypted(source_tablespace_encryption) == is_request_to_encrypt)
|
|
return false;
|
|
|
|
// Retrieve table references that use tablespaces.
|
|
if (dd::fetch_tablespace_table_refs(thd, ts, trefs)) {
|
|
return true;
|
|
}
|
|
|
|
// Nothing to update, if there are no tables in it.
|
|
if (trefs->empty()) return false;
|
|
|
|
// Acquire MDL lock on objects.
|
|
std::vector<dd::String_type> schemaset;
|
|
for (auto &tref : *trefs) {
|
|
table_mdl_reqs->push_front(dd::mdl_req(thd, tref, MDL_SHARED_UPGRADABLE));
|
|
schemaset.push_back(tref.m_schema_name);
|
|
}
|
|
for (auto &sname : schemaset) {
|
|
table_mdl_reqs->push_front(dd::mdl_schema_req(thd, sname));
|
|
}
|
|
if (thd->mdl_context.acquire_locks(table_mdl_reqs,
|
|
thd->variables.lock_wait_timeout)) {
|
|
return true;
|
|
}
|
|
|
|
// Acquire DD objects to update.
|
|
auto &dc = *thd->dd_client();
|
|
for (auto &tref : *trefs) {
|
|
// Acquire table object pair
|
|
auto tblp = get_mod_pair<dd::Table>(&dc, tref.m_schema_name, tref.m_name);
|
|
if (tblp.first == nullptr) {
|
|
my_error(ER_UNKNOWN_TABLE, MYF(0), tref.m_name.c_str());
|
|
tpl->clear();
|
|
return true;
|
|
}
|
|
tpl->push_back(tblp);
|
|
}
|
|
|
|
/*
|
|
Check if any table's schema has default encryption is conflicting with
|
|
the requested encryption type.
|
|
*/
|
|
bool is_schema_encryption_conflicting = false;
|
|
for (auto &tref : *trefs) {
|
|
if (tref.m_schema_encryption != is_request_to_encrypt) {
|
|
is_schema_encryption_conflicting = true;
|
|
break;
|
|
}
|
|
}
|
|
if (is_schema_encryption_conflicting) {
|
|
if (opt_table_encryption_privilege_check &&
|
|
check_table_encryption_admin_access(thd)) {
|
|
my_error(is_request_to_encrypt ? ER_TABLESPACE_CANNOT_BE_ENCRYPTED
|
|
: ER_TABLESPACE_CANNOT_BE_DECRYPTED,
|
|
MYF(0));
|
|
return true;
|
|
}
|
|
// We throw warning only when creating a unencrypted table in a schema
|
|
// which has default encryption enabled.
|
|
else if (is_request_to_encrypt == false)
|
|
push_warning(thd, Sql_condition::SL_WARNING,
|
|
WARN_UNENCRYPTED_TABLE_IN_ENCRYPTED_DB,
|
|
ER_THD(thd, WARN_UNENCRYPTED_TABLE_IN_ENCRYPTED_DB));
|
|
}
|
|
|
|
// Update encryption as 'Y/N' for all tables in this tablespace.
|
|
for (auto &tp : *tpl) {
|
|
dd::Properties *table_options = &tp.second->options();
|
|
table_options->set("encrypt_type", (is_request_to_encrypt ? "Y" : "N"));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
We upgrade the table lock to X so that we can update the dictionary
|
|
table metadata with proper ENCRYPTION clause.
|
|
|
|
@param thd Thread.
|
|
@param table_mdl_reqs Pointer to MDL_request_list containing all MDL
|
|
lock requests.
|
|
|
|
@note hton->alter_tablespace() in InnoDB SE doesn't support rollback yet.
|
|
To workaround this issue, which caused assertions in ntest runs
|
|
we make lock upgrade immune to KILL QUERY and low timeout
|
|
values.
|
|
|
|
@returns true on error,
|
|
false on success or when there are no tables in tablespace.
|
|
*/
|
|
|
|
static bool upgrade_lock_for_tables_in_tablespace(
|
|
THD *thd, MDL_request_list *table_mdl_reqs) {
|
|
/*
|
|
The following code gets executed only when there is request to change
|
|
the tablespace ENCRYPTION "and" if there are some tables in the
|
|
tablespace.
|
|
*/
|
|
if (table_mdl_reqs->elements() == 0) return false;
|
|
|
|
// Install KILL QUERY immunizer.
|
|
dd::DD_kill_immunizer m_kill_immunizer(thd);
|
|
|
|
DEBUG_SYNC(thd, "upgrade_lock_for_tables_in_tablespace_kill_point");
|
|
|
|
MDL_request_list::Iterator it(*table_mdl_reqs);
|
|
const size_t req_count = table_mdl_reqs->elements();
|
|
for (size_t i = 0; i < req_count; ++i) {
|
|
MDL_request *r = it++;
|
|
if (r->key.mdl_namespace() == MDL_key::TABLE &&
|
|
thd->mdl_context.upgrade_shared_lock(r->ticket, MDL_EXCLUSIVE,
|
|
LONG_TIMEOUT))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Sql_cmd_alter_tablespace::Sql_cmd_alter_tablespace(
|
|
const LEX_STRING &ts_name, const Tablespace_options *options)
|
|
: Sql_cmd_tablespace{ts_name, options} {}
|
|
|
|
bool Sql_cmd_alter_tablespace::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Check if user has permission to alter tablespace, if encryption type
|
|
provided differ from global 'default_table_encryption' setting.
|
|
*/
|
|
if (m_options->encryption.str && opt_table_encryption_privilege_check &&
|
|
dd::is_encrypted(m_options->encryption) !=
|
|
thd->variables.default_table_encryption &&
|
|
check_table_encryption_admin_access(thd)) {
|
|
my_error(ER_CANNOT_SET_TABLESPACE_ENCRYPTION, MYF(0));
|
|
return true;
|
|
}
|
|
|
|
if (lock_tablespace_names(thd, m_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser(&dc);
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(&dc, m_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_dd_hton(thd, tsmp.first->engine(), m_options->engine_name,
|
|
m_tablespace_name.str,
|
|
"ALTER TABLESPACE ... <tablespace_options>", &hton)) {
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton;
|
|
if (ha_is_storage_engine_disabled(hton)) {
|
|
my_error(ER_DISABLED_STORAGE_ENGINE, MYF(0),
|
|
ha_resolve_storage_engine_name(hton));
|
|
return true;
|
|
}
|
|
|
|
Table_pair_list table_object_pairs;
|
|
dd::Tablespace_table_ref_vec trefs;
|
|
MDL_request_list table_mdl_reqs;
|
|
if (m_options->encryption.str) {
|
|
// The storage engine must support encryption.
|
|
if (dd::is_encrypted(m_options->encryption) &&
|
|
!((hton)->flags & HTON_SUPPORTS_TABLE_ENCRYPTION)) {
|
|
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "ENCRYPTION");
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
If we are on SE supporting ENCRYPTION, then store the option
|
|
and update ENCRYPTION clause of tables in this tablespace.
|
|
*/
|
|
if (hton->flags & HTON_SUPPORTS_TABLE_ENCRYPTION) {
|
|
tsmp.second->options().set("encryption",
|
|
dd::make_string_type(m_options->encryption));
|
|
if (set_table_encryption_type(thd, *tsmp.first, &trefs,
|
|
&table_object_pairs, m_options->encryption,
|
|
&table_mdl_reqs))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Even if the tablespace already exists in the DD we still need to
|
|
validate the name, since we are not allowed to modify
|
|
tablepspaces created by the system.
|
|
FUTURE: Would be better if this was made into a
|
|
property/attribute of dd::Tablespace
|
|
*/
|
|
if (validate_tablespace_name(ALTER_TABLESPACE, m_tablespace_name.str, hton)) {
|
|
return true;
|
|
}
|
|
|
|
st_alter_tablespace ts_info{m_tablespace_name.str,
|
|
nullptr,
|
|
ALTER_TABLESPACE,
|
|
ALTER_TABLESPACE_OPTIONS,
|
|
nullptr,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
if (map_errors(
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second),
|
|
"ALTER TABLESPACE ... <tablespace_options>", &ts_info)) {
|
|
return true;
|
|
}
|
|
|
|
// Upgrade SU to X on table names.
|
|
if (upgrade_lock_for_tables_in_tablespace(thd, &table_mdl_reqs)) return true;
|
|
|
|
// Wait and remove TABLE object from TDC.
|
|
for (auto &tref : trefs) {
|
|
// Lock and release the mutex each time to allow others to access the tdc.
|
|
// Alter tablespace can afford to wait for mutex repeatedly.
|
|
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tref.m_schema_name.c_str(),
|
|
tref.m_name.c_str(), false /*has_lock*/);
|
|
}
|
|
|
|
if (dc.update(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
// Persist the modified object changes in DD.
|
|
if (!table_object_pairs.empty()) {
|
|
for (auto &tp : table_object_pairs) {
|
|
if (dc.update(tp.second)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Per convention only engines supporting atomic DDL are allowed to
|
|
modify data-dictionary objects in handler::create() and other
|
|
similar calls. However, DROP and ALTER TABLESPACE for engines which
|
|
don't support atomic DDL still needs to be handled by doing commit
|
|
right after updating data-dictionary.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true;
|
|
}
|
|
|
|
if (complete_stmt(thd, hton, [&]() { rollback_on_return.disable(); })) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Sql_cmd_alter_tablespace_add_datafile::Sql_cmd_alter_tablespace_add_datafile(
|
|
const LEX_STRING &tsname, const LEX_STRING &dfname,
|
|
const Tablespace_options *options)
|
|
: Sql_cmd_tablespace{tsname, options}, m_datafile_name(dfname) {}
|
|
|
|
bool Sql_cmd_alter_tablespace_add_datafile::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
if (lock_tablespace_names(thd, m_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(&dc, m_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
|
|
if (m_datafile_name.length > FN_REFLEN) {
|
|
my_error(ER_PATH_LENGTH, MYF(0), "DATAFILE");
|
|
return true;
|
|
}
|
|
|
|
dd::Tablespace_file *tsf_obj = tsmp.second->add_file();
|
|
tsf_obj->set_filename(
|
|
dd::String_type{m_datafile_name.str, m_datafile_name.length});
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_dd_hton(thd, tsmp.first->engine(), m_options->engine_name,
|
|
m_tablespace_name.str, "ALTER TABLESPACE ... ADD DATAFILE",
|
|
&hton)) {
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton;
|
|
if (ha_is_storage_engine_disabled(hton)) {
|
|
my_error(ER_DISABLED_STORAGE_ENGINE, MYF(0),
|
|
ha_resolve_storage_engine_name(hton));
|
|
return true;
|
|
}
|
|
/*
|
|
Even if the tablespace already exists in the DD we still need to
|
|
validate the name, since we are not allowed to modify
|
|
tablespaces created by the system.
|
|
|
|
FUTURE: Would be better if this was made into a
|
|
property/attribute of dd::Tablespace
|
|
*/
|
|
if (validate_tablespace_name(ALTER_TABLESPACE, m_tablespace_name.str, hton)) {
|
|
return true;
|
|
}
|
|
|
|
st_alter_tablespace ts_info{m_tablespace_name.str,
|
|
nullptr,
|
|
ALTER_TABLESPACE,
|
|
ALTER_TABLESPACE_ADD_FILE,
|
|
m_datafile_name.str,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
if (map_errors(
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second),
|
|
"ALTER TABLESPACE ... ADD DATAFILE", &ts_info)) {
|
|
return true;
|
|
}
|
|
|
|
if (dc.update(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
ALTER TABLESPACE for engines which don't support atomic DDL still
|
|
needs to be handled by doing commit right after updating
|
|
data-dictionary.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true;
|
|
}
|
|
|
|
if (complete_stmt(thd, hton, [&]() { rollback_on_return.disable(); })) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Sql_cmd_alter_tablespace_drop_datafile::Sql_cmd_alter_tablespace_drop_datafile(
|
|
const LEX_STRING &tsname, const LEX_STRING &dfname,
|
|
const Tablespace_options *options)
|
|
: Sql_cmd_tablespace{tsname, options}, m_datafile_name(dfname) {}
|
|
|
|
bool Sql_cmd_alter_tablespace_drop_datafile::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
if (lock_tablespace_names(thd, m_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(&dc, m_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
|
|
if (tsmp.second->remove_file(
|
|
dd::String_type{m_datafile_name.str, m_datafile_name.length})) {
|
|
my_error(ER_MISSING_TABLESPACE_FILE, MYF(0), m_tablespace_name.str,
|
|
m_datafile_name.str);
|
|
return true;
|
|
}
|
|
handlerton *hton = nullptr;
|
|
if (get_dd_hton(thd, tsmp.first->engine(), m_options->engine_name,
|
|
m_tablespace_name.str, "ALTER TABLESPACE ... DROP DATAFILE",
|
|
&hton)) {
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton;
|
|
if (ha_is_storage_engine_disabled(hton)) {
|
|
my_error(ER_DISABLED_STORAGE_ENGINE, MYF(0),
|
|
ha_resolve_storage_engine_name(hton));
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Even if the tablespace already exists in the DD we still need to
|
|
validate the name, since we are not allowed to modify tablespaces
|
|
created by the system.
|
|
*/
|
|
if (validate_tablespace_name(ALTER_TABLESPACE, m_tablespace_name.str, hton)) {
|
|
return true;
|
|
}
|
|
st_alter_tablespace ts_info{m_tablespace_name.str,
|
|
nullptr,
|
|
ALTER_TABLESPACE,
|
|
ALTER_TABLESPACE_DROP_FILE,
|
|
m_datafile_name.str,
|
|
nullptr,
|
|
*m_options};
|
|
if (map_errors(
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second),
|
|
"ALTER TABLESPACE ... DROP DATAFILE", &ts_info)) {
|
|
return true;
|
|
}
|
|
|
|
if (dc.update(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
ALTER TABLESPACE for engines which don't support atomic
|
|
DDL still needs to be handled by doing commit right after updating
|
|
data-dictionary.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true; /* purecov: inspected */
|
|
}
|
|
|
|
if (complete_stmt(thd, hton, [&]() { rollback_on_return.disable(); })) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Sql_cmd_alter_tablespace_rename::Sql_cmd_alter_tablespace_rename(
|
|
const LEX_STRING &old_name, const LEX_STRING &new_name)
|
|
: Sql_cmd_tablespace{old_name, nullptr}, m_new_name(new_name) {}
|
|
|
|
bool Sql_cmd_alter_tablespace_rename::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
// Can't check the name in SE, yet. Need to acquire Tablespace
|
|
// object first, so that we can get the engine name.
|
|
|
|
// Lock both tablespace names in one go
|
|
if (lock_tablespace_names(thd, m_tablespace_name, m_new_name)) {
|
|
return true;
|
|
}
|
|
dd::cache::Dictionary_client *dc = thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser(dc);
|
|
|
|
dd::String_type old_name = dd::make_string_type(m_tablespace_name);
|
|
dd::String_type new_name = dd::make_string_type(m_new_name);
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(dc, m_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), m_tablespace_name.str);
|
|
return true;
|
|
}
|
|
tsmp.second->set_name(new_name);
|
|
|
|
const dd::Tablespace *existing_new_ts_def = nullptr;
|
|
if (dc->acquire(new_name, &existing_new_ts_def)) {
|
|
return true;
|
|
}
|
|
if (existing_new_ts_def != nullptr) {
|
|
my_error(ER_TABLESPACE_EXISTS, MYF(0), new_name.c_str());
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_dd_hton(thd, tsmp.first->engine(), {nullptr, 0},
|
|
m_tablespace_name.str, "ALTER TABLESPACE ... RENAME TO",
|
|
&hton)) {
|
|
return true;
|
|
}
|
|
if (ha_is_storage_engine_disabled(hton)) {
|
|
my_error(ER_DISABLED_STORAGE_ENGINE, MYF(0),
|
|
ha_resolve_storage_engine_name(hton));
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton;
|
|
|
|
/*
|
|
Now with the hton, we need to validate BOTH the old and the new
|
|
name - since we are not allowed to rename reserved names
|
|
FUTURE - Could be a property/attribute of dd::Tablespace
|
|
*/
|
|
if (validate_tablespace_name(DROP_TABLESPACE, m_tablespace_name.str, hton)) {
|
|
return true;
|
|
}
|
|
|
|
// Also validate the new tablespace name in the SE
|
|
if (validate_tablespace_name(CREATE_TABLESPACE, m_new_name.str, hton)) {
|
|
return true;
|
|
}
|
|
|
|
dd::Tablespace_table_ref_vec trefs;
|
|
if (dd::fetch_tablespace_table_refs(thd, *tsmp.first, &trefs)) {
|
|
return true;
|
|
}
|
|
MDL_request_list table_reqs;
|
|
for (auto &tref : trefs) {
|
|
table_reqs.push_front(dd::mdl_req(thd, tref, MDL_EXCLUSIVE));
|
|
}
|
|
|
|
if (thd->mdl_context.acquire_locks(&table_reqs,
|
|
thd->variables.lock_wait_timeout)) {
|
|
return true;
|
|
}
|
|
|
|
for (auto &tref : trefs) {
|
|
// Lock and release the mutex each time to allow others to access the tdc.
|
|
// Rename tablespace can afford to wait for mutex repeatedly.
|
|
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tref.m_schema_name.c_str(),
|
|
tref.m_name.c_str(), false /*has_lock*/);
|
|
}
|
|
st_alter_tablespace ts_info{
|
|
m_tablespace_name.str, nullptr, ALTER_TABLESPACE,
|
|
ALTER_TABLESPACE_RENAME, nullptr, nullptr,
|
|
Tablespace_options{}};
|
|
if (map_errors(
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second),
|
|
"ALTER TABLESPACE ... RENAME TO", &ts_info)) {
|
|
return true;
|
|
}
|
|
|
|
// TODO WL#9536: Until crash-safe ddl is implemented we need to do
|
|
// manual compensation in case of rollback
|
|
auto compensate_grd = dd::sdi_utils::make_guard(hton, [&](handlerton *hton) {
|
|
std::unique_ptr<dd::Tablespace> comp{tsmp.first->clone()};
|
|
(void)hton->alter_tablespace(hton, thd, &ts_info, tsmp.second, comp.get());
|
|
});
|
|
|
|
DBUG_EXECUTE_IF("tspr_post_se", {
|
|
my_error(ER_UNKNOWN_ERROR, MYF(0));
|
|
return true;
|
|
});
|
|
|
|
if (dc->update(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
DBUG_EXECUTE_IF("tspr_post_update", {
|
|
my_error(ER_UNKNOWN_ERROR, MYF(0));
|
|
return true;
|
|
});
|
|
|
|
/*
|
|
ALTER TABLESPACE for engines which don't support atomic
|
|
DDL still needs to be handled by doing commit right after updating
|
|
data-dictionary.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true;
|
|
}
|
|
|
|
if (!ddl_is_atomic(hton)) {
|
|
compensate_grd.release();
|
|
}
|
|
|
|
DBUG_EXECUTE_IF("tspr_post_intcmt", {
|
|
my_error(ER_UNKNOWN_ERROR, MYF(0));
|
|
return true;
|
|
});
|
|
|
|
if (complete_stmt(thd, hton, [&]() {
|
|
rollback_on_return.disable();
|
|
compensate_grd.release();
|
|
})) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Sql_cmd_create_undo_tablespace::Sql_cmd_create_undo_tablespace(
|
|
ts_command_type cmd_type, const LEX_STRING &utsname,
|
|
const LEX_STRING &dfname, const Tablespace_options *options)
|
|
: m_cmd(cmd_type),
|
|
m_undo_tablespace_name(utsname),
|
|
m_datafile_name(dfname),
|
|
m_options(options) {}
|
|
|
|
bool Sql_cmd_create_undo_tablespace::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_stmt_hton(thd, m_options->engine_name, m_undo_tablespace_name.str,
|
|
"CREATE UNDO TABLESPACE", &hton)) {
|
|
return true;
|
|
}
|
|
|
|
rollback_on_return.m_hton = hton; // Allow rollback to call hton->post_ddl
|
|
|
|
// Check the tablespace name and acquire an MDL X lock.
|
|
if (validate_tablespace_name(CREATE_UNDO_TABLESPACE,
|
|
m_undo_tablespace_name.str, hton) ||
|
|
lock_tablespace_names(thd, m_undo_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
// Check if same tablespace already exists.
|
|
auto tsn = dd::make_string_type(m_undo_tablespace_name);
|
|
const dd::Tablespace *ts = nullptr;
|
|
|
|
if (dc.acquire(tsn, &ts)) {
|
|
return true;
|
|
}
|
|
|
|
if (ts != nullptr) {
|
|
my_error(ER_TABLESPACE_EXISTS, MYF(0), tsn.c_str());
|
|
return true;
|
|
}
|
|
|
|
// Create new tablespace.
|
|
std::unique_ptr<dd::Tablespace> tablespace(
|
|
dd::create_object<dd::Tablespace>());
|
|
|
|
// Set tablespace name
|
|
tablespace->set_name(tsn);
|
|
|
|
// Engine type
|
|
tablespace->set_engine(ha_resolve_storage_engine_name(hton));
|
|
|
|
if (m_datafile_name.length > FN_REFLEN) {
|
|
my_error(ER_PATH_LENGTH, MYF(0), "DATAFILE");
|
|
return true;
|
|
}
|
|
|
|
// ENCRYPTION clause is not supported for undo tablespace.
|
|
if (hton->flags & HTON_SUPPORTS_TABLE_ENCRYPTION) {
|
|
tablespace->options().set("encryption", "N");
|
|
}
|
|
|
|
// Add datafile
|
|
tablespace->add_file()->set_filename(dd::make_string_type(m_datafile_name));
|
|
|
|
// Write changes to dictionary.
|
|
if (dc.store(tablespace.get())) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Commit after creation of tablespace in the data-dictionary for
|
|
storage engines which don't support atomic DDL. We do this to
|
|
avoid being left with tablespace in SE but not in data-dictionary
|
|
in case of crash. Indeed, in this case, we can end-up with tablespace
|
|
present in the data-dictionary and not present in SE. But this can be
|
|
easily fixed by doing DROP TABLESPACE.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true;
|
|
}
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(&dc, m_undo_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0),
|
|
m_undo_tablespace_name.str);
|
|
return true;
|
|
}
|
|
st_alter_tablespace ts_info{m_undo_tablespace_name.str,
|
|
nullptr,
|
|
CREATE_UNDO_TABLESPACE,
|
|
TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED,
|
|
m_datafile_name.str,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
int ha_error =
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second);
|
|
if (map_errors(ha_error, "CREATE UNDO TABLEPSPACE", &ts_info)) {
|
|
if (!ddl_is_atomic(hton)) {
|
|
/*
|
|
For engines which don't support atomic DDL addition of tablespace to
|
|
data-dictionary has been committed already so we need to revert it.
|
|
*/
|
|
if (dc.drop(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
Disable_gtid_state_update_guard disabler{thd};
|
|
(void)trans_commit_stmt(thd);
|
|
(void)trans_commit(thd);
|
|
}
|
|
return true;
|
|
} // if (map_errors
|
|
|
|
/*
|
|
Per convention only engines supporting atomic DDL are allowed to
|
|
modify data-dictionary objects in handler::create() and other
|
|
similar calls.
|
|
*/
|
|
if (ddl_is_atomic(hton) && dc.update(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
if (complete_stmt(
|
|
thd, hton, [&]() { rollback_on_return.disable(); }, true, true)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum_sql_command Sql_cmd_create_undo_tablespace::sql_command_code() const {
|
|
return SQLCOM_ALTER_TABLESPACE;
|
|
}
|
|
|
|
Sql_cmd_alter_undo_tablespace::Sql_cmd_alter_undo_tablespace(
|
|
ts_command_type cmd_type, const LEX_STRING &utsname,
|
|
const LEX_STRING &dfname, const Tablespace_options *options,
|
|
ts_alter_tablespace_type at_type)
|
|
: m_cmd(cmd_type),
|
|
m_undo_tablespace_name(utsname),
|
|
m_datafile_name(dfname),
|
|
m_at_type(at_type),
|
|
m_options(options) {
|
|
// These only at_type values that the syntax currently accepts
|
|
DBUG_ASSERT(at_type == TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED ||
|
|
at_type == ALTER_UNDO_TABLESPACE_SET_ACTIVE ||
|
|
at_type == ALTER_UNDO_TABLESPACE_SET_INACTIVE);
|
|
}
|
|
|
|
bool Sql_cmd_alter_undo_tablespace::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_stmt_hton(thd, m_options->engine_name, m_undo_tablespace_name.str,
|
|
"ALTER UNDO TABLESPACE", &hton)) {
|
|
return true;
|
|
}
|
|
|
|
// Check the tablespace name and acquire an MDL X lock.
|
|
if (validate_tablespace_name(ALTER_UNDO_TABLESPACE,
|
|
m_undo_tablespace_name.str, hton) ||
|
|
lock_tablespace_names(thd, m_undo_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
// Get the existing dd::Tablespace for this tablespace name.
|
|
auto tsn = dd::make_string_type(m_undo_tablespace_name);
|
|
const dd::Tablespace *ts = nullptr;
|
|
|
|
if (dc.acquire(tsn, &ts)) {
|
|
return true;
|
|
}
|
|
|
|
// Must already exist
|
|
if (ts == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), tsn.c_str());
|
|
return true;
|
|
}
|
|
|
|
auto tsmp = get_mod_pair<dd::Tablespace>(&dc, m_undo_tablespace_name.str);
|
|
if (tsmp.first == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0),
|
|
m_undo_tablespace_name.str);
|
|
return true;
|
|
}
|
|
st_alter_tablespace ts_info{m_undo_tablespace_name.str,
|
|
nullptr,
|
|
ALTER_UNDO_TABLESPACE,
|
|
m_at_type,
|
|
nullptr,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
int ha_error =
|
|
hton->alter_tablespace(hton, thd, &ts_info, tsmp.first, tsmp.second);
|
|
if (map_errors(ha_error, "ALTER UNDO TABLEPSPACE", &ts_info)) {
|
|
if (!ddl_is_atomic(hton)) {
|
|
Disable_gtid_state_update_guard disabler{thd};
|
|
(void)trans_commit_stmt(thd);
|
|
(void)trans_commit(thd);
|
|
}
|
|
return true;
|
|
} // if (map_errors
|
|
|
|
/*
|
|
Per convention only engines supporting atomic DDL are allowed to
|
|
modify data-dictionary objects in handler::create() and other
|
|
similar calls.
|
|
*/
|
|
if (ddl_is_atomic(hton) && dc.update(tsmp.second)) {
|
|
return true;
|
|
}
|
|
|
|
if (complete_stmt(
|
|
thd, hton, [&]() { rollback_on_return.disable(); }, true, true)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum_sql_command Sql_cmd_alter_undo_tablespace::sql_command_code() const {
|
|
return SQLCOM_ALTER_TABLESPACE;
|
|
}
|
|
|
|
Sql_cmd_drop_undo_tablespace::Sql_cmd_drop_undo_tablespace(
|
|
ts_command_type cmd_type, const LEX_STRING &utsname,
|
|
const LEX_STRING &dfname, const Tablespace_options *options)
|
|
: m_cmd(cmd_type),
|
|
m_undo_tablespace_name(utsname),
|
|
m_datafile_name(dfname),
|
|
m_options(options) {}
|
|
|
|
bool Sql_cmd_drop_undo_tablespace::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_stmt_hton(thd, m_options->engine_name, m_undo_tablespace_name.str,
|
|
"DROP UNDO TABLESPACE", &hton)) {
|
|
return true;
|
|
}
|
|
rollback_on_return.m_hton = hton;
|
|
|
|
/*
|
|
Check the tablespace name and acquire an MDL X lock.
|
|
Even if the tablespace already exists in the DD we still need to
|
|
validate the name, since we are not allowed to modify
|
|
tablespaces created by the system.
|
|
*/
|
|
if (validate_tablespace_name(DROP_UNDO_TABLESPACE, m_undo_tablespace_name.str,
|
|
hton) ||
|
|
lock_tablespace_names(thd, m_undo_tablespace_name)) {
|
|
return true;
|
|
}
|
|
|
|
auto &dc = *thd->dd_client();
|
|
dd::cache::Dictionary_client::Auto_releaser releaser{&dc};
|
|
|
|
// Get the existing dd::Tablespace for this tablespace name.
|
|
auto tsn = dd::make_string_type(m_undo_tablespace_name);
|
|
const dd::Tablespace *ts = nullptr;
|
|
|
|
if (dc.acquire(tsn, &ts)) {
|
|
return true;
|
|
}
|
|
|
|
if (ts == nullptr) {
|
|
my_error(ER_TABLESPACE_MISSING_WITH_NAME, MYF(0), tsn.c_str());
|
|
return true;
|
|
}
|
|
|
|
st_alter_tablespace ts_info{m_undo_tablespace_name.str,
|
|
nullptr,
|
|
DROP_UNDO_TABLESPACE,
|
|
TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED,
|
|
nullptr,
|
|
nullptr,
|
|
*m_options};
|
|
|
|
int ha_error = hton->alter_tablespace(hton, thd, &ts_info, ts, nullptr);
|
|
if (map_errors(ha_error, "DROP UNDO TABLEPSPACE", &ts_info)) {
|
|
if (!ddl_is_atomic(hton)) {
|
|
/*
|
|
For engines which don't support atomic DDL we might have
|
|
orphan tablespace entries in the data-dictionary which do not
|
|
correspond to tablespaces in SEs. To allow user to do manual
|
|
clean-up we drop tablespace from the dictionary even if SE
|
|
says it is missing (but still report error).
|
|
*/
|
|
if (dc.drop(ts)) {
|
|
return true;
|
|
}
|
|
|
|
Disable_gtid_state_update_guard disabler{thd};
|
|
(void)trans_commit_stmt(thd);
|
|
(void)trans_commit(thd);
|
|
}
|
|
return true;
|
|
} // if (map_errors
|
|
|
|
if (dc.drop(ts)) {
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
DROP for engines which don't support atomic DDL still needs to be
|
|
handled by doing commit right after updating data-dictionary.
|
|
*/
|
|
if (intermediate_commit_unless_atomic_ddl(thd, hton)) {
|
|
return true; /* purecov: inspected */
|
|
}
|
|
|
|
if (complete_stmt(
|
|
thd, hton, [&]() { rollback_on_return.disable(); }, true, true)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum_sql_command Sql_cmd_drop_undo_tablespace::sql_command_code() const {
|
|
return SQLCOM_ALTER_TABLESPACE;
|
|
}
|
|
|
|
Sql_cmd_logfile_group::Sql_cmd_logfile_group(
|
|
ts_command_type cmd_type, const LEX_STRING &logfile_group_name,
|
|
const Tablespace_options *options, const LEX_STRING &undofile_name)
|
|
: m_cmd(cmd_type),
|
|
m_logfile_group_name(logfile_group_name),
|
|
m_undofile_name(undofile_name),
|
|
m_options(options) {}
|
|
|
|
bool Sql_cmd_logfile_group::execute(THD *thd) {
|
|
Rollback_guard rollback_on_return{thd};
|
|
|
|
if (check_global_access(thd, CREATE_TABLESPACE_ACL)) {
|
|
return true;
|
|
}
|
|
|
|
handlerton *hton = nullptr;
|
|
if (get_stmt_hton(thd, m_options->engine_name, m_logfile_group_name.str,
|
|
"CREATE/ALTER/DROP LOGFILE GROUP", &hton)) {
|
|
return true;
|
|
}
|
|
|
|
// Lock tablespace(since the logfile group is stored as such)
|
|
if (lock_tablespace_names(thd, m_logfile_group_name)) {
|
|
return true;
|
|
}
|
|
|
|
st_alter_tablespace ts_info{nullptr, m_logfile_group_name.str,
|
|
m_cmd, TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED,
|
|
nullptr, m_undofile_name.str,
|
|
*m_options};
|
|
|
|
if (map_errors(hton->alter_tablespace(hton, thd, &ts_info, nullptr, nullptr),
|
|
"CREATE/ALTER/DROP LOGFILE GROUP", &ts_info)) {
|
|
return true;
|
|
}
|
|
|
|
// The CREATE/ALTER/DROP LOGFILE GROUP command is atomic in the SE
|
|
// but does not modify the DD and thus there is no active transaction
|
|
// -> turn off "using_trans"
|
|
const bool using_trans = false;
|
|
if (complete_stmt(
|
|
thd, hton, [&]() { rollback_on_return.disable(); }, using_trans)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
enum_sql_command Sql_cmd_logfile_group::sql_command_code() const {
|
|
return SQLCOM_ALTER_TABLESPACE; /* purecov: inspected */
|
|
}
|
|
|