Commit 1bd0c9b5 authored by Marko Mäkelä's avatar Marko Mäkelä

Bug#12861864 RACE CONDITION IN BTR_GET_SIZE() AND DROP INDEX/TABLE/DATABASE

also filed as Bug#13146269, Bug#13713178

btr_get_size(): Add mtr_t parameter. Require that the caller S-latches
index->lock. If index->page==FIL_NULL or the index is to be dropped,
return ULINT_UNDEFINED to indicate that the statistics are
unavailable.

dict_update_statistics(): If btr_get_size() returns ULINT_UNDEFINED,
fake the index cardinality statistics.

dict_index_set_page(): Unused function, remove.

row_drop_table_for_mysql(): Before starting to drop the table, mark
the indexes unavailable in the data dictionary cache while holding
index->lock X-latch.

ha_innobase::prepare_drop_index(), ha_innobase::final_drop_index():
When setting index->to_be_dropped, acquire the index->lock X-latch.

rb:960 approved by Jimmy Yang
parent 5682272e
2012-02-28 The InnoDB Team
* btr/btr0btr.c, dict/dict0dict.c, include/btr0btr.h,
include/dict0dict.h, include/dict0dict.ic, include/dict0mem.h,
handler/handler0alter.cc, row/row0mysql.c:
Fix Bug#12861864 RACE CONDITION IN BTR_GET_SIZE() AND
DROP INDEX/TABLE/DATABASE
2012-02-15 The InnoDB Team 2012-02-15 The InnoDB Team
* btr/btr0btr.c, btr/btr0cur.c, fsp/fsp0fsp.c, ibuf/ibuf0ibuf.c, * btr/btr0btr.c, btr/btr0cur.c, fsp/fsp0fsp.c, ibuf/ibuf0ibuf.c,
......
...@@ -991,45 +991,49 @@ btr_page_alloc( ...@@ -991,45 +991,49 @@ btr_page_alloc(
/**************************************************************//** /**************************************************************//**
Gets the number of pages in a B-tree. Gets the number of pages in a B-tree.
@return number of pages */ @return number of pages, or ULINT_UNDEFINED if the index is unavailable */
UNIV_INTERN UNIV_INTERN
ulint ulint
btr_get_size( btr_get_size(
/*=========*/ /*=========*/
dict_index_t* index, /*!< in: index */ dict_index_t* index, /*!< in: index */
ulint flag) /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ ulint flag, /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */
mtr_t* mtr) /*!< in/out: mini-transaction where index
is s-latched */
{ {
fseg_header_t* seg_header; fseg_header_t* seg_header;
page_t* root; page_t* root;
ulint n; ulint n;
ulint dummy; ulint dummy;
mtr_t mtr;
mtr_start(&mtr); ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index),
MTR_MEMO_S_LOCK));
mtr_s_lock(dict_index_get_lock(index), &mtr); if (index->page == FIL_NULL
|| index->to_be_dropped
|| *index->name == TEMP_INDEX_PREFIX) {
return(ULINT_UNDEFINED);
}
root = btr_root_get(index, &mtr); root = btr_root_get(index, mtr);
if (flag == BTR_N_LEAF_PAGES) { if (flag == BTR_N_LEAF_PAGES) {
seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF;
fseg_n_reserved_pages(seg_header, &n, &mtr); fseg_n_reserved_pages(seg_header, &n, mtr);
} else if (flag == BTR_TOTAL_SIZE) { } else if (flag == BTR_TOTAL_SIZE) {
seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP;
n = fseg_n_reserved_pages(seg_header, &dummy, &mtr); n = fseg_n_reserved_pages(seg_header, &dummy, mtr);
seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF;
n += fseg_n_reserved_pages(seg_header, &dummy, &mtr); n += fseg_n_reserved_pages(seg_header, &dummy, mtr);
} else { } else {
ut_error; ut_error;
} }
mtr_commit(&mtr);
return(n); return(n);
} }
......
...@@ -4267,16 +4267,27 @@ dict_update_statistics( ...@@ -4267,16 +4267,27 @@ dict_update_statistics(
(srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE
|| (srv_force_recovery < SRV_FORCE_NO_LOG_REDO || (srv_force_recovery < SRV_FORCE_NO_LOG_REDO
&& dict_index_is_clust(index)))) { && dict_index_is_clust(index)))) {
mtr_t mtr;
ulint size; ulint size;
size = btr_get_size(index, BTR_TOTAL_SIZE);
index->stat_index_size = size; mtr_start(&mtr);
mtr_s_lock(dict_index_get_lock(index), &mtr);
size = btr_get_size(index, BTR_TOTAL_SIZE, &mtr);
if (size != ULINT_UNDEFINED) {
sum_of_index_sizes += size; sum_of_index_sizes += size;
index->stat_index_size = size;
size = btr_get_size(
index, BTR_N_LEAF_PAGES, &mtr);
}
size = btr_get_size(index, BTR_N_LEAF_PAGES); mtr_commit(&mtr);
if (size == 0) { switch (size) {
case ULINT_UNDEFINED:
goto fake_statistics;
case 0:
/* The root node of the tree is a leaf */ /* The root node of the tree is a leaf */
size = 1; size = 1;
} }
...@@ -4293,6 +4304,7 @@ dict_update_statistics( ...@@ -4293,6 +4304,7 @@ dict_update_statistics(
various means, also via secondary indexes. */ various means, also via secondary indexes. */
ulint i; ulint i;
fake_statistics:
sum_of_index_sizes++; sum_of_index_sizes++;
index->stat_index_size = index->stat_n_leaf_pages = 1; index->stat_index_size = index->stat_n_leaf_pages = 1;
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 2005, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2005, 2012, Oracle and/or its affiliates. All Rights Reserved.
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
...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with 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., 59 Temple this program; if not, write to the Free Software Foundation, Inc.,
Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/ *****************************************************************************/
...@@ -1010,7 +1010,9 @@ ha_innobase::prepare_drop_index( ...@@ -1010,7 +1010,9 @@ ha_innobase::prepare_drop_index(
goto func_exit; goto func_exit;
} }
rw_lock_x_lock(dict_index_get_lock(index));
index->to_be_dropped = TRUE; index->to_be_dropped = TRUE;
rw_lock_x_unlock(dict_index_get_lock(index));
} }
/* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined /* If FOREIGN_KEY_CHECKS = 1 you may not drop an index defined
...@@ -1129,7 +1131,9 @@ func_exit: ...@@ -1129,7 +1131,9 @@ func_exit:
= dict_table_get_first_index(prebuilt->table); = dict_table_get_first_index(prebuilt->table);
do { do {
rw_lock_x_lock(dict_index_get_lock(index));
index->to_be_dropped = FALSE; index->to_be_dropped = FALSE;
rw_lock_x_unlock(dict_index_get_lock(index));
index = dict_table_get_next_index(index); index = dict_table_get_next_index(index);
} while (index); } while (index);
} }
...@@ -1189,7 +1193,9 @@ ha_innobase::final_drop_index( ...@@ -1189,7 +1193,9 @@ ha_innobase::final_drop_index(
for (index = dict_table_get_first_index(prebuilt->table); for (index = dict_table_get_first_index(prebuilt->table);
index; index = dict_table_get_next_index(index)) { index; index = dict_table_get_next_index(index)) {
rw_lock_x_lock(dict_index_get_lock(index));
index->to_be_dropped = FALSE; index->to_be_dropped = FALSE;
rw_lock_x_unlock(dict_index_get_lock(index));
} }
goto func_exit; goto func_exit;
......
...@@ -536,13 +536,16 @@ btr_parse_page_reorganize( ...@@ -536,13 +536,16 @@ btr_parse_page_reorganize(
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
/**************************************************************//** /**************************************************************//**
Gets the number of pages in a B-tree. Gets the number of pages in a B-tree.
@return number of pages */ @return number of pages, or ULINT_UNDEFINED if the index is unavailable */
UNIV_INTERN UNIV_INTERN
ulint ulint
btr_get_size( btr_get_size(
/*=========*/ /*=========*/
dict_index_t* index, /*!< in: index */ dict_index_t* index, /*!< in: index */
ulint flag); /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ ulint flag, /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */
mtr_t* mtr) /*!< in/out: mini-transaction where index
is s-latched */
__attribute__((nonnull, warn_unused_result));
/**************************************************************//** /**************************************************************//**
Allocates a new file page to be used in an index tree. NOTE: we assume Allocates a new file page to be used in an index tree. NOTE: we assume
that the caller has made the reservation for free extents! that the caller has made the reservation for free extents!
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
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
...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with 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., 59 Temple this program; if not, write to the Free Software Foundation, Inc.,
Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/ *****************************************************************************/
...@@ -1018,14 +1018,6 @@ dict_index_get_page( ...@@ -1018,14 +1018,6 @@ dict_index_get_page(
/*================*/ /*================*/
const dict_index_t* tree); /*!< in: index */ const dict_index_t* tree); /*!< in: index */
/*********************************************************************//** /*********************************************************************//**
Sets the page number of the root of index tree. */
UNIV_INLINE
void
dict_index_set_page(
/*================*/
dict_index_t* index, /*!< in/out: index */
ulint page); /*!< in: page number */
/*********************************************************************//**
Gets the read-write lock of the index tree. Gets the read-write lock of the index tree.
@return read-write lock */ @return read-write lock */
UNIV_INLINE UNIV_INLINE
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
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
...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with 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., 59 Temple this program; if not, write to the Free Software Foundation, Inc.,
Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/ *****************************************************************************/
...@@ -722,21 +722,6 @@ dict_index_get_page( ...@@ -722,21 +722,6 @@ dict_index_get_page(
return(index->page); return(index->page);
} }
/*********************************************************************//**
Sets the page number of the root of index tree. */
UNIV_INLINE
void
dict_index_set_page(
/*================*/
dict_index_t* index, /*!< in/out: index */
ulint page) /*!< in: page number */
{
ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
index->page = page;
}
/*********************************************************************//** /*********************************************************************//**
Gets the read-write lock of the index tree. Gets the read-write lock of the index tree.
@return read-write lock */ @return read-write lock */
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
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
...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with 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., 59 Temple this program; if not, write to the Free Software Foundation, Inc.,
Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/ *****************************************************************************/
...@@ -305,7 +305,9 @@ struct dict_index_struct{ ...@@ -305,7 +305,9 @@ struct dict_index_struct{
unsigned to_be_dropped:1; unsigned to_be_dropped:1;
/*!< TRUE if this index is marked to be /*!< TRUE if this index is marked to be
dropped in ha_innobase::prepare_drop_index(), dropped in ha_innobase::prepare_drop_index(),
otherwise FALSE */ otherwise FALSE. Protected by
dict_sys->mutex, dict_operation_lock and
index->lock.*/
dict_field_t* fields; /*!< array of field descriptions */ dict_field_t* fields; /*!< array of field descriptions */
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
UT_LIST_NODE_T(dict_index_t) UT_LIST_NODE_T(dict_index_t)
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 2000, 2011, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2000, 2012, Oracle and/or its affiliates. All Rights Reserved.
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
...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ...@@ -11,8 +11,8 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with 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., 59 Temple this program; if not, write to the Free Software Foundation, Inc.,
Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/ *****************************************************************************/
...@@ -3013,6 +3013,7 @@ row_drop_table_for_mysql( ...@@ -3013,6 +3013,7 @@ row_drop_table_for_mysql(
{ {
dict_foreign_t* foreign; dict_foreign_t* foreign;
dict_table_t* table; dict_table_t* table;
dict_index_t* index;
ulint space_id; ulint space_id;
ulint err; ulint err;
const char* table_name; const char* table_name;
...@@ -3305,6 +3306,18 @@ check_next_foreign: ...@@ -3305,6 +3306,18 @@ check_next_foreign:
"END;\n" "END;\n"
, FALSE, trx); , FALSE, trx);
/* Mark all indexes unavailable in the data dictionary cache
before starting to drop the table. */
for (index = dict_table_get_first_index(table);
index != NULL;
index = dict_table_get_next_index(index)) {
rw_lock_x_lock(dict_index_get_lock(index));
ut_ad(!index->to_be_dropped);
index->to_be_dropped = TRUE;
rw_lock_x_unlock(dict_index_get_lock(index));
}
switch (err) { switch (err) {
ibool is_temp; ibool is_temp;
const char* name_or_path; const char* name_or_path;
...@@ -3384,6 +3397,17 @@ check_next_foreign: ...@@ -3384,6 +3397,17 @@ check_next_foreign:
the undo log. We can directly exit here the undo log. We can directly exit here
and return the DB_TOO_MANY_CONCURRENT_TRXS and return the DB_TOO_MANY_CONCURRENT_TRXS
error. */ error. */
/* Mark all indexes available in the data dictionary
cache again. */
for (index = dict_table_get_first_index(table);
index != NULL;
index = dict_table_get_next_index(index)) {
rw_lock_x_lock(dict_index_get_lock(index));
index->to_be_dropped = FALSE;
rw_lock_x_unlock(dict_index_get_lock(index));
}
break; break;
case DB_OUT_OF_FILE_SPACE: case DB_OUT_OF_FILE_SPACE:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment