Commit 0627a0f3 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-12586 ALTER TABLE…ALGORITHM=INPLACE fails with non-constant DEFAULT values

ha_innobase::check_if_supported_inplace_alter(): For now, reject
ALGORITHM=INPLACE when a non-constant DEFAULT expression is specified
for ADD COLUMN or for changing a NULL column to NOT NULL.

Later, we should evaluate the non-constant column values in these cases.
parent 85ea3277
......@@ -2,7 +2,7 @@ CREATE TABLE t1 (
`i1` INT(10) UNSIGNED NOT NULL,
`d1` TIMESTAMP NULL DEFAULT NULL
) ENGINE=innodb;
show create table t1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`i1` int(10) unsigned NOT NULL,
......@@ -25,5 +25,26 @@ CREATE TABLE t1 (
) ENGINE=innodb;
INSERT INTO t1 (i1) VALUES (1), (2), (3), (4), (5);
ALTER TABLE t1 CHANGE `d1` `d1` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
drop table t1;
set sql_mode = '';
ALTER TABLE t1 ADD COLUMN d2 TIMESTAMP DEFAULT '2017-05-08 16:23:45',
LOCK=NONE;
ALTER TABLE t1 ADD COLUMN d3 TIMESTAMP DEFAULT d1, LOCK=NONE;
ERROR 0A000: LOCK=NONE is not supported for this operation. Try LOCK=SHARED
ALTER TABLE t1 ADD COLUMN d3 TIMESTAMP DEFAULT d1, ALGORITHM=INPLACE;
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
ALTER TABLE t1 ADD COLUMN d3 TIMESTAMP DEFAULT d1;
SELECT d1-d3, d2 FROM t1;
d1-d3 d2
0 2017-05-08 16:23:45
0 2017-05-08 16:23:45
0 2017-05-08 16:23:45
0 2017-05-08 16:23:45
0 2017-05-08 16:23:45
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`i1` int(10) unsigned NOT NULL,
`d1` timestamp NOT NULL DEFAULT current_timestamp(),
`d2` timestamp NOT NULL DEFAULT '2017-05-08 16:23:45',
`d3` timestamp NOT NULL DEFAULT `d1`
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
......@@ -5,7 +5,7 @@ CREATE TABLE t1 (
`d1` TIMESTAMP NULL DEFAULT NULL
) ENGINE=innodb;
show create table t1;
SHOW CREATE TABLE t1;
INSERT INTO t1 (i1) VALUES (1), (2), (3), (4), (5);
select * from t1;
......@@ -19,9 +19,14 @@ CREATE TABLE t1 (
) ENGINE=innodb;
INSERT INTO t1 (i1) VALUES (1), (2), (3), (4), (5);
ALTER TABLE t1 CHANGE `d1` `d1` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
drop table t1;
set sql_mode = '';
ALTER TABLE t1 ADD COLUMN d2 TIMESTAMP DEFAULT '2017-05-08 16:23:45',
LOCK=NONE;
--error ER_ALTER_OPERATION_NOT_SUPPORTED
ALTER TABLE t1 ADD COLUMN d3 TIMESTAMP DEFAULT d1, LOCK=NONE;
--error ER_ALTER_OPERATION_NOT_SUPPORTED
ALTER TABLE t1 ADD COLUMN d3 TIMESTAMP DEFAULT d1, ALGORITHM=INPLACE;
ALTER TABLE t1 ADD COLUMN d3 TIMESTAMP DEFAULT d1;
SELECT d1-d3, d2 FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
......@@ -722,35 +722,6 @@ ha_innobase::check_if_supported_inplace_alter(
}
}
/* If we have column that has changed from NULL -> NOT NULL
and column default has changed we need to do additional
check. */
if ((ha_alter_info->handler_flags
& Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) &&
(ha_alter_info->handler_flags
& Alter_inplace_info::ALTER_COLUMN_DEFAULT)) {
Alter_info *alter_info = ha_alter_info->alter_info;
List_iterator<Create_field> def_it(alter_info->create_list);
Create_field *def;
while ((def=def_it++)) {
/* If this is first column definition whose SQL type
is TIMESTAMP and it is defined as NOT NULL and
it has either constant default or function default
we must use "Copy" method. */
if (is_timestamp_type(def->sql_type)) {
if ((def->flags & NOT_NULL_FLAG) != 0 && // NOT NULL
(def->default_value != NULL || // constant default ?
def->unireg_check != Field::NONE)) { // function default
ha_alter_info->unsupported_reason = innobase_get_err_msg(
ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
}
break;
}
}
}
ulint n_indexes = UT_LIST_GET_LEN((m_prebuilt->table)->indexes);
/* If InnoDB dictionary and MySQL frm file are not consistent
......@@ -1035,6 +1006,72 @@ ha_innobase::check_if_supported_inplace_alter(
}
}
/* When changing a NULL column to NOT NULL and specifying a
DEFAULT value, ensure that the DEFAULT expression is a constant.
Also, in ADD COLUMN, for now we only support a
constant DEFAULT expression. */
cf_it.rewind();
Field **af = altered_table->field;
while (Create_field* cf = cf_it++) {
DBUG_ASSERT(cf->field
|| (ha_alter_info->handler_flags
& Alter_inplace_info::ADD_COLUMN));
if (const Field* f = cf->field) {
/* This could be changing an existing column
from NULL to NOT NULL. For now, ensure that
the DEFAULT is a constant. */
if (~ha_alter_info->handler_flags
& (Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE
| Alter_inplace_info::ALTER_COLUMN_DEFAULT)
|| (*af)->real_maybe_null()) {
/* This ALTER TABLE is not both changing
a column to NOT NULL and changing the
DEFAULT value of a column, or this column
does allow NULL after the ALTER TABLE. */
goto next_column;
}
/* Find the matching column in the old table. */
Field** fp;
for (fp = table->field; *fp; fp++) {
if (f != *fp) {
continue;
}
if (!f->real_maybe_null()) {
/* The column already is NOT NULL. */
goto next_column;
}
break;
}
/* The column must be found in the old table. */
DBUG_ASSERT(fp < &table->field[table->s->fields]);
}
if (!(*af)->default_value
|| (*af)->default_value->flags == 0) {
/* The NOT NULL column is not
carrying a non-constant DEFAULT. */
goto next_column;
}
/* TODO: Allow NULL column values to
be replaced with a non-constant DEFAULT. */
if (cf->field) {
ha_alter_info->unsupported_reason
= innobase_get_err_msg(
ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
}
DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
next_column:
af++;
}
cf_it.rewind();
DBUG_RETURN(online
? HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
: HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE);
......
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