mysqlbinlog.cc 80.1 KB
Newer Older
1
/*
Sergei Golubchik's avatar
Sergei Golubchik committed
2 3
   Copyright (c) 2000, 2012, Oracle and/or its affiliates.
   Copyright (c) 2009, 2012, Monty Program Ab
unknown's avatar
unknown committed
4

unknown's avatar
unknown committed
5 6
   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
unknown's avatar
unknown committed
7
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
8

unknown's avatar
unknown committed
9 10 11 12
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
unknown's avatar
unknown committed
13

unknown's avatar
unknown committed
14 15
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
16 17
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/
unknown's avatar
unknown committed
18

19 20 21 22
/* 

   TODO: print the catalog (some USE catalog.db ????).

23
   Standalone program to read a MySQL binary log (or relay log).
24 25 26 27 28 29 30 31

   Should be able to read any file of these categories, even with
   --start-position.
   An important fact: the Format_desc event of the log is at most the 3rd event
   of the log; if it is the 3rd then there is this combination:
   Format_desc_of_slave, Rotate_of_master, Format_desc_of_master.
*/

unknown's avatar
unknown committed
32 33
#define MYSQL_CLIENT
#undef MYSQL_SERVER
34
#define TABLE TABLE_CLIENT
unknown's avatar
unknown committed
35
#include "client_priv.h"
36
#include <my_time.h>
37
/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */
38
#include "sql_priv.h"
39
#include "log_event.h"
40
#include "sql_common.h"
unknown's avatar
unknown committed
41

42 43
#include "sql_string.h"   // needed for Rpl_filter
#include "sql_list.h"     // needed for Rpl_filter
44 45
#include "rpl_filter.h"

Sergei Golubchik's avatar
Sergei Golubchik committed
46 47
#include "mysqld.h"

48
Rpl_filter *binlog_filter= 0;
49

50
#define BIN_LOG_HEADER_SIZE	4
unknown's avatar
unknown committed
51
#define PROBE_HEADER_LEN	(EVENT_LEN_OFFSET+4)
52

53

unknown's avatar
unknown committed
54 55
#define CLIENT_CAPABILITIES	(CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)

Sergei Golubchik's avatar
Sergei Golubchik committed
56 57 58
/* Needed for Rpl_filter */
CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci;

unknown's avatar
unknown committed
59
char server_version[SERVER_VERSION_LENGTH];
60
ulong server_id = 0;
unknown's avatar
unknown committed
61 62 63 64

// needed by net_serv.c
ulong bytes_sent = 0L, bytes_received = 0L;
ulong mysqld_net_retry_count = 10L;
65
ulong open_files_limit;
unknown's avatar
unknown committed
66
uint test_flags = 0; 
67
static uint opt_protocol= 0;
unknown's avatar
unknown committed
68
static FILE *result_file;
unknown's avatar
unknown committed
69 70 71 72

#ifndef DBUG_OFF
static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace";
#endif
Sergei Golubchik's avatar
Sergei Golubchik committed
73
static const char *load_groups[]=
74
{ "mysqlbinlog", "client", "client-server", "client-mariadb", 0 };
unknown's avatar
unknown committed
75

76 77
static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
unknown's avatar
unknown committed
78

79
static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0;
80
static bool opt_hexdump= 0, opt_version= 0;
81 82
const char *base64_output_mode_names[]=
{"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS};
83 84 85 86
TYPELIB base64_output_mode_typelib=
  { array_elements(base64_output_mode_names) - 1, "",
    base64_output_mode_names, NULL };
static enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC;
87 88
static char *opt_base64_output_mode_str= NullS;
static char* database= 0;
89 90
static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
static my_bool debug_info_flag, debug_check_flag;
91
static my_bool force_if_open_opt= 1;
92
static my_bool opt_verify_binlog_checksum= 1;
93
static ulonglong offset = 0;
94
static char* host = 0;
95
static int port= 0;
96
static uint my_end_arg;
97
static const char* sock= 0;
Sergei Golubchik's avatar
Sergei Golubchik committed
98
static char *opt_plugindir= 0, *opt_default_auth= 0;
99

100 101 102
#ifdef HAVE_SMEM
static char *shared_memory_base_name= 0;
#endif
103
static char* user = 0;
104
static char* pass = 0;
105
static char *charset= 0;
106

107 108
static uint verbose= 0;

109 110 111 112 113 114 115
static ulonglong start_position, stop_position;
#define start_position_mot ((my_off_t)start_position)
#define stop_position_mot  ((my_off_t)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;
unknown's avatar
unknown committed
116 117
static short binlog_flags = 0; 
static MYSQL* mysql = NULL;
118
static const char* dirname_for_local_load= 0;
119
static bool opt_skip_annotate_row_events= 0;
120

121 122 123 124 125 126 127 128 129 130
/**
  Pointer to the Format_description_log_event of the currently active binlog.

  This will be changed each time a new Format_description_log_event is
  found in the binlog. It is finally destroyed at program termination.
*/
static Format_description_log_event* glob_description_event= NULL;

/**
  Exit status for functions in this file.
131
*/
132 133 134 135 136 137 138 139
enum Exit_status {
  /** No error occurred and execution should continue. */
  OK_CONTINUE= 0,
  /** An error occurred and execution should stop. */
  ERROR_STOP,
  /** No error occurred but execution should stop. */
  OK_STOP
};
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
/**
  Pointer to the last read Annotate_rows_log_event. Having read an
  Annotate_rows event, we should not print it immediatedly because all
  subsequent rbr events can be filtered away, and have to keep it for a while.
  Also because of that when reading a remote Annotate event we have to keep
  its binary log representation in a separately allocated buffer.
*/
static Annotate_rows_log_event *annotate_event= NULL;

void free_annotate_event()
{
  if (annotate_event)
  {
    delete annotate_event;
    annotate_event= 0;
  }
}

Log_event* read_remote_annotate_event(uchar* net_buf, ulong event_len,
                                      const char **error_msg)
{
  uchar *event_buf;
  Log_event* event;

  if (!(event_buf= (uchar*) my_malloc(event_len + 1, MYF(MY_WME))))
  {
    error("Out of memory");
    return 0;
  }

  memcpy(event_buf, net_buf, event_len);
  event_buf[event_len]= 0;

  if (!(event= Log_event::read_log_event((const char*) event_buf, event_len,
175 176
                                         error_msg, glob_description_event,
                                         opt_verify_binlog_checksum)))
177
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
178
    my_free(event_buf);
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
    return 0;
  }
  /*
    Ensure the event->temp_buf is pointing to the allocated buffer.
    (TRUE = free temp_buf on the event deletion)
  */
  event->register_temp_buf((char*)event_buf, TRUE);

  return event;
}

void keep_annotate_event(Annotate_rows_log_event* event)
{
  free_annotate_event();
  annotate_event= event;
}

void print_annotate_event(PRINT_EVENT_INFO *print_event_info)
{
  if (annotate_event)
  {
    annotate_event->print(result_file, print_event_info);
    delete annotate_event;  // the event should not be printed more than once
    annotate_event= 0;
  }
}

206 207 208 209 210 211
static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info,
                                          const char* logname);
static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
                                           const char* logname);
static Exit_status dump_log_entries(const char* logname);
static Exit_status safe_connect();
unknown's avatar
unknown committed
212

unknown's avatar
unknown committed
213

unknown's avatar
unknown committed
214 215
class Load_log_processor
{
216
  char target_dir_name[FN_REFLEN];
217
  size_t target_dir_name_len;
unknown's avatar
unknown committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

  /*
    When we see first event corresponding to some LOAD DATA statement in
    binlog, we create temporary file to store data to be loaded.
    We add name of this file to file_names array using its file_id as index.
    If we have Create_file event (i.e. we have binary log in pre-5.0.3
    format) we also store save event object to be able which is needed to
    emit LOAD DATA statement when we will meet Exec_load_data event.
    If we have Begin_load_query event we simply store 0 in
    File_name_record::event field.
  */
  struct File_name_record
  {
    char *fname;
    Create_file_log_event *event;
  };
234 235 236 237 238 239 240
  /*
    @todo Should be a map (e.g., a hash map), not an array.  With the
    present implementation, the number of elements in this array is
    about the number of files loaded since the server started, which
    may be big after a few years.  We should be able to use existing
    library data structures for this. /Sven
  */
241 242
  DYNAMIC_ARRAY file_names;

243 244 245 246 247 248 249 250 251 252 253 254 255 256
  /**
    Looks for a non-existing filename by adding a numerical suffix to
    the given base name, creates the generated file, and returns the
    filename by modifying the filename argument.

    @param[in,out] filename Base filename

    @param[in,out] file_name_end Pointer to last character of
    filename.  The numerical suffix will be written to this position.
    Note that there must be a least five bytes of allocated memory
    after file_name_end.

    @retval -1 Error (can't find new filename).
    @retval >=0 Found file.
257 258
  */
  File create_unique_file(char *filename, char *file_name_end)
259
    {
260 261 262 263 264 265 266 267 268 269
      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;
270 271 272
    }

public:
273
  Load_log_processor() {}
274
  ~Load_log_processor() {}
275 276 277

  int init()
  {
unknown's avatar
unknown committed
278
    return init_dynamic_array(&file_names, sizeof(File_name_record),
279
			      100, 100);
280
  }
281

unknown's avatar
unknown committed
282
  void init_by_dir_name(const char *dir)
283
    {
unknown's avatar
unknown committed
284 285
      target_dir_name_len= (convert_dirname(target_dir_name, dir, NullS) -
			    target_dir_name);
286 287 288
    }
  void init_by_cur_dir()
    {
289 290 291
      if (my_getwd(target_dir_name,sizeof(target_dir_name),MYF(MY_WME)))
	exit(1);
      target_dir_name_len= strlen(target_dir_name);
292 293
    }
  void destroy()
294 295 296 297
  {
    File_name_record *ptr= (File_name_record *)file_names.buffer;
    File_name_record *end= ptr + file_names.elements;
    for (; ptr < end; ptr++)
298
    {
299
      if (ptr->fname)
300
      {
301
        my_free(ptr->fname);
302 303
        delete ptr->event;
        bzero((char *)ptr, sizeof(File_name_record));
304 305
      }
    }
unknown's avatar
unknown committed
306

307 308 309
    delete_dynamic(&file_names);
  }

310 311 312
  /**
    Obtain Create_file event for LOAD DATA statement by its file_id
    and remove it from this Load_log_processor's list of events.
unknown's avatar
unknown committed
313

314 315 316 317 318
    Checks whether we have already seen a Create_file_log_event with
    the given file_id.  If yes, returns a pointer to the event and
    removes the event from array describing active temporary files.
    From this moment, the caller is responsible for freeing the memory
    occupied by the event.
unknown's avatar
unknown committed
319

320
    @param[in] file_id File id identifying LOAD DATA statement.
unknown's avatar
unknown committed
321

322 323
    @return Pointer to Create_file_log_event, or NULL if we have not
    seen any Create_file_log_event with this file_id.
unknown's avatar
unknown committed
324
  */
325 326
  Create_file_log_event *grab_event(uint file_id)
    {
unknown's avatar
unknown committed
327 328 329
      File_name_record *ptr;
      Create_file_log_event *res;

330 331
      if (file_id >= file_names.elements)
        return 0;
unknown's avatar
unknown committed
332 333 334 335 336 337
      ptr= dynamic_element(&file_names, file_id, File_name_record*);
      if ((res= ptr->event))
        bzero((char *)ptr, sizeof(File_name_record));
      return res;
    }

338 339 340 341 342 343 344 345 346 347 348 349 350 351
  /**
    Obtain file name of temporary file for LOAD DATA statement by its
    file_id and remove it from this Load_log_processor's list of events.

    @param[in] file_id Identifier for the LOAD DATA statement.

    Checks whether we have already seen Begin_load_query event for
    this file_id. If yes, returns the file name of the corresponding
    temporary file and removes the filename from the array of active
    temporary files.  From this moment, the caller is responsible for
    freeing the memory occupied by this name.

    @return String with the name of the temporary file, or NULL if we
    have not seen any Begin_load_query_event with this file_id.
unknown's avatar
unknown committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365
  */
  char *grab_fname(uint file_id)
    {
      File_name_record *ptr;
      char *res= 0;

      if (file_id >= file_names.elements)
        return 0;
      ptr= dynamic_element(&file_names, file_id, File_name_record*);
      if (!ptr->event)
      {
        res= ptr->fname;
        bzero((char *)ptr, sizeof(File_name_record));
      }
366 367
      return res;
    }
368 369 370
  Exit_status process(Create_file_log_event *ce);
  Exit_status process(Begin_load_query_log_event *ce);
  Exit_status process(Append_block_log_event *ae);
371
  File prepare_new_file_for_old_format(Load_log_event *le, char *filename);
372 373
  Exit_status load_old_format_file(NET* net, const char *server_fname,
                                   uint server_fname_len, File file);
374
  Exit_status process_first_event(const char *bname, size_t blen,
375
                                  const uchar *block,
376
                                  size_t block_len, uint file_id,
377
                                  Create_file_log_event *ce);
unknown's avatar
unknown committed
378 379
};

