mysqlbinlog.cc 38.2 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2001-2004 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   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
12

unknown's avatar
unknown committed
13 14 15 16
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/* 

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

   Standalone program to read a MySQL binary log (or relay log);
   can read files produced by 3.23, 4.x, 5.0 servers. 

   Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0.
   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
unknown's avatar
unknown committed
34
#include "client_priv.h"
35
#include <my_time.h>
unknown's avatar
unknown committed
36
#include "log_event.h"
37 38
/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */
#include "mysql_priv.h" 
unknown's avatar
unknown committed
39

40
#define BIN_LOG_HEADER_SIZE	4
unknown's avatar
unknown committed
41
#define PROBE_HEADER_LEN	(EVENT_LEN_OFFSET+4)
42

43

unknown's avatar
unknown committed
44 45
#define CLIENT_CAPABILITIES	(CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)

unknown's avatar
unknown committed
46
char server_version[SERVER_VERSION_LENGTH];
47
ulong server_id = 0;
unknown's avatar
unknown committed
48 49 50 51

// needed by net_serv.c
ulong bytes_sent = 0L, bytes_received = 0L;
ulong mysqld_net_retry_count = 10L;
52
ulong open_files_limit;
unknown's avatar
unknown committed
53
uint test_flags = 0; 
54
static uint opt_protocol= 0;
unknown's avatar
unknown committed
55
static FILE *result_file;
unknown's avatar
unknown committed
56 57 58 59

#ifndef DBUG_OFF
static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace";
#endif
unknown's avatar
unknown committed
60
static const char *load_default_groups[]= { "mysqlbinlog","client",0 };
unknown's avatar
unknown committed
61

unknown's avatar
unknown committed
62
void sql_print_error(const char *format, ...);
unknown's avatar
unknown committed
63

64
static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0;
65 66
static const char* database= 0;
static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
67
static ulonglong offset = 0;
68
static const char* host = 0;
unknown's avatar
unknown committed
69
static int port = MYSQL_PORT;
70
static const char* sock= 0;
71
static const char* user = 0;
72
static char* pass = 0;
73 74 75 76 77 78 79 80

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
81 82
static short binlog_flags = 0; 
static MYSQL* mysql = NULL;
83
static const char* dirname_for_local_load= 0;
84
static bool stop_passed= 0;
85

86 87 88 89 90 91 92 93
/*
  check_header() will set the pointer below.
  Why do we need here a pointer on an event instead of an event ?
  This is because the event will be created (alloced) in read_log_event()
  (which returns a pointer) in check_header().
*/
Format_description_log_event* description_event; 

unknown's avatar
unknown committed
94 95 96 97
static int dump_local_log_entries(const char* logname);
static int dump_remote_log_entries(const char* logname);
static int dump_log_entries(const char* logname);
static int dump_remote_file(NET* net, const char* fname);
unknown's avatar
unknown committed
98 99 100
static void die(const char* fmt, ...);
static MYSQL* safe_connect();

unknown's avatar
unknown committed
101

unknown's avatar
unknown committed
102 103
class Load_log_processor
{
104
  char target_dir_name[FN_REFLEN];
105 106 107
  int target_dir_name_len;
  DYNAMIC_ARRAY file_names;

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
  /*
    Looking for new uniquie filename that doesn't exist yet by 
    adding postfix -%x

    SYNOPSIS 
       create_unique_file()
       
       filename       buffer for filename
       file_name_end  tail of buffer that should be changed
                      should point to a memory enough to printf("-%x",..)

    RETURN VALUES
      values less than 0      - can't find new filename
      values great or equal 0 - created file with found filename
  */
  File create_unique_file(char *filename, char *file_name_end)
124
    {
125 126 127 128 129 130 131 132 133 134
      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;
135 136 137
    }

public:
138
  Load_log_processor() {}
139
  ~Load_log_processor()
140 141 142 143 144 145 146 147 148 149
  {
    destroy();
    delete_dynamic(&file_names);
  }

  int init()
  {
    return init_dynamic_array(&file_names,sizeof(Create_file_log_event*),
			      100,100 CALLER_INFO);
  }
150

unknown's avatar
unknown committed
151
  void init_by_dir_name(const char *dir)
152
    {
unknown's avatar
unknown committed
153 154
      target_dir_name_len= (convert_dirname(target_dir_name, dir, NullS) -
			    target_dir_name);
155 156 157
    }
  void init_by_cur_dir()
    {
158 159 160
      if (my_getwd(target_dir_name,sizeof(target_dir_name),MYF(MY_WME)))
	exit(1);
      target_dir_name_len= strlen(target_dir_name);
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    }
  void destroy()
    {
      Create_file_log_event **ptr= (Create_file_log_event**)file_names.buffer;
      Create_file_log_event **end= ptr + file_names.elements;
      for (; ptr<end; ptr++)
      {
	if (*ptr)
	{
	  my_free((char*)(*ptr)->fname,MYF(MY_WME));
	  delete *ptr;
	  *ptr= 0;
	}
      }
    }
  Create_file_log_event *grab_event(uint file_id)
    {
178 179
      if (file_id >= file_names.elements)
        return 0;
180 181 182 183 184 185
      Create_file_log_event **ptr= 
	(Create_file_log_event**)file_names.buffer + file_id;
      Create_file_log_event *res= *ptr;
      *ptr= 0;
      return res;
    }
186
  int process(Create_file_log_event *ce);
unknown's avatar
unknown committed
187
  int process(Append_block_log_event *ae);
188 189 190
  File prepare_new_file_for_old_format(Load_log_event *le, char *filename);
  int load_old_format_file(NET* net, const char *server_fname,
			   uint server_fname_len, File file);
unknown's avatar
unknown committed
191 192
};

