Commit 93fb92d3 authored by Nikita Malyavin's avatar Nikita Malyavin Committed by Sergei Golubchik

MDEV-29021 ALTER TABLE fails when a stored virtual column is dropped+added

We shouldn't rely on `fill_extra_persistent_columns`, as it only updates
fields which have an index > cols->n_bits (replication bitmap width).
Actually, it should never be used, as its approach is error-prone.

Normal update_virtual_fields+update_default_fields should be done.
parent ea46fdce
...@@ -643,6 +643,67 @@ insert t1 (b) values ('k'); ...@@ -643,6 +643,67 @@ insert t1 (b) values ('k');
insert t1 (b) values ('m'); insert t1 (b) values ('m');
set debug_sync= 'now signal goforit'; set debug_sync= 'now signal goforit';
connection con2; connection con2;
connection default;
drop table t1;
set debug_sync= reset;
#
# MDEV-29021 ALTER TABLE fails when a stored virtual column is dropped and added
#
create table t1 (a char(9), b char(9) as (a) stored) engine=InnoDB;
insert into t1(a) values ('foobar');
set debug_sync= 'now wait_for downgraded';
connection con2;
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
alter ignore table t1 drop b, add b char(3) as (a) stored, algorithm=copy, lock=none;
connection default;
update t1 set a = 'foobarqux';
set debug_sync= 'now signal goforit';
connection con2;
Warnings:
Warning 1265 Data truncated for column 'b' at row 1
Warning 1265 Data truncated for column 'b' at row 2
connection default;
drop table t1;
set debug_sync= reset;
# (duplicate) MDEV-29007 Assertion `marked_for_write_or_computed()'
# failed upon online ADD COLUMN .. FIRST
create table t (a int);
insert into t values (1),(2);
set debug_sync= 'now wait_for downgraded';
connection con2;
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
alter table t add c int first, algorithm=copy, lock=none;
connection default;
insert into t values (3);
set debug_sync= 'now signal goforit';
connection con2;
connection default;
drop table t;
set debug_sync= reset;
# UNIQUE blob duplicates are not ignored.
create table t1 (b blob);
insert into t1 values ('foo'),('bar');
set debug_sync= 'now wait_for downgraded';
connection con2;
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
alter table t1 add unique(b), algorithm=copy, lock=none;
connection default;
insert into t1 values ('qux'),('foo');
set debug_sync= 'now signal goforit';
connection con2;
ERROR 23000: Duplicate entry 'foo' for key 'b'
select * from t1;
b
foo
bar
qux
foo
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`b` blob DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
connection default;
drop table t1; drop table t1;
set debug_sync= reset; set debug_sync= reset;
# #
......
...@@ -796,7 +796,81 @@ set debug_sync= 'now signal goforit'; ...@@ -796,7 +796,81 @@ set debug_sync= 'now signal goforit';
--connection con2 --connection con2
--reap --reap
--connection default
drop table t1;
set debug_sync= reset;
--echo #
--echo # MDEV-29021 ALTER TABLE fails when a stored virtual column is dropped and added
--echo #
create table t1 (a char(9), b char(9) as (a) stored) engine=InnoDB;
insert into t1(a) values ('foobar');
--send set debug_sync= 'now wait_for downgraded'
--connection con2
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
--send alter ignore table t1 drop b, add b char(3) as (a) stored, algorithm=copy, lock=none
--connection default
--reap
update t1 set a = 'foobarqux';
set debug_sync= 'now signal goforit';
--connection con2
--reap
--connection default
drop table t1;
set debug_sync= reset;
--echo # (duplicate) MDEV-29007 Assertion `marked_for_write_or_computed()'
--echo # failed upon online ADD COLUMN .. FIRST
create table t (a int);
insert into t values (1),(2);
--send
set debug_sync= 'now wait_for downgraded';
--connection con2
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
--send
alter table t add c int first, algorithm=copy, lock=none;
--connection default
--reap
insert into t values (3);
set debug_sync= 'now signal goforit';
--connection con2
--reap
--connection default
drop table t;
set debug_sync= reset;
--echo # UNIQUE blob duplicates are not ignored.
create table t1 (b blob);
insert into t1 values ('foo'),('bar');
--send
set debug_sync= 'now wait_for downgraded';
--connection con2
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for goforit';
--send
alter table t1 add unique(b), algorithm=copy, lock=none;
--connection default
--reap
insert into t1 values ('qux'),('foo');
set debug_sync= 'now signal goforit';
--connection con2
--error ER_DUP_ENTRY
--reap
select * from t1;
show create table t1;
# Cleanup
--connection default
drop table t1; drop table t1;
set debug_sync= reset; set debug_sync= reset;
......
...@@ -32,8 +32,24 @@ a z1 z2 ...@@ -32,8 +32,24 @@ a z1 z2
4 5 6 4 5 6
5 6 7 5 6 7
6 7 8 6 7 8
#UPDATE query alter table t1 add column z3 int default(a+2);
connection master; connection master;
insert into t1 values(7);
insert into t1 values(8);
connection slave;
select * from t1 order by a;
a z1 z2 z3
1 2 3 3
2 3 4 4
3 4 5 5
4 5 6 6
5 6 7 7
6 7 8 8
7 8 9 9
8 9 10 10
connection master;
delete from t1 where a > 6;
#UPDATE query
update t1 set a = a+10; update t1 set a = a+10;
select * from t1 order by a; select * from t1 order by a;
a a
...@@ -45,13 +61,13 @@ a ...@@ -45,13 +61,13 @@ a
16 16
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
11 12 13 11 12 13 13
12 13 14 12 13 14 14
13 14 15 13 14 15 15
14 15 16 14 15 16 16
15 16 17 15 16 17 17
16 17 18 16 17 18 18
connection master; connection master;
update t1 set a = a-10; update t1 set a = a-10;
select * from t1 order by a; select * from t1 order by a;
...@@ -64,13 +80,13 @@ a ...@@ -64,13 +80,13 @@ a
6 6
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
1 2 3 1 2 3 3
2 3 4 2 3 4 4
3 4 5 3 4 5 5
4 5 6 4 5 6 6
5 6 7 5 6 7 7
6 7 8 6 7 8 8
#DELETE quert #DELETE quert
connection master; connection master;
delete from t1 where a > 2 and a < 4; delete from t1 where a > 2 and a < 4;
...@@ -83,12 +99,12 @@ a ...@@ -83,12 +99,12 @@ a
6 6
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
1 2 3 1 2 3 3
2 3 4 2 3 4 4
4 5 6 4 5 6 6
5 6 7 5 6 7 7
6 7 8 6 7 8 8
#REPLACE query #REPLACE query
connection master; connection master;
replace into t1 values(1); replace into t1 values(1);
...@@ -96,13 +112,13 @@ replace into t1 values(3); ...@@ -96,13 +112,13 @@ replace into t1 values(3);
replace into t1 values(1); replace into t1 values(1);
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
1 2 3 1 2 3 3
2 3 4 2 3 4 4
3 4 5 3 4 5 5
4 5 6 4 5 6 6
5 6 7 5 6 7 7
6 7 8 6 7 8 8
#SELECT query #SELECT query
connection master; connection master;
select * from t1 where a > 2 and a < 4; select * from t1 where a > 2 and a < 4;
...@@ -110,8 +126,8 @@ a ...@@ -110,8 +126,8 @@ a
3 3
connection slave; connection slave;
select * from t1 where a > 2 and a < 4; select * from t1 where a > 2 and a < 4;
a z1 z2 a z1 z2 z3
3 4 5 3 4 5 5
#UPDATE with SELECT query #UPDATE with SELECT query
connection master; connection master;
update t1 set a = a + 10 where a > 2 and a < 4; update t1 set a = a + 10 where a > 2 and a < 4;
...@@ -125,13 +141,13 @@ a ...@@ -125,13 +141,13 @@ a
13 13
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
1 2 3 1 2 3 3
2 3 4 2 3 4 4
4 5 6 4 5 6 6
5 6 7 5 6 7 7
6 7 8 6 7 8 8
13 14 15 13 14 15 15
connection master; connection master;
update t1 set a = a - 10 where a = 13; update t1 set a = a - 10 where a = 13;
select * from t1 order by a; select * from t1 order by a;
...@@ -144,13 +160,13 @@ a ...@@ -144,13 +160,13 @@ a
6 6
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
1 2 3 1 2 3 3
2 3 4 2 3 4 4
3 4 5 3 4 5 5
4 5 6 4 5 6 6
5 6 7 5 6 7 7
6 7 8 6 7 8 8
#Break Unique Constraint #Break Unique Constraint
alter table t1 add column z4 int as (a % 6) persistent unique; alter table t1 add column z4 int as (a % 6) persistent unique;
connection master; connection master;
...@@ -168,27 +184,27 @@ a ...@@ -168,27 +184,27 @@ a
connection slave; connection slave;
include/wait_for_slave_sql_error.inc [errno=1062] include/wait_for_slave_sql_error.inc [errno=1062]
select * from t1 order by a; select * from t1 order by a;
a z1 z2 z4 a z1 z2 z3 z4
1 2 3 1 1 2 3 3 1
2 3 4 2 2 3 4 4 2
3 4 5 3 3 4 5 5 3
4 5 6 4 4 5 6 6 4
5 6 7 5 5 6 7 7 5
6 7 8 0 6 7 8 8 0
alter table t1 drop column z4; alter table t1 drop column z4;
start slave; start slave;
include/wait_for_slave_sql_to_start.inc include/wait_for_slave_sql_to_start.inc
connection master; connection master;
connection slave; connection slave;
select * from t1 order by a; select * from t1 order by a;
a z1 z2 a z1 z2 z3
1 2 3 1 2 3 3
2 3 4 2 3 4 4
3 4 5 3 4 5 5
4 5 6 4 5 6 6
5 6 7 5 6 7 7
6 7 8 6 7 8 8
7 8 9 7 8 9 9
connection master; connection master;
select * from t1 order by a; select * from t1 order by a;
a a
......
...@@ -20,11 +20,19 @@ insert into t1 values(6); ...@@ -20,11 +20,19 @@ insert into t1 values(6);
--sync_slave_with_master --sync_slave_with_master
select * from t1 order by a; select * from t1 order by a;
alter table t1 add column z3 int default(a+2);
--connection master
insert into t1 values(7);
insert into t1 values(8);
--sync_slave_with_master
select * from t1 order by a;
--connection master
delete from t1 where a > 6;
--echo #UPDATE query --echo #UPDATE query
--connection master
update t1 set a = a+10; update t1 set a = a+10;
select * from t1 order by a; select * from t1 order by a;
......
...@@ -5097,6 +5097,14 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) ...@@ -5097,6 +5097,14 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
bitmap_set_bit(table->write_set, table->s->vers.end_fieldno); bitmap_set_bit(table->write_set, table->s->vers.end_fieldno);
} }
/* Mark extra replica columns for write */
for (Field **field_ptr= table->field; *field_ptr; ++field_ptr)
{
Field *field= *field_ptr;
if (field->field_index >= m_cols.n_bits && field->stored_in_db())
bitmap_set_bit(table->write_set, field->field_index);
}
this->slave_exec_mode= slave_exec_mode_options; // fix the mode this->slave_exec_mode= slave_exec_mode_options; // fix the mode
// Do event specific preparations // Do event specific preparations
......
...@@ -144,34 +144,6 @@ pack_row(TABLE *table, MY_BITMAP const* cols, ...@@ -144,34 +144,6 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
#endif #endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
/**
Fills @c table->record[0] with computed values of extra persistent column
which are present on slave but not on master.
@param table Table whose record[0] buffer is prepared.
@param master_cols No of columns on master
@returns 0 on success
*/
static int fill_extra_persistent_columns(TABLE *table, int master_cols)
{
int error= 0;
if (!table->vfield)
return 0;
for (Field **vfield_ptr= table->vfield; *vfield_ptr; ++vfield_ptr)
{
Field *vfield= *vfield_ptr;
if (vfield->field_index >= master_cols && (vfield->stored_in_db() ||
(vfield->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG))))
{
bitmap_set_bit(table->write_set, vfield->field_index);
error= vfield->vcol_info->expr->save_in_field(vfield,0);
}
}
return error;
}
/** /**
Unpack a row into @c table->record[0]. Unpack a row into @c table->record[0].
...@@ -411,12 +383,25 @@ int unpack_row(rpl_group_info *rgi, TABLE *table, uint const colcnt, ...@@ -411,12 +383,25 @@ int unpack_row(rpl_group_info *rgi, TABLE *table, uint const colcnt,
if (copy_fields) if (copy_fields)
{ {
for (auto *copy=copy_fields; copy != copy_fields_end; copy++) for (const auto *copy=copy_fields; copy != copy_fields_end; copy++)
{ {
copy->do_copy(copy); copy->do_copy(copy);
} }
} }
if (table->default_field)
{
error= table->update_default_fields(table->in_use->lex->ignore);
if (unlikely(error))
DBUG_RETURN(error);
}
if (table->vfield)
{
error= table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE);
if (unlikely(error))
DBUG_RETURN(error);
}
/* /*
throw away master's extra fields throw away master's extra fields
*/ */
...@@ -442,12 +427,6 @@ int unpack_row(rpl_group_info *rgi, TABLE *table, uint const colcnt, ...@@ -442,12 +427,6 @@ int unpack_row(rpl_group_info *rgi, TABLE *table, uint const colcnt,
} }
} }
/*
Add Extra slave persistent columns
*/
if (unlikely(error= fill_extra_persistent_columns(table, cols->n_bits)))
DBUG_RETURN(error);
/* /*
We should now have read all the null bytes, otherwise something is We should now have read all the null bytes, otherwise something is
really wrong. really wrong.
......
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