mysqlbinlog.cc 24.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
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
66 67 68
static void die(const char* fmt, ...);
static MYSQL* safe_connect();

unknown's avatar
unknown committed
69

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

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
  /*
    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)
unknown's avatar
unknown committed
92
    {
93 94 95 96 97 98 99 100 101 102
      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;
unknown's avatar
unknown committed
103 104 105
    }

public:
106
  Load_log_processor() {}
unknown's avatar
unknown committed
107
  ~Load_log_processor()
108 109 110 111 112 113 114 115 116 117
  {
    destroy();
    delete_dynamic(&file_names);
  }

  int init()
  {
    return init_dynamic_array(&file_names,sizeof(Create_file_log_event*),
			      100,100 CALLER_INFO);
  }
unknown's avatar
unknown committed
118

unknown's avatar
unknown committed
119
  void init_by_dir_name(const char *dir)
unknown's avatar
unknown committed
120
    {
unknown's avatar
unknown committed
121 122
      target_dir_name_len= (convert_dirname(target_dir_name, dir, NullS) -
			    target_dir_name);
unknown's avatar
unknown committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    }
  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)
    {
146 147
      if (file_id >= file_names.elements)
        return 0;
unknown's avatar
unknown committed
148 149 150 151 152 153
      Create_file_log_event **ptr= 
	(Create_file_log_event**)file_names.buffer + file_id;
      Create_file_log_event *res= *ptr;
      *ptr= 0;
      return res;
    }
154
  int process(Create_file_log_event *ce);
unknown's avatar
unknown committed
155
  int process(Append_block_log_event *ae);
156 157 158
  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
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
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
184

unknown's avatar
unknown committed
185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
int Load_log_processor::load_old_format_file(NET* net, const char*server_fname,
					     uint server_fname_len, File file)
{
  char buf[FN_REFLEN+1];
  buf[0] = 0;
  memcpy(buf + 1, server_fname, server_fname_len + 1);
  if (my_net_write(net, buf, server_fname_len +2) || net_flush(net))
  {
    sql_print_error("Failed  requesting the remote dump of %s", server_fname);
    return -1;
  }
  
  for (;;)
  {
    uint packet_len = my_net_read(net);
    if (packet_len == 0)
    {
      if (my_net_write(net, "", 0) || net_flush(net))
      {
	sql_print_error("Failed sending the ack packet");
	return -1;
      }
      /*
	we just need to send something, as the server will read but
	not examine the packet - this is because mysql_load() sends 
	an OK when it is done
      */
      break;
    }
    else if (packet_len == packet_error)
    {
      sql_print_error("Failed reading a packet during the dump of %s ", 
		      server_fname);
      return -1;
    }
    
    if (my_write(file, (byte*) net->read_pos, packet_len,MYF(MY_WME|MY_NABP)))
      return -1;
  }
  
  return 0;
}

unknown's avatar
unknown committed
229

230
int Load_log_processor::process(Create_file_log_event *ce)
unknown's avatar
unknown committed
231 232 233 234
{
  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
235
  int error= 0;
236 237
  char *fname, *ptr;
  File file;
unknown's avatar
unknown committed
238

unknown's avatar
unknown committed
239
  if (set_dynamic(&file_names,(gptr)&ce,ce->file_id))
unknown's avatar
unknown committed
240
  {
241 242 243
    sql_print_error("Could not construct local filename %s%s",
		    target_dir_name,bname);
    return -1;
unknown's avatar
unknown committed
244
  }
unknown's avatar
unknown committed
245 246
  if (!(fname= my_malloc(full_len,MYF(MY_WME))))
    return -1;
unknown's avatar
unknown committed
247

248 249
  memcpy(fname, target_dir_name, target_dir_name_len);
  ptr= fname + target_dir_name_len;
unknown's avatar
unknown committed
250 251 252 253
  memcpy(ptr,bname,blen);
  ptr+= blen;
  ptr+= my_sprintf(ptr,(ptr,"-%x",ce->file_id));

254 255 256 257 258 259 260
  if ((file= create_unique_file(fname,ptr)) < 0)
  {
    sql_print_error("Could not construct local filename %s%s",
		    target_dir_name,bname);
    return -1;
  }
  ce->set_fname_outside_temp_buf(fname,strlen(fname));
unknown's avatar
unknown committed
261

unknown's avatar
unknown committed
262 263 264 265 266
  if (my_write(file,(byte*) ce->block,ce->block_len,MYF(MY_WME|MY_NABP)))
    error= -1;
  if (my_close(file,MYF(MY_WME)))
    error= -1;
  return error;
267 268
}