unknown's avatar
unknown committed
193 194


195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le,
							 char *filename)
{
  uint len;
  char *tail;
  File file;
  
  fn_format(filename, le->fname, target_dir_name, "", 1);
  len= strlen(filename);
  tail= filename + len;
  
  if ((file= create_unique_file(filename,tail)) < 0)
  {
    sql_print_error("Could not construct local filename %s",filename);
    return -1;
  }
  
  le->set_fname_outside_temp_buf(filename,len+strlen(tail));
  
  return file;
}
unknown's avatar
unknown committed
216

unknown's avatar
unknown committed
217

218 219 220 221 222 223 224 225 226 227 228 229 230 231
int Load_log_processor::load_old_format_file(NET* net, const char*server_fname,
					     uint server_fname_len, File file)
{
  char buf[FN_REFLEN+1];
  buf[0] = 0;
  memcpy(buf + 1, server_fname, server_fname_len + 1);
  if (my_net_write(net, buf, server_fname_len +2) || net_flush(net))
  {
    sql_print_error("Failed  requesting the remote dump of %s", server_fname);
    return -1;
  }
  
  for (;;)
  {
232
    ulong packet_len = my_net_read(net);
233
    if (packet_len == 0)
234
    {
235
      if (my_net_write(net, "", 0) || net_flush(net))
236
      {
237 238
	sql_print_error("Failed sending the ack packet");
	return -1;
239
      }
240 241 242 243 244 245
      /*
	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;
246
    }
247 248 249 250 251 252 253
    else if (packet_len == packet_error)
    {
      sql_print_error("Failed reading a packet during the dump of %s ", 
		      server_fname);
      return -1;
    }
    
254 255 256 257 258 259 260
    if (packet_len > UINT_MAX)
    {
      sql_print_error("Illegal length of packet read from net");
      return -1;
    }
    if (my_write(file, (byte*) net->read_pos, 
		 (uint) packet_len, MYF(MY_WME|MY_NABP)))
261 262 263 264 265
      return -1;
  }
  
  return 0;
}
266

unknown's avatar
unknown committed
267

268
int Load_log_processor::process(Create_file_log_event *ce)
unknown's avatar
unknown committed
269 270 271 272
{
  const char *bname= ce->fname+dirname_length(ce->fname);
  uint blen= ce->fname_len - (bname-ce->fname);
  uint full_len= target_dir_name_len + blen + 9 + 9 + 1;
unknown's avatar
unknown committed
273
  int error= 0;
274 275
  char *fname, *ptr;
  File file;
unknown's avatar
unknown committed
276
  DBUG_ENTER("Load_log_processor::process");
unknown's avatar
unknown committed
277

unknown's avatar
unknown committed
278
  if (set_dynamic(&file_names,(gptr)&ce,ce->file_id))
unknown's avatar
unknown committed
279
  {
280 281
    sql_print_error("Could not construct local filename %s%s",
		    target_dir_name,bname);
unknown's avatar
unknown committed
282
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
283
  }
unknown's avatar
unknown committed
284
  if (!(fname= my_malloc(full_len,MYF(MY_WME))))
unknown's avatar
unknown committed
285
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
286

287 288
  memcpy(fname, target_dir_name, target_dir_name_len);
  ptr= fname + target_dir_name_len;
unknown's avatar
unknown committed
289 290 291 292
  memcpy(ptr,bname,blen);
  ptr+= blen;
  ptr+= my_sprintf(ptr,(ptr,"-%x",ce->file_id));

293 294 295 296
  if ((file= create_unique_file(fname,ptr)) < 0)
  {
    sql_print_error("Could not construct local filename %s%s",
		    target_dir_name,bname);
unknown's avatar
unknown committed
297
    DBUG_RETURN(-1);
298 299
  }
  ce->set_fname_outside_temp_buf(fname,strlen(fname));
unknown's avatar
unknown committed
300

unknown's avatar
unknown committed
301 302
  if (my_write(file,(byte*) ce->block,ce->block_len,MYF(MY_WME|MY_NABP)))
    error= -1;
unknown's avatar
unknown committed
303
  if (my_close(file, MYF(MY_WME)))
unknown's avatar
unknown committed
304
    error= -1;
unknown's avatar
unknown committed
305
  DBUG_RETURN(error);
306 307
}

unknown's avatar
unknown committed
308 309 310

int Load_log_processor::process(Append_block_log_event *ae)
{
unknown's avatar
unknown committed
311
  DBUG_ENTER("Load_log_processor::process");
unknown's avatar
unknown committed
312 313 314 315
  Create_file_log_event* ce= ((ae->file_id < file_names.elements) ?
			      *((Create_file_log_event**)file_names.buffer +
				ae->file_id) :
			      0);
unknown's avatar
unknown committed
316

unknown's avatar
unknown committed
317 318 319 320 321 322
  if (ce)
  {
    File file;
    int error= 0;
    if (((file= my_open(ce->fname,
			O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0))
unknown's avatar
unknown committed
323
      DBUG_RETURN(-1);
unknown's avatar
unknown committed
324 325 326 327
    if (my_write(file,(byte*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP)))
      error= -1;
    if (my_close(file,MYF(MY_WME)))
      error= -1;
unknown's avatar
unknown committed
328
    DBUG_RETURN(error);
unknown's avatar
unknown committed
329 330 331 332
  }

  /*
    There is no Create_file event (a bad binlog or a big
333 334
    --start-position). Assuming it's a big --start-position, we just do
    nothing and print a warning.
unknown's avatar
unknown committed
335
  */
unknown's avatar
unknown committed
336 337
  fprintf(stderr,"Warning: ignoring Append_block as there is no \
Create_file event for file_id: %u\n",ae->file_id);
unknown's avatar
unknown committed
338
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
339 340 341
}


342 343
Load_log_processor load_processor;

344

345
/*
346 347 348 349 350
  Process an event

  SYNOPSIS
    process_event()

351 352 353 354
  RETURN
    0           ok and continue
    1           error and terminate
    -1          ok and terminate
355
 
356 357 358
  TODO
    This function returns 0 even in some error cases. This should be changed.
*/
359 360 361 362 363



int process_event(LAST_EVENT_INFO *last_event_info, Log_event *ev,
                  my_off_t pos)
364 365
{
  char ll_buff[21];
366
  Log_event_type ev_type= ev->get_type_code();
unknown's avatar
unknown committed
367 368
  DBUG_ENTER("process_event");

369 370 371 372
  /*
    Format events are not concerned by --offset and such, we always need to
    read them to be able to process the wanted events.
  */
373
  if ((rec_count >= offset) &&
374 375
      ((my_time_t)(ev->when) >= start_datetime) ||
      (ev_type == FORMAT_DESCRIPTION_EVENT))
unknown's avatar
unknown committed
376
  {
377 378 379 380 381 382 383 384 385 386
    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
    }
387 388 389 390 391 392
    if (((my_time_t)(ev->when) >= stop_datetime)
        || (pos >= stop_position_mot))
    {
      stop_passed= 1; // skip all next binlogs
      DBUG_RETURN(-1);
    }
393 394 395
    if (!short_form)
      fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
    
396
    switch (ev_type) {
397 398 399 400 401
    case QUERY_EVENT:
      if (one_database)
      {
	const char * log_dbname = ((Query_log_event*)ev)->db;
	if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
unknown's avatar
unknown committed
402
	  goto end;
403
      }
404
      ev->print(result_file, short_form, last_event_info);
unknown's avatar
unknown committed
405
      break;
406
    case CREATE_FILE_EVENT:
unknown's avatar
unknown committed
407
    {
408 409 410 411 412 413 414 415 416 417 418
      Create_file_log_event* ce= (Create_file_log_event*)ev;
      if (one_database)
      {
	/*
	  We test if this event has to be ignored. If yes, we don't save 
	      this event; this will have the good side-effect of ignoring all 
	      related Append_block and Exec_load.
	      Note that Load event from 3.23 is not tested.
	*/
	const char * log_dbname = ce->db;            
	if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
unknown's avatar
unknown committed
419
	  goto end;				// Next event
420 421 422 423 424 425 426 427
      }
      /*
	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.
      */
428 429 430
      ce->print(result_file, short_form, last_event_info, TRUE);
      // If this binlog is not 3.23 ; why this test??
      if (description_event->binlog_version >= 3)
431
      {
unknown's avatar
unknown committed
432 433
	if (load_processor.process(ce))
	  break;				// Error
434 435 436 437 438
	ev= 0;
      }
      break;
    }
    case APPEND_BLOCK_EVENT:
439
      ev->print(result_file, short_form, last_event_info);
unknown's avatar
unknown committed
440 441
      if (load_processor.process((Append_block_log_event*) ev))
	break;					// Error
442 443 444
      break;
    case EXEC_LOAD_EVENT:
    {
445
      ev->print(result_file, short_form, last_event_info);
446 447 448 449
      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
450
	event (a bad binlog, or most probably --start-position is after the
451 452 453 454
	Create_file event). Print a warning comment.
      */
      if (ce)
      {
455
	ce->print(result_file, short_form, last_event_info, TRUE);
456 457 458 459 460 461 462 463
	my_free((char*)ce->fname,MYF(MY_WME));
	delete ce;
      }
      else
	fprintf(stderr,"Warning: ignoring Exec_load as there is no \
Create_file event for file_id: %u\n",exv->file_id);
      break;
    }
464 465 466 467 468 469 470 471 472 473 474 475
    case FORMAT_DESCRIPTION_EVENT:
      delete description_event;
      description_event= (Format_description_log_event*) ev;
      ev->print(result_file, short_form, last_event_info);
      /*
        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;
      break;
476
    default:
477
      ev->print(result_file, short_form, last_event_info);
unknown's avatar
unknown committed
478 479
    }
  }
unknown's avatar
unknown committed
480 481

end:
482
  rec_count++;
483 484
  if (ev)
    delete ev;
unknown's avatar
unknown committed
485
  DBUG_RETURN(0);
unknown's avatar
unknown committed
486 487 488
}


489 490
static struct my_option my_long_options[] =
{
491 492 493 494 495 496 497 498 499
  /*
    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,
   "Directory where character sets are.", (gptr*) &charsets_dir,
   (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
500 501 502 503
#ifndef DBUG_OFF
  {"debug", '#', "Output debug log.", (gptr*) &default_dbug_option,
   (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
504
  {"database", 'd', "List entries for just this database (local log only).",
505 506
   (gptr*) &database, (gptr*) &database, 0, GET_STR_ALLOC, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
507 508 509 510 511 512 513
  {"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.",
   (gptr*) &disable_log_bin, (gptr*) &disable_log_bin, 0, GET_BOOL,
   NO_ARG, 0, 0, 0, 0, 0, 0},
514
  {"force-read", 'f', "Force reading unknown binlog events.",
515 516
   (gptr*) &force_opt, (gptr*) &force_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
517
  {"help", '?', "Display this help and exit.",
518
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
519
  {"host", 'h', "Get the binlog from server.", (gptr*) &host, (gptr*) &host,
520
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
521
  {"offset", 'o', "Skip the first N entries.", (gptr*) &offset, (gptr*) &offset,
522
   0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
523
  {"password", 'p', "Password to connect to remote server.",
524
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
525
  {"port", 'P', "Use port to connect to the remote server.",
526 527
   (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0,
   0, 0, 0},
528 529 530 531 532
  {"position", 'j', "Deprecated. Use --start-position instead.",
   (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL,
   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},
533 534 535
  {"protocol", OPT_MYSQL_PROTOCOL,
   "The protocol of connection (tcp,socket,pipe,memory).",
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
536
  {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
537
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
538
  {"read-from-remote-server", 'R', "Read binary logs from a MySQL server",
539 540
   (gptr*) &remote_opt, (gptr*) &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
541 542 543 544
  {"open_files_limit", OPT_OPEN_FILES_LIMIT,
   "Used to reserve file descriptors for usage by this program",
   (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG,
   REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0},
545
  {"short-form", 's', "Just show the queries, no extra info.",
546 547
   (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
548
  {"socket", 'S', "Socket file to use for connection.",
549
   (gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 
unknown's avatar
unknown committed
550
   0, 0},
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
  {"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).",
   (gptr*) &start_datetime_str, (gptr*) &start_datetime_str,
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"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).",
   (gptr*) &stop_datetime_str, (gptr*) &stop_datetime_str,
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"start-position", OPT_START_POSITION,
   "Start reading the binlog at position N. Applies to the first binlog "
   "passed on the command line.",
   (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL,
   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},
  {"stop-position", OPT_STOP_POSITION,
   "Stop reading the binlog at position N. Applies to the last binlog "
   "passed on the command line.",
   (gptr*) &stop_position, (gptr*) &stop_position, 0, GET_ULL,
   REQUIRED_ARG, (ulonglong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
   (ulonglong)(~(my_off_t)0), 0, 0, 0},
580 581
  {"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 \
582
binlog of the MySQL server. If you send the output to the same MySQL server, \
583 584 585
that may lead to an endless loop.",
   (gptr*) &to_last_remote_log, (gptr*) &to_last_remote_log, 0, GET_BOOL,
   NO_ARG, 0, 0, 0, 0, 0, 0},
586
  {"user", 'u', "Connect to the remote server as username.",
587 588
   (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0,
   0, 0},
589
  {"local-load", 'l', "Prepare local temporary files for LOAD DATA INFILE in the specified directory.",
590
   (gptr*) &dirname_for_local_load, (gptr*) &dirname_for_local_load, 0,
591
   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
592 593 594 595 596
  {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};

unknown's avatar
unknown committed
597 598 599

void sql_print_error(const char *format,...)
{
unknown's avatar
unknown committed
600 601 602 603 604 605
  va_list args;
  va_start(args, format);
  fprintf(stderr, "ERROR: ");
  vfprintf(stderr, format, args);
  fprintf(stderr, "\n");
  va_end(args);
unknown's avatar
unknown committed
606
}
unknown's avatar
unknown committed
607

608 609 610
static void cleanup()
{
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
611 612 613 614
  my_free((char*) database, MYF(MY_ALLOW_ZERO_PTR));
  my_free((char*) host, MYF(MY_ALLOW_ZERO_PTR));
  my_free((char*) user, MYF(MY_ALLOW_ZERO_PTR));
  my_free((char*) dirname_for_local_load, MYF(MY_ALLOW_ZERO_PTR));
615 616
}

unknown's avatar
unknown committed
617 618 619 620 621 622 623 624
static void die(const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  fprintf(stderr, "ERROR: ");
  vfprintf(stderr, fmt, args);
  fprintf(stderr, "\n");
  va_end(args);
625
  cleanup();
unknown's avatar
unknown committed
626
  my_end(0);
unknown's avatar
unknown committed
627 628 629
  exit(1);
}

unknown's avatar
unknown committed
630 631
#include <help_start.h>

unknown's avatar
unknown committed
632 633
static void print_version()
{
634
  printf("%s Ver 3.1 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
unknown's avatar
unknown committed
635
  NETWARE_SET_SCREEN_MODE(1);
unknown's avatar
unknown committed
636 637 638
}


unknown's avatar
unknown committed
639 640
static void usage()
{
unknown's avatar
unknown committed
641
  print_version();
unknown's avatar
unknown committed
642 643 644
  puts("By Monty and Sasha, for your professional use\n\
This software comes with NO WARRANTY:  This is free software,\n\
and you are welcome to modify and redistribute it under the GPL license\n");
unknown's avatar
unknown committed
645 646

  printf("\
unknown's avatar
unknown committed
647
Dumps a MySQL binary log in a format usable for viewing or for piping to\n\
unknown's avatar
unknown committed
648
the mysql command line client\n\n");
649 650 651
  printf("Usage: %s [options] log-files\n", my_progname);
  my_print_help(my_long_options);
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
652 653
}

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

static my_time_t convert_str_to_timestamp(const char* str)
{
  int was_cut;
  MYSQL_TIME l_time;
  long dummy_my_timezone;
  bool dummy_in_dst_time_gap;
  /* We require a total specification (date AND time) */
  if (str_to_datetime(str, strlen(str), &l_time, 0, &was_cut) !=
      MYSQL_TIMESTAMP_DATETIME || was_cut)
  {
    fprintf(stderr, "Incorrect date and time argument: %s\n", str);
    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
677 678
#include <help_end.h>

679
extern "C" my_bool
680 681 682
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
683
  bool tty_password=0;
684
  switch (optid) {
unknown's avatar
unknown committed
685
#ifndef DBUG_OFF
686 687 688
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    break;
unknown's avatar
unknown committed
689
#endif
690 691 692 693
  case 'd':
    one_database = 1;
    break;
  case 'p':
694 695 696 697 698 699 700 701 702 703 704
    if (argument)
    {
      my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
      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;
705 706 707 708 709
    break;
  case 'r':
    if (!(result_file = my_fopen(argument, O_WRONLY | O_BINARY, MYF(MY_WME))))
      exit(1);
    break;
710 711
  case 'R':
    remote_opt= 1;
712
    break;
713 714
  case OPT_MYSQL_PROTOCOL:
  {
715
    if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0)
716 717 718 719 720 721
    {
      fprintf(stderr, "Unknown option to protocol: %s\n", argument);
      exit(1);
    }
    break;
  }
722 723 724 725 726 727
  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;
728 729 730 731 732 733
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(0);
unknown's avatar
unknown committed
734
  }
735 736 737
  if (tty_password)
    pass= get_tty_password(NullS);

738 739 740
  return 0;
}

unknown's avatar
unknown committed
741

742 743 744
static int parse_args(int *argc, char*** argv)
{
  int ho_error;
unknown's avatar
unknown committed
745

746
  result_file = stdout;
unknown's avatar
unknown committed
747
  load_defaults("my",load_default_groups,argc,argv);
748
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
749 750
    exit(ho_error);

unknown's avatar
unknown committed
751 752 753 754 755
  return 0;
}

static MYSQL* safe_connect()
{
unknown's avatar
unknown committed
756
  MYSQL *local_mysql= mysql_init(NULL);
757

758
  if (!local_mysql)
unknown's avatar
unknown committed
759
    die("Failed on mysql_init");
unknown's avatar
unknown committed
760

761 762
  if (opt_protocol)
    mysql_options(local_mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol);
unknown's avatar
unknown committed
763
  if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0))
unknown's avatar
unknown committed
764 765 766 767 768 769
  {
    char errmsg[256];
    strmake(errmsg, mysql_error(local_mysql), sizeof(errmsg)-1);
    mysql_close(local_mysql);
    die("failed on connect: %s", errmsg);
  }
770
  local_mysql->reconnect= 1;
unknown's avatar
unknown committed
771 772 773
  return local_mysql;
}

unknown's avatar
unknown committed
774 775

static int dump_log_entries(const char* logname)
unknown's avatar
unknown committed
776
{
777 778
  return (remote_opt ? dump_remote_log_entries(logname) :
          dump_local_log_entries(logname));
unknown's avatar
unknown committed
779 780
}

unknown's avatar
unknown committed
781

782 783 784 785 786 787 788
/*
  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.
*/
static int check_master_version(MYSQL* mysql,
                                Format_description_log_event
                                **description_event)
789 790 791 792
{
  MYSQL_RES* res = 0;
  MYSQL_ROW row;
  const char* version;
unknown's avatar
unknown committed
793

794 795
  if (mysql_query(mysql, "SELECT VERSION()") ||
      !(res = mysql_store_result(mysql)))
unknown's avatar
unknown committed
796 797 798 799 800 801
  {
    char errmsg[256];
    strmake(errmsg, mysql_error(mysql), sizeof(errmsg)-1);
    mysql_close(mysql);
    die("Error checking master version: %s", errmsg);
  }
802 803 804 805 806 807 808 809 810 811 812 813 814
  if (!(row = mysql_fetch_row(res)))
  {
    mysql_free_result(res);
    mysql_close(mysql);
    die("Master returned no rows for SELECT VERSION()");
    return 1;
  }
  if (!(version = row[0]))
  {
    mysql_free_result(res);
    mysql_close(mysql);
    die("Master reported NULL for the version");
  }
unknown's avatar
unknown committed
815

816
  switch (*version) {
817
  case '3':
818
    *description_event= new Format_description_log_event(1);
819 820
    break;
  case '4':
821
    *description_event= new Format_description_log_event(3);
822
  case '5':
823 824 825 826 827 828 829
    /*
      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).
    */
    *description_event= new Format_description_log_event(3);
830 831 832 833 834 835 836 837 838
    break;
  default:
    sql_print_error("Master reported unrecognized MySQL version '%s'",
		    version);
    mysql_free_result(res);
    mysql_close(mysql);
    return 1;
  }
  mysql_free_result(res);
839
  return 0;
840
}
unknown's avatar
unknown committed
841

842

unknown's avatar
unknown committed
843
static int dump_remote_log_entries(const char* logname)
844

unknown's avatar
unknown committed
845 846
{
  char buf[128];
847 848
  LAST_EVENT_INFO last_event_info;
  uint len, logname_len;
849 850 851 852
  NET* net;
  int error= 0;
  my_off_t old_off= start_position_mot;
  char fname[FN_REFLEN+1];
unknown's avatar
unknown committed
853 854
  DBUG_ENTER("dump_remote_log_entries");

855 856 857 858 859 860 861
  /*
    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).
  */
  mysql= safe_connect();
  net= &mysql->net;
862 863 864 865 866 867 868 869 870 871 872 873

  if (check_master_version(mysql, &description_event))
  {
    fprintf(stderr, "Could not find server version");
    DBUG_RETURN(1);
  }
  if (!description_event || !description_event->is_valid())
  {
    fprintf(stderr, "Invalid Format_description log event; \
could be out of memory");
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
874

875 876 877 878 879
  /*
    COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
    cast to uint32.
  */
  int4store(buf, (uint32)start_position);
880
  int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
881 882 883 884 885 886 887 888 889

  size_s tlen = strlen(logname);
  if (tlen > UINT_MAX) 
  {
    fprintf(stderr,"Log name too long\n");
    error= 1;
    goto err;
  }
  logname_len = (uint) tlen;
890
  int4store(buf + 6, 0);
891 892
  memcpy(buf + 10, logname, logname_len);
  if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1))
unknown's avatar
unknown committed
893 894
  {
    fprintf(stderr,"Got fatal error sending the log dump command\n");
895 896
    error= 1;
    goto err;
unknown's avatar
unknown committed
897
  }
unknown's avatar
unknown committed
898

899
  for (;;)
unknown's avatar
unknown committed
900
  {
901
    const char *error_msg;
902 903
    Log_event *ev;

unknown's avatar
unknown committed
904
    len = net_safe_read(mysql);
unknown's avatar
unknown committed
905
    if (len == packet_error)
unknown's avatar
unknown committed
906 907 908
    {
      fprintf(stderr, "Got error reading packet from server: %s\n",
	      mysql_error(mysql));
909 910
      error= 1;
      goto err;
unknown's avatar
unknown committed
911
    }
unknown's avatar
unknown committed
912
    if (len < 8 && net->read_pos[0] == 254)
unknown's avatar
unknown committed
913 914 915
      break; // end of data
    DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
			len, net->read_pos[5]));
916 917 918
    if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 ,
                                        len - 1, &error_msg,
                                        description_event)))
