Commit 5e035790 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

Bug #42230 during add index, cannot do queries on storage engines

           that implement add_index

The problem was that ALTER TABLE blocked reads on an InnoDB table
while adding a secondary index, even if this was not needed. It is
only needed for the final step where the .frm file is updated.

The reason queries were blocked, was that ALTER TABLE upgraded the
metadata lock from MDL_SHARED_NO_WRITE (which blocks writes) to
MDL_EXCLUSIVE (which blocks all accesses) before index creation.

The way the server handles index creation, is that storage engines
publish their capabilities to the server and the server determines
which of the following three ways this can be handled: 1) build a
new version of the table; 2) change the existing table but with
exclusive metadata lock; 3) change the existing table but without
metadata lock upgrade.

For InnoDB and secondary index creation, option 3) should have been
selected. However this failed for two reasons. First, InnoDB did
not publish this capability properly.

Second, the ALTER TABLE code failed to made proper use of the
information supplied by the storage engine. A variable
need_lock_for_indexes was set accordingly, but was not later used.
This patch fixes this problem by only doing metadata lock upgrade
before index creation/deletion if this variable has been set.

This patch also changes some of the related terminology used 
in the code. Specifically the use of "fast" and "online" with
respect to ALTER TABLE. "Fast" was used to indicate that an
ALTER TABLE operation could be done without involving a
temporary table. "Fast" has been renamed "in-place" to more
accurately describe the behavior.

"Online" meant that the operation could be done without taking
a table lock. However, in the current implementation writes
are always prohibited during ALTER TABLE and an exclusive
metadata lock is held while updating the .frm, so ALTER TABLE
is not completely online. This patch replaces "online" with 
"in-place", with additional comments indicating if concurrent
reads are allowed during index creation/deletion or not.

An important part of this update of terminology is renaming
of the handler flags used by handlers to indicate if index
creation/deletion can be done in-place and if concurrent reads
are allowed. For example, the HA_ONLINE_ADD_INDEX_NO_WRITES
flag has been renamed to HA_INPLACE_ADD_INDEX_NO_READ_WRITE,
while HA_ONLINE_ADD_INDEX is now HA_INPLACE_ADD_INDEX_NO_WRITE.
Note that this is a rename to clarify current behavior, the
flag values have not changed and no flags have been removed or
added.

