Skip to content

Commit 9286c9e

Browse files
committed
MDEV-28254 Wrong position for row_start, row_end after adding column to implicit versioned table
Implicit system-versioned table does not contain system fields in SHOW CREATE. Therefore after mysqldump recovery such table has system fields in the last place in frm image. The original table meanwhile does not guarantee these system fields on last place because adding new fields via ALTER TABLE places them last. Thus the order of fields may be different between master and slave, so row-based replication may fail. To fix this on ALTER TABLE we now place system-invisible fields always last in frm image. If the table was created via old revision and has an incorrect order of fields it can be fixed via any copy operation of ALTER TABLE, f.ex.: ALTER TABLE t1 FORCE; To check the order of fields in frm file one can use hexdump: hexdump -C t1.frm Note, the replication fails only when all 3 conditions are met: 1. row-based or mixed mode replication; 2. table has new fields added via ALTER TABLE; 3. table was rebuilt on some, but not all nodes via mysqldump image. Otherwise it will operate properly even with incorrect order of fields.
1 parent 88a9f13 commit 9286c9e

File tree

4 files changed

+107
-1
lines changed

4 files changed

+107
-1
lines changed

mysql-test/include/grep.inc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Grep file for regular expression and output to STDOUT
2+
#
3+
# Usage:
4+
# --let $grep_file= /path/to/your/file
5+
# --let $grep_regex= your_regex_string
6+
# --source include/grep.inc
7+
8+
--perl
9+
open (my $fh, "<", "$ENV{grep_file}") or die $!;
10+
while (<$fh>)
11+
{
12+
/$ENV{grep_regex}/ &&
13+
print;
14+
}
15+
close $fh;
16+
EOF

mysql-test/suite/versioning/r/rpl_row.result

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,42 @@ connection slave;
1111
connection master;
1212
drop table t1;
1313
set binlog_row_image= @old_row_image;
14+
#
15+
# MDEV-28254 Wrong position for row_start, row_end after adding column
16+
# to implicit versioned table
17+
#
18+
set @@system_versioning_alter_history= keep;
19+
set @@session.time_zone='+00:00';
20+
create table t1 (x int) with system versioning engine innodb;
21+
alter table t1 add column y int, algorithm=inplace;
22+
check table t1;
23+
Table Op Msg_type Msg_text
24+
test.t1 check status OK
25+
connection slave;
26+
drop table t1;
27+
show create table t1;
28+
Table Create Table
29+
t1 CREATE TABLE `t1` (
30+
`x` int(11) DEFAULT NULL,
31+
`y` int(11) DEFAULT NULL
32+
) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING
33+
connection master;
34+
set timestamp= 12345;
35+
insert t1 values (1, 1);
36+
select *, unix_timestamp(row_start) as row_start, unix_timestamp(row_end) as row_end from t1;
37+
x y row_start row_end
38+
1 1 12345.000000 2147483647.999999
39+
set timestamp= default;
40+
### INSERT INTO `test`.`t1`
41+
### SET
42+
### @1=1
43+
### @2=1
44+
### @3=12345.000000
45+
### @4=2147483647.999999
46+
connection slave;
47+
select * from t1;
48+
x y
49+
1 1
50+
connection master;
51+
drop table t1;
1452
include/rpl_end.inc

mysql-test/suite/versioning/t/rpl_row.test

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
--source include/have_binlog_format_row.inc
22
--source include/master-slave.inc
3+
--source include/have_innodb.inc
34

45
--echo # MDEV-16252: MINIMAL binlog_row_image does not work for versioned tables
56
set @old_row_image= @@binlog_row_image;
@@ -15,4 +16,44 @@ update t1 set i = 0;
1516
drop table t1;
1617
set binlog_row_image= @old_row_image;
1718

19+
--echo #
20+
--echo # MDEV-28254 Wrong position for row_start, row_end after adding column
21+
--echo # to implicit versioned table
22+
--echo #
23+
--let TMP= $MYSQLTEST_VARDIR/tmp
24+
--let $MYSQLD_DATADIR= `select @@datadir`
25+
set @@system_versioning_alter_history= keep;
26+
set @@session.time_zone='+00:00';
27+
28+
create table t1 (x int) with system versioning engine innodb;
29+
alter table t1 add column y int, algorithm=inplace;
30+
check table t1;
31+
--exec $MYSQL_DUMP --databases test > $TMP/dump.sql
32+
33+
--sync_slave_with_master
34+
drop table t1;
35+
--exec $MYSQL_SLAVE test < $TMP/dump.sql
36+
show create table t1;
37+
38+
--connection master
39+
set timestamp= 12345;
40+
--let $start_pos= query_get_value("SHOW MASTER STATUS", Position, 1)
41+
insert t1 values (1, 1);
42+
select *, unix_timestamp(row_start) as row_start, unix_timestamp(row_end) as row_end from t1;
43+
--let $stop_pos= query_get_value("SHOW MASTER STATUS", Position, 1)
44+
set timestamp= default;
45+
46+
# NOTE: pipe grep is not Windows-compatible
47+
--let grep_file= $TMP/out.txt
48+
--let grep_regex= ^###
49+
--exec $MYSQL_BINLOG -v -j $start_pos --stop-position=$stop_pos -o 3 $MYSQLD_DATADIR/master-bin.000001 > $grep_file
50+
--source include/grep.inc
51+
--sync_slave_with_master
52+
select * from t1;
53+
54+
--connection master
55+
drop table t1;
56+
--remove_files_wildcard $TMP *.txt
57+
--remove_files_wildcard $TMP *.sql
58+
1859
--source include/rpl_end.inc

sql/sql_table.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8027,6 +8027,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
80278027
{
80288028
/* New column definitions are added here */
80298029
List<Create_field> new_create_list;
8030+
/* System-invisible fields must be added last */
8031+
List<Create_field> new_create_tail;
80308032
/* New key definitions are added here */
80318033
List<Key> new_key_list;
80328034
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
@@ -8251,7 +8253,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
82518253
dropped_sys_vers_fields|= field->flags;
82528254
drop_it.remove();
82538255
}
8254-
else
8256+
else if (field->invisible < INVISIBLE_SYSTEM)
82558257
{
82568258
/*
82578259
This field was not dropped and not changed, add it to the list
@@ -8276,6 +8278,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
82768278
alter_it.remove();
82778279
}
82788280
}
8281+
else
8282+
{
8283+
DBUG_ASSERT(field->invisible == INVISIBLE_SYSTEM);
8284+
def= new (thd->mem_root) Create_field(thd, field, field);
8285+
new_create_tail.push_back(def, thd->mem_root);
8286+
}
82798287
}
82808288
dropped_sys_vers_fields &= VERS_SYSTEM_FIELD;
82818289
if ((dropped_sys_vers_fields ||
@@ -8408,6 +8416,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
84088416
alter_it.remove();
84098417
}
84108418
}
8419+
8420+
new_create_list.append(&new_create_tail);
8421+
84118422
if (unlikely(alter_info->alter_list.elements))
84128423
{
84138424
my_error(ER_BAD_FIELD_ERROR, MYF(0),

0 commit comments

Comments
 (0)