mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
[MDEV-10570] Add Flashback support
==== 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 to mysqlbinlog ==== --flashback (-B) It will let mysqlbinlog to work on FLASHBACK mode. ==== New Arguments to mysqld ==== --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 ==== 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:
This commit is contained in:
@ -66,6 +66,10 @@ enum options_client
|
|||||||
OPT_MYSQLDUMP_SLAVE_APPLY,
|
OPT_MYSQLDUMP_SLAVE_APPLY,
|
||||||
OPT_MYSQLDUMP_SLAVE_DATA,
|
OPT_MYSQLDUMP_SLAVE_DATA,
|
||||||
OPT_MYSQLDUMP_INCLUDE_MASTER_HOST_PORT,
|
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_CSV, OPT_SLAP_CREATE_STRING,
|
||||||
OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, OPT_SLAP_AUTO_GENERATE_WRITE_NUM,
|
OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, OPT_SLAP_AUTO_GENERATE_WRITE_NUM,
|
||||||
OPT_SLAP_AUTO_GENERATE_ADD_AUTO,
|
OPT_SLAP_AUTO_GENERATE_ADD_AUTO,
|
||||||
|
@ -66,6 +66,10 @@ Rpl_filter *binlog_filter= 0;
|
|||||||
/* Needed for Rpl_filter */
|
/* Needed for Rpl_filter */
|
||||||
CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci;
|
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];
|
char server_version[SERVER_VERSION_LENGTH];
|
||||||
ulong server_id = 0;
|
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 error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||||
static void warning(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;
|
static bool opt_hexdump= 0, opt_version= 0;
|
||||||
const char *base64_output_mode_names[]=
|
const char *base64_output_mode_names[]=
|
||||||
{"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS};
|
{"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 enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC;
|
||||||
static char *opt_base64_output_mode_str= NullS;
|
static char *opt_base64_output_mode_str= NullS;
|
||||||
static char* database= 0;
|
static char* database= 0;
|
||||||
|
static char* table= 0;
|
||||||
static my_bool force_opt= 0, short_form= 0, remote_opt= 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 debug_info_flag, debug_check_flag;
|
||||||
static my_bool force_if_open_opt= 1;
|
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 const char* dirname_for_local_load= 0;
|
||||||
static bool opt_skip_annotate_row_events= 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.
|
Pointer to the Format_description_log_event of the currently active binlog.
|
||||||
|
|
||||||
@ -790,6 +801,23 @@ print_skip_replication_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev)
|
|||||||
pinfo->skip_replication= cur_val;
|
pinfo->skip_replication= cur_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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.
|
Prints the given event in base64 format.
|
||||||
|
|
||||||
@ -952,6 +980,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
|
|||||||
Exit_status retval= OK_CONTINUE;
|
Exit_status retval= OK_CONTINUE;
|
||||||
IO_CACHE *const head= &print_event_info->head_cache;
|
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
|
Format events are not concerned by --offset and such, we always need to
|
||||||
read them to be able to process the wanted events.
|
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;
|
retval= OK_STOP;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
if (!short_form)
|
if (!short_form && !opt_flashback)
|
||||||
fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
|
fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
|
||||||
|
|
||||||
if (!opt_hexdump)
|
if (!opt_hexdump)
|
||||||
@ -1214,12 +1248,128 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
|
|||||||
case TABLE_MAP_EVENT:
|
case TABLE_MAP_EVENT:
|
||||||
{
|
{
|
||||||
Table_map_log_event *map= ((Table_map_log_event *)ev);
|
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);
|
print_event_info->m_table_map_ignored.set_table(map->get_table_id(), map);
|
||||||
destroy_evt= FALSE;
|
destroy_evt= FALSE;
|
||||||
goto end;
|
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
|
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).
|
print the kept Annotate event (if there is any).
|
||||||
@ -1294,6 +1444,38 @@ end:
|
|||||||
*/
|
*/
|
||||||
if (ev)
|
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) */
|
if (destroy_evt) /* destroy it later if not set (ignored table map) */
|
||||||
delete ev;
|
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.",
|
"already have. NOTE: you will need a SUPER privilege to use this option.",
|
||||||
&disable_log_bin, &disable_log_bin, 0, GET_BOOL,
|
&disable_log_bin, &disable_log_bin, 0, GET_BOOL,
|
||||||
NO_ARG, 0, 0, 0, 0, 0, 0},
|
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", 'F', "Force if binlog was not closed properly.",
|
||||||
&force_if_open_opt, &force_if_open_opt, 0, GET_BOOL, NO_ARG,
|
&force_if_open_opt, &force_if_open_opt, 0, GET_BOOL, NO_ARG,
|
||||||
1, 0, 0, 0, 0, 0},
|
1, 0, 0, 0, 0, 0},
|
||||||
@ -1395,6 +1584,19 @@ static struct my_option my_options[] =
|
|||||||
"prefix for the file names.",
|
"prefix for the file names.",
|
||||||
&result_file_name, &result_file_name, 0, GET_STR, REQUIRED_ARG,
|
&result_file_name, &result_file_name, 0, GET_STR, REQUIRED_ARG,
|
||||||
0, 0, 0, 0, 0, 0},
|
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,
|
{"server-id", 0,
|
||||||
"Extract only binlog entries created by the server having the given id.",
|
"Extract only binlog entries created by the server having the given id.",
|
||||||
&server_id, &server_id, 0, GET_ULONG,
|
&server_id, &server_id, 0, GET_ULONG,
|
||||||
@ -1458,6 +1660,9 @@ static struct my_option my_options[] =
|
|||||||
&stop_position, &stop_position, 0, GET_ULL,
|
&stop_position, &stop_position, 0, GET_ULL,
|
||||||
REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
|
REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
|
||||||
(ulonglong)(~(my_off_t)0), 0, 0, 0},
|
(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 \
|
{"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 \
|
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, \
|
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(pass);
|
||||||
my_free(database);
|
my_free(database);
|
||||||
|
my_free(table);
|
||||||
my_free(host);
|
my_free(host);
|
||||||
my_free(user);
|
my_free(user);
|
||||||
my_free(const_cast<char*>(dirname_for_local_load));
|
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;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#include <sslopt-case.h>
|
#include <sslopt-case.h>
|
||||||
|
case 'B':
|
||||||
|
opt_flashback= 1;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
one_database = 1;
|
one_database = 1;
|
||||||
break;
|
break;
|
||||||
@ -1658,10 +1867,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
|||||||
case 'R':
|
case 'R':
|
||||||
remote_opt= 1;
|
remote_opt= 1;
|
||||||
break;
|
break;
|
||||||
|
case 'T':
|
||||||
|
one_table= 1;
|
||||||
|
break;
|
||||||
case OPT_MYSQL_PROTOCOL:
|
case OPT_MYSQL_PROTOCOL:
|
||||||
opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
|
opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
|
||||||
opt->name);
|
opt->name);
|
||||||
break;
|
break;
|
||||||
|
#ifdef WHEN_FLASHBACK_REVIEW_READY
|
||||||
|
case opt_flashback_review:
|
||||||
|
opt_flashback_review= 1;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case OPT_START_DATETIME:
|
case OPT_START_DATETIME:
|
||||||
start_datetime= convert_str_to_timestamp(start_datetime_str);
|
start_datetime= convert_str_to_timestamp(start_datetime_str);
|
||||||
break;
|
break;
|
||||||
@ -1862,7 +2079,7 @@ static Exit_status dump_log_entries(const char* logname)
|
|||||||
dump_local_log_entries(&print_event_info, logname));
|
dump_local_log_entries(&print_event_info, logname));
|
||||||
|
|
||||||
/* Set delimiter back to semicolon */
|
/* Set delimiter back to semicolon */
|
||||||
if (!opt_raw_mode)
|
if (!opt_raw_mode && !opt_flashback)
|
||||||
fprintf(result_file, "DELIMITER ;\n");
|
fprintf(result_file, "DELIMITER ;\n");
|
||||||
strmov(print_event_info.delimiter, ";");
|
strmov(print_event_info.delimiter, ";");
|
||||||
return rc;
|
return rc;
|
||||||
@ -2660,6 +2877,9 @@ int main(int argc, char** argv)
|
|||||||
DBUG_ENTER("main");
|
DBUG_ENTER("main");
|
||||||
DBUG_PROCESS(argv[0]);
|
DBUG_PROCESS(argv[0]);
|
||||||
|
|
||||||
|
(void) my_init_dynamic_array(&binlog_events, sizeof(LEX_STRING), 1024, 1024,
|
||||||
|
MYF(0));
|
||||||
|
|
||||||
my_init_time(); // for time functions
|
my_init_time(); // for time functions
|
||||||
tzset(); // set tzname
|
tzset(); // set tzname
|
||||||
|
|
||||||
@ -2795,6 +3015,29 @@ int main(int argc, char** argv)
|
|||||||
start_position= BIN_LOG_HEADER_SIZE;
|
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)
|
if (!opt_raw_mode)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -208,6 +208,9 @@ The following options may be given as the first argument:
|
|||||||
--extra-port=# Extra port number to use for tcp connections in a
|
--extra-port=# Extra port number to use for tcp connections in a
|
||||||
one-thread-per-connection manner. 0 means don't use
|
one-thread-per-connection manner. 0 means don't use
|
||||||
another port
|
another port
|
||||||
|
--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
|
||||||
--flush Flush MyISAM tables to disk between SQL commands
|
--flush Flush MyISAM tables to disk between SQL commands
|
||||||
--flush-time=# A dedicated thread is created to flush all tables at the
|
--flush-time=# A dedicated thread is created to flush all tables at the
|
||||||
given interval
|
given interval
|
||||||
@ -1233,6 +1236,7 @@ explicit-defaults-for-timestamp FALSE
|
|||||||
external-locking FALSE
|
external-locking FALSE
|
||||||
extra-max-connections 1
|
extra-max-connections 1
|
||||||
extra-port 0
|
extra-port 0
|
||||||
|
flashback FALSE
|
||||||
flush FALSE
|
flush FALSE
|
||||||
flush-time 0
|
flush-time 0
|
||||||
ft-boolean-syntax + -><()~*:""&|
|
ft-boolean-syntax + -><()~*:""&|
|
||||||
|
480
mysql-test/suite/binlog/r/flashback.result
Normal file
480
mysql-test/suite/binlog/r/flashback.result
Normal file
@ -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;
|
2
mysql-test/suite/binlog/t/flashback-master.opt
Normal file
2
mysql-test/suite/binlog/t/flashback-master.opt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
--flashback
|
||||||
|
--timezone=GMT-8
|
163
mysql-test/suite/binlog/t/flashback.test
Normal file
163
mysql-test/suite/binlog/t/flashback.test
Normal file
@ -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;
|
@ -8,20 +8,20 @@ Variable_name Value
|
|||||||
binlog_format ROW
|
binlog_format ROW
|
||||||
SET binlog_format=STATEMENT;
|
SET binlog_format=STATEMENT;
|
||||||
Warnings:
|
Warnings:
|
||||||
Warning 1105 MariaDB Galera does not support binlog format: STATEMENT
|
Warning 1105 MariaDB Galera and flashback does not support binlog format: STATEMENT
|
||||||
SHOW WARNINGS;
|
SHOW WARNINGS;
|
||||||
Level Code Message
|
Level Code Message
|
||||||
Warning 1105 MariaDB Galera does not support binlog format: STATEMENT
|
Warning 1105 MariaDB Galera and flashback does not support binlog format: STATEMENT
|
||||||
SHOW VARIABLES LIKE 'binlog_format';
|
SHOW VARIABLES LIKE 'binlog_format';
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
binlog_format STATEMENT
|
binlog_format STATEMENT
|
||||||
CREATE TABLE IF NOT EXISTS test.t1 AS SELECT * FROM information_schema.routines WHERE 1 = 0;
|
CREATE TABLE IF NOT EXISTS test.t1 AS SELECT * FROM information_schema.routines WHERE 1 = 0;
|
||||||
SET binlog_format=MIXED;
|
SET binlog_format=MIXED;
|
||||||
Warnings:
|
Warnings:
|
||||||
Warning 1105 MariaDB Galera does not support binlog format: MIXED
|
Warning 1105 MariaDB Galera and flashback does not support binlog format: MIXED
|
||||||
SHOW WARNINGS;
|
SHOW WARNINGS;
|
||||||
Level Code Message
|
Level Code Message
|
||||||
Warning 1105 MariaDB Galera does not support binlog format: MIXED
|
Warning 1105 MariaDB Galera and flashback does not support binlog format: MIXED
|
||||||
SHOW VARIABLES LIKE 'binlog_format';
|
SHOW VARIABLES LIKE 'binlog_format';
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
binlog_format MIXED
|
binlog_format MIXED
|
||||||
|
418
sql/log_event.cc
418
sql/log_event.cc
@ -300,17 +300,34 @@ public:
|
|||||||
constructor, but it would be possible to create a subclass
|
constructor, but it would be possible to create a subclass
|
||||||
holding the IO_CACHE itself.
|
holding the IO_CACHE itself.
|
||||||
*/
|
*/
|
||||||
Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0)
|
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_cache(cache), m_file(file), m_flags(flags), m_ev(ev)
|
||||||
{
|
{
|
||||||
reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
|
reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Write_on_release_cache()
|
~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);
|
copy_event_cache_to_file_and_reinit(m_cache, m_file);
|
||||||
if (m_flags & FLUSH_F)
|
if (m_flags & FLUSH_F)
|
||||||
fflush(m_file);
|
fflush(m_file);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -340,6 +357,7 @@ private:
|
|||||||
IO_CACHE *m_cache;
|
IO_CACHE *m_cache;
|
||||||
FILE *m_file;
|
FILE *m_file;
|
||||||
flag_set m_flags;
|
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);
|
d= (ulong) (i64 / 1000000);
|
||||||
t= (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) (d / 10000), (int) (d % 10000) / 100, (int) (d % 100),
|
||||||
(int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100);
|
(int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100);
|
||||||
return 8;
|
return 8;
|
||||||
@ -2994,22 +3012,30 @@ size_t
|
|||||||
Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
|
Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
|
||||||
PRINT_EVENT_INFO *print_event_info,
|
PRINT_EVENT_INFO *print_event_info,
|
||||||
MY_BITMAP *cols_bitmap,
|
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 *value0= value;
|
||||||
const uchar *null_bits= value;
|
const uchar *null_bits= value;
|
||||||
uint null_bit_index= 0;
|
uint null_bit_index= 0;
|
||||||
char typestr[64]= "";
|
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
|
Skip metadata bytes which gives the information about nullabity of master
|
||||||
columns. Master writes one bit for each affected column.
|
columns. Master writes one bit for each affected column.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
|
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 ++)
|
for (size_t i= 0; i < td->size(); i ++)
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -3018,47 +3044,226 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
|
|||||||
|
|
||||||
if (bitmap_is_set(cols_bitmap, i) == 0)
|
if (bitmap_is_set(cols_bitmap, i) == 0)
|
||||||
continue;
|
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)
|
if (!is_null)
|
||||||
{
|
{
|
||||||
size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
|
size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
|
||||||
if (value + fsize > m_rows_end)
|
if (value + fsize > m_rows_end)
|
||||||
{
|
{
|
||||||
my_b_printf(file, "***Corrupted replication event was detected."
|
if (!no_fill_output)
|
||||||
" Not printing the value***\n");
|
my_b_printf(file, "***Corrupted replication event was detected."
|
||||||
|
" Not printing the value***\n");
|
||||||
value+= fsize;
|
value+= fsize;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(size= log_event_print_value(file,is_null? NULL: value,
|
|
||||||
td->type(i), td->field_metadata(i),
|
if (!no_fill_output)
|
||||||
typestr, sizeof(typestr))))
|
{
|
||||||
|
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;
|
return 0;
|
||||||
|
|
||||||
if (!is_null)
|
if (!is_null)
|
||||||
value+= size;
|
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_write(file, (uchar*)" /* ", 4);
|
||||||
|
|
||||||
my_b_printf(file, "%s ", typestr);
|
my_b_printf(file, "%s ", typestr);
|
||||||
|
|
||||||
my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
|
my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
|
||||||
td->field_metadata(i),
|
td->field_metadata(i),
|
||||||
td->maybe_null(i), is_null);
|
td->maybe_null(i), is_null);
|
||||||
my_b_write(file, (uchar*)"*/", 2);
|
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++;
|
null_bit_index++;
|
||||||
}
|
}
|
||||||
return value - value0;
|
return value - value0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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)
|
Print a row event into IO cache in human readable form (in SQL format)
|
||||||
|
|
||||||
@ -3071,8 +3276,12 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
|
|||||||
Table_map_log_event *map;
|
Table_map_log_event *map;
|
||||||
table_def *td;
|
table_def *td;
|
||||||
const char *sql_command, *sql_clause1, *sql_clause2;
|
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();
|
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)
|
if (m_extra_row_data)
|
||||||
{
|
{
|
||||||
uint8 extra_data_len= m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET];
|
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_command= "INSERT INTO";
|
||||||
sql_clause1= "### SET\n";
|
sql_clause1= "### SET\n";
|
||||||
sql_clause2= NULL;
|
sql_clause2= NULL;
|
||||||
|
sql_command_short= "I";
|
||||||
break;
|
break;
|
||||||
case DELETE_ROWS_EVENT:
|
case DELETE_ROWS_EVENT:
|
||||||
sql_command= "DELETE FROM";
|
sql_command= "DELETE FROM";
|
||||||
sql_clause1= "### WHERE\n";
|
sql_clause1= "### WHERE\n";
|
||||||
sql_clause2= NULL;
|
sql_clause2= NULL;
|
||||||
|
sql_command_short= "D";
|
||||||
break;
|
break;
|
||||||
case UPDATE_ROWS_EVENT:
|
case UPDATE_ROWS_EVENT:
|
||||||
sql_command= "UPDATE";
|
sql_command= "UPDATE";
|
||||||
sql_clause1= "### WHERE\n";
|
sql_clause1= "### WHERE\n";
|
||||||
sql_clause2= "### SET\n";
|
sql_clause2= "### SET\n";
|
||||||
|
sql_command_short= "U";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sql_command= sql_clause1= sql_clause2= NULL;
|
sql_command= sql_clause1= sql_clause2= NULL;
|
||||||
|
sql_command_short= "";
|
||||||
DBUG_ASSERT(0); /* Not possible */
|
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",
|
my_b_printf(file, "### %s %`s.%`s\n",
|
||||||
sql_command,
|
sql_command,
|
||||||
map->get_db_name(), map->get_table_name());
|
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 */
|
/* Print the first image */
|
||||||
if (!(length= print_verbose_one_row(file, td, print_event_info,
|
if (!(length= print_verbose_one_row(file, td, print_event_info,
|
||||||
&m_cols, value,
|
&m_cols, value,
|
||||||
@ -3156,6 +3376,17 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
|
|||||||
goto end;
|
goto end;
|
||||||
value+= length;
|
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:
|
end:
|
||||||
@ -3171,7 +3402,7 @@ void Log_event::print_base64(IO_CACHE* file,
|
|||||||
PRINT_EVENT_INFO* print_event_info,
|
PRINT_EVENT_INFO* print_event_info,
|
||||||
bool more)
|
bool more)
|
||||||
{
|
{
|
||||||
const uchar *ptr= (const uchar *)temp_buf;
|
uchar *ptr= (uchar *)temp_buf;
|
||||||
uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
|
uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
|
||||||
DBUG_ENTER("Log_event::print_base64");
|
DBUG_ENTER("Log_event::print_base64");
|
||||||
|
|
||||||
@ -3183,6 +3414,51 @@ void Log_event::print_base64(IO_CACHE* file,
|
|||||||
DBUG_VOID_RETURN;
|
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))
|
if (my_base64_encode(ptr, (size_t) size, tmp_str))
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(0);
|
DBUG_ASSERT(0);
|
||||||
@ -3198,8 +3474,12 @@ void Log_event::print_base64(IO_CACHE* file,
|
|||||||
if (!more)
|
if (!more)
|
||||||
my_b_printf(file, "'%s\n", print_event_info->delimiter);
|
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)
|
if (print_event_info->verbose)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
Rows_log_event *ev= NULL;
|
Rows_log_event *ev= NULL;
|
||||||
Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET];
|
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 &&
|
if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
|
||||||
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
|
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
|
||||||
size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
|
size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
|
||||||
|
|
||||||
switch (et)
|
switch (et)
|
||||||
{
|
{
|
||||||
case TABLE_MAP_EVENT:
|
case TABLE_MAP_EVENT:
|
||||||
@ -3215,6 +3495,13 @@ void Log_event::print_base64(IO_CACHE* file,
|
|||||||
Table_map_log_event *map;
|
Table_map_log_event *map;
|
||||||
map= new Table_map_log_event((const char*) ptr, size,
|
map= new Table_map_log_event((const char*) ptr, size,
|
||||||
glob_description_event);
|
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);
|
print_event_info->m_table_map.set_table(map->get_table_id(), map);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3263,14 +3550,27 @@ void Log_event::print_base64(IO_CACHE* file,
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev)
|
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);
|
ev->print_verbose(file, print_event_info);
|
||||||
|
#endif
|
||||||
delete ev;
|
delete ev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my_free(tmp_str);
|
my_free(tmp_str);
|
||||||
DBUG_VOID_RETURN;
|
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)
|
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
|
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",
|
DBUG_EXECUTE_IF ("simulate_file_write_error",
|
||||||
{(&cache)->write_pos= (&cache)->write_end- 500;});
|
{(&cache)->write_pos= (&cache)->write_end- 500;});
|
||||||
print_query_header(&cache, print_event_info);
|
print_query_header(&cache, print_event_info);
|
||||||
my_b_write(&cache, (uchar*) query, q_len);
|
if (!is_flashback)
|
||||||
my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
|
{
|
||||||
|
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 */
|
#endif /* MYSQL_CLIENT */
|
||||||
|
|
||||||
@ -7351,11 +7667,11 @@ void
|
|||||||
Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
|
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 cache(&print_event_info->head_cache, file,
|
||||||
Write_on_release_cache::FLUSH_F);
|
Write_on_release_cache::FLUSH_F, this);
|
||||||
char buf[21];
|
char buf[21];
|
||||||
char buf2[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);
|
print_header(&cache, print_event_info, FALSE);
|
||||||
longlong10_to_str(seq_no, buf, 10);
|
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;
|
print_event_info->server_id_printed= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
|
if (!is_flashback)
|
||||||
buf, print_event_info->delimiter);
|
my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
|
||||||
|
buf, print_event_info->delimiter);
|
||||||
}
|
}
|
||||||
if (!(flags2 & FL_STANDALONE))
|
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 */
|
#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)
|
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 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)
|
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);
|
print_header(&cache, print_event_info, FALSE);
|
||||||
my_b_printf(&cache, "\tXid = %s\n", buf);
|
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 */
|
#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)
|
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 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)
|
if (print_event_info->short_form)
|
||||||
return;
|
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_end= m_rows_buf + data_size;
|
||||||
m_rows_cur= m_rows_end;
|
m_rows_cur= m_rows_end;
|
||||||
memcpy(m_rows_buf, ptr_rows_data, data_size);
|
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
|
else
|
||||||
m_cols.bitmap= 0; // to not free it
|
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 head= &print_event_info->head_cache;
|
||||||
IO_CACHE *const body= &print_event_info->body_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)
|
if (!print_event_info->short_form)
|
||||||
{
|
{
|
||||||
bool const last_stmt_event= get_flags(STMT_END_F);
|
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))
|
if (get_flags(STMT_END_F))
|
||||||
{
|
{
|
||||||
copy_event_cache_to_file_and_reinit(head, file);
|
reinit_io_cache(head, READ_CACHE, 0L, FALSE, FALSE);
|
||||||
copy_event_cache_to_file_and_reinit(body, file);
|
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
|
#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_EXECUTE_IF("simulate_cache_read_error",
|
||||||
{DBUG_SET("+d,simulate_my_b_fill_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,
|
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,
|
void Delete_rows_log_event::print(FILE *file,
|
||||||
PRINT_EVENT_INFO* print_event_info)
|
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,
|
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);
|
myf const flags = MYF(MY_WME | MY_NABP);
|
||||||
open_cached_file(&head_cache, NULL, NULL, 0, flags);
|
open_cached_file(&head_cache, NULL, NULL, 0, flags);
|
||||||
open_cached_file(&body_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
|
#endif
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "rpl_utility.h"
|
#include "rpl_utility.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "rpl_tblmap.h"
|
#include "rpl_tblmap.h"
|
||||||
|
#include "sql_string.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MYSQL_SERVER
|
#ifdef MYSQL_SERVER
|
||||||
@ -52,7 +53,9 @@
|
|||||||
#include "rpl_gtid.h"
|
#include "rpl_gtid.h"
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
|
#ifndef MYSQL_CLIENT
|
||||||
class String;
|
class String;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PREFIX_SQL_LOAD "SQL_LOAD-"
|
#define PREFIX_SQL_LOAD "SQL_LOAD-"
|
||||||
#define LONG_FIND_ROW_THRESHOLD 60 /* seconds */
|
#define LONG_FIND_ROW_THRESHOLD 60 /* seconds */
|
||||||
@ -845,9 +848,16 @@ typedef struct st_print_event_info
|
|||||||
~st_print_event_info() {
|
~st_print_event_info() {
|
||||||
close_cached_file(&head_cache);
|
close_cached_file(&head_cache);
|
||||||
close_cached_file(&body_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 */
|
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 */
|
/* Settings on how to print the events */
|
||||||
@ -875,6 +885,10 @@ typedef struct st_print_event_info
|
|||||||
*/
|
*/
|
||||||
IO_CACHE head_cache;
|
IO_CACHE head_cache;
|
||||||
IO_CACHE body_cache;
|
IO_CACHE body_cache;
|
||||||
|
#ifdef WHEN_FLASHBACK_REVIEW_READY
|
||||||
|
/* Storing the SQL for reviewing */
|
||||||
|
IO_CACHE review_sql_cache;
|
||||||
|
#endif
|
||||||
} PRINT_EVENT_INFO;
|
} PRINT_EVENT_INFO;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1223,6 +1237,37 @@ public:
|
|||||||
void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
|
void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
|
||||||
bool is_more);
|
bool is_more);
|
||||||
#endif
|
#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
|
read_log_event() functions read an event from a binlog or relay
|
||||||
log; used by SHOW BINLOG EVENTS, the binlog_dump thread on the
|
log; used by SHOW BINLOG EVENTS, the binlog_dump thread on the
|
||||||
@ -4362,12 +4407,14 @@ public:
|
|||||||
#ifdef MYSQL_CLIENT
|
#ifdef MYSQL_CLIENT
|
||||||
/* not for direct call, each derived has its own ::print() */
|
/* not for direct call, each derived has its own ::print() */
|
||||||
virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
|
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,
|
void print_verbose(IO_CACHE *file,
|
||||||
PRINT_EVENT_INFO *print_event_info);
|
PRINT_EVENT_INFO *print_event_info);
|
||||||
size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
|
size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
|
||||||
PRINT_EVENT_INFO *print_event_info,
|
PRINT_EVENT_INFO *print_event_info,
|
||||||
MY_BITMAP *cols_bitmap,
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef MYSQL_SERVER
|
#ifdef MYSQL_SERVER
|
||||||
@ -4506,6 +4553,8 @@ protected:
|
|||||||
uchar *m_rows_cur; /* One-after the end of the data */
|
uchar *m_rows_cur; /* One-after the end of the data */
|
||||||
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 */
|
||||||
|
|
||||||
flag_set m_flags; /* Flags for row-level events */
|
flag_set m_flags; /* Flags for row-level events */
|
||||||
|
|
||||||
Log_event_type m_type; /* Actual event type */
|
Log_event_type m_type; /* Actual event type */
|
||||||
@ -5040,6 +5089,29 @@ 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
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,
|
static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
|
||||||
FILE *file)
|
FILE *file)
|
||||||
{
|
{
|
||||||
|
@ -393,7 +393,7 @@ bool opt_bin_log_compress;
|
|||||||
uint opt_bin_log_compress_min_len;
|
uint opt_bin_log_compress_min_len;
|
||||||
my_bool opt_log, debug_assert_if_crashed_table= 0, opt_help= 0;
|
my_bool opt_log, debug_assert_if_crashed_table= 0, opt_help= 0;
|
||||||
my_bool debug_assert_on_not_freed_memory= 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;
|
static my_bool opt_abort;
|
||||||
ulonglong log_output_options;
|
ulonglong log_output_options;
|
||||||
my_bool opt_userstat_running;
|
my_bool opt_userstat_running;
|
||||||
@ -7413,6 +7413,10 @@ struct my_option my_long_options[]=
|
|||||||
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
/* We must always support the next option to make scripts like mysqltest
|
/* We must always support the next option to make scripts like mysqltest
|
||||||
easier to do */
|
easier to do */
|
||||||
|
{"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,
|
{"gdb", 0,
|
||||||
"Set up signals usable for debugging. Deprecated, use --debug-gdb instead.",
|
"Set up signals usable for debugging. Deprecated, use --debug-gdb instead.",
|
||||||
&opt_debugging, &opt_debugging,
|
&opt_debugging, &opt_debugging,
|
||||||
@ -9587,6 +9591,18 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
|
|||||||
else
|
else
|
||||||
global_system_variables.option_bits&= ~OPTION_BIG_SELECTS;
|
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 &&
|
if (!opt_bootstrap && WSREP_PROVIDER_EXISTS &&
|
||||||
global_system_variables.binlog_format != BINLOG_FORMAT_ROW)
|
global_system_variables.binlog_format != BINLOG_FORMAT_ROW)
|
||||||
{
|
{
|
||||||
|
@ -114,6 +114,7 @@ extern uint opt_bin_log_compress_min_len;
|
|||||||
extern my_bool opt_log, opt_bootstrap;
|
extern my_bool opt_log, opt_bootstrap;
|
||||||
extern my_bool opt_backup_history_log;
|
extern my_bool opt_backup_history_log;
|
||||||
extern my_bool opt_backup_progress_log;
|
extern my_bool opt_backup_progress_log;
|
||||||
|
extern my_bool opt_support_flashback;
|
||||||
extern ulonglong log_output_options;
|
extern ulonglong log_output_options;
|
||||||
extern ulong log_backup_output_options;
|
extern ulong log_backup_output_options;
|
||||||
extern my_bool opt_log_queries_not_using_indexes;
|
extern my_bool opt_log_queries_not_using_indexes;
|
||||||
|
@ -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.
|
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 a warning to the error log.
|
||||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
|
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]);
|
binlog_format_names[var->save_result.ulonglong_value]);
|
||||||
|
|
||||||
if (var->type == OPT_GLOBAL)
|
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]);
|
binlog_format_names[var->save_result.ulonglong_value]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user