Commit 8bf9f218 authored by Libing Song's avatar Libing Song Committed by Andrew Hutchings

MDEV-32894 mysqlbinlog flashback support binlog_row_image FULL_NODUP mode

Summary
=======
With FULL_NODUP mode, before image inclues all columns and after
image inclues only the changed columns. flashback will swap the
value of changed columns from after image to before image.
For example:
  BI: c1, c2, c3_old, c4_old
  AI: c3_new, c4_new
flashback will reconstruct the before and after images to
  BI: c1, c2, c3_new, c4_new
  AI: c3_old, c4_old

Implementation
==============
When parsing the before and after image, position and length of
the fields are collected into ai_fields and bi_fields, if it is an
Update_rows_event and the after image doesn't includes all columns.

The changed fields are swapped between bi_fields and ai_fields.
Then it recreates the before image and after image by using
bi_fields and ai_fields. nullbit will be set to 1 if the
field is NULL, otherwise nullbit will be 0.

It also optimized flashback a little bit.
- calc_row_event_length is used instead of print_verbose_one_row
- swap_buff1 and swap_buff2 are removed.
parent f552febe
......@@ -706,6 +706,60 @@ DROP TABLE t1;
# MDEV-30698 Cover missing test cases for mariadb-binlog options
# --raw [and] --flashback
#
#
# < CASE 8 >
# Verify flashback works well for binlog_row_image full_nodup mode
#
CREATE TABLE t1 (
c01 TINYINT PRIMARY KEY,
c02 SMALLINT,
c03 MEDIUMINT,
c04 INT,
c05 BIGINT,
c06 CHAR(10),
c07 VARCHAR(20),
c08 TEXT,
c09 ENUM('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'),
c10 SET('black', 'white', 'red', 'yellow'),
c11 TIMESTAMP(3),
c12 DATETIME(3)
) ENGINE = InnoDB;
INSERT INTO t1 VALUES (1, 1, 1, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', '2023-11-26 10:00:00');
INSERT INTO t1 VALUES (2, 1, 1, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', '2023-11-26 10:00:00');
INSERT INTO t1 VALUES (3, 1, NULL, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', NULL);
INSERT INTO t1 VALUES (4, 1, NULL, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', NULL);
FLUSH BINARY LOGS;
# The update includes the cases that
# Value -> Value
# Value -> NULL
# NULL -> value
# and the changed null bits in both first and second null byte
UPDATE t1 SET c02 = NULL, c03 = 2, c09 = 'two',
c10 = NULL, c12 = '2023-11-26 11:00:00';
FLUSH BINARY LOGS;
#
# Data before flashback
#
SELECT * FROM t1;
c01 c02 c03 c04 c05 c06 c07 c08 c09 c10 c11 c12
1 NULL 2 1 1 A A A two NULL 2023-11-26 10:00:00.123 2023-11-26 11:00:00.000
2 NULL 2 1 1 A A A two NULL 2023-11-26 10:00:00.123 2023-11-26 11:00:00.000
3 NULL 2 1 1 A A A two NULL 2023-11-26 10:00:00.123 2023-11-26 11:00:00.000
4 NULL 2 1 1 A A A two NULL 2023-11-26 10:00:00.123 2023-11-26 11:00:00.000
#
# Data after flashback
#
SELECT * FROM t1;
c01 c02 c03 c04 c05 c06 c07 c08 c09 c10 c11 c12
1 1 1 1 1 A A A one black 2023-11-26 10:00:00.123 2023-11-26 10:00:00.000
2 1 1 1 1 A A A one black 2023-11-26 10:00:00.123 2023-11-26 10:00:00.000
3 1 NULL 1 1 A A A one black 2023-11-26 10:00:00.123 NULL
4 1 NULL 1 1 A A A one black 2023-11-26 10:00:00.123 NULL
DROP TABLE t1;
SET binlog_format=statement;
Warnings:
Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT
......
[use_full_mode]
binlog_row_image=FULL
[use_full_nodup_mode]
binlog_row_image=FULL_NODUP
......@@ -372,6 +372,73 @@ DROP TABLE t1;
--error 1 # --raw mode and --flashback mode are not allowed
--exec $MYSQL_BINLOG -vv -B --raw --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000003> $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_8.sql
--echo #
--echo # < CASE 8 >
--echo # Verify flashback works well for binlog_row_image full_nodup mode
--echo #
CREATE TABLE t1 (
c01 TINYINT PRIMARY KEY,
c02 SMALLINT,
c03 MEDIUMINT,
c04 INT,
c05 BIGINT,
c06 CHAR(10),
c07 VARCHAR(20),
c08 TEXT,
c09 ENUM('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'),
c10 SET('black', 'white', 'red', 'yellow'),
c11 TIMESTAMP(3),
c12 DATETIME(3)
) ENGINE = InnoDB;
INSERT INTO t1 VALUES (1, 1, 1, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', '2023-11-26 10:00:00');
INSERT INTO t1 VALUES (2, 1, 1, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', '2023-11-26 10:00:00');
INSERT INTO t1 VALUES (3, 1, NULL, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', NULL);
INSERT INTO t1 VALUES (4, 1, NULL, 1, 1, 'A', 'A', 'A', 'one', 'black',
'2023-11-26 10:00:00.123', NULL);
--let $checksum_old= `CHECKSUM TABLE t1`
FLUSH BINARY LOGS;
--echo # The update includes the cases that
--echo # Value -> Value
--echo # Value -> NULL
--echo # NULL -> value
--echo # and the changed null bits in both first and second null byte
UPDATE t1 SET c02 = NULL, c03 = 2, c09 = 'two',
c10 = NULL, c12 = '2023-11-26 11:00:00';
--let $master_file= query_get_value("SHOW MASTER STATUS", File, 1)
--let $MYSQLD_DATADIR= `select @@datadir`
FLUSH BINARY LOGS;
--echo #
--echo # Data before flashback
--echo #
SELECT * FROM t1;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/$master_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback.sql
--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback.sql;"
--echo #
--echo # Data after flashback
--echo #
SELECT * FROM t1;
# After flashback, t1's checksum should be same to original checksum
--let $checksum_new = `CHECKSUM TABLE t1`
if ($checksum_new != $checksum_old)
{
--die "After flashback, t1's checksum is different from the original checksum"
}
DROP TABLE t1;
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback.sql
## Clear
SET binlog_format=statement;
--error ER_FLASHBACK_NOT_SUPPORTED
......
......@@ -4620,6 +4620,12 @@ class Rows_log_event : public Log_event
#endif
#ifdef MYSQL_CLIENT
struct Field_info
{
const uchar *pos; // Point to a field in before or after image
size_t length; // Length of the field.
};
/* not for direct call, each derived has its own ::print() */
virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
void change_to_flashback_event(PRINT_EVENT_INFO *print_event_info, uchar *rows_buff, Log_event_type ev_type);
......@@ -4628,12 +4634,11 @@ class Rows_log_event : public Log_event
size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
PRINT_EVENT_INFO *print_event_info,
MY_BITMAP *cols_bitmap,
const uchar *ptr, const uchar *prefix,
const my_bool no_fill_output= 0); // if no_fill_output=1, then print result is unnecessary
const uchar *ptr, const uchar *prefix);
size_t calc_row_event_length(table_def *td,
PRINT_EVENT_INFO *print_event_info,
MY_BITMAP *cols_bitmap,
const uchar *value);
const uchar *value,
Field_info *fields);
void count_row_events(PRINT_EVENT_INFO *print_event_info);
#endif
......
This diff is collapsed.
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