diff -ur mysql-5.1.31/client/client_priv.h mysql-5.1.31-binlog/client/client_priv.h --- mysql-5.1.31/client/client_priv.h 2009-02-11 00:23:17.000000000 +0000 +++ mysql-5.1.31-binlog/client/client_priv.h 2009-02-22 15:48:24.000000000 +0000 @@ -80,5 +80,5 @@ OPT_FIX_TABLE_NAMES, OPT_FIX_DB_NAMES, OPT_SSL_VERIFY_SERVER_CERT, OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE, OPT_WRITE_BINLOG, OPT_DUMP_DATE, - OPT_MAX_CLIENT_OPTION + OPT_MAX_CLIENT_OPTION, OPT_RAW_OUTPUT, OPT_OUTPUT_DIR }; diff -ur mysql-5.1.31/client/mysqlbinlog.cc mysql-5.1.31-binlog/client/mysqlbinlog.cc --- mysql-5.1.31/client/mysqlbinlog.cc 2009-02-11 00:23:17.000000000 +0000 +++ mysql-5.1.31-binlog/client/mysqlbinlog.cc 2009-03-12 23:31:04.000000000 +0000 @@ -36,6 +36,8 @@ #include "mysql_priv.h" #include "log_event.h" #include "sql_common.h" +#include +#include #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) @@ -53,6 +55,8 @@ uint test_flags = 0; static uint opt_protocol= 0; static FILE *result_file; +MY_TMPDIR tmpdir; +char **defaults_argv; #ifndef DBUG_OFF static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace"; @@ -62,7 +66,7 @@ static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); -static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; +static bool one_database=0, disable_log_bin= 0; static bool opt_hexdump= 0; const char *base64_output_mode_names[]= {"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS}; @@ -72,9 +76,12 @@ static enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC; static const char *opt_base64_output_mode_str= NullS; static const char* database= 0; +static const char* output_file= 0; +static const char* output_dir= 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 force_if_open_opt= 1, raw_mode= 0; +static uint to_last_remote_log= 0; static ulonglong offset = 0; static const char* host = 0; static int port= 0; @@ -123,7 +130,9 @@ const char* logname); static Exit_status dump_log_entries(const char* logname); static Exit_status safe_connect(); - +static void force_quit(int param); +static void quit(Exit_status retval); +static void init_signals(void); class Load_log_processor { @@ -1010,7 +1019,12 @@ {"read-from-remote-server", 'R', "Read binary logs from a MySQL server", (uchar**) &remote_opt, (uchar**) &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, + {"raw", OPT_RAW_OUTPUT, "Requires -R. Output raw binlog data instead of SQL statements, output is to log files.", + (uchar**) &raw_mode, (uchar**) &raw_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, + {"result-dir", OPT_OUTPUT_DIR, "Requires --raw. Direct output to a given dir.", (uchar**) &output_dir, (uchar**) &output_dir, 0, GET_STR, + REQUIRED_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.", (uchar**) &output_file, (uchar**) &output_file, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-id", OPT_SERVER_ID, "Extract only binlog entries created by the server having the given id.", @@ -1059,10 +1073,11 @@ (ulonglong)(~(my_off_t)0), 0, 0, 0}, {"to-last-log", 't', "Requires -R. Will not stop at the end of the \ requested binlog but rather continue printing until the end of the last \ -binlog of the MySQL server. If you send the output to the same MySQL server, \ +binlog of the MySQL server if set to 1, or wait for more data if set to 2. \ +If you send the output to the same MySQL server, \ that may lead to an endless loop.", - (uchar**) &to_last_remote_log, (uchar**) &to_last_remote_log, 0, GET_BOOL, - NO_ARG, 0, 0, 0, 0, 0, 0}, + (uchar**) &to_last_remote_log, (uchar**) &to_last_remote_log, 0, GET_UINT, + REQUIRED_ARG, 0, 0, 2, 0, 0, 0}, {"user", 'u', "Connect to the remote server as username.", (uchar**) &user, (uchar**) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -1238,10 +1253,6 @@ 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; @@ -1353,17 +1364,24 @@ Set safe delimiter, to dump things like CREATE PROCEDURE safely */ - fprintf(result_file, "DELIMITER /*!*/;\n"); + if (!raw_mode) + { + fprintf(result_file, "DELIMITER /*!*/;\n"); + } strmov(print_event_info.delimiter, "/*!*/;"); print_event_info.verbose= short_form ? 0 : verbose; + rc= (remote_opt ? dump_remote_log_entries(&print_event_info, logname) : dump_local_log_entries(&print_event_info, logname)); /* Set delimiter back to semicolon */ - fprintf(result_file, "DELIMITER ;\n"); - strmov(print_event_info.delimiter, ";"); + if (!raw_mode) + { + fprintf(result_file, "DELIMITER ;\n"); + strmov(print_event_info.delimiter, ";"); + } return rc; } @@ -1463,9 +1481,11 @@ uchar buf[128]; ulong len; uint logname_len; + uint server_id; NET* net; my_off_t old_off= start_position_mot; char fname[FN_REFLEN+1]; + char log_file_name[FN_REFLEN+1]; Exit_status retval= OK_CONTINUE; DBUG_ENTER("dump_remote_log_entries"); @@ -1495,7 +1515,17 @@ DBUG_RETURN(ERROR_STOP); } logname_len = (uint) tlen; - int4store(buf + 6, 0); + if (to_last_remote_log == 2) + { + // Fake a server ID to log continously. This will show as a slave on the mysql server. + server_id= 255; + } + else + { + server_id= 0; + } + + int4store(buf + 6, server_id); memcpy(buf + 10, logname, logname_len); if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1)) { @@ -1516,6 +1546,7 @@ } 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 (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 , @@ -1555,6 +1586,36 @@ part of our log) and then we will stop when we receive the fake one soon. */ + if (result_file != stdout) + my_fclose(result_file, MYF(0)); + if (raw_mode) + { + if (output_dir != 0) + { + strcpy(log_file_name, output_dir); + #ifdef __WIN__ + strcat(log_file_name, "\\"); + #else + strcat(log_file_name, "/"); + #endif + strcat(log_file_name, rev->new_log_ident); + } + else if (output_file != 0) + { + strcpy(log_file_name, output_file); + strcat(log_file_name, rev->new_log_ident); + } + else + { + strcpy(log_file_name, rev->new_log_ident); + } + if (!(result_file = my_fopen(log_file_name, O_WRONLY | O_BINARY, MYF(MY_WME)))) + { + error("Could not create log file '%s'", log_file_name); + DBUG_RETURN(ERROR_STOP); + } + } + if (rev->when == 0) { if (!to_last_remote_log) @@ -1584,10 +1645,37 @@ don't increment old_off. Real Format_description_log_event always starts from BIN_LOG_HEADER_SIZE position. */ - if (old_off != BIN_LOG_HEADER_SIZE) - len= 1; // fake event, don't increment old_off + if ((old_off != BIN_LOG_HEADER_SIZE) && (!raw_mode)) + len= 1; // fake event when not in raw mode, don't increment old_off + + if (raw_mode) + { + fprintf(result_file,"%s", BINLOG_MAGIC); + // Need to handle these events correctly in raw mode too or this could get messy + 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; + ev= 0; + } + } + + //printf("Old off %d, len %d, type %s \n", old_off, len - 1, ev->get_type_str()); + + if (raw_mode) + { + fwrite(net->read_pos + 1 , 1, len - 1, result_file); + if (ev) + { + ev->temp_buf=0; + delete ev; + } + } + else + { + Exit_status retval= process_event(print_event_info, ev, old_off, logname); } - Exit_status retval= process_event(print_event_info, ev, old_off, logname); + if (retval != OK_CONTINUE) DBUG_RETURN(retval); } @@ -1623,7 +1711,6 @@ DBUG_RETURN(OK_CONTINUE); } - /** Reads the @c Format_description_log_event from the beginning of a local input file. @@ -1933,18 +2020,68 @@ int main(int argc, char** argv) { - char **defaults_argv; Exit_status retval= OK_CONTINUE; ulonglong save_stop_position; MY_INIT(argv[0]); DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); + MY_STAT stat_info; + int err; + + init_signals(); my_init_time(); // for time functions parse_args(&argc, (char***)&argv); defaults_argv=argv; + /* + You should not set output_file and output_dir at the same time! + */ + + if ((output_file != 0) && (output_dir != 0)) + { + error("You cannot use --result-dir and --result-file simultaneously"); + exit(1); + } + + /* + You should not set output_dir and not turn on --raw mode + */ + + if ((output_dir != 0) && (raw_mode == 0)) + { + error("You cannot use --result-dir without --raw"); + exit(1); + } + + if ((output_dir != 0 ) && + (!my_stat(output_dir, &stat_info,MYF(0)))) + { + // Directory doesn't exist, lets try and make it + + err = mkdir(output_dir, 0770); + if (err != 0) + { + error("Directory '%s' doesn't exist and could not be created, err %d", output_dir, err); + exit(1); + } + } + + /* + Make this last of the post-option tests so we don't exit with a + file open + */ + + if ((output_file != 0) && (!raw_mode)) + { + if (!(result_file = my_fopen(output_file, O_WRONLY | O_BINARY, MYF(MY_WME)))) + { + error("Could not create log file '%s'", output_file); + exit(1); + } + } + if (!argc) { usage(); @@ -1957,7 +2094,6 @@ my_set_max_open_files(open_files_limit); - MY_TMPDIR tmpdir; tmpdir.list= 0; if (!dirname_for_local_load) { @@ -1973,27 +2109,30 @@ else load_processor.init_by_cur_dir(); - fprintf(result_file, - "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n"); - - if (disable_log_bin) + if (!raw_mode) + { fprintf(result_file, - "/*!32316 SET @OLD_SQL_LOG_BIN=@@SQL_LOG_BIN, SQL_LOG_BIN=0*/;\n"); + "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n"); - /* - In mysqlbinlog|mysql, don't want mysql to be disconnected after each - transaction (which would be the case with GLOBAL.COMPLETION_TYPE==2). - */ - fprintf(result_file, - "/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE," - "COMPLETION_TYPE=0*/;\n"); + if (disable_log_bin) + fprintf(result_file, + "/*!32316 SET @OLD_SQL_LOG_BIN=@@SQL_LOG_BIN, SQL_LOG_BIN=0*/;\n"); - if (charset) + /* + In mysqlbinlog|mysql, don't want mysql to be disconnected after each + transaction (which would be the case with GLOBAL.COMPLETION_TYPE==2). + */ fprintf(result_file, + "/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE," + "COMPLETION_TYPE=0*/;\n"); + + if (charset) + fprintf(result_file, "\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" "\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) ; ) @@ -2007,28 +2146,48 @@ start_position= BIN_LOG_HEADER_SIZE; } - /* - Issue a ROLLBACK in case the last printed binlog was crashed and had half - of transaction. - */ - fprintf(result_file, - "# End of log file\nROLLBACK /* added by mysqlbinlog */;\n" - "/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;\n"); - if (disable_log_bin) - fprintf(result_file, "/*!32316 SET SQL_LOG_BIN=@OLD_SQL_LOG_BIN*/;\n"); - - if (charset) + if (!raw_mode) + { + /* + Issue a ROLLBACK in case the last printed binlog was crashed and had half + of transaction. + */ fprintf(result_file, + "# End of log file\nROLLBACK /* added by mysqlbinlog */;\n" + "/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;\n"); + if (disable_log_bin) + fprintf(result_file, "/*!32316 SET SQL_LOG_BIN=@OLD_SQL_LOG_BIN*/;\n"); + + if (charset) + fprintf(result_file, "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n" "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n" "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); + } + quit(retval); +} +static void init_signals(void) +{ + int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT}; + + for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++) + signal(signals[i], force_quit); +} + +static void force_quit(int param) +{ + quit(OK_CONTINUE); +} + +static void quit(Exit_status retval) +{ if (tmpdir.list) free_tmpdir(&tmpdir); if (result_file != stdout) my_fclose(result_file, MYF(0)); cleanup(); - free_defaults(defaults_argv); +// free_defaults(defaults_argv); my_free_open_file_info(); load_processor.destroy(); /* We cannot free DBUG, it is used in global destructors after exit(). */ @@ -2036,7 +2195,7 @@ exit(retval == ERROR_STOP ? 1 : 0); /* Keep compilers happy. */ - DBUG_RETURN(retval == ERROR_STOP ? 1 : 0); + //DBUG_RETURN(retval == ERROR_STOP ? 1 : 0); } /*