unknown's avatar
unknown committed
919
    {
unknown's avatar
unknown committed
920
      fprintf(stderr, "Could not construct log event object\n");
921 922
      error= 1;
      goto err;
923
    }   
unknown's avatar
unknown committed
924 925

    Log_event_type type= ev->get_type_code();
926 927
    if (description_event->binlog_version >= 3 ||
        (type != LOAD_EVENT && type != CREATE_FILE_EVENT))
unknown's avatar
unknown committed
928
    {
929 930 931 932 933 934 935 936 937 938
      /*
        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)
939 940 941 942 943 944 945 946 947
      {
        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.
        */
948
        if (rev->when == 0)
949
        {
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
          if (!to_last_remote_log)
          {
            if ((rev->ident_len != logname_len) ||
                memcmp(rev->new_log_ident, logname, logname_len))
            {
              error= 0;
              goto err;
            }
            /*
              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
967 968
        }
      }
969
      if ((error= process_event(&last_event_info,ev,old_off)))
970 971 972 973
      {
	error= ((error < 0) ? 0 : 1);
        goto err;
      }
unknown's avatar
unknown committed
974 975
    }
    else
976
    {
unknown's avatar
unknown committed
977 978 979 980
      Load_log_event *le= (Load_log_event*)ev;
      const char *old_fname= le->fname;
      uint old_len= le->fname_len;
      File file;
981
      
unknown's avatar
unknown committed
982
      if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
983 984 985 986
      {
        error= 1;
        goto err;
      }
987 988
      
      if ((error= process_event(&last_event_info,ev,old_off)))
989
      {
990
 	my_close(file,MYF(MY_WME));
991 992
	error= ((error < 0) ? 0 : 1);
        goto err;
993
      }
994 995 996
      error= load_processor.load_old_format_file(net,old_fname,old_len,file);
      my_close(file,MYF(MY_WME));
      if (error)
997
      {
998 999
        error= 1;
        goto err;
1000
      }
unknown's avatar
unknown committed
1001
    }
1002
    /*
unknown's avatar
unknown committed
1003
      Let's adjust offset for remote log as for local log to produce 
1004
      similar text.
1005
    */
1006
    old_off+= len-1;
unknown's avatar
unknown committed
1007
  }
1008

1009 1010 1011
err:
  mysql_close(mysql);
  DBUG_RETURN(error);
unknown's avatar
unknown committed
1012 1013
}

1014

1015 1016
static void check_header(IO_CACHE* file, 
                        Format_description_log_event **description_event) 
1017
{
unknown's avatar
unknown committed
1018
  byte header[BIN_LOG_HEADER_SIZE];
unknown's avatar
unknown committed
1019
  byte buf[PROBE_HEADER_LEN];
1020
  my_off_t tmp_pos, pos;
unknown's avatar
unknown committed
1021

1022 1023
  *description_event= new Format_description_log_event(3);
  pos= my_b_tell(file);
1024
  my_b_seek(file, (my_off_t)0);
unknown's avatar
unknown committed
1025 1026 1027 1028
  if (my_b_read(file, header, sizeof(header)))
    die("Failed reading header;  Probably an empty file");
  if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
    die("File is not a binary log file");
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043

  /*
    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(;;)
1044
  {
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
    tmp_pos= my_b_tell(file); /* should be 4 the first time */
    if (my_b_read(file, buf, sizeof(buf)))
    {
      if (file->error)
        die("\
Could not read entry at offset %lu : Error in log format or read error",
            tmp_pos); 
      /*
        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
1069
    {
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
      DBUG_PRINT("info",("buf[4]=%d", buf[4]));
      /* always test for a Start_v3, even if no --start-position */
      if (buf[4] == START_EVENT_V3)       /* This is 3.23 or 4.x */
      {
        if (uint4korr(buf + EVENT_LEN_OFFSET) < 
            (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN))
        {
          /* This is 3.23 (format 1) */
          delete *description_event;
          *description_event= new Format_description_log_event(1);
        }
        break;
      }
      else if (tmp_pos >= start_position)
        break;
      else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */
      {
        my_b_seek(file, tmp_pos); /* seek back to event's start */
        if (!(*description_event= (Format_description_log_event*) 
              Log_event::read_log_event(file, *description_event)))
          /* EOF can't be hit here normally, so it's a real error */
          die("Could not read a Format_description_log_event event \
at offset %lu ; this could be a log format error or read error",
              tmp_pos); 
        DBUG_PRINT("info",("Setting description_event"));
      }
      else if (buf[4] == ROTATE_EVENT)
      {
        my_b_seek(file, tmp_pos); /* seek back to event's start */
        if (!Log_event::read_log_event(file, *description_event))
          /* EOF can't be hit here normally, so it's a real error */
          die("Could not read a Rotate_log_event event \
at offset %lu ; this could be a log format error or read error",
              tmp_pos); 
      }
      else
        break;
unknown's avatar
unknown committed
1107
    }
1108 1109 1110 1111
  }
  my_b_seek(file, pos);
}

