Commit 0d10b5a5 authored by Alexey Botchkov's avatar Alexey Botchkov

MDEV-8713 Add continuous binary log backup to mysqlbinlog.

        --raw, --stop-never and --stop-never-slave-server-id=id options
        added to the mysqlbinlog tool.
parent 727bbdd1
......@@ -73,6 +73,8 @@ ulong opt_binlog_rows_event_max_size;
uint test_flags = 0;
static uint opt_protocol= 0;
static FILE *result_file;
static char *result_file_name= 0;
static const char *output_prefix= "";
#ifndef DBUG_OFF
static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace";
......@@ -96,6 +98,8 @@ static char* database= 0;
static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
static my_bool debug_info_flag, debug_check_flag;
static my_bool force_if_open_opt= 1;
static my_bool opt_raw_mode= 0, opt_stop_never= 0;
static ulong opt_stop_never_slave_server_id= 0;
static my_bool opt_verify_binlog_checksum= 1;
static ulonglong offset = 0;
static char* host = 0;
......@@ -120,7 +124,6 @@ static ulonglong start_position, stop_position;
static char *start_datetime_str, *stop_datetime_str;
static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX;
static ulonglong rec_count= 0;
static short binlog_flags = 0;
static MYSQL* mysql = NULL;
static const char* dirname_for_local_load= 0;
static bool opt_skip_annotate_row_events= 0;
......@@ -142,7 +145,9 @@ enum Exit_status {
/** An error occurred and execution should stop. */
ERROR_STOP,
/** No error occurred but execution should stop. */
OK_STOP
OK_STOP,
/** No error occurred - end of file reached. */
OK_EOF,
};
/**
......@@ -1368,8 +1373,14 @@ static struct my_option my_options[] =
{"read-from-remote-server", 'R', "Read binary logs from a MySQL server.",
&remote_opt, &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
0, 0},
{"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"raw", 0, "Requires -R. Output raw binlog data instead of SQL "
"statements. Output files named after server logs.",
&opt_raw_mode, &opt_raw_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
0, 0},
{"result-file", 'r', "Direct output to a given file. With --raw this is a "
"prefix for the file names.",
&result_file_name, &result_file_name, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"server-id", 0,
"Extract only binlog entries created by the server having the given id.",
&server_id, &server_id, 0, GET_ULONG,
......@@ -1418,6 +1429,14 @@ static struct my_option my_options[] =
"(you should probably use quotes for your shell to set it properly).",
&stop_datetime_str, &stop_datetime_str,
0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"stop-never", 0, "Wait for more data from the server "
"instead of stopping at the end of the last log. Implies --to-last-log.",
&opt_stop_never, &opt_stop_never, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"stop-never-slave-server-id", 0,
"The slave server_id used for --read-from-remote-server --stop-never.",
&opt_stop_never_slave_server_id, &opt_stop_never_slave_server_id, 0,
GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"stop-position", OPT_STOP_POSITION,
"Stop reading the binlog at position N. Applies to the last binlog "
"passed on the command line.",
......@@ -1620,10 +1639,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
else
tty_password=1;
break;
case 'r':
if (!(result_file = my_fopen(argument, O_WRONLY | O_BINARY, MYF(MY_WME))))
exit(1);
break;
case 'R':
remote_opt= 1;
break;
......@@ -1719,7 +1734,6 @@ static int parse_args(int *argc, char*** argv)
{
int ho_error;
result_file = stdout;
if ((ho_error=handle_options(argc, argv, my_options, get_one_option)))
exit(ho_error);
if (debug_info_flag)
......@@ -1809,6 +1823,7 @@ static Exit_status dump_log_entries(const char* logname)
Set safe delimiter, to dump things
like CREATE PROCEDURE safely
*/
if (!opt_raw_mode)
fprintf(result_file, "DELIMITER /*!*/;\n");
strmov(print_event_info.delimiter, "/*!*/;");
......@@ -1818,6 +1833,7 @@ static Exit_status dump_log_entries(const char* logname)
dump_local_log_entries(&print_event_info, logname));
/* Set delimiter back to semicolon */
if (!opt_raw_mode)
fprintf(result_file, "DELIMITER ;\n");
strmov(print_event_info.delimiter, ";");
return rc;
......@@ -1924,87 +1940,19 @@ static Exit_status check_master_version()
}
/**
Requests binlog dump from a remote server and prints the events it
receives.
@param[in,out] print_event_info Parameters and context state
determining how to print.
@param[in] logname Name of input binlog.
@retval ERROR_STOP An error occurred - the program should terminate.
@retval OK_CONTINUE No error, the program should continue.
@retval OK_STOP No error, but the end of the specified range of
events to process has been reached and the program should terminate.
*/
static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
const char* logname)
static Exit_status handle_event_text_mode(PRINT_EVENT_INFO *print_event_info,
ulong *len,
const char* logname,
uint logname_len, my_off_t old_off)
{
uchar buf[128];
ulong len;
uint logname_len;
NET* net;
my_off_t old_off= start_position_mot;
char fname[FN_REFLEN+1];
Exit_status retval= OK_CONTINUE;
DBUG_ENTER("dump_remote_log_entries");
/*
Even if we already read one binlog (case of >=2 binlogs on command line),
we cannot re-use the same connection as before, because it is now dead
(COM_BINLOG_DUMP kills the thread when it finishes).
*/
if ((retval= safe_connect()) != OK_CONTINUE)
DBUG_RETURN(retval);
net= &mysql->net;
if ((retval= check_master_version()) != OK_CONTINUE)
DBUG_RETURN(retval);
/*
COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
cast to uint32.
*/
DBUG_ASSERT(start_position <= UINT_MAX32);
int4store(buf, (uint32)start_position);
if (!opt_skip_annotate_row_events)
binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
size_t tlen = strlen(logname);
if (tlen > UINT_MAX)
{
error("Log name too long.");
DBUG_RETURN(ERROR_STOP);
}
logname_len = (uint) tlen;
int4store(buf + 6, 0);
memcpy(buf + 10, logname, logname_len);
if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1))
{
error("Got fatal error sending the log dump command.");
DBUG_RETURN(ERROR_STOP);
}
for (;;)
{
const char *error_msg;
Log_event *ev;
NET *net= &mysql->net;
DBUG_ENTER("handle_event_text_mode");
len= cli_safe_read(mysql);
if (len == packet_error)
{
error("Got error reading packet from server: %s", mysql_error(mysql));
DBUG_RETURN(ERROR_STOP);
}
if (len < 8 && net->read_pos[0] == 254)
break; // end of data
DBUG_PRINT("info",( "len: %lu net->read_pos[5]: %d\n",
len, net->read_pos[5]));
if (net->read_pos[5] == ANNOTATE_ROWS_EVENT)
{
if (!(ev= read_remote_annotate_event(net->read_pos + 1, len - 1,
if (!(ev= read_remote_annotate_event(net->read_pos + 1, *len - 1,
&error_msg)))
{
error("Could not construct annotate event object: %s", error_msg);
......@@ -2014,7 +1962,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
else
{
if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 ,
len - 1, &error_msg,
*len - 1, &error_msg,
glob_description_event,
opt_verify_binlog_checksum)))
{
......@@ -2053,13 +2001,14 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
*/
if (rev->when == 0)
{
*len= 1; // fake Rotate, so don't increment old_off
if (!to_last_remote_log)
{
if ((rev->ident_len != logname_len) ||
memcmp(rev->new_log_ident, logname, logname_len))
{
delete ev;
DBUG_RETURN(OK_CONTINUE);
DBUG_RETURN(OK_EOF);
}
/*
Otherwise, this is a fake Rotate for our log, at the very
......@@ -2068,9 +2017,8 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
because it serves as a useful marker between binlogs then.
*/
delete ev;
continue;
DBUG_RETURN(OK_CONTINUE);
}
len= 1; // fake Rotate, so don't increment old_off
}
}
else if (type == FORMAT_DESCRIPTION_EVENT)
......@@ -2083,7 +2031,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
starts from BIN_LOG_HEADER_SIZE position.
*/
if (old_off != BIN_LOG_HEADER_SIZE)
len= 1; // fake event, don't increment old_off
*len= 1; // fake event, don't increment old_off
}
Exit_status retval= process_event(print_event_info, ev, old_off, logname);
if (retval != OK_CONTINUE)
......@@ -2096,6 +2044,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
uint old_len= le->fname_len;
File file;
Exit_status retval;
char fname[FN_REFLEN+1];
if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
{
......@@ -2113,6 +2062,229 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
if (retval != OK_CONTINUE)
DBUG_RETURN(retval);
}
DBUG_RETURN(OK_CONTINUE);
}
static char out_file_name[FN_REFLEN + 1];
static Exit_status handle_event_raw_mode(PRINT_EVENT_INFO *print_event_info,
ulong *len,
const char* logname, uint logname_len)
{
const char *error_msg;
const unsigned char *read_pos= mysql->net.read_pos + 1;
Log_event_type type;
DBUG_ENTER("handle_event_raw_mode");
DBUG_ASSERT(opt_raw_mode && remote_opt);
type= (Log_event_type) read_pos[EVENT_TYPE_OFFSET];
if (type == HEARTBEAT_LOG_EVENT)
DBUG_RETURN(OK_CONTINUE);
if (type == ROTATE_EVENT || type == FORMAT_DESCRIPTION_EVENT)
{
Log_event *ev;
if (!(ev= Log_event::read_log_event((const char*) read_pos ,
*len - 1, &error_msg,
glob_description_event,
opt_verify_binlog_checksum)))
{
error("Could not construct %s event object: %s",
type == ROTATE_EVENT ? "rotate" : "format description", error_msg);
DBUG_RETURN(ERROR_STOP);
}
/*
If reading from a remote host, ensure the temp_buf for the
Log_event class is pointing to the incoming stream.
*/
ev->register_temp_buf((char *) read_pos, FALSE);
if (type == ROTATE_EVENT)
{
Exit_status ret_val= OK_CONTINUE;
Rotate_log_event *rev= (Rotate_log_event *)ev;
char *pe= strmake(out_file_name, output_prefix, sizeof(out_file_name)-1);
strmake(pe, rev->new_log_ident, sizeof(out_file_name) - (pe-out_file_name));
/*
If this is a fake Rotate event, and not about our log, we can stop
transfer. If this a real Rotate event (so it's not about our log,
it's in our log describing the next log), we print it (because it's
part of our log) and then we will stop when we receive the fake one
soon.
*/
if (rev->when == 0)
{
if (!to_last_remote_log)
{
if ((rev->ident_len != logname_len) ||
memcmp(rev->new_log_ident, logname, logname_len))
{
ret_val= OK_EOF;
}
/*
Otherwise, this is a fake Rotate for our log, at the very
beginning for sure. Skip it, because it was not in the original
log. If we are running with to_last_remote_log, we print it,
because it serves as a useful marker between binlogs then.
*/
}
*len= 1; // fake Rotate, so don't increment old_off
ev->temp_buf= 0;
delete ev;
DBUG_RETURN(ret_val);
}
ev->temp_buf= 0;
delete ev;
}
else /* if (type == FORMAT_DESCRIPTION_EVENT) */
{
DBUG_ASSERT(type == FORMAT_DESCRIPTION_EVENT);
if (result_file)
my_fclose(result_file, MYF(0));
if (!(result_file= my_fopen(out_file_name,
O_WRONLY | O_BINARY, MYF(MY_WME))))
{
error("Could not create output log file: %s", out_file_name);
DBUG_RETURN(ERROR_STOP);
}
/* TODO - add write error simulation here */
if (my_fwrite(result_file, (const uchar *) BINLOG_MAGIC,
BIN_LOG_HEADER_SIZE, MYF(MY_NABP)))
{
error("Could not write into log file '%s'", out_file_name);
DBUG_RETURN(ERROR_STOP);
}
delete glob_description_event;
glob_description_event= (Format_description_log_event*) ev;
print_event_info->common_header_len=
glob_description_event->common_header_len;
ev->temp_buf= 0;
/* We do not want to delete the event here. */
}
}
if (my_fwrite(result_file, read_pos, *len - 1, MYF(MY_NABP)))
{
error("Could not write into log file '%s'", out_file_name);
DBUG_RETURN(ERROR_STOP);
}
DBUG_RETURN(OK_CONTINUE);
}
/**
Requests binlog dump from a remote server and prints the events it
receives.
@param[in,out] print_event_info Parameters and context state
determining how to print.
@param[in] logname Name of input binlog.
@retval ERROR_STOP An error occurred - the program should terminate.
@retval OK_CONTINUE No error, the program should continue.
@retval OK_STOP No error, but the end of the specified range of
events to process has been reached and the program should terminate.
*/
static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
const char* logname)
{
uchar buf[128];
ulong len;
uint logname_len;
NET* net;
my_off_t old_off= start_position_mot;
Exit_status retval= OK_CONTINUE;
short binlog_flags = 0;
ulong slave_id;
DBUG_ENTER("dump_remote_log_entries");
/*
Even if we already read one binlog (case of >=2 binlogs on command line),
we cannot re-use the same connection as before, because it is now dead
(COM_BINLOG_DUMP kills the thread when it finishes).
*/
if ((retval= safe_connect()) != OK_CONTINUE)
DBUG_RETURN(retval);
net= &mysql->net;
if ((retval= check_master_version()) != OK_CONTINUE)
DBUG_RETURN(retval);
/*
COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
cast to uint32.
*/
DBUG_ASSERT(start_position <= UINT_MAX32);
int4store(buf, (uint32)start_position);
if (!opt_skip_annotate_row_events)
binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
if (!opt_stop_never)
binlog_flags|= BINLOG_DUMP_NON_BLOCK;
int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
size_t tlen = strlen(logname);
if (tlen > UINT_MAX)
{
error("Log name too long.");
DBUG_RETURN(ERROR_STOP);
}
logname_len = (uint) tlen;
if (opt_stop_never)
{
DBUG_ASSERT(to_last_remote_log);
slave_id= (opt_stop_never_slave_server_id == 0) ?
1 : opt_stop_never_slave_server_id;
}
else
slave_id= 0;
int4store(buf + 6, slave_id);
memcpy(buf + 10, logname, logname_len);
if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1))
{
error("Got fatal error sending the log dump command.");
DBUG_RETURN(ERROR_STOP);
}
for (;;)
{
len= cli_safe_read(mysql);
if (len == packet_error)
{
error("Got error reading packet from server: %s", mysql_error(mysql));
DBUG_RETURN(ERROR_STOP);
}
if (len < 8 && net->read_pos[0] == 254)
break; // end of data
DBUG_PRINT("info",( "len: %lu net->read_pos[5]: %d\n",
len, net->read_pos[5]));
if (opt_raw_mode)
{
retval= handle_event_raw_mode(print_event_info, &len,
logname, logname_len);
}
else
{
retval= handle_event_text_mode(print_event_info, &len,
logname, logname_len, old_off);
}
if (retval != OK_CONTINUE)
{
if (retval == OK_EOF)
break;
DBUG_RETURN(retval);
}
/*
Let's adjust offset for remote log as for local log to produce
similar text and to have --stop-position to work identically.
......@@ -2449,6 +2621,7 @@ static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info,
return retval;
}
int main(int argc, char** argv)
{
char **defaults_argv;
......@@ -2495,6 +2668,43 @@ int main(int argc, char** argv)
my_set_max_open_files(open_files_limit);
if (opt_stop_never)
to_last_remote_log= TRUE;
if (opt_raw_mode)
{
if (!remote_opt)
{
error("The --raw mode only works with --read-from-remote-server");
exit(1);
}
if (one_database)
warning("The --database option is ignored in raw mode");
if (stop_position != (ulonglong)(~(my_off_t)0))
warning("The --stop-position option is ignored in raw mode");
if (stop_datetime != MY_TIME_T_MAX)
warning("The --stop-datetime option is ignored in raw mode");
result_file= 0;
if (result_file_name)
output_prefix= result_file_name;
}
else
{
if (result_file_name)
{
if (!(result_file= my_fopen(result_file_name,
O_WRONLY | O_BINARY, MYF(MY_WME))))
{
error("Could not create log file '%s'", result_file_name);
exit(1);
}
}
else
result_file= stdout;
}
MY_TMPDIR tmpdir;
tmpdir.list= 0;
if (!dirname_for_local_load)
......@@ -2517,6 +2727,8 @@ int main(int argc, char** argv)
else
load_processor.init_by_cur_dir();
if (!opt_raw_mode)
{
fprintf(result_file, "/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;\n");
fprintf(result_file,
......@@ -2540,6 +2752,7 @@ int main(int argc, char** argv)
"\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;"
"\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;"
"\n/*!40101 SET NAMES %s */;\n", charset);
}
for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ;
(--argc >= 0) ; )
......@@ -2553,6 +2766,8 @@ int main(int argc, char** argv)
start_position= BIN_LOG_HEADER_SIZE;
}
if (!opt_raw_mode)
{
/*
Issue a ROLLBACK in case the last printed binlog was crashed and had half
of transaction.
......@@ -2570,10 +2785,11 @@ int main(int argc, char** argv)
"/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");
fprintf(result_file, "/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;\n");
}
if (tmpdir.list)
free_tmpdir(&tmpdir);
if (result_file != stdout)
if (result_file && result_file != stdout)
my_fclose(result_file, MYF(0));
cleanup();
free_annotate_event();
......
reset master;
set timestamp=1000000000;
drop table if exists t1;
CREATE TABLE t1 (c01 BIT);
INSERT INTO t1 VALUES (0);
INSERT INTO t1 VALUES (1);
DROP TABLE t1;
CREATE TABLE t1 (c01 BIT(7));
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
INSERT INTO t1 VALUES (4);
INSERT INTO t1 VALUES (8);
INSERT INTO t1 VALUES (16);
INSERT INTO t1 VALUES (32);
INSERT INTO t1 VALUES (64);
INSERT INTO t1 VALUES (127);
DELETE FROM t1 WHERE c01=127;
UPDATE t1 SET c01=15 WHERE c01=16;
DROP TABLE t1;
CREATE TABLE t1 (a BIT(20), b CHAR(2));
INSERT INTO t1 VALUES (b'00010010010010001001', 'ab');
DROP TABLE t1;
CREATE TABLE t1 (c02 BIT(64));
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
INSERT INTO t1 VALUES (128);
INSERT INTO t1 VALUES (b'1111111111111111111111111111111111111111111111111111111111111111');
DROP TABLE t1;
CREATE TABLE t1 (c03 TINYINT);
INSERT INTO t1 VALUES (1),(2),(3);
INSERT INTO t1 VALUES (-128);
UPDATE t1 SET c03=2 WHERE c03=1;
DELETE FROM t1 WHERE c03=-128;
DROP TABLE t1;
CREATE TABLE t1 (c04 TINYINT UNSIGNED);
INSERT INTO t1 VALUES (128), (255);
UPDATE t1 SET c04=2 WHERE c04=1;
DELETE FROM t1 WHERE c04=255;
DROP TABLE t1;
CREATE TABLE t1 (c06 BOOL);
INSERT INTO t1 VALUES (TRUE);
DELETE FROM t1 WHERE c06=TRUE;
DROP TABLE t1;
CREATE TABLE t1 (c07 SMALLINT);
INSERT INTO t1 VALUES (1234);
DELETE FROM t1 WHERE c07=1234;
DROP TABLE t1;
CREATE TABLE t1 (c08 SMALLINT UNSIGNED);
INSERT INTO t1 VALUES (32768), (65535);
UPDATE t1 SET c08=2 WHERE c08=32768;
DELETE FROM t1 WHERE c08=65535;
DROP TABLE t1;
CREATE TABLE t1 (c10 MEDIUMINT);
INSERT INTO t1 VALUES (12345);
DELETE FROM t1 WHERE c10=12345;
DROP TABLE t1;
CREATE TABLE t1 (c11 MEDIUMINT UNSIGNED);
INSERT INTO t1 VALUES (8388608), (16777215);
UPDATE t1 SET c11=2 WHERE c11=8388608;
DELETE FROM t1 WHERE c11=16777215;
DROP TABLE t1;
CREATE TABLE t1 (c13 INT);
INSERT INTO t1 VALUES (123456);
DELETE FROM t1 WHERE c13=123456;
DROP TABLE t1;
CREATE TABLE t1 (c14 INT UNSIGNED);
INSERT INTO t1 VALUES (2147483648), (4294967295);
UPDATE t1 SET c14=2 WHERE c14=2147483648;
DELETE FROM t1 WHERE c14=4294967295;
DROP TABLE t1;
CREATE TABLE t1 (c16 BIGINT);
INSERT INTO t1 VALUES (1234567890);
DELETE FROM t1 WHERE c16=1234567890;
DROP TABLE t1;
CREATE TABLE t1 (c17 BIGINT UNSIGNED);
INSERT INTO t1 VALUES (9223372036854775808), (18446744073709551615);
UPDATE t1 SET c17=2 WHERE c17=9223372036854775808;
DELETE FROM t1 WHERE c17=18446744073709551615;
DROP TABLE t1;
CREATE TABLE t1 (c19 FLOAT);
INSERT INTO t1 VALUES (123.2234);
DELETE FROM t1 WHERE c19>123;
DROP TABLE t1;
CREATE TABLE t1 (c22 DOUBLE);
INSERT INTO t1 VALUES (123434.22344545);
DELETE FROM t1 WHERE c22>123434;
DROP TABLE t1;
CREATE TABLE t1 (c25 DECIMAL(10,5));
INSERT INTO t1 VALUES (124.45);
INSERT INTO t1 VALUES (-543.21);
DELETE FROM t1 WHERE c25=124.45;
DROP TABLE t1;
CREATE TABLE t1 (c28 DATE);
INSERT INTO t1 VALUES ('2001-02-03');
DELETE FROM t1 WHERE c28='2001-02-03';
DROP TABLE t1;
CREATE TABLE t1 (c29 DATETIME);
INSERT INTO t1 VALUES ('2001-02-03 10:20:30');
DELETE FROM t1 WHERE c29='2001-02-03 10:20:30';
DROP TABLE t1;
CREATE TABLE t1 (c30 TIMESTAMP);
INSERT INTO t1 VALUES ('2001-02-03 10:20:30');
DELETE FROM t1 WHERE c30='2001-02-03 10:20:30';
DROP TABLE t1;
CREATE TABLE t1 (c31 TIME);
INSERT INTO t1 VALUES ('11:22:33');
DELETE FROM t1 WHERE c31='11:22:33';
DROP TABLE t1;
CREATE TABLE t1 (c32 YEAR);
INSERT INTO t1 VALUES ('2001');
DELETE FROM t1 WHERE c32=2001;
DROP TABLE t1;
CREATE TABLE t1 (c33 CHAR);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c33='a';
DROP TABLE t1;
CREATE TABLE t1 (c34 CHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c34='';
DROP TABLE t1;
CREATE TABLE t1 (c35 CHAR(1));
INSERT INTO t1 VALUES ('b');
DELETE FROM t1 WHERE c35='b';
DROP TABLE t1;
CREATE TABLE t1 (c36 CHAR(255));
INSERT INTO t1 VALUES (repeat('c',255));
DELETE FROM t1 WHERE c36>'c';
DROP TABLE t1;
CREATE TABLE t1 (c37 NATIONAL CHAR);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c37='a';
DROP TABLE t1;
CREATE TABLE t1 (c38 NATIONAL CHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c38='';
DROP TABLE t1;
CREATE TABLE t1 (c39 NATIONAL CHAR(1));
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c39='a';
DROP TABLE t1;
CREATE TABLE t1 (c40 NATIONAL CHAR(255));
INSERT INTO t1 VALUES (repeat('a', 255));
INSERT INTO t1 VALUES (repeat(_latin1 0xDF, 255));
DELETE FROM t1 WHERE c40>'a';
DROP TABLE t1;
CREATE TABLE t1 (c45 VARCHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c45='';
DROP TABLE t1;
CREATE TABLE t1 (c46 VARCHAR(1));
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c46='a';
DROP TABLE t1;
CREATE TABLE t1 (c47 VARCHAR(255));
INSERT INTO t1 VALUES (repeat('a',255));
DELETE FROM t1 WHERE c47>'a';
DROP TABLE t1;
CREATE TABLE t1 (c48 VARCHAR(261));
INSERT INTO t1 VALUES (repeat('a',261));
DELETE FROM t1 WHERE c48>'a';
DROP TABLE t1;
CREATE TABLE t1 (c49 NATIONAL VARCHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c49='';
DROP TABLE t1;
CREATE TABLE t1 (c50 NATIONAL VARCHAR(1));
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c50='a';
DROP TABLE t1;
CREATE TABLE t1 (c51 NATIONAL VARCHAR(255));
INSERT INTO t1 VALUES (repeat('a',255));
INSERT INTO t1 VALUES (repeat(_latin1 0xDF, 255));
DELETE FROM t1 WHERE c51>'a';
DROP TABLE t1;
CREATE TABLE t1 (c52 NATIONAL VARCHAR(261));
INSERT INTO t1 VALUES (repeat('a',261));
INSERT INTO t1 VALUES (repeat(_latin1 0xDF, 261));
DELETE FROM t1 WHERE c52>'a';
DROP TABLE t1;
CREATE TABLE t1 (c57 BINARY);
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c57='a';
DROP TABLE t1;
CREATE TABLE t1 (c58 BINARY(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c58='';
DROP TABLE t1;
CREATE TABLE t1 (c59 BINARY(1));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c59='a';
DROP TABLE t1;
CREATE TABLE t1 (c60 BINARY(255));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES (repeat('a\0',120));
DELETE FROM t1 WHERE c60<0x02;
DROP TABLE t1;
CREATE TABLE t1 (c61 VARBINARY(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c61='';
DROP TABLE t1;
CREATE TABLE t1 (c62 VARBINARY(1));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c62=0x02;
DROP TABLE t1;
CREATE TABLE t1 (c63 VARBINARY(255));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES (repeat('a\0',120));
DELETE FROM t1 WHERE c63=0x02;
DROP TABLE t1;
flush logs;
CREATE TABLE t1 (c65 TINYBLOB);
INSERT INTO t1 VALUES ('tinyblob1');
DELETE FROM t1 WHERE c65='tinyblob1';
DROP TABLE t1;
CREATE TABLE t1 (c68 BLOB);
INSERT INTO t1 VALUES ('blob1');
DELETE FROM t1 WHERE c68='blob1';
DROP TABLE t1;
CREATE TABLE t1 (c71 MEDIUMBLOB);
INSERT INTO t1 VALUES ('mediumblob1');
DELETE FROM t1 WHERE c71='mediumblob1';
DROP TABLE t1;
CREATE TABLE t1 (c74 LONGBLOB);
INSERT INTO t1 VALUES ('longblob1');
DELETE FROM t1 WHERE c74='longblob1';
DROP TABLE t1;
CREATE TABLE t1 (c66 TINYTEXT);
INSERT INTO t1 VALUES ('tinytext1');
DELETE FROM t1 WHERE c66='tinytext1';
DROP TABLE t1;
CREATE TABLE t1 (c69 TEXT);
INSERT INTO t1 VALUES ('text1');
DELETE FROM t1 WHERE c69='text1';
DROP TABLE t1;
CREATE TABLE t1 (c72 MEDIUMTEXT);
INSERT INTO t1 VALUES ('mediumtext1');
DELETE FROM t1 WHERE c72='mediumtext1';
DROP TABLE t1;
CREATE TABLE t1 (c75 LONGTEXT);
INSERT INTO t1 VALUES ('longtext1');
DELETE FROM t1 WHERE c75='longtext1';
DROP TABLE t1;
CREATE TABLE t1 (c77 ENUM('a','b','c'));
INSERT INTO t1 VALUES ('b');
DELETE FROM t1 WHERE c77='b';
DROP TABLE t1;
CREATE TABLE t1 (c78 SET('a','b','c','d','e','f'));
INSERT INTO t1 VALUES ('a,b');
INSERT INTO t1 VALUES ('a,c');
INSERT INTO t1 VALUES ('b,c');
INSERT INTO t1 VALUES ('a,b,c');
INSERT INTO t1 VALUES ('a,b,c,d');
INSERT INTO t1 VALUES ('a,b,c,d,e');
INSERT INTO t1 VALUES ('a,b,c,d,e,f');
DELETE FROM t1 WHERE c78='a,b';
DROP TABLE t1;
CREATE TABLE t1 (a int NOT NULL DEFAULT 0, b int NOT NULL DEFAULT 0);
CREATE TABLE t2 (a int NOT NULL DEFAULT 0, b int NOT NULL DEFAULT 0);
INSERT INTO t1 SET a=1;
INSERT INTO t1 SET b=1;
INSERT INTO t2 SET a=1;
INSERT INTO t2 SET b=1;
UPDATE t1, t2 SET t1.a=10, t2.a=20;
DROP TABLE t1,t2;
flush logs;
End of tests
--source include/have_log_bin.inc
reset master;
# we need this for getting fixed timestamps inside of this test
set timestamp=1000000000;
--disable_warnings
drop table if exists t1;
--enable_warnings
CREATE TABLE t1 (c01 BIT);
INSERT INTO t1 VALUES (0);
INSERT INTO t1 VALUES (1);
DROP TABLE t1;
CREATE TABLE t1 (c01 BIT(7));
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
INSERT INTO t1 VALUES (4);
INSERT INTO t1 VALUES (8);
INSERT INTO t1 VALUES (16);
INSERT INTO t1 VALUES (32);
INSERT INTO t1 VALUES (64);
INSERT INTO t1 VALUES (127);
DELETE FROM t1 WHERE c01=127;
UPDATE t1 SET c01=15 WHERE c01=16;
DROP TABLE t1;
CREATE TABLE t1 (a BIT(20), b CHAR(2));
INSERT INTO t1 VALUES (b'00010010010010001001', 'ab');
DROP TABLE t1;
CREATE TABLE t1 (c02 BIT(64));
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
INSERT INTO t1 VALUES (128);
INSERT INTO t1 VALUES (b'1111111111111111111111111111111111111111111111111111111111111111');
DROP TABLE t1;
CREATE TABLE t1 (c03 TINYINT);
INSERT INTO t1 VALUES (1),(2),(3);
INSERT INTO t1 VALUES (-128);
UPDATE t1 SET c03=2 WHERE c03=1;
DELETE FROM t1 WHERE c03=-128;
DROP TABLE t1;
CREATE TABLE t1 (c04 TINYINT UNSIGNED);
INSERT INTO t1 VALUES (128), (255);
UPDATE t1 SET c04=2 WHERE c04=1;
DELETE FROM t1 WHERE c04=255;
DROP TABLE t1;
CREATE TABLE t1 (c06 BOOL);
INSERT INTO t1 VALUES (TRUE);
DELETE FROM t1 WHERE c06=TRUE;
DROP TABLE t1;
CREATE TABLE t1 (c07 SMALLINT);
INSERT INTO t1 VALUES (1234);
DELETE FROM t1 WHERE c07=1234;
DROP TABLE t1;
CREATE TABLE t1 (c08 SMALLINT UNSIGNED);
INSERT INTO t1 VALUES (32768), (65535);
UPDATE t1 SET c08=2 WHERE c08=32768;
DELETE FROM t1 WHERE c08=65535;
DROP TABLE t1;
CREATE TABLE t1 (c10 MEDIUMINT);
INSERT INTO t1 VALUES (12345);
DELETE FROM t1 WHERE c10=12345;
DROP TABLE t1;
CREATE TABLE t1 (c11 MEDIUMINT UNSIGNED);
INSERT INTO t1 VALUES (8388608), (16777215);
UPDATE t1 SET c11=2 WHERE c11=8388608;
DELETE FROM t1 WHERE c11=16777215;
DROP TABLE t1;
CREATE TABLE t1 (c13 INT);
INSERT INTO t1 VALUES (123456);
DELETE FROM t1 WHERE c13=123456;
DROP TABLE t1;
CREATE TABLE t1 (c14 INT UNSIGNED);
INSERT INTO t1 VALUES (2147483648), (4294967295);
UPDATE t1 SET c14=2 WHERE c14=2147483648;
DELETE FROM t1 WHERE c14=4294967295;
DROP TABLE t1;
CREATE TABLE t1 (c16 BIGINT);
INSERT INTO t1 VALUES (1234567890);
DELETE FROM t1 WHERE c16=1234567890;
DROP TABLE t1;
CREATE TABLE t1 (c17 BIGINT UNSIGNED);
INSERT INTO t1 VALUES (9223372036854775808), (18446744073709551615);
UPDATE t1 SET c17=2 WHERE c17=9223372036854775808;
DELETE FROM t1 WHERE c17=18446744073709551615;
DROP TABLE t1;
CREATE TABLE t1 (c19 FLOAT);
INSERT INTO t1 VALUES (123.2234);
DELETE FROM t1 WHERE c19>123;
DROP TABLE t1;
CREATE TABLE t1 (c22 DOUBLE);
INSERT INTO t1 VALUES (123434.22344545);
DELETE FROM t1 WHERE c22>123434;
DROP TABLE t1;
#
CREATE TABLE t1 (c25 DECIMAL(10,5));
INSERT INTO t1 VALUES (124.45);
INSERT INTO t1 VALUES (-543.21);
DELETE FROM t1 WHERE c25=124.45;
DROP TABLE t1;
#
CREATE TABLE t1 (c28 DATE);
INSERT INTO t1 VALUES ('2001-02-03');
DELETE FROM t1 WHERE c28='2001-02-03';
DROP TABLE t1;
CREATE TABLE t1 (c29 DATETIME);
INSERT INTO t1 VALUES ('2001-02-03 10:20:30');
DELETE FROM t1 WHERE c29='2001-02-03 10:20:30';
DROP TABLE t1;
CREATE TABLE t1 (c30 TIMESTAMP);
INSERT INTO t1 VALUES ('2001-02-03 10:20:30');
DELETE FROM t1 WHERE c30='2001-02-03 10:20:30';
DROP TABLE t1;
CREATE TABLE t1 (c31 TIME);
INSERT INTO t1 VALUES ('11:22:33');
DELETE FROM t1 WHERE c31='11:22:33';
DROP TABLE t1;
CREATE TABLE t1 (c32 YEAR);
INSERT INTO t1 VALUES ('2001');
DELETE FROM t1 WHERE c32=2001;
DROP TABLE t1;
#
CREATE TABLE t1 (c33 CHAR);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c33='a';
DROP TABLE t1;
CREATE TABLE t1 (c34 CHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c34='';
DROP TABLE t1;
CREATE TABLE t1 (c35 CHAR(1));
INSERT INTO t1 VALUES ('b');
DELETE FROM t1 WHERE c35='b';
DROP TABLE t1;
CREATE TABLE t1 (c36 CHAR(255));
INSERT INTO t1 VALUES (repeat('c',255));
DELETE FROM t1 WHERE c36>'c';
DROP TABLE t1;
#
CREATE TABLE t1 (c37 NATIONAL CHAR);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c37='a';
DROP TABLE t1;
CREATE TABLE t1 (c38 NATIONAL CHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c38='';
DROP TABLE t1;
CREATE TABLE t1 (c39 NATIONAL CHAR(1));
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c39='a';
DROP TABLE t1;
CREATE TABLE t1 (c40 NATIONAL CHAR(255));
INSERT INTO t1 VALUES (repeat('a', 255));
INSERT INTO t1 VALUES (repeat(_latin1 0xDF, 255));
DELETE FROM t1 WHERE c40>'a';
DROP TABLE t1;
#
CREATE TABLE t1 (c45 VARCHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c45='';
DROP TABLE t1;
CREATE TABLE t1 (c46 VARCHAR(1));
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c46='a';
DROP TABLE t1;
CREATE TABLE t1 (c47 VARCHAR(255));
INSERT INTO t1 VALUES (repeat('a',255));
DELETE FROM t1 WHERE c47>'a';
DROP TABLE t1;
CREATE TABLE t1 (c48 VARCHAR(261));
INSERT INTO t1 VALUES (repeat('a',261));
DELETE FROM t1 WHERE c48>'a';
DROP TABLE t1;
#
CREATE TABLE t1 (c49 NATIONAL VARCHAR(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c49='';
DROP TABLE t1;
CREATE TABLE t1 (c50 NATIONAL VARCHAR(1));
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c50='a';
DROP TABLE t1;
CREATE TABLE t1 (c51 NATIONAL VARCHAR(255));
INSERT INTO t1 VALUES (repeat('a',255));
INSERT INTO t1 VALUES (repeat(_latin1 0xDF, 255));
DELETE FROM t1 WHERE c51>'a';
DROP TABLE t1;
CREATE TABLE t1 (c52 NATIONAL VARCHAR(261));
INSERT INTO t1 VALUES (repeat('a',261));
INSERT INTO t1 VALUES (repeat(_latin1 0xDF, 261));
DELETE FROM t1 WHERE c52>'a';
DROP TABLE t1;
#
CREATE TABLE t1 (c57 BINARY);
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c57='a';
DROP TABLE t1;
CREATE TABLE t1 (c58 BINARY(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c58='';
DROP TABLE t1;
CREATE TABLE t1 (c59 BINARY(1));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c59='a';
DROP TABLE t1;
CREATE TABLE t1 (c60 BINARY(255));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES (repeat('a\0',120));
DELETE FROM t1 WHERE c60<0x02;
DROP TABLE t1;
#
CREATE TABLE t1 (c61 VARBINARY(0));
INSERT INTO t1 VALUES ('');
DELETE FROM t1 WHERE c61='';
DROP TABLE t1;
CREATE TABLE t1 (c62 VARBINARY(1));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES ('a');
DELETE FROM t1 WHERE c62=0x02;
DROP TABLE t1;
CREATE TABLE t1 (c63 VARBINARY(255));
INSERT INTO t1 VALUES (0x00);
INSERT INTO t1 VALUES (0x02);
INSERT INTO t1 VALUES (repeat('a\0',120));
DELETE FROM t1 WHERE c63=0x02;
DROP TABLE t1;
#
flush logs;
CREATE TABLE t1 (c65 TINYBLOB);
INSERT INTO t1 VALUES ('tinyblob1');
DELETE FROM t1 WHERE c65='tinyblob1';
DROP TABLE t1;
CREATE TABLE t1 (c68 BLOB);
INSERT INTO t1 VALUES ('blob1');
DELETE FROM t1 WHERE c68='blob1';
DROP TABLE t1;
CREATE TABLE t1 (c71 MEDIUMBLOB);
INSERT INTO t1 VALUES ('mediumblob1');
DELETE FROM t1 WHERE c71='mediumblob1';
DROP TABLE t1;
CREATE TABLE t1 (c74 LONGBLOB);
INSERT INTO t1 VALUES ('longblob1');
DELETE FROM t1 WHERE c74='longblob1';
DROP TABLE t1;
CREATE TABLE t1 (c66 TINYTEXT);
INSERT INTO t1 VALUES ('tinytext1');
DELETE FROM t1 WHERE c66='tinytext1';
DROP TABLE t1;
CREATE TABLE t1 (c69 TEXT);
INSERT INTO t1 VALUES ('text1');
DELETE FROM t1 WHERE c69='text1';
DROP TABLE t1;
CREATE TABLE t1 (c72 MEDIUMTEXT);
INSERT INTO t1 VALUES ('mediumtext1');
DELETE FROM t1 WHERE c72='mediumtext1';
DROP TABLE t1;
CREATE TABLE t1 (c75 LONGTEXT);
INSERT INTO t1 VALUES ('longtext1');
DELETE FROM t1 WHERE c75='longtext1';
DROP TABLE t1;
#
CREATE TABLE t1 (c77 ENUM('a','b','c'));
INSERT INTO t1 VALUES ('b');
DELETE FROM t1 WHERE c77='b';
DROP TABLE t1;
#
CREATE TABLE t1 (c78 SET('a','b','c','d','e','f'));
INSERT INTO t1 VALUES ('a,b');
INSERT INTO t1 VALUES ('a,c');
INSERT INTO t1 VALUES ('b,c');
INSERT INTO t1 VALUES ('a,b,c');
INSERT INTO t1 VALUES ('a,b,c,d');
INSERT INTO t1 VALUES ('a,b,c,d,e');
INSERT INTO t1 VALUES ('a,b,c,d,e,f');
DELETE FROM t1 WHERE c78='a,b';
DROP TABLE t1;
#
# Check multi-table update
#
CREATE TABLE t1 (a int NOT NULL DEFAULT 0, b int NOT NULL DEFAULT 0);
CREATE TABLE t2 (a int NOT NULL DEFAULT 0, b int NOT NULL DEFAULT 0);
INSERT INTO t1 SET a=1;
INSERT INTO t1 SET b=1;
INSERT INTO t2 SET a=1;
INSERT INTO t2 SET b=1;
UPDATE t1, t2 SET t1.a=10, t2.a=20;
DROP TABLE t1,t2;
flush logs;
let $MYSQLD_DATADIR= `select @@datadir`;
# Test reading one file in raw mode
--exec $MYSQL_BINLOG --raw --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT --result-file=$MYSQLTEST_VARDIR/tmp/ master-bin.000001
--diff_files $MYSQLTEST_VARDIR/tmp/master-bin.000001 $MYSQLD_DATADIR/master-bin.000001
--remove_file $MYSQLTEST_VARDIR/tmp/master-bin.000001
# Test reading all files in raw mode
# Don't test the end file since this is still open with mysqld so will be different
--exec $MYSQL_BINLOG --raw --read-from-remote-server --to-last-log --user=root --host=127.0.0.1 --port=$MASTER_MYPORT --result-file=$MYSQLTEST_VARDIR/tmp/ master-bin.000001
--diff_files $MYSQLTEST_VARDIR/tmp/master-bin.000001 $MYSQLD_DATADIR/master-bin.000001
--diff_files $MYSQLTEST_VARDIR/tmp/master-bin.000002 $MYSQLD_DATADIR/master-bin.000002
--remove_file $MYSQLTEST_VARDIR/tmp/master-bin.000001
--remove_file $MYSQLTEST_VARDIR/tmp/master-bin.000002
--remove_file $MYSQLTEST_VARDIR/tmp/master-bin.000003
# Test output to different filename
--exec $MYSQL_BINLOG --raw --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT --result-file=$MYSQLTEST_VARDIR/tmp/server1- master-bin.000001
--diff_files $MYSQLTEST_VARDIR/tmp/server1-master-bin.000001 $MYSQLD_DATADIR/master-bin.000001
--remove_file $MYSQLTEST_VARDIR/tmp/server1-master-bin.000001
--echo End of tests
......@@ -320,11 +320,11 @@
/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
#define UNDEF_POS (-1)
#endif /* !MYSQL_CLIENT */
/* BINLOG_DUMP options */
#define BINLOG_DUMP_NON_BLOCK 1
#endif /* !MYSQL_CLIENT */
#define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
#ifndef MYSQL_CLIENT
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment