Commit 628bc576 authored by Jan Lindström's avatar Jan Lindström

MDEV-9918: [ERROR] mysqld got signal 11 during ALTER TABLE `name` COLUMN ADD

Problem was that in-place online alter table was used on a table
that had mismatch between MySQL frm file and InnoDB data dictionary.
Fixed so that traditional "Copy" method is used if the MySQL frm
and InnoDB data dictionary is not consistent.
parent e5410da1
call mtr.add_suppression("Table .* has a primary key in InnoDB data dictionary, but not in MySQL.*");
call mtr.add_suppression("InnoDB: Table .* contains .* indexes inside InnoDB, which is different from the number of indexes .* defined in the MySQL.*");
create table t1 (pk int, i int, key(i)) engine=InnoDB;
insert into t1 values (1,1),(2,2);
flush tables;
# Save the .frm file without the PK
alter table t1 add primary key (pk);
# Stop the server, replace the frm with the old one and restart the server
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`pk` int(11) DEFAULT NULL,
`i` int(11) DEFAULT NULL,
KEY `i` (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Warnings:
Warning 1082 InnoDB: Table test/t1 has a primary key in InnoDB data dictionary, but not in MySQL!
Warning 1082 InnoDB: Table test/t1 contains 2 indexes inside InnoDB, which is different from the number of indexes 1 defined in the MySQL
select * from t1;
pk i
1 1
2 2
alter table t1 add j int;
Warnings:
Warning 1082 InnoDB: Table test/t1 contains 2 indexes inside InnoDB, which is different from the number of indexes 1 defined in the MySQL
show warnings;
Level Code Message
Warning 1082 InnoDB: Table test/t1 contains 2 indexes inside InnoDB, which is different from the number of indexes 1 defined in the MySQL
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`pk` int(11) DEFAULT NULL,
`i` int(11) DEFAULT NULL,
`j` int(11) DEFAULT NULL,
KEY `i` (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 add primary key (pk);
show warnings;
Level Code Message
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`pk` int(11) NOT NULL DEFAULT '0',
`i` int(11) DEFAULT NULL,
`j` int(11) DEFAULT NULL,
PRIMARY KEY (`pk`),
KEY `i` (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
--source include/have_innodb.inc
--source include/not_embedded.inc
#
# MDEV-9918: [ERROR] mysqld got signal 11 during ALTER TABLE `name` COLUMN ADD
#
call mtr.add_suppression("Table .* has a primary key in InnoDB data dictionary, but not in MySQL.*");
call mtr.add_suppression("InnoDB: Table .* contains .* indexes inside InnoDB, which is different from the number of indexes .* defined in the MySQL.*");
create table t1 (pk int, i int, key(i)) engine=InnoDB;
insert into t1 values (1,1),(2,2);
--let $datadir= `select @@datadir`
flush tables;
--echo # Save the .frm file without the PK
--copy_file $datadir/test/t1.frm $MYSQLTEST_VARDIR/tmp/t1.frm
alter table t1 add primary key (pk);
--echo # Stop the server, replace the frm with the old one and restart the server
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--shutdown_server 10
--source include/wait_until_disconnected.inc
--remove_file $datadir/test/t1.frm
--copy_file $MYSQLTEST_VARDIR/tmp/t1.frm $datadir/test/t1.frm
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc
show create table t1;
select * from t1;
alter table t1 add j int;
show warnings;
show create table t1;
alter table t1 add primary key (pk);
show warnings;
show create table t1;
# Cleanup
drop table t1;
......@@ -4,7 +4,7 @@ Copyright (c) 2000, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, 2009 Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2013, 2014 SkySQL Ab. All Rights Reserved.
Copyright (c) 2013, 2016, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
......@@ -5513,20 +5513,14 @@ table_opened:
prebuilt->clust_index_was_generated = FALSE;
if (UNIV_UNLIKELY(primary_key >= MAX_KEY)) {
sql_print_error("Table %s has a primary key in "
"InnoDB data dictionary, but not "
"in MySQL!", name);
ib_table->dict_frm_mismatch = DICT_FRM_NO_PK;
/* This mismatch could cause further problems
if not attended, bring this to the user's attention
by printing a warning in addition to log a message
in the errorlog */
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has a "
"primary key in InnoDB data "
"dictionary, but not in "
"MySQL!", name);
ib_push_frm_error(thd, ib_table, table, 0, true);
/* If primary_key >= MAX_KEY, its (primary_key)
value could be out of bound if continue to index
......@@ -5573,27 +5567,14 @@ table_opened:
}
} else {
if (primary_key != MAX_KEY) {
sql_print_error(
"Table %s has no primary key in InnoDB data "
"dictionary, but has one in MySQL! If you "
"created the table with a MySQL version < "
"3.23.54 and did not define a primary key, "
"but defined a unique key with all non-NULL "
"columns, then MySQL internally treats that "
"key as the primary key. You can fix this "
"error by dump + DROP + CREATE + reimport "
"of the table.", name);
ib_table->dict_frm_mismatch = DICT_NO_PK_FRM_HAS;
/* This mismatch could cause further problems
if not attended, bring this to the user attention
by printing a warning in addition to log a message
in the errorlog */
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has no "
"primary key in InnoDB data "
"dictionary, but has one in "
"MySQL!", name);
ib_push_frm_error(thd, ib_table, table, 0, true);
}
prebuilt->clust_index_was_generated = TRUE;
......@@ -11669,12 +11650,8 @@ ha_innobase::info_low(
}
if (table->s->keys != num_innodb_index) {
sql_print_error("InnoDB: Table %s contains %lu "
"indexes inside InnoDB, which "
"is different from the number of "
"indexes %u defined in the MySQL ",
ib_table->name, num_innodb_index,
table->s->keys);
ib_table->dict_frm_mismatch = DICT_FRM_INCONSISTENT_KEYS;
ib_push_frm_error(user_thd, ib_table, table, num_innodb_index, true);
}
if (!(flag & HA_STATUS_NO_LOCK)) {
......@@ -11694,15 +11671,8 @@ ha_innobase::info_low(
dict_index_t* index = innobase_get_index(i);
if (index == NULL) {
sql_print_error("Table %s contains fewer "
"indexes inside InnoDB than "
"are defined in the MySQL "
".frm file. Have you mixed up "
".frm files from different "
"installations? See "
REFMAN
"innodb-troubleshooting.html\n",
ib_table->name);
ib_table->dict_frm_mismatch = DICT_FRM_INCONSISTENT_KEYS;
ib_push_frm_error(user_thd, ib_table, table, num_innodb_index, true);
break;
}
......@@ -17874,3 +17844,96 @@ ib_push_warning(
my_free(buf);
va_end(args);
}
/********************************************************************//**
Helper function to push frm mismatch error to error log and
if needed to sql-layer. */
UNIV_INTERN
void
ib_push_frm_error(
/*==============*/
THD* thd, /*!< in: MySQL thd */
dict_table_t* ib_table, /*!< in: InnoDB table */
TABLE* table, /*!< in: MySQL table */
ulint n_keys, /*!< in: InnoDB #keys */
bool push_warning) /*!< in: print warning ? */
{
switch (ib_table->dict_frm_mismatch) {
case DICT_FRM_NO_PK:
sql_print_error("Table %s has a primary key in "
"InnoDB data dictionary, but not "
"in MySQL!"
" Have you mixed up "
".frm files from different "
"installations? See "
REFMAN
"innodb-troubleshooting.html\n",
ib_table->name);
if (push_warning) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has a "
"primary key in InnoDB data "
"dictionary, but not in "
"MySQL!", ib_table->name);
}
break;
case DICT_NO_PK_FRM_HAS:
sql_print_error(
"Table %s has no primary key in InnoDB data "
"dictionary, but has one in MySQL! If you "
"created the table with a MySQL version < "
"3.23.54 and did not define a primary key, "
"but defined a unique key with all non-NULL "
"columns, then MySQL internally treats that "
"key as the primary key. You can fix this "
"error by dump + DROP + CREATE + reimport "
"of the table.", ib_table->name);
if (push_warning) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has no "
"primary key in InnoDB data "
"dictionary, but has one in "
"MySQL!",
ib_table->name);
}
break;
case DICT_FRM_INCONSISTENT_KEYS:
sql_print_error("InnoDB: Table %s contains %lu "
"indexes inside InnoDB, which "
"is different from the number of "
"indexes %u defined in the MySQL "
" Have you mixed up "
".frm files from different "
"installations? See "
REFMAN
"innodb-troubleshooting.html\n",
ib_table->name, n_keys,
table->s->keys);
if (push_warning) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s contains %lu "
"indexes inside InnoDB, which "
"is different from the number of "
"indexes %u defined in the MySQL ",
ib_table->name, n_keys,
table->s->keys);
}
break;
case DICT_FRM_CONSISTENT:
default:
sql_print_error("InnoDB: Table %s is consistent "
"on InnoDB data dictionary and MySQL "
" FRM file.",
ib_table->name);
ut_error;
break;
}
}
/*****************************************************************************
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2016, 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
......@@ -640,3 +641,16 @@ innobase_copy_frm_flags_from_table_share(
/*=====================================*/
dict_table_t* innodb_table, /*!< in/out: InnoDB table */
const TABLE_SHARE* table_share); /*!< in: table share */
/********************************************************************//**
Helper function to push frm mismatch error to error log and
if needed to sql-layer. */
UNIV_INTERN
void
ib_push_frm_error(
/*==============*/
THD* thd, /*!< in: MySQL thd */
dict_table_t* ib_table, /*!< in: InnoDB table */
TABLE* table, /*!< in: MySQL table */
ulint n_keys, /*!< in: InnoDB #keys */
bool push_warning); /*!< in: print warning ? */
......@@ -435,6 +435,20 @@ ha_innobase::check_if_supported_inplace_alter(
}
}
ulint n_indexes = UT_LIST_GET_LEN((prebuilt->table)->indexes);
/* If InnoDB dictionary and MySQL frm file are not consistent
use "Copy" method. */
if (prebuilt->table->dict_frm_mismatch) {
ha_alter_info->unsupported_reason = innobase_get_err_msg(
ER_NO_SUCH_INDEX);
ib_push_frm_error(user_thd, prebuilt->table, altered_table,
n_indexes, true);
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
}
/* We should be able to do the operation in-place.
See if we can do it online (LOCK=NONE). */
bool online = true;
......
......@@ -2,6 +2,7 @@
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2015, 2016, 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
......@@ -903,6 +904,18 @@ if table->memcached_sync_count == DICT_TABLE_IN_DDL means there's DDL running on
the table, DML from memcached will be blocked. */
#define DICT_TABLE_IN_DDL -1
/** These are used when MySQL FRM and InnoDB data dictionary are
in inconsistent state. */
typedef enum {
DICT_FRM_CONSISTENT = 0, /*!< Consistent state */
DICT_FRM_NO_PK = 1, /*!< MySQL has no primary key
but InnoDB dictionary has
non-generated one. */
DICT_NO_PK_FRM_HAS = 2, /*!< MySQL has primary key but
InnoDB dictionary has not. */
DICT_FRM_INCONSISTENT_KEYS = 3 /*!< Key count mismatch */
} dict_frm_t;
/** Data structure for a database table. Most fields will be
initialized to 0, NULL or FALSE in dict_mem_table_create(). */
struct dict_table_t{
......@@ -961,6 +974,10 @@ struct dict_table_t{
/*!< True if the table belongs to a system
database (mysql, information_schema or
performance_schema) */
dict_frm_t dict_frm_mismatch;
/*!< !DICT_FRM_CONSISTENT==0 if data
dictionary information and
MySQL FRM information mismatch. */
#ifndef UNIV_HOTBACKUP
hash_node_t name_hash; /*!< hash chain node */
hash_node_t id_hash; /*!< hash chain node */
......
......@@ -4,7 +4,7 @@ Copyright (c) 2000, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, 2009 Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2013, 2014 SkySQL Ab. All Rights Reserved.
Copyright (c) 2013, 2016, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
......@@ -5950,20 +5950,14 @@ table_opened:
prebuilt->clust_index_was_generated = FALSE;
if (UNIV_UNLIKELY(primary_key >= MAX_KEY)) {
sql_print_error("Table %s has a primary key in "
"InnoDB data dictionary, but not "
"in MySQL!", name);
ib_table->dict_frm_mismatch = DICT_FRM_NO_PK;
/* This mismatch could cause further problems
if not attended, bring this to the user's attention
by printing a warning in addition to log a message
in the errorlog */
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has a "
"primary key in InnoDB data "
"dictionary, but not in "
"MySQL!", name);
ib_push_frm_error(thd, ib_table, table, 0, true);
/* If primary_key >= MAX_KEY, its (primary_key)
value could be out of bound if continue to index
......@@ -6010,27 +6004,14 @@ table_opened:
}
} else {
if (primary_key != MAX_KEY) {
sql_print_error(
"Table %s has no primary key in InnoDB data "
"dictionary, but has one in MySQL! If you "
"created the table with a MySQL version < "
"3.23.54 and did not define a primary key, "
"but defined a unique key with all non-NULL "
"columns, then MySQL internally treats that "
"key as the primary key. You can fix this "
"error by dump + DROP + CREATE + reimport "
"of the table.", name);
ib_table->dict_frm_mismatch = DICT_NO_PK_FRM_HAS;
/* This mismatch could cause further problems
if not attended, bring this to the user attention
by printing a warning in addition to log a message
in the errorlog */
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has no "
"primary key in InnoDB data "
"dictionary, but has one in "
"MySQL!", name);
ib_push_frm_error(thd, ib_table, table, 0, true);
}
prebuilt->clust_index_was_generated = TRUE;
......@@ -12244,12 +12225,8 @@ ha_innobase::info_low(
}
if (table->s->keys != num_innodb_index) {
sql_print_error("InnoDB: Table %s contains %lu "
"indexes inside InnoDB, which "
"is different from the number of "
"indexes %u defined in the MySQL ",
ib_table->name, num_innodb_index,
table->s->keys);
ib_table->dict_frm_mismatch = DICT_FRM_INCONSISTENT_KEYS;
ib_push_frm_error(user_thd, ib_table, table, num_innodb_index, true);
}
if (!(flag & HA_STATUS_NO_LOCK)) {
......@@ -12269,15 +12246,8 @@ ha_innobase::info_low(
dict_index_t* index = innobase_get_index(i);
if (index == NULL) {
sql_print_error("Table %s contains fewer "
"indexes inside InnoDB than "
"are defined in the MySQL "
".frm file. Have you mixed up "
".frm files from different "
"installations? See "
REFMAN
"innodb-troubleshooting.html\n",
ib_table->name);
ib_table->dict_frm_mismatch = DICT_FRM_INCONSISTENT_KEYS;
ib_push_frm_error(user_thd, ib_table, table, num_innodb_index, true);
break;
}
......@@ -19182,3 +19152,96 @@ ib_push_warning(
my_free(buf);
va_end(args);
}
/********************************************************************//**
Helper function to push frm mismatch error to error log and
if needed to sql-layer. */
UNIV_INTERN
void
ib_push_frm_error(
/*==============*/
THD* thd, /*!< in: MySQL thd */
dict_table_t* ib_table, /*!< in: InnoDB table */
TABLE* table, /*!< in: MySQL table */
ulint n_keys, /*!< in: InnoDB #keys */
bool push_warning) /*!< in: print warning ? */
{
switch (ib_table->dict_frm_mismatch) {
case DICT_FRM_NO_PK:
sql_print_error("Table %s has a primary key in "
"InnoDB data dictionary, but not "
"in MySQL!"
" Have you mixed up "
".frm files from different "
"installations? See "
REFMAN
"innodb-troubleshooting.html\n",
ib_table->name);
if (push_warning) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has a "
"primary key in InnoDB data "
"dictionary, but not in "
"MySQL!", ib_table->name);
}
break;
case DICT_NO_PK_FRM_HAS:
sql_print_error(
"Table %s has no primary key in InnoDB data "
"dictionary, but has one in MySQL! If you "
"created the table with a MySQL version < "
"3.23.54 and did not define a primary key, "
"but defined a unique key with all non-NULL "
"columns, then MySQL internally treats that "
"key as the primary key. You can fix this "
"error by dump + DROP + CREATE + reimport "
"of the table.", ib_table->name);
if (push_warning) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s has no "
"primary key in InnoDB data "
"dictionary, but has one in "
"MySQL!",
ib_table->name);
}
break;
case DICT_FRM_INCONSISTENT_KEYS:
sql_print_error("InnoDB: Table %s contains %lu "
"indexes inside InnoDB, which "
"is different from the number of "
"indexes %u defined in the MySQL "
" Have you mixed up "
".frm files from different "
"installations? See "
REFMAN
"innodb-troubleshooting.html\n",
ib_table->name, n_keys,
table->s->keys);
if (push_warning) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_SUCH_INDEX,
"InnoDB: Table %s contains %lu "
"indexes inside InnoDB, which "
"is different from the number of "
"indexes %u defined in the MySQL ",
ib_table->name, n_keys,
table->s->keys);
}
break;
case DICT_FRM_CONSISTENT:
default:
sql_print_error("InnoDB: Table %s is consistent "
"on InnoDB data dictionary and MySQL "
" FRM file.",
ib_table->name);
ut_error;
break;
}
}
/*****************************************************************************
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2016, 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
......@@ -645,3 +646,16 @@ innobase_copy_frm_flags_from_table_share(
/*=====================================*/
dict_table_t* innodb_table, /*!< in/out: InnoDB table */
const TABLE_SHARE* table_share); /*!< in: table share */
/********************************************************************//**
Helper function to push frm mismatch error to error log and
if needed to sql-layer. */
UNIV_INTERN
void
ib_push_frm_error(
/*==============*/
THD* thd, /*!< in: MySQL thd */
dict_table_t* ib_table, /*!< in: InnoDB table */
TABLE* table, /*!< in: MySQL table */
ulint n_keys, /*!< in: InnoDB #keys */
bool push_warning); /*!< in: print warning ? */
......@@ -435,6 +435,20 @@ ha_innobase::check_if_supported_inplace_alter(
}
}
ulint n_indexes = UT_LIST_GET_LEN((prebuilt->table)->indexes);
/* If InnoDB dictionary and MySQL frm file are not consistent
use "Copy" method. */
if (prebuilt->table->dict_frm_mismatch) {
ha_alter_info->unsupported_reason = innobase_get_err_msg(
ER_NO_SUCH_INDEX);
ib_push_frm_error(user_thd, prebuilt->table, altered_table,
n_indexes, true);
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
}
/* We should be able to do the operation in-place.
See if we can do it online (LOCK=NONE). */
bool online = true;
......
......@@ -2,6 +2,7 @@
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2015, 2016, 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
......@@ -914,6 +915,18 @@ if table->memcached_sync_count == DICT_TABLE_IN_DDL means there's DDL running on
the table, DML from memcached will be blocked. */
#define DICT_TABLE_IN_DDL -1
/** These are used when MySQL FRM and InnoDB data dictionary are
in inconsistent state. */
typedef enum {
DICT_FRM_CONSISTENT = 0, /*!< Consistent state */
DICT_FRM_NO_PK = 1, /*!< MySQL has no primary key
but InnoDB dictionary has
non-generated one. */
DICT_NO_PK_FRM_HAS = 2, /*!< MySQL has primary key but
InnoDB dictionary has not. */
DICT_FRM_INCONSISTENT_KEYS = 3 /*!< Key count mismatch */
} dict_frm_t;
/** Data structure for a database table. Most fields will be
initialized to 0, NULL or FALSE in dict_mem_table_create(). */
struct dict_table_t{
......@@ -972,6 +985,10 @@ struct dict_table_t{
/*!< True if the table belongs to a system
database (mysql, information_schema or
performance_schema) */
dict_frm_t dict_frm_mismatch;
/*!< !DICT_FRM_CONSISTENT==0 if data
dictionary information and
MySQL FRM information mismatch. */
#ifndef UNIV_HOTBACKUP
hash_node_t name_hash; /*!< hash chain node */
hash_node_t id_hash; /*!< hash chain node */
......
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