unknown's avatar
unknown committed
380

381 382 383 384 385 386 387
/**
  Creates and opens a new temporary file in the directory specified by previous call to init_by_dir_name() or init_by_cur_dir().

  @param[in] le The basename of the created file will start with the
  basename of the file pointed to by this Load_log_event.

  @param[out] filename Buffer to save the filename in.
unknown's avatar
unknown committed
388

389 390
  @return File handle >= 0 on success, -1 on error.
*/
391 392 393
File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le,
							 char *filename)
{
394
  size_t len;
395 396 397
  char *tail;
  File file;
  
398
  fn_format(filename, le->fname, target_dir_name, "", MY_REPLACE_DIR);
399 400 401 402 403
  len= strlen(filename);
  tail= filename + len;
  
  if ((file= create_unique_file(filename,tail)) < 0)
  {
404
    error("Could not construct local filename %s.",filename);
405 406 407
    return -1;
  }
  
408
  le->set_fname_outside_temp_buf(filename,len+(uint) strlen(tail));
409 410 411
  
  return file;
}
unknown's avatar
unknown committed
412

unknown's avatar
unknown committed
413

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
/**
  Reads a file from a server and saves it locally.

  @param[in,out] net The server to read from.

  @param[in] server_fname The name of the file that the server should
  read.

  @param[in] server_fname_len The length of server_fname.

  @param[in,out] file The file to write to.

  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
*/
Exit_status Load_log_processor::load_old_format_file(NET* net,
                                                     const char*server_fname,
                                                     uint server_fname_len,
                                                     File file)
