mysqlbinlog.cc 21.5 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2001 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 17 18
   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 */

#define MYSQL_CLIENT
#undef MYSQL_SERVER
unknown's avatar
unknown committed
19
#include "client_priv.h"
unknown's avatar
unknown committed
20
#include <time.h>
unknown's avatar
unknown committed
21
#include <assert.h>
unknown's avatar
unknown committed
22
#include "log_event.h"
unknown's avatar
unknown committed
23

24
#define BIN_LOG_HEADER_SIZE	4
unknown's avatar
unknown committed
25
#define PROBE_HEADER_LEN	(EVENT_LEN_OFFSET+4)
26

27

unknown's avatar
unknown committed
28 29
#define CLIENT_CAPABILITIES	(CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)

unknown's avatar
unknown committed
30
char server_version[SERVER_VERSION_LENGTH];
31
ulong server_id = 0;
unknown's avatar
unknown committed
32 33 34 35 36

// needed by net_serv.c
ulong bytes_sent = 0L, bytes_received = 0L;
ulong mysqld_net_retry_count = 10L;
uint test_flags = 0; 
unknown's avatar
unknown committed
37 38

static FILE *result_file;
unknown's avatar
unknown committed
39 40 41 42

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

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

47
static bool one_database = 0;
48 49
static const char* database= 0;
static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
50
static ulonglong offset = 0;
51
static const char* host = 0;
unknown's avatar
unknown committed
52
static int port = MYSQL_PORT;
53
static const char* sock= 0;
54
static const char* user = 0;
55
static char* pass = 0;
56
static ulonglong position = 0;
unknown's avatar
unknown committed
57 58 59
static short binlog_flags = 0; 
static MYSQL* mysql = NULL;

unknown's avatar
unknown committed
60 61
static const char* dirname_for_local_load= 0;

unknown's avatar
unknown committed
62 63 64 65 66 67 68
static void dump_local_log_entries(const char* logname);
static void dump_remote_log_entries(const char* logname);
static void dump_log_entries(const char* logname);
static void dump_remote_file(NET* net, const char* fname);
static void die(const char* fmt, ...);
static MYSQL* safe_connect();

unknown's avatar
unknown committed
69 70 71 72 73 74
class Load_log_processor
{
  char target_dir_name[MY_NFILE];
  int target_dir_name_len;
  DYNAMIC_ARRAY file_names;

unknown's avatar
unknown committed
75
  const char *create_file(Create_file_log_event *ce);
unknown's avatar
unknown committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
  void append_to_file(const char* fname, int flags, 
		      gptr data, uint size)
    {
      File file;
      if (((file= my_open(fname,flags,MYF(MY_WME))) < 0) ||
	  my_write(file,(byte*) data,size,MYF(MY_WME|MY_NABP)) ||
	  my_close(file,MYF(MY_WME)))
	exit(1);
    }

public:
  Load_log_processor()
    {
      init_dynamic_array(&file_names,sizeof(Create_file_log_event*),
			 100,100 CALLER_INFO);
    }

  ~Load_log_processor()
    {
      destroy();
      delete_dynamic(&file_names);
    }

unknown's avatar
unknown committed
99
  void init_by_dir_name(const char *dir)
unknown's avatar
unknown committed
100
    {
unknown's avatar
unknown committed
101 102
      target_dir_name_len= (convert_dirname(target_dir_name, dir, NullS) -
			    target_dir_name);
unknown's avatar
unknown committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    }
  void init_by_cur_dir()
    {
      if (my_getwd(target_dir_name,sizeof(target_dir_name),MYF(MY_WME)))
	exit(1);
      target_dir_name_len= strlen(target_dir_name);
    }
  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)
    {
126 127
      if (file_id >= file_names.elements)
        return 0;
unknown's avatar
unknown committed
128 129 130 131 132 133 134 135 136
      Create_file_log_event **ptr= 
	(Create_file_log_event**)file_names.buffer + file_id;
      Create_file_log_event *res= *ptr;
      *ptr= 0;
      return res;
    }
  void process(Create_file_log_event *ce)
    {
      const char *fname= create_file(ce);
unknown's avatar
unknown committed
137 138
      append_to_file(fname,O_CREAT|O_EXCL|O_BINARY|O_WRONLY,ce->block,
		     ce->block_len);
unknown's avatar
unknown committed
139 140 141
    }
  void process(Append_block_log_event *ae)
    {
142 143
      Create_file_log_event* ce= (ae->file_id < file_names.elements) ?
          *((Create_file_log_event**)file_names.buffer + ae->file_id) : 0;
144 145
        
      if (ce)
unknown's avatar
unknown committed
146 147
        append_to_file(ce->fname,O_APPEND|O_BINARY|O_WRONLY, ae->block,
		       ae->block_len);
148
      else
unknown's avatar
unknown committed
149
      {
150 151 152 153 154 155 156
        /*
          There is no Create_file event (a bad binlog or a big
          --position). Assuming it's a big --position, we just do nothing and
          print a warning.
        */
	fprintf(stderr,"Warning: ignoring Append_block as there is no \
Create_file event for file_id: %u\n",ae->file_id);
unknown's avatar
unknown committed
157
      }
unknown's avatar
unknown committed
158 159 160
    }
};