unknown's avatar
unknown committed
1112

unknown's avatar
unknown committed
1113
static int dump_local_log_entries(const char* logname)
unknown's avatar
unknown committed
1114
{
unknown's avatar
unknown committed
1115
  File fd = -1;
1116
  IO_CACHE cache,*file= &cache;
1117
  LAST_EVENT_INFO last_event_info;
1118
  byte tmp_buff[BIN_LOG_HEADER_SIZE];
unknown's avatar
unknown committed
1119
  int error= 0;
1120 1121 1122 1123

  if (logname && logname[0] != '-')
  {
    if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
unknown's avatar
unknown committed
1124
      return 1;
1125
    if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0,
1126
		      MYF(MY_WME | MY_NABP)))
unknown's avatar
unknown committed
1127 1128
    {
      my_close(fd, MYF(MY_WME));
1129
      return 1;
unknown's avatar
unknown committed
1130
    }
1131
    check_header(file, &description_event);
1132
  }
1133
  else // reading from stdin;
1134
  {
1135
    if (init_io_cache(file, fileno(stdin), 0, READ_CACHE, (my_off_t) 0,
1136
		      0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
unknown's avatar
unknown committed
1137
      return 1;
1138
    check_header(file, &description_event);
1139
    if (start_position)
1140
    {
1141
      /* skip 'start_position' characters from stdin */
unknown's avatar
unknown committed
1142
      byte buff[IO_SIZE];
1143
      my_off_t length,tmp;
1144
      for (length= start_position_mot ; length > 0 ; length-=tmp)
1145 1146
      {
	tmp=min(length,sizeof(buff));
1147
	if (my_b_read(file, buff, (uint) tmp))
1148 1149 1150 1151
        {
          error= 1;
          goto end;
        }
1152 1153 1154 1155
      }
    }
  }

1156 1157 1158 1159
  if (!description_event || !description_event->is_valid())
    die("Invalid Format_description log event; could be out of memory");

  if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
unknown's avatar
unknown committed
1160
  {
1161 1162
    error= 1;
    goto end;
unknown's avatar
unknown committed
1163
  }
1164
  for (;;)
1165
  {
1166
    char llbuff[21];
unknown's avatar
unknown committed
1167 1168
    my_off_t old_off = my_b_tell(file);

1169
    Log_event* ev = Log_event::read_log_event(file, description_event);
1170 1171 1172
    if (!ev)
    {
      if (file->error)
unknown's avatar
unknown committed
1173 1174
      {
	fprintf(stderr,
1175 1176 1177 1178
                "Could not read entry at offset %s:"
                "Error in log format or read error\n",
                llstr(old_off,llbuff));
        error= 1;
unknown's avatar
unknown committed
1179
      }
1180
      // file->error == 0 means EOF, that's OK, we break in this case
1181 1182
      break;
    }
1183
    if ((error= process_event(&last_event_info,ev,old_off)))
unknown's avatar
unknown committed
1184
    {
1185 1186
      if (error < 0)
        error= 0;
unknown's avatar
unknown committed
1187
      break;
unknown's avatar
unknown committed
1188
    }
1189
  }
unknown's avatar
unknown committed
1190 1191

end:
1192
  if (fd >= 0)
1193
    my_close(fd, MYF(MY_WME));
1194
  end_io_cache(file);
1195
  delete description_event;
unknown's avatar
unknown committed
1196
  return error;
unknown's avatar
unknown committed
1197 1198
}

1199

unknown's avatar
unknown committed
1200 1201
int main(int argc, char** argv)
{
unknown's avatar
unknown committed
1202
  static char **defaults_argv;
1203 1204
  int exit_value= 0;
  ulonglong save_stop_position;
unknown's avatar
unknown committed
1205
  MY_INIT(argv[0]);
unknown's avatar
unknown committed
1206 1207
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);
unknown's avatar
unknown committed
1208

1209 1210
  init_time(); // for time functions

unknown's avatar
unknown committed
1211
  parse_args(&argc, (char***)&argv);
unknown's avatar
unknown committed
1212
  defaults_argv=argv;
unknown's avatar
unknown committed
1213

1214
  if (!argc)
1215 1216
  {
    usage();
unknown's avatar
unknown committed
1217
    free_defaults(defaults_argv);
unknown's avatar
unknown committed
1218
    exit(1);
1219
  }
unknown's avatar
unknown committed
1220

1221
  my_set_max_open_files(open_files_limit);
1222 1223 1224 1225 1226 1227 1228

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

1232 1233
  if (load_processor.init())
    exit(1);
1234 1235
  if (dirname_for_local_load)
    load_processor.init_by_dir_name(dirname_for_local_load);
1236 1237
  else
    load_processor.init_by_cur_dir();
unknown's avatar
unknown committed
1238

unknown's avatar
unknown committed
1239
  fprintf(result_file,
unknown's avatar
unknown committed
1240
	  "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n");
1241 1242 1243 1244 1245

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

1246 1247
  for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ;
       (--argc >= 0) && !stop_passed ; )
