Skip to content

Commit 4e7ee16

Browse files
committed
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.
1 parent 4eae9ee commit 4e7ee16

File tree

5 files changed

+160
-31
lines changed

5 files changed

+160
-31
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
set default_storage_engine=innodb;
2+
#
3+
# MDEV-18295 IMPORT TABLESPACE fails with instant-altered tables
4+
#
5+
create table t2 (x int, z int default 41);
6+
alter table t2 discard tablespace;
7+
create table t1 (x int);
8+
insert into t1 values (1);
9+
alter table t1 add z int default 42, algorithm instant;
10+
select * from t1;
11+
x z
12+
1 42
13+
flush tables t1 for export;
14+
unlock tables;
15+
# The metadata has to be updated to instant ADD COLUMN.
16+
alter table t2 import tablespace;
17+
select * from t2;
18+
x z
19+
1 42
20+
insert into t2 set x=2;
21+
select * from t2;
22+
x z
23+
1 42
24+
2 41
25+
alter table t1 discard tablespace;
26+
flush tables t2 for export;
27+
unlock tables;
28+
# Both the metadata and the data file used instant ADD COLUMN.
29+
alter table t1 import tablespace;
30+
select * from t1;
31+
x z
32+
1 42
33+
2 41
34+
drop table t2;
35+
create table t2 select * from t1;
36+
alter table t1 discard tablespace;
37+
flush tables t2 for export;
38+
unlock tables;
39+
# The instant ADD COLUMN has to be removed from the metadata.
40+
alter table t1 import tablespace;
41+
select * from t1;
42+
x z
43+
1 42
44+
2 41
45+
drop table t2;
46+
drop table t1;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--source include/have_innodb.inc
2+
set default_storage_engine=innodb;
3+
4+
--echo #
5+
--echo # MDEV-18295 IMPORT TABLESPACE fails with instant-altered tables
6+
--echo #
7+
8+
create table t2 (x int, z int default 41);
9+
alter table t2 discard tablespace;
10+
11+
create table t1 (x int);
12+
insert into t1 values (1);
13+
alter table t1 add z int default 42, algorithm instant;
14+
select * from t1;
15+
flush tables t1 for export;
16+
--let $MYSQLD_DATADIR= `select @@datadir`
17+
--move_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
18+
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
19+
unlock tables;
20+
21+
--echo # The metadata has to be updated to instant ADD COLUMN.
22+
alter table t2 import tablespace;
23+
24+
select * from t2;
25+
insert into t2 set x=2;
26+
select * from t2;
27+
28+
alter table t1 discard tablespace;
29+
flush tables t2 for export;
30+
--move_file $MYSQLD_DATADIR/test/t2.cfg $MYSQLD_DATADIR/test/t1.cfg
31+
--copy_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t1.ibd
32+
unlock tables;
33+
34+
--echo # Both the metadata and the data file used instant ADD COLUMN.
35+
alter table t1 import tablespace;
36+
select * from t1;
37+
38+
drop table t2;
39+
create table t2 select * from t1;
40+
41+
alter table t1 discard tablespace;
42+
flush tables t2 for export;
43+
--move_file $MYSQLD_DATADIR/test/t2.cfg $MYSQLD_DATADIR/test/t1.cfg
44+
--copy_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t1.ibd
45+
unlock tables;
46+
47+
--echo # The instant ADD COLUMN has to be removed from the metadata.
48+
alter table t1 import tablespace;
49+
select * from t1;
50+
51+
drop table t2;
52+
drop table t1;

storage/innobase/handler/ha_innodb.cc

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved.
44
Copyright (c) 2008, 2009 Google Inc.
55
Copyright (c) 2009, Percona Inc.
66
Copyright (c) 2012, Facebook Inc.
7-
Copyright (c) 2013, 2018, MariaDB Corporation.
7+
Copyright (c) 2013, 2019, MariaDB Corporation.
88

99
Portions of this file contain modifications contributed and copyrighted by
1010
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -12682,22 +12682,19 @@ ha_innobase::discard_or_import_tablespace(
1268212682
DBUG_RETURN(HA_ERR_TABLE_READONLY);
1268312683
}
1268412684