unknown's avatar
unknown committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 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

const char *Load_log_processor::create_file(Create_file_log_event *ce)
{
  const char *bname= ce->fname+dirname_length(ce->fname);
  uint blen= ce->fname_len - (bname-ce->fname);
  uint full_len= target_dir_name_len + blen + 9 + 9 + 1;
  uint version= 0;
  char *tmp, *ptr;

  if (!(tmp= my_malloc(full_len,MYF(MY_WME))) ||
      set_dynamic(&file_names,(gptr)&ce,ce->file_id))
  {
    die("Could not construct local filename %s%s",target_dir_name,bname);
    return 0;
  }

  memcpy(tmp, target_dir_name, target_dir_name_len);
  ptr= tmp+ target_dir_name_len;
  memcpy(ptr,bname,blen);
  ptr+= blen;
  ptr+= my_sprintf(ptr,(ptr,"-%x",ce->file_id));

  /*
    Note that this code has a possible race condition if there was was
    many simultaneous clients running which tried to create files at the same
    time. Fortunately this should never be the case.

    A better way to do this would be to use 'create_tmp_file() and avoid this
    race condition altogether on the expense of getting more cryptic file
    names.
  */
  for (;;)
  {
    sprintf(ptr,"-%x",version);
    if (access(tmp,F_OK))
      break;
    /* If we have to try more than 1000 times, something is seriously wrong */
    if (version++ > 1000)
    {
      die("Could not construct local filename %s%s",target_dir_name,bname);
      return 0;
    }
  }
204
  ce->set_fname_outside_temp_buf(tmp,strlen(tmp));
unknown's avatar
unknown committed
205 206 207 208
  return tmp;
}


unknown's avatar
unknown committed
209 210
Load_log_processor load_processor;