433
{
434
  uchar buf[FN_REFLEN+1];
435 436 437 438
  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))
  {
439 440
    error("Failed requesting the remote dump of %s.", server_fname);
    return ERROR_STOP;
441 442 443 444
  }
  
  for (;;)
  {
445
    ulong packet_len = my_net_read(net);
446
    if (packet_len == 0)
447
    {
448
      if (my_net_write(net, (uchar*) "", 0) || net_flush(net))
449
      {
450 451
        error("Failed sending the ack packet.");
        return ERROR_STOP;
452
      }
453 454 455 456 457 458
      /*
	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;
459
    }
460 461
    else if (packet_len == packet_error)
    {
462 463
      error("Failed reading a packet during the dump of %s.", server_fname);
      return ERROR_STOP;
464 465
    }
    
466 467
    if (packet_len > UINT_MAX)
    {
468 469
      error("Illegal length of packet read from net.");
      return ERROR_STOP;
470
    }
471
    if (my_write(file, (uchar*) net->read_pos, 
472
		 (uint) packet_len, MYF(MY_WME|MY_NABP)))
473
      return ERROR_STOP;
474 475
  }
  
476
  return OK_CONTINUE;
477
}
478

unknown's avatar
unknown committed
479

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
/**
  Process the first event in the sequence of events representing a
  LOAD DATA statement.

  Creates a temporary file to be used in LOAD DATA and writes first
  block of data to it. Registers its file name (and optional
  Create_file event) in the array of active temporary files.

  @param bname Base name for temporary file to be created.
  @param blen Base name length.
  @param block First block of data to be loaded.
  @param block_len First block length.
  @param file_id Identifies the LOAD DATA statement.
  @param ce Pointer to Create_file event object if we are processing
  this type of event.

  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
unknown's avatar
unknown committed
498
*/
499
Exit_status Load_log_processor::process_first_event(const char *bname,
500
                                                    size_t blen,
501
                                                    const uchar *block,
502
                                                    size_t block_len,
503 504
                                                    uint file_id,
                                                    Create_file_log_event *ce)
unknown's avatar
unknown committed
505 506
{
  uint full_len= target_dir_name_len + blen + 9 + 9 + 1;
507
  Exit_status retval= OK_CONTINUE;
508 509
  char *fname, *ptr;
  File file;
unknown's avatar
unknown committed
510 511
  File_name_record rec;
  DBUG_ENTER("Load_log_processor::process_first_event");
unknown's avatar
unknown committed
512

513
  if (!(fname= (char*) my_malloc(full_len,MYF(MY_WME))))
514 515 516 517 518
  {
    error("Out of memory.");
    delete ce;
    DBUG_RETURN(ERROR_STOP);
  }
unknown's avatar
unknown committed
519

520 521
  memcpy(fname, target_dir_name, target_dir_name_len);
  ptr= fname + target_dir_name_len;
unknown's avatar
unknown committed
522 523
  memcpy(ptr,bname,blen);
  ptr+= blen;
Sergei Golubchik's avatar
Sergei Golubchik committed
524
  ptr+= sprintf(ptr, "-%x", file_id);
unknown's avatar
unknown committed
525

526 527
  if ((file= create_unique_file(fname,ptr)) < 0)
  {
528 529
    error("Could not construct local filename %s%s.",
          target_dir_name,bname);
530
    my_free(fname);
531 532
    delete ce;
    DBUG_RETURN(ERROR_STOP);
533
  }
unknown's avatar
unknown committed
534

unknown's avatar
unknown committed
535 536 537
  rec.fname= fname;
  rec.event= ce;

538 539 540 541 542
  /*
     fname is freed in process_event()
     after Execute_load_query_log_event or Execute_load_log_event
     will have been processed, otherwise in Load_log_processor::destroy()
  */
543
  if (set_dynamic(&file_names, (uchar*)&rec, file_id))
unknown's avatar
unknown committed
544
  {
545
    error("Out of memory.");
546
    my_free(fname);
547 548
    delete ce;
    DBUG_RETURN(ERROR_STOP);
unknown's avatar
unknown committed
549 550 551
  }

  if (ce)
552
    ce->set_fname_outside_temp_buf(fname, (uint) strlen(fname));
unknown's avatar
unknown committed
553

554
  if (my_write(file, (uchar*)block, block_len, MYF(MY_WME|MY_NABP)))
555 556 557 558
  {
    error("Failed writing to file.");
    retval= ERROR_STOP;
  }
unknown's avatar
unknown committed
559
  if (my_close(file, MYF(MY_WME)))
560 561 562 563 564
  {
    error("Failed closing file.");
    retval= ERROR_STOP;
  }
  DBUG_RETURN(retval);
565 566
}

unknown's avatar
unknown committed
567

568 569 570 571 572 573 574 575 576 577 578
/**
  Process the given Create_file_log_event.

  @see Load_log_processor::process_first_event(const char*,uint,const char*,uint,uint,Create_file_log_event*)

  @param ce Create_file_log_event to process.

  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
*/
Exit_status  Load_log_processor::process(Create_file_log_event *ce)
unknown's avatar
unknown committed
579 580 581 582 583 584 585 586 587
{
  const char *bname= ce->fname + dirname_length(ce->fname);
  uint blen= ce->fname_len - (bname-ce->fname);

  return process_first_event(bname, blen, ce->block, ce->block_len,
                             ce->file_id, ce);
}


588 589 590 591 592 593 594 595 596 597 598
/**
  Process the given Begin_load_query_log_event.

  @see Load_log_processor::process_first_event(const char*,uint,const char*,uint,uint,Create_file_log_event*)

  @param ce Begin_load_query_log_event to process.

  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
*/
Exit_status Load_log_processor::process(Begin_load_query_log_event *blqe)
unknown's avatar
unknown committed
599 600 601 602 603 604
{
  return process_first_event("SQL_LOAD_MB", 11, blqe->block, blqe->block_len,
                             blqe->file_id, 0);
}


605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
/**
  Process the given Append_block_log_event.

  Appends the chunk of the file contents specified by the event to the
  file created by a previous Begin_load_query_log_event or
  Create_file_log_event.

  If the file_id for the event does not correspond to any file
  previously registered through a Begin_load_query_log_event or
  Create_file_log_event, this member function will print a warning and
  return OK_CONTINUE.  It is safe to return OK_CONTINUE, because no
  query will be written for this event.  We should not print an error
  and fail, since the missing file_id could be because a (valid)
  --start-position has been specified after the Begin/Create event but
  before this Append event.

  @param ae Append_block_log_event to process.

  @retval ERROR_STOP An error occurred - the program should terminate.

  @retval OK_CONTINUE No error, the program should continue.
*/
Exit_status Load_log_processor::process(Append_block_log_event *ae)
unknown's avatar
unknown committed
628
{
unknown's avatar
unknown committed
629
  DBUG_ENTER("Load_log_processor::process");
unknown's avatar
unknown committed
630 631 632
  const char* fname= ((ae->file_id < file_names.elements) ?
                       dynamic_element(&file_names, ae->file_id,
                                       File_name_record*)->fname : 0);
unknown's avatar
unknown committed
633

unknown's avatar
unknown committed
634
  if (fname)
unknown's avatar
unknown committed
635 636
  {
    File file;
637
    Exit_status retval= OK_CONTINUE;
unknown's avatar
unknown committed
638
    if (((file= my_open(fname,
unknown's avatar
unknown committed
639
			O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0))
640 641 642 643
    {
      error("Failed opening file %s", fname);
      DBUG_RETURN(ERROR_STOP);
    }
644
    if (my_write(file,(uchar*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP)))
645 646 647 648
    {
      error("Failed writing to file %s", fname);
      retval= ERROR_STOP;
    }
unknown's avatar
unknown committed
649
    if (my_close(file,MYF(MY_WME)))
650 651 652 653 654
    {
      error("Failed closing file %s", fname);
      retval= ERROR_STOP;
    }
    DBUG_RETURN(retval);
unknown's avatar
unknown committed
655 656 657 658
  }

  /*
    There is no Create_file event (a bad binlog or a big
659 660
    --start-position). Assuming it's a big --start-position, we just do
    nothing and print a warning.
unknown's avatar
unknown committed
661
  */
662 663 664
  warning("Ignoring Append_block as there is no "
          "Create_file event for file_id: %u", ae->file_id);
  DBUG_RETURN(OK_CONTINUE);
unknown's avatar
unknown committed
665 666 667
}


668
static Load_log_processor load_processor;
669

670

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
/**
  Replace windows-style backslashes by forward slashes so it can be
  consumed by the mysql client, which requires Unix path.

  @todo This is only useful under windows, so may be ifdef'ed out on
  other systems.  /Sven

  @todo If a Create_file_log_event contains a filename with a
  backslash (valid under unix), then we have problems under windows.
  /Sven

  @param[in,out] fname Filename to modify. The filename is modified
  in-place.
*/
static void convert_path_to_forward_slashes(char *fname)
{
  while (*fname)
  {
    if (*fname == '\\')
      *fname= '/';
    fname++;
  }
}


696 697 698 699 700 701 702 703 704 705
/**
  Indicates whether the given database should be filtered out,
  according to the --database=X option.

  @param log_dbname Name of database.

  @return nonzero if the database with the given name should be
  filtered out, 0 otherwise.
*/
static bool shall_skip_database(const char *log_dbname)
unknown's avatar
unknown committed
706 707 708 709 710 711 712
{
  return one_database &&
         (log_dbname != NULL) &&
         strcmp(log_dbname, database);
}


713
/**
714
  Print "use <db>" statement when current db is to be changed.
715 716 717 718 719

  We have to control emiting USE statements according to rewrite-db options.
  We have to do it here (see process_event() below) and to suppress
  producing USE statements by corresponding log event print-functions.
*/
720

721 722
static void
print_use_stmt(PRINT_EVENT_INFO* pinfo, const Query_log_event *ev)
723
{
724 725 726
  const char* db= ev->db;
  const size_t db_len= ev->db_len;

727 728
  // pinfo->db is the current db.
  // If current db is the same as required db, do nothing.
729 730
  if ((ev->flags & LOG_EVENT_SUPPRESS_USE_F) || !db ||
      !memcmp(pinfo->db, db, db_len + 1))
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
    return;

  // Current db and required db are different.
  // Check for rewrite rule for required db. (Note that in a rewrite rule
  // neither db_from nor db_to part can be empty).
  size_t len_to= 0;
  const char *db_to= binlog_filter->get_rewrite_db(db, &len_to);

  // If there is no rewrite rule for db (in this case len_to is left = 0),
  // printing of the corresponding USE statement is left for log event
  // print-function.
  if (!len_to)
    return;

  // In case of rewrite rule print USE statement for db_to
unknown's avatar
unknown committed
746
  my_fprintf(result_file, "use %`s%s\n", db_to, pinfo->delimiter);
747 748 749 750 751 752 753

  // Copy the *original* db to pinfo to suppress emiting
  // of USE stmts by log_event print-functions.
  memcpy(pinfo->db, db, db_len + 1);
}


754
/**
755
   Print "SET skip_replication=..." statement when needed.
756 757 758 759 760 761 762

   Not all servers support this (only MariaDB from some version on). So we
   mark the SET to only execute from the version of MariaDB that supports it,
   and also only output it if we actually see events with the flag set, to not
   get spurious errors on MySQL@Oracle servers of higher version that do not
   support the flag.

763
   So we start out assuming @@skip_replication is 0, and only output a SET
764 765 766
   statement when it changes.
*/
static void
767
print_skip_replication_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev)
768 769 770
{
  int cur_val;

771 772
  cur_val= (ev->flags & LOG_EVENT_SKIP_REPLICATION_F) != 0;
  if (cur_val == pinfo->skip_replication)
773
    return;                                     /* Not changed. */
774
  fprintf(result_file, "/*!50521 SET skip_replication=%d*/%s\n",
775
          cur_val, pinfo->delimiter);
776
  pinfo->skip_replication= cur_val;
777 778
}

779 780 781 782 783 784 785 786 787 788 789 790
/**
  Prints the given event in base64 format.

  The header is printed to the head cache and the body is printed to
  the body cache of the print_event_info structure.  This allows all
  base64 events corresponding to the same statement to be joined into
  one BINLOG statement.

  @param[in] ev Log_event to print.
  @param[in,out] result_file FILE to which the output will be written.
  @param[in,out] print_event_info Parameters and context state
  determining how to print.
791

792 793 794 795
  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
*/
static Exit_status
796 797 798
write_event_header_and_base64(Log_event *ev, FILE *result_file,
                              PRINT_EVENT_INFO *print_event_info)
{
799 800
  IO_CACHE *head= &print_event_info->head_cache;
  IO_CACHE *body= &print_event_info->body_cache;
801 802
  DBUG_ENTER("write_event_header_and_base64");

803 804 805
  /* Write header and base64 output to cache */
  ev->print_header(head, print_event_info, FALSE);
  ev->print_base64(body, print_event_info, FALSE);
806 807

  /* Read data from cache and write to result file */
808 809 810 811 812 813 814
  if (copy_event_cache_to_file_and_reinit(head, result_file) ||
      copy_event_cache_to_file_and_reinit(body, result_file))
  {
    error("Error writing event to file.");
    DBUG_RETURN(ERROR_STOP);
  }
  DBUG_RETURN(OK_CONTINUE);
815 816 817
}


818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
/**
  Print the given event, and either delete it or delegate the deletion
  to someone else.

  The deletion may be delegated in two cases: (1) the event is a
  Format_description_log_event, and is saved in
  glob_description_event; (2) the event is a Create_file_log_event,
  and is saved in load_processor.

  @param[in,out] print_event_info Parameters and context state
  determining how to print.
  @param[in] ev Log_event to process.
  @param[in] pos Offset from beginning of binlog file.
  @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.
837
*/
838 839
Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
                          my_off_t pos, const char *logname)
840 841
{
  char ll_buff[21];
842
  Log_event_type ev_type= ev->get_type_code();
843
  my_bool destroy_evt= TRUE;
unknown's avatar
unknown committed
844
  DBUG_ENTER("process_event");
unknown's avatar
unknown committed
845
  print_event_info->short_form= short_form;
846
  Exit_status retval= OK_CONTINUE;
847
  IO_CACHE *const head= &print_event_info->head_cache;
unknown's avatar
unknown committed
848

849 850 851 852
  /*
    Format events are not concerned by --offset and such, we always need to
    read them to be able to process the wanted events.
  */
Staale Smedseng's avatar
Staale Smedseng committed
853
  if (((rec_count >= offset) &&
854
       (ev->when >= start_datetime)) ||
855
      (ev_type == FORMAT_DESCRIPTION_EVENT))
unknown's avatar
unknown committed
856
  {
857 858 859 860 861 862 863 864 865
    if (ev_type != FORMAT_DESCRIPTION_EVENT)
    {
      /*
        We have found an event after start_datetime, from now on print
        everything (in case the binlog has timestamps increasing and
        decreasing, we do this to avoid cutting the middle).
      */
      start_datetime= 0;
      offset= 0; // print everything and protect against cycling rec_count
866 867 868 869 870 871 872 873 874 875 876
      /*
        Skip events according to the --server-id flag.  However, don't
        skip format_description or rotate events, because they they
        are really "global" events that are relevant for the entire
        binlog, even if they have a server_id.  Also, we have to read
        the format_description event so that we can parse subsequent
        events.
      */
      if (ev_type != ROTATE_EVENT &&
          server_id && (server_id != ev->server_id))
        goto end;
877
    }
878
    if ((ev->when >= stop_datetime)
879 880
        || (pos >= stop_position_mot))
    {
881 882 883
      /* end the program */
      retval= OK_STOP;
      goto end;
884
    }
885 886
    if (!short_form)
      fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
887

unknown's avatar
unknown committed
888
    if (!opt_hexdump)
unknown's avatar
unknown committed
889
      print_event_info->hexdump_from= 0; /* Disabled */
unknown's avatar
unknown committed
890
    else
unknown's avatar
unknown committed
891
      print_event_info->hexdump_from= pos;
892

893
    print_event_info->base64_output_mode= opt_base64_output_mode;
894

895 896
    DBUG_PRINT("debug", ("event_type: %s", ev->get_type_str()));

897
    switch (ev_type) {
898
    case QUERY_EVENT:
899 900
    {
      Query_log_event *qe= (Query_log_event*)ev;
901 902 903 904 905 906 907 908 909 910 911 912 913
      if (!qe->is_trans_keyword())
      {
        if (shall_skip_database(qe->db))
          goto end;
      }
      else
      {
        /*
          In case the event for one of these statements is obtained
          from binary log 5.0, make it compatible with 5.1
        */
        qe->flags|= LOG_EVENT_SUPPRESS_USE_F;
      }
914
      print_use_stmt(print_event_info, qe);
915
      if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
916 917 918 919 920 921
      {
        if ((retval= write_event_header_and_base64(ev, result_file,
                                                   print_event_info)) !=
            OK_CONTINUE)
          goto end;
      }
922
      else
923
      {
924
        print_skip_replication_statement(print_event_info, ev);
925
        ev->print(result_file, print_event_info);
926
      }
927 928
      if (head->error == -1)
        goto err;
unknown's avatar
unknown committed
929
      break;
930
    }
931

932
    case CREATE_FILE_EVENT:
unknown's avatar
unknown committed
933
    {
934
      Create_file_log_event* ce= (Create_file_log_event*)ev;
unknown's avatar
unknown committed
935 936 937 938 939 940
      /*
        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.
      */
941
      if (shall_skip_database(ce->db))
unknown's avatar
unknown committed
942
        goto end;                // Next event
943 944 945 946 947 948 949
      /*
	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.
      */
950
      if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
951
      {
952 953 954 955
        if ((retval= write_event_header_and_base64(ce, result_file,
                                                   print_event_info)) !=
            OK_CONTINUE)
          goto end;
956 957
      }
      else
958
      {
959
        print_skip_replication_statement(print_event_info, ev);
960
        ce->print(result_file, print_event_info, TRUE);
961 962
        if (head->error == -1)
          goto err;
963
      }
964
      // If this binlog is not 3.23 ; why this test??
965
      if (glob_description_event->binlog_version >= 3)
966
      {
967 968 969 970 971 972 973
        /*
          transfer the responsibility for destroying the event to
          load_processor
        */
        ev= NULL;
        if ((retval= load_processor.process(ce)) != OK_CONTINUE)
          goto end;
974 975 976
      }
      break;
    }
977

978
    case APPEND_BLOCK_EVENT:
979 980 981 982 983
      /*
        Append_block_log_events can safely print themselves even if
        the subsequent call load_processor.process fails, because the
        output of Append_block_log_event::print is only a comment.
      */
unknown's avatar
unknown committed
984
      ev->print(result_file, print_event_info);
985 986
      if (head->error == -1)
        goto err;
987 988 989
      if ((retval= load_processor.process((Append_block_log_event*) ev)) !=
          OK_CONTINUE)
        goto end;
990
      break;
991

992 993
    case EXEC_LOAD_EVENT:
    {
unknown's avatar
unknown committed
994
      ev->print(result_file, print_event_info);
995 996
      if (head->error == -1)
        goto err;
997 998 999 1000
      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
1001
	event (a bad binlog, or most probably --start-position is after the
1002 1003 1004 1005
	Create_file event). Print a warning comment.
      */
      if (ce)
      {
1006 1007 1008 1009 1010
        /*
          We must not convert earlier, since the file is used by
          my_open() in Load_log_processor::append().
        */
        convert_path_to_forward_slashes((char*) ce->fname);
unknown's avatar
unknown committed
1011
	ce->print(result_file, print_event_info, TRUE);
1012
	my_free((void*)ce->fname);
1013
	delete ce;
1014 1015
        if (head->error == -1)
          goto err;
1016 1017
      }
      else
1018 1019
        warning("Ignoring Execute_load_log_event as there is no "
                "Create_file event for file_id: %u", exv->file_id);
1020 1021
      break;
    }
1022
    case FORMAT_DESCRIPTION_EVENT:
1023 1024
      delete glob_description_event;
      glob_description_event= (Format_description_log_event*) ev;
unknown's avatar
unknown committed
1025 1026
      print_event_info->common_header_len=
        glob_description_event->common_header_len;
unknown's avatar
unknown committed
1027
      ev->print(result_file, print_event_info);
1028 1029
      if (head->error == -1)
        goto err;
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
      if (!remote_opt)
      {
        ev->free_temp_buf(); // free memory allocated in dump_local_log_entries
      }
      else
      {
        /*
          disassociate but not free dump_remote_log_entries time memory
        */
        ev->temp_buf= 0;
      }
1041 1042 1043 1044 1045 1046 1047
      /*
        We don't want this event to be deleted now, so let's hide it (I
        (Guilhem) should later see if this triggers a non-serious Valgrind
        error). Not serious error, because we will free description_event
        later.
      */
      ev= 0;
1048
      if (!force_if_open_opt &&
unknown's avatar
unknown committed
1049
          (glob_description_event->flags & LOG_EVENT_BINLOG_IN_USE_F))
1050
      {
1051 1052 1053 1054
        error("Attempting to dump binlog '%s', which was not closed properly. "
              "Most probably, mysqld is still writing it, or it crashed. "
              "Rerun with --force-if-open to ignore this problem.", logname);
        DBUG_RETURN(ERROR_STOP);
1055
      }
1056
      break;
unknown's avatar
unknown committed
1057
    case BEGIN_LOAD_QUERY_EVENT:
unknown's avatar
unknown committed
1058
      ev->print(result_file, print_event_info);
1059 1060
      if (head->error == -1)
        goto err;
1061 1062 1063
      if ((retval= load_processor.process((Begin_load_query_log_event*) ev)) !=
          OK_CONTINUE)
        goto end;
unknown's avatar
unknown committed
1064 1065 1066 1067 1068 1069
      break;
    case EXECUTE_LOAD_QUERY_EVENT:
    {
      Execute_load_query_log_event *exlq= (Execute_load_query_log_event*)ev;
      char *fname= load_processor.grab_fname(exlq->file_id);

1070
      if (!shall_skip_database(exlq->db))
unknown's avatar
unknown committed
1071
      {
1072
        print_use_stmt(print_event_info, exlq);
unknown's avatar
unknown committed
1073
        if (fname)
1074 1075
        {
          convert_path_to_forward_slashes(fname);
1076
          print_skip_replication_statement(print_event_info, ev);
1077
          exlq->print(result_file, print_event_info, fname);
1078 1079 1080
          if (head->error == -1)
          {
            if (fname)
Sujatha Sivakumar's avatar
Sujatha Sivakumar committed
1081
              my_free(fname);
1082 1083
            goto err;
          }
1084 1085 1086 1087
        }
        else
          warning("Ignoring Execute_load_query since there is no "
                  "Begin_load_query event for file_id: %u", exlq->file_id);
unknown's avatar
unknown committed
1088 1089 1090
      }

      if (fname)
1091
	my_free(fname);
unknown's avatar
unknown committed
1092 1093
      break;
    }
1094
    case ANNOTATE_ROWS_EVENT:
1095
      if (!opt_skip_annotate_row_events)
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
      {
        /*
          We don't print Annotate event just now because all subsequent
          rbr-events can be filtered away. Instead we'll keep the event
          till it will be printed together with the first not filtered
          away Table map or the last rbr will be processed.
        */
        keep_annotate_event((Annotate_rows_log_event*) ev);
        destroy_evt= FALSE;
      }
      break;
1107
    case TABLE_MAP_EVENT:
1108 1109 1110 1111 1112 1113 1114 1115
    {
      Table_map_log_event *map= ((Table_map_log_event *)ev);
      if (shall_skip_database(map->get_db_name()))
      {
        print_event_info->m_table_map_ignored.set_table(map->get_table_id(), map);
        destroy_evt= FALSE;
        goto end;
      }
1116 1117 1118 1119 1120 1121 1122
      /*
        The Table map is to be printed, so it's just the time when we may
        print the kept Annotate event (if there is any).
        print_annotate_event() also deletes the kept Annotate event.
      */
      print_annotate_event(print_event_info);

1123 1124 1125 1126 1127 1128 1129
      size_t len_to= 0;
      const char* db_to= binlog_filter->get_rewrite_db(map->get_db_name(), &len_to);
      if (len_to && map->rewrite_db(db_to, len_to, glob_description_event))
      {
        error("Could not rewrite database name");
        goto err;
      }
1130
    }
1131 1132 1133 1134 1135 1136
    case WRITE_ROWS_EVENT:
    case DELETE_ROWS_EVENT:
    case UPDATE_ROWS_EVENT:
    case PRE_GA_WRITE_ROWS_EVENT:
    case PRE_GA_DELETE_ROWS_EVENT:
    case PRE_GA_UPDATE_ROWS_EVENT:
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
    {
      if (ev_type != TABLE_MAP_EVENT)
      {
        Rows_log_event *e= (Rows_log_event*) ev;
        Table_map_log_event *ignored_map= 
          print_event_info->m_table_map_ignored.get_table(e->get_table_id());
        bool skip_event= (ignored_map != NULL);

        /* 
           end of statement check:
             i) destroy/free ignored maps
            ii) if skip event, flush cache now
         */
        if (e->get_flags(Rows_log_event::STMT_END_F))
        {
          /* 
            Now is safe to clear ignored map (clear_tables will also
            delete original table map events stored in the map).
          */
          if (print_event_info->m_table_map_ignored.count() > 0)
            print_event_info->m_table_map_ignored.clear_tables();

1159 1160 1161 1162 1163 1164 1165
          /*
            If there is a kept Annotate event and all corresponding
            rbr-events were filtered away, the Annotate event was not
            freed and it is just the time to do it.
          */
          free_annotate_event();

1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
          /* 
             One needs to take into account an event that gets
             filtered but was last event in the statement. If this is
             the case, previous rows events that were written into
             IO_CACHEs still need to be copied from cache to
             result_file (as it would happen in ev->print(...) if
             event was not skipped).
          */
          if (skip_event)
          {
            if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, result_file) ||
                copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, result_file)))
              goto err;
          }
        }

        /* skip the event check */
        if (skip_event)
          goto end;
      }
1186 1187 1188 1189 1190 1191 1192
      /*
        These events must be printed in base64 format, if printed.
        base64 format requires a FD event to be safe, so if no FD
        event has been printed, we give an error.  Except if user
        passed --short-form, because --short-form disables printing
        row events.
      */
1193 1194
      if (!print_event_info->printed_fd_event && !short_form &&
          opt_base64_output_mode != BASE64_OUTPUT_DECODE_ROWS)
1195 1196 1197
      {
        const char* type_str= ev->get_type_str();
        if (opt_base64_output_mode == BASE64_OUTPUT_NEVER)
1198 1199 1200
          error("--base64-output=never specified, but binlog contains a "
                "%s event which must be printed in base64.",
                type_str);
1201
        else
1202 1203 1204 1205 1206 1207
          error("malformed binlog: it does not contain any "
                "Format_description_log_event. I now found a %s event, which "
                "is not safe to process without a "
                "Format_description_log_event.",
                type_str);
        goto err;
1208 1209
      }
      /* FALL THROUGH */
1210
    }
1211
    default:
1212
      print_skip_replication_statement(print_event_info, ev);
unknown's avatar
unknown committed
1213
      ev->print(result_file, print_event_info);
1214 1215
      if (head->error == -1)
        goto err;
unknown's avatar
unknown committed
1216 1217
    }
  }
