00001 /* Copyright (C) 2001-2004 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 00019 TODO: print the catalog (some USE catalog.db ????). 00020 00021 Standalone program to read a MySQL binary log (or relay log); 00022 can read files produced by 3.23, 4.x, 5.0 servers. 00023 00024 Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0. 00025 Should be able to read any file of these categories, even with 00026 --start-position. 00027 An important fact: the Format_desc event of the log is at most the 3rd event 00028 of the log; if it is the 3rd then there is this combination: 00029 Format_desc_of_slave, Rotate_of_master, Format_desc_of_master. 00030 */ 00031 00032 #define MYSQL_CLIENT 00033 #undef MYSQL_SERVER 00034 #include "client_priv.h" 00035 #include <my_time.h> 00036 /* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */ 00037 #include "mysql_priv.h" 00038 #include "log_event.h" 00039 #include "sql_common.h" 00040 00041 #define BIN_LOG_HEADER_SIZE 4 00042 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) 00043 00044 00045 #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES) 00046 00047 char server_version[SERVER_VERSION_LENGTH]; 00048 ulong server_id = 0; 00049 00050 // needed by net_serv.c 00051 ulong bytes_sent = 0L, bytes_received = 0L; 00052 ulong mysqld_net_retry_count = 10L; 00053 ulong open_files_limit; 00054 uint test_flags = 0; 00055 static uint opt_protocol= 0; 00056 static FILE *result_file; 00057 00058 #ifndef DBUG_OFF 00059 static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace"; 00060 #endif 00061 static const char *load_default_groups[]= { "mysqlbinlog","client",0 }; 00062 00063 void sql_print_error(const char *format, ...); 00064 00065 static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; 00066 static bool opt_hexdump= 0; 00067 static bool opt_base64_output= 0; 00068 static const char* database= 0; 00069 static my_bool force_opt= 0, short_form= 0, remote_opt= 0; 00070 static ulonglong offset = 0; 00071 static const char* host = 0; 00072 static int port= 0; 00073 static const char* sock= 0; 00074 static const char* user = 0; 00075 static char* pass = 0; 00076 static char *charset= 0; 00077 00078 static ulonglong start_position, stop_position; 00079 #define start_position_mot ((my_off_t)start_position) 00080 #define stop_position_mot ((my_off_t)stop_position) 00081 00082 static char *start_datetime_str, *stop_datetime_str; 00083 static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX; 00084 static ulonglong rec_count= 0; 00085 static short binlog_flags = 0; 00086 static MYSQL* mysql = NULL; 00087 static const char* dirname_for_local_load= 0; 00088 static bool stop_passed= 0; 00089 00090 /* 00091 check_header() will set the pointer below. 00092 Why do we need here a pointer on an event instead of an event ? 00093 This is because the event will be created (alloced) in read_log_event() 00094 (which returns a pointer) in check_header(). 00095 */ 00096 Format_description_log_event* description_event; 00097 00098 static int dump_local_log_entries(const char* logname); 00099 static int dump_remote_log_entries(const char* logname); 00100 static int dump_log_entries(const char* logname); 00101 static int dump_remote_file(NET* net, const char* fname); 00102 static void die(const char* fmt, ...); 00103 static MYSQL* safe_connect(); 00104 00105 00106 class Load_log_processor 00107 { 00108 char target_dir_name[FN_REFLEN]; 00109 int target_dir_name_len; 00110 00111 /* 00112 When we see first event corresponding to some LOAD DATA statement in 00113 binlog, we create temporary file to store data to be loaded. 00114 We add name of this file to file_names array using its file_id as index. 00115 If we have Create_file event (i.e. we have binary log in pre-5.0.3 00116 format) we also store save event object to be able which is needed to 00117 emit LOAD DATA statement when we will meet Exec_load_data event. 00118 If we have Begin_load_query event we simply store 0 in 00119 File_name_record::event field. 00120 */ 00121 struct File_name_record 00122 { 00123 char *fname; 00124 Create_file_log_event *event; 00125 }; 00126 DYNAMIC_ARRAY file_names; 00127 00128 /* 00129 Looking for new uniquie filename that doesn't exist yet by 00130 adding postfix -%x 00131 00132 SYNOPSIS 00133 create_unique_file() 00134 00135 filename buffer for filename 00136 file_name_end tail of buffer that should be changed 00137 should point to a memory enough to printf("-%x",..) 00138 00139 RETURN VALUES 00140 values less than 0 - can't find new filename 00141 values great or equal 0 - created file with found filename 00142 */ 00143 File create_unique_file(char *filename, char *file_name_end) 00144 { 00145 File res; 00146 /* If we have to try more than 1000 times, something is seriously wrong */ 00147 for (uint version= 0; version<1000; version++) 00148 { 00149 sprintf(file_name_end,"-%x",version); 00150 if ((res= my_create(filename,0, 00151 O_CREAT|O_EXCL|O_BINARY|O_WRONLY,MYF(0)))!=-1) 00152 return res; 00153 } 00154 return -1; 00155 } 00156 00157 public: 00158 Load_log_processor() {} 00159 ~Load_log_processor() 00160 { 00161 destroy(); 00162 delete_dynamic(&file_names); 00163 } 00164 00165 int init() 00166 { 00167 return init_dynamic_array(&file_names, sizeof(File_name_record), 00168 100,100 CALLER_INFO); 00169 } 00170 00171 void init_by_dir_name(const char *dir) 00172 { 00173 target_dir_name_len= (convert_dirname(target_dir_name, dir, NullS) - 00174 target_dir_name); 00175 } 00176 void init_by_cur_dir() 00177 { 00178 if (my_getwd(target_dir_name,sizeof(target_dir_name),MYF(MY_WME))) 00179 exit(1); 00180 target_dir_name_len= strlen(target_dir_name); 00181 } 00182 void destroy() 00183 { 00184 File_name_record *ptr= (File_name_record *)file_names.buffer; 00185 File_name_record *end= ptr + file_names.elements; 00186 for (; ptr<end; ptr++) 00187 { 00188 if (ptr->fname) 00189 { 00190 my_free(ptr->fname, MYF(MY_WME)); 00191 delete ptr->event; 00192 bzero((char *)ptr, sizeof(File_name_record)); 00193 } 00194 } 00195 } 00196 00197 /* 00198 Obtain Create_file event for LOAD DATA statement by its file_id. 00199 00200 SYNOPSIS 00201 grab_event() 00202 file_id - file_id identifiying LOAD DATA statement 00203 00204 DESCRIPTION 00205 Checks whenever we have already seen Create_file event for this file_id. 00206 If yes then returns pointer to it and removes it from array describing 00207 active temporary files. Since this moment caller is responsible for 00208 freeing memory occupied by this event and associated file name. 00209 00210 RETURN VALUES 00211 Pointer to Create_file event or 0 if there was no such event 00212 with this file_id. 00213 */ 00214 Create_file_log_event *grab_event(uint file_id) 00215 { 00216 File_name_record *ptr; 00217 Create_file_log_event *res; 00218 00219 if (file_id >= file_names.elements) 00220 return 0; 00221 ptr= dynamic_element(&file_names, file_id, File_name_record*); 00222 if ((res= ptr->event)) 00223 bzero((char *)ptr, sizeof(File_name_record)); 00224 return res; 00225 } 00226 00227 /* 00228 Obtain file name of temporary file for LOAD DATA statement by its file_id. 00229 00230 SYNOPSIS 00231 grab_fname() 00232 file_id - file_id identifiying LOAD DATA statement 00233 00234 DESCRIPTION 00235 Checks whenever we have already seen Begin_load_query event for this 00236 file_id. If yes then returns file name of corresponding temporary file. 00237 Removes record about this file from the array of active temporary files. 00238 Since this moment caller is responsible for freeing memory occupied by 00239 this name. 00240 00241 RETURN VALUES 00242 String with name of temporary file or 0 if we have not seen Begin_load_query 00243 event with this file_id. 00244 */ 00245 char *grab_fname(uint file_id) 00246 { 00247 File_name_record *ptr; 00248 char *res= 0; 00249 00250 if (file_id >= file_names.elements) 00251 return 0; 00252 ptr= dynamic_element(&file_names, file_id, File_name_record*); 00253 if (!ptr->event) 00254 { 00255 res= ptr->fname; 00256 bzero((char *)ptr, sizeof(File_name_record)); 00257 } 00258 return res; 00259 } 00260 int process(Create_file_log_event *ce); 00261 int process(Begin_load_query_log_event *ce); 00262 int process(Append_block_log_event *ae); 00263 File prepare_new_file_for_old_format(Load_log_event *le, char *filename); 00264 int load_old_format_file(NET* net, const char *server_fname, 00265 uint server_fname_len, File file); 00266 int process_first_event(const char *bname, uint blen, const char *block, 00267 uint block_len, uint file_id, 00268 Create_file_log_event *ce); 00269 }; 00270 00271 00272 00273 File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le, 00274 char *filename) 00275 { 00276 uint len; 00277 char *tail; 00278 File file; 00279 00280 fn_format(filename, le->fname, target_dir_name, "", 1); 00281 len= strlen(filename); 00282 tail= filename + len; 00283 00284 if ((file= create_unique_file(filename,tail)) < 0) 00285 { 00286 sql_print_error("Could not construct local filename %s",filename); 00287 return -1; 00288 } 00289 00290 le->set_fname_outside_temp_buf(filename,len+strlen(tail)); 00291 00292 return file; 00293 } 00294 00295 00296 int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, 00297 uint server_fname_len, File file) 00298 { 00299 char buf[FN_REFLEN+1]; 00300 buf[0] = 0; 00301 memcpy(buf + 1, server_fname, server_fname_len + 1); 00302 if (my_net_write(net, buf, server_fname_len +2) || net_flush(net)) 00303 { 00304 sql_print_error("Failed requesting the remote dump of %s", server_fname); 00305 return -1; 00306 } 00307 00308 for (;;) 00309 { 00310 ulong packet_len = my_net_read(net); 00311 if (packet_len == 0) 00312 { 00313 if (my_net_write(net, "", 0) || net_flush(net)) 00314 { 00315 sql_print_error("Failed sending the ack packet"); 00316 return -1; 00317 } 00318 /* 00319 we just need to send something, as the server will read but 00320 not examine the packet - this is because mysql_load() sends 00321 an OK when it is done 00322 */ 00323 break; 00324 } 00325 else if (packet_len == packet_error) 00326 { 00327 sql_print_error("Failed reading a packet during the dump of %s ", 00328 server_fname); 00329 return -1; 00330 } 00331 00332 if (packet_len > UINT_MAX) 00333 { 00334 sql_print_error("Illegal length of packet read from net"); 00335 return -1; 00336 } 00337 if (my_write(file, (byte*) net->read_pos, 00338 (uint) packet_len, MYF(MY_WME|MY_NABP))) 00339 return -1; 00340 } 00341 00342 return 0; 00343 } 00344 00345 00346 /* 00347 Process first event in the sequence of events representing LOAD DATA 00348 statement. 00349 00350 SYNOPSIS 00351 process_first_event() 00352 bname - base name for temporary file to be created 00353 blen - base name length 00354 block - first block of data to be loaded 00355 block_len - first block length 00356 file_id - identifies LOAD DATA statement 00357 ce - pointer to Create_file event object if we are processing 00358 this type of event. 00359 00360 DESCRIPTION 00361 Creates temporary file to be used in LOAD DATA and writes first block of 00362 data to it. Registers its file name (and optional Create_file event) 00363 in the array of active temporary files. 00364 00365 RETURN VALUES 00366 0 - success 00367 non-0 - error 00368 */ 00369 00370 int Load_log_processor::process_first_event(const char *bname, uint blen, 00371 const char *block, uint block_len, 00372 uint file_id, 00373 Create_file_log_event *ce) 00374 { 00375 uint full_len= target_dir_name_len + blen + 9 + 9 + 1; 00376 int error= 0; 00377 char *fname, *ptr; 00378 File file; 00379 File_name_record rec; 00380 DBUG_ENTER("Load_log_processor::process_first_event"); 00381 00382 if (!(fname= my_malloc(full_len,MYF(MY_WME)))) 00383 DBUG_RETURN(-1); 00384 00385 memcpy(fname, target_dir_name, target_dir_name_len); 00386 ptr= fname + target_dir_name_len; 00387 memcpy(ptr,bname,blen); 00388 ptr+= blen; 00389 ptr+= my_sprintf(ptr, (ptr, "-%x", file_id)); 00390 00391 if ((file= create_unique_file(fname,ptr)) < 0) 00392 { 00393 sql_print_error("Could not construct local filename %s%s", 00394 target_dir_name,bname); 00395 DBUG_RETURN(-1); 00396 } 00397 00398 rec.fname= fname; 00399 rec.event= ce; 00400 00401 if (set_dynamic(&file_names, (gptr)&rec, file_id)) 00402 { 00403 sql_print_error("Could not construct local filename %s%s", 00404 target_dir_name, bname); 00405 DBUG_RETURN(-1); 00406 } 00407 00408 if (ce) 00409 ce->set_fname_outside_temp_buf(fname, strlen(fname)); 00410 00411 if (my_write(file, (byte*)block, block_len, MYF(MY_WME|MY_NABP))) 00412 error= -1; 00413 if (my_close(file, MYF(MY_WME))) 00414 error= -1; 00415 DBUG_RETURN(error); 00416 } 00417 00418 00419 int Load_log_processor::process(Create_file_log_event *ce) 00420 { 00421 const char *bname= ce->fname + dirname_length(ce->fname); 00422 uint blen= ce->fname_len - (bname-ce->fname); 00423 00424 return process_first_event(bname, blen, ce->block, ce->block_len, 00425 ce->file_id, ce); 00426 } 00427 00428 00429 int Load_log_processor::process(Begin_load_query_log_event *blqe) 00430 { 00431 return process_first_event("SQL_LOAD_MB", 11, blqe->block, blqe->block_len, 00432 blqe->file_id, 0); 00433 } 00434 00435 00436 int Load_log_processor::process(Append_block_log_event *ae) 00437 { 00438 DBUG_ENTER("Load_log_processor::process"); 00439 const char* fname= ((ae->file_id < file_names.elements) ? 00440 dynamic_element(&file_names, ae->file_id, 00441 File_name_record*)->fname : 0); 00442 00443 if (fname) 00444 { 00445 File file; 00446 int error= 0; 00447 if (((file= my_open(fname, 00448 O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0)) 00449 DBUG_RETURN(-1); 00450 if (my_write(file,(byte*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP))) 00451 error= -1; 00452 if (my_close(file,MYF(MY_WME))) 00453 error= -1; 00454 DBUG_RETURN(error); 00455 } 00456 00457 /* 00458 There is no Create_file event (a bad binlog or a big 00459 --start-position). Assuming it's a big --start-position, we just do 00460 nothing and print a warning. 00461 */ 00462 fprintf(stderr,"Warning: ignoring Append_block as there is no \ 00463 Create_file event for file_id: %u\n",ae->file_id); 00464 DBUG_RETURN(-1); 00465 } 00466 00467 00468 Load_log_processor load_processor; 00469 00470 00471 static bool check_database(const char *log_dbname) 00472 { 00473 return one_database && 00474 (log_dbname != NULL) && 00475 strcmp(log_dbname, database); 00476 } 00477 00478 00479 /* 00480 Process an event 00481 00482 SYNOPSIS 00483 process_event() 00484 00485 RETURN 00486 0 ok and continue 00487 1 error and terminate 00488 -1 ok and terminate 00489 00490 TODO 00491 This function returns 0 even in some error cases. This should be changed. 00492 */ 00493 00494 00495 00496 int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, 00497 my_off_t pos) 00498 { 00499 char ll_buff[21]; 00500 Log_event_type ev_type= ev->get_type_code(); 00501 DBUG_ENTER("process_event"); 00502 print_event_info->short_form= short_form; 00503 00504 /* 00505 Format events are not concerned by --offset and such, we always need to 00506 read them to be able to process the wanted events. 00507 */ 00508 if ((rec_count >= offset) && 00509 ((my_time_t)(ev->when) >= start_datetime) || 00510 (ev_type == FORMAT_DESCRIPTION_EVENT)) 00511 { 00512 if (ev_type != FORMAT_DESCRIPTION_EVENT) 00513 { 00514 /* 00515 We have found an event after start_datetime, from now on print 00516 everything (in case the binlog has timestamps increasing and 00517 decreasing, we do this to avoid cutting the middle). 00518 */ 00519 start_datetime= 0; 00520 offset= 0; // print everything and protect against cycling rec_count 00521 } 00522 if (server_id && (server_id != ev->server_id)) { 00523 DBUG_RETURN(0); 00524 } 00525 if (((my_time_t)(ev->when) >= stop_datetime) 00526 || (pos >= stop_position_mot)) 00527 { 00528 stop_passed= 1; // skip all next binlogs 00529 DBUG_RETURN(-1); 00530 } 00531 if (!short_form) 00532 fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); 00533 00534 if (!opt_hexdump) 00535 print_event_info->hexdump_from= 0; /* Disabled */ 00536 else 00537 print_event_info->hexdump_from= pos; 00538 00539 print_event_info->base64_output= opt_base64_output; 00540 00541 switch (ev_type) { 00542 case QUERY_EVENT: 00543 if (check_database(((Query_log_event*)ev)->db)) 00544 goto end; 00545 if (opt_base64_output) 00546 { 00547 ev->print_header(result_file, print_event_info); 00548 ev->print_base64(result_file, print_event_info); 00549 } 00550 else 00551 ev->print(result_file, print_event_info); 00552 break; 00553 case CREATE_FILE_EVENT: 00554 { 00555 Create_file_log_event* ce= (Create_file_log_event*)ev; 00556 /* 00557 We test if this event has to be ignored. If yes, we don't save 00558 this event; this will have the good side-effect of ignoring all 00559 related Append_block and Exec_load. 00560 Note that Load event from 3.23 is not tested. 00561 */ 00562 if (check_database(ce->db)) 00563 goto end; // Next event 00564 /* 00565 We print the event, but with a leading '#': this is just to inform 00566 the user of the original command; the command we want to execute 00567 will be a derivation of this original command (we will change the 00568 filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' 00569 below. 00570 */ 00571 if (opt_base64_output) 00572 { 00573 ce->print_header(result_file, print_event_info); 00574 ce->print_base64(result_file, print_event_info); 00575 } 00576 else 00577 ce->print(result_file, print_event_info, TRUE); 00578 00579 // If this binlog is not 3.23 ; why this test?? 00580 if (description_event->binlog_version >= 3) 00581 { 00582 if (load_processor.process(ce)) 00583 break; // Error 00584 ev= 0; 00585 } 00586 break; 00587 } 00588 case APPEND_BLOCK_EVENT: 00589 ev->print(result_file, print_event_info); 00590 if (load_processor.process((Append_block_log_event*) ev)) 00591 break; // Error 00592 break; 00593 case EXEC_LOAD_EVENT: 00594 { 00595 ev->print(result_file, print_event_info); 00596 Execute_load_log_event *exv= (Execute_load_log_event*)ev; 00597 Create_file_log_event *ce= load_processor.grab_event(exv->file_id); 00598 /* 00599 if ce is 0, it probably means that we have not seen the Create_file 00600 event (a bad binlog, or most probably --start-position is after the 00601 Create_file event). Print a warning comment. 00602 */ 00603 if (ce) 00604 { 00605 ce->print(result_file, print_event_info, TRUE); 00606 my_free((char*)ce->fname,MYF(MY_WME)); 00607 delete ce; 00608 } 00609 else 00610 fprintf(stderr,"Warning: ignoring Exec_load as there is no \ 00611 Create_file event for file_id: %u\n",exv->file_id); 00612 break; 00613 } 00614 case FORMAT_DESCRIPTION_EVENT: 00615 delete description_event; 00616 description_event= (Format_description_log_event*) ev; 00617 print_event_info->common_header_len= description_event->common_header_len; 00618 ev->print(result_file, print_event_info); 00619 /* 00620 We don't want this event to be deleted now, so let's hide it (I 00621 (Guilhem) should later see if this triggers a non-serious Valgrind 00622 error). Not serious error, because we will free description_event 00623 later. 00624 */ 00625 ev= 0; 00626 break; 00627 case BEGIN_LOAD_QUERY_EVENT: 00628 ev->print(result_file, print_event_info); 00629 load_processor.process((Begin_load_query_log_event*) ev); 00630 break; 00631 case EXECUTE_LOAD_QUERY_EVENT: 00632 { 00633 Execute_load_query_log_event *exlq= (Execute_load_query_log_event*)ev; 00634 char *fname= load_processor.grab_fname(exlq->file_id); 00635 00636 if (check_database(exlq->db)) 00637 { 00638 if (fname) 00639 my_free(fname, MYF(MY_WME)); 00640 goto end; 00641 } 00642 00643 if (fname) 00644 { 00645 exlq->print(result_file, print_event_info, fname); 00646 my_free(fname, MYF(MY_WME)); 00647 } 00648 else 00649 fprintf(stderr,"Warning: ignoring Execute_load_query as there is no \ 00650 Begin_load_query event for file_id: %u\n", exlq->file_id); 00651 break; 00652 } 00653 default: 00654 ev->print(result_file, print_event_info); 00655 } 00656 } 00657 00658 end: 00659 rec_count++; 00660 if (ev) 00661 delete ev; 00662 DBUG_RETURN(0); 00663 } 00664 00665 00666 static struct my_option my_long_options[] = 00667 { 00668 {"help", '?', "Display this help and exit.", 00669 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00670 #ifdef __NETWARE__ 00671 {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.", 00672 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00673 #endif 00674 {"base64-output", OPT_BASE64_OUTPUT, 00675 "Print all binlog entries using base64 encoding. " 00676 "This is for debugging only. Logs produced using this option " 00677 "should not be applied on production systems.", 00678 (gptr*) &opt_base64_output, (gptr*) &opt_base64_output, 0, GET_BOOL, 00679 NO_ARG, 0, 0, 0, 0, 0, 0}, 00680 /* 00681 mysqlbinlog needs charsets knowledge, to be able to convert a charset 00682 number found in binlog to a charset name (to be able to print things 00683 like this: 00684 SET @`a`:=_cp850 0x4DFC6C6C6572 COLLATE `cp850_general_ci`; 00685 */ 00686 {"character-sets-dir", OPT_CHARSETS_DIR, 00687 "Directory where character sets are.", (gptr*) &charsets_dir, 00688 (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00689 {"database", 'd', "List entries for just this database (local log only).", 00690 (gptr*) &database, (gptr*) &database, 0, GET_STR_ALLOC, REQUIRED_ARG, 00691 0, 0, 0, 0, 0, 0}, 00692 #ifndef DBUG_OFF 00693 {"debug", '#', "Output debug log.", (gptr*) &default_dbug_option, 00694 (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00695 #endif 00696 {"disable-log-bin", 'D', "Disable binary log. This is useful, if you " 00697 "enabled --to-last-log and are sending the output to the same MySQL server. " 00698 "This way you could avoid an endless loop. You would also like to use it " 00699 "when restoring after a crash to avoid duplication of the statements you " 00700 "already have. NOTE: you will need a SUPER privilege to use this option.", 00701 (gptr*) &disable_log_bin, (gptr*) &disable_log_bin, 0, GET_BOOL, 00702 NO_ARG, 0, 0, 0, 0, 0, 0}, 00703 {"force-read", 'f', "Force reading unknown binlog events.", 00704 (gptr*) &force_opt, (gptr*) &force_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 00705 0, 0}, 00706 {"hexdump", 'H', "Augment output with hexadecimal and ASCII event dump.", 00707 (gptr*) &opt_hexdump, (gptr*) &opt_hexdump, 0, GET_BOOL, NO_ARG, 00708 0, 0, 0, 0, 0, 0}, 00709 {"host", 'h', "Get the binlog from server.", (gptr*) &host, (gptr*) &host, 00710 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00711 {"local-load", 'l', "Prepare local temporary files for LOAD DATA INFILE in the specified directory.", 00712 (gptr*) &dirname_for_local_load, (gptr*) &dirname_for_local_load, 0, 00713 GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00714 {"offset", 'o', "Skip the first N entries.", (gptr*) &offset, (gptr*) &offset, 00715 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00716 {"password", 'p', "Password to connect to remote server.", 00717 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00718 {"port", 'P', "Use port to connect to the remote server.", 00719 (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 00720 0, 0, 0}, 00721 {"position", 'j', "Deprecated. Use --start-position instead.", 00722 (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL, 00723 REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE, 00724 /* COM_BINLOG_DUMP accepts only 4 bytes for the position */ 00725 (ulonglong)(~(uint32)0), 0, 0, 0}, 00726 {"protocol", OPT_MYSQL_PROTOCOL, 00727 "The protocol of connection (tcp,socket,pipe,memory).", 00728 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00729 {"read-from-remote-server", 'R', "Read binary logs from a MySQL server", 00730 (gptr*) &remote_opt, (gptr*) &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 00731 0, 0}, 00732 {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR, 00733 REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00734 {"server-id", OPT_SERVER_ID, 00735 "Extract only binlog entries created by the server having the given id.", 00736 (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, 00737 REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00738 {"set-charset", OPT_SET_CHARSET, 00739 "Add 'SET NAMES character_set' to the output.", (gptr*) &charset, 00740 (gptr*) &charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00741 {"short-form", 's', "Just show the queries, no extra info.", 00742 (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 00743 0, 0}, 00744 {"socket", 'S', "Socket file to use for connection.", 00745 (gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 00746 0, 0}, 00747 {"start-datetime", OPT_START_DATETIME, 00748 "Start reading the binlog at first event having a datetime equal or " 00749 "posterior to the argument; the argument must be a date and time " 00750 "in the local time zone, in any format accepted by the MySQL server " 00751 "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 " 00752 "(you should probably use quotes for your shell to set it properly).", 00753 (gptr*) &start_datetime_str, (gptr*) &start_datetime_str, 00754 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00755 {"start-position", OPT_START_POSITION, 00756 "Start reading the binlog at position N. Applies to the first binlog " 00757 "passed on the command line.", 00758 (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL, 00759 REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE, 00760 /* COM_BINLOG_DUMP accepts only 4 bytes for the position */ 00761 (ulonglong)(~(uint32)0), 0, 0, 0}, 00762 {"stop-datetime", OPT_STOP_DATETIME, 00763 "Stop reading the binlog at first event having a datetime equal or " 00764 "posterior to the argument; the argument must be a date and time " 00765 "in the local time zone, in any format accepted by the MySQL server " 00766 "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 " 00767 "(you should probably use quotes for your shell to set it properly).", 00768 (gptr*) &stop_datetime_str, (gptr*) &stop_datetime_str, 00769 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00770 {"stop-position", OPT_STOP_POSITION, 00771 "Stop reading the binlog at position N. Applies to the last binlog " 00772 "passed on the command line.", 00773 (gptr*) &stop_position, (gptr*) &stop_position, 0, GET_ULL, 00774 REQUIRED_ARG, (ulonglong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE, 00775 (ulonglong)(~(my_off_t)0), 0, 0, 0}, 00776 {"to-last-log", 't', "Requires -R. Will not stop at the end of the \ 00777 requested binlog but rather continue printing until the end of the last \ 00778 binlog of the MySQL server. If you send the output to the same MySQL server, \ 00779 that may lead to an endless loop.", 00780 (gptr*) &to_last_remote_log, (gptr*) &to_last_remote_log, 0, GET_BOOL, 00781 NO_ARG, 0, 0, 0, 0, 0, 0}, 00782 {"user", 'u', "Connect to the remote server as username.", 00783 (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 00784 0, 0}, 00785 {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 00786 0, 0, 0, 0, 0}, 00787 {"open_files_limit", OPT_OPEN_FILES_LIMIT, 00788 "Used to reserve file descriptors for usage by this program", 00789 (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG, 00790 REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0}, 00791 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} 00792 }; 00793 00794 00795 void sql_print_error(const char *format,...) 00796 { 00797 va_list args; 00798 va_start(args, format); 00799 fprintf(stderr, "ERROR: "); 00800 vfprintf(stderr, format, args); 00801 fprintf(stderr, "\n"); 00802 va_end(args); 00803 } 00804 00805 static void cleanup() 00806 { 00807 my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); 00808 my_free((char*) database, MYF(MY_ALLOW_ZERO_PTR)); 00809 my_free((char*) host, MYF(MY_ALLOW_ZERO_PTR)); 00810 my_free((char*) user, MYF(MY_ALLOW_ZERO_PTR)); 00811 my_free((char*) dirname_for_local_load, MYF(MY_ALLOW_ZERO_PTR)); 00812 } 00813 00814 static void die(const char* fmt, ...) 00815 { 00816 va_list args; 00817 va_start(args, fmt); 00818 fprintf(stderr, "ERROR: "); 00819 vfprintf(stderr, fmt, args); 00820 fprintf(stderr, "\n"); 00821 va_end(args); 00822 cleanup(); 00823 /* We cannot free DBUG, it is used in global destructors after exit(). */ 00824 my_end(MY_DONT_FREE_DBUG); 00825 exit(1); 00826 } 00827 00828 #include <help_start.h> 00829 00830 static void print_version() 00831 { 00832 printf("%s Ver 3.1 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); 00833 NETWARE_SET_SCREEN_MODE(1); 00834 } 00835 00836 00837 static void usage() 00838 { 00839 print_version(); 00840 puts("By Monty and Sasha, for your professional use\n\ 00841 This software comes with NO WARRANTY: This is free software,\n\ 00842 and you are welcome to modify and redistribute it under the GPL license\n"); 00843 00844 printf("\ 00845 Dumps a MySQL binary log in a format usable for viewing or for piping to\n\ 00846 the mysql command line client\n\n"); 00847 printf("Usage: %s [options] log-files\n", my_progname); 00848 my_print_help(my_long_options); 00849 my_print_variables(my_long_options); 00850 } 00851 00852 00853 static my_time_t convert_str_to_timestamp(const char* str) 00854 { 00855 int was_cut; 00856 MYSQL_TIME l_time; 00857 long dummy_my_timezone; 00858 my_bool dummy_in_dst_time_gap; 00859 /* We require a total specification (date AND time) */ 00860 if (str_to_datetime(str, strlen(str), &l_time, 0, &was_cut) != 00861 MYSQL_TIMESTAMP_DATETIME || was_cut) 00862 { 00863 fprintf(stderr, "Incorrect date and time argument: %s\n", str); 00864 exit(1); 00865 } 00866 /* 00867 Note that Feb 30th, Apr 31st cause no error messages and are mapped to 00868 the next existing day, like in mysqld. Maybe this could be changed when 00869 mysqld is changed too (with its "strict" mode?). 00870 */ 00871 return 00872 my_system_gmt_sec(&l_time, &dummy_my_timezone, &dummy_in_dst_time_gap); 00873 } 00874 00875 #include <help_end.h> 00876 00877 extern "C" my_bool 00878 get_one_option(int optid, const struct my_option *opt __attribute__((unused)), 00879 char *argument) 00880 { 00881 bool tty_password=0; 00882 switch (optid) { 00883 #ifdef __NETWARE__ 00884 case OPT_AUTO_CLOSE: 00885 setscreenmode(SCR_AUTOCLOSE_ON_EXIT); 00886 break; 00887 #endif 00888 #ifndef DBUG_OFF 00889 case '#': 00890 DBUG_PUSH(argument ? argument : default_dbug_option); 00891 break; 00892 #endif 00893 case 'd': 00894 one_database = 1; 00895 break; 00896 case 'p': 00897 if (argument) 00898 { 00899 my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); 00900 char *start=argument; 00901 pass= my_strdup(argument,MYF(MY_FAE)); 00902 while (*argument) *argument++= 'x'; /* Destroy argument */ 00903 if (*start) 00904 start[1]=0; /* Cut length of argument */ 00905 } 00906 else 00907 tty_password=1; 00908 break; 00909 case 'r': 00910 if (!(result_file = my_fopen(argument, O_WRONLY | O_BINARY, MYF(MY_WME)))) 00911 exit(1); 00912 break; 00913 case 'R': 00914 remote_opt= 1; 00915 break; 00916 case OPT_MYSQL_PROTOCOL: 00917 { 00918 if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0) 00919 { 00920 fprintf(stderr, "Unknown option to protocol: %s\n", argument); 00921 exit(1); 00922 } 00923 break; 00924 } 00925 case OPT_START_DATETIME: 00926 start_datetime= convert_str_to_timestamp(start_datetime_str); 00927 break; 00928 case OPT_STOP_DATETIME: 00929 stop_datetime= convert_str_to_timestamp(stop_datetime_str); 00930 break; 00931 case 'V': 00932 print_version(); 00933 exit(0); 00934 case '?': 00935 usage(); 00936 exit(0); 00937 } 00938 if (tty_password) 00939 pass= get_tty_password(NullS); 00940 00941 return 0; 00942 } 00943 00944 00945 static int parse_args(int *argc, char*** argv) 00946 { 00947 int ho_error; 00948 00949 result_file = stdout; 00950 load_defaults("my",load_default_groups,argc,argv); 00951 if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option))) 00952 exit(ho_error); 00953 00954 return 0; 00955 } 00956 00957 static MYSQL* safe_connect() 00958 { 00959 MYSQL *local_mysql= mysql_init(NULL); 00960 00961 if (!local_mysql) 00962 die("Failed on mysql_init"); 00963 00964 if (opt_protocol) 00965 mysql_options(local_mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); 00966 if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0)) 00967 { 00968 char errmsg[256]; 00969 strmake(errmsg, mysql_error(local_mysql), sizeof(errmsg)-1); 00970 mysql_close(local_mysql); 00971 die("failed on connect: %s", errmsg); 00972 } 00973 local_mysql->reconnect= 1; 00974 return local_mysql; 00975 } 00976 00977 00978 static int dump_log_entries(const char* logname) 00979 { 00980 return (remote_opt ? dump_remote_log_entries(logname) : 00981 dump_local_log_entries(logname)); 00982 } 00983 00984 00985 /* 00986 This is not as smart as check_header() (used for local log); it will not work 00987 for a binlog which mixes format. TODO: fix this. 00988 */ 00989 static int check_master_version(MYSQL* mysql, 00990 Format_description_log_event 00991 **description_event) 00992 { 00993 MYSQL_RES* res = 0; 00994 MYSQL_ROW row; 00995 const char* version; 00996 00997 if (mysql_query(mysql, "SELECT VERSION()") || 00998 !(res = mysql_store_result(mysql))) 00999 { 01000 char errmsg[256]; 01001 strmake(errmsg, mysql_error(mysql), sizeof(errmsg)-1); 01002 mysql_close(mysql); 01003 die("Error checking master version: %s", errmsg); 01004 } 01005 if (!(row = mysql_fetch_row(res))) 01006 { 01007 mysql_free_result(res); 01008 mysql_close(mysql); 01009 die("Master returned no rows for SELECT VERSION()"); 01010 return 1; 01011 } 01012 if (!(version = row[0])) 01013 { 01014 mysql_free_result(res); 01015 mysql_close(mysql); 01016 die("Master reported NULL for the version"); 01017 } 01018 01019 switch (*version) { 01020 case '3': 01021 *description_event= new Format_description_log_event(1); 01022 break; 01023 case '4': 01024 *description_event= new Format_description_log_event(3); 01025 case '5': 01026 /* 01027 The server is soon going to send us its Format_description log 01028 event, unless it is a 5.0 server with 3.23 or 4.0 binlogs. 01029 So we first assume that this is 4.0 (which is enough to read the 01030 Format_desc event if one comes). 01031 */ 01032 *description_event= new Format_description_log_event(3); 01033 break; 01034 default: 01035 sql_print_error("Master reported unrecognized MySQL version '%s'", 01036 version); 01037 mysql_free_result(res); 01038 mysql_close(mysql); 01039 return 1; 01040 } 01041 mysql_free_result(res); 01042 return 0; 01043 } 01044 01045 01046 static int dump_remote_log_entries(const char* logname) 01047 01048 { 01049 char buf[128]; 01050 PRINT_EVENT_INFO print_event_info; 01051 ulong len; 01052 uint logname_len; 01053 NET* net; 01054 int error= 0; 01055 my_off_t old_off= start_position_mot; 01056 char fname[FN_REFLEN+1]; 01057 DBUG_ENTER("dump_remote_log_entries"); 01058 01059 /* 01060 Even if we already read one binlog (case of >=2 binlogs on command line), 01061 we cannot re-use the same connection as before, because it is now dead 01062 (COM_BINLOG_DUMP kills the thread when it finishes). 01063 */ 01064 mysql= safe_connect(); 01065 net= &mysql->net; 01066 01067 if (check_master_version(mysql, &description_event)) 01068 { 01069 fprintf(stderr, "Could not find server version"); 01070 DBUG_RETURN(1); 01071 } 01072 if (!description_event || !description_event->is_valid()) 01073 { 01074 fprintf(stderr, "Invalid Format_description log event; \ 01075 could be out of memory"); 01076 DBUG_RETURN(1); 01077 } 01078 01079 /* 01080 COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to 01081 cast to uint32. 01082 */ 01083 int4store(buf, (uint32)start_position); 01084 int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags); 01085 01086 size_s tlen = strlen(logname); 01087 if (tlen > UINT_MAX) 01088 { 01089 fprintf(stderr,"Log name too long\n"); 01090 error= 1; 01091 goto err; 01092 } 01093 logname_len = (uint) tlen; 01094 int4store(buf + 6, 0); 01095 memcpy(buf + 10, logname, logname_len); 01096 if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1)) 01097 { 01098 fprintf(stderr,"Got fatal error sending the log dump command\n"); 01099 error= 1; 01100 goto err; 01101 } 01102 01103 for (;;) 01104 { 01105 const char *error_msg; 01106 Log_event *ev; 01107 01108 len= cli_safe_read(mysql); 01109 if (len == packet_error) 01110 { 01111 fprintf(stderr, "Got error reading packet from server: %s\n", 01112 mysql_error(mysql)); 01113 error= 1; 01114 goto err; 01115 } 01116 if (len < 8 && net->read_pos[0] == 254) 01117 break; // end of data 01118 DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", 01119 len, net->read_pos[5])); 01120 if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 , 01121 len - 1, &error_msg, 01122 description_event))) 01123 { 01124 fprintf(stderr, "Could not construct log event object\n"); 01125 error= 1; 01126 goto err; 01127 } 01128 01129 Log_event_type type= ev->get_type_code(); 01130 if (description_event->binlog_version >= 3 || 01131 (type != LOAD_EVENT && type != CREATE_FILE_EVENT)) 01132 { 01133 /* 01134 If this is a Rotate event, maybe it's the end of the requested binlog; 01135 in this case we are done (stop transfer). 01136 This is suitable for binlogs, not relay logs (but for now we don't read 01137 relay logs remotely because the server is not able to do that). If one 01138 day we read relay logs remotely, then we will have a problem with the 01139 detection below: relay logs contain Rotate events which are about the 01140 binlogs, so which would trigger the end-detection below. 01141 */ 01142 if (type == ROTATE_EVENT) 01143 { 01144 Rotate_log_event *rev= (Rotate_log_event *)ev; 01145 /* 01146 If this is a fake Rotate event, and not about our log, we can stop 01147 transfer. If this a real Rotate event (so it's not about our log, 01148 it's in our log describing the next log), we print it (because it's 01149 part of our log) and then we will stop when we receive the fake one 01150 soon. 01151 */ 01152 if (rev->when == 0) 01153 { 01154 if (!to_last_remote_log) 01155 { 01156 if ((rev->ident_len != logname_len) || 01157 memcmp(rev->new_log_ident, logname, logname_len)) 01158 { 01159 error= 0; 01160 goto err; 01161 } 01162 /* 01163 Otherwise, this is a fake Rotate for our log, at the very 01164 beginning for sure. Skip it, because it was not in the original 01165 log. If we are running with to_last_remote_log, we print it, 01166 because it serves as a useful marker between binlogs then. 01167 */ 01168 continue; 01169 } 01170 len= 1; // fake Rotate, so don't increment old_off 01171 } 01172 } 01173 if ((error= process_event(&print_event_info, ev, old_off))) 01174 { 01175 error= ((error < 0) ? 0 : 1); 01176 goto err; 01177 } 01178 } 01179 else 01180 { 01181 Load_log_event *le= (Load_log_event*)ev; 01182 const char *old_fname= le->fname; 01183 uint old_len= le->fname_len; 01184 File file; 01185 01186 if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) 01187 { 01188 error= 1; 01189 goto err; 01190 } 01191 01192 if ((error= process_event(&print_event_info, ev, old_off))) 01193 { 01194 my_close(file,MYF(MY_WME)); 01195 error= ((error < 0) ? 0 : 1); 01196 goto err; 01197 } 01198 error= load_processor.load_old_format_file(net,old_fname,old_len,file); 01199 my_close(file,MYF(MY_WME)); 01200 if (error) 01201 { 01202 error= 1; 01203 goto err; 01204 } 01205 } 01206 /* 01207 Let's adjust offset for remote log as for local log to produce 01208 similar text. 01209 */ 01210 old_off+= len-1; 01211 } 01212 01213 err: 01214 mysql_close(mysql); 01215 DBUG_RETURN(error); 01216 } 01217 01218 01219 static void check_header(IO_CACHE* file, 01220 Format_description_log_event **description_event) 01221 { 01222 byte header[BIN_LOG_HEADER_SIZE]; 01223 byte buf[PROBE_HEADER_LEN]; 01224 my_off_t tmp_pos, pos; 01225 01226 *description_event= new Format_description_log_event(3); 01227 pos= my_b_tell(file); 01228 my_b_seek(file, (my_off_t)0); 01229 if (my_b_read(file, header, sizeof(header))) 01230 die("Failed reading header; Probably an empty file"); 01231 if (memcmp(header, BINLOG_MAGIC, sizeof(header))) 01232 die("File is not a binary log file"); 01233 01234 /* 01235 Imagine we are running with --start-position=1000. We still need 01236 to know the binlog format's. So we still need to find, if there is 01237 one, the Format_desc event, or to know if this is a 3.23 01238 binlog. So we need to first read the first events of the log, 01239 those around offset 4. Even if we are reading a 3.23 binlog from 01240 the start (no --start-position): we need to know the header length 01241 (which is 13 in 3.23, 19 in 4.x) to be able to successfully print 01242 the first event (Start_log_event_v3). So even in this case, we 01243 need to "probe" the first bytes of the log *before* we do a real 01244 read_log_event(). Because read_log_event() needs to know the 01245 header's length to work fine. 01246 */ 01247 for(;;) 01248 { 01249 tmp_pos= my_b_tell(file); /* should be 4 the first time */ 01250 if (my_b_read(file, buf, sizeof(buf))) 01251 { 01252 if (file->error) 01253 die("\ 01254 Could not read entry at offset %lu : Error in log format or read error", 01255 tmp_pos); 01256 /* 01257 Otherwise this is just EOF : this log currently contains 0-2 01258 events. Maybe it's going to be filled in the next 01259 milliseconds; then we are going to have a problem if this a 01260 3.23 log (imagine we are locally reading a 3.23 binlog which 01261 is being written presently): we won't know it in 01262 read_log_event() and will fail(). Similar problems could 01263 happen with hot relay logs if --start-position is used (but a 01264 --start-position which is posterior to the current size of the log). 01265 These are rare problems anyway (reading a hot log + when we 01266 read the first events there are not all there yet + when we 01267 read a bit later there are more events + using a strange 01268 --start-position). 01269 */ 01270 break; 01271 } 01272 else 01273 { 01274 DBUG_PRINT("info",("buf[4]=%d", buf[4])); 01275 /* always test for a Start_v3, even if no --start-position */ 01276 if (buf[4] == START_EVENT_V3) /* This is 3.23 or 4.x */ 01277 { 01278 if (uint4korr(buf + EVENT_LEN_OFFSET) < 01279 (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN)) 01280 { 01281 /* This is 3.23 (format 1) */ 01282 delete *description_event; 01283 *description_event= new Format_description_log_event(1); 01284 } 01285 break; 01286 } 01287 else if (tmp_pos >= start_position) 01288 break; 01289 else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */ 01290 { 01291 my_b_seek(file, tmp_pos); /* seek back to event's start */ 01292 if (!(*description_event= (Format_description_log_event*) 01293 Log_event::read_log_event(file, *description_event))) 01294 /* EOF can't be hit here normally, so it's a real error */ 01295 die("Could not read a Format_description_log_event event \ 01296 at offset %lu ; this could be a log format error or read error", 01297 tmp_pos); 01298 DBUG_PRINT("info",("Setting description_event")); 01299 } 01300 else if (buf[4] == ROTATE_EVENT) 01301 { 01302 Log_event *ev; 01303 my_b_seek(file, tmp_pos); /* seek back to event's start */ 01304 if (!(ev= Log_event::read_log_event(file, *description_event))) 01305 /* EOF can't be hit here normally, so it's a real error */ 01306 die("Could not read a Rotate_log_event event at offset %lu ;" 01307 " this could be a log format error or read error", tmp_pos); 01308 delete ev; 01309 } 01310 else 01311 break; 01312 } 01313 } 01314 my_b_seek(file, pos); 01315 } 01316 01317 01318 static int dump_local_log_entries(const char* logname) 01319 { 01320 File fd = -1; 01321 IO_CACHE cache,*file= &cache; 01322 PRINT_EVENT_INFO print_event_info; 01323 byte tmp_buff[BIN_LOG_HEADER_SIZE]; 01324 int error= 0; 01325 01326 if (logname && logname[0] != '-') 01327 { 01328 if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) 01329 return 1; 01330 if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0, 01331 MYF(MY_WME | MY_NABP))) 01332 { 01333 my_close(fd, MYF(MY_WME)); 01334 return 1; 01335 } 01336 check_header(file, &description_event); 01337 } 01338 else // reading from stdin; 01339 { 01340 if (init_io_cache(file, fileno(stdin), 0, READ_CACHE, (my_off_t) 0, 01341 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) 01342 return 1; 01343 check_header(file, &description_event); 01344 if (start_position) 01345 { 01346 /* skip 'start_position' characters from stdin */ 01347 byte buff[IO_SIZE]; 01348 my_off_t length,tmp; 01349 for (length= start_position_mot ; length > 0 ; length-=tmp) 01350 { 01351 tmp=min(length,sizeof(buff)); 01352 if (my_b_read(file, buff, (uint) tmp)) 01353 { 01354 error= 1; 01355 goto end; 01356 } 01357 } 01358 } 01359 } 01360 01361 if (!description_event || !description_event->is_valid()) 01362 die("Invalid Format_description log event; could be out of memory"); 01363 01364 if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) 01365 { 01366 error= 1; 01367 goto end; 01368 } 01369 for (;;) 01370 { 01371 char llbuff[21]; 01372 my_off_t old_off = my_b_tell(file); 01373 01374 Log_event* ev = Log_event::read_log_event(file, description_event); 01375 if (!ev) 01376 { 01377 /* 01378 if binlog wasn't closed properly ("in use" flag is set) don't complain 01379 about a corruption, but treat it as EOF and move to the next binlog. 01380 */ 01381 if (description_event->flags & LOG_EVENT_BINLOG_IN_USE_F) 01382 file->error= 0; 01383 else if (file->error) 01384 { 01385 fprintf(stderr, 01386 "Could not read entry at offset %s:" 01387 "Error in log format or read error\n", 01388 llstr(old_off,llbuff)); 01389 error= 1; 01390 } 01391 // file->error == 0 means EOF, that's OK, we break in this case 01392 break; 01393 } 01394 if ((error= process_event(&print_event_info, ev, old_off))) 01395 { 01396 if (error < 0) 01397 error= 0; 01398 break; 01399 } 01400 } 01401 01402 end: 01403 if (fd >= 0) 01404 my_close(fd, MYF(MY_WME)); 01405 end_io_cache(file); 01406 delete description_event; 01407 return error; 01408 } 01409 01410 01411 int main(int argc, char** argv) 01412 { 01413 static char **defaults_argv; 01414 int exit_value= 0; 01415 ulonglong save_stop_position; 01416 MY_INIT(argv[0]); 01417 DBUG_ENTER("main"); 01418 DBUG_PROCESS(argv[0]); 01419 01420 init_time(); // for time functions 01421 01422 parse_args(&argc, (char***)&argv); 01423 defaults_argv=argv; 01424 01425 if (!argc) 01426 { 01427 usage(); 01428 free_defaults(defaults_argv); 01429 exit(1); 01430 } 01431 01432 my_set_max_open_files(open_files_limit); 01433 01434 MY_TMPDIR tmpdir; 01435 tmpdir.list= 0; 01436 if (!dirname_for_local_load) 01437 { 01438 if (init_tmpdir(&tmpdir, 0)) 01439 exit(1); 01440 dirname_for_local_load= my_strdup(my_tmpdir(&tmpdir), MY_WME); 01441 } 01442 01443 if (load_processor.init()) 01444 exit(1); 01445 if (dirname_for_local_load) 01446 load_processor.init_by_dir_name(dirname_for_local_load); 01447 else 01448 load_processor.init_by_cur_dir(); 01449 01450 fprintf(result_file, 01451 "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n"); 01452 01453 if (disable_log_bin) 01454 fprintf(result_file, 01455 "/*!32316 SET @OLD_SQL_LOG_BIN=@@SQL_LOG_BIN, SQL_LOG_BIN=0*/;\n"); 01456 01457 /* 01458 In mysqlbinlog|mysql, don't want mysql to be disconnected after each 01459 transaction (which would be the case with GLOBAL.COMPLETION_TYPE==2). 01460 */ 01461 fprintf(result_file, 01462 "/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE," 01463 "COMPLETION_TYPE=0*/;\n"); 01464 01465 if (charset) 01466 fprintf(result_file, 01467 "\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" 01468 "\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" 01469 "\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" 01470 "\n/*!40101 SET NAMES %s */;\n", charset); 01471 01472 for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ; 01473 (--argc >= 0) && !stop_passed ; ) 01474 { 01475 if (argc == 0) // last log, --stop-position applies 01476 stop_position= save_stop_position; 01477 if (dump_log_entries(*(argv++))) 01478 { 01479 exit_value=1; 01480 break; 01481 } 01482 // For next log, --start-position does not apply 01483 start_position= BIN_LOG_HEADER_SIZE; 01484 } 01485 01486 /* 01487 Issue a ROLLBACK in case the last printed binlog was crashed and had half 01488 of transaction. 01489 */ 01490 fprintf(result_file, 01491 "# End of log file\nROLLBACK /* added by mysqlbinlog */;\n" 01492 "/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;\n"); 01493 if (disable_log_bin) 01494 fprintf(result_file, "/*!32316 SET SQL_LOG_BIN=@OLD_SQL_LOG_BIN*/;\n"); 01495 01496 if (charset) 01497 fprintf(result_file, 01498 "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n" 01499 "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n" 01500 "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); 01501 01502 if (tmpdir.list) 01503 free_tmpdir(&tmpdir); 01504 if (result_file != stdout) 01505 my_fclose(result_file, MYF(0)); 01506 cleanup(); 01507 free_defaults(defaults_argv); 01508 my_free_open_file_info(); 01509 /* We cannot free DBUG, it is used in global destructors after exit(). */ 01510 my_end(MY_DONT_FREE_DBUG); 01511 exit(exit_value); 01512 DBUG_RETURN(exit_value); // Keep compilers happy 01513 } 01514 01515 /* 01516 We must include this here as it's compiled with different options for 01517 the server 01518 */ 01519 01520 #if defined(__WIN__) && !defined(USING_CMAKE) 01521 #include "my_decimal.h" 01522 #include "decimal.c" 01523 #include "my_decimal.cpp" 01524 #include "log_event.cpp" 01525 #else 01526 #include "my_decimal.h" 01527 #include "decimal.c" 01528 #include "my_decimal.cc" 01529 #include "log_event.cc" 01530 #endif 01531
1.4.7