211 212 213 214 215 216
static struct my_option my_long_options[] =
{
#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
unknown's avatar
unknown committed
217
  {"database", 'd', "List entries for just this database (local log only).",
218 219
   (gptr*) &database, (gptr*) &database, 0, GET_STR_ALLOC, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
220
  {"force-read", 'f', "Force reading unknown binlog events.",
221 222
   (gptr*) &force_opt, (gptr*) &force_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
223
  {"help", '?', "Display this help and exit.",
224
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
225
  {"host", 'h', "Get the binlog from server.", (gptr*) &host, (gptr*) &host,
226
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
227
  {"offset", 'o', "Skip the first N entries.", (gptr*) &offset, (gptr*) &offset,
228
   0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
229
  {"password", 'p', "Password to connect to remote server.",
230
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
231
  {"port", 'P', "Use port to connect to the remote server.",
232 233
   (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0,
   0, 0, 0},
unknown's avatar
unknown committed
234
  {"position", 'j', "Start reading the binlog at position N.",
235 236
   (gptr*) &position, (gptr*) &position, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
237
  {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
238
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
239
  {"read-from-remote-server", 'R', "Read binary logs from a MySQL server",
240 241
   (gptr*) &remote_opt, (gptr*) &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
242
  {"short-form", 's', "Just show the queries, no extra info.",
243 244
   (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
245 246 247 248
  {"socket", 'S', "Socket file to use for connection.",
   (gptr*) &sock, (gptr*) &sock, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 
   0, 0},
  {"user", 'u', "Connect to the remote server as username.",
249 250
   (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
251 252 253
  {"local-load", 'l', "Prepare files for local load in directory.",
   (gptr*) &dirname_for_local_load, (gptr*) &dirname_for_local_load, 0,
   GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},
254 255 256 257 258
  {"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
259 260 261

void sql_print_error(const char *format,...)
{
unknown's avatar
unknown committed
262 263 264 265 266 267
  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
268
}
unknown's avatar
unknown committed
269

270 271 272 273 274
static void cleanup()
{
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
}

unknown's avatar
unknown committed
275 276 277 278 279 280 281 282
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);
283
  cleanup();
unknown's avatar
unknown committed
284 285 286
  exit(1);
}

unknown's avatar
unknown committed
287 288
static void print_version()
{
unknown's avatar
unknown committed
289
  printf("%s Ver 2.4 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
unknown's avatar
unknown committed
290 291 292
}


unknown's avatar
unknown committed
293 294
static void usage()
{
unknown's avatar
unknown committed
295
  print_version();
unknown's avatar
unknown committed
296 297 298
  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
299 300

  printf("\
unknown's avatar
unknown committed
301
Dumps a MySQL binary log in a format usable for viewing or for piping to\n\
unknown's avatar
unknown committed
302
the mysql command line client\n\n");
303 304 305
  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
306 307 308 309 310
}

static void dump_remote_file(NET* net, const char* fname)
{
  char buf[FN_REFLEN+1];
unknown's avatar
unknown committed
311
  uint len = (uint) strlen(fname);
unknown's avatar
unknown committed
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
  buf[0] = 0;
  memcpy(buf + 1, fname, len + 1);
  if(my_net_write(net, buf, len +2) || net_flush(net))
    die("Failed  requesting the remote dump of %s", fname);
  for(;;)
    {
      uint packet_len = my_net_read(net);
      if(packet_len == 0)
	{
	  if(my_net_write(net, "", 0) || net_flush(net))
	    die("Failed sending the ack packet");

	  // we just need to send something, as the server will read but
	  // not examine the packet - this is because mysql_load() sends an OK when it is done
	  break;
	}
      else if(packet_len == packet_error)
	die("Failed reading a packet during the dump of %s ", fname);

      if(!short_form)
332
	(void)my_fwrite(result_file, (byte*) net->read_pos, packet_len,MYF(0));
unknown's avatar
unknown committed
333 334
    }

335
  fflush(result_file);
unknown's avatar
unknown committed
336 337 338
}


339
extern "C" my_bool
340 341 342
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
343
  bool tty_password=0;
unknown's avatar
unknown committed
344
  switch (optid) {
unknown's avatar
unknown committed
345
#ifndef DBUG_OFF
346 347 348
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    break;
unknown's avatar
unknown committed
349
#endif
350 351 352 353
  case 'd':
    one_database = 1;
    break;
  case 'p':
354 355 356 357 358 359 360 361 362 363 364
    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;
365 366 367 368 369
    break;
  case 'r':
    if (!(result_file = my_fopen(argument, O_WRONLY | O_BINARY, MYF(MY_WME))))
      exit(1);
    break;
370 371
  case 'R':
    remote_opt= 1;
372 373 374 375 376 377 378
    break;
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(0);
unknown's avatar
unknown committed
379
  }
380 381 382
  if (tty_password)
    pass= get_tty_password(NullS);

383 384 385
  return 0;
}

unknown's avatar
unknown committed
386

387 388 389
static int parse_args(int *argc, char*** argv)
{
  int ho_error;
unknown's avatar
unknown committed
390

391
  result_file = stdout;
unknown's avatar
unknown committed
392
  load_defaults("my",load_default_groups,argc,argv);
393
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
394 395
    exit(ho_error);

unknown's avatar
unknown committed
396 397 398 399 400
  return 0;
}

static MYSQL* safe_connect()
{
unknown's avatar
unknown committed
401
  MYSQL *local_mysql = mysql_init(NULL);
unknown's avatar
unknown committed
402
  if(!local_mysql)
unknown's avatar
unknown committed
403
    die("Failed on mysql_init");
unknown's avatar
unknown committed
404

unknown's avatar
unknown committed
405
  if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0))
unknown's avatar
unknown committed
406
    die("failed on connect: %s", mysql_error(local_mysql));
unknown's avatar
unknown committed
407 408 409 410 411 412

  return local_mysql;
}

static void dump_log_entries(const char* logname)
{
413
  if (remote_opt)
unknown's avatar
unknown committed
414 415 416 417 418
    dump_remote_log_entries(logname);
  else
    dump_local_log_entries(logname);  
}

419 420 421 422 423 424
static int check_master_version(MYSQL* mysql)
{
  MYSQL_RES* res = 0;
  MYSQL_ROW row;
  const char* version;
  int old_format = 0;
unknown's avatar
unknown committed
425

426 427
  if (mysql_query(mysql, "SELECT VERSION()") ||
      !(res = mysql_store_result(mysql)))
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
  {
    mysql_close(mysql);
    die("Error checking master version: %s",
		    mysql_error(mysql));
  }
  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
446

447
  switch (*version) {
448 449 450 451
  case '3':
    old_format = 1;
    break;
  case '4':
452
  case '5':
453 454 455 456 457 458 459 460 461 462 463 464
    old_format = 0;
    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);
  return old_format;
}
unknown's avatar
unknown committed
465

466

unknown's avatar
unknown committed
467 468 469
static void dump_remote_log_entries(const char* logname)
{
  char buf[128];
470
  char last_db[FN_REFLEN+1] = "";
unknown's avatar
unknown committed
471 472
  uint len;
  NET* net = &mysql->net;
473 474
  int old_format;
  old_format = check_master_version(mysql);
unknown's avatar
unknown committed
475

476 477 478
  if (!position)
    position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam
  if (position < BIN_LOG_HEADER_SIZE)
479
  {
480
    position = BIN_LOG_HEADER_SIZE;
481
    // warn the guity
482
    sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE);
483
  }
unknown's avatar
unknown committed
484
  int4store(buf, position);
485
  int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
unknown's avatar
unknown committed
486
  len = (uint) strlen(logname);
487 488
  int4store(buf + 6, 0);
  memcpy(buf + 10, logname,len);
489
  if (simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1))
unknown's avatar
unknown committed
490
    die("Error sending the log dump command");
unknown's avatar
unknown committed
491

492
  for (;;)
unknown's avatar
unknown committed
493
  {
494
    const char *error;
unknown's avatar
unknown committed
495
    len = net_safe_read(mysql);
unknown's avatar
unknown committed
496
    if (len == packet_error)
unknown's avatar
unknown committed
497
      die("Error reading packet from server: %s", mysql_error(mysql));
unknown's avatar
unknown committed
498
    if (len < 8 && net->read_pos[0] == 254)
unknown's avatar
unknown committed
499 500 501
      break; // end of data
    DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
			len, net->read_pos[5]));
502 503
    Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
					      len - 1, &error, old_format);
504
    if (ev)
unknown's avatar
unknown committed
505
    {
506
      ev->print(result_file, short_form, last_db);
507
      if (ev->get_type_code() == LOAD_EVENT)
unknown's avatar
unknown committed
508 509 510 511 512 513 514 515
	dump_remote_file(net, ((Load_log_event*)ev)->fname);
      delete ev;
    }
    else
      die("Could not construct log event object");
  }
}

516

unknown's avatar
unknown committed
517
static int check_header(IO_CACHE* file)
518
{
unknown's avatar
unknown committed
519
  byte header[BIN_LOG_HEADER_SIZE];
unknown's avatar
unknown committed
520
  byte buf[PROBE_HEADER_LEN];
521
  int old_format=0;
unknown's avatar
unknown committed
522

523 524
  my_off_t pos = my_b_tell(file);
  my_b_seek(file, (my_off_t)0);
unknown's avatar
unknown committed
525 526 527
  if (my_b_read(file, header, sizeof(header)))
    die("Failed reading header;  Probably an empty file");
  if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
unknown's avatar
unknown committed
528
    die("File is not a binary log file");
unknown's avatar
unknown committed
529
  if (!my_b_read(file, buf, sizeof(buf)))
530
  {
unknown's avatar
unknown committed
531 532 533
    if (buf[4] == START_EVENT)
    {
      uint event_len;
534 535
      event_len = uint4korr(buf + EVENT_LEN_OFFSET);
      old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
unknown's avatar
unknown committed
536
    }
537 538 539 540 541
  }
  my_b_seek(file, pos);
  return old_format;
}

unknown's avatar
unknown committed
542

unknown's avatar
unknown committed
543 544
static void dump_local_log_entries(const char* logname)
{
unknown's avatar
unknown committed
545
  File fd = -1;
546
  IO_CACHE cache,*file= &cache;
547
  ulonglong rec_count = 0;
548 549
  char last_db[FN_REFLEN+1];
  byte tmp_buff[BIN_LOG_HEADER_SIZE];
550
  bool old_format = 0;
551

unknown's avatar
unknown committed
552 553
  last_db[0]=0;

554 555 556 557 558 559 560
  if (logname && logname[0] != '-')
  {
    if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
      exit(1);
    if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0,
		      MYF(MY_WME | MY_NABP)))
      exit(1);
unknown's avatar
unknown committed
561
    old_format = check_header(file);
562 563 564
  }
  else
  {
565
    if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0,
566 567
		      0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
      exit(1);
unknown's avatar
unknown committed
568
    old_format = check_header(file);
569 570 571
    if (position)
    {
      /* skip 'position' characters from stdout */
unknown's avatar
unknown committed
572
      byte buff[IO_SIZE];
573
      my_off_t length,tmp;
574
      for (length= (my_off_t) position ; length > 0 ; length-=tmp)
575 576
      {
	tmp=min(length,sizeof(buff));
577
	if (my_b_read(file, buff, (uint) tmp))
578 579 580 581 582 583 584 585
	  exit(1);
      }
    }
    file->pos_in_file=position;
    file->seek_not_done=0;
  }

  if (!position)
unknown's avatar
unknown committed
586
    my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE); // Skip header
587
  for (;;)
588
  {
589
    char llbuff[21];
unknown's avatar
unknown committed
590 591
    my_off_t old_off = my_b_tell(file);

592
    Log_event* ev = Log_event::read_log_event(file, old_format);
593 594 595
    if (!ev)
    {
      if (file->error)
596 597
	die("\
Could not read entry at offset %s : Error in log format or read error",
unknown's avatar
unknown committed
598
	    llstr(old_off,llbuff));
599
      // file->error == 0 means EOF, that's OK, we break in this case
600 601 602
      break;
    }
    if (rec_count >= offset)
unknown's avatar
unknown committed
603
    {
604 605 606 607 608 609
      if (!short_form)
        fprintf(result_file, "# at %s\n",llstr(old_off,llbuff));
      
      switch (ev->get_type_code()) {
      case QUERY_EVENT:
        if (one_database)
610 611 612 613 614 615 616 617 618
        {
          const char * log_dbname = ((Query_log_event*)ev)->db;
          if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
          {
            rec_count++;
            delete ev;
            continue; // next
          }
        }
619 620
	ev->print(result_file, short_form, last_db);
        break;
unknown's avatar
unknown committed
621 622 623
      case CREATE_FILE_EVENT:
      {
	Create_file_log_event* ce= (Create_file_log_event*)ev;
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
        if (one_database)
        {
          /*
            We test if this event has to be ignored. If yes, we don't save this
            event; this will have the good side-effect of ignoring all related
            Append_block and Exec_load.
            Note that Load event from 3.23 is not tested.
          */
          const char * log_dbname = ce->db;            
          if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
          {
            rec_count++;
            delete ev;
            continue; // next
          }
        }
unknown's avatar
unknown committed
640
        /*
641 642 643 644 645
          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.
unknown's avatar
unknown committed
646 647
        */
	ce->print(result_file, short_form, last_db, true);
unknown's avatar
unknown committed
648 649 650 651 652 653 654 655 656 657 658 659 660
	load_processor.process(ce);
	ev= 0;
	break;
      }
      case APPEND_BLOCK_EVENT:
	ev->print(result_file, short_form, last_db);
	load_processor.process((Append_block_log_event*)ev);
	break;
      case EXEC_LOAD_EVENT:
      {
	ev->print(result_file, short_form, last_db);
	Execute_load_log_event *exv= (Execute_load_log_event*)ev;
	Create_file_log_event *ce= load_processor.grab_event(exv->file_id);
661 662 663 664 665 666 667 668 669 670 671 672 673 674
        /*
          if ce is 0, it probably means that we have not seen the Create_file
          event (a bad binlog, or most probably --position is after the
          Create_file event). Print a warning comment.
        */
        if (ce)
        {
          ce->print(result_file, short_form, last_db,true);
          my_free((char*)ce->fname,MYF(MY_WME));
          delete ce;
        }
        else
          fprintf(stderr,"Warning: ignoring Exec_load as there is no \
Create_file event for file_id: %u\n",exv->file_id);
unknown's avatar
unknown committed
675 676 677 678 679
	break;
      }
      default:
	ev->print(result_file, short_form, last_db);
      }
unknown's avatar
unknown committed
680
    }
681
    rec_count++;
unknown's avatar
unknown committed
682 683
    if (ev)
      delete ev;
684
  }
685
  if (fd >= 0)
686
    my_close(fd, MYF(MY_WME));
687
  end_io_cache(file);
unknown's avatar
unknown committed
688 689
}

unknown's avatar
unknown committed
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
#if MYSQL_VERSION_ID < 40101

typedef struct st_my_tmpdir
{
  char **list;
  uint cur, max;
} MY_TMPDIR;

#if defined( __WIN__) || defined(OS2)
#define DELIM ';'
#else
#define DELIM ':'
#endif

my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist)
{
  char *end, *copy;
  char buff[FN_REFLEN];
  DYNAMIC_ARRAY t_arr;
  if (my_init_dynamic_array(&t_arr, sizeof(char*), 1, 5))
    return TRUE;
  if (!pathlist || !pathlist[0])
  {
    /* Get default temporary directory */
    pathlist=getenv("TMPDIR");	/* Use this if possible */
#if defined( __WIN__) || defined(OS2)
    if (!pathlist)
      pathlist=getenv("TEMP");
    if (!pathlist)
      pathlist=getenv("TMP");
#endif
    if (!pathlist || !pathlist[0])
      pathlist=(char*) P_tmpdir;
  }
  do
  {
    end=strcend(pathlist, DELIM);
    convert_dirname(buff, pathlist, end);
    if (!(copy=my_strdup(buff, MYF(MY_WME))))
      return TRUE;
    if (insert_dynamic(&t_arr, (gptr)&copy))
      return TRUE;
    pathlist=end+1;
  }
  while (*end);
  freeze_size(&t_arr);
  tmpdir->list=(char **)t_arr.buffer;
  tmpdir->max=t_arr.elements-1;
  tmpdir->cur=0;
  return FALSE;
}

char *my_tmpdir(MY_TMPDIR *tmpdir)
{
  char *dir;
  dir=tmpdir->list[tmpdir->cur];
  tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur+1;
  return dir;
}

void free_tmpdir(MY_TMPDIR *tmpdir)
{
  uint i;
  for (i=0; i<=tmpdir->max; i++)
    my_free(tmpdir->list[i], MYF(0));
  my_free((gptr)tmpdir->list, MYF(0));
}

#endif
759

unknown's avatar
unknown committed
760 761
int main(int argc, char** argv)
{
unknown's avatar
unknown committed
762
  static char **defaults_argv;
unknown's avatar
unknown committed
763
  MY_INIT(argv[0]);
unknown's avatar
unknown committed
764

unknown's avatar
unknown committed
765
  parse_args(&argc, (char***)&argv);
unknown's avatar
unknown committed
766
  defaults_argv=argv;
unknown's avatar
unknown committed
767

768
  if (!argc)
769 770
  {
    usage();
unknown's avatar
unknown committed
771
    free_defaults(defaults_argv);
772 773
    return -1;
  }
unknown's avatar
unknown committed
774

775
  if (remote_opt)
776
    mysql = safe_connect();
unknown's avatar
unknown committed
777

unknown's avatar
unknown committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791
  MY_TMPDIR tmpdir;
  tmpdir.list= 0;
  if (!dirname_for_local_load)
  {
    if (init_tmpdir(&tmpdir, 0))
      exit(1);
    dirname_for_local_load= my_tmpdir(&tmpdir);
  }

  if (dirname_for_local_load)
    load_processor.init_by_dir_name(dirname_for_local_load);
  else
    load_processor.init_by_cur_dir();

792 793 794
  while (--argc >= 0)
    dump_log_entries(*(argv++));

unknown's avatar
unknown committed
795 796
  if (tmpdir.list)
    free_tmpdir(&tmpdir);
797 798
  if (result_file != stdout)
    my_fclose(result_file, MYF(0));
799
  if (remote_opt)
unknown's avatar
unknown committed
800
    mysql_close(mysql);
801
  cleanup();
unknown's avatar
unknown committed
802 803
  free_defaults(defaults_argv);
  my_end(0);
unknown's avatar
unknown committed
804 805 806 807 808 809 810 811
  return 0;
}

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

unknown's avatar
unknown committed
812 813 814
#ifdef __WIN__
#include "log_event.cpp"
#else
unknown's avatar
unknown committed
815
#include "log_event.cc"
unknown's avatar
unknown committed
816
#endif
817 818

FIX_GCC_LINKING_PROBLEM