diff options
author | Lixun Peng <lixun@mariadb.org> | 2017-01-20 10:04:42 +0800 |
---|---|---|
committer | Lixun Peng <lixun@mariadb.org> | 2017-01-20 10:04:42 +0800 |
commit | c2bda7d0185275a9e183f2c6a5d9f7e3a47f1492 (patch) | |
tree | deca021a0735f5b7dcbcef689ecfe556acb6e549 | |
parent | c849b7df616cc8a5378609f243898644c7689f44 (diff) | |
download | mariadb-git-MDEV-10570-plinux.tar.gz |
[MDEV-10570] Merge Existing Community Patch “Flashback”MDEV-10570-plinux
==== Description ====
Flashback can rollback the instances/databases/tables to an old snapshot.
It's implement on Server-Level by full image format binary logs (--binlog-row-image=FULL), so it supports all engines.
Currently, it’s a feature inside mysqlbinlog tool (with --flashback arguments).
Because the flashback binlog events will store in the memory, you should check if there is enough memory in your machine.
==== New Arguments ====
--flashback (-B)
It will let mysqlbinlog to work on FLASHBACK mode.
==== Example ====
I have a table "t" in database "test", we can compare the output with "--flashback" and without.
#client/mysqlbinlog /data/mysqldata_10.0/binlog/mysql-bin.000001 -vv -d test -T t --start-datetime="2013-03-27 14:54:00" > /tmp/1.sql
#client/mysqlbinlog /data/mysqldata_10.0/binlog/mysql-bin.000001 -vv -d test -T t --start-datetime="2013-03-27 14:54:00" -B > /tmp/2.sql
Then, importing the output flashback file (/tmp/2.log), it can flashback your database/table to the special time (--start-datetime).
And if you know the exact postion, "--start-postion" is also works, mysqlbinlog will output the flashback logs that can flashback to "--start-postion" position.
==== Implement ====
1. As we know, if binlog_format is ROW (binlog-row-image=FULL in 10.1 and later), all columns value are store in the row event, so we can get the data before mis-operation.
2. Just do following things:
2.1 Change Event Type, INSERT->DELETE, DELETE->INSERT.
For example:
INSERT INTO t VALUES (...) ---> DELETE FROM t WHERE ...
DELETE FROM t ... ---> INSERT INTO t VALUES (...)
2.2 For Update_Event, swapping the SET part and WHERE part.
For example:
UPDATE t SET cols1 = vals1 WHERE cols2 = vals2
--->
UPDATE t SET cols2 = vals2 WHERE cols1 = vals1
2.3 For Multi-Rows Event, reverse the rows sequence, from the last row to the first row.
For example:
DELETE FROM t WHERE id=1; DELETE FROM t WHERE id=2; ...; DELETE FROM t WHERE id=n;
--->
DELETE FROM t WHERE id=n; ...; DELETE FROM t WHERE id=2; DELETE FROM t WHERE id=1;
2.4 Output those events from the last one to the first one which mis-operation happened.
For example:
-rw-r--r-- | client/client_priv.h | 4 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 251 | ||||
-rw-r--r-- | mysql-test/r/mysqld--help.result | 4 | ||||
-rw-r--r-- | mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result | 480 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/mysqlbinlog_row_flashback-master.opt | 2 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test | 163 | ||||
-rw-r--r-- | sql/log_event.cc | 418 | ||||
-rw-r--r-- | sql/log_event.h | 77 | ||||
-rw-r--r-- | sql/mysqld.cc | 18 | ||||
-rw-r--r-- | sql/mysqld.h | 1 | ||||
-rw-r--r-- | sql/sys_vars.cc | 7 |
11 files changed, 1374 insertions, 51 deletions
diff --git a/client/client_priv.h b/client/client_priv.h index 1d85791fa73..e96e187fb34 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -66,6 +66,10 @@ enum options_client OPT_MYSQLDUMP_SLAVE_APPLY, OPT_MYSQLDUMP_SLAVE_DATA, OPT_MYSQLDUMP_INCLUDE_MASTER_HOST_PORT, +#ifdef WHEN_FLASHBACK_REVIEW_READY + OPT_REVIEW, + OPT_REVIEW_DBNAME, OPT_REVIEW_TABLENAME, +#endif OPT_SLAP_CSV, OPT_SLAP_CREATE_STRING, OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, OPT_SLAP_AUTO_GENERATE_WRITE_NUM, OPT_SLAP_AUTO_GENERATE_ADD_AUTO, diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 558dbebc89c..39a879bc619 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -66,6 +66,10 @@ Rpl_filter *binlog_filter= 0; /* Needed for Rpl_filter */ CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci; +/* Needed for Flashback */ +DYNAMIC_ARRAY binlog_events; // Storing the events output string +String stop_event_string; // Storing the STOP_EVENT output string + char server_version[SERVER_VERSION_LENGTH]; ulong server_id = 0; @@ -89,7 +93,7 @@ static const char *load_groups[]= static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); -static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; +static bool one_database=0, one_table=0, to_last_remote_log= 0, disable_log_bin= 0; static bool opt_hexdump= 0, opt_version= 0; const char *base64_output_mode_names[]= {"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS}; @@ -99,6 +103,7 @@ TYPELIB base64_output_mode_typelib= static enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC; static char *opt_base64_output_mode_str= NullS; static char* database= 0; +static char* table= 0; static my_bool force_opt= 0, short_form= 0, remote_opt= 0; static my_bool debug_info_flag, debug_check_flag; static my_bool force_if_open_opt= 1; @@ -132,6 +137,12 @@ static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 0; static bool opt_skip_annotate_row_events= 0; +static my_bool opt_flashback; +#ifdef WHEN_FLASHBACK_REVIEW_READY +static my_bool opt_flashback_review; +static char *flashback_review_dbname, *flashback_review_tablename; +#endif + /** Pointer to the Format_description_log_event of the currently active binlog. @@ -791,6 +802,23 @@ print_skip_replication_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev) } /** + Indicates whether the given table should be filtered out, + according to the --table=X option. + + @param log_tblname Name of table. + + @return nonzero if the table with the given name should be + filtered out, 0 otherwise. +*/ +static bool shall_skip_table(const char *log_tblname) +{ + return one_table && + (log_tblname != NULL) && + strcmp(log_tblname, table); +} + + +/** Prints the given event in base64 format. The header is printed to the head cache and the body is printed to @@ -952,6 +980,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, Exit_status retval= OK_CONTINUE; IO_CACHE *const head= &print_event_info->head_cache; + /* Bypass flashback settings to event */ + ev->is_flashback= opt_flashback; +#ifdef WHEN_FLASHBACK_REVIEW_READY + ev->need_flashback_review= opt_flashback_review; +#endif + /* Format events are not concerned by --offset and such, we always need to read them to be able to process the wanted events. @@ -988,7 +1022,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, retval= OK_STOP; goto end; } - if (!short_form) + if (!short_form && !opt_flashback) fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); if (!opt_hexdump) @@ -1214,12 +1248,128 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, case TABLE_MAP_EVENT: { Table_map_log_event *map= ((Table_map_log_event *)ev); - if (shall_skip_database(map->get_db_name())) + if (shall_skip_database(map->get_db_name()) || + shall_skip_table(map->get_table_name())) { print_event_info->m_table_map_ignored.set_table(map->get_table_id(), map); destroy_evt= FALSE; goto end; } +#ifdef WHEN_FLASHBACK_REVIEW_READY + /* Create review table for Flashback */ + if (opt_flashback_review) + { + // Check if the table was already created? + Table_map_log_event *exist_table; + exist_table= print_event_info->m_table_map.get_table(map->get_table_id()); + + if (!exist_table) + { + + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + char tmp_sql[8096]; + int tmp_sql_offset; + + conn = mysql_init(NULL); + if (!mysql_real_connect(conn, host, user, pass, + map->get_db_name(), port, sock, 0)) + { + fprintf(stderr, "%s\n", mysql_error(conn)); + exit(1); + } + + if (mysql_query(conn, "SET group_concat_max_len=10000;")) + { + fprintf(stderr, "%s\n", mysql_error(conn)); + exit(1); + } + + memset(tmp_sql, 0, sizeof(tmp_sql)); + sprintf(tmp_sql, " " + "SELECT Group_concat(cols) " + "FROM (SELECT 'op_type char(1)' cols " + " UNION ALL " + " SELECT Concat('`', column_name, '_old` ', column_type, ' ', " + " IF(character_set_name IS NOT NULL, " + " Concat('character set ', character_set_name, ' '), ' '), " + " IF(collation_name IS NOT NULL, " + " Concat('collate ', collation_name, ' '), ' ')) cols " + " FROM information_schema.columns " + " WHERE table_schema = '%s' " + " AND table_name = '%s' " + " UNION ALL " + " SELECT Concat('`', column_name, '_new` ', column_type, ' ', " + " IF(character_set_name IS NOT NULL, " + " Concat('character set ', character_set_name, ' '), ' '), " + " IF(collation_name IS NOT NULL, " + " Concat('collate ', collation_name, ' '), ' ')) cols " + " FROM information_schema.columns " + " WHERE table_schema = '%s' " + " AND table_name = '%s') tmp;", + map->get_db_name(), map->get_table_name(), + map->get_db_name(), map->get_table_name()); + + if (mysql_query(conn, tmp_sql)) + { + fprintf(stderr, "%s\n", mysql_error(conn)); + exit(1); + } + res = mysql_use_result(conn); + if ((row = mysql_fetch_row(res)) != NULL) // only one row + { + if (flashback_review_dbname) + { + ev->set_flashback_review_dbname(flashback_review_dbname); + } + else + { + ev->set_flashback_review_dbname(map->get_db_name()); + } + if (flashback_review_tablename) + { + ev->set_flashback_review_tablename(flashback_review_tablename); + } + else + { + memset(tmp_sql, 0, sizeof(tmp_sql)); + sprintf(tmp_sql, "__%s", map->get_table_name()); + ev->set_flashback_review_tablename(tmp_sql); + } + memset(tmp_sql, 0, sizeof(tmp_sql)); + tmp_sql_offset= sprintf(tmp_sql, "CREATE TABLE IF NOT EXISTS"); + tmp_sql_offset+= sprintf(tmp_sql + tmp_sql_offset, " `%s`.`%s` (%s) %s", + ev->get_flashback_review_dbname(), + ev->get_flashback_review_tablename(), + row[0], + print_event_info->delimiter); + } + fprintf(result_file, "%s\n", tmp_sql); + mysql_free_result(res); + mysql_close(conn); + } + else + { + char tmp_str[128]; + + if (flashback_review_dbname) + ev->set_flashback_review_dbname(flashback_review_dbname); + else + ev->set_flashback_review_dbname(map->get_db_name()); + + if (flashback_review_tablename) + ev->set_flashback_review_tablename(flashback_review_tablename); + else + { + memset(tmp_str, 0, sizeof(tmp_str)); + sprintf(tmp_str, "__%s", map->get_table_name()); + ev->set_flashback_review_tablename(tmp_str); + } + } + } +#endif + /* The Table map is to be printed, so it's just the time when we may print the kept Annotate event (if there is any). @@ -1294,6 +1444,38 @@ end: */ if (ev) { + /* Holding event output if needed */ + if (!ev->output_buf.is_empty()) + { + LEX_STRING tmp_str; + + tmp_str.length= ev->output_buf.length(); + tmp_str.str= ev->output_buf.release(); + + if (opt_flashback) + { + if (ev_type == STOP_EVENT) + stop_event_string.reset(tmp_str.str, tmp_str.length, tmp_str.length, + &my_charset_bin); + else + { + if (push_dynamic(&binlog_events, (uchar *) &tmp_str)) + { + error("Out of memory: can't allocate memory to store the flashback events."); + exit(1); + } + } + } + else + { + my_fwrite(result_file, (const uchar *) tmp_str.str, tmp_str.length, + MYF(MY_NABP)); + my_free(tmp_str.str); + } + } + + if (remote_opt) + ev->temp_buf= 0; if (destroy_evt) /* destroy it later if not set (ignored table map) */ delete ev; } @@ -1352,6 +1534,13 @@ static struct my_option my_options[] = "already have. NOTE: you will need a SUPER privilege to use this option.", &disable_log_bin, &disable_log_bin, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"flashback", 'B', "Flashback feature can rollback you committed data to a special time point.", +#ifdef WHEN_FLASHBACK_REVIEW_READY + "before Flashback feature writing a row, original row can insert to review-dbname.review-tablename," + "and mysqlbinlog will login mysql by user(-u) and password(-p) and host(-h).", +#endif + &opt_flashback, &opt_flashback, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, {"force-if-open", 'F', "Force if binlog was not closed properly.", &force_if_open_opt, &force_if_open_opt, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, @@ -1395,6 +1584,19 @@ static struct my_option my_options[] = "prefix for the file names.", &result_file_name, &result_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef WHEN_FLASHBACK_REVIEW_READY + {"review", opt_flashback_review, "Print review sql in output file.", + &opt_flashback_review, &opt_flashback_review, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, + {"review-dbname", opt_flashback_flashback_review_dbname, + "Writing flashback original row data into this db", + &flashback_review_dbname, &flashback_review_dbname, + 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"review-tablename", opt_flashback_flashback_review_tablename, + "Writing flashback original row data into this table", + &flashback_review_tablename, &flashback_review_tablename, + 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"server-id", 0, "Extract only binlog entries created by the server having the given id.", &server_id, &server_id, 0, GET_ULONG, @@ -1458,6 +1660,9 @@ static struct my_option my_options[] = &stop_position, &stop_position, 0, GET_ULL, REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE, (ulonglong)(~(my_off_t)0), 0, 0, 0}, + {"table", 'T', "List entries for just this table (local log only).", + &table, &table, 0, GET_STR_ALLOC, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, {"to-last-log", 't', "Requires -R. Will not stop at the end of the \ requested binlog but rather continue printing until the end of the last \ binlog of the MySQL server. If you send the output to the same MySQL server, \ @@ -1567,6 +1772,7 @@ static void cleanup() { my_free(pass); my_free(database); + my_free(table); my_free(host); my_free(user); my_free(const_cast<char*>(dirname_for_local_load)); @@ -1637,6 +1843,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; #endif #include <sslopt-case.h> + case 'B': + opt_flashback= 1; + break; case 'd': one_database = 1; break; @@ -1658,10 +1867,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'R': remote_opt= 1; break; + case 'T': + one_table= 1; + break; case OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); break; +#ifdef WHEN_FLASHBACK_REVIEW_READY + case opt_flashback_review: + opt_flashback_review= 1; + break; +#endif case OPT_START_DATETIME: start_datetime= convert_str_to_timestamp(start_datetime_str); break; @@ -1862,7 +2079,7 @@ static Exit_status dump_log_entries(const char* logname) dump_local_log_entries(&print_event_info, logname)); /* Set delimiter back to semicolon */ - if (!opt_raw_mode) + if (!opt_raw_mode && !opt_flashback) fprintf(result_file, "DELIMITER ;\n"); strmov(print_event_info.delimiter, ";"); return rc; @@ -2660,6 +2877,9 @@ int main(int argc, char** argv) DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); + (void) my_init_dynamic_array(&binlog_events, sizeof(LEX_STRING), 1024, 1024, + MYF(0)); + my_init_time(); // for time functions tzset(); // set tzname @@ -2795,6 +3015,29 @@ int main(int argc, char** argv) start_position= BIN_LOG_HEADER_SIZE; } + /* + If enable flashback, need to print the events from the end to the + beginning + */ + if (opt_flashback) + { + for (uint i= binlog_events.elements; i > 0; --i) + { + LEX_STRING *event_str= dynamic_element(&binlog_events, i - 1, + LEX_STRING*); + fprintf(result_file, "%s", event_str->str); + my_free(event_str->str); + } + fprintf(result_file, "COMMIT\n/*!*/;\n"); + } + delete_dynamic(&binlog_events); + + /* Set delimiter back to semicolon */ + if (!stop_event_string.is_empty()) + fprintf(result_file, "%s", stop_event_string.ptr()); + if (!opt_raw_mode && opt_flashback) + fprintf(result_file, "DELIMITER ;\n"); + if (!opt_raw_mode) { /* diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 203921ce08f..b5e65cc1f0d 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -1072,6 +1072,9 @@ The following options may be given as the first argument: passwords that cannot be validated (passwords specified as a hash) (Defaults to on; use --skip-strict-password-validation to disable.) + --support-flashback Setup the server to use flashback. This enables binary + log in row mode and will enable extra logging for DDL's + needed by flashback feature -s, --symbolic-links Enable symbolic link support. --sync-binlog=# Synchronously flush binary log to disk after every #th @@ -1464,6 +1467,7 @@ stack-trace TRUE standards-compliant-cte TRUE stored-program-cache 256 strict-password-validation TRUE +support-flashback FALSE symbolic-links FALSE sync-binlog 0 sync-frm FALSE diff --git a/mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result b/mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result new file mode 100644 index 00000000000..94833c5224a --- /dev/null +++ b/mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result @@ -0,0 +1,480 @@ +# +# Preparatory cleanup. +# +DROP TABLE IF EXISTS t1; +# +# We need a fixed timestamp to avoid varying results. +# +SET timestamp=1000000000; +# +# Delete all existing binary logs. +# +RESET MASTER; +CREATE TABLE t1 ( +c01 tinyint, +c02 smallint, +c03 mediumint, +c04 int, +c05 bigint, +c06 char(10), +c07 varchar(20), +c08 TEXT +) ENGINE=InnoDB; +# +# Insert data to t1 +# +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(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 255)); +# +# Update t1 +# +UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3; +# +# Clear t1 +# +DELETE FROM t1; +FLUSH LOGS; +# +# Show mysqlbinlog result without -B +# +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Start: binlog v 4, server v #.##.## created 010909 9:46:40 at startup +ROLLBACK/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Gtid list [] +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000001 +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX GTID 0-1-1 ddl +/*!100101 SET @@session.skip_parallel_replication=0*//*!*/; +/*!100001 SET @@session.gtid_domain_id=0*//*!*/; +/*!100001 SET @@session.server_id=1*//*!*/; +/*!100001 SET @@session.gtid_seq_no=1*//*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0 +use `test`/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.pseudo_thread_id=#/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/; +SET @@session.sql_mode=1342177280/*!*/; +SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +SET @@session.lc_time_names=0/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +CREATE TABLE t1 ( +c01 tinyint, +c02 smallint, +c03 mediumint, +c04 int, +c05 bigint, +c06 char(10), +c07 varchar(20), +c08 TEXT +) ENGINE=InnoDB +/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX GTID 0-1-2 trans +/*!100001 SET @@session.gtid_seq_no=2*//*!*/; +BEGIN +/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F +### INSERT INTO `test`.`t1` +### SET +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +COMMIT/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX GTID 0-1-3 trans +/*!100001 SET @@session.gtid_seq_no=3*//*!*/; +BEGIN +/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F +### INSERT INTO `test`.`t1` +### SET +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +COMMIT/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX GTID 0-1-4 trans +/*!100001 SET @@session.gtid_seq_no=4*//*!*/; +BEGIN +/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F +### INSERT INTO `test`.`t1` +### SET +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */ +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +COMMIT/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX GTID 0-1-5 trans +/*!100001 SET @@session.gtid_seq_no=5*//*!*/; +BEGIN +/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Update_rows: table id # flags: STMT_END_F +### UPDATE `test`.`t1` +### WHERE +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### SET +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### UPDATE `test`.`t1` +### WHERE +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### SET +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +COMMIT/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX GTID 0-1-6 trans +/*!100001 SET @@session.gtid_seq_no=6*//*!*/; +BEGIN +/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Delete_rows: table id # flags: STMT_END_F +### DELETE FROM `test`.`t1` +### WHERE +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### DELETE FROM `test`.`t1` +### WHERE +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### DELETE FROM `test`.`t1` +### WHERE +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */ +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +COMMIT/*!*/; +# at # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Rotate to master-bin.000002 pos: 4 +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; +# +# Show mysqlbinlog result with -B +# +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Start: binlog v 4, server v #.##.## created 010909 9:46:40 at startup +ROLLBACK/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Gtid list [] +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Binlog checkpoint master-bin.000001 +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1` mapped to number # +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Rotate to master-bin.000002 pos: 4 +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +BEGIN/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F +### INSERT INTO `test`.`t1` +### SET +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */ +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### INSERT INTO `test`.`t1` +### SET +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### INSERT INTO `test`.`t1` +### SET +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +COMMIT +/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +BEGIN/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Update_rows: table id # flags: STMT_END_F +### UPDATE `test`.`t1` +### WHERE +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### SET +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### UPDATE `test`.`t1` +### WHERE +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +### SET +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +COMMIT +/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +BEGIN/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Delete_rows: table id # flags: STMT_END_F +### DELETE FROM `test`.`t1` +### WHERE +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */ +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +COMMIT +/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +BEGIN/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Delete_rows: table id # flags: STMT_END_F +### DELETE FROM `test`.`t1` +### WHERE +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=4 /* INT meta=0 nullable=1 is_null=0 */ +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +COMMIT +/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Xid = # +BEGIN/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Delete_rows: table id # flags: STMT_END_F +### DELETE FROM `test`.`t1` +### WHERE +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */ +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */ +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ +### @4=0 /* INT meta=0 nullable=1 is_null=0 */ +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */ +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */ +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */ +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */ +COMMIT +/*!*/; +#010909 9:46:40 server id 1 end_log_pos # CRC32 XXX Query thread_id=# exec_time=# error_code=0 +use `test`/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.pseudo_thread_id=#/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/; +SET @@session.sql_mode=1342177280/*!*/; +SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +SET @@session.lc_time_names=0/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +COMMIT +/*!*/; +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; +# +# Insert data to t1 +# +TRUNCATE TABLE t1; +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(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60)); +# +# Delete all existing binary logs. +# +RESET MASTER; +SELECT * FROM t1; +c01 c02 c03 c04 c05 c06 c07 c08 +0 0 0 0 0 +1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz +127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +# +# Operate some data +# +UPDATE t1 SET c01=20; +UPDATE t1 SET c02=200; +UPDATE t1 SET c03=2000; +DELETE FROM t1; +FLUSH LOGS; +# +# Flashback & Check the result +# +SELECT * FROM t1; +c01 c02 c03 c04 c05 c06 c07 c08 +127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz +0 0 0 0 0 +RESET MASTER; +# +# UPDATE multi-rows in one event +# +BEGIN; +UPDATE t1 SET c01=10 WHERE c01=0; +UPDATE t1 SET c01=20 WHERE c01=10; +COMMIT; +FLUSH LOGS; +# +# Flashback & Check the result +# +SELECT * FROM t1; +c01 c02 c03 c04 c05 c06 c07 c08 +127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz +0 0 0 0 0 +DROP TABLE t1; +# +# Self-referencing foreign keys +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, FOREIGN KEY my_fk(b) REFERENCES t1(a)) ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 VALUES (1, NULL); +INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3); +COMMIT; +SELECT * FROM t1; +a b +1 NULL +2 1 +3 2 +4 3 +RESET MASTER; +DELETE FROM t1 ORDER BY a DESC; +FLUSH LOGS; +# +# Flashback & Check the result +# +SELECT * FROM t1; +a b +1 NULL +2 1 +3 2 +4 3 +DROP TABLE t1; diff --git a/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback-master.opt b/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback-master.opt new file mode 100644 index 00000000000..7410f219a68 --- /dev/null +++ b/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback-master.opt @@ -0,0 +1,2 @@ +--support-flashback +--timezone=GMT-8 diff --git a/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test b/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test new file mode 100644 index 00000000000..2f395a2a7b1 --- /dev/null +++ b/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test @@ -0,0 +1,163 @@ +--source include/have_log_bin.inc +--source include/have_innodb.inc + +--echo # +--echo # Preparatory cleanup. +--echo # +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # +--echo # We need a fixed timestamp to avoid varying results. +--echo # +SET timestamp=1000000000; + +--echo # +--echo # Delete all existing binary logs. +--echo # +RESET MASTER; + + +CREATE TABLE t1 ( + c01 tinyint, + c02 smallint, + c03 mediumint, + c04 int, + c05 bigint, + c06 char(10), + c07 varchar(20), + c08 TEXT +) ENGINE=InnoDB; + +--echo # +--echo # Insert data to t1 +--echo # +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(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 255)); + + +--echo # +--echo # Update t1 +--echo # +UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3; + +--echo # +--echo # Clear t1 +--echo # +DELETE FROM t1; + +FLUSH LOGS; + +--echo # +--echo # Show mysqlbinlog result without -B +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--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 + +--echo # +--echo # Show mysqlbinlog result with -B +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--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 + +--echo # +--echo # Insert data to t1 +--echo # +TRUNCATE TABLE t1; +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(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60)); + +--echo # +--echo # Delete all existing binary logs. +--echo # +RESET MASTER; +SELECT * FROM t1; + +--echo # +--echo # Operate some data +--echo # + +UPDATE t1 SET c01=20; +UPDATE t1 SET c02=200; +UPDATE t1 SET c03=2000; + +DELETE FROM t1; + +FLUSH LOGS; + +--echo # +--echo # Flashback & Check the result +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--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;" + +SELECT * FROM t1; + +RESET MASTER; + +--echo # +--echo # UPDATE multi-rows in one event +--echo # +BEGIN; +UPDATE t1 SET c01=10 WHERE c01=0; +UPDATE t1 SET c01=20 WHERE c01=10; +COMMIT; + +FLUSH LOGS; + +--echo # +--echo # Flashback & Check the result +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--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;" + +SELECT * FROM t1; + +DROP TABLE t1; + +--echo # +--echo # Self-referencing foreign keys +--echo # +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, FOREIGN KEY my_fk(b) REFERENCES t1(a)) ENGINE=InnoDB; + +BEGIN; +INSERT INTO t1 VALUES (1, NULL); +INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3); +COMMIT; + +SELECT * FROM t1; + +# New binlog +RESET MASTER; + +DELETE FROM t1 ORDER BY a DESC; + +FLUSH LOGS; + +--echo # +--echo # Flashback & Check the result +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--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;" + +SELECT * FROM t1; + +DROP TABLE t1; diff --git a/sql/log_event.cc b/sql/log_event.cc index 0af348ab453..893781223fb 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -300,17 +300,34 @@ public: constructor, but it would be possible to create a subclass holding the IO_CACHE itself. */ - Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0) - : m_cache(cache), m_file(file), m_flags(flags) + Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0, Log_event *ev = NULL) + : m_cache(cache), m_file(file), m_flags(flags), m_ev(ev) { reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE); } ~Write_on_release_cache() { +#ifdef MYSQL_CLIENT + if(m_ev == NULL) + { + copy_event_cache_to_file_and_reinit(m_cache, m_file); + if (m_flags & FLUSH_F) + fflush(m_file); + } + else // if m_ev<>NULL, then storing the output in output_buf + { + LEX_STRING tmp_str; + if (copy_event_cache_to_string_and_reinit(m_cache, &tmp_str)) + exit(1); + m_ev->output_buf.append(tmp_str.str, tmp_str.length); + my_free(tmp_str.str); + } +#else /* MySQL_SERVER */ copy_event_cache_to_file_and_reinit(m_cache, m_file); if (m_flags & FLUSH_F) fflush(m_file); +#endif } /* @@ -340,6 +357,7 @@ private: IO_CACHE *m_cache; FILE *m_file; flag_set m_flags; + Log_event *m_ev; // Used for Flashback }; /* @@ -2760,7 +2778,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr, d= (ulong) (i64 / 1000000); t= (ulong) (i64 % 1000000); - my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d", + my_b_printf(file, "'%04d-%02d-%02d %02d:%02d:%02d'", (int) (d / 10000), (int) (d % 10000) / 100, (int) (d % 100), (int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100); return 8; @@ -2994,22 +3012,30 @@ size_t Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, PRINT_EVENT_INFO *print_event_info, MY_BITMAP *cols_bitmap, - const uchar *value, const uchar *prefix) + const uchar *value, const uchar *prefix, + const my_bool no_fill_output) { const uchar *value0= value; const uchar *null_bits= value; uint null_bit_index= 0; char typestr[64]= ""; - + +#ifdef WHEN_FLASHBACK_REVIEW_READY + /* Storing the review SQL */ + IO_CACHE *review_sql= &print_event_info->review_sql_cache; + LEX_STRING review_str; +#endif + /* Skip metadata bytes which gives the information about nullabity of master columns. Master writes one bit for each affected column. */ value+= (bitmap_bits_set(cols_bitmap) + 7) / 8; - - my_b_printf(file, "%s", prefix); - + + if (!no_fill_output) + my_b_printf(file, "%s", prefix); + for (size_t i= 0; i < td->size(); i ++) { size_t size; @@ -3018,41 +3044,102 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, if (bitmap_is_set(cols_bitmap, i) == 0) continue; - - my_b_printf(file, "### @%d=", static_cast<int>(i + 1)); + + if (!no_fill_output) + my_b_printf(file, "### @%d=", static_cast<int>(i + 1)); + if (!is_null) { size_t fsize= td->calc_field_size((uint)i, (uchar*) value); if (value + fsize > m_rows_end) { - my_b_printf(file, "***Corrupted replication event was detected." - " Not printing the value***\n"); + if (!no_fill_output) + my_b_printf(file, "***Corrupted replication event was detected." + " Not printing the value***\n"); value+= fsize; return 0; } } - if (!(size= log_event_print_value(file,is_null? NULL: value, - td->type(i), td->field_metadata(i), - typestr, sizeof(typestr)))) + + if (!no_fill_output) + { + size= log_event_print_value(file,is_null? NULL: value, + td->type(i), td->field_metadata(i), + typestr, sizeof(typestr)); +#ifdef WHEN_FLASHBACK_REVIEW_READY + if (need_flashback_review) + { + String tmp_str, hex_str; + IO_CACHE tmp_cache; + + // Using a tmp IO_CACHE to get the value output + open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP)); + size= log_event_print_value(&tmp_cache, is_null? NULL: value, + td->type(i), td->field_metadata(i), + typestr, sizeof(typestr)); + if (copy_event_cache_to_string_and_reinit(&tmp_cache, &review_str)) + exit(1); + close_cached_file(&tmp_cache); + + switch (td->type(i)) // Converting a string to HEX format + { + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_BLOB: + // Avoid write_pos changed to a new area + // tmp_str.free(); + tmp_str.append(review_str.str + 1, review_str.length - 2); // Removing quotation marks + if (hex_str.alloc(tmp_str.length()*2+1)) // If out of memory + { + fprintf(stderr, "\nError: Out of memory. " + "Could not print correct binlog event.\n"); + exit(1); + } + octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length()); + my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr()); + break; + default: + tmp_str.free(); + tmp_str.append(review_str.str, review_str.length); + my_b_printf(review_sql, ", %s", tmp_str.ptr()); + break; + } + my_free(revieww_str.str); + } +#endif + } + else + { + IO_CACHE tmp_cache; + open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP)); + size= log_event_print_value(&tmp_cache,is_null? NULL: value, + td->type(i), td->field_metadata(i), + typestr, sizeof(typestr)); + close_cached_file(&tmp_cache); + } + + if (!size) return 0; if (!is_null) value+= size; - if (print_event_info->verbose > 1) + if (print_event_info->verbose > 1 && !no_fill_output) { my_b_write(file, (uchar*)" /* ", 4); my_b_printf(file, "%s ", typestr); - + my_b_printf(file, "meta=%d nullable=%d is_null=%d ", td->field_metadata(i), td->maybe_null(i), is_null); my_b_write(file, (uchar*)"*/", 2); } - - my_b_write_byte(file, '\n'); - + + if (!no_fill_output) + my_b_write_byte(file, '\n'); + null_bit_index++; } return value - value0; @@ -3060,6 +3147,124 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, /** + Exchange the SET part and WHERE part for the Update events. + Revert the operations order for the Write and Delete events. + And then revert the events order from the last one to the first one. + + @param[in] print_event_info PRINT_EVENT_INFO + @param[in] rows_buff Packed event buff +*/ + +void Rows_log_event::change_to_flashback_event(PRINT_EVENT_INFO *print_event_info, + uchar *rows_buff, Log_event_type ev_type) +{ + Table_map_log_event *map; + table_def *td; + DYNAMIC_ARRAY rows_arr; + uchar *swap_buff1, *swap_buff2; + uchar *rows_pos= rows_buff + m_rows_before_size; + + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || + !(td= map->create_table_def())) + return; + + /* If the write rows event contained no values for the AI */ + if (((get_general_type_code() == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end))) + goto end; + + (void) my_init_dynamic_array(&rows_arr, sizeof(LEX_STRING), 8, 8, MYF(0)); + + for (uchar *value= m_rows_buf; value < m_rows_end; ) + { + uchar *start_pos= value; + size_t length1= 0; + if (!(length1= print_verbose_one_row(NULL, td, print_event_info, + &m_cols, value, + (const uchar*) "", TRUE))) + { + fprintf(stderr, "\nError row length: %zu\n", length1); + exit(1); + } + value+= length1; + + swap_buff1= (uchar *) my_malloc(length1, MYF(0)); + if (!swap_buff1) + { + fprintf(stderr, "\nError: Out of memory. " + "Could not exchange to flashback event.\n"); + exit(1); + } + memcpy(swap_buff1, start_pos, length1); + + // For Update_event, we have the second part + size_t length2= 0; + if (ev_type == UPDATE_ROWS_EVENT || + ev_type == UPDATE_ROWS_EVENT_V1) + { + if (!(length2= print_verbose_one_row(NULL, td, print_event_info, + &m_cols, value, + (const uchar*) "", TRUE))) + { + fprintf(stderr, "\nError row length: %zu\n", length2); + exit(1); + } + value+= length2; + + swap_buff2= (uchar *) my_malloc(length2, MYF(0)); + if (!swap_buff2) + { + fprintf(stderr, "\nError: Out of memory. " + "Could not exchange to flashback event.\n"); + exit(1); + } + memcpy(swap_buff2, start_pos + length1, length2); // WHERE part + } + + if (ev_type == UPDATE_ROWS_EVENT || + ev_type == UPDATE_ROWS_EVENT_V1) + { + /* Swap SET and WHERE part */ + memcpy(start_pos, swap_buff2, length2); + memcpy(start_pos + length2, swap_buff1, length1); + } + + /* Free tmp buffers */ + my_free(swap_buff1); + if (ev_type == UPDATE_ROWS_EVENT || + ev_type == UPDATE_ROWS_EVENT_V1) + my_free(swap_buff2); + + /* Copying one row into a buff, and pushing into the array */ + LEX_STRING one_row; + + one_row.length= length1 + length2; + one_row.str= (char *) my_malloc(one_row.length, MYF(0)); + memcpy(one_row.str, start_pos, one_row.length); + if (one_row.str == NULL || push_dynamic(&rows_arr, (uchar *) &one_row)) + { + fprintf(stderr, "\nError: Out of memory. " + "Could not push flashback event into array.\n"); + exit(1); + } + } + + /* Copying rows from the end to the begining into event */ + for (uint i= rows_arr.elements; i > 0; --i) + { + LEX_STRING *one_row= dynamic_element(&rows_arr, i - 1, LEX_STRING*); + + memcpy(rows_pos, (uchar *)one_row->str, one_row->length); + rows_pos+= one_row->length; + my_free(one_row->str); + } + delete_dynamic(&rows_arr); + +end: + delete td; +} + + +/** Print a row event into IO cache in human readable form (in SQL format) @param[in] file IO cache @@ -3071,8 +3276,12 @@ void Rows_log_event::print_verbose(IO_CACHE *file, Table_map_log_event *map; table_def *td; const char *sql_command, *sql_clause1, *sql_clause2; + const char *sql_command_short __attribute__((unused)); Log_event_type general_type_code= get_general_type_code(); - +#ifdef WHEN_FLASHBACK_REVIEW_READY + IO_CACHE *review_sql= &print_event_info->review_sql_cache; +#endif + if (m_extra_row_data) { uint8 extra_data_len= m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET]; @@ -3102,19 +3311,23 @@ void Rows_log_event::print_verbose(IO_CACHE *file, sql_command= "INSERT INTO"; sql_clause1= "### SET\n"; sql_clause2= NULL; + sql_command_short= "I"; break; case DELETE_ROWS_EVENT: sql_command= "DELETE FROM"; sql_clause1= "### WHERE\n"; sql_clause2= NULL; + sql_command_short= "D"; break; case UPDATE_ROWS_EVENT: sql_command= "UPDATE"; sql_clause1= "### WHERE\n"; sql_clause2= "### SET\n"; + sql_command_short= "U"; break; default: sql_command= sql_clause1= sql_clause2= NULL; + sql_command_short= ""; DBUG_ASSERT(0); /* Not possible */ } @@ -3140,6 +3353,13 @@ void Rows_log_event::print_verbose(IO_CACHE *file, my_b_printf(file, "### %s %`s.%`s\n", sql_command, map->get_db_name(), map->get_table_name()); + +#ifdef WHEN_FLASHBACK_REVIEW_READY + if (need_flashback_review) + my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'", + map->get_review_dbname(), map->get_review_tablename(), sql_command_short); +#endif + /* Print the first image */ if (!(length= print_verbose_one_row(file, td, print_event_info, &m_cols, value, @@ -3156,6 +3376,17 @@ void Rows_log_event::print_verbose(IO_CACHE *file, goto end; value+= length; } +#ifdef WHEN_FLASHBACK_REVIEW_READY + else + { + if (need_flashback_review) + for (size_t i= 0; i < td->size(); i ++) + my_b_printf(review_sql, ", NULL"); + } + + if (need_flashback_review) + my_b_printf(review_sql, ")%s\n", print_event_info->delimiter); +#endif } end: @@ -3171,7 +3402,7 @@ void Log_event::print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool more) { - const uchar *ptr= (const uchar *)temp_buf; + uchar *ptr= (uchar *)temp_buf; uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET); DBUG_ENTER("Log_event::print_base64"); @@ -3183,6 +3414,51 @@ void Log_event::print_base64(IO_CACHE* file, DBUG_VOID_RETURN; } + if (is_flashback) + { + uint tmp_size= size; + Rows_log_event *ev= NULL; + Log_event_type ev_type = (enum Log_event_type) ptr[EVENT_TYPE_OFFSET]; + if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF && + checksum_alg != BINLOG_CHECKSUM_ALG_OFF) + tmp_size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header + switch (ev_type) { + case WRITE_ROWS_EVENT: + ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT; + ev= new Delete_rows_log_event((const char*) ptr, tmp_size, + glob_description_event); + ev->change_to_flashback_event(print_event_info, ptr, ev_type); + break; + case WRITE_ROWS_EVENT_V1: + ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT_V1; + ev= new Delete_rows_log_event((const char*) ptr, tmp_size, + glob_description_event); + ev->change_to_flashback_event(print_event_info, ptr, ev_type); + break; + case DELETE_ROWS_EVENT: + ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT; + ev= new Write_rows_log_event((const char*) ptr, tmp_size, + glob_description_event); + ev->change_to_flashback_event(print_event_info, ptr, ev_type); + break; + case DELETE_ROWS_EVENT_V1: + ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT_V1; + ev= new Write_rows_log_event((const char*) ptr, tmp_size, + glob_description_event); + ev->change_to_flashback_event(print_event_info, ptr, ev_type); + break; + case UPDATE_ROWS_EVENT: + case UPDATE_ROWS_EVENT_V1: + ev= new Update_rows_log_event((const char*) ptr, tmp_size, + glob_description_event); + ev->change_to_flashback_event(print_event_info, ptr, ev_type); + break; + default: + break; + } + delete ev; + } + if (my_base64_encode(ptr, (size_t) size, tmp_str)) { DBUG_ASSERT(0); @@ -3198,8 +3474,12 @@ void Log_event::print_base64(IO_CACHE* file, if (!more) my_b_printf(file, "'%s\n", print_event_info->delimiter); } - + +#ifdef WHEN_FLASHBACK_REVIEW_READY + if (print_event_info->verbose || need_flashback_review) +#else if (print_event_info->verbose) +#endif { Rows_log_event *ev= NULL; Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET]; @@ -3207,7 +3487,7 @@ void Log_event::print_base64(IO_CACHE* file, if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF && checksum_alg != BINLOG_CHECKSUM_ALG_OFF) size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header - + switch (et) { case TABLE_MAP_EVENT: @@ -3215,6 +3495,13 @@ void Log_event::print_base64(IO_CACHE* file, Table_map_log_event *map; map= new Table_map_log_event((const char*) ptr, size, glob_description_event); +#ifdef WHEN_FLASHBACK_REVIEW_READY + if (need_flashback_review) + { + map->set_review_dbname(m_review_dbname.ptr()); + map->set_review_tablename(m_review_tablename.ptr()); + } +#endif print_event_info->m_table_map.set_table(map->get_table_id(), map); break; } @@ -3263,14 +3550,27 @@ void Log_event::print_base64(IO_CACHE* file, default: break; } - + if (ev) { +#ifdef WHEN_FLASHBACK_REVIEW_READY + ev->need_flashback_review= need_flashback_review; + if (print_event_info->verbose) + ev->print_verbose(file, print_event_info); + else + { + IO_CACHE tmp_cache; + open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP)); + ev->print_verbose(&tmp_cache, print_event_info); + close_cached_file(&tmp_cache); + } +#else ev->print_verbose(file, print_event_info); +#endif delete ev; } } - + my_free(tmp_str); DBUG_VOID_RETURN; } @@ -4631,7 +4931,7 @@ void Query_log_event::print_query_header(IO_CACHE* file, void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - Write_on_release_cache cache(&print_event_info->head_cache, file); + Write_on_release_cache cache(&print_event_info->head_cache, file, 0, this); /** reduce the size of io cache so that the write function is called @@ -4640,8 +4940,24 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) DBUG_EXECUTE_IF ("simulate_file_write_error", {(&cache)->write_pos= (&cache)->write_end- 500;}); print_query_header(&cache, print_event_info); - my_b_write(&cache, (uchar*) query, q_len); - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + if (!is_flashback) + { + my_b_write(&cache, (uchar*) query, q_len); + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + } + else // is_flashback == 1 + { + if (strcmp("BEGIN", query) == 0) + { + my_b_write(&cache, (uchar*) "COMMIT", 6); + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + } + else if (strcmp("COMMIT", query) == 0) + { + my_b_write(&cache, (uchar*) "BEGIN", 5); + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + } + } } #endif /* MYSQL_CLIENT */ @@ -7351,11 +7667,11 @@ void Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, - Write_on_release_cache::FLUSH_F); + Write_on_release_cache::FLUSH_F, this); char buf[21]; char buf2[21]; - if (!print_event_info->short_form) + if (!print_event_info->short_form & !is_flashback) { print_header(&cache, print_event_info, FALSE); longlong10_to_str(seq_no, buf, 10); @@ -7401,11 +7717,12 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) print_event_info->server_id_printed= true; } - my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n", - buf, print_event_info->delimiter); + if (!is_flashback) + my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n", + buf, print_event_info->delimiter); } if (!(flags2 & FL_STANDALONE)) - my_b_printf(&cache, "BEGIN\n%s\n", print_event_info->delimiter); + my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter); } #endif /* MYSQL_SERVER */ @@ -8047,7 +8364,7 @@ bool Xid_log_event::write() void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, - Write_on_release_cache::FLUSH_F); + Write_on_release_cache::FLUSH_F, this); if (!print_event_info->short_form) { @@ -8057,7 +8374,7 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) print_header(&cache, print_event_info, FALSE); my_b_printf(&cache, "\tXid = %s\n", buf); } - my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter); + my_b_printf(&cache, is_flashback ? "BEGIN%s\n" : "COMMIT%s\n", print_event_info->delimiter); } #endif /* MYSQL_CLIENT */ @@ -8706,7 +9023,7 @@ void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, - Write_on_release_cache::FLUSH_F); + Write_on_release_cache::FLUSH_F, this); if (print_event_info->short_form) return; @@ -10042,6 +10359,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, m_rows_end= m_rows_buf + data_size; m_rows_cur= m_rows_end; memcpy(m_rows_buf, ptr_rows_data, data_size); + m_rows_before_size= ptr_rows_data - (const uchar *) buf; // Get the size that before SET part } else m_cols.bitmap= 0; // to not free it @@ -10958,6 +11276,10 @@ void Rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; +#ifdef WHEN_FLASHBACK_REVIEW_READY + IO_CACHE *const sql= &print_event_info->review_sql_cache; +#endif + if (!print_event_info->short_form) { bool const last_stmt_event= get_flags(STMT_END_F); @@ -10970,8 +11292,19 @@ void Rows_log_event::print_helper(FILE *file, if (get_flags(STMT_END_F)) { - copy_event_cache_to_file_and_reinit(head, file); - copy_event_cache_to_file_and_reinit(body, file); + reinit_io_cache(head, READ_CACHE, 0L, FALSE, FALSE); + 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); + +#ifdef WHEN_FLASHBACK_REVIEW_READY + reinit_io_cache(sql, READ_CACHE, 0L, FALSE, FALSE); + output_buf.append(sql, sql->end_of_file); + reinit_io_cache(sql, WRITE_CACHE, 0, FALSE, TRUE); +#endif } } #endif @@ -12374,7 +12707,7 @@ void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { DBUG_EXECUTE_IF("simulate_cache_read_error", {DBUG_SET("+d,simulate_my_b_fill_error");}); - Rows_log_event::print_helper(file, print_event_info, "Write_rows"); + Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows"); } void Write_rows_compressed_log_event::print(FILE *file, @@ -13048,7 +13381,7 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi) void Delete_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { - Rows_log_event::print_helper(file, print_event_info, "Delete_rows"); + Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows"); } void Delete_rows_compressed_log_event::print(FILE *file, @@ -13600,6 +13933,9 @@ st_print_event_info::st_print_event_info() myf const flags = MYF(MY_WME | MY_NABP); open_cached_file(&head_cache, NULL, NULL, 0, flags); open_cached_file(&body_cache, NULL, NULL, 0, flags); +#ifdef WHEN_FLASHBACK_REVIEW_READY + open_cached_file(&review_sql_cache, NULL, NULL, 0, flags); +#endif } #endif diff --git a/sql/log_event.h b/sql/log_event.h index 7b8704636af..d1b75476065 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -41,6 +41,7 @@ #include "rpl_utility.h" #include "hash.h" #include "rpl_tblmap.h" +#include "sql_string.h" #endif #ifdef MYSQL_SERVER @@ -52,7 +53,9 @@ #include "rpl_gtid.h" /* Forward declarations */ +#ifndef MYSQL_CLIENT class String; +#endif #define PREFIX_SQL_LOAD "SQL_LOAD-" #define LONG_FIND_ROW_THRESHOLD 60 /* seconds */ @@ -845,9 +848,16 @@ typedef struct st_print_event_info ~st_print_event_info() { close_cached_file(&head_cache); close_cached_file(&body_cache); +#ifdef WHEN_FLASHBACK_REVIEW_READY + close_cached_file(&review_sql_cache); +#endif } bool init_ok() /* tells if construction was successful */ - { return my_b_inited(&head_cache) && my_b_inited(&body_cache); } + { return my_b_inited(&head_cache) && my_b_inited(&body_cache) +#ifdef WHEN_FLASHBACK_REVIEW_READY + && my_b_inited(&review_sql_cache) +#endif + ; } /* Settings on how to print the events */ @@ -875,6 +885,10 @@ typedef struct st_print_event_info */ IO_CACHE head_cache; IO_CACHE body_cache; +#ifdef WHEN_FLASHBACK_REVIEW_READY + /* Storing the SQL for reviewing */ + IO_CACHE review_sql_cache; +#endif } PRINT_EVENT_INFO; #endif @@ -1223,6 +1237,37 @@ public: void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool is_more); #endif + + /* The following code used for Flashback */ +#ifdef MYSQL_CLIENT + my_bool is_flashback; + my_bool need_flashback_review; + String output_buf; // Storing the event output +#ifdef WHEN_FLASHBACK_REVIEW_READY + String m_review_dbname; + String m_review_tablename; + + void set_review_dbname(const char *name) + { + if (name) + { + m_review_dbname.free(); + m_review_dbname.append(name); + } + } + void set_review_tablename(const char *name) + { + if (name) + { + m_review_tablename.free(); + m_review_tablename.append(name); + } + } + const char *get_review_dbname() const { return m_review_dbname.ptr(); } + const char *get_review_tablename() const { return m_review_tablename.ptr(); } +#endif +#endif + /* read_log_event() functions read an event from a binlog or relay log; used by SHOW BINLOG EVENTS, the binlog_dump thread on the @@ -4362,12 +4407,14 @@ public: #ifdef MYSQL_CLIENT /* not for direct call, each derived has its own ::print() */ virtual void 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); void print_verbose(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info); 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 uchar *ptr, const uchar *prefix, + const my_bool no_fill_output= 0); // if no_fill_output=1, then print result is unnecessary #endif #ifdef MYSQL_SERVER @@ -4506,6 +4553,8 @@ protected: uchar *m_rows_cur; /* One-after the end of the data */ uchar *m_rows_end; /* One-after the end of the allocated space */ + size_t m_rows_before_size; /* The length before m_rows_buf */ + flag_set m_flags; /* Flags for row-level events */ Log_event_type m_type; /* Actual event type */ @@ -5040,6 +5089,30 @@ public: }; +static inline bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to) +{ + String tmp; + + reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE); + if (tmp.append(cache, cache->end_of_file)) + goto err; + reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE); + + //if (tmp.append('\0')) + // goto err; + /* + Can't change the order, because the String::release() will clear the length. + */ + to->length= tmp.length(); + to->str= tmp.release(); + + return false; + +err: + perror("Out of memory: can't allocate memory in copy_event_cache_to_string_and_reinit()."); + return true; +} + static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file) { diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4b79ea43c7c..fb85154343b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -393,7 +393,7 @@ bool opt_bin_log_compress; uint opt_bin_log_compress_min_len; my_bool opt_log, debug_assert_if_crashed_table= 0, opt_help= 0; my_bool debug_assert_on_not_freed_memory= 0; -my_bool disable_log_notes; +my_bool disable_log_notes, opt_support_flashback= 0; static my_bool opt_abort; ulonglong log_output_options; my_bool opt_userstat_running; @@ -7405,6 +7405,10 @@ struct my_option my_long_options[]= 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, /* We must always support the next option to make scripts like mysqltest easier to do */ + {"support_flashback", 0, + "Setup the server to use flashback. This enables binary log in row mode and will enable extra logging for DDL's needed by flashback feature", + &opt_support_flashback, &opt_support_flashback, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"gdb", 0, "Set up signals usable for debugging. Deprecated, use --debug-gdb instead.", &opt_debugging, &opt_debugging, @@ -9579,6 +9583,18 @@ static int get_options(int *argc_ptr, char ***argv_ptr) else global_system_variables.option_bits&= ~OPTION_BIG_SELECTS; + if (opt_support_flashback) + { + /* Force binary logging */ + if (!opt_bin_logname) + opt_bin_logname= (char*) ""; // Use default name + opt_bin_log= opt_bin_log_used= 1; + + /* Force format to row */ + binlog_format_used= 1; + global_system_variables.binlog_format= BINLOG_FORMAT_ROW; + } + if (!opt_bootstrap && WSREP_PROVIDER_EXISTS && global_system_variables.binlog_format != BINLOG_FORMAT_ROW) { diff --git a/sql/mysqld.h b/sql/mysqld.h index 96944c012ce..9022b8cef03 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -114,6 +114,7 @@ extern uint opt_bin_log_compress_min_len; extern my_bool opt_log, opt_bootstrap; extern my_bool opt_backup_history_log; extern my_bool opt_backup_progress_log; +extern my_bool opt_support_flashback; extern ulonglong log_output_options; extern ulong log_backup_output_options; extern my_bool opt_log_queries_not_using_indexes; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index bdfedc30d46..42582ab5d18 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -449,16 +449,17 @@ static bool binlog_format_check(sys_var *self, THD *thd, set_var *var) /* MariaDB Galera does not support STATEMENT or MIXED binlog format currently. */ - if (WSREP(thd) && var->save_result.ulonglong_value != BINLOG_FORMAT_ROW) + if ((WSREP(thd) || opt_support_flashback) && + var->save_result.ulonglong_value != BINLOG_FORMAT_ROW) { // Push a warning to the error log. push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, - "MariaDB Galera does not support binlog format: %s", + "MariaDB Galera and flashback does not support binlog format: %s", binlog_format_names[var->save_result.ulonglong_value]); if (var->type == OPT_GLOBAL) { - WSREP_ERROR("MariaDB Galera does not support binlog format: %s", + WSREP_ERROR("MariaDB Galera and flashback does not support binlog format: %s", binlog_format_names[var->save_result.ulonglong_value]); return true; } |