Commit 007d3ed9 authored by Lixun Peng's avatar Lixun Peng

MDEV-12067 flashback does not correcly revert update/replace statements

Problem
-------
For one-statement contains multiple row events, Flashback didn't reverse the
sequence of row events inside one-statement.

Solution
--------
Using a new array 'events_in_stmt' to store the row events of one-statement,
when parsed the last one event, then print from the last one to the first one.

In the same time, fixed another bug, without -vv will not insert the table_map
into print_event_info->m_table_map, then change_to_flashback_event() will not
execute because of Table_map_log_event is empty.
parent 92f1837a
...@@ -68,6 +68,7 @@ CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci; ...@@ -68,6 +68,7 @@ CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci;
/* Needed for Flashback */ /* Needed for Flashback */
DYNAMIC_ARRAY binlog_events; // Storing the events output string DYNAMIC_ARRAY binlog_events; // Storing the events output string
DYNAMIC_ARRAY events_in_stmt; // Storing the events that in one statement
String stop_event_string; // Storing the STOP_EVENT output string String stop_event_string; // Storing the STOP_EVENT output string
char server_version[SERVER_VERSION_LENGTH]; char server_version[SERVER_VERSION_LENGTH];
...@@ -894,6 +895,25 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, ...@@ -894,6 +895,25 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
print_event_info->m_table_map_ignored.get_table(table_id); print_event_info->m_table_map_ignored.get_table(table_id);
bool skip_event= (ignored_map != NULL); bool skip_event= (ignored_map != NULL);
if (opt_flashback)
{
Rows_log_event *e= (Rows_log_event*) ev;
// The last Row_log_event will be the first event in Flashback
if (is_stmt_end)
e->clear_flags(Rows_log_event::STMT_END_F);
// The first Row_log_event will be the last event in Flashback
if (events_in_stmt.elements == 0)
e->set_flags(Rows_log_event::STMT_END_F);
// Update the temp_buf
e->update_flags();
if (insert_dynamic(&events_in_stmt, (uchar *) &ev))
{
error("Out of memory: can't allocate memory to store the flashback events.");
exit(1);
}
}
/* /*
end of statement check: end of statement check:
i) destroy/free ignored maps i) destroy/free ignored maps
...@@ -945,7 +965,36 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, ...@@ -945,7 +965,36 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (skip_event) if (skip_event)
return 0; return 0;
return print_base64(print_event_info, ev); if (!opt_flashback)
return print_base64(print_event_info, ev);
else
{
if (is_stmt_end)
{
bool res= false;
Log_event *e= NULL;
// Print the row_event from the last one to the first one
for (uint i= events_in_stmt.elements; i > 0; --i)
{
e= *(dynamic_element(&events_in_stmt, i - 1, Log_event**));
res= res || print_base64(print_event_info, e);
}
// Copy all output into the Log_event
ev->output_buf.copy(e->output_buf);
// Delete Log_event
for (uint i= 0; i < events_in_stmt.elements-1; ++i)
{
e= *(dynamic_element(&events_in_stmt, i, Log_event**));
delete e;
}
reset_dynamic(&events_in_stmt);
return res;
}
}
return 0;
} }
...@@ -1386,6 +1435,8 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, ...@@ -1386,6 +1435,8 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
} }
if (print_base64(print_event_info, ev)) if (print_base64(print_event_info, ev))
goto err; goto err;
if (opt_flashback)
reset_dynamic(&events_in_stmt);
break; break;
} }
case WRITE_ROWS_EVENT: case WRITE_ROWS_EVENT:
...@@ -1402,9 +1453,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, ...@@ -1402,9 +1453,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
case DELETE_ROWS_COMPRESSED_EVENT_V1: case DELETE_ROWS_COMPRESSED_EVENT_V1:
{ {
Rows_log_event *e= (Rows_log_event*) ev; Rows_log_event *e= (Rows_log_event*) ev;
bool is_stmt_end= e->get_flags(Rows_log_event::STMT_END_F);
if (print_row_event(print_event_info, ev, e->get_table_id(), if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Rows_log_event::STMT_END_F))) e->get_flags(Rows_log_event::STMT_END_F)))
goto err; goto err;
if (!is_stmt_end)
destroy_evt= FALSE;
break; break;
} }
case PRE_GA_WRITE_ROWS_EVENT: case PRE_GA_WRITE_ROWS_EVENT:
...@@ -1412,9 +1466,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, ...@@ -1412,9 +1466,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
case PRE_GA_UPDATE_ROWS_EVENT: case PRE_GA_UPDATE_ROWS_EVENT:
{ {
Old_rows_log_event *e= (Old_rows_log_event*) ev; Old_rows_log_event *e= (Old_rows_log_event*) ev;
bool is_stmt_end= e->get_flags(Rows_log_event::STMT_END_F);
if (print_row_event(print_event_info, ev, e->get_table_id(), if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Old_rows_log_event::STMT_END_F))) e->get_flags(Old_rows_log_event::STMT_END_F)))
goto err; goto err;
if (!is_stmt_end)
destroy_evt= FALSE;
break; break;
} }
case START_ENCRYPTION_EVENT: case START_ENCRYPTION_EVENT:
...@@ -1459,7 +1516,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, ...@@ -1459,7 +1516,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
&my_charset_bin); &my_charset_bin);
else else
{ {
if (push_dynamic(&binlog_events, (uchar *) &tmp_str)) if (insert_dynamic(&binlog_events, (uchar *) &tmp_str))
{ {
error("Out of memory: can't allocate memory to store the flashback events."); error("Out of memory: can't allocate memory to store the flashback events.");
exit(1); exit(1);
...@@ -2915,9 +2972,12 @@ int main(int argc, char** argv) ...@@ -2915,9 +2972,12 @@ int main(int argc, char** argv)
my_set_max_open_files(open_files_limit); my_set_max_open_files(open_files_limit);
if (opt_flashback) if (opt_flashback)
{
my_init_dynamic_array(&binlog_events, sizeof(LEX_STRING), 1024, 1024, my_init_dynamic_array(&binlog_events, sizeof(LEX_STRING), 1024, 1024,
MYF(0)); MYF(0));
my_init_dynamic_array(&events_in_stmt, sizeof(Rows_log_event*), 1024, 1024,
MYF(0));
}
if (opt_stop_never) if (opt_stop_never)
to_last_remote_log= TRUE; to_last_remote_log= TRUE;
...@@ -3031,6 +3091,7 @@ int main(int argc, char** argv) ...@@ -3031,6 +3091,7 @@ int main(int argc, char** argv)
} }
fprintf(result_file, "COMMIT\n/*!*/;\n"); fprintf(result_file, "COMMIT\n/*!*/;\n");
delete_dynamic(&binlog_events); delete_dynamic(&binlog_events);
delete_dynamic(&events_in_stmt);
} }
/* Set delimiter back to semicolon */ /* Set delimiter back to semicolon */
......
...@@ -6,7 +6,7 @@ DROP TABLE IF EXISTS t1; ...@@ -6,7 +6,7 @@ DROP TABLE IF EXISTS t1;
# We need a fixed timestamp to avoid varying results. # We need a fixed timestamp to avoid varying results.
# #
SET timestamp=1000000000; SET timestamp=1000000000;
# # < CASE 1 >
# Delete all existing binary logs. # Delete all existing binary logs.
# #
RESET MASTER; RESET MASTER;
...@@ -20,22 +20,22 @@ c06 char(10), ...@@ -20,22 +20,22 @@ c06 char(10),
c07 varchar(20), c07 varchar(20),
c08 TEXT c08 TEXT
) ENGINE=InnoDB; ) ENGINE=InnoDB;
# # < CASE 1 >
# Insert data to t1 # Insert data to t1
# #
INSERT INTO t1 VALUES(0,0,0,0,0,'','',''); INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz"); INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 255)); INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 255));
# # < CASE 1 >
# Update t1 # Update t1
# #
UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3; UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3;
# # < CASE 1 >
# Clear t1 # Clear t1
# #
DELETE FROM t1; DELETE FROM t1;
FLUSH LOGS; FLUSH LOGS;
# # < CASE 1 >
# Show mysqlbinlog result without -B # Show mysqlbinlog result without -B
# #
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
...@@ -258,7 +258,7 @@ DELIMITER ; ...@@ -258,7 +258,7 @@ DELIMITER ;
ROLLBACK /* added by mysqlbinlog */; ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
# # < CASE 1 >
# Show mysqlbinlog result with -B # Show mysqlbinlog result with -B
# #
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
...@@ -426,14 +426,14 @@ DELIMITER ; ...@@ -426,14 +426,14 @@ DELIMITER ;
ROLLBACK /* added by mysqlbinlog */; ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
# # < CASE 1 >
# Insert data to t1 # Insert data to t1
# #
TRUNCATE TABLE t1; TRUNCATE TABLE t1;
INSERT INTO t1 VALUES(0,0,0,0,0,'','',''); INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz"); INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60)); INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60));
# # < CASE 1 >
# Delete all existing binary logs. # Delete all existing binary logs.
# #
RESET MASTER; RESET MASTER;
...@@ -442,7 +442,7 @@ c01 c02 c03 c04 c05 c06 c07 c08 ...@@ -442,7 +442,7 @@ c01 c02 c03 c04 c05 c06 c07 c08
0 0 0 0 0 0 0 0 0 0
1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz 1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz
127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# # < CASE 1 >
# Operate some data # Operate some data
# #
UPDATE t1 SET c01=20; UPDATE t1 SET c01=20;
...@@ -450,7 +450,7 @@ UPDATE t1 SET c02=200; ...@@ -450,7 +450,7 @@ UPDATE t1 SET c02=200;
UPDATE t1 SET c03=2000; UPDATE t1 SET c03=2000;
DELETE FROM t1; DELETE FROM t1;
FLUSH LOGS; FLUSH LOGS;
# # < CASE 1 >
# Flashback & Check the result # Flashback & Check the result
# #
SELECT * FROM t1; SELECT * FROM t1;
...@@ -459,7 +459,7 @@ c01 c02 c03 c04 c05 c06 c07 c08 ...@@ -459,7 +459,7 @@ c01 c02 c03 c04 c05 c06 c07 c08
1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz 1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz
0 0 0 0 0 0 0 0 0 0
RESET MASTER; RESET MASTER;
# # < CASE 2 >
# UPDATE multi-rows in one event # UPDATE multi-rows in one event
# #
BEGIN; BEGIN;
...@@ -467,7 +467,7 @@ UPDATE t1 SET c01=10 WHERE c01=0; ...@@ -467,7 +467,7 @@ UPDATE t1 SET c01=10 WHERE c01=0;
UPDATE t1 SET c01=20 WHERE c01=10; UPDATE t1 SET c01=20 WHERE c01=10;
COMMIT; COMMIT;
FLUSH LOGS; FLUSH LOGS;
# # < CASE 2 >
# Flashback & Check the result # Flashback & Check the result
# #
SELECT * FROM t1; SELECT * FROM t1;
...@@ -476,7 +476,7 @@ c01 c02 c03 c04 c05 c06 c07 c08 ...@@ -476,7 +476,7 @@ c01 c02 c03 c04 c05 c06 c07 c08
1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz 1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz
0 0 0 0 0 0 0 0 0 0
DROP TABLE t1; DROP TABLE t1;
# # < CASE 3 >
# Self-referencing foreign keys # Self-referencing foreign keys
# #
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, FOREIGN KEY my_fk(b) REFERENCES t1(a)) ENGINE=InnoDB; CREATE TABLE t1 (a INT PRIMARY KEY, b INT, FOREIGN KEY my_fk(b) REFERENCES t1(a)) ENGINE=InnoDB;
...@@ -493,7 +493,110 @@ a b ...@@ -493,7 +493,110 @@ a b
RESET MASTER; RESET MASTER;
DELETE FROM t1 ORDER BY a DESC; DELETE FROM t1 ORDER BY a DESC;
FLUSH LOGS; FLUSH LOGS;
# < CASE 3 >
# Flashback & Check the result
# #
SELECT * FROM t1;
a b
1 NULL
2 1
3 2
4 3
DROP TABLE t1;
# < CASE 4 >
# Trigger
#
CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 VALUES (1, NULL);
INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3);
INSERT INTO t2 VALUES (6, 7), (7, 8), (8, 9);
COMMIT;
SELECT * FROM t1;
a b
1 NULL
2 1
3 2
4 3
SELECT * FROM t2;
a b
6 7
7 8
8 9
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t2 WHERE a = NEW.b;
RESET MASTER;
INSERT INTO t1 VALUES (5, 6), (7, 8);
SELECT * FROM t1;
a b
1 NULL
2 1
3 2
4 3
5 6
7 8
SELECT * FROM t2;
a b
7 8
FLUSH LOGS;
# < CASE 4 >
# Flashback & Check the result
#
SELECT * FROM t1;
a b
1 NULL
2 1
3 2
4 3
SELECT * FROM t2;
a b
6 7
7 8
8 9
DROP TRIGGER trg1;
DROP TABLE t1;
DROP TABLE t2;
# < CASE 5 >
# REPLCAE Queries
#
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, UNIQUE uk(b)) ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 VALUES (1, NULL);
INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3);
INSERT INTO t1 VALUES (5, 4), (6, 5), (7, 6);
COMMIT;
SELECT * FROM t1;
a b
1 NULL
2 1
3 2
4 3
5 4
6 5
7 6
RESET MASTER;
REPLACE INTO t1 VALUES (3, 100);
REPLACE INTO t1 SET a=4, b=200;
SELECT * FROM t1;
a b
1 NULL
2 1
5 4
6 5
7 6
3 100
4 200
REPLACE INTO t1 VALUES (5,5);
SELECT * FROM t1;
a b
1 NULL
2 1
5 5
7 6
3 100
4 200
FLUSH LOGS;
# < CASE 5 >
# Flashback & Check the result # Flashback & Check the result
# #
SELECT * FROM t1; SELECT * FROM t1;
...@@ -502,9 +605,77 @@ a b ...@@ -502,9 +605,77 @@ a b
2 1 2 1
3 2 3 2
4 3 4 3
5 4
6 5
7 6
DROP TABLE t1;
# < CASE 6 >
# Test Case from MDEV-21067
#
CREATE DATABASE world;
CREATE TABLE world.City (
ID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(64),
CountryCode VARCHAR(64),
District VARCHAR(64),
Population INT
) ENGINE=InnoDB;
CREATE TABLE test.test (
ID INT AUTO_INCREMENT PRIMARY KEY,
REC VARCHAR(64),
ts TIMESTAMP
) ENGINE=InnoDB;
INSERT INTO world.City VALUES (NULL, 'Davenport', 'USA', 'Iowa', 100);
INSERT INTO world.City VALUES (NULL, 'Boulder', 'USA', 'Colorado', 1000);
INSERT INTO world.City VALUES (NULL, 'Gweru', 'ZWE', 'Midlands', 10000);
RESET MASTER;
CHECKSUM TABLE world.City;
Table Checksum
world.City 563256876
INSERT INTO test.test VALUES (NULL, 'Good record 1', CURRENT_TIMESTAMP());
INSERT INTO world.City VALUES (NULL, 'Wrong value 1', '000', 'Wrong', 0);
INSERT INTO world.City VALUES (NULL, 'Wrong value 2', '000', 'Wrong', 0) , (NULL, 'Wrong value 3', '000', 'Wrong', 0);
INSERT INTO test.test VALUES (NULL, 'Good record 2', CURRENT_TIMESTAMP());
UPDATE world.City SET Population = 99999999 WHERE ID IN (1, 2, 3);
INSERT INTO test.test VALUES (NULL, 'Good record 3', CURRENT_TIMESTAMP());
DELETE FROM world.City WHERE ID BETWEEN 1 AND 2;
INSERT INTO test.test VALUES (NULL, 'Good record 5', CURRENT_TIMESTAMP());
REPLACE INTO world.City VALUES (4074, 'Wrong value 4', '000', 'Wrong', 0);
REPLACE INTO world.City VALUES (4078, 'Wrong value 5', '000', 'Wrong', 0), (NULL, 'Wrong value 6', '000', 'Wrong', 0);
INSERT INTO test.test VALUES (NULL, 'Good record 6', CURRENT_TIMESTAMP());
INSERT INTO world.City
SELECT NULL, Name, CountryCode, District, Population FROM world.City WHERE ID BETWEEN 2 AND 10;
INSERT INTO test.test VALUES (NULL, 'Good record 7', CURRENT_TIMESTAMP());
INSERT INTO test.test VALUES (NULL, 'Good record 8', CURRENT_TIMESTAMP());
DELETE FROM world.City;
INSERT INTO test.test VALUES (NULL, 'Good record 9', CURRENT_TIMESTAMP());
FLUSH LOGS;
# < CASE 6 >
# Flashback & Check the result
#
SELECT * FROM world.City;
ID Name CountryCode District Population
1 Davenport USA Iowa 100
2 Boulder USA Colorado 1000
3 Gweru ZWE Midlands 10000
SELECT * FROM test.test;
ID REC ts
1 Good record 1 2001-09-09 09:46:40
2 Good record 2 2001-09-09 09:46:40
3 Good record 3 2001-09-09 09:46:40
4 Good record 5 2001-09-09 09:46:40
5 Good record 6 2001-09-09 09:46:40
6 Good record 7 2001-09-09 09:46:40
7 Good record 8 2001-09-09 09:46:40
8 Good record 9 2001-09-09 09:46:40
CHECKSUM TABLE world.City;
Table Checksum
world.City 563256876
DROP TABLE test.test;
DROP TABLE world.City;
DROP DATABASE world;
SET binlog_format=statement; SET binlog_format=statement;
Warnings: Warnings:
Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT
SET GLOBAL binlog_format=statement; SET GLOBAL binlog_format=statement;
ERROR HY000: Flashback does not support binlog_format STATEMENT ERROR HY000: Flashback does not support binlog_format STATEMENT
DROP TABLE t1;
...@@ -13,12 +13,11 @@ DROP TABLE IF EXISTS t1; ...@@ -13,12 +13,11 @@ DROP TABLE IF EXISTS t1;
--echo # --echo #
SET timestamp=1000000000; SET timestamp=1000000000;
--echo # --echo # < CASE 1 >
--echo # Delete all existing binary logs. --echo # Delete all existing binary logs.
--echo # --echo #
RESET MASTER; RESET MASTER;
CREATE TABLE t1 ( CREATE TABLE t1 (
c01 tinyint, c01 tinyint,
c02 smallint, c02 smallint,
...@@ -30,7 +29,7 @@ CREATE TABLE t1 ( ...@@ -30,7 +29,7 @@ CREATE TABLE t1 (
c08 TEXT c08 TEXT
) ENGINE=InnoDB; ) ENGINE=InnoDB;
--echo # --echo # < CASE 1 >
--echo # Insert data to t1 --echo # Insert data to t1
--echo # --echo #
INSERT INTO t1 VALUES(0,0,0,0,0,'','',''); INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
...@@ -38,19 +37,19 @@ INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz") ...@@ -38,19 +37,19 @@ INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz")
INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 255)); INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 255));
--echo # --echo # < CASE 1 >
--echo # Update t1 --echo # Update t1
--echo # --echo #
UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3; UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3;
--echo # --echo # < CASE 1 >
--echo # Clear t1 --echo # Clear t1
--echo # --echo #
DELETE FROM t1; DELETE FROM t1;
FLUSH LOGS; FLUSH LOGS;
--echo # --echo # < CASE 1 >
--echo # Show mysqlbinlog result without -B --echo # Show mysqlbinlog result without -B
--echo # --echo #
...@@ -59,7 +58,7 @@ let $MYSQLD_DATADIR= `select @@datadir`; ...@@ -59,7 +58,7 @@ let $MYSQLD_DATADIR= `select @@datadir`;
--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ --replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/
--exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001 --exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001
--echo # --echo # < CASE 1 >
--echo # Show mysqlbinlog result with -B --echo # Show mysqlbinlog result with -B
--echo # --echo #
...@@ -68,7 +67,7 @@ let $MYSQLD_DATADIR= `select @@datadir`; ...@@ -68,7 +67,7 @@ let $MYSQLD_DATADIR= `select @@datadir`;
--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ --replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/
--exec $MYSQL_BINLOG -B --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001 --exec $MYSQL_BINLOG -B --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001
--echo # --echo # < CASE 1 >
--echo # Insert data to t1 --echo # Insert data to t1
--echo # --echo #
TRUNCATE TABLE t1; TRUNCATE TABLE t1;
...@@ -76,13 +75,13 @@ INSERT INTO t1 VALUES(0,0,0,0,0,'','',''); ...@@ -76,13 +75,13 @@ INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz"); INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60)); INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60));
--echo # --echo # < CASE 1 >
--echo # Delete all existing binary logs. --echo # Delete all existing binary logs.
--echo # --echo #
RESET MASTER; RESET MASTER;
SELECT * FROM t1; SELECT * FROM t1;
--echo # --echo # < CASE 1 >
--echo # Operate some data --echo # Operate some data
--echo # --echo #
...@@ -94,12 +93,13 @@ DELETE FROM t1; ...@@ -94,12 +93,13 @@ DELETE FROM t1;
FLUSH LOGS; FLUSH LOGS;
--echo # --echo # < CASE 1 >
--echo # Flashback & Check the result --echo # Flashback & Check the result
--echo # --echo #
let $MYSQLD_DATADIR= `select @@datadir`; let $MYSQLD_DATADIR= `select @@datadir`;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_original_1.sql
--exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_1.sql --exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_1.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_1.sql;" --exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_1.sql;"
...@@ -107,9 +107,10 @@ SELECT * FROM t1; ...@@ -107,9 +107,10 @@ SELECT * FROM t1;
RESET MASTER; RESET MASTER;
--echo # --echo # < CASE 2 >
--echo # UPDATE multi-rows in one event --echo # UPDATE multi-rows in one event
--echo # --echo #
BEGIN; BEGIN;
UPDATE t1 SET c01=10 WHERE c01=0; UPDATE t1 SET c01=10 WHERE c01=0;
UPDATE t1 SET c01=20 WHERE c01=10; UPDATE t1 SET c01=20 WHERE c01=10;
...@@ -117,12 +118,13 @@ COMMIT; ...@@ -117,12 +118,13 @@ COMMIT;
FLUSH LOGS; FLUSH LOGS;
--echo # --echo # < CASE 2 >
--echo # Flashback & Check the result --echo # Flashback & Check the result
--echo # --echo #
let $MYSQLD_DATADIR= `select @@datadir`; let $MYSQLD_DATADIR= `select @@datadir`;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_original_2.sql
--exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_2.sql --exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_2.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_2.sql;" --exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_2.sql;"
...@@ -130,9 +132,10 @@ SELECT * FROM t1; ...@@ -130,9 +132,10 @@ SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo # --echo # < CASE 3 >
--echo # Self-referencing foreign keys --echo # Self-referencing foreign keys
--echo # --echo #
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, FOREIGN KEY my_fk(b) REFERENCES t1(a)) ENGINE=InnoDB; CREATE TABLE t1 (a INT PRIMARY KEY, b INT, FOREIGN KEY my_fk(b) REFERENCES t1(a)) ENGINE=InnoDB;
BEGIN; BEGIN;
...@@ -149,19 +152,191 @@ DELETE FROM t1 ORDER BY a DESC; ...@@ -149,19 +152,191 @@ DELETE FROM t1 ORDER BY a DESC;
FLUSH LOGS; FLUSH LOGS;
--echo # --echo # < CASE 3 >
--echo # Flashback & Check the result --echo # Flashback & Check the result
--echo # --echo #
let $MYSQLD_DATADIR= `select @@datadir`; let $MYSQLD_DATADIR= `select @@datadir`;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_original_3.sql
--exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_3.sql --exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_3.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_3.sql;" --exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_3.sql;"
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1;
--echo # < CASE 4 >
--echo # Trigger
--echo #
CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 VALUES (1, NULL);
INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3);
INSERT INTO t2 VALUES (6, 7), (7, 8), (8, 9);
COMMIT;
SELECT * FROM t1;
SELECT * FROM t2;
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW DELETE FROM t2 WHERE a = NEW.b;
# New binlog
RESET MASTER;
INSERT INTO t1 VALUES (5, 6), (7, 8);
SELECT * FROM t1;
SELECT * FROM t2;
FLUSH LOGS;
--echo # < CASE 4 >
--echo # Flashback & Check the result
--echo #
let $MYSQLD_DATADIR= `select @@datadir`;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_original_4.sql
--exec $MYSQL_BINLOG -B $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_4.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_4.sql;"
SELECT * FROM t1;
SELECT * FROM t2;
DROP TRIGGER trg1;
DROP TABLE t1;
DROP TABLE t2;
--echo # < CASE 5 >
--echo # REPLCAE Queries
--echo #
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, UNIQUE uk(b)) ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 VALUES (1, NULL);
INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3);
INSERT INTO t1 VALUES (5, 4), (6, 5), (7, 6);
COMMIT;
SELECT * FROM t1;
# New binlog
RESET MASTER;
REPLACE INTO t1 VALUES (3, 100);
REPLACE INTO t1 SET a=4, b=200;
SELECT * FROM t1;
REPLACE INTO t1 VALUES (5,5);
SELECT * FROM t1;
FLUSH LOGS;
--echo # < CASE 5 >
--echo # Flashback & Check the result
--echo #
let $MYSQLD_DATADIR= `select @@datadir`;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_original_5.sql
--exec $MYSQL_BINLOG -B $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_5.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_5.sql;"
SELECT * FROM t1;
DROP TABLE t1;
--echo # < CASE 6 >
--echo # Test Case from MDEV-21067
--echo #
# Init Structure
CREATE DATABASE world;
CREATE TABLE world.City (
ID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(64),
CountryCode VARCHAR(64),
District VARCHAR(64),
Population INT
) ENGINE=InnoDB;
CREATE TABLE test.test (
ID INT AUTO_INCREMENT PRIMARY KEY,
REC VARCHAR(64),
ts TIMESTAMP
) ENGINE=InnoDB;
INSERT INTO world.City VALUES (NULL, 'Davenport', 'USA', 'Iowa', 100);
INSERT INTO world.City VALUES (NULL, 'Boulder', 'USA', 'Colorado', 1000);
INSERT INTO world.City VALUES (NULL, 'Gweru', 'ZWE', 'Midlands', 10000);
RESET MASTER;
CHECKSUM TABLE world.City;
# Insert test data
INSERT INTO test.test VALUES (NULL, 'Good record 1', CURRENT_TIMESTAMP());
INSERT INTO world.City VALUES (NULL, 'Wrong value 1', '000', 'Wrong', 0);
INSERT INTO world.City VALUES (NULL, 'Wrong value 2', '000', 'Wrong', 0) , (NULL, 'Wrong value 3', '000', 'Wrong', 0);
INSERT INTO test.test VALUES (NULL, 'Good record 2', CURRENT_TIMESTAMP());
UPDATE world.City SET Population = 99999999 WHERE ID IN (1, 2, 3);
INSERT INTO test.test VALUES (NULL, 'Good record 3', CURRENT_TIMESTAMP());
DELETE FROM world.City WHERE ID BETWEEN 1 AND 2;
INSERT INTO test.test VALUES (NULL, 'Good record 5', CURRENT_TIMESTAMP());
REPLACE INTO world.City VALUES (4074, 'Wrong value 4', '000', 'Wrong', 0);
REPLACE INTO world.City VALUES (4078, 'Wrong value 5', '000', 'Wrong', 0), (NULL, 'Wrong value 6', '000', 'Wrong', 0);
INSERT INTO test.test VALUES (NULL, 'Good record 6', CURRENT_TIMESTAMP());
INSERT INTO world.City
SELECT NULL, Name, CountryCode, District, Population FROM world.City WHERE ID BETWEEN 2 AND 10;
INSERT INTO test.test VALUES (NULL, 'Good record 7', CURRENT_TIMESTAMP());
INSERT INTO test.test VALUES (NULL, 'Good record 8', CURRENT_TIMESTAMP());
DELETE FROM world.City;
INSERT INTO test.test VALUES (NULL, 'Good record 9', CURRENT_TIMESTAMP());
FLUSH LOGS;
--echo # < CASE 6 >
--echo # Flashback & Check the result
--echo #
let $MYSQLD_DATADIR= `select @@datadir`;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG --database=world --table=City -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_original_6.sql
--exec $MYSQL_BINLOG --database=world --table=City -B $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_6.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_6.sql;"
SELECT * FROM world.City;
SELECT * FROM test.test;
CHECKSUM TABLE world.City;
DROP TABLE test.test;
DROP TABLE world.City;
DROP DATABASE world;
## Clear
SET binlog_format=statement; SET binlog_format=statement;
--error ER_FLASHBACK_NOT_SUPPORTED --error ER_FLASHBACK_NOT_SUPPORTED
SET GLOBAL binlog_format=statement; SET GLOBAL binlog_format=statement;
DROP TABLE t1;
...@@ -3477,7 +3477,8 @@ void Log_event::print_base64(IO_CACHE* file, ...@@ -3477,7 +3477,8 @@ void Log_event::print_base64(IO_CACHE* file,
#ifdef WHEN_FLASHBACK_REVIEW_READY #ifdef WHEN_FLASHBACK_REVIEW_READY
if (print_event_info->verbose || need_flashback_review) if (print_event_info->verbose || need_flashback_review)
#else #else
if (print_event_info->verbose) // Flashback need the table_map to parse the event
if (print_event_info->verbose || is_flashback)
#endif #endif
{ {
Rows_log_event *ev= NULL; Rows_log_event *ev= NULL;
...@@ -3564,7 +3565,8 @@ void Log_event::print_base64(IO_CACHE* file, ...@@ -3564,7 +3565,8 @@ void Log_event::print_base64(IO_CACHE* file,
close_cached_file(&tmp_cache); close_cached_file(&tmp_cache);
} }
#else #else
ev->print_verbose(file, print_event_info); if (print_event_info->verbose)
ev->print_verbose(file, print_event_info);
#endif #endif
delete ev; delete ev;
} }
...@@ -10251,6 +10253,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, ...@@ -10251,6 +10253,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
post_start+= RW_FLAGS_OFFSET; post_start+= RW_FLAGS_OFFSET;
} }
m_flags_pos= post_start - buf;
m_flags= uint2korr(post_start); m_flags= uint2korr(post_start);
post_start+= 2; post_start+= 2;
...@@ -11299,18 +11302,18 @@ void Rows_log_event::print_helper(FILE *file, ...@@ -11299,18 +11302,18 @@ void Rows_log_event::print_helper(FILE *file,
if (get_flags(STMT_END_F)) if (get_flags(STMT_END_F))
{ {
reinit_io_cache(head, READ_CACHE, 0L, FALSE, FALSE); LEX_STRING tmp_str;
output_buf.append(head, head->end_of_file);
reinit_io_cache(head, WRITE_CACHE, 0, FALSE, TRUE);
reinit_io_cache(body, READ_CACHE, 0L, FALSE, FALSE);
output_buf.append(body, body->end_of_file);
reinit_io_cache(body, WRITE_CACHE, 0, FALSE, TRUE);
copy_event_cache_to_string_and_reinit(head, &tmp_str);
output_buf.append(&tmp_str);
my_free(tmp_str.str);
copy_event_cache_to_string_and_reinit(body, &tmp_str);
output_buf.append(&tmp_str);
my_free(tmp_str.str);
#ifdef WHEN_FLASHBACK_REVIEW_READY #ifdef WHEN_FLASHBACK_REVIEW_READY
reinit_io_cache(sql, READ_CACHE, 0L, FALSE, FALSE); copy_event_cache_to_string_and_reinit(sql, &tmp_str);
output_buf.append(sql, sql->end_of_file); output_buf.append(&tmp_str);
reinit_io_cache(sql, WRITE_CACHE, 0, FALSE, TRUE); my_free(tmp_str.str);
#endif #endif
} }
} }
......
...@@ -4397,6 +4397,7 @@ class Rows_log_event : public Log_event ...@@ -4397,6 +4397,7 @@ class Rows_log_event : public Log_event
void set_flags(flag_set flags_arg) { m_flags |= flags_arg; } void set_flags(flag_set flags_arg) { m_flags |= flags_arg; }
void clear_flags(flag_set flags_arg) { m_flags &= ~flags_arg; } void clear_flags(flag_set flags_arg) { m_flags &= ~flags_arg; }
flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; } flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
void update_flags() { int2store(temp_buf + m_flags_pos, m_flags); }
Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */ Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */
virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */ virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */
...@@ -4555,6 +4556,7 @@ class Rows_log_event : public Log_event ...@@ -4555,6 +4556,7 @@ class Rows_log_event : public Log_event
uchar *m_rows_end; /* One-after the end of the allocated space */ uchar *m_rows_end; /* One-after the end of the allocated space */
size_t m_rows_before_size; /* The length before m_rows_buf */ size_t m_rows_before_size; /* The length before m_rows_buf */
size_t m_flags_pos; /* The position of the m_flags */
flag_set m_flags; /* Flags for row-level events */ flag_set m_flags; /* Flags for row-level events */
......
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