Skip to content

Commit

Permalink
MDEV-32894 mysqlbinlog flashback support binlog_row_image FULL_NODUP …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
SongLibing authored and LinuxJedi committed Jan 18, 2024
1 parent f552feb commit 8bf9f21
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 71 deletions.
54 changes: 54 additions & 0 deletions mysql-test/suite/binlog/r/flashback.result
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions mysql-test/suite/binlog/t/flashback.combinations
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[use_full_mode]
binlog_row_image=FULL
[use_full_nodup_mode]
binlog_row_image=FULL_NODUP
67 changes: 67 additions & 0 deletions mysql-test/suite/binlog/t/flashback.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions sql/log_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down

0 comments on commit 8bf9f21

Please sign in to comment.