diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2018-05-07 15:30:57 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2018-05-07 15:39:29 +0300 |
commit | e44ca6cc9c300cbdf93c64110bd8cf2be8125379 (patch) | |
tree | a50f01c1052e8736ba4b0ca53ef91edcd972b65c | |
parent | d257c425f2cc8bcc3f042bad51aefba5c2e76fed (diff) | |
download | mariadb-git-e44ca6cc9c300cbdf93c64110bd8cf2be8125379.tar.gz |
MDEV-14825 Assertion `col->ord_part' in row_build_index_entry_low upon ROLLBACK or DELETE with concurrent ALTER on partitioned table
If creating a secondary index fails (typically, ADD UNIQUE INDEX fails
due to duplicate key), it is possible that concurrently running UPDATE
or DELETE will access the index stub and hit the debug assertion.
It does not make any sense to keep updating an uncommitted index whose
creation has failed.
dict_index_t::is_corrupted(): Replaces dict_index_is_corrupted().
Also take online_status into account.
Replace some calls to dict_index_is_clust() with calls to
dict_index_t::is_primary().
-rw-r--r-- | mysql-test/suite/innodb/r/alter_partitioned_debug.result | 27 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/alter_partitioned_debug.test | 34 | ||||
-rw-r--r-- | storage/innobase/dict/dict0defrag_bg.cc | 4 | ||||
-rw-r--r-- | storage/innobase/dict/dict0load.cc | 16 | ||||
-rw-r--r-- | storage/innobase/dict/dict0stats.cc | 7 | ||||
-rw-r--r-- | storage/innobase/fts/fts0fts.cc | 2 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 22 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 17 | ||||
-rw-r--r-- | storage/innobase/include/dict0dict.h | 12 | ||||
-rw-r--r-- | storage/innobase/include/dict0dict.ic | 15 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 10 | ||||
-rw-r--r-- | storage/innobase/row/row0ins.cc | 3 | ||||
-rw-r--r-- | storage/innobase/row/row0log.cc | 18 | ||||
-rw-r--r-- | storage/innobase/row/row0merge.cc | 4 | ||||
-rw-r--r-- | storage/innobase/row/row0purge.cc | 3 | ||||
-rw-r--r-- | storage/innobase/row/row0sel.cc | 6 |
16 files changed, 115 insertions, 85 deletions
diff --git a/mysql-test/suite/innodb/r/alter_partitioned_debug.result b/mysql-test/suite/innodb/r/alter_partitioned_debug.result new file mode 100644 index 00000000000..d2ec602c6d7 --- /dev/null +++ b/mysql-test/suite/innodb/r/alter_partitioned_debug.result @@ -0,0 +1,27 @@ +CREATE TABLE t1 (a INT, b VARCHAR(10)) ENGINE=InnoDB +PARTITION BY RANGE(a) +(PARTITION pa VALUES LESS THAN (3), +PARTITION pb VALUES LESS THAN (5)); +INSERT INTO t1 VALUES(2,'two'),(2,'two'),(4,'four'); +connect ddl,localhost,root,,test; +SET DEBUG_SYNC = 'inplace_after_index_build SIGNAL go WAIT_FOR done'; +ALTER TABLE t1 ADD UNIQUE KEY (a,b(3)); +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR go'; +BEGIN; +SELECT * FROM t1 FOR UPDATE; +a b +2 two +2 two +4 four +SET DEBUG_SYNC = 'now SIGNAL done'; +connection ddl; +ERROR 23000: Duplicate entry '2-two' for key 'a' +connection default; +DELETE FROM t1; +disconnect ddl; +SET DEBUG_SYNC = 'RESET'; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/alter_partitioned_debug.test b/mysql-test/suite/innodb/t/alter_partitioned_debug.test new file mode 100644 index 00000000000..34565e12036 --- /dev/null +++ b/mysql-test/suite/innodb/t/alter_partitioned_debug.test @@ -0,0 +1,34 @@ +--source include/have_innodb.inc +--source include/have_partition.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +CREATE TABLE t1 (a INT, b VARCHAR(10)) ENGINE=InnoDB +PARTITION BY RANGE(a) +(PARTITION pa VALUES LESS THAN (3), +PARTITION pb VALUES LESS THAN (5)); + +INSERT INTO t1 VALUES(2,'two'),(2,'two'),(4,'four'); + +connect ddl,localhost,root,,test; +SET DEBUG_SYNC = 'inplace_after_index_build SIGNAL go WAIT_FOR done'; +send ALTER TABLE t1 ADD UNIQUE KEY (a,b(3)); + +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR go'; +BEGIN; +SELECT * FROM t1 FOR UPDATE; +SET DEBUG_SYNC = 'now SIGNAL done'; + +connection ddl; +--error ER_DUP_ENTRY +reap; + +connection default; +DELETE FROM t1; +disconnect ddl; + +SET DEBUG_SYNC = 'RESET'; + +CHECK TABLE t1; +DROP TABLE t1; diff --git a/storage/innobase/dict/dict0defrag_bg.cc b/storage/innobase/dict/dict0defrag_bg.cc index 7b9a0373c48..949bbbc0d74 100644 --- a/storage/innobase/dict/dict0defrag_bg.cc +++ b/storage/innobase/dict/dict0defrag_bg.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2016, MariaDB Corporation. All Rights Reserved. +Copyright (c) 2016, 2018, 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 @@ -232,7 +232,7 @@ dict_stats_process_entry_from_defrag_pool() ? dict_table_find_index_on_id(table, index_id) : NULL; - if (!index || dict_index_is_corrupted(index)) { + if (!index || index->is_corrupted()) { if (table) { dict_table_close(table, TRUE, FALSE); } diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 1bebab914c8..f39c4900995 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -2521,10 +2521,10 @@ dict_load_indexes( } ut_ad(index); + ut_ad(!dict_index_is_online_ddl(index)); /* Check whether the index is corrupted */ - if (dict_index_is_corrupted(index)) { - + if (index->is_corrupted()) { ib::error() << "Index " << index->name << " of table " << table->name << " is corrupted"; @@ -3044,10 +3044,7 @@ err_exit: table = NULL; goto func_exit; } else { - dict_index_t* clust_index; - clust_index = dict_table_get_first_index(table); - - if (dict_index_is_corrupted(clust_index)) { + if (table->indexes.start->is_corrupted()) { table->corrupted = true; } } @@ -3095,14 +3092,11 @@ err_exit: if (!srv_force_recovery || !index - || !dict_index_is_clust(index)) { - + || !index->is_primary()) { dict_table_remove_from_cache(table); table = NULL; - - } else if (dict_index_is_corrupted(index) + } else if (index->is_corrupted() && table->is_readable()) { - /* It is possible we force to load a corrupted clustered index if srv_load_corrupted is set. Mark the table as corrupted in this case */ diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 67991814540..98a846bcaba 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -158,9 +158,8 @@ dict_stats_should_ignore_index( /*===========================*/ const dict_index_t* index) /*!< in: index */ { - return((index->type & DICT_FTS) - || dict_index_is_corrupted(index) - || dict_index_is_spatial(index) + return((index->type & (DICT_FTS | DICT_SPATIAL)) + || index->is_corrupted() || index->to_be_dropped || !index->is_committed()); } @@ -2228,7 +2227,7 @@ dict_stats_update_persistent( index = dict_table_get_first_index(table); if (index == NULL - || dict_index_is_corrupted(index) + || index->is_corrupted() || (index->type | DICT_UNIQUE) != (DICT_CLUSTERED | DICT_UNIQUE)) { /* Table definition is corrupt */ diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index c42cba4c12e..b5c4a44514d 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -6549,7 +6549,7 @@ fts_check_corrupt_index( if (index->id == aux_table->index_id) { ut_ad(index->type & DICT_FTS); dict_table_close(table, true, false); - return(dict_index_is_corrupted(index)); + return index->is_corrupted(); } } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index fd8e2de4819..bf2294f0774 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -9406,13 +9406,13 @@ ha_innobase::index_read( dict_index_t* index = m_prebuilt->index; - if (index == NULL || dict_index_is_corrupted(index)) { + if (index == NULL || index->is_corrupted()) { m_prebuilt->index_usable = FALSE; DBUG_RETURN(HA_ERR_CRASHED); } if (!m_prebuilt->index_usable) { - DBUG_RETURN(dict_index_is_corrupted(index) + DBUG_RETURN(index->is_corrupted() ? HA_ERR_INDEX_CORRUPT : HA_ERR_TABLE_DEF_CHANGED); } @@ -9671,14 +9671,14 @@ ha_innobase::change_active_index( m_prebuilt->trx, m_prebuilt->index); if (!m_prebuilt->index_usable) { - if (dict_index_is_corrupted(m_prebuilt->index)) { + if (m_prebuilt->index->is_corrupted()) { char table_name[MAX_FULL_NAME_LEN + 1]; innobase_format_name( table_name, sizeof table_name, m_prebuilt->index->table->name.m_name); - if (dict_index_is_clust(m_prebuilt->index)) { + if (m_prebuilt->index->is_primary()) { ut_ad(m_prebuilt->index->table->corrupted); push_warning_printf( m_user_thd, Sql_condition::WARN_LEVEL_WARN, @@ -13662,7 +13662,7 @@ ha_innobase::records_in_range( n_rows = HA_POS_ERROR; goto func_exit; } - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { n_rows = HA_ERR_INDEX_CORRUPT; goto func_exit; } @@ -14522,7 +14522,7 @@ ha_innobase::defragment_table( for (index = dict_table_get_first_index(table); index; index = dict_table_get_next_index(index)) { - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { continue; } @@ -14721,7 +14721,7 @@ ha_innobase::check( clustered index, we will do so here */ index = dict_table_get_first_index(m_prebuilt->table); - if (!dict_index_is_corrupted(index)) { + if (!index->is_corrupted()) { dict_set_corrupted( index, m_prebuilt->trx, "CHECK TABLE"); } @@ -14759,7 +14759,7 @@ ha_innobase::check( } if (!(check_opt->flags & T_QUICK) - && !dict_index_is_corrupted(index)) { + && !index->is_corrupted()) { /* Enlarge the fatal lock wait timeout during CHECK TABLE. */ my_atomic_addlong( @@ -14811,7 +14811,7 @@ ha_innobase::check( DBUG_EXECUTE_IF( "dict_set_index_corrupted", - if (!dict_index_is_clust(index)) { + if (!index->is_primary()) { m_prebuilt->index_usable = FALSE; // row_mysql_lock_data_dictionary(m_prebuilt->trx); dict_set_corrupted(index, m_prebuilt->trx, "dict_set_index_corrupted"); @@ -14819,7 +14819,7 @@ ha_innobase::check( }); if (UNIV_UNLIKELY(!m_prebuilt->index_usable)) { - if (dict_index_is_corrupted(m_prebuilt->index)) { + if (index->is_corrupted()) { push_warning_printf( m_user_thd, Sql_condition::WARN_LEVEL_WARN, @@ -14859,7 +14859,7 @@ ha_innobase::check( DBUG_EXECUTE_IF( "dict_set_index_corrupted", - if (!dict_index_is_clust(index)) { + if (!index->is_primary()) { ret = DB_CORRUPTION; }); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index d064ebccfe2..a8f05f039fa 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4784,8 +4784,7 @@ new_clustered_failed: = dict_table_get_first_index(user_table); index != NULL; index = dict_table_get_next_index(index)) { - if (!index->to_be_dropped - && dict_index_is_corrupted(index)) { + if (!index->to_be_dropped && index->is_corrupted()) { my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0)); goto error_handled; } @@ -4795,8 +4794,7 @@ new_clustered_failed: = dict_table_get_first_index(user_table); index != NULL; index = dict_table_get_next_index(index)) { - if (!index->to_be_dropped - && dict_index_is_corrupted(index)) { + if (!index->to_be_dropped && index->is_corrupted()) { my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0)); goto error_handled; } @@ -5597,8 +5595,7 @@ ha_innobase::prepare_inplace_alter_table( if (indexed_table->corrupted || dict_table_get_first_index(indexed_table) == NULL - || dict_index_is_corrupted( - dict_table_get_first_index(indexed_table))) { + || dict_table_get_first_index(indexed_table)->is_corrupted()) { /* The clustered index is corrupted. */ my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0)); DBUG_RETURN(true); @@ -5885,7 +5882,7 @@ found_fk: " with name %s", key->name); } else { ut_ad(!index->to_be_dropped); - if (!dict_index_is_clust(index)) { + if (!index->is_primary()) { drop_index[n_drop_index++] = index; } else { drop_primary = index; @@ -5986,7 +5983,7 @@ check_if_can_drop_indexes: for (dict_index_t* index = dict_table_get_first_index(indexed_table); index != NULL; index = dict_table_get_next_index(index)) { - if (!index->to_be_dropped && dict_index_is_corrupted(index)) { + if (!index->to_be_dropped && index->is_corrupted()) { my_error(ER_INDEX_CORRUPT, MYF(0), index->name()); goto err_exit; } @@ -7717,7 +7714,7 @@ commit_try_rebuild( DBUG_ASSERT(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE); DBUG_ASSERT(index->is_committed()); - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { my_error(ER_INDEX_CORRUPT, MYF(0), index->name()); DBUG_RETURN(true); } @@ -7966,7 +7963,7 @@ commit_try_norebuild( DBUG_ASSERT(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE); DBUG_ASSERT(!index->is_committed()); - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { /* Report a duplicate key error for the index that was flagged corrupted, most likely diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 82f3cf403ba..0592b64b669 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -702,7 +702,7 @@ dict_table_get_next_index( /* Skip corrupted index */ #define dict_table_skip_corrupt_index(index) \ - while (index && dict_index_is_corrupted(index)) { \ + while (index && index->is_corrupted()) { \ index = dict_table_get_next_index(index); \ } @@ -1836,16 +1836,6 @@ dict_table_is_corrupted( MY_ATTRIBUTE((nonnull, warn_unused_result)); /**********************************************************************//** -Check whether the index is corrupted. -@return nonzero for corrupted index, zero for valid indexes */ -UNIV_INLINE -ulint -dict_index_is_corrupted( -/*====================*/ - const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); - -/**********************************************************************//** Flags an index and table corrupted both in the data dictionary cache and in the system table SYS_INDEXES. */ void diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index f93c803294d..e20da7c708a 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -1487,21 +1487,6 @@ dict_table_is_corrupted( } /********************************************************************//** -Check whether the index is corrupted. -@return nonzero for corrupted index, zero for valid indexes */ -UNIV_INLINE -ulint -dict_index_is_corrupted( -/*====================*/ - const dict_index_t* index) /*!< in: index */ -{ - ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); - - return((index->type & DICT_CORRUPT) - || (index->table && index->table->corrupted)); -} - -/********************************************************************//** Check if the tablespace for the table has been discarded. @return true if the tablespace has been discarded. */ UNIV_INLINE diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 3c8366ae28e..18e600c25d3 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -980,6 +980,9 @@ struct dict_index_t{ { return DICT_CLUSTERED == (type & (DICT_CLUSTERED | DICT_IBUF)); } + + /** @return whether the index is corrupted */ + inline bool is_corrupted() const; }; /** The status of online index creation */ @@ -1724,6 +1727,13 @@ inline bool dict_index_t::is_readable() const return(UNIV_LIKELY(!table->file_unreadable)); } +inline bool dict_index_t::is_corrupted() const +{ + return UNIV_UNLIKELY(online_status >= ONLINE_INDEX_ABORTED + || (type & DICT_CORRUPT) + || (table && table->corrupted)); +} + /*******************************************************************//** Initialise the table lock list. */ void diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 76809222f2c..1aa97bc34c5 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -3686,8 +3686,7 @@ row_ins( node->index = NULL; node->entry = NULL; break;); /* Skip corrupted secondary index and its entry */ - while (node->index && dict_index_is_corrupted(node->index)) { - + while (node->index && node->index->is_corrupted()) { node->index = dict_table_get_next_index(node->index); node->entry = UT_LIST_GET_NEXT(tuple_list, node->entry); } diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 90f219dce6a..3dbc1a4b93e 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -293,7 +293,7 @@ row_log_online_op( ut_ad(rw_lock_own(dict_index_get_lock(index), RW_LOCK_S) || rw_lock_own(dict_index_get_lock(index), RW_LOCK_X)); - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { return; } @@ -613,8 +613,8 @@ row_log_table_delete( &index->lock, RW_LOCK_FLAG_S | RW_LOCK_FLAG_X | RW_LOCK_FLAG_SX)); - if (dict_index_is_corrupted(index) - || !dict_index_is_online_ddl(index) + if (index->online_status != ONLINE_INDEX_CREATION + || (index->type & DICT_CORRUPT) || index->table->corrupted || index->online_log->error != DB_SUCCESS) { return; } @@ -922,8 +922,8 @@ row_log_table_low( ut_ad(!old_pk || !insert); ut_ad(!old_pk || old_pk->n_v_fields == 0); - if (dict_index_is_corrupted(index) - || !dict_index_is_online_ddl(index) + if (index->online_status != ONLINE_INDEX_CREATION + || (index->type & DICT_CORRUPT) || index->table->corrupted || index->online_log->error != DB_SUCCESS) { return; } @@ -2672,7 +2672,7 @@ next_block: goto interrupted; } - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { error = DB_INDEX_CORRUPT; goto func_exit; } @@ -3167,7 +3167,7 @@ row_log_apply_op_low( ut_ad(rw_lock_own(dict_index_get_lock(index), RW_LOCK_X) == has_index_lock); - ut_ad(!dict_index_is_corrupted(index)); + ut_ad(!index->is_corrupted()); ut_ad(trx_id != 0 || op == ROW_OP_DELETE); DBUG_LOG("ib_create_index", @@ -3411,7 +3411,7 @@ row_log_apply_op( ut_ad(rw_lock_own(dict_index_get_lock(index), RW_LOCK_X) == has_index_lock); - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { *error = DB_INDEX_CORRUPT; return(NULL); } @@ -3548,7 +3548,7 @@ next_block: goto func_exit; } - if (dict_index_is_corrupted(index)) { + if (index->is_corrupted()) { error = DB_INDEX_CORRUPT; goto func_exit; } diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index cbd5aa2f316..0f55ce00336 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -4443,13 +4443,13 @@ row_merge_is_index_usable( const trx_t* trx, /*!< in: transaction */ const dict_index_t* index) /*!< in: index to check */ { - if (!dict_index_is_clust(index) + if (!index->is_primary() && dict_index_is_online_ddl(index)) { /* Indexes that are being created are not useable. */ return(false); } - return(!dict_index_is_corrupted(index) + return(!index->is_corrupted() && (dict_table_is_temporary(index->table) || index->trx_id == 0 || !MVCC::is_view_active(trx->read_view) diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 697d59598f2..448e2984b41 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -888,8 +888,7 @@ try_again: clust_index = dict_table_get_first_index(node->table); - if (clust_index == NULL - || dict_index_is_corrupted(clust_index)) { + if (!clust_index || clust_index->is_corrupted()) { /* The table was corrupt in the data dictionary. dict_set_corrupted() works on an index, and we do not have an index to call it with. */ diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 9f14be17c11..b5e1bab352d 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4253,18 +4253,14 @@ row_search_mvcc( ut_ad(!sync_check_iterate(sync_check())); if (dict_table_is_discarded(prebuilt->table)) { - DBUG_RETURN(DB_TABLESPACE_DELETED); - } else if (!prebuilt->table->is_readable()) { DBUG_RETURN(fil_space_get(prebuilt->table->space) ? DB_DECRYPTION_FAILED : DB_TABLESPACE_NOT_FOUND); } else if (!prebuilt->index_usable) { DBUG_RETURN(DB_MISSING_HISTORY); - - } else if (dict_index_is_corrupted(prebuilt->index)) { - + } else if (prebuilt->index->is_corrupted()) { DBUG_RETURN(DB_CORRUPTION); } |