12685-
dict_table_t* dict_table = m_prebuilt->table;
12686-
12687-
if (dict_table->is_temporary()) {
12688-
12685+
if (m_prebuilt->table->is_temporary()) {
1268912686
ib_senderrf(
1269012687
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
1269112688
ER_CANNOT_DISCARD_TEMPORARY_TABLE);
1269212689

1269312690
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
1269412691
}
1269512692

12696-
if (dict_table->space == fil_system.sys_space) {
12693+
if (m_prebuilt->table->space == fil_system.sys_space) {
1269712694
ib_senderrf(
1269812695
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
1269912696
ER_TABLE_IN_SYSTEM_TABLESPACE,
12700-
dict_table->name.m_name);
12697+
m_prebuilt->table->name.m_name);
1270112698

1270212699
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
1270312700
}
@@ -12706,7 +12703,7 @@ ha_innobase::discard_or_import_tablespace(
1270612703

1270712704
/* Obtain an exclusive lock on the table. */
1270812705
dberr_t err = row_mysql_lock_table(
12709-
m_prebuilt->trx, dict_table, LOCK_X,
12706+
m_prebuilt->trx, m_prebuilt->table, LOCK_X,
1271012707
discard ? "setting table lock for DISCARD TABLESPACE"
1271112708
: "setting table lock for IMPORT TABLESPACE");
1271212709

@@ -12719,32 +12716,32 @@ ha_innobase::discard_or_import_tablespace(
1271912716
user may want to set the DISCARD flag in order to IMPORT
1272012717
a new tablespace. */
1272112718

12722-
if (!dict_table->is_readable()) {
12719+
if (!m_prebuilt->table->is_readable()) {
1272312720
ib_senderrf(
1272412721
m_prebuilt->trx->mysql_thd,
1272512722
IB_LOG_LEVEL_WARN, ER_TABLESPACE_MISSING,
12726-
dict_table->name.m_name);
12723+
m_prebuilt->table->name.m_name);
1272712724
}
1272812725

1272912726
err = row_discard_tablespace_for_mysql(
12730-
dict_table->name.m_name, m_prebuilt->trx);
12727+
m_prebuilt->table->name.m_name, m_prebuilt->trx);
1273112728

12732-
} else if (dict_table->is_readable()) {
12729+
} else if (m_prebuilt->table->is_readable()) {
1273312730
/* Commit the transaction in order to
1273412731
release the table lock. */
1273512732
trx_commit_for_mysql(m_prebuilt->trx);
1273612733

1273712734
ib::error() << "Unable to import tablespace "
12738-
<< dict_table->name << " because it already"
12735+
<< m_prebuilt->table->name << " because it already"
1273912736
" exists. Please DISCARD the tablespace"
1274012737
" before IMPORT.";
1274112738
ib_senderrf(
1274212739
m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
12743-
ER_TABLESPACE_EXISTS, dict_table->name.m_name);
12740+
ER_TABLESPACE_EXISTS, m_prebuilt->table->name.m_name);
1274412741

1274512742
DBUG_RETURN(HA_ERR_TABLE_EXIST);
1274612743
} else {
12747-
err = row_import_for_mysql(dict_table, m_prebuilt);
12744+
err = row_import_for_mysql(m_prebuilt->table, m_prebuilt);
1274812745

1274912746
if (err == DB_SUCCESS) {
1275012747

@@ -12760,12 +12757,35 @@ ha_innobase::discard_or_import_tablespace(
1276012757
/* Commit the transaction in order to release the table lock. */
1276112758
trx_commit_for_mysql(m_prebuilt->trx);
1276212759

12763-
if (err == DB_SUCCESS && !discard
12764-
&& dict_stats_is_persistent_enabled(dict_table)) {
12760+
if (discard || err != DB_SUCCESS) {
12761+
DBUG_RETURN(convert_error_code_to_mysql(
12762+
err, m_prebuilt->table->flags, NULL));
12763+
}
12764+
12765+
/* Evict and reload the table definition in order to invoke
12766+
btr_cur_instant_init(). */
12767+
table_id_t id = m_prebuilt->table->id;
12768+
ut_ad(id);
12769+
mutex_enter(&dict_sys->mutex);
12770+
dict_table_close(m_prebuilt->table, TRUE, FALSE);
12771+
dict_table_remove_from_cache(m_prebuilt->table);
12772+
m_prebuilt->table = dict_table_open_on_id(id, TRUE,
12773+
DICT_TABLE_OP_NORMAL);
12774+
mutex_exit(&dict_sys->mutex);
12775+
if (!m_prebuilt->table) {
12776+
err = DB_TABLE_NOT_FOUND;
12777+
} else {
12778+
if (const Field* ai = table->found_next_number_field) {
12779+
initialize_auto_increment(m_prebuilt->table, ai);
12780+
}
12781+
dict_stats_init(m_prebuilt->table);
12782+
}
12783+
12784+
if (dict_stats_is_persistent_enabled(m_prebuilt->table)) {
1276512785
dberr_t ret;
1276612786

1276712787
/* Adjust the persistent statistics. */
12768-
ret = dict_stats_update(dict_table,
12788+
ret = dict_stats_update(m_prebuilt->table,
1276912789
DICT_STATS_RECALC_PERSISTENT);
1277012790

1277112791
if (ret != DB_SUCCESS) {
@@ -12775,11 +12795,12 @@ ha_innobase::discard_or_import_tablespace(
1277512795
ER_ALTER_INFO,
1277612796
"Error updating stats for table '%s'"
1277712797
" after table rebuild: %s",
12778-
dict_table->name.m_name, ut_strerr(ret));
12798+
m_prebuilt->table->name.m_name,
12799+
ut_strerr(ret));
1277912800
}
1278012801
}
1278112802

12782-
DBUG_RETURN(convert_error_code_to_mysql(err, dict_table->flags, NULL));
12803+
DBUG_RETURN(0);
1278312804
}
1278412805

1278512806
/**

storage/innobase/row/row0import.cc

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
33
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
4-
Copyright (c) 2015, 2018, MariaDB Corporation.
4+
Copyright (c) 2015, 2019, MariaDB Corporation.
55
66
This program is free software; you can redistribute it and/or modify it under
77
the terms of the GNU General Public License as published by the Free Software
@@ -1820,13 +1820,22 @@ PageConverter::update_index_page(
18201820

18211821
if (dict_index_is_clust(m_index->m_srv_index)) {
18221822
if (page_is_root(page)) {
1823+
dict_index_t* index = const_cast<dict_index_t*>(
1824+
m_index->m_srv_index);
18231825
/* Preserve the PAGE_ROOT_AUTO_INC. */
1824-
if (m_index->m_srv_index->table->supports_instant()
1825-
&& btr_cur_instant_root_init(
1826-
const_cast<dict_index_t*>(
1827-
m_index->m_srv_index),
1828-
page)) {
1829-
return(DB_CORRUPTION);
1826+
if (index->table->supports_instant()) {
1827+
if (btr_cur_instant_root_init(index, page)) {
1828+
return(DB_CORRUPTION);
1829+
}
1830+
1831+
/* Provisionally set all instantly
1832+
added columns to be DEFAULT NULL. */
1833+
for (unsigned i = index->n_core_fields;
1834+
i < index->n_fields; i++) {
1835+
dict_col_t* col = index->fields[i].col;
1836+
col->def_val.len = UNIV_SQL_NULL;
1837+
col->def_val.data = NULL;
1838+
}
18301839
}
18311840
} else {
18321841
/* Clear PAGE_MAX_TRX_ID so that it can be

storage/innobase/row/row0mysql.cc

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,13 +3074,14 @@ row_discard_tablespace(
30743074
table->flags2 |= DICT_TF2_DISCARDED;
30753075
dict_table_change_id_in_cache(table, new_id);
30763076

3077-
/* Reset the root page numbers. */
3077+
dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
3078+
if (index) index->remove_instant();
30783079

3079-
for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
3080-
index != 0;
3081-
index = UT_LIST_GET_NEXT(indexes, index)) {
3080+
/* Reset the root page numbers. */
3081+
for (; index; index = UT_LIST_GET_NEXT(indexes, index)) {
30823082
index->page = FIL_NULL;
30833083
}
3084+
30843085
/* If the tablespace did not already exist or we couldn't
30853086
write to it, we treat that as a successful DISCARD. It is
30863087
unusable anyway. */

0 commit comments

Comments
 (0)