unknown's avatar
unknown committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

int Load_log_processor::process(Append_block_log_event *ae)
{
  Create_file_log_event* ce= ((ae->file_id < file_names.elements) ?
			      *((Create_file_log_event**)file_names.buffer +
				ae->file_id) :
			      0);

  if (ce)
  {
    File file;
    int error= 0;
    if (((file= my_open(ce->fname,
			O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0))
      return -1;
    if (my_write(file,(byte*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP)))
      error= -1;
    if (my_close(file,MYF(MY_WME)))
      error= -1;
    return error;
  }

  /*
    There is no Create_file event (a bad binlog or a big
    --position). Assuming it's a big --position, we just do nothing and
    print a warning.
  */
  fprintf(stderr,"Warning: ignoring Append_block as there is no \
Create_file event for file_id: %u\n",ae->file_id);
  return -1;
}


302 303
Load_log_processor load_processor;

unknown's avatar
unknown committed
304 305 306

int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, 
		  my_off_t pos, int old_format)
307 308 309
{
  char ll_buff[21];
  if ((*rec_count) >= offset)
unknown's avatar
unknown committed
310
  {
311 312 313 314 315 316 317 318 319 320 321 322
    if (!short_form)
      fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
    
    switch (ev->get_type_code()) {
    case QUERY_EVENT:
      if (one_database)
      {
	const char * log_dbname = ((Query_log_event*)ev)->db;
	if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
	{
	  (*rec_count)++;
	  delete ev;
unknown's avatar
unknown committed
323
	  return 0; // Time for next event
324 325 326
	}
      }
      ev->print(result_file, short_form, last_db);
unknown's avatar
unknown committed
327
      break;
328
    case CREATE_FILE_EVENT:
unknown's avatar
unknown committed
329
    {
330 331 332 333 334 335 336 337 338 339 340 341 342 343
      Create_file_log_event* ce= (Create_file_log_event*)ev;
      if (one_database)
      {
	/*
	  We test if this event has to be ignored. If yes, we don't save 
	      this event; this will have the good side-effect of ignoring all 
	      related Append_block and Exec_load.
	      Note that Load event from 3.23 is not tested.
	*/
	const char * log_dbname = ce->db;            
	if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
	{
	  (*rec_count)++;
	  delete ev;
unknown's avatar
unknown committed
344
	  return 0; // next
345 346 347 348 349 350 351 352 353
	}
      }
      /*
	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.
      */
354
      ce->print(result_file, short_form, last_db, TRUE);
355 356
      if (!old_format)
      {
unknown's avatar
unknown committed
357 358
	if (load_processor.process(ce))
	  break;				// Error
359 360 361 362 363 364
	ev= 0;
      }
      break;
    }
    case APPEND_BLOCK_EVENT:
      ev->print(result_file, short_form, last_db);
unknown's avatar
unknown committed
365 366
      if (load_processor.process((Append_block_log_event*) ev))
	break;					// Error
367 368 369 370 371 372 373 374 375 376 377 378 379
      break;
    case EXEC_LOAD_EVENT:
    {
      ev->print(result_file, short_form, last_db);
      Execute_load_log_event *exv= (Execute_load_log_event*)ev;
      Create_file_log_event *ce= load_processor.grab_event(exv->file_id);
      /*
	if ce is 0, it probably means that we have not seen the Create_file
	event (a bad binlog, or most probably --position is after the
	Create_file event). Print a warning comment.
      */
      if (ce)
      {
380
	ce->print(result_file, short_form, last_db, TRUE);
381 382 383 384 385 386 387 388 389 390
	my_free((char*)ce->fname,MYF(MY_WME));
	delete ce;
      }
      else
	fprintf(stderr,"Warning: ignoring Exec_load as there is no \
Create_file event for file_id: %u\n",exv->file_id);
      break;
    }
    default:
      ev->print(result_file, short_form, last_db);
unknown's avatar
unknown committed
391 392
    }
  }
393 394 395
  (*rec_count)++;
  if (ev)
    delete ev;
unknown's avatar
unknown committed
396
  return 0;
unknown's avatar
unknown committed
397 398
}

unknown's avatar
unknown committed
399

400 401 402 403 404 405
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
406
  {"database", 'd', "List entries for just this database (local log only).",
407 408
   (gptr*) &database, (gptr*) &database, 0, GET_STR_ALLOC, REQUIRED_ARG,
   0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
409
  {"force-read", 'f', "Force reading unknown binlog events.",
410 411
   (gptr*) &force_opt, (gptr*) &force_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
412
  {"help", '?', "Display this help and exit.",
413
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
414
  {"host", 'h', "Get the binlog from server.", (gptr*) &host, (gptr*) &host,
415
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
416
  {"offset", 'o', "Skip the first N entries.", (gptr*) &offset, (gptr*) &offset,
417
   0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
418
  {"password", 'p', "Password to connect to remote server.",
419
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
420
  {"port", 'P', "Use port to connect to the remote server.",
421 422
   (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0,
   0, 0, 0},
unknown's avatar
unknown committed
423
  {"position", 'j', "Start reading the binlog at position N.",
424 425
   (gptr*) &position, (gptr*) &position, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
426
  {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR,
427
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
428
  {"read-from-remote-server", 'R', "Read binary logs from a MySQL server",
429 430
   (gptr*) &remote_opt, (gptr*) &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
431
  {"short-form", 's', "Just show the queries, no extra info.",
432 433
   (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
434
  {"socket", 'S', "Socket file to use for connection.",
435
   (gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 
unknown's avatar
unknown committed
436 437
   0, 0},
  {"user", 'u', "Connect to the remote server as username.",
438 439
   (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0,
   0, 0},
unknown's avatar
unknown committed
440 441 442
  {"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},
443 444 445 446 447
  {"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
448 449 450

void sql_print_error(const char *format,...)
{
unknown's avatar
unknown committed
451 452 453 454 455 456
  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
457
}
unknown's avatar
unknown committed
458

459 460 461
static void cleanup()
{
  my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
462 463 464 465
  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));
466 467
}

unknown's avatar
unknown committed
468 469 470 471 472 473 474 475
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);
476
  cleanup();
477
  my_end(0);
unknown's avatar
unknown committed
478 479 480
  exit(1);
}

unknown's avatar
unknown committed
481 482
static void print_version()
{
483
  printf("%s Ver 2.6 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
unknown's avatar
unknown committed
484 485 486
}


unknown's avatar
unknown committed
487 488
static void usage()
{
unknown's avatar
unknown committed
489
  print_version();
unknown's avatar
unknown committed
490 491 492
  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
493 494

  printf("\
unknown's avatar
unknown committed
495
Dumps a MySQL binary log in a format usable for viewing or for piping to\n\
unknown's avatar
unknown committed
496
the mysql command line client\n\n");
497 498 499
  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
500 501
}

502
extern "C" my_bool
503 504 505
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
{
506
  bool tty_password=0;
unknown's avatar
unknown committed
507
  switch (optid) {
unknown's avatar
unknown committed
508
#ifndef DBUG_OFF
509 510 511
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    break;
unknown's avatar
unknown committed
512
#endif
513 514 515 516
  case 'd':
    one_database = 1;
    break;
  case 'p':
517 518 519 520 521 522 523 524 525 526 527
    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;
528 529 530 531 532
    break;
  case 'r':
    if (!(result_file = my_fopen(argument, O_WRONLY | O_BINARY, MYF(MY_WME))))
      exit(1);
    break;
533 534
  case 'R':
    remote_opt= 1;
535 536 537 538 539 540 541
    break;
  case 'V':
    print_version();
    exit(0);
  case '?':
    usage();
    exit(0);
unknown's avatar
unknown committed
542
  }
543 544 545
  if (tty_password)
    pass= get_tty_password(NullS);

546 547 548
  return 0;
}

unknown's avatar
unknown committed
549

550 551 552
static int parse_args(int *argc, char*** argv)
{
  int ho_error;
unknown's avatar
unknown committed
553

554
  result_file = stdout;
unknown's avatar
unknown committed
555
  load_defaults("my",load_default_groups,argc,argv);
556
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
557 558
    exit(ho_error);

unknown's avatar
unknown committed
559 560 561 562 563
  return 0;
}

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

unknown's avatar
unknown committed
566
  if(!local_mysql)
unknown's avatar
unknown committed
567
    die("Failed on mysql_init");
568 569 570
  
  mysql_options(local_mysql, MYSQL_INIT_COMMAND,
		"/*!32210 SET @@session.max_insert_delayed_threads=0*/");
unknown's avatar
unknown committed
571
  if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0))
unknown's avatar
unknown committed
572
    die("failed on connect: %s", mysql_error(local_mysql));
unknown's avatar
unknown committed
573 574 575 576

  return local_mysql;
}

unknown's avatar
unknown committed
577 578

static int dump_log_entries(const char* logname)
unknown's avatar
unknown committed
579
{
580
  if (remote_opt)
unknown's avatar
unknown committed
581 582
    return dump_remote_log_entries(logname);
  return dump_local_log_entries(logname);  
unknown's avatar
unknown committed
583 584
}

unknown's avatar
unknown committed
585

586 587 588 589 590 591
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
592

593 594
  if (mysql_query(mysql, "SELECT VERSION()") ||
      !(res = mysql_store_result(mysql)))
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
  {
    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
613

614
  switch (*version) {
615 616 617 618
  case '3':
    old_format = 1;
    break;
  case '4':
619
  case '5':
620 621 622 623 624 625 626 627 628 629 630 631
    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
632

633

unknown's avatar
unknown committed
634
static int dump_remote_log_entries(const char* logname)
unknown's avatar
unknown committed
635 636
{
  char buf[128];
637
  char last_db[FN_REFLEN+1] = "";
unknown's avatar
unknown committed
638 639
  uint len;
  NET* net = &mysql->net;
640 641
  int old_format;
  old_format = check_master_version(mysql);
unknown's avatar
unknown committed
642

643 644 645
  if (!position)
    position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam
  if (position < BIN_LOG_HEADER_SIZE)
646
  {
647
    position = BIN_LOG_HEADER_SIZE;
unknown's avatar
unknown committed
648
    // warn the user
649
    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);
650
  }
unknown's avatar
unknown committed
651
  int4store(buf, position);
652
  int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags);
unknown's avatar
unknown committed
653
  len = (uint) strlen(logname);
654 655
  int4store(buf + 6, 0);
  memcpy(buf + 10, logname,len);
656
  if (simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1))
unknown's avatar
unknown committed
657 658 659 660
  {
    fprintf(stderr,"Got fatal error sending the log dump command\n");
    return 1;
  }
unknown's avatar
unknown committed
661

662 663 664 665
  my_off_t old_off= 0;  
  ulonglong rec_count= 0;
  char fname[FN_REFLEN+1];

666
  for (;;)
unknown's avatar
unknown committed
667
  {
668
    const char *error;
unknown's avatar
unknown committed
669
    len = net_safe_read(mysql);
unknown's avatar
unknown committed
670
    if (len == packet_error)
unknown's avatar
unknown committed
671 672 673 674 675
    {
      fprintf(stderr, "Got error reading packet from server: %s\n",
	      mysql_error(mysql));
      return 1;
    }
unknown's avatar
unknown committed
676
    if (len < 8 && net->read_pos[0] == 254)
unknown's avatar
unknown committed
677 678 679
      break; // end of data
    DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
			len, net->read_pos[5]));
680 681
    Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
					      len - 1, &error, old_format);
682
    if (!ev)
unknown's avatar
unknown committed
683
    {
unknown's avatar
unknown committed
684 685
      fprintf(stderr, "Could not construct log event object\n");
      return 1;
686
    }   
unknown's avatar
unknown committed
687 688 689 690 691 692 693

    Log_event_type type= ev->get_type_code();
    if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT))
    {
      if (process_event(&rec_count,last_db,ev,old_off,old_format))
	return 1;
    }
694 695
    else
    {
unknown's avatar
unknown committed
696 697 698 699 700 701 702 703
      Load_log_event *le= (Load_log_event*)ev;
      const char *old_fname= le->fname;
      uint old_len= le->fname_len;
      File file;

      if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
	return 1;
      if (process_event(&rec_count,last_db,ev,old_off,old_format))
704
      {
unknown's avatar
unknown committed
705 706
	my_close(file,MYF(MY_WME));
	return 1;
707
      }
unknown's avatar
unknown committed
708
      if (load_processor.load_old_format_file(net,old_fname,old_len,file))
709
      {
unknown's avatar
unknown committed
710 711
	my_close(file,MYF(MY_WME));
	return 1;
712
      }
unknown's avatar
unknown committed
713
      my_close(file,MYF(MY_WME));
unknown's avatar
unknown committed
714
    }
715 716
    
    /*
unknown's avatar
unknown committed
717 718
      Let's adjust offset for remote log as for local log to produce 
      similar text..
719 720 721
    */
    if (old_off)
      old_off+= len-1;
unknown's avatar
unknown committed
722
    else
723
      old_off= BIN_LOG_HEADER_SIZE;
unknown's avatar
unknown committed
724
  }
unknown's avatar
unknown committed
725
  return 0;
unknown's avatar
unknown committed
726 727
}

728

unknown's avatar
unknown committed
729
static int check_header(IO_CACHE* file)
730
{
unknown's avatar
unknown committed
731
  byte header[BIN_LOG_HEADER_SIZE];
unknown's avatar
unknown committed
732
  byte buf[PROBE_HEADER_LEN];
733
  int old_format=0;
unknown's avatar
unknown committed
734

735 736
  my_off_t pos = my_b_tell(file);
  my_b_seek(file, (my_off_t)0);
unknown's avatar
unknown committed
737 738 739
  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
740
    die("File is not a binary log file");
unknown's avatar
unknown committed
741
  if (!my_b_read(file, buf, sizeof(buf)))
742
  {
unknown's avatar
unknown committed
743 744 745
    if (buf[4] == START_EVENT)
    {
      uint event_len;
746 747
      event_len = uint4korr(buf + EVENT_LEN_OFFSET);
      old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
unknown's avatar
unknown committed
748
    }
749 750 751 752 753
  }
  my_b_seek(file, pos);
  return old_format;
}

unknown's avatar
unknown committed
754

unknown's avatar
unknown committed
755
static int dump_local_log_entries(const char* logname)
unknown's avatar
unknown committed
756
{
unknown's avatar
unknown committed
757
  File fd = -1;
758
  IO_CACHE cache,*file= &cache;
759
  ulonglong rec_count = 0;
760 761
  char last_db[FN_REFLEN+1];
  byte tmp_buff[BIN_LOG_HEADER_SIZE];
762
  bool old_format = 0;
unknown's avatar
unknown committed
763
  int error= 0;
764

unknown's avatar
unknown committed
765
  last_db[0]= 0;
unknown's avatar
unknown committed
766

767 768 769
  if (logname && logname[0] != '-')
  {
    if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
unknown's avatar
unknown committed
770
      return 1;
771 772
    if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0,
		      MYF(MY_WME | MY_NABP)))
unknown's avatar
unknown committed
773 774
    {
      my_close(fd, MYF(MY_WME));
775
      exit(1);
unknown's avatar
unknown committed
776
    }
unknown's avatar
unknown committed
777
    old_format = check_header(file);
778 779 780
  }
  else
  {
781
    if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0,
782
		      0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
unknown's avatar
unknown committed
783
      return 1;
unknown's avatar
unknown committed
784
    old_format = check_header(file);
785 786 787
    if (position)
    {
      /* skip 'position' characters from stdout */
unknown's avatar
unknown committed
788
      byte buff[IO_SIZE];
789
      my_off_t length,tmp;
790
      for (length= (my_off_t) position ; length > 0 ; length-=tmp)
791 792
      {
	tmp=min(length,sizeof(buff));
793
	if (my_b_read(file, buff, (uint) tmp))
unknown's avatar
unknown committed
794 795 796 797
	{
	  error= 1;
	  goto end;
	}
798 799 800 801 802 803 804
      }
    }
    file->pos_in_file=position;
    file->seek_not_done=0;
  }

  if (!position)
unknown's avatar
unknown committed
805 806 807 808 809 810 811 812 813
  {
    // Skip header
    if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
    {
      error= 1;
      goto end;
    }
  }

814
  for (;;)
815
  {
816
    char llbuff[21];
unknown's avatar
unknown committed
817 818
    my_off_t old_off = my_b_tell(file);

819
    Log_event* ev = Log_event::read_log_event(file, old_format);
820 821 822
    if (!ev)
    {
      if (file->error)
unknown's avatar
unknown committed
823 824 825 826 827 828 829
      {
	fprintf(stderr,
		"Could not read entry at offset %s:"
		"Error in log format or read error\n",
		llstr(old_off,llbuff));
	error= 1;
      }
830
      // file->error == 0 means EOF, that's OK, we break in this case
831 832
      break;
    }
unknown's avatar
unknown committed
833 834 835 836 837
    if (process_event(&rec_count,last_db,ev,old_off,false))
    {
      error= 1;
      break;
    }
838
  }
unknown's avatar
unknown committed
839 840

end:
841
  if (fd >= 0)
842
    my_close(fd, MYF(MY_WME));
843
  end_io_cache(file);
unknown's avatar
unknown committed
844
  return error;
unknown's avatar
unknown committed
845 846
}

unknown's avatar
unknown committed
847

unknown's avatar
unknown committed
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
#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
917

unknown's avatar
unknown committed
918

unknown's avatar
unknown committed
919 920
int main(int argc, char** argv)
{
unknown's avatar
unknown committed
921
  static char **defaults_argv;
unknown's avatar
unknown committed
922
  int exit_value;
unknown's avatar
unknown committed
923
  MY_INIT(argv[0]);
unknown's avatar
unknown committed
924

unknown's avatar
unknown committed
925
  parse_args(&argc, (char***)&argv);
unknown's avatar
unknown committed
926
  defaults_argv=argv;
unknown's avatar
unknown committed
927

928
  if (!argc)
929 930
  {
    usage();
unknown's avatar
unknown committed
931
    free_defaults(defaults_argv);
unknown's avatar
unknown committed
932
    exit(1);
933
  }
unknown's avatar
unknown committed
934

935
  if (remote_opt)
936
    mysql = safe_connect();
unknown's avatar
unknown committed
937

unknown's avatar
unknown committed
938 939 940 941 942 943
  MY_TMPDIR tmpdir;
  tmpdir.list= 0;
  if (!dirname_for_local_load)
  {
    if (init_tmpdir(&tmpdir, 0))
      exit(1);
944
    dirname_for_local_load= my_strdup(my_tmpdir(&tmpdir),MY_WME);
unknown's avatar
unknown committed
945 946
  }

947 948
  if (load_processor.init())
    exit(1);
unknown's avatar
unknown committed
949 950 951 952 953
  if (dirname_for_local_load)
    load_processor.init_by_dir_name(dirname_for_local_load);
  else
    load_processor.init_by_cur_dir();

unknown's avatar
unknown committed
954
  exit_value= 0;
955
  while (--argc >= 0)
unknown's avatar
unknown committed
956 957 958 959 960 961 962
  {
    if (dump_log_entries(*(argv++)))
    {
      exit_value=1;
      break;
    }
  }
963

unknown's avatar
unknown committed
964 965
  if (tmpdir.list)
    free_tmpdir(&tmpdir);
966 967
  if (result_file != stdout)
    my_fclose(result_file, MYF(0));
968
  if (remote_opt)
unknown's avatar
unknown committed
969
    mysql_close(mysql);
970
  cleanup();
unknown's avatar
unknown committed
971 972
  free_defaults(defaults_argv);
  my_end(0);
unknown's avatar
unknown committed
973 974
  exit(exit_value);
  return exit_value;				// Keep compilers happy
unknown's avatar
unknown committed
975 976 977 978 979 980 981
}

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

unknown's avatar
unknown committed
982 983 984
#ifdef __WIN__
#include "log_event.cpp"
#else
unknown's avatar
unknown committed
985
#include "log_event.cc"
unknown's avatar
unknown committed
986
#endif
987 988

FIX_GCC_LINKING_PROBLEM