Test case added to innodb_mysql_sync.test.
parent 47238d44
...@@ -90,3 +90,68 @@ test.t1 optimize status Operation failed ...@@ -90,3 +90,68 @@ test.t1 optimize status Operation failed
# Connection default # Connection default
DROP TABLE t1; DROP TABLE t1;
SET DEBUG_SYNC= 'RESET'; SET DEBUG_SYNC= 'RESET';
#
# Bug#42230 during add index, cannot do queries on storage engines
# that implement add_index
#
DROP DATABASE IF EXISTS db1;
DROP TABLE IF EXISTS t1;
# Test 1: Secondary index, should not block reads (original test case).
# Connection default
CREATE DATABASE db1;
CREATE TABLE db1.t1(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, value INT) engine=innodb;
INSERT INTO db1.t1(value) VALUES (1), (2);
SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
# Sending:
ALTER TABLE db1.t1 ADD INDEX(value);
# Connection con1
SET DEBUG_SYNC= "now WAIT_FOR manage";
USE db1;
SELECT * FROM t1;
id value
1 1
2 2
SET DEBUG_SYNC= "now SIGNAL query";
# Connection default
# Reaping: ALTER TABLE db1.t1 ADD INDEX(value)
DROP DATABASE db1;
# Test 2: Primary index (implicit), should block reads.
CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb;
SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
# Sending:
ALTER TABLE t1 ADD UNIQUE INDEX(a);
# Connection con1
SET DEBUG_SYNC= "now WAIT_FOR manage";
USE test;
# Sending:
SELECT * FROM t1;
# Connection con2
# Waiting for SELECT to be blocked by the metadata lock on t1
SET DEBUG_SYNC= "now SIGNAL query";
# Connection default
# Reaping: ALTER TABLE t1 ADD UNIQUE INDEX(a)
# Connection con1
# Reaping: SELECT * FROM t1
a b
# Test 3: Primary index (explicit), should block reads.
# Connection default
ALTER TABLE t1 DROP INDEX a;
SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
# Sending:
ALTER TABLE t1 ADD PRIMARY KEY (a);
# Connection con1
SET DEBUG_SYNC= "now WAIT_FOR manage";
# Sending:
SELECT * FROM t1;
# Connection con2
# Waiting for SELECT to be blocked by the metadata lock on t1
SET DEBUG_SYNC= "now SIGNAL query";
# Connection default
# Reaping: ALTER TABLE t1 ADD PRIMARY KEY (a)
# Connection con1
# Reaping: SELECT * FROM t1
a b
# Test 4: Secondary unique index, should not block reads.
# Connection default
SET DEBUG_SYNC= "RESET";
DROP TABLE t1;
...@@ -147,6 +147,139 @@ SET DEBUG_SYNC= 'RESET'; ...@@ -147,6 +147,139 @@ SET DEBUG_SYNC= 'RESET';
disconnect con1; disconnect con1;
--echo #
--echo # Bug#42230 during add index, cannot do queries on storage engines
--echo # that implement add_index
--echo #
--disable_warnings
DROP DATABASE IF EXISTS db1;
DROP TABLE IF EXISTS t1;
--enable_warnings
connect(con1,localhost,root);
connect(con2,localhost,root);
--echo # Test 1: Secondary index, should not block reads (original test case).
--echo # Connection default
connection default;
CREATE DATABASE db1;
CREATE TABLE db1.t1(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, value INT) engine=innodb;
INSERT INTO db1.t1(value) VALUES (1), (2);
SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
--echo # Sending:
--send ALTER TABLE db1.t1 ADD INDEX(value)
--echo # Connection con1
connection con1;
SET DEBUG_SYNC= "now WAIT_FOR manage";
# Neither of these two statements should be blocked
USE db1;
SELECT * FROM t1;
SET DEBUG_SYNC= "now SIGNAL query";
--echo # Connection default
connection default;
--echo # Reaping: ALTER TABLE db1.t1 ADD INDEX(value)
--reap
DROP DATABASE db1;
--echo # Test 2: Primary index (implicit), should block reads.
CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb;
SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
--echo # Sending:
--send ALTER TABLE t1 ADD UNIQUE INDEX(a)
--echo # Connection con1
connection con1;
SET DEBUG_SYNC= "now WAIT_FOR manage";
USE test;
--echo # Sending:
--send SELECT * FROM t1
--echo # Connection con2
connection con2;
--echo # Waiting for SELECT to be blocked by the metadata lock on t1
let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
WHERE state= 'Waiting for table metadata lock'
AND info='SELECT * FROM t1';
--source include/wait_condition.inc
SET DEBUG_SYNC= "now SIGNAL query";
--echo # Connection default
connection default;
--echo # Reaping: ALTER TABLE t1 ADD UNIQUE INDEX(a)
--reap
--echo # Connection con1
connection con1;
--echo # Reaping: SELECT * FROM t1
--reap
--echo # Test 3: Primary index (explicit), should block reads.
--echo # Connection default
connection default;
ALTER TABLE t1 DROP INDEX a;
SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
--echo # Sending:
--send ALTER TABLE t1 ADD PRIMARY KEY (a)
--echo # Connection con1
connection con1;
SET DEBUG_SYNC= "now WAIT_FOR manage";
--echo # Sending:
--send SELECT * FROM t1
--echo # Connection con2
connection con2;
--echo # Waiting for SELECT to be blocked by the metadata lock on t1
let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
WHERE state= 'Waiting for table metadata lock'
AND info='SELECT * FROM t1';
--source include/wait_condition.inc
SET DEBUG_SYNC= "now SIGNAL query";
--echo # Connection default
connection default;
--echo # Reaping: ALTER TABLE t1 ADD PRIMARY KEY (a)
--reap
--echo # Connection con1
connection con1;
--echo # Reaping: SELECT * FROM t1
--reap
--echo # Test 4: Secondary unique index, should not block reads.
# This requires HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE to be supported
# by InnoDB. Adding this flag currently introduces a regression so
# this test is disabled until the regression has been fixed.
--echo # Connection default
connection default;
#SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query";
#--echo # Sending:
#--send ALTER TABLE t1 ADD UNIQUE (b)
#--echo # Connection con1
#connection con1;
#SET DEBUG_SYNC= "now WAIT_FOR manage";
#SELECT * FROM t1;
#SET DEBUG_SYNC= "now SIGNAL query";
#--echo # Connection default
#connection default;
#--echo # Reaping: ALTER TABLE t1 ADD UNIQUE (b)
#--reap
disconnect con1;
disconnect con2;
SET DEBUG_SYNC= "RESET";
DROP TABLE t1;
# Check that all connections opened by test cases in this file are really # Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence. # gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
/* Copyright 2005-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. /* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -6432,28 +6432,28 @@ uint ha_partition::alter_table_flags(uint flags) ...@@ -6432,28 +6432,28 @@ uint ha_partition::alter_table_flags(uint flags)
already altered, partitions. So both ADD and DROP can only be supported in already altered, partitions. So both ADD and DROP can only be supported in
pairs. pairs.
*/ */
flags_to_check= HA_ONLINE_ADD_INDEX_NO_WRITES; flags_to_check= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
flags_to_check|= HA_ONLINE_DROP_INDEX_NO_WRITES; flags_to_check|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
if ((flags_to_return & flags_to_check) != flags_to_check) if ((flags_to_return & flags_to_check) != flags_to_check)
flags_to_return&= ~flags_to_check; flags_to_return&= ~flags_to_check;
flags_to_check= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES; flags_to_check= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
flags_to_check|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES; flags_to_check|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
if ((flags_to_return & flags_to_check) != flags_to_check) if ((flags_to_return & flags_to_check) != flags_to_check)
flags_to_return&= ~flags_to_check; flags_to_return&= ~flags_to_check;
flags_to_check= HA_ONLINE_ADD_PK_INDEX_NO_WRITES; flags_to_check= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
flags_to_check|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES; flags_to_check|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
if ((flags_to_return & flags_to_check) != flags_to_check) if ((flags_to_return & flags_to_check) != flags_to_check)
flags_to_return&= ~flags_to_check; flags_to_return&= ~flags_to_check;
flags_to_check= HA_ONLINE_ADD_INDEX; flags_to_check= HA_INPLACE_ADD_INDEX_NO_WRITE;
flags_to_check|= HA_ONLINE_DROP_INDEX; flags_to_check|= HA_INPLACE_DROP_INDEX_NO_WRITE;
if ((flags_to_return & flags_to_check) != flags_to_check) if ((flags_to_return & flags_to_check) != flags_to_check)
flags_to_return&= ~flags_to_check; flags_to_return&= ~flags_to_check;
flags_to_check= HA_ONLINE_ADD_UNIQUE_INDEX; flags_to_check= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
flags_to_check|= HA_ONLINE_DROP_UNIQUE_INDEX; flags_to_check|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
if ((flags_to_return & flags_to_check) != flags_to_check) if ((flags_to_return & flags_to_check) != flags_to_check)
flags_to_return&= ~flags_to_check; flags_to_return&= ~flags_to_check;
flags_to_check= HA_ONLINE_ADD_PK_INDEX; flags_to_check= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
flags_to_check|= HA_ONLINE_DROP_PK_INDEX; flags_to_check|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
if ((flags_to_return & flags_to_check) != flags_to_check) if ((flags_to_return & flags_to_check) != flags_to_check)
flags_to_return&= ~flags_to_check; flags_to_return&= ~flags_to_check;
DBUG_RETURN(flags_to_return); DBUG_RETURN(flags_to_return);
......
#ifndef HANDLER_INCLUDED #ifndef HANDLER_INCLUDED
#define HANDLER_INCLUDED #define HANDLER_INCLUDED
/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -172,28 +172,31 @@ ...@@ -172,28 +172,31 @@
bits in alter_table_flags: bits in alter_table_flags:
*/ */
/* /*
These bits are set if different kinds of indexes can be created These bits are set if different kinds of indexes can be created or dropped
off-line without re-create of the table (but with a table lock). in-place without re-creating the table using a temporary table.
NO_READ_WRITE indicates that the handler needs concurrent reads and writes
of table data to be blocked.
Partitioning needs both ADD and DROP to be supported by its underlying Partitioning needs both ADD and DROP to be supported by its underlying
handlers, due to error handling, see bug#57778. handlers, due to error handling, see bug#57778.
*/ */
#define HA_ONLINE_ADD_INDEX_NO_WRITES (1L << 0) /*add index w/lock*/ #define HA_INPLACE_ADD_INDEX_NO_READ_WRITE (1L << 0)
#define HA_ONLINE_DROP_INDEX_NO_WRITES (1L << 1) /*drop index w/lock*/ #define HA_INPLACE_DROP_INDEX_NO_READ_WRITE (1L << 1)
#define HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES (1L << 2) /*add unique w/lock*/ #define HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE (1L << 2)
#define HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES (1L << 3) /*drop uniq. w/lock*/ #define HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE (1L << 3)
#define HA_ONLINE_ADD_PK_INDEX_NO_WRITES (1L << 4) /*add prim. w/lock*/ #define HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE (1L << 4)
#define HA_ONLINE_DROP_PK_INDEX_NO_WRITES (1L << 5) /*drop prim. w/lock*/ #define HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE (1L << 5)
/* /*
These are set if different kinds of indexes can be created on-line These are set if different kinds of indexes can be created or dropped
(without a table lock). If a handler is capable of one or more of in-place while still allowing concurrent reads (but not writes) of table
these, it should also set the corresponding *_NO_WRITES bit(s). data. If a handler is capable of one or more of these, it should also set
the corresponding *_NO_READ_WRITE bit(s).
*/ */
#define HA_ONLINE_ADD_INDEX (1L << 6) /*add index online*/ #define HA_INPLACE_ADD_INDEX_NO_WRITE (1L << 6)
#define HA_ONLINE_DROP_INDEX (1L << 7) /*drop index online*/ #define HA_INPLACE_DROP_INDEX_NO_WRITE (1L << 7)
#define HA_ONLINE_ADD_UNIQUE_INDEX (1L << 8) /*add unique online*/ #define HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE (1L << 8)
#define HA_ONLINE_DROP_UNIQUE_INDEX (1L << 9) /*drop uniq. online*/ #define HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE (1L << 9)
#define HA_ONLINE_ADD_PK_INDEX (1L << 10)/*add prim. online*/ #define HA_INPLACE_ADD_PK_INDEX_NO_WRITE (1L << 10)
#define HA_ONLINE_DROP_PK_INDEX (1L << 11)/*drop prim. online*/ #define HA_INPLACE_DROP_PK_INDEX_NO_WRITE (1L << 11)
/* /*
HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is
supported at all. supported at all.
......
/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -4745,7 +4745,7 @@ mysql_discard_or_import_tablespace(THD *thd, ...@@ -4745,7 +4745,7 @@ mysql_discard_or_import_tablespace(THD *thd,
@details Checks if any index is being modified (present as both DROP INDEX @details Checks if any index is being modified (present as both DROP INDEX
and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling
online ALTER TABLE. in-place ALTER TABLE.
@param table The table being altered @param table The table being altered
@param alter_info The ALTER TABLE structure @param alter_info The ALTER TABLE structure
...@@ -4861,7 +4861,7 @@ mysql_compare_tables(TABLE *table, ...@@ -4861,7 +4861,7 @@ mysql_compare_tables(TABLE *table,
like to keep mysql_compare_tables() idempotent (not altering any like to keep mysql_compare_tables() idempotent (not altering any
of the arguments) we create a copy of alter_info here and of the arguments) we create a copy of alter_info here and
pass it to mysql_prepare_create_table, then use the result pass it to mysql_prepare_create_table, then use the result
to evaluate possibility of fast ALTER TABLE, and then to evaluate possibility of in-place ALTER TABLE, and then
destroy the copy. destroy the copy.
*/ */
Alter_info tmp_alter_info(*alter_info, thd->mem_root); Alter_info tmp_alter_info(*alter_info, thd->mem_root);
...@@ -4902,9 +4902,9 @@ mysql_compare_tables(TABLE *table, ...@@ -4902,9 +4902,9 @@ mysql_compare_tables(TABLE *table,
There was a bug prior to mysql-4.0.25. Number of null fields was There was a bug prior to mysql-4.0.25. Number of null fields was
calculated incorrectly. As a result frm and data files gets out of calculated incorrectly. As a result frm and data files gets out of
sync after fast alter table. There is no way to determine by which sync after in-place alter table. There is no way to determine by which
mysql version (in 4.0 and 4.1 branches) table was created, thus we mysql version (in 4.0 and 4.1 branches) table was created, thus we
disable fast alter table for all tables created by mysql versions disable in-place alter table for all tables created by mysql versions
prior to 5.0 branch. prior to 5.0 branch.
See BUG#6236. See BUG#6236.
*/ */
...@@ -4927,7 +4927,7 @@ mysql_compare_tables(TABLE *table, ...@@ -4927,7 +4927,7 @@ mysql_compare_tables(TABLE *table,
} }
/* /*
Use transformed info to evaluate possibility of fast ALTER TABLE Use transformed info to evaluate possibility of in-place ALTER TABLE
but use the preserved field to persist modifications. but use the preserved field to persist modifications.
*/ */
new_field_it.init(alter_info->create_list); new_field_it.init(alter_info->create_list);
...@@ -5207,7 +5207,7 @@ blob_length_by_type(enum_field_types type) ...@@ -5207,7 +5207,7 @@ blob_length_by_type(enum_field_types type)
semantic checks. semantic checks.
This function is invoked when we know that we're going to This function is invoked when we know that we're going to
perform ALTER TABLE via a temporary table -- i.e. fast ALTER TABLE perform ALTER TABLE via a temporary table -- i.e. in-place ALTER TABLE
is not possible, perhaps because the ALTER statement contains is not possible, perhaps because the ALTER statement contains
instructions that require change in table data, not only in instructions that require change in table data, not only in
table definition or indexes. table definition or indexes.
...@@ -6102,7 +6102,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6102,7 +6102,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
} }
/* /*
If there are index changes only, try to do them online. "Index If there are index changes only, try to do them in-place. "Index
changes only" means also that the handler for the table does not changes only" means also that the handler for the table does not
change. The table is open and locked. The handler can be accessed. change. The table is open and locked. The handler can be accessed.
*/ */
...@@ -6110,8 +6110,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6110,8 +6110,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{ {
int pk_changed= 0; int pk_changed= 0;
ulong alter_flags= 0; ulong alter_flags= 0;
ulong needed_online_flags= 0; ulong needed_inplace_with_read_flags= 0;
ulong needed_fast_flags= 0; ulong needed_inplace_flags= 0;
KEY *key; KEY *key;
uint *idx_p; uint *idx_p;
uint *idx_end_p; uint *idx_end_p;
...@@ -6135,8 +6135,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6135,8 +6135,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{ {
DBUG_PRINT("info", ("Dropping primary key")); DBUG_PRINT("info", ("Dropping primary key"));
/* Primary key. */ /* Primary key. */
needed_online_flags|= HA_ONLINE_DROP_PK_INDEX; needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES; needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
pk_changed++; pk_changed++;
candidate_key_count--; candidate_key_count--;
} }
...@@ -6146,8 +6146,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6146,8 +6146,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
bool is_candidate_key= true; bool is_candidate_key= true;
/* Non-primary unique key. */ /* Non-primary unique key. */
needed_online_flags|= HA_ONLINE_DROP_UNIQUE_INDEX; needed_inplace_with_read_flags|=
needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES; HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
/* /*
Check if all fields in key are declared Check if all fields in key are declared
...@@ -6166,12 +6167,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6166,12 +6167,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
else else
{ {
/* Non-unique key. */ /* Non-unique key. */
needed_online_flags|= HA_ONLINE_DROP_INDEX; needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE;
needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES; needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
} }
} }
no_pk= ((table->s->primary_key == MAX_KEY) || no_pk= ((table->s->primary_key == MAX_KEY) ||
(needed_online_flags & HA_ONLINE_DROP_PK_INDEX)); (needed_inplace_with_read_flags &
HA_INPLACE_DROP_PK_INDEX_NO_WRITE));
/* Check added indexes. */ /* Check added indexes. */
for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
idx_p < idx_end_p; idx_p < idx_end_p;
...@@ -6209,57 +6211,59 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6209,57 +6211,59 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{ {
DBUG_PRINT("info", ("Adding primary key")); DBUG_PRINT("info", ("Adding primary key"));
/* Primary key. */ /* Primary key. */
needed_online_flags|= HA_ONLINE_ADD_PK_INDEX; needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES; needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
pk_changed++; pk_changed++;
no_pk= false; no_pk= false;
} }
else else
{ {
/* Non-primary unique key. */ /* Non-primary unique key. */
needed_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX; needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES; needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
} }
} }
else else
{ {
/* Non-unique key. */ /* Non-unique key. */
needed_online_flags|= HA_ONLINE_ADD_INDEX; needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE;
needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES; needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
} }
} }
if ((candidate_key_count > 0) && if ((candidate_key_count > 0) &&
(needed_online_flags & HA_ONLINE_DROP_PK_INDEX)) (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE))
{ {
/* /*
Dropped primary key when there is some other unique Dropped primary key when there is some other unique
not null key that should be converted to primary key not null key that should be converted to primary key
*/ */
needed_online_flags|= HA_ONLINE_ADD_PK_INDEX; needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES; needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
pk_changed= 2; pk_changed= 2;
} }
DBUG_PRINT("info", ("needed_online_flags: 0x%lx, needed_fast_flags: 0x%lx", DBUG_PRINT("info",
needed_online_flags, needed_fast_flags)); ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx",
needed_inplace_with_read_flags, needed_inplace_flags));
/* /*
Online or fast add/drop index is possible only if In-place add/drop index is possible only if
the primary key is not added and dropped in the same statement. the primary key is not added and dropped in the same statement.
Otherwise we have to recreate the table. Otherwise we have to recreate the table.
need_copy_table is no-zero at this place. need_copy_table is no-zero at this place.
*/ */
if ( pk_changed < 2 ) if ( pk_changed < 2 )
{ {
if ((alter_flags & needed_online_flags) == needed_online_flags) if ((alter_flags & needed_inplace_with_read_flags) ==
needed_inplace_with_read_flags)
{ {
/* All required online flags are present. */ /* All required in-place flags to allow concurrent reads are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY; need_copy_table= ALTER_TABLE_METADATA_ONLY;
need_lock_for_indexes= FALSE; need_lock_for_indexes= FALSE;
} }
else if ((alter_flags & needed_fast_flags) == needed_fast_flags) else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags)
{ {
/* All required fast flags are present. */ /* All required in-place flags are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY; need_copy_table= ALTER_TABLE_METADATA_ONLY;
} }
} }
...@@ -6413,10 +6417,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6413,10 +6417,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
} }
else else
{ {
if (!table->s->tmp_table && /*
Ensure that we will upgrade the metadata lock if
handler::enable/disable_indexes() will be called.
*/
if (alter_info->keys_onoff != LEAVE_AS_IS ||
table->file->indexes_are_disabled())
need_lock_for_indexes= true;
if (!table->s->tmp_table && need_lock_for_indexes &&
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err_new_table_cleanup; goto err_new_table_cleanup;
thd_proc_info(thd, "manage keys"); thd_proc_info(thd, "manage keys");
DEBUG_SYNC(thd, "alter_table_manage_keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(), alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff); alter_info->keys_onoff);
error= trans_commit_stmt(thd); error= trans_commit_stmt(thd);
......
...@@ -2574,11 +2574,12 @@ innobase_alter_table_flags( ...@@ -2574,11 +2574,12 @@ innobase_alter_table_flags(
/*=======================*/ /*=======================*/
uint flags) uint flags)
{ {
return(HA_ONLINE_ADD_INDEX_NO_WRITES return(HA_INPLACE_ADD_INDEX_NO_READ_WRITE
| HA_ONLINE_DROP_INDEX_NO_WRITES | HA_INPLACE_ADD_INDEX_NO_WRITE
| HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES | HA_INPLACE_DROP_INDEX_NO_READ_WRITE
| HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES | HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE
| HA_ONLINE_ADD_PK_INDEX_NO_WRITES); | HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE
| HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE);
} }
/*****************************************************************//** /*****************************************************************//**
......
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