unknown's avatar
unknown committed
1248
  {
1249 1250
    if (argc == 0) // last log, --stop-position applies
      stop_position= save_stop_position;
unknown's avatar
unknown committed
1251 1252 1253 1254 1255
    if (dump_log_entries(*(argv++)))
    {
      exit_value=1;
      break;
    }
1256 1257
    // For next log, --start-position does not apply
    start_position= BIN_LOG_HEADER_SIZE;
unknown's avatar
unknown committed
1258
  }
1259

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

1263 1264
  if (tmpdir.list)
    free_tmpdir(&tmpdir);
1265 1266
  if (result_file != stdout)
    my_fclose(result_file, MYF(0));
1267
  cleanup();
unknown's avatar
unknown committed
1268
  free_defaults(defaults_argv);
1269
  my_free_open_file_info();
unknown's avatar
unknown committed
1270
  my_end(0);
unknown's avatar
unknown committed
1271
  exit(exit_value);
unknown's avatar
unknown committed
1272
  DBUG_RETURN(exit_value);			// Keep compilers happy
unknown's avatar
unknown committed
1273 1274 1275 1276 1277 1278 1279
}

/*
  We must include this here as it's compiled with different options for
  the server
*/

unknown's avatar
unknown committed
1280
#ifdef __WIN__
unknown's avatar
unknown committed
1281 1282 1283
#include "my_decimal.h"
#include "decimal.c"
#include "my_decimal.cpp"
unknown's avatar
unknown committed
1284 1285
#include "log_event.cpp"
#else
unknown's avatar
unknown committed
1286 1287 1288
#include "my_decimal.h"
#include "decimal.c"
#include "my_decimal.cc"
unknown's avatar
unknown committed
1289
#include "log_event.cc"
unknown's avatar
unknown committed
1290
#endif
1291 1292

FIX_GCC_LINKING_PROBLEM