unknown's avatar
unknown committed
1218

1219 1220 1221 1222
  goto end;

err:
  retval= ERROR_STOP;
unknown's avatar
unknown committed
1223
end:
1224
  rec_count++;
1225
  
1226
  /*
1227 1228 1229 1230 1231
    Destroy the log_event object. 
    MariaDB MWL#36: mainline does this:
      If reading from a remote host,
      set the temp_buf to NULL so that memory isn't freed twice.
    We no longer do that, we use Rpl_filter::event_owns_temp_buf instead.
1232
  */
1233
  if (ev)
1234
  {
1235 1236
    if (destroy_evt) /* destroy it later if not set (ignored table map) */
      delete ev;
1237
  }
1238
  DBUG_RETURN(retval);
unknown's avatar
unknown committed
1239 1240 1241
}


Sergei Golubchik's avatar
Sergei Golubchik committed
1242
static struct my_option my_options[] =
1243
{
unknown's avatar
unknown committed
1244 1245
  {"help", '?', "Display this help and exit.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1246
  {"base64-output", OPT_BASE64_OUTPUT_MODE,
1247
    /* 'unspec' is not mentioned because it is just a placeholder. */
1248 1249
   "Determine when the output statements should be base64-encoded BINLOG "
   "statements: 'never' disables it and works only for binlogs without "
1250 1251 1252
   "row-based events; 'decode-rows' decodes row events into commented SQL "
   "statements if the --verbose option is also given; 'auto' prints base64 "
   "only when necessary (i.e., for row-based events and format description "
1253 1254 1255 1256 1257
   "events); 'always' prints base64 whenever possible. 'always' is "
   "deprecated, will be removed in a future version, and should not be used "
   "in a production system.  --base64-output with no 'name' argument is "
   "equivalent to --base64-output=always and is also deprecated.  If no "
   "--base64-output[=name] option is given at all, the default is 'auto'.",
1258
   &opt_base64_output_mode_str, &opt_base64_output_mode_str,
1259
   0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1260 1261 1262 1263 1264 1265 1266
  /*
    mysqlbinlog needs charsets knowledge, to be able to convert a charset
    number found in binlog to a charset name (to be able to print things
    like this:
    SET @`a`:=_cp850 0x4DFC6C6C6572 COLLATE `cp850_general_ci`;
  */
  {"character-sets-dir", OPT_CHARSETS_DIR,
1267 1268
   "Directory for character set files.", &charsets_dir,
   &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
1269
  {"database", 'd', "List entries for just this database (local log only).",
1270
   &database, &database, 0, GET_STR_ALLOC, REQUIRED_ARG,
unknown's avatar
unknown committed
1271
   0, 0, 0, 0, 0, 0},
1272
#ifndef DBUG_OFF
1273 1274
  {"debug", '#', "Output debug log.", &default_dbug_option,
   &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1275
#endif
1276
  {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit .",
1277
   &debug_check_flag, &debug_check_flag, 0,
1278 1279
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
1280
   &debug_info_flag, &debug_info_flag,
1281
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1282 1283
  {"default_auth", OPT_DEFAULT_AUTH,
   "Default authentication client-side plugin to use.",
1284
   &opt_default_auth, &opt_default_auth, 0,
1285
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1286 1287 1288 1289 1290
  {"disable-log-bin", 'D', "Disable binary log. This is useful, if you "
    "enabled --to-last-log and are sending the output to the same MySQL server. "
    "This way you could avoid an endless loop. You would also like to use it "
    "when restoring after a crash to avoid duplication of the statements you "
    "already have. NOTE: you will need a SUPER privilege to use this option.",
1291
   &disable_log_bin, &disable_log_bin, 0, GET_BOOL,
1292
   NO_ARG, 0, 0, 0, 0, 0, 0},
1293
  {"force-if-open", 'F', "Force if binlog was not closed properly.",
1294
   &force_if_open_opt, &force_if_open_opt, 0, GET_BOOL, NO_ARG,
1295
   1, 0, 0, 0, 0, 0},
1296
  {"force-read", 'f', "Force reading unknown binlog events.",
1297
   &force_opt, &force_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
1298
   0, 0},
1299
  {"hexdump", 'H', "Augment output with hexadecimal and ASCII event dump.",
1300
   &opt_hexdump, &opt_hexdump, 0, GET_BOOL, NO_ARG,
1301
   0, 0, 0, 0, 0, 0},
1302
  {"host", 'h', "Get the binlog from server.", &host, &host,
1303
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
1304
  {"local-load", 'l', "Prepare local temporary files for LOAD DATA INFILE in the specified directory.",
1305
   &dirname_for_local_load, &dirname_for_local_load, 0,
unknown's avatar
unknown committed
1306
   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1307
  {"offset", 'o', "Skip the first N entries.", &offset, &offset,
1308
   0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1309
  {"password", 'p', "Password to connect to remote server.",
1310
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1311
  {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
Sergei Golubchik's avatar
Sergei Golubchik committed
1312
    &opt_plugindir, &opt_plugindir, 0,
1313
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1314 1315 1316 1317 1318 1319
  {"port", 'P', "Port number to use for connection or 0 for default to, in "
   "order of preference, my.cnf, $MYSQL_TCP_PORT, "
#if MYSQL_PORT_DEFAULT == 0
   "/etc/services, "
#endif
   "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
1320
   &port, &port, 0, GET_INT, REQUIRED_ARG,
1321
   0, 0, 0, 0, 0, 0},
1322
  {"protocol", OPT_MYSQL_PROTOCOL,
Staale Smedseng's avatar
Staale Smedseng committed
1323
   "The protocol to use for connection (tcp, socket, pipe, memory).",
1324
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
Staale Smedseng's avatar
Staale Smedseng committed
1325
  {"read-from-remote-server", 'R', "Read binary logs from a MySQL server.",
1326
   &remote_opt, &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
1327
   0, 0},
unknown's avatar
unknown committed
1328 1329
  {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1330
  {"server-id", 0,
unknown's avatar
unknown committed
1331
   "Extract only binlog entries created by the server having the given id.",
1332
   &server_id, &server_id, 0, GET_ULONG,
unknown's avatar
unknown committed
1333
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1334
  {"set-charset", OPT_SET_CHARSET,
1335 1336
   "Add 'SET NAMES character_set' to the output.", &charset,
   &charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1337 1338
#ifdef HAVE_SMEM
  {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
1339 1340
   "Base name of shared memory.", &shared_memory_base_name,
   &shared_memory_base_name,
1341 1342
   0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif
1343 1344 1345 1346
  {"short-form", 's', "Just show regular queries: no extra info and no "
   "row-based events. This is for testing only, and should not be used in "
   "production systems. If you want to suppress base64-output, consider "
   "using --base64-output=never instead.",
1347
   &short_form, &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
1348
   0, 0},
Staale Smedseng's avatar
Staale Smedseng committed
1349
  {"socket", 'S', "The socket file to use for connection.",
1350
   &sock, &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0,
unknown's avatar
unknown committed
1351
   0, 0},
1352 1353 1354 1355 1356 1357
  {"start-datetime", OPT_START_DATETIME,
   "Start reading the binlog at first event having a datetime equal or "
   "posterior to the argument; the argument must be a date and time "
   "in the local time zone, in any format accepted by the MySQL server "
   "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 "
   "(you should probably use quotes for your shell to set it properly).",
1358
   &start_datetime_str, &start_datetime_str,
1359
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1360
  {"start-position", 'j',
unknown's avatar
unknown committed
1361 1362
   "Start reading the binlog at position N. Applies to the first binlog "
   "passed on the command line.",
1363
   &start_position, &start_position, 0, GET_ULL,
unknown's avatar
unknown committed
1364 1365 1366
   REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE,
   /* COM_BINLOG_DUMP accepts only 4 bytes for the position */
   (ulonglong)(~(uint32)0), 0, 0, 0},
1367 1368 1369 1370 1371 1372
  {"stop-datetime", OPT_STOP_DATETIME,
   "Stop reading the binlog at first event having a datetime equal or "
   "posterior to the argument; the argument must be a date and time "
   "in the local time zone, in any format accepted by the MySQL server "
   "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 "
   "(you should probably use quotes for your shell to set it properly).",
1373
   &stop_datetime_str, &stop_datetime_str,
1374 1375 1376 1377
   0, GET_STR_ALLOC, 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.",
1378
   &stop_position, &stop_position, 0, GET_ULL,
1379
   REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
1380
   (ulonglong)(~(my_off_t)0), 0, 0, 0},
1381 1382
  {"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 \
1383
binlog of the MySQL server. If you send the output to the same MySQL server, \
1384
that may lead to an endless loop.",
1385
   &to_last_remote_log, &to_last_remote_log, 0, GET_BOOL,
1386
   NO_ARG, 0, 0, 0, 0, 0, 0},
1387
  {"user", 'u', "Connect to the remote server as username.",
1388
   &user, &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0,
1389
   0, 0},
1390
  {"verbose", 'v', "Reconstruct SQL statements out of row events. "
Staale Smedseng's avatar
Staale Smedseng committed
1391
                   "-v -v adds comments on column data types.",
1392
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1393 1394
  {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
unknown's avatar
unknown committed
1395
  {"open_files_limit", OPT_OPEN_FILES_LIMIT,
Staale Smedseng's avatar
Staale Smedseng committed
1396
   "Used to reserve file descriptors for use by this program.",
1397
   &open_files_limit, &open_files_limit, 0, GET_ULONG,
unknown's avatar
unknown committed
1398
   REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0},
1399 1400 1401
  {"verify-binlog-checksum", 'c', "Verify checksum binlog events.",
   (uchar**) &opt_verify_binlog_checksum, (uchar**) &opt_verify_binlog_checksum,
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1402
  {"rewrite-db", OPT_REWRITE_DB,
1403 1404
   "Updates to a database with a different name than the original. \
Example: rewrite-db='from->to'.",
1405
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1406
  {"skip-annotate-row-events", OPT_SKIP_ANNOTATE_ROWS_EVENTS,
1407
   "Don't print Annotate_rows events stored in the binary log.",
1408 1409
   (uchar**) &opt_skip_annotate_row_events,
   (uchar**) &opt_skip_annotate_row_events,
1410
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1411 1412 1413
  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};

unknown's avatar
unknown committed
1414

1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
/**
  Auxiliary function used by error() and warning().

  Prints the given text (normally "WARNING: " or "ERROR: "), followed
  by the given vprintf-style string, followed by a newline.

  @param format Printf-style format string.
  @param args List of arguments for the format string.
  @param msg Text to print before the string.
*/
static void error_or_warning(const char *format, va_list args, const char *msg)
unknown's avatar
unknown committed
1426
{
1427
  fprintf(stderr, "%s: ", msg);
unknown's avatar
unknown committed
1428 1429
  vfprintf(stderr, format, args);
  fprintf(stderr, "\n");
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
}

/**
  Prints a message to stderr, prefixed with the text "ERROR: " and
  suffixed with a newline.

  @param format Printf-style format string, followed by printf
  varargs.
*/
static void error(const char *format,...)
{
  va_list args;
  va_start(args, format);
  error_or_warning(format, args, "ERROR");
  va_end(args);
}


/**
  This function is used in log_event.cc to report errors.

  @param format Printf-style format string, followed by printf
  varargs.
*/
static void sql_print_error(const char *format,...)
{
  va_list args;
  va_start(args, format);
  error_or_warning(format, args, "ERROR");
  va_end(args);
}

/**
  Prints a message to stderr, prefixed with the text "WARNING: " and
  suffixed with a newline.

  @param format Printf-style format string, followed by printf
  varargs.
*/
static void warning(const char *format,...)
{
  va_list args;
  va_start(args, format);
  error_or_warning(format, args, "WARNING");
unknown's avatar
unknown committed
1474
  va_end(args);
unknown's avatar
unknown committed
1475
}
unknown's avatar
unknown committed
1476

1477 1478 1479
/**
  Frees memory for global variables in this file.
*/
1480 1481
static void cleanup()
{
1482 1483 1484 1485
  my_free(pass);
  my_free(database);
  my_free(host);
  my_free(user);
Sergei Golubchik's avatar
Sergei Golubchik committed
1486
  my_free(const_cast<char*>(dirname_for_local_load));
1487

1488
  delete binlog_filter;
1489 1490 1491
  delete glob_description_event;
  if (mysql)
    mysql_close(mysql);
unknown's avatar
unknown committed
1492 1493
}

unknown's avatar
unknown committed
1494

unknown's avatar
unknown committed
1495 1496
static void print_version()
{
1497
  printf("%s Ver 3.3 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
unknown's avatar
unknown committed
1498 1499 1500
}


unknown's avatar
unknown committed
1501 1502
static void usage()
{
unknown's avatar
unknown committed
1503
  print_version();
unknown's avatar
unknown committed
1504 1505
  puts("By Monty and Sasha, for your professional use\n\
This software comes with NO WARRANTY:  This is free software,\n\
Staale Smedseng's avatar
Staale Smedseng committed
1506
and you are welcome to modify and redistribute it under the GPL license.\n");
unknown's avatar
unknown committed
1507 1508

  printf("\
unknown's avatar
unknown committed
1509
Dumps a MySQL binary log in a format usable for viewing or for piping to\n\
Staale Smedseng's avatar
Staale Smedseng committed
1510
the mysql command line client.\n\n");
1511
  printf("Usage: %s [options] log-files\n", my_progname);
Sergei Golubchik's avatar
Sergei Golubchik committed
1512 1513
  my_print_help(my_options);
  my_print_variables(my_options);
unknown's avatar
unknown committed
1514 1515
}

1516 1517 1518 1519 1520 1521

static my_time_t convert_str_to_timestamp(const char* str)
{
  int was_cut;
  MYSQL_TIME l_time;
  long dummy_my_timezone;
1522
  uint dummy_in_dst_time_gap;
1523
  /* We require a total specification (date AND time) */
1524
  if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &was_cut) !=
1525 1526
      MYSQL_TIMESTAMP_DATETIME || was_cut)
  {
1527
    error("Incorrect date and time argument: %s", str);
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538
    exit(1);
  }
  /*
    Note that Feb 30th, Apr 31st cause no error messages and are mapped to
    the next existing day, like in mysqld. Maybe this could be changed when
    mysqld is changed too (with its "strict" mode?).
  */
  return
    my_system_gmt_sec(&l_time, &dummy_my_timezone, &dummy_in_dst_time_gap);
}

unknown's avatar
unknown committed
1539

1540
extern "C" my_bool
1541 1542 1543
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
1544
  bool tty_password=0;
1545
  switch (optid) {
unknown's avatar
unknown committed
1546
#ifndef DBUG_OFF
1547 1548 1549
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    break;
unknown's avatar
unknown committed
1550
#endif
1551 1552 1553 1554
  case 'd':
    one_database = 1;
    break;
  case 'p':
1555 1556
    if (argument == disabled_my_option)
      argument= (char*) "";                     // Don't require password
1557 1558
    if (argument)
    {
1559
      my_free(pass);
1560 1561 1562 1563 1564 1565 1566 1567
      char *start=argument;
      pass= my_strdup(argument,MYF(MY_FAE));
      while (*argument) *argument++= 'x';		/* Destroy argument */
      if (*start)
        start[1]=0;				/* Cut length of argument */
    }
    else
      tty_password=1;
1568 1569 1570 1571 1572
    break;
  case 'r':
    if (!(result_file = my_fopen(argument, O_WRONLY | O_BINARY, MYF(MY_WME))))
      exit(1);
    break;
1573 1574
  case 'R':
    remote_opt= 1;
1575
    break;
1576
  case OPT_MYSQL_PROTOCOL:
1577 1578
    opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
                                    opt->name);
1579
    break;
1580 1581 1582 1583 1584 1585
  case OPT_START_DATETIME:
    start_datetime= convert_str_to_timestamp(start_datetime_str);
    break;
  case OPT_STOP_DATETIME:
    stop_datetime= convert_str_to_timestamp(stop_datetime_str);
    break;
1586 1587 1588 1589 1590 1591 1592 1593 1594
  case OPT_BASE64_OUTPUT_MODE:
    if (argument == NULL)
      opt_base64_output_mode= BASE64_OUTPUT_ALWAYS;
    else
    {
      opt_base64_output_mode= (enum_base64_output_mode)
        (find_type_or_exit(argument, &base64_output_mode_typelib, opt->name)-1);
    }
    break;
1595 1596
  case OPT_REWRITE_DB:    // db_from->db_to
  {
1597
    /* See also handling of OPT_REPLICATE_REWRITE_DB in sql/mysqld.cc */
1598 1599 1600 1601 1602 1603 1604 1605 1606 1607
    char* ptr;
    char* key= argument;  // db-from
    char* val;            // db-to

    // Where key begins
    while (*key && my_isspace(&my_charset_latin1, *key))
      key++;

    // Where val begins
    if (!(ptr= strstr(argument, "->")))
1608 1609
    {
      sql_print_error("Bad syntax in rewrite-db: missing '->'!\n");
1610 1611 1612 1613 1614 1615
      return 1;
    }
    val= ptr + 2;
    while (*val && my_isspace(&my_charset_latin1, *val))
      val++;

1616
    // Write \0 and skip blanks at the end of key
1617 1618 1619 1620 1621
    *ptr-- = 0;
    while (my_isspace(&my_charset_latin1, *ptr) && ptr > argument)
      *ptr-- = 0;

    if (!*key)
1622 1623
    {
      sql_print_error("Bad syntax in rewrite-db: empty db-from!\n");
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
      return 1;
    }

    // Skip blanks at the end of val
    ptr= val;
    while (*ptr && !my_isspace(&my_charset_latin1, *ptr))
      ptr++;
    *ptr= 0;

    if (!*val)
1634 1635
    {
      sql_print_error("Bad syntax in rewrite-db: empty db-to!\n");
1636 1637 1638 1639 1640 1641
      return 1;
    }

    binlog_filter->add_db_rewrite(key, val);
    break;
  }
1642 1643 1644 1645 1646 1647
  case 'v':
    if (argument == disabled_my_option)
      verbose= 0;
    else
      verbose++;
    break;
1648 1649
  case 'V':
    print_version();
1650 1651
    opt_version= 1;
    break;
1652 1653
  case '?':
    usage();
1654 1655
    opt_version= 1;
    break;
unknown's avatar
unknown committed
1656
  }
1657 1658 1659
  if (tty_password)
    pass= get_tty_password(NullS);

1660 1661 1662
  return 0;
}

unknown's avatar
unknown committed
1663

1664 1665 1666
static int parse_args(int *argc, char*** argv)
{
  int ho_error;
unknown's avatar
unknown committed
1667

1668
  result_file = stdout;
Sergei Golubchik's avatar
Sergei Golubchik committed
1669
  if ((ho_error=handle_options(argc, argv, my_options, get_one_option)))
1670
    exit(ho_error);
1671 1672 1673 1674
  if (debug_info_flag)
    my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
  if (debug_check_flag)
    my_end_arg= MY_CHECK_ERROR;
unknown's avatar
unknown committed
1675 1676 1677
  return 0;
}

1678 1679 1680 1681 1682 1683 1684 1685 1686

/**
  Create and initialize the global mysql object, and connect to the
  server.

  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
*/
static Exit_status safe_connect()
unknown's avatar
unknown committed
1687
{
1688
  /* Close any old connections to MySQL */
1689 1690 1691
  if (mysql)
    mysql_close(mysql);

1692
  mysql= mysql_init(NULL);
1693

1694 1695 1696 1697 1698
  if (!mysql)
  {
    error("Failed on mysql_init.");
    return ERROR_STOP;
  }
unknown's avatar
unknown committed
1699

Sergei Golubchik's avatar
Sergei Golubchik committed
1700 1701
  if (opt_plugindir && *opt_plugindir)
    mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugindir);
1702 1703 1704 1705

  if (opt_default_auth && *opt_default_auth)
    mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);

1706
  if (opt_protocol)
1707
    mysql_options(mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol);
1708 1709 1710 1711 1712
#ifdef HAVE_SMEM
  if (shared_memory_base_name)
    mysql_options(mysql, MYSQL_SHARED_MEMORY_BASE_NAME,
                  shared_memory_base_name);
#endif
1713
  if (!mysql_real_connect(mysql, host, user, pass, 0, port, sock, 0))
unknown's avatar
unknown committed
1714
  {
1715 1716
    error("Failed on connect: %s", mysql_error(mysql));
    return ERROR_STOP;
unknown's avatar
unknown committed
1717
  }
1718 1719
  mysql->reconnect= 1;
  return OK_CONTINUE;
unknown's avatar
unknown committed
1720 1721
}

unknown's avatar
unknown committed
1722

1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736
/**
  High-level function for dumping a named binlog.

  This function calls dump_remote_log_entries() or
  dump_local_log_entries() to do the job.

  @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_log_entries(const char* logname)
unknown's avatar
unknown committed
1737
{
1738
  Exit_status rc;
1739
  PRINT_EVENT_INFO print_event_info;
1740 1741

  if (!print_event_info.init_ok())
1742
    return ERROR_STOP;
1743 1744 1745 1746 1747
  /*
     Set safe delimiter, to dump things
     like CREATE PROCEDURE safely
  */
  fprintf(result_file, "DELIMITER /*!*/;\n");
1748
  strmov(print_event_info.delimiter, "/*!*/;");
1749 1750
  
  print_event_info.verbose= short_form ? 0 : verbose;
1751 1752 1753 1754 1755 1756

  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");
1757
  strmov(print_event_info.delimiter, ";");
1758
  return rc;
unknown's avatar
unknown committed
1759 1760
}

unknown's avatar
unknown committed
1761

1762 1763 1764 1765 1766 1767 1768 1769 1770
/**
  When reading a remote binlog, this function is used to grab the
  Format_description_log_event in the beginning of the stream.
  
  This is not as smart as check_header() (used for local log); it will
  not work for a binlog which mixes format. TODO: fix this.

  @retval ERROR_STOP An error occurred - the program should terminate.
  @retval OK_CONTINUE No error, the program should continue.
1771
*/
1772
static Exit_status check_master_version()
1773 1774 1775
{
  MYSQL_RES* res = 0;
  MYSQL_ROW row;
1776
  uint version;
unknown's avatar
unknown committed
1777

1778 1779
  if (mysql_query(mysql, "SELECT VERSION()") ||
      !(res = mysql_store_result(mysql)))
unknown's avatar
unknown committed
1780
  {
1781 1782 1783
    error("Could not find server version: "
          "Query failed when checking master version: %s", mysql_error(mysql));
    return ERROR_STOP;
unknown's avatar
unknown committed
1784
  }
1785 1786
  if (!(row = mysql_fetch_row(res)))
  {
1787 1788 1789
    error("Could not find server version: "
          "Master returned no rows for SELECT VERSION().");
    goto err;
1790
  }
1791

1792
  if (!(version = atoi(row[0])))
1793
  {
1794 1795 1796
    error("Could not find server version: "
          "Master reported NULL for the version.");
    goto err;
1797
  }
1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809
  /* 
     Make a notice to the server that this client
     is checksum-aware. It does not need the first fake Rotate
     necessary checksummed. 
     That preference is specified below.
  */
  if (mysql_query(mysql, "SET @master_binlog_checksum='NONE'"))
  {
    error("Could not notify master about checksum awareness."
          "Master returned '%s'", mysql_error(mysql));
    goto err;
  }
1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822

  /*
    Announce our capabilities to the server, so it will send us all the events
    that we know about.
  */
  if (mysql_query(mysql, "SET @mariadb_slave_capability="
                  STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_MINE)))
  {
    error("Could not inform master about capability. Master returned '%s'",
          mysql_error(mysql));
    goto err;
  }

1823
  delete glob_description_event;
1824 1825
  switch (version) {
  case 3:
1826
    glob_description_event= new Format_description_log_event(1);
1827
    break;
1828
  case 4:
1829
    glob_description_event= new Format_description_log_event(3);
1830
    break;
1831 1832
  case 5:
  case 10:
1833 1834 1835 1836 1837 1838
    /*
      The server is soon going to send us its Format_description log
      event, unless it is a 5.0 server with 3.23 or 4.0 binlogs.
      So we first assume that this is 4.0 (which is enough to read the
      Format_desc event if one comes).
    */
1839
    glob_description_event= new Format_description_log_event(3);
1840 1841
    break;
  default:
1842 1843
    glob_description_event= NULL;
    error("Could not find server version: "
1844
          "Master reported unrecognized MySQL version '%s'.", row[0]);
1845
    goto err;
1846
  }
1847 1848 1849 1850 1851 1852
  if (!glob_description_event || !glob_description_event->is_valid())
  {
    error("Failed creating Format_description_log_event; out of memory?");
    goto err;
  }

1853
  mysql_free_result(res);
1854 1855 1856 1857 1858
  return OK_CONTINUE;

err:
  mysql_free_result(res);
  return ERROR_STOP;
1859
}
unknown's avatar
unknown committed
1860

1861

1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876
/**
  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)
1877

unknown's avatar
unknown committed
1878
{
1879
  uchar buf[128];
unknown's avatar
unknown committed
1880 1881
  ulong len;
  uint logname_len;
1882 1883 1884
  NET* net;
  my_off_t old_off= start_position_mot;
  char fname[FN_REFLEN+1];
1885
  Exit_status retval= OK_CONTINUE;
unknown's avatar
unknown committed
1886 1887
  DBUG_ENTER("dump_remote_log_entries");

1888 1889 1890 1891 1892
  /*
    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).
  */
1893 1894
  if ((retval= safe_connect()) != OK_CONTINUE)
    DBUG_RETURN(retval);
1895
  net= &mysql->net;
1896

1897 1898
  if ((retval= check_master_version()) != OK_CONTINUE)
    DBUG_RETURN(retval);
unknown's avatar
unknown committed
1899

1900 1901 1902 1903 1904
  /*
    COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
    cast to uint32.
  */
  int4store(buf, (uint32)start_position);
1905
  if (!opt_skip_annotate_row_events)
1906
    binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT;
1907
  int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
1908

1909
  size_t tlen = strlen(logname);
1910 1911
  if (tlen > UINT_MAX) 
  {
1912 1913
    error("Log name too long.");
    DBUG_RETURN(ERROR_STOP);
1914 1915
  }
  logname_len = (uint) tlen;
1916
  int4store(buf + 6, 0);
1917 1918
  memcpy(buf + 10, logname, logname_len);
  if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1))
unknown's avatar
unknown committed
1919
  {
1920 1921
    error("Got fatal error sending the log dump command.");
    DBUG_RETURN(ERROR_STOP);
unknown's avatar
unknown committed
1922
  }
unknown's avatar
unknown committed
1923

1924
  for (;;)
unknown's avatar
unknown committed
1925
  {
1926
    const char *error_msg;
1927 1928
    Log_event *ev;

1929
    len= cli_safe_read(mysql);
unknown's avatar
unknown committed
1930
    if (len == packet_error)
unknown's avatar
unknown committed
1931
    {
1932 1933
      error("Got error reading packet from server: %s", mysql_error(mysql));
      DBUG_RETURN(ERROR_STOP);
unknown's avatar
unknown committed
1934
    }
unknown's avatar
unknown committed
1935
    if (len < 8 && net->read_pos[0] == 254)
unknown's avatar
unknown committed
1936
      break; // end of data
unknown's avatar
unknown committed
1937
    DBUG_PRINT("info",( "len: %lu  net->read_pos[5]: %d\n",
unknown's avatar
unknown committed
1938
			len, net->read_pos[5]));
1939
    if (net->read_pos[5] == ANNOTATE_ROWS_EVENT)
unknown's avatar
unknown committed
1940
    {
1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951
      if (!(ev= read_remote_annotate_event(net->read_pos + 1, len - 1,
                                           &error_msg)))
      {
        error("Could not construct annotate event object: %s", error_msg);
        DBUG_RETURN(ERROR_STOP);
      }   
    }
    else
    {
      if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 ,
                                          len - 1, &error_msg,
1952 1953
                                          glob_description_event,
                                          opt_verify_binlog_checksum)))
1954 1955 1956 1957 1958 1959 1960 1961 1962 1963
      {
        error("Could not construct log event object: %s", 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 *) net->read_pos + 1, FALSE);
    }
unknown's avatar
unknown committed
1964 1965

    Log_event_type type= ev->get_type_code();
1966
    if (glob_description_event->binlog_version >= 3 ||
1967
        (type != LOAD_EVENT && type != CREATE_FILE_EVENT))
unknown's avatar
unknown committed
1968
    {
1969 1970 1971 1972 1973 1974 1975 1976 1977 1978
      /*
        If this is a Rotate event, maybe it's the end of the requested binlog;
        in this case we are done (stop transfer).
        This is suitable for binlogs, not relay logs (but for now we don't read
        relay logs remotely because the server is not able to do that). If one
        day we read relay logs remotely, then we will have a problem with the
        detection below: relay logs contain Rotate events which are about the
        binlogs, so which would trigger the end-detection below.
      */
      if (type == ROTATE_EVENT)
1979 1980 1981 1982 1983 1984 1985 1986 1987
      {
        Rotate_log_event *rev= (Rotate_log_event *)ev;
        /*
          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.
        */
1988
        if (rev->when == 0)
1989
        {
1990 1991 1992 1993 1994
          if (!to_last_remote_log)
          {
            if ((rev->ident_len != logname_len) ||
                memcmp(rev->new_log_ident, logname, logname_len))
            {
1995
              DBUG_RETURN(OK_CONTINUE);
1996 1997 1998 1999 2000 2001 2002 2003 2004 2005
            }
            /*
              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.
            */
            continue;
          }
          len= 1; // fake Rotate, so don't increment old_off
2006 2007
        }
      }
2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
      else if (type == FORMAT_DESCRIPTION_EVENT)
      {
        /*
          This could be an fake Format_description_log_event that server
          (5.0+) automatically sends to a slave on connect, before sending
          a first event at the requested position.  If this is the case,
          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
      }
2020 2021 2022
      Exit_status retval= process_event(print_event_info, ev, old_off, logname);
      if (retval != OK_CONTINUE)
        DBUG_RETURN(retval);
unknown's avatar
unknown committed
2023 2024
    }
    else
2025
    {
unknown's avatar
unknown committed
2026 2027 2028 2029
      Load_log_event *le= (Load_log_event*)ev;
      const char *old_fname= le->fname;
      uint old_len= le->fname_len;
      File file;
2030
      Exit_status retval;
2031

unknown's avatar
unknown committed
2032
      if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
2033
        DBUG_RETURN(ERROR_STOP);
2034

2035 2036
      retval= process_event(print_event_info, ev, old_off, logname);
      if (retval != OK_CONTINUE)
2037
      {
2038
        my_close(file,MYF(MY_WME));
2039
        DBUG_RETURN(retval);
2040
      }
2041
      retval= load_processor.load_old_format_file(net,old_fname,old_len,file);
2042
      my_close(file,MYF(MY_WME));
2043 2044
      if (retval != OK_CONTINUE)
        DBUG_RETURN(retval);
unknown's avatar
unknown committed
2045
    }
2046
    /*
2047 2048
      Let's adjust offset for remote log as for local log to produce
      similar text and to have --stop-position to work identically.
2049
    */
2050
    old_off+= len-1;
unknown's avatar
unknown committed
2051
  }
2052

2053
  DBUG_RETURN(OK_CONTINUE);
unknown's avatar
unknown committed
2054 2055
}

2056

2057
/**
2058 2059
  Reads the @c Format_description_log_event from the beginning of a
  local input file.
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072

  The @c Format_description_log_event is only read if it is outside
  the range specified with @c --start-position; otherwise, it will be
  seen later.  If this is an old binlog, a fake @c
  Format_description_event is created.  This also prints a @c
  Format_description_log_event to the output, unless we reach the
  --start-position range.  In this case, it is assumed that a @c
  Format_description_log_event will be found when reading events the
  usual way.

  @param file The file to which a @c Format_description_log_event will
  be printed.

2073 2074 2075 2076
  @param[in,out] print_event_info Parameters and context state
  determining how to print.

  @param[in] logname Name of input binlog.
2077

2078 2079 2080 2081
  @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.
2082
*/
2083 2084 2085
static Exit_status check_header(IO_CACHE* file,
                                PRINT_EVENT_INFO *print_event_info,
                                const char* logname)
2086
{
2087 2088
  uchar header[BIN_LOG_HEADER_SIZE];
  uchar buf[PROBE_HEADER_LEN];
2089
  my_off_t tmp_pos, pos;
unknown's avatar
unknown committed
2090

2091 2092 2093 2094 2095 2096 2097
  delete glob_description_event;
  if (!(glob_description_event= new Format_description_log_event(3)))
  {
    error("Failed creating Format_description_log_event; out of memory?");
    return ERROR_STOP;
  }

2098
  pos= my_b_tell(file);
2099
  my_b_seek(file, (my_off_t)0);
unknown's avatar
unknown committed
2100
  if (my_b_read(file, header, sizeof(header)))
2101
  {
2102 2103
    error("Failed reading header; probably an empty file.");
    return ERROR_STOP;
2104
  }
unknown's avatar
unknown committed
2105
  if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
2106
  {
2107 2108
    error("File is not a binary log file.");
    return ERROR_STOP;
2109
  }
2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124

  /*
    Imagine we are running with --start-position=1000. We still need
    to know the binlog format's. So we still need to find, if there is
    one, the Format_desc event, or to know if this is a 3.23
    binlog. So we need to first read the first events of the log,
    those around offset 4.  Even if we are reading a 3.23 binlog from
    the start (no --start-position): we need to know the header length
    (which is 13 in 3.23, 19 in 4.x) to be able to successfully print
    the first event (Start_log_event_v3). So even in this case, we
    need to "probe" the first bytes of the log *before* we do a real
    read_log_event(). Because read_log_event() needs to know the
    header's length to work fine.
  */
  for(;;)
2125
  {
2126 2127 2128 2129
    tmp_pos= my_b_tell(file); /* should be 4 the first time */
    if (my_b_read(file, buf, sizeof(buf)))
    {
      if (file->error)
2130
      {
2131 2132 2133
        error("Could not read entry at offset %llu: "
              "Error in log format or read error.", (ulonglong)tmp_pos);
        return ERROR_STOP;
2134
      }
2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151
      /*
        Otherwise this is just EOF : this log currently contains 0-2
        events.  Maybe it's going to be filled in the next
        milliseconds; then we are going to have a problem if this a
        3.23 log (imagine we are locally reading a 3.23 binlog which
        is being written presently): we won't know it in
        read_log_event() and will fail().  Similar problems could
        happen with hot relay logs if --start-position is used (but a
        --start-position which is posterior to the current size of the log).
        These are rare problems anyway (reading a hot log + when we
        read the first events there are not all there yet + when we
        read a bit later there are more events + using a strange
        --start-position).
      */
      break;
    }
    else
unknown's avatar
unknown committed
2152
    {
2153 2154
      DBUG_PRINT("info",("buf[EVENT_TYPE_OFFSET=%d]=%d",
                         EVENT_TYPE_OFFSET, buf[EVENT_TYPE_OFFSET]));
2155
      /* always test for a Start_v3, even if no --start-position */
2156
      if (buf[EVENT_TYPE_OFFSET] == START_EVENT_V3)
2157
      {
2158
        /* This is 3.23 or 4.x */
2159 2160 2161 2162
        if (uint4korr(buf + EVENT_LEN_OFFSET) < 
            (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN))
        {
          /* This is 3.23 (format 1) */
2163 2164 2165 2166 2167 2168 2169
          delete glob_description_event;
          if (!(glob_description_event= new Format_description_log_event(1)))
          {
            error("Failed creating Format_description_log_event; "
                  "out of memory?");
            return ERROR_STOP;
          }
2170 2171 2172 2173 2174
        }
        break;
      }
      else if (tmp_pos >= start_position)
        break;
2175
      else if (buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT)
2176
      {
2177
        /* This is 5.0 */
2178
        Format_description_log_event *new_description_event;
2179
        my_b_seek(file, tmp_pos); /* seek back to event's start */
2180
        if (!(new_description_event= (Format_description_log_event*) 
2181 2182
              Log_event::read_log_event(file, glob_description_event,
                                        opt_verify_binlog_checksum)))
2183
          /* EOF can't be hit here normally, so it's a real error */
2184
        {
2185 2186 2187 2188
          error("Could not read a Format_description_log_event event at "
                "offset %llu; this could be a log format error or read error.",
                (ulonglong)tmp_pos);
          return ERROR_STOP;
2189
        }
2190 2191
        if (opt_base64_output_mode == BASE64_OUTPUT_AUTO
            || opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
2192
        {
2193 2194 2195 2196 2197
          /*
            process_event will delete *description_event and set it to
            the new one, so we should not do it ourselves in this
            case.
          */
2198 2199 2200 2201 2202 2203
          Exit_status retval= process_event(print_event_info,
                                            new_description_event, tmp_pos,
                                            logname);
          if (retval != OK_CONTINUE)
            return retval;
        }
2204 2205
        else
        {
2206 2207
          delete glob_description_event;
          glob_description_event= new_description_event;
2208
        }
2209 2210
        DBUG_PRINT("info",("Setting description_event"));
      }
2211
      else if (buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT)
2212
      {
2213
        Log_event *ev;
2214
        my_b_seek(file, tmp_pos); /* seek back to event's start */
2215 2216
        if (!(ev= Log_event::read_log_event(file, glob_description_event,
                                            opt_verify_binlog_checksum)))
2217
        {
2218 2219 2220 2221 2222
          /* EOF can't be hit here normally, so it's a real error */
          error("Could not read a Rotate_log_event event at offset %llu;"
                " this could be a log format error or read error.",
                (ulonglong)tmp_pos);
          return ERROR_STOP;
2223
        }
2224
        delete ev;
2225 2226 2227
      }
      else
        break;
unknown's avatar
unknown committed
2228
    }
2229 2230
  }
  my_b_seek(file, pos);
2231
  return OK_CONTINUE;
2232 2233
}

unknown's avatar
unknown committed
2234

2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249
/**
  Reads a local binlog and prints the events it sees.

  @param[in] logname Name of input binlog.

  @param[in,out] print_event_info Parameters and context state
  determining how to print.

  @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_local_log_entries(PRINT_EVENT_INFO *print_event_info,
                                          const char* logname)
unknown's avatar
unknown committed
2250
{
unknown's avatar
unknown committed
2251
  File fd = -1;
2252
  IO_CACHE cache,*file= &cache;
2253
  uchar tmp_buff[BIN_LOG_HEADER_SIZE];
2254
  Exit_status retval= OK_CONTINUE;
2255

2256
  if (logname && strcmp(logname, "-") != 0)
2257
  {
2258
    /* read from normal file */
2259
    if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
2260
      return ERROR_STOP;
2261
    if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0,
2262
		      MYF(MY_WME | MY_NABP)))
unknown's avatar
unknown committed
2263 2264
    {
      my_close(fd, MYF(MY_WME));
2265
      return ERROR_STOP;
unknown's avatar
unknown committed
2266
    }
2267 2268
    if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE)
      goto end;
2269
  }
2270
  else
2271
  {
2272
    /* read from stdin */
2273
    /*
2274 2275 2276 2277 2278 2279
      Windows opens stdin in text mode by default. Certain characters
      such as CTRL-Z are interpeted as events and the read() method
      will stop. CTRL-Z is the EOF marker in Windows. to get past this
      you have to open stdin in binary mode. Setmode() is used to set
      stdin in binary mode. Errors on setting this mode result in 
      halting the function and printing an error message to stderr.
2280 2281 2282 2283
    */
#if defined (__WIN__) || (_WIN64)
    if (_setmode(fileno(stdin), O_BINARY) == -1)
    {
2284 2285
      error("Could not set binary mode on stdin.");
      return ERROR_STOP;
2286 2287
    }
#endif 
2288
    if (init_io_cache(file, my_fileno(stdin), 0, READ_CACHE, (my_off_t) 0,
2289
		      0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
2290 2291 2292 2293 2294 2295
    {
      error("Failed to init IO cache.");
      return ERROR_STOP;
    }
    if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE)
      goto end;
2296
    if (start_position)
2297
    {
2298
      /* skip 'start_position' characters from stdin */
2299
      uchar buff[IO_SIZE];
2300
      my_off_t length,tmp;
2301
      for (length= start_position_mot ; length > 0 ; length-=tmp)
2302 2303
      {
	tmp=min(length,sizeof(buff));
2304
	if (my_b_read(file, buff, (uint) tmp))
2305
        {
2306 2307
          error("Failed reading from file.");
          goto err;
2308
        }
2309 2310 2311 2312
      }
    }
  }

2313
  if (!glob_description_event || !glob_description_event->is_valid())
2314
  {
2315 2316
    error("Invalid Format_description log event; could be out of memory.");
    goto err;
2317
  }
2318 2319

  if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
unknown's avatar
unknown committed
2320
  {
2321 2322
    error("Failed reading from file.");
    goto err;
unknown's avatar
unknown committed
2323
  }
2324
  for (;;)
2325
  {
2326
    char llbuff[21];
unknown's avatar
unknown committed
2327 2328
    my_off_t old_off = my_b_tell(file);

2329 2330
    Log_event* ev = Log_event::read_log_event(file, glob_description_event,
                                              opt_verify_binlog_checksum);
2331 2332
    if (!ev)
    {
2333 2334
      /*
        if binlog wasn't closed properly ("in use" flag is set) don't complain
2335
        about a corruption, but treat it as EOF and move to the next binlog.
2336
      */
2337
      if (glob_description_event->flags & LOG_EVENT_BINLOG_IN_USE_F)
2338
        file->error= 0;
2339
      else if (file->error)
unknown's avatar
unknown committed
2340
      {
2341 2342 2343 2344
        error("Could not read entry at offset %s: "
              "Error in log format or read error.",
              llstr(old_off,llbuff));
        goto err;
unknown's avatar
unknown committed
2345
      }
2346
      // file->error == 0 means EOF, that's OK, we break in this case
2347
      goto end;
unknown's avatar
unknown committed
2348
    }
2349 2350 2351
    if ((retval= process_event(print_event_info, ev, old_off, logname)) !=
        OK_CONTINUE)
      goto end;
2352
  }
unknown's avatar
unknown committed
2353

2354 2355 2356 2357 2358
  /* NOTREACHED */

err:
  retval= ERROR_STOP;

unknown's avatar
unknown committed
2359
end:
2360
  if (fd >= 0)
2361
    my_close(fd, MYF(MY_WME));
2362 2363 2364 2365 2366 2367 2368
  /*
    Since the end_io_cache() writes to the
    file errors may happen.
   */
  if (end_io_cache(file))
    retval= ERROR_STOP;

2369
  return retval;
unknown's avatar
unknown committed
2370 2371
}

2372 2373
/* Used in sql_alloc(). Inited and freed in main() */
MEM_ROOT s_mem_root;
2374

unknown's avatar
unknown committed
2375 2376
int main(int argc, char** argv)
{
unknown's avatar
unknown committed
2377
  char **defaults_argv;
2378
  Exit_status retval= OK_CONTINUE;
2379
  ulonglong save_stop_position;
unknown's avatar
unknown committed
2380
  MY_INIT(argv[0]);
unknown's avatar
unknown committed
2381 2382
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);
unknown's avatar
unknown committed
2383

2384
  my_init_time(); // for time functions
2385

2386
  init_alloc_root(&s_mem_root, 16384, 0);
2387 2388 2389
  if (load_defaults("my", load_groups, &argc, &argv))
    exit(1);

2390 2391 2392 2393 2394 2395
  if (!(binlog_filter= new Rpl_filter))
  {
    error("Failed to create Rpl_filter");
    exit(1);
  }

2396
  defaults_argv= argv;
unknown's avatar
unknown committed
2397 2398
  parse_args(&argc, (char***)&argv);

2399
  if (!argc || opt_version)
2400
  {
2401 2402 2403
    if (!argc)
      usage();
    cleanup();
unknown's avatar
unknown committed
2404
    free_defaults(defaults_argv);
2405
    my_end(my_end_arg);
2406
    exit(!opt_version);
2407
  }
unknown's avatar
unknown committed
2408

2409 2410
  if (opt_base64_output_mode == BASE64_OUTPUT_UNSPEC)
    opt_base64_output_mode= BASE64_OUTPUT_AUTO;
2411 2412 2413 2414 2415 2416 2417
  if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
    warning("The --base64-output=always flag and the --base64-output flag "
            "(with '=MODE' omitted), are deprecated. "
            "The output generated when these flags are used cannot be "
            "parsed by mysql 5.6.0 and later. "
            "The flags will be removed in a future version. "
            "Please use --base64-output=auto instead.");
2418

2419
  my_set_max_open_files(open_files_limit);
2420 2421 2422 2423 2424 2425 2426

  MY_TMPDIR tmpdir;
  tmpdir.list= 0;
  if (!dirname_for_local_load)
  {
    if (init_tmpdir(&tmpdir, 0))
      exit(1);
unknown's avatar
unknown committed
2427
    dirname_for_local_load= my_strdup(my_tmpdir(&tmpdir), MY_WME);
2428 2429
  }

2430 2431
  if (load_processor.init())
    exit(1);
2432 2433
  if (dirname_for_local_load)
    load_processor.init_by_dir_name(dirname_for_local_load);
2434 2435
  else
    load_processor.init_by_cur_dir();
unknown's avatar
unknown committed
2436

unknown's avatar
unknown committed
2437
  fprintf(result_file,
unknown's avatar
unknown committed
2438
	  "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n");
2439 2440 2441 2442 2443

  if (disable_log_bin)
    fprintf(result_file,
            "/*!32316 SET @OLD_SQL_LOG_BIN=@@SQL_LOG_BIN, SQL_LOG_BIN=0*/;\n");

2444 2445 2446 2447 2448 2449 2450 2451
  /*
    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");

2452 2453 2454 2455 2456 2457 2458
  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);

2459
  for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ;
2460
       (--argc >= 0) ; )
unknown's avatar
unknown committed
2461
  {
2462 2463
    if (argc == 0) // last log, --stop-position applies
      stop_position= save_stop_position;
2464
    if ((retval= dump_log_entries(*argv++)) != OK_CONTINUE)
unknown's avatar
unknown committed
2465
      break;
2466

2467 2468
    // For next log, --start-position does not apply
    start_position= BIN_LOG_HEADER_SIZE;
unknown's avatar
unknown committed
2469
  }
2470

2471 2472 2473 2474 2475
  /*
    Issue a ROLLBACK in case the last printed binlog was crashed and had half
    of transaction.
  */
  fprintf(result_file,
2476
          "# End of log file\nROLLBACK /* added by mysqlbinlog */;\n"
2477
          "/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;\n");
2478 2479 2480
  if (disable_log_bin)
    fprintf(result_file, "/*!32316 SET SQL_LOG_BIN=@OLD_SQL_LOG_BIN*/;\n");

2481 2482 2483 2484 2485 2486
  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");

2487 2488
  if (tmpdir.list)
    free_tmpdir(&tmpdir);
2489 2490
  if (result_file != stdout)
    my_fclose(result_file, MYF(0));
2491
  cleanup();
2492
  free_annotate_event();
2493
  free_root(&s_mem_root, MYF(0));
unknown's avatar
unknown committed
2494
  free_defaults(defaults_argv);
2495
  my_free_open_file_info();
2496
  load_processor.destroy();
2497
  /* We cannot free DBUG, it is used in global destructors after exit(). */
2498
  my_end(my_end_arg | MY_DONT_FREE_DBUG);
2499

2500 2501 2502
  exit(retval == ERROR_STOP ? 1 : 0);
  /* Keep compilers happy. */
  DBUG_RETURN(retval == ERROR_STOP ? 1 : 0);
unknown's avatar
unknown committed
2503 2504
}

2505 2506 2507 2508 2509 2510

void *sql_alloc(size_t size)
{
  return alloc_root(&s_mem_root, size);
}

unknown's avatar
unknown committed
2511 2512 2513 2514 2515
/*
  We must include this here as it's compiled with different options for
  the server
*/

2516
#undef TABLE
unknown's avatar
unknown committed
2517 2518 2519
#include "my_decimal.h"
#include "decimal.c"
#include "my_decimal.cc"
unknown's avatar
unknown committed
2520
#include "log_event.cc"
2521
#include "log_event_old.cc"
2522
#include "rpl_utility.cc"
2523
#include "sql_string.cc"
2524 2525
#include "sql_list.cc"
#include "rpl_filter.cc"