diff options
Diffstat (limited to 'client/mysqlbinlog.cc')
-rw-r--r-- | client/mysqlbinlog.cc | 561 |
1 files changed, 359 insertions, 202 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 35f0db76ad6..9e30b1ae71e 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2001-2003 MySQL AB +/* Copyright (C) 2001-2004 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -35,7 +35,6 @@ ulong bytes_sent = 0L, bytes_received = 0L; ulong mysqld_net_retry_count = 10L; uint test_flags = 0; static uint opt_protocol= 0; - static FILE *result_file; #ifndef DBUG_OFF @@ -59,28 +58,47 @@ static short binlog_flags = 0; static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 0; -static void dump_local_log_entries(const char* logname); -static void dump_remote_log_entries(const char* logname); -static void dump_log_entries(const char* logname); -static void dump_remote_file(NET* net, const char* fname); +static int dump_local_log_entries(const char* logname); +static int dump_remote_log_entries(const char* logname); +static int dump_log_entries(const char* logname); +static int dump_remote_file(NET* net, const char* fname); static void die(const char* fmt, ...); static MYSQL* safe_connect(); + class Load_log_processor { char target_dir_name[MY_NFILE]; int target_dir_name_len; DYNAMIC_ARRAY file_names; - const char *create_file(Create_file_log_event *ce); - void append_to_file(const char* fname, int flags, - gptr data, uint size) + /* + Looking for new uniquie filename that doesn't exist yet by + adding postfix -%x + + SYNOPSIS + create_unique_file() + + filename buffer for filename + file_name_end tail of buffer that should be changed + should point to a memory enough to printf("-%x",..) + + RETURN VALUES + values less than 0 - can't find new filename + values great or equal 0 - created file with found filename + */ + File create_unique_file(char *filename, char *file_name_end) { - File file; - if (((file= my_open(fname,flags,MYF(MY_WME))) < 0) || - my_write(file,(byte*) data,size,MYF(MY_WME|MY_NABP)) || - my_close(file,MYF(MY_WME))) - exit(1); + File res; + /* If we have to try more than 1000 times, something is seriously wrong */ + for (uint version= 0; version<1000; version++) + { + sprintf(file_name_end,"-%x",version); + if ((res= my_create(filename,0, + O_CREAT|O_EXCL|O_BINARY|O_WRONLY,MYF(0)))!=-1) + return res; + } + return -1; } public: @@ -131,83 +149,252 @@ public: *ptr= 0; return res; } - void process(Create_file_log_event *ce) - { - const char *fname= create_file(ce); - append_to_file(fname,O_CREAT|O_EXCL|O_BINARY|O_WRONLY,ce->block, - ce->block_len); - } - void process(Append_block_log_event *ae) + int process(Create_file_log_event *ce); + int process(Append_block_log_event *ae); + File prepare_new_file_for_old_format(Load_log_event *le, char *filename); + int load_old_format_file(NET* net, const char *server_fname, + uint server_fname_len, File file); +}; + + + +File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le, + char *filename) +{ + uint len; + char *tail; + File file; + + fn_format(filename, le->fname, target_dir_name, "", 1); + len= strlen(filename); + tail= filename + len; + + if ((file= create_unique_file(filename,tail)) < 0) + { + sql_print_error("Could not construct local filename %s",filename); + return -1; + } + + le->set_fname_outside_temp_buf(filename,len+strlen(tail)); + + return file; +} + + +int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, + uint server_fname_len, File file) +{ + char buf[FN_REFLEN+1]; + buf[0] = 0; + memcpy(buf + 1, server_fname, server_fname_len + 1); + if (my_net_write(net, buf, server_fname_len +2) || net_flush(net)) + { + sql_print_error("Failed requesting the remote dump of %s", server_fname); + return -1; + } + + for (;;) + { + uint packet_len = my_net_read(net); + if (packet_len == 0) { - Create_file_log_event* ce= (ae->file_id < file_names.elements) ? - *((Create_file_log_event**)file_names.buffer + ae->file_id) : 0; - - if (ce) - append_to_file(ce->fname,O_APPEND|O_BINARY|O_WRONLY, ae->block, - ae->block_len); - else + if (my_net_write(net, "", 0) || net_flush(net)) { - /* - There is no Create_file event (a bad binlog or a big - --position). Assuming it's a big --position, we just do nothing and - print a warning. - */ - fprintf(stderr,"Warning: ignoring Append_block as there is no \ -Create_file event for file_id: %u\n",ae->file_id); + sql_print_error("Failed sending the ack packet"); + return -1; } + /* + we just need to send something, as the server will read but + not examine the packet - this is because mysql_load() sends + an OK when it is done + */ + break; } -}; + else if (packet_len == packet_error) + { + sql_print_error("Failed reading a packet during the dump of %s ", + server_fname); + return -1; + } + + if (my_write(file, (byte*) net->read_pos, packet_len,MYF(MY_WME|MY_NABP))) + return -1; + } + + return 0; +} -const char *Load_log_processor::create_file(Create_file_log_event *ce) +int Load_log_processor::process(Create_file_log_event *ce) { const char *bname= ce->fname+dirname_length(ce->fname); uint blen= ce->fname_len - (bname-ce->fname); uint full_len= target_dir_name_len + blen + 9 + 9 + 1; - uint version= 0; - char *tmp, *ptr; + int error= 0; + char *fname, *ptr; + File file; - if (!(tmp= my_malloc(full_len,MYF(MY_WME))) || - set_dynamic(&file_names,(gptr)&ce,ce->file_id)) + if (set_dynamic(&file_names,(gptr)&ce,ce->file_id)) { - die("Could not construct local filename %s%s",target_dir_name,bname); - return 0; + sql_print_error("Could not construct local filename %s%s", + target_dir_name,bname); + return -1; } + if (!(fname= my_malloc(full_len,MYF(MY_WME)))) + return -1; - memcpy(tmp, target_dir_name, target_dir_name_len); - ptr= tmp+ target_dir_name_len; + memcpy(fname, target_dir_name, target_dir_name_len); + ptr= fname + target_dir_name_len; memcpy(ptr,bname,blen); ptr+= blen; ptr+= my_sprintf(ptr,(ptr,"-%x",ce->file_id)); - /* - Note that this code has a possible race condition if there was was - many simultaneous clients running which tried to create files at the same - time. Fortunately this should never be the case. + if ((file= create_unique_file(fname,ptr)) < 0) + { + sql_print_error("Could not construct local filename %s%s", + target_dir_name,bname); + return -1; + } + ce->set_fname_outside_temp_buf(fname,strlen(fname)); + + if (my_write(file,(byte*) ce->block,ce->block_len,MYF(MY_WME|MY_NABP))) + error= -1; + if (my_close(file,MYF(MY_WME))) + error= -1; + return error; +} + + +int Load_log_processor::process(Append_block_log_event *ae) +{ + Create_file_log_event* ce= ((ae->file_id < file_names.elements) ? + *((Create_file_log_event**)file_names.buffer + + ae->file_id) : + 0); - A better way to do this would be to use 'create_tmp_file() and avoid this - race condition altogether on the expense of getting more cryptic file - names. + if (ce) + { + File file; + int error= 0; + if (((file= my_open(ce->fname, + O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0)) + return -1; + if (my_write(file,(byte*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP))) + error= -1; + if (my_close(file,MYF(MY_WME))) + error= -1; + return error; + } + + /* + There is no Create_file event (a bad binlog or a big + --position). Assuming it's a big --position, we just do nothing and + print a warning. */ - for (;;) + fprintf(stderr,"Warning: ignoring Append_block as there is no \ +Create_file event for file_id: %u\n",ae->file_id); + return -1; +} + + +Load_log_processor load_processor; + + +int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, + my_off_t pos, int old_format) +{ + char ll_buff[21]; + if ((*rec_count) >= offset) { - sprintf(ptr,"-%x",version); - if (access(tmp,F_OK)) + if (!short_form) + fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); + + switch (ev->get_type_code()) { + case QUERY_EVENT: + if (one_database) + { + const char * log_dbname = ((Query_log_event*)ev)->db; + if ((log_dbname != NULL) && (strcmp(log_dbname, database))) + { + (*rec_count)++; + delete ev; + return 0; // Time for next event + } + } + ev->print(result_file, short_form, last_db); break; - /* If we have to try more than 1000 times, something is seriously wrong */ - if (version++ > 1000) + case CREATE_FILE_EVENT: { - die("Could not construct local filename %s%s",target_dir_name,bname); - return 0; + Create_file_log_event* ce= (Create_file_log_event*)ev; + if (one_database) + { + /* + We test if this event has to be ignored. If yes, we don't save + this event; this will have the good side-effect of ignoring all + related Append_block and Exec_load. + Note that Load event from 3.23 is not tested. + */ + const char * log_dbname = ce->db; + if ((log_dbname != NULL) && (strcmp(log_dbname, database))) + { + (*rec_count)++; + delete ev; + return 0; // next + } + } + /* + We print the event, but with a leading '#': this is just to inform + the user of the original command; the command we want to execute + will be a derivation of this original command (we will change the + filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' + below. + */ + ce->print(result_file, short_form, last_db, true); + if (!old_format) + { + if (load_processor.process(ce)) + break; // Error + ev= 0; + } + break; + } + case APPEND_BLOCK_EVENT: + ev->print(result_file, short_form, last_db); + if (load_processor.process((Append_block_log_event*) ev)) + break; // Error + break; + case EXEC_LOAD_EVENT: + { + ev->print(result_file, short_form, last_db); + Execute_load_log_event *exv= (Execute_load_log_event*)ev; + Create_file_log_event *ce= load_processor.grab_event(exv->file_id); + /* + if ce is 0, it probably means that we have not seen the Create_file + event (a bad binlog, or most probably --position is after the + Create_file event). Print a warning comment. + */ + if (ce) + { + ce->print(result_file, short_form, last_db,true); + my_free((char*)ce->fname,MYF(MY_WME)); + delete ce; + } + else + fprintf(stderr,"Warning: ignoring Exec_load as there is no \ +Create_file event for file_id: %u\n",exv->file_id); + break; + } + default: + ev->print(result_file, short_form, last_db); } } - ce->set_fname_outside_temp_buf(tmp,strlen(tmp)); - return tmp; + (*rec_count)++; + if (ev) + delete ev; + return 0; } -Load_log_processor load_processor; - static struct my_option my_long_options[] = { #ifndef DBUG_OFF @@ -247,7 +434,7 @@ static struct my_option my_long_options[] = (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", - (gptr*) &sock, (gptr*) &sock, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, + (gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"user", 'u', "Connect to the remote server as username.", (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, @@ -290,7 +477,7 @@ static void die(const char* fmt, ...) static void print_version() { - printf("%s Ver 2.5 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); + printf("%s Ver 3.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); } @@ -309,37 +496,6 @@ the mysql command line client\n\n"); my_print_variables(my_long_options); } -static void dump_remote_file(NET* net, const char* fname) -{ - char buf[FN_REFLEN+1]; - uint len = (uint) strlen(fname); - buf[0] = 0; - memcpy(buf + 1, fname, len + 1); - if(my_net_write(net, buf, len +2) || net_flush(net)) - die("Failed requesting the remote dump of %s", fname); - for(;;) - { - uint packet_len = my_net_read(net); - if(packet_len == 0) - { - if(my_net_write(net, "", 0) || net_flush(net)) - die("Failed sending the ack packet"); - - // we just need to send something, as the server will read but - // not examine the packet - this is because mysql_load() sends an OK when it is done - break; - } - else if(packet_len == packet_error) - die("Failed reading a packet during the dump of %s ", fname); - - if(!short_form) - (void)my_fwrite(result_file, (byte*) net->read_pos, packet_len,MYF(0)); - } - - fflush(result_file); -} - - extern "C" my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), char *argument) @@ -425,14 +581,15 @@ static MYSQL* safe_connect() return local_mysql; } -static void dump_log_entries(const char* logname) + +static int dump_log_entries(const char* logname) { if (remote_opt) - dump_remote_log_entries(logname); - else - dump_local_log_entries(logname); + return dump_remote_log_entries(logname); + return dump_local_log_entries(logname); } + static int check_master_version(MYSQL* mysql) { MYSQL_RES* res = 0; @@ -481,7 +638,7 @@ static int check_master_version(MYSQL* mysql) } -static void dump_remote_log_entries(const char* logname) +static int dump_remote_log_entries(const char* logname) { char buf[128]; char last_db[FN_REFLEN+1] = ""; @@ -495,7 +652,7 @@ static void dump_remote_log_entries(const char* logname) if (position < BIN_LOG_HEADER_SIZE) { position = BIN_LOG_HEADER_SIZE; - // warn the guity + // warn the user sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE); } int4store(buf, position); @@ -504,30 +661,75 @@ static void dump_remote_log_entries(const char* logname) int4store(buf + 6, 0); memcpy(buf + 10, logname,len); if (simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1)) - die("Error sending the log dump command"); + { + fprintf(stderr,"Got fatal error sending the log dump command\n"); + return 1; + } + + my_off_t old_off= 0; + ulonglong rec_count= 0; + char fname[FN_REFLEN+1]; for (;;) { const char *error; len = net_safe_read(mysql); if (len == packet_error) - die("Error reading packet from server: %s", mysql_error(mysql)); + { + fprintf(stderr, "Got error reading packet from server: %s\n", + mysql_error(mysql)); + return 1; + } if (len < 8 && net->read_pos[0] == 254) break; // end of data DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", len, net->read_pos[5])); Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 , len - 1, &error, old_format); - if (ev) + if (!ev) { - ev->print(result_file, short_form, last_db); - if (ev->get_type_code() == LOAD_EVENT) - dump_remote_file(net, ((Load_log_event*)ev)->fname); - delete ev; + fprintf(stderr, "Could not construct log event object\n"); + return 1; + } + + Log_event_type type= ev->get_type_code(); + if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT)) + { + if (process_event(&rec_count,last_db,ev,old_off,old_format)) + return 1; } else - die("Could not construct log event object"); + { + Load_log_event *le= (Load_log_event*)ev; + const char *old_fname= le->fname; + uint old_len= le->fname_len; + File file; + + if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) + return 1; + if (process_event(&rec_count,last_db,ev,old_off,old_format)) + { + my_close(file,MYF(MY_WME)); + return 1; + } + if (load_processor.load_old_format_file(net,old_fname,old_len,file)) + { + my_close(file,MYF(MY_WME)); + return 1; + } + my_close(file,MYF(MY_WME)); + } + + /* + Let's adjust offset for remote log as for local log to produce + similar text.. + */ + if (old_off) + old_off+= len-1; + else + old_off= BIN_LOG_HEADER_SIZE; } + return 0; } @@ -557,7 +759,7 @@ static int check_header(IO_CACHE* file) } -static void dump_local_log_entries(const char* logname) +static int dump_local_log_entries(const char* logname) { File fd = -1; IO_CACHE cache,*file= &cache; @@ -565,23 +767,27 @@ static void dump_local_log_entries(const char* logname) char last_db[FN_REFLEN+1]; byte tmp_buff[BIN_LOG_HEADER_SIZE]; bool old_format = 0; + int error= 0; - last_db[0]=0; + last_db[0]= 0; if (logname && logname[0] != '-') { if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) - exit(1); + return 1; if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0, MYF(MY_WME | MY_NABP))) + { + my_close(fd, MYF(MY_WME)); exit(1); + } old_format = check_header(file); } else { if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0, 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) - exit(1); + return 1; old_format = check_header(file); if (position) { @@ -592,7 +798,10 @@ static void dump_local_log_entries(const char* logname) { tmp=min(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) - exit(1); + { + error= 1; + goto end; + } } } file->pos_in_file=position; @@ -600,7 +809,15 @@ static void dump_local_log_entries(const char* logname) } if (!position) - my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE); // Skip header + { + // Skip header + if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) + { + error= 1; + goto end; + } + } + for (;;) { char llbuff[21]; @@ -610,104 +827,35 @@ static void dump_local_log_entries(const char* logname) if (!ev) { if (file->error) - die("\ -Could not read entry at offset %s : Error in log format or read error", - llstr(old_off,llbuff)); + { + fprintf(stderr, + "Could not read entry at offset %s:" + "Error in log format or read error\n", + llstr(old_off,llbuff)); + error= 1; + } // file->error == 0 means EOF, that's OK, we break in this case break; } - if (rec_count >= offset) + if (process_event(&rec_count,last_db,ev,old_off,false)) { - if (!short_form) - fprintf(result_file, "# at %s\n",llstr(old_off,llbuff)); - - switch (ev->get_type_code()) { - case QUERY_EVENT: - if (one_database) - { - const char * log_dbname = ((Query_log_event*)ev)->db; - if ((log_dbname != NULL) && (strcmp(log_dbname, database))) - { - rec_count++; - delete ev; - continue; // next - } - } - ev->print(result_file, short_form, last_db); - break; - case CREATE_FILE_EVENT: - { - Create_file_log_event* ce= (Create_file_log_event*)ev; - if (one_database) - { - /* - We test if this event has to be ignored. If yes, we don't save this - event; this will have the good side-effect of ignoring all related - Append_block and Exec_load. - Note that Load event from 3.23 is not tested. - */ - const char * log_dbname = ce->db; - if ((log_dbname != NULL) && (strcmp(log_dbname, database))) - { - rec_count++; - delete ev; - continue; // next - } - } - /* - We print the event, but with a leading '#': this is just to inform - the user of the original command; the command we want to execute - will be a derivation of this original command (we will change the - filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' - below. - */ - ce->print(result_file, short_form, last_db, true); - load_processor.process(ce); - ev= 0; - break; - } - case APPEND_BLOCK_EVENT: - ev->print(result_file, short_form, last_db); - load_processor.process((Append_block_log_event*)ev); - break; - case EXEC_LOAD_EVENT: - { - ev->print(result_file, short_form, last_db); - Execute_load_log_event *exv= (Execute_load_log_event*)ev; - Create_file_log_event *ce= load_processor.grab_event(exv->file_id); - /* - if ce is 0, it probably means that we have not seen the Create_file - event (a bad binlog, or most probably --position is after the - Create_file event). Print a warning comment. - */ - if (ce) - { - ce->print(result_file, short_form, last_db,true); - my_free((char*)ce->fname,MYF(MY_WME)); - delete ce; - } - else - fprintf(stderr,"Warning: ignoring Exec_load as there is no \ -Create_file event for file_id: %u\n",exv->file_id); - break; - } - default: - ev->print(result_file, short_form, last_db); - } + error= 1; + break; } - rec_count++; - if (ev) - delete ev; } + +end: if (fd >= 0) my_close(fd, MYF(MY_WME)); end_io_cache(file); + return error; } int main(int argc, char** argv) { static char **defaults_argv; + int exit_value; MY_INIT(argv[0]); parse_args(&argc, (char***)&argv); @@ -717,7 +865,7 @@ int main(int argc, char** argv) { usage(); free_defaults(defaults_argv); - return -1; + exit(1); } if (remote_opt) @@ -737,8 +885,16 @@ int main(int argc, char** argv) else load_processor.init_by_cur_dir(); + exit_value= 0; while (--argc >= 0) - dump_log_entries(*(argv++)); + { + if (dump_log_entries(*(argv++))) + { + exit_value=1; + break; + } + } + if (tmpdir.list) free_tmpdir(&tmpdir); if (result_file != stdout) @@ -748,7 +904,8 @@ int main(int argc, char** argv) cleanup(); free_defaults(defaults_argv); my_end(0); - return 0; + exit(exit_value); + return exit_value; // Keep compilers happy } /* |