mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
MDEV-18295 IMPORT TABLESPACE fails with instant-altered tables
When importing a tablespace, we must initialize dummy DEFAULT NULL values for any instantly added columns in order to avoid a debug assertion failure when PageConverter::update_records() invokes rec_get_offsets(). Finally, when the operation completes, we must evict and reload the table definition, so that the correct default values for instantly added columns will be loaded. ha_innobase::discard_or_import_tablespace(): On successful IMPORT TABLESPACE, evict and reload the table definition, so that btr_cur_instant_init() will load the correct metadata. PageConverter::update_index_page(): Fill in dummy DEFAULT NULL values for instantly added columns. These will be replaced upon the completion of the operation by evicting and reloading the metadata. row_discard_tablespace(): Invoke dict_table_t::remove_instant(). After DISCARD TABLESPACE, the table is no longer in "instant ALTER" format, because there is no data file attached.
This commit is contained in:
parent
4eae9eeccc
commit
4e7ee166a9
5 changed files with 160 additions and 31 deletions
46
mysql-test/suite/innodb/r/instant_alter_import.result
Normal file
46
mysql-test/suite/innodb/r/instant_alter_import.result
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
set default_storage_engine=innodb;
|
||||||
|
#
|
||||||
|
# MDEV-18295 IMPORT TABLESPACE fails with instant-altered tables
|
||||||
|
#
|
||||||
|
create table t2 (x int, z int default 41);
|
||||||
|
alter table t2 discard tablespace;
|
||||||
|
create table t1 (x int);
|
||||||
|
insert into t1 values (1);
|
||||||
|
alter table t1 add z int default 42, algorithm instant;
|
||||||
|
select * from t1;
|
||||||
|
x z
|
||||||
|
1 42
|
||||||
|
flush tables t1 for export;
|
||||||
|
unlock tables;
|
||||||
|
# The metadata has to be updated to instant ADD COLUMN.
|
||||||
|
alter table t2 import tablespace;
|
||||||
|
select * from t2;
|
||||||
|
x z
|
||||||
|
1 42
|
||||||
|
insert into t2 set x=2;
|
||||||
|
select * from t2;
|
||||||
|
x z
|
||||||
|
1 42
|
||||||
|
2 41
|
||||||
|
alter table t1 discard tablespace;
|
||||||
|
flush tables t2 for export;
|
||||||
|
unlock tables;
|
||||||
|
# Both the metadata and the data file used instant ADD COLUMN.
|
||||||
|
alter table t1 import tablespace;
|
||||||
|
select * from t1;
|
||||||
|
x z
|
||||||
|
1 42
|
||||||
|
2 41
|
||||||
|
drop table t2;
|
||||||
|
create table t2 select * from t1;
|
||||||
|
alter table t1 discard tablespace;
|
||||||
|
flush tables t2 for export;
|
||||||
|
unlock tables;
|
||||||
|
# The instant ADD COLUMN has to be removed from the metadata.
|
||||||
|
alter table t1 import tablespace;
|
||||||
|
select * from t1;
|
||||||
|
x z
|
||||||
|
1 42
|
||||||
|
2 41
|
||||||
|
drop table t2;
|
||||||
|
drop table t1;
|
52
mysql-test/suite/innodb/t/instant_alter_import.test
Normal file
52
mysql-test/suite/innodb/t/instant_alter_import.test
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
set default_storage_engine=innodb;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-18295 IMPORT TABLESPACE fails with instant-altered tables
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
create table t2 (x int, z int default 41);
|
||||||
|
alter table t2 discard tablespace;
|
||||||
|
|
||||||
|
create table t1 (x int);
|
||||||
|
insert into t1 values (1);
|
||||||
|
alter table t1 add z int default 42, algorithm instant;
|
||||||
|
select * from t1;
|
||||||
|
flush tables t1 for export;
|
||||||
|
--let $MYSQLD_DATADIR= `select @@datadir`
|
||||||
|
--move_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
|
||||||
|
unlock tables;
|
||||||
|
|
||||||
|
--echo # The metadata has to be updated to instant ADD COLUMN.
|
||||||
|
alter table t2 import tablespace;
|
||||||
|
|
||||||
|
select * from t2;
|
||||||
|
insert into t2 set x=2;
|
||||||
|
select * from t2;
|
||||||
|
|
||||||
|
alter table t1 discard tablespace;
|
||||||
|
flush tables t2 for export;
|
||||||
|
--move_file $MYSQLD_DATADIR/test/t2.cfg $MYSQLD_DATADIR/test/t1.cfg
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t1.ibd
|
||||||
|
unlock tables;
|
||||||
|
|
||||||
|
--echo # Both the metadata and the data file used instant ADD COLUMN.
|
||||||
|
alter table t1 import tablespace;
|
||||||
|
select * from t1;
|
||||||
|
|
||||||
|
drop table t2;
|
||||||
|
create table t2 select * from t1;
|
||||||
|
|
||||||
|
alter table t1 discard tablespace;
|
||||||
|
flush tables t2 for export;
|
||||||
|
--move_file $MYSQLD_DATADIR/test/t2.cfg $MYSQLD_DATADIR/test/t1.cfg
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t1.ibd
|
||||||
|
unlock tables;
|
||||||
|
|
||||||
|
--echo # The instant ADD COLUMN has to be removed from the metadata.
|
||||||
|
alter table t1 import tablespace;
|
||||||
|
select * from t1;
|
||||||
|
|
||||||
|
drop table t2;
|
||||||
|
drop table t1;
|
|
@ -4,7 +4,7 @@ Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved.
|
||||||
Copyright (c) 2008, 2009 Google Inc.
|
Copyright (c) 2008, 2009 Google Inc.
|
||||||
Copyright (c) 2009, Percona Inc.
|
Copyright (c) 2009, Percona Inc.
|
||||||
Copyright (c) 2012, Facebook Inc.
|
Copyright (c) 2012, Facebook Inc.
|
||||||
Copyright (c) 2013, 2018, MariaDB Corporation.
|
Copyright (c) 2013, 2019, MariaDB Corporation.
|
||||||
|
|
||||||
Portions of this file contain modifications contributed and copyrighted by
|
Portions of this file contain modifications contributed and copyrighted by
|
||||||
Google, Inc. Those modifications are gratefully acknowledged and are described
|
Google, Inc. Those modifications are gratefully acknowledged and are described
|
||||||
|
@ -12682,10 +12682,7 @@ ha_innobase::discard_or_import_tablespace(
|
||||||
DBUG_RETURN(HA_ERR_TABLE_READONLY);
|
DBUG_RETURN(HA_ERR_TABLE_READONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_table_t* dict_table = m_prebuilt->table;
|
if (m_prebuilt->table->is_temporary()) {
|
||||||
|
|
||||||
if (dict_table->is_temporary()) {
|
|
||||||
|
|
||||||
ib_senderrf(
|
ib_senderrf(
|
||||||
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
|
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
|
||||||
ER_CANNOT_DISCARD_TEMPORARY_TABLE);
|
ER_CANNOT_DISCARD_TEMPORARY_TABLE);
|
||||||
|
@ -12693,11 +12690,11 @@ ha_innobase::discard_or_import_tablespace(
|
||||||
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
|
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dict_table->space == fil_system.sys_space) {
|
if (m_prebuilt->table->space == fil_system.sys_space) {
|
||||||
ib_senderrf(
|
ib_senderrf(
|
||||||
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
|
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
|
||||||
ER_TABLE_IN_SYSTEM_TABLESPACE,
|
ER_TABLE_IN_SYSTEM_TABLESPACE,
|
||||||
dict_table->name.m_name);
|
m_prebuilt->table->name.m_name);
|
||||||
|
|
||||||
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
|
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
|
||||||
}
|
}
|
||||||
|
@ -12706,7 +12703,7 @@ ha_innobase::discard_or_import_tablespace(
|
||||||
|
|
||||||
/* Obtain an exclusive lock on the table. */
|
/* Obtain an exclusive lock on the table. */
|
||||||
dberr_t err = row_mysql_lock_table(
|
dberr_t err = row_mysql_lock_table(
|
||||||
m_prebuilt->trx, dict_table, LOCK_X,
|
m_prebuilt->trx, m_prebuilt->table, LOCK_X,
|
||||||
discard ? "setting table lock for DISCARD TABLESPACE"
|
discard ? "setting table lock for DISCARD TABLESPACE"
|
||||||
: "setting table lock for IMPORT TABLESPACE");
|
: "setting table lock for IMPORT TABLESPACE");
|
||||||
|
|
||||||
|
@ -12719,32 +12716,32 @@ ha_innobase::discard_or_import_tablespace(
|
||||||
user may want to set the DISCARD flag in order to IMPORT
|
user may want to set the DISCARD flag in order to IMPORT
|
||||||
a new tablespace. */
|
a new tablespace. */
|
||||||
|
|
||||||
if (!dict_table->is_readable()) {
|
if (!m_prebuilt->table->is_readable()) {
|
||||||
ib_senderrf(
|
ib_senderrf(
|
||||||
m_prebuilt->trx->mysql_thd,
|
m_prebuilt->trx->mysql_thd,
|
||||||
IB_LOG_LEVEL_WARN, ER_TABLESPACE_MISSING,
|
IB_LOG_LEVEL_WARN, ER_TABLESPACE_MISSING,
|
||||||
dict_table->name.m_name);
|
m_prebuilt->table->name.m_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = row_discard_tablespace_for_mysql(
|
err = row_discard_tablespace_for_mysql(
|
||||||
dict_table->name.m_name, m_prebuilt->trx);
|
m_prebuilt->table->name.m_name, m_prebuilt->trx);
|
||||||
|
|
||||||
} else if (dict_table->is_readable()) {
|
} else if (m_prebuilt->table->is_readable()) {
|
||||||
/* Commit the transaction in order to
|
/* Commit the transaction in order to
|
||||||
release the table lock. */
|
release the table lock. */
|
||||||
trx_commit_for_mysql(m_prebuilt->trx);
|
trx_commit_for_mysql(m_prebuilt->trx);
|
||||||
|
|
||||||
ib::error() << "Unable to import tablespace "
|
ib::error() << "Unable to import tablespace "
|
||||||
<< dict_table->name << " because it already"
|
<< m_prebuilt->table->name << " because it already"
|
||||||
" exists. Please DISCARD the tablespace"
|
" exists. Please DISCARD the tablespace"
|
||||||
" before IMPORT.";
|
" before IMPORT.";
|
||||||
ib_senderrf(
|
ib_senderrf(
|
||||||
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
|
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
|
||||||
ER_TABLESPACE_EXISTS, dict_table->name.m_name);
|
ER_TABLESPACE_EXISTS, m_prebuilt->table->name.m_name);
|
||||||
|
|
||||||
DBUG_RETURN(HA_ERR_TABLE_EXIST);
|
DBUG_RETURN(HA_ERR_TABLE_EXIST);
|
||||||
} else {
|
} else {
|
||||||
err = row_import_for_mysql(dict_table, m_prebuilt);
|
err = row_import_for_mysql(m_prebuilt->table, m_prebuilt);
|
||||||
|
|
||||||
if (err == DB_SUCCESS) {
|
if (err == DB_SUCCESS) {
|
||||||
|
|
||||||
|
@ -12760,12 +12757,35 @@ ha_innobase::discard_or_import_tablespace(
|
||||||
/* Commit the transaction in order to release the table lock. */
|
/* Commit the transaction in order to release the table lock. */
|
||||||
trx_commit_for_mysql(m_prebuilt->trx);
|
trx_commit_for_mysql(m_prebuilt->trx);
|
||||||
|
|
||||||
if (err == DB_SUCCESS && !discard
|
if (discard || err != DB_SUCCESS) {
|
||||||
&& dict_stats_is_persistent_enabled(dict_table)) {
|
DBUG_RETURN(convert_error_code_to_mysql(
|
||||||
|
err, m_prebuilt->table->flags, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Evict and reload the table definition in order to invoke
|
||||||
|
btr_cur_instant_init(). */
|
||||||
|
table_id_t id = m_prebuilt->table->id;
|
||||||
|
ut_ad(id);
|
||||||
|
mutex_enter(&dict_sys->mutex);
|
||||||
|
dict_table_close(m_prebuilt->table, TRUE, FALSE);
|
||||||
|
dict_table_remove_from_cache(m_prebuilt->table);
|
||||||
|
m_prebuilt->table = dict_table_open_on_id(id, TRUE,
|
||||||
|
DICT_TABLE_OP_NORMAL);
|
||||||
|
mutex_exit(&dict_sys->mutex);
|
||||||
|
if (!m_prebuilt->table) {
|
||||||
|
err = DB_TABLE_NOT_FOUND;
|
||||||
|
} else {
|
||||||
|
if (const Field* ai = table->found_next_number_field) {
|
||||||
|
initialize_auto_increment(m_prebuilt->table, ai);
|
||||||
|
}
|
||||||
|
dict_stats_init(m_prebuilt->table);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict_stats_is_persistent_enabled(m_prebuilt->table)) {
|
||||||
dberr_t ret;
|
dberr_t ret;
|
||||||
|
|
||||||
/* Adjust the persistent statistics. */
|
/* Adjust the persistent statistics. */
|
||||||
ret = dict_stats_update(dict_table,
|
ret = dict_stats_update(m_prebuilt->table,
|
||||||
DICT_STATS_RECALC_PERSISTENT);
|
DICT_STATS_RECALC_PERSISTENT);
|
||||||
|
|
||||||
if (ret != DB_SUCCESS) {
|
if (ret != DB_SUCCESS) {
|
||||||
|
@ -12775,11 +12795,12 @@ ha_innobase::discard_or_import_tablespace(
|
||||||
ER_ALTER_INFO,
|
ER_ALTER_INFO,
|
||||||
"Error updating stats for table '%s'"
|
"Error updating stats for table '%s'"
|
||||||
" after table rebuild: %s",
|
" after table rebuild: %s",
|
||||||
dict_table->name.m_name, ut_strerr(ret));
|
m_prebuilt->table->name.m_name,
|
||||||
|
ut_strerr(ret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_RETURN(convert_error_code_to_mysql(err, dict_table->flags, NULL));
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
||||||
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||||
Copyright (c) 2015, 2018, MariaDB Corporation.
|
Copyright (c) 2015, 2019, MariaDB Corporation.
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify it under
|
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
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
@ -1820,13 +1820,22 @@ PageConverter::update_index_page(
|
||||||
|
|
||||||
if (dict_index_is_clust(m_index->m_srv_index)) {
|
if (dict_index_is_clust(m_index->m_srv_index)) {
|
||||||
if (page_is_root(page)) {
|
if (page_is_root(page)) {
|
||||||
|
dict_index_t* index = const_cast<dict_index_t*>(
|
||||||
|
m_index->m_srv_index);
|
||||||
/* Preserve the PAGE_ROOT_AUTO_INC. */
|
/* Preserve the PAGE_ROOT_AUTO_INC. */
|
||||||
if (m_index->m_srv_index->table->supports_instant()
|
if (index->table->supports_instant()) {
|
||||||
&& btr_cur_instant_root_init(
|
if (btr_cur_instant_root_init(index, page)) {
|
||||||
const_cast<dict_index_t*>(
|
return(DB_CORRUPTION);
|
||||||
m_index->m_srv_index),
|
}
|
||||||
page)) {
|
|
||||||
return(DB_CORRUPTION);
|
/* Provisionally set all instantly
|
||||||
|
added columns to be DEFAULT NULL. */
|
||||||
|
for (unsigned i = index->n_core_fields;
|
||||||
|
i < index->n_fields; i++) {
|
||||||
|
dict_col_t* col = index->fields[i].col;
|
||||||
|
col->def_val.len = UNIV_SQL_NULL;
|
||||||
|
col->def_val.data = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Clear PAGE_MAX_TRX_ID so that it can be
|
/* Clear PAGE_MAX_TRX_ID so that it can be
|
||||||
|
|
|
@ -3074,13 +3074,14 @@ row_discard_tablespace(
|
||||||
table->flags2 |= DICT_TF2_DISCARDED;
|
table->flags2 |= DICT_TF2_DISCARDED;
|
||||||
dict_table_change_id_in_cache(table, new_id);
|
dict_table_change_id_in_cache(table, new_id);
|
||||||
|
|
||||||
/* Reset the root page numbers. */
|
dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
|
||||||
|
if (index) index->remove_instant();
|
||||||
|
|
||||||
for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
|
/* Reset the root page numbers. */
|
||||||
index != 0;
|
for (; index; index = UT_LIST_GET_NEXT(indexes, index)) {
|
||||||
index = UT_LIST_GET_NEXT(indexes, index)) {
|
|
||||||
index->page = FIL_NULL;
|
index->page = FIL_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the tablespace did not already exist or we couldn't
|
/* If the tablespace did not already exist or we couldn't
|
||||||
write to it, we treat that as a successful DISCARD. It is
|
write to it, we treat that as a successful DISCARD. It is
|
||||||
unusable anyway. */
|
unusable anyway. */
|
||||||
|
|
Loading…
Reference in a new issue