log_event.cc 142 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2004 MySQL AB
unknown's avatar
unknown committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   
   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.
   
   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.
   
   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 */


#ifndef MYSQL_CLIENT
#ifdef __GNUC__
#pragma implementation				// gcc: Class implementation
#endif
#include  "mysql_priv.h"
23
#include "slave.h"
24
#include <my_dir.h>
unknown's avatar
unknown committed
25 26
#endif /* MYSQL_CLIENT */

unknown's avatar
unknown committed
27
#define log_cs	&my_charset_latin1
28

unknown's avatar
unknown committed
29
/*
30
  pretty_print_str()
unknown's avatar
unknown committed
31
*/
32

33
#ifdef MYSQL_CLIENT
34
static void pretty_print_str(FILE* file, char* str, int len)
unknown's avatar
unknown committed
35
{
36
  char* end = str + len;
unknown's avatar
unknown committed
37
  fputc('\'', file);
38 39
  while (str < end)
  {
unknown's avatar
unknown committed
40
    char c;
41 42 43 44 45 46 47 48 49 50 51 52
    switch ((c=*str++)) {
    case '\n': fprintf(file, "\\n"); break;
    case '\r': fprintf(file, "\\r"); break;
    case '\\': fprintf(file, "\\\\"); break;
    case '\b': fprintf(file, "\\b"); break;
    case '\t': fprintf(file, "\\t"); break;
    case '\'': fprintf(file, "\\'"); break;
    case 0   : fprintf(file, "\\0"); break;
    default:
      fputc(c, file);
      break;
    }
53 54
  }
  fputc('\'', file);
unknown's avatar
unknown committed
55
}
unknown's avatar
unknown committed
56
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
57

unknown's avatar
unknown committed
58

unknown's avatar
unknown committed
59
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
60

61 62 63 64 65 66 67 68
static void clear_all_errors(THD *thd, struct st_relay_log_info *rli)
{
  thd->query_error = 0;
  thd->clear_error();
  *rli->last_slave_error = 0;
  rli->last_slave_errno = 0;
}

unknown's avatar
unknown committed
69

unknown's avatar
unknown committed
70
/*
unknown's avatar
unknown committed
71
  Ignore error code specified on command line
unknown's avatar
unknown committed
72
*/
73

74 75
inline int ignored_error_code(int err_code)
{
unknown's avatar
unknown committed
76 77
  return ((err_code == ER_SLAVE_IGNORED_TABLE) ||
          (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code)));
78
}
unknown's avatar
SCRUM  
unknown committed
79
#endif
80

unknown's avatar
unknown committed
81

unknown's avatar
unknown committed
82
/*
83
  pretty_print_str()
unknown's avatar
unknown committed
84
*/
unknown's avatar
unknown committed
85

unknown's avatar
unknown committed
86
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
87
static char *pretty_print_str(char *packet, char *str, int len)
unknown's avatar
unknown committed
88
{
unknown's avatar
unknown committed
89 90
  char *end= str + len;
  char *pos= packet;
91
  *pos++= '\'';
92 93 94
  while (str < end)
  {
    char c;
95
    switch ((c=*str++)) {
unknown's avatar
unknown committed
96 97 98 99 100 101 102
    case '\n': *pos++= '\\'; *pos++= 'n'; break;
    case '\r': *pos++= '\\'; *pos++= 'r'; break;
    case '\\': *pos++= '\\'; *pos++= '\\'; break;
    case '\b': *pos++= '\\'; *pos++= 'b'; break;
    case '\t': *pos++= '\\'; *pos++= 't'; break;
    case '\'': *pos++= '\\'; *pos++= '\''; break;
    case 0   : *pos++= '\\'; *pos++= '0'; break;
103
    default:
unknown's avatar
unknown committed
104
      *pos++= c;
105 106
      break;
    }
unknown's avatar
unknown committed
107
  }
108 109
  *pos++= '\'';
  return pos;
unknown's avatar
unknown committed
110
}
unknown's avatar
unknown committed
111
#endif /* !MYSQL_CLIENT */
112

unknown's avatar
unknown committed
113

unknown's avatar
unknown committed
114
/*
115
  slave_load_file_stem()
unknown's avatar
unknown committed
116
*/
unknown's avatar
unknown committed
117

unknown's avatar
SCRUM  
unknown committed
118
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
119 120 121
static inline char* slave_load_file_stem(char*buf, uint file_id,
					 int event_server_id)
{
unknown's avatar
unknown committed
122
  fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", MY_UNPACK_FILENAME);
123 124 125 126 127 128 129
  buf = strend(buf);
  buf = int10_to_str(::server_id, buf, 10);
  *buf++ = '-';
  buf = int10_to_str(event_server_id, buf, 10);
  *buf++ = '-';
  return int10_to_str(file_id, buf, 10);
}
unknown's avatar
SCRUM  
unknown committed
130
#endif
131

unknown's avatar
unknown committed
132

unknown's avatar
unknown committed
133
/*
134 135
  Delete all temporary files used for SQL_LOAD.

unknown's avatar
unknown committed
136 137
  SYNOPSIS
    cleanup_load_tmpdir()
unknown's avatar
unknown committed
138
*/
139

unknown's avatar
SCRUM  
unknown committed
140
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
141 142 143 144 145
static void cleanup_load_tmpdir()
{
  MY_DIR *dirp;
  FILEINFO *file;
  uint i;
unknown's avatar
unknown committed
146
  char fname[FN_REFLEN], prefbuf[31], *p;
unknown's avatar
unknown committed
147

148 149 150
  if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME))))
    return;

151 152 153 154 155 156 157 158 159 160 161 162 163
  /* 
     When we are deleting temporary files, we should only remove
     the files associated with the server id of our server.
     We don't use event_server_id here because since we've disabled
     direct binlogging of Create_file/Append_file/Exec_load events
     we cannot meet Start_log event in the middle of events from one 
     LOAD DATA.
  */
  p= strmake(prefbuf,"SQL_LOAD-",9);
  p= int10_to_str(::server_id, p, 10);
  *(p++)= '-';
  *p= 0;

164 165 166
  for (i=0 ; i < (uint)dirp->number_off_files; i++)
  {
    file=dirp->dir_entry+i;
167
    if (is_prefix(file->name, prefbuf))
unknown's avatar
unknown committed
168 169 170 171
    {
      fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
      my_delete(fname, MYF(0));
    }
172 173 174 175
  }

  my_dirend(dirp);
}
unknown's avatar
SCRUM  
unknown committed
176
#endif
177 178


unknown's avatar
unknown committed
179
/*
180
  write_str()
unknown's avatar
unknown committed
181
*/
182

183
static bool write_str(IO_CACHE *file, char *str, uint length)
184
{
185 186 187 188
  byte tmp[1];
  tmp[0]= (byte) length;
  return (my_b_safe_write(file, tmp, sizeof(tmp)) ||
	  my_b_safe_write(file, (byte*) str, length));
189 190 191
}


unknown's avatar
unknown committed
192
/*
193
  read_str()
unknown's avatar
unknown committed
194
*/
195

196 197
static inline int read_str(char **buf, char *buf_end, char **str,
			   uint8 *len)
198
{
199
  if (*buf + ((uint) (uchar) **buf) >= buf_end)
200
    return 1;
201 202 203
  *len= (uint8) **buf;
  *str= (*buf)+1;
  (*buf)+= (uint) *len+1;
204 205 206
  return 0;
}

207

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
/*
  Transforms a string into "" or its expression in 0x... form.
*/
static char *str_to_hex(char *to, char *from, uint len)
{
  char *p= to;
  if (len)
  {
    p= strmov(p, "0x");
    for (uint i= 0; i < len; i++, p+= 2)
    {
      /* val[i] is char. Casting to uchar helps greatly if val[i] < 0 */
      uint tmp= (uint) (uchar) from[i];
      p[0]= _dig_vec_upper[tmp >> 4];
      p[1]= _dig_vec_upper[tmp & 15];
    }
    *p= 0;
  }
  else
    p= strmov(p, "\"\"");
  return p; // pointer to end 0 of 'to'
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
/*
  Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
  commands just before it prints a query.
*/

static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
                             uint32 flags, const char* name, bool* need_comma) 
{
  if (bits_changed & option)
  {
    if (*need_comma)
      fprintf(file,", ");
    fprintf(file,"%s=%d", name, (bool)(flags & option));
    *need_comma= 1;
  }
}
247

unknown's avatar
unknown committed
248
/**************************************************************************
249
	Log_event methods (= the parent class of all events)
unknown's avatar
unknown committed
250
**************************************************************************/
251

unknown's avatar
unknown committed
252
/*
253
  Log_event::get_type_str()
unknown's avatar
unknown committed
254
*/
255

unknown's avatar
unknown committed
256 257
const char* Log_event::get_type_str()
{
258
  switch(get_type_code()) {
259
  case START_EVENT_V3:  return "Start_v3";
unknown's avatar
unknown committed
260 261 262 263 264
  case STOP_EVENT:   return "Stop";
  case QUERY_EVENT:  return "Query";
  case ROTATE_EVENT: return "Rotate";
  case INTVAR_EVENT: return "Intvar";
  case LOAD_EVENT:   return "Load";
265
  case NEW_LOAD_EVENT:   return "New_load";
unknown's avatar
unknown committed
266
  case SLAVE_EVENT:  return "Slave";
267 268 269 270
  case CREATE_FILE_EVENT: return "Create_file";
  case APPEND_BLOCK_EVENT: return "Append_block";
  case DELETE_FILE_EVENT: return "Delete_file";
  case EXEC_LOAD_EVENT: return "Exec_load";
271
  case RAND_EVENT: return "RAND";
272
  case XID_EVENT: return "Xid";
unknown's avatar
unknown committed
273
  case USER_VAR_EVENT: return "User var";
274
  case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
unknown's avatar
unknown committed
275 276
  case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
  case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
277
  default: return "Unknown";				/* impossible */
unknown's avatar
unknown committed
278 279 280
  }
}

281

unknown's avatar
unknown committed
282
/*
283
  Log_event::Log_event()
unknown's avatar
unknown committed
284
*/
285

unknown's avatar
unknown committed
286
#ifndef MYSQL_CLIENT
287
Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
288
  :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg)
289
{
unknown's avatar
unknown committed
290 291
  server_id=	thd->server_id;
  when=		thd->start_time;
292
  cache_stmt=	using_trans;
293 294 295
}


unknown's avatar
unknown committed
296
/*
297 298 299 300
  This minimal constructor is for when you are not even sure that there
  is a valid THD. For example in the server when we are shutting down or
  flushing logs after receiving a SIGHUP (then we must write a Rotate to
  the binlog but we have no THD, so we need this minimal constructor).
unknown's avatar
unknown committed
301 302
*/

303
Log_event::Log_event()
304
  :temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
305 306
   thd(0)
{
unknown's avatar
unknown committed
307 308 309
  server_id=	::server_id;
  when=		time(NULL);
  log_pos=	0;
310
}
unknown's avatar
unknown committed
311
#endif /* !MYSQL_CLIENT */
312 313


unknown's avatar
unknown committed
314
/*
315
  Log_event::Log_event()
316
*/
317

318
Log_event::Log_event(const char* buf,
unknown's avatar
unknown committed
319
                     const Format_description_log_event* description_event)
320
  :temp_buf(0), cache_stmt(0)
321
{
322 323
#ifndef MYSQL_CLIENT
  thd = 0;
unknown's avatar
unknown committed
324
#endif
325 326
  when = uint4korr(buf);
  server_id = uint4korr(buf + SERVER_ID_OFFSET);
327
  if (description_event->binlog_version==1)
328
  {
329 330 331
    log_pos= 0;
    flags= 0;
    return;
332
  }
333 334 335
  /* 4.0 or newer */
  log_pos= uint4korr(buf + LOG_POS_OFFSET);
  /*
336 337 338 339 340 341 342 343
    If the log is 4.0 (so here it can only be a 4.0 relay log read by
    the SQL thread or a 4.0 master binlog read by the I/O thread),
    log_pos is the beginning of the event: we transform it into the end
    of the event, which is more useful.
    But how do you know that the log is 4.0: you know it if
    description_event is version 3 *and* you are not reading a
    Format_desc (remember that mysqlbinlog starts by assuming that 5.0
    logs are in 4.0 format, until it finds a Format_desc).
344 345 346
  */
  if (description_event->binlog_version==3 &&
      buf[EVENT_TYPE_OFFSET]<FORMAT_DESCRIPTION_EVENT && log_pos)
347
  {
348 349 350
      /*
        If log_pos=0, don't change it. log_pos==0 is a marker to mean
        "don't change rli->group_master_log_pos" (see
351 352
        inc_group_relay_log_pos()). As it is unreal log_pos, adding the
        event len's is nonsense. For example, a fake Rotate event should
353
        not have its log_pos (which is 0) changed or it will modify
354 355 356 357
        Exec_master_log_pos in SHOW SLAVE STATUS, displaying a nonsense
        value of (a non-zero offset which does not exist in the master's
        binlog, so which will cause problems if the user uses this value
        in CHANGE MASTER).
358 359
      */
    log_pos+= uint4korr(buf + EVENT_LEN_OFFSET);
360
  }
361 362 363 364 365 366 367
  DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));

  flags= uint2korr(buf + FLAGS_OFFSET);
  if ((buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) ||
      (buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT))
  {
    /*
368 369
      These events always have a header which stops here (i.e. their
      header is FROZEN).
370 371
    */
    /*
372 373 374 375 376 377 378
      Initialization to zero of all other Log_event members as they're
      not specified. Currently there are no such members; in the future
      there will be an event UID (but Format_description and Rotate
      don't need this UID, as they are not propagated through
      --log-slave-updates (remember the UID is used to not play a query
      twice when you have two masters which are slaves of a 3rd master).
      Then we are done.
379 380 381 382
    */
    return;
  }
  /* otherwise, go on with reading the header from buf (nothing now) */
383 384 385
}

#ifndef MYSQL_CLIENT
unknown's avatar
SCRUM  
unknown committed
386
#ifdef HAVE_REPLICATION
387

unknown's avatar
unknown committed
388
/*
389
  Log_event::exec_event()
unknown's avatar
unknown committed
390
*/
391

392
int Log_event::exec_event(struct st_relay_log_info* rli)
393
{
unknown's avatar
unknown committed
394 395
  DBUG_ENTER("Log_event::exec_event");

396 397 398 399 400 401 402 403 404 405 406 407
  /*
    rli is null when (as far as I (Guilhem) know)
    the caller is
    Load_log_event::exec_event *and* that one is called from
    Execute_load_log_event::exec_event. 
    In this case, we don't do anything here ;
    Execute_load_log_event::exec_event will call Log_event::exec_event
    again later with the proper rli.
    Strictly speaking, if we were sure that rli is null
    only in the case discussed above, 'if (rli)' is useless here.
    But as we are not 100% sure, keep it for now.
  */
408
  if (rli)
409
  {
410
    /*
411 412 413 414
      If in a transaction, and if the slave supports transactions, just
      inc_event_relay_log_pos(). We only have to check for OPTION_BEGIN
      (not OPTION_NOT_AUTOCOMMIT) as transactions are logged with
      BEGIN/COMMIT, not with SET AUTOCOMMIT= .
415

416
      CAUTION: opt_using_transactions means
417
      innodb || bdb ; suppose the master supports InnoDB and BDB,
418
      but the slave supports only BDB, problems
419
      will arise:
420 421
      - suppose an InnoDB table is created on the master,
      - then it will be MyISAM on the slave
422 423 424 425 426 427 428 429 430 431 432 433
      - but as opt_using_transactions is true, the slave will believe he
      is transactional with the MyISAM table. And problems will come
      when one does START SLAVE; STOP SLAVE; START SLAVE; (the slave
      will resume at BEGIN whereas there has not been any rollback).
      This is the problem of using opt_using_transactions instead of a
      finer "does the slave support
      _the_transactional_handler_used_on_the_master_".

      More generally, we'll have problems when a query mixes a
      transactional handler and MyISAM and STOP SLAVE is issued in the
      middle of the "transaction". START SLAVE will resume at BEGIN
      while the MyISAM table has already been updated.
434 435
    */
    if ((thd->options & OPTION_BEGIN) && opt_using_transactions)
436
      rli->inc_event_relay_log_pos();
437 438
    else
    {
439
      rli->inc_group_relay_log_pos(log_pos);
440
      flush_relay_log_info(rli);
441
      /* 
442 443
         Note that Rotate_log_event::exec_event() does not call this
         function, so there is no chance that a fake rotate event resets
444
         last_master_timestamp.
445 446 447
         Note that we update without mutex (probably ok - except in some very
         rare cases, only consequence is that value may take some time to
         display in Seconds_Behind_Master - not critical).
448 449
      */
      rli->last_master_timestamp= when;
450
    }
451
  }
unknown's avatar
unknown committed
452
  DBUG_RETURN(0);
453
}
unknown's avatar
unknown committed
454

455

unknown's avatar
unknown committed
456
/*
457
  Log_event::pack_info()
unknown's avatar
unknown committed
458
*/
459

460
void Log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
461
{
462
  protocol->store("", &my_charset_bin);
unknown's avatar
unknown committed
463 464 465
}


unknown's avatar
unknown committed
466
/*
467
  Log_event::net_send()
unknown's avatar
unknown committed
468

469
  Only called by SHOW BINLOG EVENTS
unknown's avatar
unknown committed
470
*/
unknown's avatar
SCRUM  
unknown committed
471

472
int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
473
{
unknown's avatar
unknown committed
474 475
  const char *p= strrchr(log_name, FN_LIBCHAR);
  const char *event_type;
476 477 478
  if (p)
    log_name = p + 1;
  
479
  protocol->prepare_for_resend();
480
  protocol->store(log_name, &my_charset_bin);
481
  protocol->store((ulonglong) pos);
482
  event_type = get_type_str();
483
  protocol->store(event_type, strlen(event_type), &my_charset_bin);
484 485 486 487
  protocol->store((uint32) server_id);
  protocol->store((ulonglong) log_pos);
  pack_info(protocol);
  return protocol->write();
488
}
unknown's avatar
SCRUM  
unknown committed
489 490 491
#endif /* HAVE_REPLICATION */


unknown's avatar
unknown committed
492
/*
unknown's avatar
SCRUM  
unknown committed
493
  Log_event::init_show_field_list()
unknown's avatar
unknown committed
494
*/
unknown's avatar
SCRUM  
unknown committed
495 496 497 498 499 500 501 502 503

void Log_event::init_show_field_list(List<Item>* field_list)
{
  field_list->push_back(new Item_empty_string("Log_name", 20));
  field_list->push_back(new Item_return_int("Pos", 11,
					    MYSQL_TYPE_LONGLONG));
  field_list->push_back(new Item_empty_string("Event_type", 20));
  field_list->push_back(new Item_return_int("Server_id", 10,
					    MYSQL_TYPE_LONG));
504
  field_list->push_back(new Item_return_int("End_log_pos", 11,
unknown's avatar
SCRUM  
unknown committed
505 506 507 508
					    MYSQL_TYPE_LONGLONG));
  field_list->push_back(new Item_empty_string("Info", 20));
}

unknown's avatar
unknown committed
509
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
510

511

unknown's avatar
unknown committed
512
/*
513
  Log_event::write()
unknown's avatar
unknown committed
514
*/
515

516
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
unknown's avatar
unknown committed
517
{
518 519
  byte header[LOG_EVENT_HEADER_LEN];
  DBUG_ENTER("Log_event::write_header");
unknown's avatar
unknown committed
520

521 522
  /* Store number of bytes that will be written by this event */
  data_written= event_data_length + sizeof(header);
523

524 525 526 527
  /*
    log_pos != 0 if this is relay-log event. In this case we should not
    change the position
  */
528

529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 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 580 581 582 583
  if (is_artificial_event())
  {
    /*
      We should not do any cleanup on slave when reading this. We
      mark this by setting log_pos to 0.  Start_log_event_v3() will
      detect this on reading and set artificial_event=1 for the event.
    */
    log_pos= 0;
  }
  else  if (!log_pos)
  {
    /*
      Calculate position of end of event

      Note that with a SEQ_READ_APPEND cache, my_b_tell() does not
      work well.  So this will give slightly wrong positions for the
      Format_desc/Rotate/Stop events which the slave writes to its
      relay log. For example, the initial Format_desc will have
      end_log_pos=91 instead of 95. Because after writing the first 4
      bytes of the relay log, my_b_tell() still reports 0. Because
      my_b_append() does not update the counter which my_b_tell()
      later uses (one should probably use my_b_append_tell() to work
      around this).  To get right positions even when writing to the
      relay log, we use the (new) my_b_safe_tell().

      Note that this raises a question on the correctness of all these
      DBUG_ASSERT(my_b_tell()=rli->event_relay_log_pos).

      If in a transaction, the log_pos which we calculate below is not
      very good (because then my_b_safe_tell() returns start position
      of the BEGIN, so it's like the statement was at the BEGIN's
      place), but it's not a very serious problem (as the slave, when
      it is in a transaction, does not take those end_log_pos into
      account (as it calls inc_event_relay_log_pos()). To be fixed
      later, so that it looks less strange. But not bug.
    */

    log_pos= my_b_safe_tell(file)+data_written;
  }

  /*
    Header will be of size LOG_EVENT_HEADER_LEN for all events, except for
    FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be
    LOG_EVENT_MINIMAL_HEADER_LEN (remember these 2 have a frozen header,
    because we read them before knowing the format).
  */

  int4store(header, (ulong) when);              // timestamp
  header[EVENT_TYPE_OFFSET]= get_type_code();
  int4store(header+ SERVER_ID_OFFSET, server_id);
  int4store(header+ EVENT_LEN_OFFSET, data_written);
  int4store(header+ LOG_POS_OFFSET, log_pos);
  int2store(header+ FLAGS_OFFSET, flags);

  DBUG_RETURN(my_b_safe_write(file, header, sizeof(header)) != 0);
unknown's avatar
unknown committed
584 585 586
}


unknown's avatar
unknown committed
587
/*
588
  Log_event::read_log_event()
589 590 591 592

  This needn't be format-tolerant, because we only read
  LOG_EVENT_MINIMAL_HEADER_LEN (we just want to read the event's length).

unknown's avatar
unknown committed
593
*/
594 595

#ifndef MYSQL_CLIENT
596
int Log_event::read_log_event(IO_CACHE* file, String* packet,
597
			      pthread_mutex_t* log_lock)
unknown's avatar
unknown committed
598 599
{
  ulong data_len;
600
  int result=0;
601
  char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
602
  DBUG_ENTER("read_log_event");
603

604
  if (log_lock)
605
    pthread_mutex_lock(log_lock);
606 607
  if (my_b_read(file, (byte*) buf, sizeof(buf)))
  {
608 609 610 611 612
    /*
      If the read hits eof, we must report it as eof so the caller
      will know it can go into cond_wait to be woken up on the next
      update to the log.
    */
613
    DBUG_PRINT("error",("file->error: %d", file->error));
614 615 616
    if (!file->error)
      result= LOG_READ_EOF;
    else
617
      result= (file->error > 0 ? LOG_READ_TRUNC : LOG_READ_IO);
618
    goto end;
619
  }
620
  data_len= uint4korr(buf + EVENT_LEN_OFFSET);
621
  if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
unknown's avatar
unknown committed
622
      data_len > current_thd->variables.max_allowed_packet)
623
  {
624
    DBUG_PRINT("error",("data_len: %ld", data_len));
625
    result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
626 627
	     LOG_READ_TOO_LARGE);
    goto end;
628
  }
unknown's avatar
unknown committed
629
  packet->append(buf, sizeof(buf));
630
  data_len-= LOG_EVENT_MINIMAL_HEADER_LEN;
631 632 633
  if (data_len)
  {
    if (packet->append(file, data_len))
634
    {
635
      /*
636 637
	Here if we hit EOF it's really an error: as data_len is >=0
        there's supposed to be more bytes available. 
638
	EOF means we are reading the event partially, which should
639
	never happen: either we read badly or the binlog is truncated.
640 641 642
      */
      result= file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO;
      /* Implicit goto end; */
643
    }
644
  }
645 646 647 648

end:
  if (log_lock)
    pthread_mutex_unlock(log_lock);
649
  DBUG_RETURN(result);
unknown's avatar
unknown committed
650
}
unknown's avatar
unknown committed
651
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
652

unknown's avatar
unknown committed
653
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
654 655
#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock);
#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock);
656
#else
657
#define UNLOCK_MUTEX
658 659 660
#define LOCK_MUTEX
#endif

unknown's avatar
unknown committed
661
/*
662 663
  Log_event::read_log_event()

unknown's avatar
unknown committed
664
  NOTE:
665
    Allocates memory;  The caller is responsible for clean-up.
unknown's avatar
unknown committed
666
*/
667

unknown's avatar
unknown committed
668
#ifndef MYSQL_CLIENT
669 670
Log_event* Log_event::read_log_event(IO_CACHE* file,
				     pthread_mutex_t* log_lock,
671
                                     const Format_description_log_event *description_event)
unknown's avatar
unknown committed
672
#else
673 674
Log_event* Log_event::read_log_event(IO_CACHE* file,
                                     const Format_description_log_event *description_event)
675
#endif
unknown's avatar
unknown committed
676
{
677
  DBUG_ENTER("Log_event::read_log_event(IO_CACHE *, Format_description_log_event *");
678
  DBUG_ASSERT(description_event != 0);
679 680 681 682 683
  char head[LOG_EVENT_MINIMAL_HEADER_LEN];
  /*
    First we only want to read at most LOG_EVENT_MINIMAL_HEADER_LEN, just to
    check the event for sanity and to know its length; no need to really parse
    it. We say "at most" because this could be a 3.23 master, which has header
684 685
    of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's
    "minimal" over the set {MySQL >=4.0}).
686 687 688
  */
  uint header_size= min(description_event->common_header_len,
                        LOG_EVENT_MINIMAL_HEADER_LEN);
689

690
  LOCK_MUTEX;
691
  DBUG_PRINT("info", ("my_b_tell=%lu", my_b_tell(file)));
692
  if (my_b_read(file, (byte *) head, header_size))
693
  {
694 695
    DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
failed my_b_read"));
unknown's avatar
unknown committed
696
    UNLOCK_MUTEX;
697
    /*
698 699 700
      No error here; it could be that we are at the file's end. However
      if the next my_b_read() fails (below), it will be an error as we
      were able to read the first bytes.
701
    */
702
    DBUG_RETURN(0);
703
  }
unknown's avatar
unknown committed
704

705
  uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
706 707 708
  char *buf= 0;
  const char *error= 0;
  Log_event *res=  0;
709 710 711 712
#ifndef max_allowed_packet
  THD *thd=current_thd;
  uint max_allowed_packet= thd ? thd->variables.max_allowed_packet : ~0;
#endif
unknown's avatar
unknown committed
713

714
  if (data_len > max_allowed_packet)
unknown's avatar
unknown committed
715
  {
716 717
    error = "Event too big";
    goto err;
unknown's avatar
unknown committed
718 719
  }

720
  if (data_len < header_size)
unknown's avatar
unknown committed
721
  {
722 723
    error = "Event too small";
    goto err;
unknown's avatar
unknown committed
724
  }
725 726 727

  // some events use the extra byte to null-terminate strings
  if (!(buf = my_malloc(data_len+1, MYF(MY_WME))))
728 729 730
  {
    error = "Out of memory";
    goto err;
unknown's avatar
unknown committed
731
  }
732
  buf[data_len] = 0;
733
  memcpy(buf, head, header_size);
734
  if (my_b_read(file, (byte*) buf + header_size, data_len - header_size))
735 736 737 738
  {
    error = "read error";
    goto err;
  }
739
  if ((res= read_log_event(buf, data_len, &error, description_event)))
740
    res->register_temp_buf(buf);
741

742
err:
unknown's avatar
unknown committed
743
  UNLOCK_MUTEX;
744
  if (!res)
745
  {
746
    DBUG_ASSERT(error != 0);
747 748
    sql_print_error("Error in Log_event::read_log_event(): "
                    "'%s', data_len: %d, event_type: %d",
749
		    error,data_len,head[EVENT_TYPE_OFFSET]);
750
    my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
751 752 753 754 755 756 757 758 759
    /*
      The SQL slave thread will check if file->error<0 to know
      if there was an I/O error. Even if there is no "low-level" I/O errors
      with 'file', any of the high-level above errors is worrying
      enough to stop the SQL thread now ; as we are skipping the current event,
      going on with reading and successfully executing other events can
      only corrupt the slave's databases. So stop.
    */
    file->error= -1;
760
  }
761
  DBUG_RETURN(res);
unknown's avatar
unknown committed
762 763
}

764

unknown's avatar
unknown committed
765
/*
766
  Log_event::read_log_event()
767 768
  Binlog format tolerance is in (buf, event_len, description_event)
  constructors.
unknown's avatar
unknown committed
769
*/
770

771 772 773
Log_event* Log_event::read_log_event(const char* buf, uint event_len,
				     const char **error,
                                     const Format_description_log_event *description_event)
unknown's avatar
unknown committed
774
{
775 776
  Log_event* ev;
  DBUG_ENTER("Log_event::read_log_event(char*,...)");
777
  DBUG_ASSERT(description_event != 0);
778
  DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
779
  if (event_len < EVENT_LEN_OFFSET ||
780 781 782
      (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
  {
    *error="Sanity check failed";		// Needed to free buffer
unknown's avatar
unknown committed
783
    DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
784
  }
785

786
  switch(buf[EVENT_TYPE_OFFSET]) {
unknown's avatar
unknown committed
787
  case QUERY_EVENT:
unknown's avatar
unknown committed
788
    ev  = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
789
    break;
unknown's avatar
unknown committed
790
  case LOAD_EVENT:
unknown's avatar
unknown committed
791
    ev = new Load_log_event(buf, event_len, description_event);
unknown's avatar
unknown committed
792
    break;
793
  case NEW_LOAD_EVENT:
794
    ev = new Load_log_event(buf, event_len, description_event);
795
    break;
unknown's avatar
unknown committed
796
  case ROTATE_EVENT:
797
    ev = new Rotate_log_event(buf, event_len, description_event);
798
    break;
unknown's avatar
SCRUM  
unknown committed
799
#ifdef HAVE_REPLICATION
800
  case SLAVE_EVENT: /* can never happen (unused event) */
801 802
    ev = new Slave_log_event(buf, event_len);
    break;
unknown's avatar
SCRUM  
unknown committed
803
#endif /* HAVE_REPLICATION */
804
  case CREATE_FILE_EVENT:
805
    ev = new Create_file_log_event(buf, event_len, description_event);
806 807
    break;
  case APPEND_BLOCK_EVENT:
808
    ev = new Append_block_log_event(buf, event_len, description_event);
809 810
    break;
  case DELETE_FILE_EVENT:
811
    ev = new Delete_file_log_event(buf, event_len, description_event);
812 813
    break;
  case EXEC_LOAD_EVENT:
814
    ev = new Execute_load_log_event(buf, event_len, description_event);
815
    break;
816 817
  case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
    ev = new Start_log_event_v3(buf, description_event);
818 819
    break;
  case STOP_EVENT:
820
    ev = new Stop_log_event(buf, description_event);
821 822
    break;
  case INTVAR_EVENT:
823
    ev = new Intvar_log_event(buf, description_event);
824
    break;
825 826 827
  case XID_EVENT:
    ev = new Xid_log_event(buf, description_event);
    break;
unknown's avatar
unknown committed
828
  case RAND_EVENT:
829
    ev = new Rand_log_event(buf, description_event);
unknown's avatar
unknown committed
830
    break;
unknown's avatar
unknown committed
831
  case USER_VAR_EVENT:
832 833 834 835
    ev = new User_var_log_event(buf, description_event);
    break;
  case FORMAT_DESCRIPTION_EVENT:
    ev = new Format_description_log_event(buf, event_len, description_event); 
unknown's avatar
unknown committed
836
    break;
unknown's avatar
unknown committed
837 838 839 840 841 842
  case BEGIN_LOAD_QUERY_EVENT:
    ev = new Begin_load_query_log_event(buf, event_len, description_event);
    break;
  case EXECUTE_LOAD_QUERY_EVENT:
    ev = new Execute_load_query_log_event(buf, event_len, description_event);
    break;
843
  default:
844 845
    DBUG_PRINT("error",("Unknown evernt code: %d",(int) buf[EVENT_TYPE_OFFSET]));
    ev= NULL;
846
    break;
unknown's avatar
unknown committed
847
  }
848

849
  /*
850 851 852 853 854 855 856
    is_valid() are small event-specific sanity tests which are
    important; for example there are some my_malloc() in constructors
    (e.g. Query_log_event::Query_log_event(char*...)); when these
    my_malloc() fail we can't return an error out of the constructor
    (because constructor is "void") ; so instead we leave the pointer we
    wanted to allocate (e.g. 'query') to 0 and we test it in is_valid().
    Same for Format_description_log_event, member 'post_header_len'.
857
  */
858
  if (!ev || !ev->is_valid())
859
  {
860 861
    DBUG_PRINT("error",("Found invalid event in binary log"));

862
    delete ev;
863
#ifdef MYSQL_CLIENT
864
    if (!force_opt) /* then mysqlbinlog dies */
865 866
    {
      *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
867
      DBUG_RETURN(0);
868
    }
869
    ev= new Unknown_log_event(buf, description_event);
870 871
#else
    *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
872
    DBUG_RETURN(0);
873
#endif
874
  }
unknown's avatar
unknown committed
875
  DBUG_RETURN(ev);  
unknown's avatar
unknown committed
876 877
}

878
#ifdef MYSQL_CLIENT
879

unknown's avatar
unknown committed
880
/*
881
  Log_event::print_header()
unknown's avatar
unknown committed
882
*/
883

884 885
void Log_event::print_header(FILE* file)
{
886
  char llbuff[22];
887 888
  fputc('#', file);
  print_timestamp(file);
889
  fprintf(file, " server id %d  end_log_pos %s ", server_id,
890
	  llstr(log_pos,llbuff)); 
891 892
}

unknown's avatar
unknown committed
893
/*
894
  Log_event::print_timestamp()
unknown's avatar
unknown committed
895
*/
896

897
void Log_event::print_timestamp(FILE* file, time_t* ts)
unknown's avatar
unknown committed
898
{
unknown's avatar
unknown committed
899
  struct tm *res;
900 901
  if (!ts)
    ts = &when;
902 903
#ifdef MYSQL_SERVER				// This is always false
  struct tm tm_tmp;
unknown's avatar
unknown committed
904
  localtime_r(ts,(res= &tm_tmp));
unknown's avatar
unknown committed
905
#else
906
  res=localtime(ts);
unknown's avatar
unknown committed
907
#endif
908 909

  fprintf(file,"%02d%02d%02d %2d:%02d:%02d",
910 911 912 913 914 915
	  res->tm_year % 100,
	  res->tm_mon+1,
	  res->tm_mday,
	  res->tm_hour,
	  res->tm_min,
	  res->tm_sec);
unknown's avatar
unknown committed
916 917
}

unknown's avatar
unknown committed
918
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
919 920


unknown's avatar
unknown committed
921
/**************************************************************************
unknown's avatar
unknown committed
922
	Query_log_event methods
unknown's avatar
unknown committed
923
**************************************************************************/
924

unknown's avatar
SCRUM  
unknown committed
925
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
926

unknown's avatar
unknown committed
927
/*
928
  Query_log_event::pack_info()
929 930 931 932
  This (which is used only for SHOW BINLOG EVENTS) could be updated to
  print SET @@session_var=. But this is not urgent, as SHOW BINLOG EVENTS is
  only an information, it does not produce suitable queries to replay (for
  example it does not print LOAD DATA INFILE).
unknown's avatar
unknown committed
933
*/
934

935
void Query_log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
936
{
937
  // TODO: show the catalog ??
938 939 940
  char *buf, *pos;
  if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME))))
    return;
941 942
  pos= buf;
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
943
      && db && db_len)
944
  {
945 946
    pos= strmov(buf, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
947
    pos= strmov(pos+db_len, "`; ");
948
  }
949
  if (query && q_len)
950 951 952 953
  {
    memcpy(pos, query, q_len);
    pos+= q_len;
  }
954
  protocol->store(buf, pos-buf, &my_charset_bin);
955
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
956
}
unknown's avatar
SCRUM  
unknown committed
957
#endif
958 959


unknown's avatar
unknown committed
960
/*
961
  Query_log_event::write()
unknown's avatar
unknown committed
962

963 964 965 966
  NOTES:
    In this event we have to modify the header to have the correct
    EVENT_LEN_OFFSET as we don't yet know how many status variables we
    will print!
unknown's avatar
unknown committed
967
*/
968

969
bool Query_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
970
{
971 972 973 974 975 976 977
  uchar buf[QUERY_HEADER_LEN+
            1+4+           // code of flags2 and flags2
            1+8+           // code of sql_mode and sql_mode
            1+1+FN_REFLEN+ // code of catalog and catalog length and catalog
            1+4+           // code of autoinc and the 2 autoinc variables
            1+6            // code of charset and charset
            ], *start, *start_of_status;
978
  ulong event_length;
unknown's avatar
unknown committed
979

980
  if (!query)
981 982
    return 1;                                   // Something wrong with event

unknown's avatar
unknown committed
983 984 985 986 987
  /*
    We want to store the thread id:
    (- as an information for the user when he reads the binlog)
    - if the query uses temporary table: for the slave SQL thread to know to
    which master connection the temp table belongs.
988
    Now imagine we (write()) are called by the slave SQL thread (we are
unknown's avatar
unknown committed
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
    logging a query executed by this thread; the slave runs with
    --log-slave-updates). Then this query will be logged with
    thread_id=the_thread_id_of_the_SQL_thread. Imagine that 2 temp tables of
    the same name were created simultaneously on the master (in the master
    binlog you have
    CREATE TEMPORARY TABLE t; (thread 1)
    CREATE TEMPORARY TABLE t; (thread 2)
    ...)
    then in the slave's binlog there will be
    CREATE TEMPORARY TABLE t; (thread_id_of_the_slave_SQL_thread)
    CREATE TEMPORARY TABLE t; (thread_id_of_the_slave_SQL_thread)
    which is bad (same thread id!).

    To avoid this, we log the thread's thread id EXCEPT for the SQL
    slave thread for which we log the original (master's) thread id.
    Now this moves the bug: what happens if the thread id on the
    master was 10 and when the slave replicates the query, a
    connection number 10 is opened by a normal client on the slave,
    and updates a temp table of the same name? We get a problem
    again. To avoid this, in the handling of temp tables (sql_base.cc)
    we use thread_id AND server_id.  TODO when this is merged into
    4.1: in 4.1, slave_proxy_id has been renamed to pseudo_thread_id
    and is a session variable: that's to make mysqlbinlog work with
    temp tables. We probably need to introduce

    SET PSEUDO_SERVER_ID
    for mysqlbinlog in 4.1. mysqlbinlog would print:
    SET PSEUDO_SERVER_ID=
    SET PSEUDO_THREAD_ID=
    for each query using temp tables.
  */
  int4store(buf + Q_THREAD_ID_OFFSET, slave_proxy_id);
1021 1022 1023 1024
  int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
  buf[Q_DB_LEN_OFFSET] = (char) db_len;
  int2store(buf + Q_ERR_CODE_OFFSET, error_code);

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
  /*
    You MUST always write status vars in increasing order of code. This
    guarantees that a slightly older slave will be able to parse those he
    knows.
  */
  start_of_status= start= buf+QUERY_HEADER_LEN;
  if (flags2_inited)
  {
    *(start++)= Q_FLAGS2_CODE;
    int4store(start, flags2);
    start+= 4;
  }
  if (sql_mode_inited)
  {
    *(start++)= Q_SQL_MODE_CODE;
unknown's avatar
unknown committed
1040
    int8store(start, (ulonglong)sql_mode);
1041 1042 1043 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 1069 1070 1071
    start+= 8;
  }
  if (catalog_len >= 0) // i.e. "catalog inited" (false for 4.0 events)
  {
    *(start++)= Q_CATALOG_CODE;
    *(start++)= (uchar) catalog_len;
    bmove(start, catalog, catalog_len);
    start+= catalog_len;
    /*
      We write a \0 at the end. As we also have written the length, it's
      apparently useless; but in fact it enables us to just do
      catalog= a_pointer_to_the_buffer_of_the_read_event
      later in the slave SQL thread.
      If we didn't have the \0, we would need to memdup to build the catalog in
      the slave SQL thread. 
      And still the interest of having the length too is that in the slave SQL
      thread we immediately know at which position the catalog ends (no need to
      search for '\0'. In other words: length saves search, \0 saves mem alloc,
      at the cost of 1 redundant byte on the disk.
      Note that this is only a fix until we change 'catalog' to LEX_STRING
      (then we won't need the \0).
    */
    *(start++)= '\0';
  }
  if (auto_increment_increment != 1)
  {
    *start++= Q_AUTO_INCREMENT;
    int2store(start, auto_increment_increment);
    int2store(start+2, auto_increment_offset);
    start+= 4;
  }
1072 1073 1074 1075 1076 1077
  if (charset_inited)
  {
    *(start++)= Q_CHARSET_CODE;
    memcpy(start, charset, 6);
    start+= 6;
  }
1078 1079
  /*
    Here there could be code like
1080
    if (command-line-option-which-says-"log_this_variable" && inited)
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
    {
    *(start++)= Q_THIS_VARIABLE_CODE;
    int4store(start, this_variable);
    start+= 4;
    }
  */
  
  /* Store length of status variables */
  status_vars_len= (uint) (start-start_of_status);
  int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len);

  /*
    Calculate length of whole event
    The "1" below is the \0 in the db's length
  */
unknown's avatar
unknown committed
1096
  event_length= (uint) (start-buf) + get_post_header_size_for_derived() + db_len + 1 + q_len;
1097 1098

  return (write_header(file, event_length) ||
unknown's avatar
unknown committed
1099 1100 1101 1102
          my_b_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) ||
          write_post_header_for_derived(file) ||
          my_b_safe_write(file, (byte*) start_of_status,
                          (uint) (start-start_of_status)) ||
1103 1104
          my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
          my_b_safe_write(file, (byte*) query, q_len)) ? 1 : 0;
unknown's avatar
unknown committed
1105 1106
}

1107

unknown's avatar
unknown committed
1108
/*
1109
  Query_log_event::Query_log_event()
unknown's avatar
unknown committed
1110
*/
1111

1112 1113
#ifndef MYSQL_CLIENT
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
1114 1115
				 ulong query_length, bool using_trans,
				 bool suppress_use)
1116
  :Log_event(thd_arg,
1117 1118 1119
	     ((thd_arg->tmp_table_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0)
	      | (suppress_use          ? LOG_EVENT_SUPPRESS_USE_F    : 0)),
	     using_trans),
1120
   data_buf(0), query(query_arg), catalog(thd_arg->catalog),
1121
   db(thd_arg->db), q_len((uint32) query_length),
1122
   error_code((thd_arg->killed != THD::NOT_KILLED) ?
1123
              ((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ?
1124
               0 : thd->killed_errno()) : thd_arg->net.last_errno),
unknown's avatar
unknown committed
1125 1126
   thread_id(thd_arg->thread_id),
   /* save the original thread id; we already know the server id */
1127
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
1128
   flags2_inited(1), sql_mode_inited(1), charset_inited(1),
1129 1130 1131
   sql_mode(thd_arg->variables.sql_mode),
   auto_increment_increment(thd_arg->variables.auto_increment_increment),
   auto_increment_offset(thd_arg->variables.auto_increment_offset)
1132 1133 1134 1135
{
  time_t end_time;
  time(&end_time);
  exec_time = (ulong) (end_time  - thd->start_time);
1136
  catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
1137
  /* status_vars_len is set just before writing the event */
1138
  db_len = (db) ? (uint32) strlen(db) : 0;
1139 1140 1141 1142 1143 1144 1145 1146
  /*
    If we don't use flags2 for anything else than options contained in
    thd->options, it would be more efficient to flags2=thd_arg->options
    (OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time).
    But it's likely that we don't want to use 32 bits for 3 bits; in the future
    we will probably want to reclaim the 29 bits. So we need the &.
  */
  flags2= thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG;
1147 1148 1149 1150 1151 1152
  DBUG_ASSERT(thd->variables.character_set_client->number < 256*256);
  DBUG_ASSERT(thd->variables.collation_connection->number < 256*256);
  DBUG_ASSERT(thd->variables.collation_server->number < 256*256);
  int2store(charset, thd_arg->variables.character_set_client->number);
  int2store(charset+2, thd_arg->variables.collation_connection->number);
  int2store(charset+4, thd_arg->variables.collation_server->number);
1153
  DBUG_PRINT("info",("Query_log_event has flags2=%lu sql_mode=%lu",flags2,sql_mode));
1154
}
unknown's avatar
unknown committed
1155
#endif /* MYSQL_CLIENT */
1156

unknown's avatar
unknown committed
1157

unknown's avatar
unknown committed
1158
/*
1159
  Query_log_event::Query_log_event()
1160
  This is used by the SQL slave thread to prepare the event before execution.
unknown's avatar
unknown committed
1161
*/
1162

1163
Query_log_event::Query_log_event(const char* buf, uint event_len,
unknown's avatar
unknown committed
1164 1165
                                 const Format_description_log_event *description_event,
                                 Log_event_type event_type)
1166 1167
  :Log_event(buf, description_event), data_buf(0), query(NullS), catalog(NullS), 
   db(NullS), catalog_len(0), status_vars_len(0),
1168 1169
   flags2_inited(0), sql_mode_inited(0), charset_inited(0),
   auto_increment_increment(1), auto_increment_offset(1)
unknown's avatar
unknown committed
1170 1171
{
  ulong data_len;
1172 1173 1174 1175 1176 1177
  uint32 tmp;
  uint8 common_header_len, post_header_len;
  const char *start, *end;
  DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");

  common_header_len= description_event->common_header_len;
unknown's avatar
unknown committed
1178
  post_header_len= description_event->post_header_len[event_type-1];
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
  DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d",
                     event_len, common_header_len, post_header_len));
  
  /*
    We test if the event's length is sensible, and if so we compute data_len.
    We cannot rely on QUERY_HEADER_LEN here as it would not be format-tolerant.
    We use QUERY_HEADER_MINIMAL_LEN which is the same for 3.23, 4.0 & 5.0.
  */
  if (event_len < (uint)(common_header_len + post_header_len))
    DBUG_VOID_RETURN;				
  data_len = event_len - (common_header_len + post_header_len);
  buf+= common_header_len;
  
  slave_proxy_id= thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET);
  exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
  db_len = (uint)buf[Q_DB_LEN_OFFSET];
  error_code = uint2korr(buf + Q_ERR_CODE_OFFSET);

  /*
    5.0 format starts here.
    Depending on the format, we may or not have affected/warnings etc
    The remnent post-header to be parsed has length:
  */
  tmp= post_header_len - QUERY_HEADER_MINIMAL_LEN; 
  if (tmp)
1204
  {
1205 1206 1207 1208 1209
    status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET);
    data_len-= status_vars_len;
    DBUG_PRINT("info", ("Query_log_event has status_vars_len: %u",
                        (uint) status_vars_len));
    tmp-= 2;
1210
  }
unknown's avatar
unknown committed
1211 1212 1213 1214 1215 1216
  /*
    We have parsed everything we know in the post header for QUERY_EVENT,
    the rest of post header is either comes from older version MySQL or
    dedicated to derived events (e.g. Execute_load_query...)
  */

1217 1218 1219 1220 1221
  /* variable-part: the status vars; only in MySQL 5.0  */
  
  start= (char*) (buf+post_header_len);
  end= (char*) (start+status_vars_len);
  for (const uchar* pos= (const uchar*) start; pos < (const uchar*) end;)
1222
  {
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
    switch (*pos++) {
    case Q_FLAGS2_CODE:
      flags2_inited= 1;
      flags2= uint4korr(pos);
      DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", flags2));
      pos+= 4;
      break;
    case Q_SQL_MODE_CODE:
    {
#ifndef DBUG_OFF
      char buff[22];
#endif
      sql_mode_inited= 1;
      sql_mode= (ulong) uint8korr(pos); // QQ: Fix when sql_mode is ulonglong
      DBUG_PRINT("info",("In Query_log_event, read sql_mode: %s",
			 llstr(sql_mode, buff)));
      pos+= 8;
      break;
    }
    case Q_CATALOG_CODE:
      catalog_len= *pos;
      if (catalog_len)
        catalog= (char*) pos+1;                           // Will be copied later
      pos+= catalog_len+2;
      break;
    case Q_AUTO_INCREMENT:
      auto_increment_increment= uint2korr(pos);
      auto_increment_offset=    uint2korr(pos+2);
      pos+= 4;
      break;
1253 1254 1255 1256 1257 1258 1259
    case Q_CHARSET_CODE:
    {
      charset_inited= 1;
      memcpy(charset, pos, 6);
      pos+= 6;
      break;
    }
1260 1261 1262 1263 1264 1265
    default:
      /* That's why you must write status vars in growing order of code */
      DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\
 code: %u), skipping the rest of them", (uint) *(pos-1)));
      pos= (const uchar*) end;                         // Break look
    }
1266
  }
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
  
  /* A 2nd variable part; this is common to all versions */ 
  
  if (!(start= data_buf = (char*) my_malloc(catalog_len + data_len +2, MYF(MY_WME))))
    DBUG_VOID_RETURN;
  if (catalog)                                  // If catalog is given
  {
    memcpy((char*) start, catalog, catalog_len+1);      // Copy name and end \0
    catalog= start;
    start+= catalog_len+1;
  }
  memcpy((char*) start, end, data_len);          // Copy db and query
  ((char*) start)[data_len]= '\0';              // End query with \0 (For safetly)
  db= start;
  query= start + db_len + 1;
  q_len= data_len - db_len -1;
  /* This is used to detect wrong parsing. Could be removed in the future. */
  DBUG_PRINT("info", ("catalog: '%s'  len: %u   db: '%s'  len:  %u  q_len: %lu",
                      catalog, (uint) catalog_len, db, (uint) db_len,q_len));
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1287 1288
}

1289

unknown's avatar
unknown committed
1290
/*
1291
  Query_log_event::print()
unknown's avatar
unknown committed
1292
*/
1293

1294
#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
1295 1296
void Query_log_event::print_query_header(FILE* file, bool short_form,
                                         LAST_EVENT_INFO* last_event_info)
unknown's avatar
unknown committed
1297
{
1298
  // TODO: print the catalog ??
1299
  char buff[40],*end;				// Enough for SET TIMESTAMP
1300 1301 1302
  bool different_db= 1;
  uint32 tmp;

unknown's avatar
unknown committed
1303 1304
  if (!short_form)
  {
1305
    print_header(file);
unknown's avatar
unknown committed
1306 1307
    fprintf(file, "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n",
	    get_type_str(), (ulong) thread_id, (ulong) exec_time, error_code);
unknown's avatar
unknown committed
1308 1309
  }

unknown's avatar
unknown committed
1310
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db)
1311
  {
unknown's avatar
unknown committed
1312
    if (different_db= memcmp(last_event_info->db, db, db_len + 1))
1313
      memcpy(last_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
1314
    if (db[0] && different_db) 
1315
      fprintf(file, "use %s;\n", db);
1316
  }
1317

1318 1319 1320 1321
  end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
  *end++=';';
  *end++='\n';
  my_fwrite(file, (byte*) buff, (uint) (end-buff),MYF(MY_NABP | MY_WME));
unknown's avatar
unknown committed
1322 1323
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
    fprintf(file,"SET @@session.pseudo_thread_id=%lu;\n",(ulong)thread_id);
1324

1325
  /*
1326 1327 1328
    If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to
    print (remember we don't produce mixed relay logs so there cannot be
    5.0 events before that one so there is nothing to reset).
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
  */
  if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */
  {
    /* tmp is a bitmask of bits which have changed. */
    if (likely(last_event_info->flags2_inited)) 
      /* All bits which have changed */
      tmp= (last_event_info->flags2) ^ flags2;
    else /* that's the first Query event we read */
    {
      last_event_info->flags2_inited= 1;
      tmp= ~((uint32)0); /* all bits have changed */
    }

    if (unlikely(tmp)) /* some bits have changed */
    {
      bool need_comma= 0;
      fprintf(file, "SET ");
      print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2,
                   "@@session.foreign_key_checks", &need_comma);
      print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2,
                   "@@session.sql_auto_is_null", &need_comma);
      print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2,
                   "@@session.unique_checks", &need_comma);
      fprintf(file,";\n");
      last_event_info->flags2= flags2;
    }
  }

  /*
1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
    Now the session variables;
    it's more efficient to pass SQL_MODE as a number instead of a
    comma-separated list.
    FOREIGN_KEY_CHECKS, SQL_AUTO_IS_NULL, UNIQUE_CHECKS are session-only
    variables (they have no global version; they're not listed in
    sql_class.h), The tests below work for pure binlogs or pure relay
    logs. Won't work for mixed relay logs but we don't create mixed
    relay logs (that is, there is no relay log with a format change
    except within the 3 first events, which mysqlbinlog handles
    gracefully). So this code should always be good.
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392
  */

  if (likely(sql_mode_inited))
  {
    if (unlikely(!last_event_info->sql_mode_inited)) /* first Query event */
    {
      last_event_info->sql_mode_inited= 1;
      /* force a difference to force write */
      last_event_info->sql_mode= ~sql_mode;
    }
    if (unlikely(last_event_info->sql_mode != sql_mode))
    {
      fprintf(file,"SET @@session.sql_mode=%lu;\n",(ulong)sql_mode);
      last_event_info->sql_mode= sql_mode;
    }
  }
  if (last_event_info->auto_increment_increment != auto_increment_increment ||
      last_event_info->auto_increment_offset != auto_increment_offset)
  {
    fprintf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu;\n",
            auto_increment_increment,auto_increment_offset);
    last_event_info->auto_increment_increment= auto_increment_increment;
    last_event_info->auto_increment_offset=    auto_increment_offset;
  }

1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
  if (likely(charset_inited))
  {
    if (unlikely(!last_event_info->charset_inited)) /* first Query event */
    {
      last_event_info->charset_inited= 1;
      last_event_info->charset[0]= ~charset[0]; // force a difference to force write
    }
    if (unlikely(bcmp(last_event_info->charset, charset, 6)))
    {
      fprintf(file,"SET "
              "@@session.character_set_client=%d,"
              "@@session.collation_connection=%d,"
              "@@session.collation_server=%d"
              ";\n",
              uint2korr(charset),
              uint2korr(charset+2),
              uint2korr(charset+4));
      memcpy(last_event_info->charset, charset, 6);
    }
  }
unknown's avatar
unknown committed
1413 1414
}

1415

unknown's avatar
unknown committed
1416 1417 1418 1419
void Query_log_event::print(FILE* file, bool short_form,
                            LAST_EVENT_INFO* last_event_info)
{
  print_query_header(file, short_form, last_event_info);
unknown's avatar
unknown committed
1420
  my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
1421
  fputs(";\n", file);
unknown's avatar
unknown committed
1422
}
unknown's avatar
unknown committed
1423
#endif /* MYSQL_CLIENT */
1424

unknown's avatar
unknown committed
1425

unknown's avatar
unknown committed
1426
/*
1427
  Query_log_event::exec_event()
unknown's avatar
unknown committed
1428
*/
unknown's avatar
unknown committed
1429

unknown's avatar
SCRUM  
unknown committed
1430
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1431
int Query_log_event::exec_event(struct st_relay_log_info* rli)
unknown's avatar
unknown committed
1432 1433 1434 1435 1436 1437
{
  return exec_event(rli, query, q_len);
}


int Query_log_event::exec_event(struct st_relay_log_info* rli, const char *query_arg, uint32 q_len_arg)
unknown's avatar
unknown committed
1438
{
unknown's avatar
unknown committed
1439
  int expected_error,actual_error= 0;
1440 1441 1442 1443 1444 1445 1446
  /*
    Colleagues: please never free(thd->catalog) in MySQL. This would lead to
    bugs as here thd->catalog is a part of an alloced block, not an entire
    alloced block (see Query_log_event::exec_event()). Same for thd->db.
    Thank you.
  */
  thd->catalog= (char*) catalog;
unknown's avatar
unknown committed
1447 1448
  thd->db_length= db_len;
  thd->db= (char*) rewrite_db(db, &thd->db_length);
1449 1450
  thd->variables.auto_increment_increment= auto_increment_increment;
  thd->variables.auto_increment_offset=    auto_increment_offset;
unknown's avatar
unknown committed
1451

1452
  /*
1453 1454 1455 1456 1457 1458 1459 1460
    InnoDB internally stores the master log position it has executed so far,
    i.e. the position just after the COMMIT event.
    When InnoDB will want to store, the positions in rli won't have
    been updated yet, so group_master_log_* will point to old BEGIN
    and event_master_log* will point to the beginning of current COMMIT.
    But log_pos of the COMMIT Query event is what we want, i.e. the pos of the
    END of the current log event (COMMIT). We save it in rli so that InnoDB can
    access it.
1461
  */
unknown's avatar
unknown committed
1462
  rli->future_group_master_log_pos= log_pos;
1463 1464
  DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));

unknown's avatar
unknown committed
1465
  clear_all_errors(thd, rli);
1466 1467 1468 1469

  if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
  {
    thd->set_time((time_t)when);
unknown's avatar
unknown committed
1470 1471
    thd->query_length= q_len_arg;
    thd->query= (char*)query_arg;
unknown's avatar
unknown committed
1472
    VOID(pthread_mutex_lock(&LOCK_thread_count));
1473
    thd->query_id = next_query_id();
1474
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
1475
    thd->variables.pseudo_thread_id= thread_id;		// for temp tables
unknown's avatar
unknown committed
1476
    DBUG_PRINT("query",("%s",thd->query));
1477

unknown's avatar
unknown committed
1478
    if (ignored_error_code((expected_error= error_code)) ||
1479
	!check_expected_error(thd,rli,expected_error))
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531
    {
      if (flags2_inited)
        /*
          all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must
          take their value from flags2.
        */
        thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG);
      /*
        else, we are in a 3.23/4.0 binlog; we previously received a
        Rotate_log_event which reset thd->options and sql_mode etc, so nothing to do.
      */
      /*
        We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a
        slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not
        force us to ignore the dir too. Imagine you are a ring of machines, and
        one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE
        on this machine; you don't want it to propagate elsewhere (you don't want
        all slaves to start ignoring the dirs).
      */
      if (sql_mode_inited)
        thd->variables.sql_mode=
          (ulong) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) |
                   (sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE));
      if (charset_inited)
      {
        if (rli->cached_charset_compare(charset))
        {
          /* Verify that we support the charsets found in the event. */
          if (!(thd->variables.character_set_client=
                get_charset(uint2korr(charset), MYF(MY_WME))) ||
              !(thd->variables.collation_connection=
                get_charset(uint2korr(charset+2), MYF(MY_WME))) ||
              !(thd->variables.collation_server=
                get_charset(uint2korr(charset+4), MYF(MY_WME))))
          {
            /*
              We updated the thd->variables with nonsensical values (0), and the
              thread is not guaranteed to terminate now (as it may be configured
              to ignore EE_UNKNOWN_CHARSET);if we're going to execute a next
              statement we'll have a new charset info with it, so no problem to
              have stored 0 in thd->variables. But we invalidate cached
              charset to force a check next time (otherwise if next time
              charset is unknown again we won't detect it).
            */
            rli->cached_charset_invalidate();
            goto compare_errors;
          }
          thd->update_charset(); // for the charset change to take effect
        }
      }

      /* Execute the query (note that we bypass dispatch_command()) */
unknown's avatar
unknown committed
1532
      mysql_parse(thd, thd->query, thd->query_length);
1533 1534

    }
unknown's avatar
unknown committed
1535 1536
    else
    {
unknown's avatar
unknown committed
1537
      /*
unknown's avatar
unknown committed
1538 1539 1540 1541 1542
        The query got a really bad error on the master (thread killed etc),
        which could be inconsistent. Parse it to test the table names: if the
        replicate-*-do|ignore-table rules say "this query must be ignored" then
        we exit gracefully; otherwise we warn about the bad error and tell DBA
        to check/fix it.
unknown's avatar
unknown committed
1543
      */
unknown's avatar
unknown committed
1544
      if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
unknown's avatar
unknown committed
1545 1546
        clear_all_errors(thd, rli);        /* Can ignore query */
      else
1547
      {
unknown's avatar
unknown committed
1548
        slave_print_error(rli,expected_error, 
unknown's avatar
unknown committed
1549
                          "\
unknown's avatar
unknown committed
1550
Query partially completed on the master (error on master: %d) \
unknown's avatar
unknown committed
1551 1552 1553
and was aborted. There is a chance that your master is inconsistent at this \
point. If you are sure that your master is ok, run this query manually on the \
slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; \
unknown's avatar
unknown committed
1554
START SLAVE; . Query: '%s'", expected_error, thd->query);
unknown's avatar
unknown committed
1555 1556 1557 1558
        thd->query_error= 1;
      }
      goto end;
    }
1559

1560 1561 1562 1563
    /* If the query was not ignored, it is printed to the general log */
    if (thd->net.last_errno != ER_SLAVE_IGNORED_TABLE)
      mysql_log.write(thd,COM_QUERY,"%s",thd->query);

1564
compare_errors:
unknown's avatar
unknown committed
1565 1566

     /*
unknown's avatar
unknown committed
1567 1568 1569 1570
      If we expected a non-zero error code, and we don't get the same error
      code, and none of them should be ignored.
    */
    DBUG_PRINT("info",("expected_error: %d  last_errno: %d",
1571
 		       expected_error, thd->net.last_errno));
unknown's avatar
unknown committed
1572
    if ((expected_error != (actual_error= thd->net.last_errno)) &&
1573 1574 1575
 	expected_error &&
 	!ignored_error_code(actual_error) &&
 	!ignored_error_code(expected_error))
unknown's avatar
unknown committed
1576 1577
    {
      slave_print_error(rli, 0,
1578
 			"\
unknown's avatar
unknown committed
1579
Query caused different errors on master and slave. \
unknown's avatar
unknown committed
1580
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
unknown's avatar
unknown committed
1581
Default database: '%s'. Query: '%s'",
unknown's avatar
unknown committed
1582 1583 1584 1585
			ER_SAFE(expected_error),
			expected_error,
			actual_error ? thd->net.last_error: "no error",
			actual_error,
unknown's avatar
unknown committed
1586
			print_slave_db_safe(db), query_arg);
unknown's avatar
unknown committed
1587 1588 1589 1590 1591 1592
      thd->query_error= 1;
    }
    /*
      If we get the same error code as expected, or they should be ignored. 
    */
    else if (expected_error == actual_error ||
1593
 	     ignored_error_code(actual_error))
unknown's avatar
unknown committed
1594 1595 1596 1597 1598 1599
    {
      DBUG_PRINT("info",("error ignored"));
      clear_all_errors(thd, rli);
    }
    /*
      Other cases: mostly we expected no error and get one.
unknown's avatar
unknown committed
1600
    */
unknown's avatar
unknown committed
1601 1602 1603
    else if (thd->query_error || thd->is_fatal_error)
    {
      slave_print_error(rli,actual_error,
unknown's avatar
unknown committed
1604
			"Error '%s' on query. Default database: '%s'. Query: '%s'",
unknown's avatar
unknown committed
1605 1606
			(actual_error ? thd->net.last_error :
			 "unexpected success or fatal error"),
unknown's avatar
unknown committed
1607
			print_slave_db_safe(thd->db), query_arg);
unknown's avatar
unknown committed
1608 1609
      thd->query_error= 1;
    }
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623

    /*
      TODO: compare the values of "affected rows" around here. Something
      like:
      if ((uint32) affected_in_event != (uint32) affected_on_slave)
      {
      sql_print_error("Slave: did not get the expected number of affected \
      rows running query from master - expected %d, got %d (this numbers \
      should have matched modulo 4294967296).", 0, ...);
      thd->query_error = 1;
      }
      We may also want an option to tell the slave to ignore "affected"
      mismatch. This mismatch could be implemented with a new ER_ code, and
      to ignore it you would use --slave-skip-errors...
1624

1625 1626 1627 1628 1629 1630 1631
      To do the comparison we need to know the value of "affected" which the
      above mysql_parse() computed. And we need to know the value of
      "affected" in the master's binlog. Both will be implemented later. The
      important thing is that we now have the format ready to log the values
      of "affected" in the binlog. So we can release 5.0.0 before effectively
      logging "affected" and effectively comparing it.
    */
unknown's avatar
unknown committed
1632 1633
  } /* End of if (db_ok(... */

unknown's avatar
unknown committed
1634
end:
1635
  VOID(pthread_mutex_lock(&LOCK_thread_count));
1636 1637 1638 1639 1640 1641
  /*
    Probably we have set thd->query, thd->db, thd->catalog to point to places
    in the data_buf of this event. Now the event is going to be deleted
    probably, so data_buf will be freed, so the thd->... listed above will be
    pointers to freed memory. 
    So we must set them to 0, so that those bad pointers values are not later
1642 1643 1644
    used. Note that "cleanup" queries like automatic DROP TEMPORARY TABLE
    don't suffer from these assignments to 0 as DROP TEMPORARY
    TABLE uses the db.table syntax.
1645 1646
  */
  thd->db= thd->catalog= 0;	        // prevent db from being freed
1647
  thd->query= 0;			// just to be sure
unknown's avatar
unknown committed
1648
  thd->query_length= thd->db_length =0;
1649
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
1650
  close_thread_tables(thd);      
unknown's avatar
unknown committed
1651
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1652 1653 1654 1655 1656 1657 1658
  /*
    If there was an error we stop. Otherwise we increment positions. Note that
    we will not increment group* positions if we are just after a SET
    ONE_SHOT, because SET ONE_SHOT should not be separated from its following
    updating query.
  */
  return (thd->query_error ? thd->query_error : 
1659
          (thd->one_shot_set ? (rli->inc_event_relay_log_pos(),0) :
1660
           Log_event::exec_event(rli))); 
unknown's avatar
unknown committed
1661
}
unknown's avatar
SCRUM  
unknown committed
1662
#endif
unknown's avatar
unknown committed
1663

unknown's avatar
unknown committed
1664

unknown's avatar
unknown committed
1665
/**************************************************************************
1666
	Start_log_event_v3 methods
unknown's avatar
unknown committed
1667
**************************************************************************/
1668

1669 1670 1671 1672 1673 1674 1675 1676
#ifndef MYSQL_CLIENT
Start_log_event_v3::Start_log_event_v3() :Log_event(), binlog_version(BINLOG_VERSION), artificial_event(0)
{
  created= when;
  memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
#endif

unknown's avatar
unknown committed
1677
/*
1678
  Start_log_event_v3::pack_info()
unknown's avatar
unknown committed
1679
*/
1680

unknown's avatar
SCRUM  
unknown committed
1681
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1682
void Start_log_event_v3::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
1683
{
1684 1685 1686 1687
  char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos;
  pos= strmov(buf, "Server ver: ");
  pos= strmov(pos, server_version);
  pos= strmov(pos, ", Binlog ver: ");
unknown's avatar
unknown committed
1688 1689
  pos= int10_to_str(binlog_version, pos, 10);
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
unknown's avatar
unknown committed
1690
}
unknown's avatar
SCRUM  
unknown committed
1691
#endif
1692 1693


unknown's avatar
unknown committed
1694
/*
1695
  Start_log_event_v3::print()
unknown's avatar
unknown committed
1696
*/
unknown's avatar
unknown committed
1697 1698

#ifdef MYSQL_CLIENT
1699
void Start_log_event_v3::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
unknown's avatar
unknown committed
1700
{
1701 1702 1703 1704 1705 1706 1707 1708 1709
  if (!short_form)
  {
    print_header(file);
    fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
            server_version);
    print_timestamp(file);
    if (created)
      fprintf(file," at startup");
    fputc('\n', file);
1710 1711 1712
    if (flags & LOG_EVENT_BINLOG_IN_USE_F)
      fprintf(file, "# Warning: this binlog was not closed properly. "
              "Most probably mysqld crashed writing it.\n");
1713
  }
1714 1715
  if (!artificial_event && created)
  {
1716
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
1717 1718 1719 1720 1721 1722 1723 1724 1725
    /*
      This is for mysqlbinlog: like in replication, we want to delete the stale
      tmp files left by an unclean shutdown of mysqld (temporary tables)
      and rollback unfinished transaction.
      Probably this can be done with RESET CONNECTION (syntax to be defined).
    */
    fprintf(file,"RESET CONNECTION;\n");
#else
    fprintf(file,"ROLLBACK;\n");
1726
#endif
1727
  }
unknown's avatar
unknown committed
1728 1729
  fflush(file);
}
unknown's avatar
unknown committed
1730
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
1731

unknown's avatar
unknown committed
1732
/*
1733
  Start_log_event_v3::Start_log_event_v3()
unknown's avatar
unknown committed
1734
*/
1735

1736 1737 1738
Start_log_event_v3::Start_log_event_v3(const char* buf,
                                       const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
1739
{
1740 1741
  buf+= description_event->common_header_len;
  binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
1742 1743
  memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
	 ST_SERVER_VER_LEN);
1744 1745 1746
  created= uint4korr(buf+ST_CREATED_OFFSET);
  /* We use log_pos to mark if this was an artificial event or not */
  artificial_event= (log_pos == 0);
unknown's avatar
unknown committed
1747 1748
}

1749

unknown's avatar
unknown committed
1750
/*
1751
  Start_log_event_v3::write()
unknown's avatar
unknown committed
1752
*/
1753

1754
bool Start_log_event_v3::write(IO_CACHE* file)
1755
{
1756
  char buff[START_V3_HEADER_LEN];
1757 1758 1759
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
  int4store(buff + ST_CREATED_OFFSET,created);
1760 1761
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, (byte*) buff, sizeof(buff)));
1762
}
1763

1764

unknown's avatar
unknown committed
1765
/*
1766
  Start_log_event_v3::exec_event()
1767 1768 1769 1770

  The master started

  IMPLEMENTATION
unknown's avatar
unknown committed
1771 1772 1773 1774
    - To handle the case where the master died without having time to write
      DROP TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is
      TODO), we clean up all temporary tables that we got, if we are sure we
      can (see below).
1775 1776

  TODO
1777 1778 1779 1780 1781
    - Remove all active user locks.
      Guilhem 2003-06: this is true but not urgent: the worst it can cause is
      the use of a bit of memory for a user lock which will not be used
      anymore. If the user lock is later used, the old one will be released. In
      other words, no deadlock problem.
unknown's avatar
unknown committed
1782 1783
*/

unknown's avatar
SCRUM  
unknown committed
1784
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1785
int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
1786
{
1787
  DBUG_ENTER("Start_log_event_v3::exec_event");
1788
  switch (binlog_version)
1789 1790 1791 1792 1793 1794 1795 1796
  {
  case 3:
  case 4:
    /*
      This can either be 4.x (then a Start_log_event_v3 is only at master
      startup so we are sure the master has restarted and cleared his temp
      tables; the event always has 'created'>0) or 5.0 (then we have to test
      'created').
unknown's avatar
unknown committed
1797
    */
1798 1799 1800 1801 1802
    if (created)
    {
      close_temporary_tables(thd);
      cleanup_load_tmpdir();
    }
unknown's avatar
unknown committed
1803 1804
    break;

1805
    /*
unknown's avatar
unknown committed
1806 1807
       Now the older formats; in that case load_tmpdir is cleaned up by the I/O
       thread.
unknown's avatar
unknown committed
1808
    */
1809
  case 1:
1810
    if (strncmp(rli->relay_log.description_event_for_exec->server_version,
1811 1812 1813 1814 1815 1816 1817 1818
                "3.23.57",7) >= 0 && created)
    {
      /*
        Can distinguish, based on the value of 'created': this event was
        generated at master startup.
      */
      close_temporary_tables(thd);
    }
unknown's avatar
unknown committed
1819
    /*
1820 1821 1822
      Otherwise, can't distinguish a Start_log_event generated at
      master startup and one generated by master FLUSH LOGS, so cannot
      be sure temp tables have to be dropped. So do nothing.
unknown's avatar
unknown committed
1823 1824 1825 1826
    */
    break;
  default:
    /* this case is impossible */
1827
    DBUG_RETURN(1);
unknown's avatar
unknown committed
1828
  }
1829
  DBUG_RETURN(Log_event::exec_event(rli));
1830
}
unknown's avatar
unknown committed
1831
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
1832

1833 1834 1835 1836 1837 1838 1839 1840 1841
/***************************************************************************
       Format_description_log_event methods
****************************************************************************/

/*
  Format_description_log_event 1st ctor.

  SYNOPSIS
    Format_description_log_event::Format_description_log_event
1842
      binlog_version              the binlog version for which we want to build
1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
                                  an event. Can be 1 (=MySQL 3.23), 3 (=4.0.x
                                  x>=2 and 4.1) or 4 (MySQL 5.0). Note that the
                                  old 4.0 (binlog version 2) is not supported;
                                  it should not be used for replication with
                                  5.0.

  DESCRIPTION
    Ctor. Can be used to create the event to write to the binary log (when the
    server starts or when FLUSH LOGS), or to create artificial events to parse
    binlogs from MySQL 3.23 or 4.x.
    When in a client, only the 2nd use is possible.
*/

Format_description_log_event::
1857
Format_description_log_event(uint8 binlog_ver, const char* server_ver)
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868
  :Start_log_event_v3()
{
  created= when;
  binlog_version= binlog_ver;
  switch (binlog_ver) {
  case 4: /* MySQL 5.0 */
    memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
    common_header_len= LOG_EVENT_HEADER_LEN;
    number_of_event_types= LOG_EVENT_TYPES;
    /* we'll catch my_malloc() error in is_valid() */
    post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
1869
                                       MYF(MY_ZEROFILL));
1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
    /*
      This long list of assignments is not beautiful, but I see no way to
      make it nicer, as the right members are #defines, not array members, so
      it's impossible to write a loop.
    */
    if (post_header_len)
    {
      post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
      post_header_len[QUERY_EVENT-1]= QUERY_HEADER_LEN;
      post_header_len[ROTATE_EVENT-1]= ROTATE_HEADER_LEN;
      post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
      post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
      post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
      post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
      post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
      post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
      post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
unknown's avatar
unknown committed
1887 1888
      post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= post_header_len[APPEND_BLOCK_EVENT-1];
      post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902
    }
    break;

  case 1: /* 3.23 */
  case 3: /* 4.0.x x>=2 */
    /*
      We build an artificial (i.e. not sent by the master) event, which
      describes what those old master versions send.
    */
    if (binlog_ver==1)
      strmov(server_version, server_ver ? server_ver : "3.23");
    else
      strmov(server_version, server_ver ? server_ver : "4.0");
    common_header_len= binlog_ver==1 ? OLD_HEADER_LEN :
1903
      LOG_EVENT_MINIMAL_HEADER_LEN;
1904 1905 1906 1907 1908 1909 1910 1911 1912
    /*
      The first new event in binlog version 4 is Format_desc. So any event type
      after that does not exist in older versions. We use the events known by
      version 3, even if version 1 had only a subset of them (this is not a
      problem: it uses a few bytes for nothing but unifies code; it does not
      make the slave detect less corruptions).
    */
    number_of_event_types= FORMAT_DESCRIPTION_EVENT - 1;
    post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
1913
                                       MYF(0));
1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943
    if (post_header_len)
    {
      post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
      post_header_len[QUERY_EVENT-1]= QUERY_HEADER_MINIMAL_LEN;
      post_header_len[STOP_EVENT-1]= 0;
      post_header_len[ROTATE_EVENT-1]= (binlog_ver==1) ? 0 : ROTATE_HEADER_LEN;
      post_header_len[INTVAR_EVENT-1]= 0;
      post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
      post_header_len[SLAVE_EVENT-1]= 0;
      post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
      post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
      post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
      post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
      post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
      post_header_len[RAND_EVENT-1]= 0;
      post_header_len[USER_VAR_EVENT-1]= 0;
    }
    break;
  default: /* Includes binlog version 2 i.e. 4.0.x x<=1 */
    post_header_len= 0; /* will make is_valid() fail */
    break;
  }
}


/*
  The problem with this constructor is that the fixed header may have a
  length different from this version, but we don't know this length as we
  have not read the Format_description_log_event which says it, yet. This
  length is in the post-header of the event, but we don't know where the
1944
  post-header starts.
1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
  So this type of event HAS to:
  - either have the header's length at the beginning (in the header, at a
  fixed position which will never be changed), not in the post-header. That
  would make the header be "shifted" compared to other events.
  - or have a header of size LOG_EVENT_MINIMAL_HEADER_LEN (19), in all future
  versions, so that we know for sure.
  I (Guilhem) chose the 2nd solution. Rotate has the same constraint (because
  it is sent before Format_description_log_event).
*/

Format_description_log_event::
Format_description_log_event(const char* buf,
                             uint event_len,
                             const
                             Format_description_log_event*
1960
                             description_event)
1961 1962 1963 1964 1965 1966 1967 1968 1969
  :Start_log_event_v3(buf, description_event)
{
  DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)");
  buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
  if ((common_header_len=buf[ST_COMMON_HEADER_LEN_OFFSET]) < OLD_HEADER_LEN)
    DBUG_VOID_RETURN; /* sanity check */
  number_of_event_types=
    event_len-(LOG_EVENT_MINIMAL_HEADER_LEN+ST_COMMON_HEADER_LEN_OFFSET+1);
  DBUG_PRINT("info", ("common_header_len=%d number_of_event_types=%d",
1970
                      common_header_len, number_of_event_types));
1971 1972 1973
  /* If alloc fails, we'll detect it in is_valid() */
  post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
                                      number_of_event_types*
1974
                                      sizeof(*post_header_len), MYF(0));
1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988
  DBUG_VOID_RETURN;
}


bool Format_description_log_event::write(IO_CACHE* file)
{
  /*
    We don't call Start_log_event_v3::write() because this would make 2
    my_b_safe_write().
  */
  byte buff[FORMAT_DESCRIPTION_HEADER_LEN];
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
  int4store(buff + ST_CREATED_OFFSET,created);
1989
  buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
1990 1991 1992 1993 1994
  memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (byte*) post_header_len,
         LOG_EVENT_TYPES);
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, buff, sizeof(buff)));
}
1995

1996 1997 1998
/*
  SYNOPSIS
    Format_description_log_event::exec_event()
1999

2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
  IMPLEMENTATION
    Save the information which describes the binlog's format, to be able to
    read all coming events.
    Call Start_log_event_v3::exec_event().
*/

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Format_description_log_event::exec_event(struct st_relay_log_info* rli)
{
  DBUG_ENTER("Format_description_log_event::exec_event");
2010 2011

  /* save the information describing this binlog */
2012 2013 2014
  delete rli->relay_log.description_event_for_exec;
  rli->relay_log.description_event_for_exec= this;

2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035
  /*
    As a transaction NEVER spans on 2 or more binlogs:
    if we have an active transaction at this point, the master died
    while writing the transaction to the binary log, i.e. while
    flushing the binlog cache to the binlog. As the write was started,
    the transaction had been committed on the master, so we lack of
    information to replay this transaction on the slave; all we can do
    is stop with error.
    Note: this event could be sent by the master to inform us of the
    format of its binlog; in other words maybe it is not at its
    original place when it comes to us; we'll know this by checking
    log_pos ("artificial" events have log_pos == 0).
  */
  if (!artificial_event && created && thd->transaction.all.nht)
  {
    slave_print_error(rli, 0, "Rolling back unfinished transaction (no "
                      "COMMIT or ROLLBACK) from relay log. A probable cause "
                      "is that the master died while writing the transaction "
                      "to its binary log.");
    end_trans(thd, ROLLBACK);
  }
2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
  /*
    If this event comes from ourselves, there is no cleaning task to perform,
    we don't call Start_log_event_v3::exec_event() (this was just to update the
    log's description event).
  */
  if (server_id == (uint32) ::server_id)
  {
    /*
      Do not modify rli->group_master_log_pos, as this event did not exist on
      the master. That is, just update the *relay log* coordinates; this is
      done by passing log_pos=0 to inc_group_relay_log_pos, like we do in
      Stop_log_event::exec_event().
      If in a transaction, don't touch group_* coordinates.
    */
    if (thd->options & OPTION_BEGIN)
      rli->inc_event_relay_log_pos();
    else
    {
      rli->inc_group_relay_log_pos(0);
      flush_relay_log_info(rli);
    }
    DBUG_RETURN(0);
  }

  /*
    If the event was not requested by the slave i.e. the master sent it while
    the slave asked for a position >4, the event will make
    rli->group_master_log_pos advance. Say that the slave asked for position
2064 2065 2066
    1000, and the Format_desc event's end is 96. Then in the beginning of
    replication rli->group_master_log_pos will be 0, then 96, then jump to
    first really asked event (which is >96). So this is ok.
2067 2068 2069 2070 2071 2072
  */
  DBUG_RETURN(Start_log_event_v3::exec_event(rli));
}
#endif

  /**************************************************************************
2073
        Load_log_event methods
2074
   General note about Load_log_event: the binlogging of LOAD DATA INFILE is
2075
   going to be changed in 5.0 (or maybe in 5.1; not decided yet).
2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087
   However, the 5.0 slave could still have to read such events (from a 4.x
   master), convert them (which just means maybe expand the header, when 5.0
   servers have a UID in events) (remember that whatever is after the header
   will be like in 4.x, as this event's format is not modified in 5.0 as we
   will use new types of events to log the new LOAD DATA INFILE features).
   To be able to read/convert, we just need to not assume that the common
   header is of length LOG_EVENT_HEADER_LEN (we must use the description
   event).
   Note that I (Guilhem) manually tested replication of a big LOAD DATA INFILE
   between 3.23 and 5.0, and between 4.0 and 5.0, and it works fine (and the
   positions displayed in SHOW SLAVE STATUS then are fine too).
  **************************************************************************/
2088

unknown's avatar
unknown committed
2089
/*
2090
  Load_log_event::pack_info()
unknown's avatar
unknown committed
2091
*/
2092

unknown's avatar
SCRUM  
unknown committed
2093
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
2094
uint Load_log_event::get_query_buffer_length()
2095
{
unknown's avatar
unknown committed
2096
  return
2097 2098
    5 + db_len + 3 +                        // "use DB; "
    18 + fname_len + 2 +                    // "LOAD DATA INFILE 'file''"
unknown's avatar
unknown committed
2099
    7 +					    // LOCAL
2100
    9 +                                     // " REPLACE or IGNORE "
2101
    13 + table_name_len*2 +                 // "INTO TABLE `table`"
2102 2103 2104 2105
    21 + sql_ex.field_term_len*4 + 2 +      // " FIELDS TERMINATED BY 'str'"
    23 + sql_ex.enclosed_len*4 + 2 +        // " OPTIONALLY ENCLOSED BY 'str'"
    12 + sql_ex.escaped_len*4 + 2 +         // " ESCAPED BY 'str'"
    21 + sql_ex.line_term_len*4 + 2 +       // " FIELDS TERMINATED BY 'str'"
2106 2107
    19 + sql_ex.line_start_len*4 + 2 +      // " LINES STARTING BY 'str'"
    15 + 22 +                               // " IGNORE xxx  LINES"
2108
    3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
unknown's avatar
unknown committed
2109
}
2110

unknown's avatar
unknown committed
2111 2112 2113 2114 2115 2116 2117

void Load_log_event::print_query(bool need_db, char *buf,
                                 char **end, char **fn_start, char **fn_end)
{
  char *pos= buf;

  if (need_db && db && db_len)
2118
  {
2119 2120
    pos= strmov(pos, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
2121
    pos= strmov(pos+db_len, "`; ");
2122
  }
2123

unknown's avatar
unknown committed
2124
  pos= strmov(pos, "LOAD DATA ");
unknown's avatar
unknown committed
2125 2126 2127 2128

  if (fn_start)
    *fn_start= pos;

unknown's avatar
unknown committed
2129 2130 2131
  if (check_fname_outside_temp_buf())
    pos= strmov(pos, "LOCAL ");
  pos= strmov(pos, "INFILE '");
2132
  memcpy(pos, fname, fname_len);
unknown's avatar
unknown committed
2133
  pos= strmov(pos+fname_len, "' ");
2134

unknown's avatar
unknown committed
2135
  if (sql_ex.opt_flags & REPLACE_FLAG)
2136
    pos= strmov(pos, " REPLACE ");
unknown's avatar
unknown committed
2137
  else if (sql_ex.opt_flags & IGNORE_FLAG)
2138 2139
    pos= strmov(pos, " IGNORE ");

unknown's avatar
unknown committed
2140 2141 2142 2143 2144 2145
  pos= strmov(pos ,"INTO");

  if (fn_end)
    *fn_end= pos;

  pos= strmov(pos ," TABLE `");
2146 2147 2148
  memcpy(pos, table_name, table_name_len);
  pos+= table_name_len;

unknown's avatar
unknown committed
2149
  /* We have to create all optinal fields as the default is not empty */
2150
  pos= strmov(pos, "` FIELDS TERMINATED BY ");
unknown's avatar
unknown committed
2151 2152 2153 2154 2155
  pos= pretty_print_str(pos, sql_ex.field_term, sql_ex.field_term_len);
  if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
    pos= strmov(pos, " OPTIONALLY ");
  pos= strmov(pos, " ENCLOSED BY ");
  pos= pretty_print_str(pos, sql_ex.enclosed, sql_ex.enclosed_len);
2156

unknown's avatar
unknown committed
2157 2158
  pos= strmov(pos, " ESCAPED BY ");
  pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len);
2159

unknown's avatar
unknown committed
2160 2161
  pos= strmov(pos, " LINES TERMINATED BY ");
  pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len);
2162 2163
  if (sql_ex.line_start_len)
  {
2164
    pos= strmov(pos, " STARTING BY ");
2165
    pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len);
2166
  }
2167

unknown's avatar
unknown committed
2168
  if ((long) skip_lines > 0)
2169 2170
  {
    pos= strmov(pos, " IGNORE ");
unknown's avatar
unknown committed
2171
    pos= longlong10_to_str((longlong) skip_lines, pos, 10);
2172 2173
    pos= strmov(pos," LINES ");    
  }
2174 2175 2176 2177

  if (num_fields)
  {
    uint i;
unknown's avatar
unknown committed
2178
    const char *field= fields;
2179
    pos= strmov(pos, " (");
2180 2181 2182
    for (i = 0; i < num_fields; i++)
    {
      if (i)
unknown's avatar
unknown committed
2183 2184 2185 2186
      {
        *pos++= ' ';
        *pos++= ',';
      }
2187
      memcpy(pos, field, field_lens[i]);
unknown's avatar
unknown committed
2188 2189
      pos+=   field_lens[i];
      field+= field_lens[i]  + 1;
2190
    }
2191
    *pos++= ')';
2192
  }
2193

unknown's avatar
unknown committed
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205
  *end= pos;
}


void Load_log_event::pack_info(Protocol *protocol)
{
  char *buf, *end;

  if (!(buf= my_malloc(get_query_buffer_length(), MYF(MY_WME))))
    return;
  print_query(TRUE, buf, &end, 0, 0);
  protocol->store(buf, end-buf, &my_charset_bin);
unknown's avatar
unknown committed
2206
  my_free(buf, MYF(0));
2207
}
unknown's avatar
unknown committed
2208
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2209

2210

unknown's avatar
unknown committed
2211
/*
2212
  Load_log_event::write_data_header()
unknown's avatar
unknown committed
2213
*/
2214

2215
bool Load_log_event::write_data_header(IO_CACHE* file)
2216
{
2217
  char buf[LOAD_HEADER_LEN];
unknown's avatar
unknown committed
2218
  int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
2219 2220 2221 2222 2223
  int4store(buf + L_EXEC_TIME_OFFSET, exec_time);
  int4store(buf + L_SKIP_LINES_OFFSET, skip_lines);
  buf[L_TBL_LEN_OFFSET] = (char)table_name_len;
  buf[L_DB_LEN_OFFSET] = (char)db_len;
  int4store(buf + L_NUM_FIELDS_OFFSET, num_fields);
2224
  return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN) != 0;
2225
}
2226

2227

unknown's avatar
unknown committed
2228
/*
2229
  Load_log_event::write_data_body()
unknown's avatar
unknown committed
2230
*/
2231

2232
bool Load_log_event::write_data_body(IO_CACHE* file)
2233
{
2234 2235 2236
  if (sql_ex.write_data(file))
    return 1;
  if (num_fields && fields && field_lens)
2237
  {
2238 2239 2240
    if (my_b_safe_write(file, (byte*)field_lens, num_fields) ||
	my_b_safe_write(file, (byte*)fields, field_block_len))
      return 1;
2241
  }
2242 2243 2244
  return (my_b_safe_write(file, (byte*)table_name, table_name_len + 1) ||
	  my_b_safe_write(file, (byte*)db, db_len + 1) ||
	  my_b_safe_write(file, (byte*)fname, fname_len));
2245 2246
}

2247

unknown's avatar
unknown committed
2248
/*
2249
  Load_log_event::Load_log_event()
unknown's avatar
unknown committed
2250
*/
2251

2252
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
2253 2254 2255
Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
			       const char *db_arg, const char *table_name_arg,
			       List<Item> &fields_arg,
unknown's avatar
unknown committed
2256
			       enum enum_duplicates handle_dup,
2257
			       bool ignore, bool using_trans)
2258 2259 2260
  :Log_event(thd_arg, !thd_arg->tmp_table_used ?
	     0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
   thread_id(thd_arg->thread_id),
2261
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
unknown's avatar
unknown committed
2262 2263
   num_fields(0),fields(0),
   field_lens(0),field_block_len(0),
unknown's avatar
unknown committed
2264
   table_name(table_name_arg ? table_name_arg : ""),
2265
   db(db_arg), fname(ex->file_name), local_fname(FALSE)
unknown's avatar
unknown committed
2266 2267 2268
{
  time_t end_time;
  time(&end_time);
2269
  exec_time = (ulong) (end_time  - thd_arg->start_time);
2270 2271 2272
  /* db can never be a zero pointer in 4.0 */
  db_len = (uint32) strlen(db);
  table_name_len = (uint32) strlen(table_name);
unknown's avatar
unknown committed
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285
  fname_len = (fname) ? (uint) strlen(fname) : 0;
  sql_ex.field_term = (char*) ex->field_term->ptr();
  sql_ex.field_term_len = (uint8) ex->field_term->length();
  sql_ex.enclosed = (char*) ex->enclosed->ptr();
  sql_ex.enclosed_len = (uint8) ex->enclosed->length();
  sql_ex.line_term = (char*) ex->line_term->ptr();
  sql_ex.line_term_len = (uint8) ex->line_term->length();
  sql_ex.line_start = (char*) ex->line_start->ptr();
  sql_ex.line_start_len = (uint8) ex->line_start->length();
  sql_ex.escaped = (char*) ex->escaped->ptr();
  sql_ex.escaped_len = (uint8) ex->escaped->length();
  sql_ex.opt_flags = 0;
  sql_ex.cached_new_format = -1;
2286
    
unknown's avatar
unknown committed
2287
  if (ex->dumpfile)
unknown's avatar
unknown committed
2288
    sql_ex.opt_flags|= DUMPFILE_FLAG;
unknown's avatar
unknown committed
2289
  if (ex->opt_enclosed)
unknown's avatar
unknown committed
2290
    sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
2291

unknown's avatar
unknown committed
2292
  sql_ex.empty_flags= 0;
2293

2294
  switch (handle_dup) {
unknown's avatar
unknown committed
2295
  case DUP_REPLACE:
unknown's avatar
unknown committed
2296
    sql_ex.opt_flags|= REPLACE_FLAG;
unknown's avatar
unknown committed
2297 2298 2299 2300
    break;
  case DUP_UPDATE:				// Impossible here
  case DUP_ERROR:
    break;	
unknown's avatar
unknown committed
2301
  }
2302 2303
  if (ignore)
    sql_ex.opt_flags|= IGNORE_FLAG;
2304

unknown's avatar
unknown committed
2305 2306 2307 2308 2309 2310 2311 2312 2313 2314
  if (!ex->field_term->length())
    sql_ex.empty_flags |= FIELD_TERM_EMPTY;
  if (!ex->enclosed->length())
    sql_ex.empty_flags |= ENCLOSED_EMPTY;
  if (!ex->line_term->length())
    sql_ex.empty_flags |= LINE_TERM_EMPTY;
  if (!ex->line_start->length())
    sql_ex.empty_flags |= LINE_START_EMPTY;
  if (!ex->escaped->length())
    sql_ex.empty_flags |= ESCAPED_EMPTY;
2315
    
unknown's avatar
unknown committed
2316
  skip_lines = ex->skip_lines;
2317

unknown's avatar
unknown committed
2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328
  List_iterator<Item> li(fields_arg);
  field_lens_buf.length(0);
  fields_buf.length(0);
  Item* item;
  while ((item = li++))
  {
    num_fields++;
    uchar len = (uchar) strlen(item->name);
    field_block_len += len + 1;
    fields_buf.append(item->name, len + 1);
    field_lens_buf.append((char*)&len, 1);
2329 2330
  }

unknown's avatar
unknown committed
2331 2332 2333
  field_lens = (const uchar*)field_lens_buf.ptr();
  fields = fields_buf.ptr();
}
unknown's avatar
unknown committed
2334
#endif /* !MYSQL_CLIENT */
2335

2336

2337
/*
2338
  Load_log_event::Load_log_event()
2339

unknown's avatar
unknown committed
2340 2341 2342
  NOTE
    The caller must do buf[event_len] = 0 before he starts using the
    constructed event.
2343 2344
*/

2345 2346 2347 2348
Load_log_event::Load_log_event(const char *buf, uint event_len,
                               const Format_description_log_event *description_event)
  :Log_event(buf, description_event), num_fields(0), fields(0),
   field_lens(0),field_block_len(0),
unknown's avatar
unknown committed
2349
   table_name(0), db(0), fname(0), local_fname(FALSE)
unknown's avatar
unknown committed
2350
{
unknown's avatar
unknown committed
2351
  DBUG_ENTER("Load_log_event");
2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363
  /*
    I (Guilhem) manually tested replication of LOAD DATA INFILE for 3.23->5.0,
    4.0->5.0 and 5.0->5.0 and it works.
  */
  if (event_len)
    copy_log_event(buf, event_len,
                   ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
                    LOAD_HEADER_LEN + 
                    description_event->common_header_len :
                    LOAD_HEADER_LEN + LOG_EVENT_HEADER_LEN),
                   description_event);
  /* otherwise it's a derived class, will call copy_log_event() itself */
unknown's avatar
unknown committed
2364
  DBUG_VOID_RETURN;
2365 2366
}

2367

unknown's avatar
unknown committed
2368
/*
2369
  Load_log_event::copy_log_event()
unknown's avatar
unknown committed
2370
*/
2371

2372
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
2373 2374
                                   int body_offset,
                                   const Format_description_log_event *description_event)
2375
{
2376
  DBUG_ENTER("Load_log_event::copy_log_event");
2377
  uint data_len;
2378
  char* buf_end = (char*)buf + event_len;
2379 2380
  /* this is the beginning of the post-header */
  const char* data_head = buf + description_event->common_header_len;
unknown's avatar
unknown committed
2381
  slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
2382 2383 2384 2385 2386
  exec_time = uint4korr(data_head + L_EXEC_TIME_OFFSET);
  skip_lines = uint4korr(data_head + L_SKIP_LINES_OFFSET);
  table_name_len = (uint)data_head[L_TBL_LEN_OFFSET];
  db_len = (uint)data_head[L_DB_LEN_OFFSET];
  num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET);
unknown's avatar
unknown committed
2387
	  
unknown's avatar
unknown committed
2388
  if ((int) event_len < body_offset)
unknown's avatar
unknown committed
2389
    DBUG_RETURN(1);
2390 2391 2392 2393
  /*
    Sql_ex.init() on success returns the pointer to the first byte after
    the sql_ex structure, which is the start of field lengths array.
  */
2394 2395 2396
  if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
                                        buf_end,
                                        buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
unknown's avatar
unknown committed
2397
    DBUG_RETURN(1);
2398
  
2399
  data_len = event_len - body_offset;
2400
  if (num_fields > data_len) // simple sanity check against corruption
unknown's avatar
unknown committed
2401
    DBUG_RETURN(1);
2402
  for (uint i = 0; i < num_fields; i++)
2403
    field_block_len += (uint)field_lens[i] + 1;
2404

unknown's avatar
unknown committed
2405 2406 2407 2408
  fields = (char*)field_lens + num_fields;
  table_name  = fields + field_block_len;
  db = table_name + table_name_len + 1;
  fname = db + db_len + 1;
2409 2410
  fname_len = strlen(fname);
  // null termination is accomplished by the caller doing buf[event_len]=0
2411

unknown's avatar
unknown committed
2412
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2413 2414 2415
}


unknown's avatar
unknown committed
2416
/*
2417
  Load_log_event::print()
unknown's avatar
unknown committed
2418
*/
2419 2420

#ifdef MYSQL_CLIENT
2421
void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
unknown's avatar
unknown committed
2422
{
2423
  print(file, short_form, last_event_info, 0);
unknown's avatar
unknown committed
2424 2425
}

unknown's avatar
unknown committed
2426

2427
void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info,
unknown's avatar
unknown committed
2428
			   bool commented)
unknown's avatar
unknown committed
2429
{
unknown's avatar
unknown committed
2430
  DBUG_ENTER("Load_log_event::print");
unknown's avatar
unknown committed
2431 2432
  if (!short_form)
  {
2433
    print_header(file);
2434
    fprintf(file, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
unknown's avatar
unknown committed
2435 2436 2437
	    thread_id, exec_time);
  }

2438
  bool different_db= 1;
2439
  if (db)
unknown's avatar
unknown committed
2440
  {
2441 2442 2443 2444 2445 2446
    /*
      If the database is different from the one of the previous statement, we
      need to print the "use" command, and we update the last_db.
      But if commented, the "use" is going to be commented so we should not
      update the last_db.
    */
2447
    if ((different_db= memcmp(last_event_info->db, db, db_len + 1)) &&
2448
        !commented)
2449
      memcpy(last_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
2450
  }
2451
  
2452
  if (db && db[0] && different_db)
unknown's avatar
unknown committed
2453 2454 2455
    fprintf(file, "%suse %s;\n", 
            commented ? "# " : "",
            db);
unknown's avatar
unknown committed
2456

2457 2458 2459
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
    fprintf(file,"%sSET @@session.pseudo_thread_id=%lu;\n",
            commented ? "# " : "", (ulong)thread_id);
unknown's avatar
unknown committed
2460 2461
  fprintf(file, "%sLOAD DATA ",
          commented ? "# " : "");
2462 2463
  if (check_fname_outside_temp_buf())
    fprintf(file, "LOCAL ");
2464
  fprintf(file, "INFILE '%-*s' ", fname_len, fname);
unknown's avatar
unknown committed
2465

unknown's avatar
unknown committed
2466
  if (sql_ex.opt_flags & REPLACE_FLAG)
unknown's avatar
unknown committed
2467
    fprintf(file," REPLACE ");
unknown's avatar
unknown committed
2468
  else if (sql_ex.opt_flags & IGNORE_FLAG)
unknown's avatar
unknown committed
2469 2470
    fprintf(file," IGNORE ");
  
2471
  fprintf(file, "INTO TABLE `%s`", table_name);
unknown's avatar
unknown committed
2472 2473
  fprintf(file, " FIELDS TERMINATED BY ");
  pretty_print_str(file, sql_ex.field_term, sql_ex.field_term_len);
unknown's avatar
unknown committed
2474

unknown's avatar
unknown committed
2475 2476 2477 2478
  if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
    fprintf(file," OPTIONALLY ");
  fprintf(file, " ENCLOSED BY ");
  pretty_print_str(file, sql_ex.enclosed, sql_ex.enclosed_len);
unknown's avatar
unknown committed
2479
     
unknown's avatar
unknown committed
2480 2481
  fprintf(file, " ESCAPED BY ");
  pretty_print_str(file, sql_ex.escaped, sql_ex.escaped_len);
unknown's avatar
unknown committed
2482
     
unknown's avatar
unknown committed
2483 2484 2485
  fprintf(file," LINES TERMINATED BY ");
  pretty_print_str(file, sql_ex.line_term, sql_ex.line_term_len);

unknown's avatar
unknown committed
2486

2487
  if (sql_ex.line_start)
unknown's avatar
unknown committed
2488
  {
2489
    fprintf(file," STARTING BY ");
2490
    pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len);
unknown's avatar
unknown committed
2491
  }
2492 2493
  if ((long) skip_lines > 0)
    fprintf(file, " IGNORE %ld LINES", (long) skip_lines);
unknown's avatar
unknown committed
2494

2495 2496 2497 2498
  if (num_fields)
  {
    uint i;
    const char* field = fields;
2499 2500
    fprintf(file, " (");
    for (i = 0; i < num_fields; i++)
unknown's avatar
unknown committed
2501
    {
unknown's avatar
unknown committed
2502
      if (i)
2503 2504
	fputc(',', file);
      fprintf(file, field);
unknown's avatar
unknown committed
2505
	  
2506
      field += field_lens[i]  + 1;
unknown's avatar
unknown committed
2507
    }
2508 2509
    fputc(')', file);
  }
unknown's avatar
unknown committed
2510 2511

  fprintf(file, ";\n");
unknown's avatar
unknown committed
2512
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2513
}
unknown's avatar
unknown committed
2514
#endif /* MYSQL_CLIENT */
2515

2516

unknown's avatar
unknown committed
2517
/*
2518
  Load_log_event::set_fields()
unknown's avatar
unknown committed
2519 2520 2521 2522 2523

  Note that this function can not use the member variable 
  for the database, since LOAD DATA INFILE on the slave
  can be for a different database than the current one.
  This is the reason for the affected_db argument to this method.
unknown's avatar
unknown committed
2524
*/
2525

2526
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
2527 2528
void Load_log_event::set_fields(const char* affected_db, 
				List<Item> &field_list)
unknown's avatar
unknown committed
2529 2530
{
  uint i;
unknown's avatar
unknown committed
2531
  const char* field = fields;
2532
  for (i= 0; i < num_fields; i++)
unknown's avatar
unknown committed
2533
  {
unknown's avatar
unknown committed
2534
    field_list.push_back(new Item_field(affected_db, table_name, field));
2535
    field+= field_lens[i]  + 1;
unknown's avatar
unknown committed
2536
  }
unknown's avatar
unknown committed
2537
}
unknown's avatar
unknown committed
2538
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
2539 2540


unknown's avatar
SCRUM  
unknown committed
2541
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
2542 2543
/*
  Does the data loading job when executing a LOAD DATA on the slave
2544

unknown's avatar
unknown committed
2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558
  SYNOPSIS
    Load_log_event::exec_event
      net  
      rli                             
      use_rli_only_for_errors	  - if set to 1, rli is provided to 
                                  Load_log_event::exec_event only for this 
				  function to have RPL_LOG_NAME and 
				  rli->last_slave_error, both being used by 
				  error reports. rli's position advancing
				  is skipped (done by the caller which is
				  Execute_load_log_event::exec_event).
				  - if set to 0, rli is provided for full use,
				  i.e. for error reports and position
				  advancing.
2559

unknown's avatar
unknown committed
2560 2561 2562 2563 2564 2565 2566
  DESCRIPTION
    Does the data loading job when executing a LOAD DATA on the slave
 
  RETURN VALUE
    0           Success                                                 
    1    	Failure
*/
2567

unknown's avatar
unknown committed
2568 2569
int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, 
			       bool use_rli_only_for_errors)
2570
{
2571 2572
  thd->db_length= db_len;
  thd->db= (char*) rewrite_db(db, &thd->db_length);
2573
  DBUG_ASSERT(thd->query == 0);
unknown's avatar
unknown committed
2574
  thd->query_length= 0;                         // Should not be needed
unknown's avatar
unknown committed
2575
  thd->query_error= 0;
unknown's avatar
unknown committed
2576
  clear_all_errors(thd, rli);
2577 2578 2579 2580
  /*
    Usually mysql_init_query() is called by mysql_parse(), but we need it here
    as the present method does not call mysql_parse().
  */
unknown's avatar
unknown committed
2581
  mysql_init_query(thd, 0, 0);
unknown's avatar
unknown committed
2582
  if (!use_rli_only_for_errors)
2583
  {
2584
    /* Saved for InnoDB, see comment in Query_log_event::exec_event() */
unknown's avatar
unknown committed
2585
    rli->future_group_master_log_pos= log_pos;
2586
    DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
2587
  }
2588 2589
 
   /*
unknown's avatar
unknown committed
2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600
    We test replicate_*_db rules. Note that we have already prepared the file
    to load, even if we are going to ignore and delete it now. So it is
    possible that we did a lot of disk writes for nothing. In other words, a
    big LOAD DATA INFILE on the master will still consume a lot of space on
    the slave (space in the relay log + space of temp files: twice the space
    of the file to load...) even if it will finally be ignored.
    TODO: fix this; this can be done by testing rules in
    Create_file_log_event::exec_event() and then discarding Append_block and
    al. Another way is do the filtering in the I/O thread (more efficient: no
    disk writes at all).
  */
2601
  if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
2602
  {
2603 2604
    thd->set_time((time_t)when);
    VOID(pthread_mutex_lock(&LOCK_thread_count));
2605
    thd->query_id = next_query_id();
2606
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
2607 2608 2609 2610 2611 2612 2613
    /*
      Initing thd->row_count is not necessary in theory as this variable has no
      influence in the case of the slave SQL thread (it is used to generate a
      "data truncated" warning but which is absorbed and never gets to the
      error log); still we init it to avoid a Valgrind message.
    */
    mysql_reset_errors(thd);
2614 2615 2616 2617

    TABLE_LIST tables;
    bzero((char*) &tables,sizeof(tables));
    tables.db = thd->db;
2618
    tables.alias = tables.table_name = (char*) table_name;
2619
    tables.lock_type = TL_WRITE;
unknown's avatar
unknown committed
2620
    tables.updating= 1;
unknown's avatar
unknown committed
2621

2622 2623 2624 2625 2626 2627 2628 2629 2630 2631
    // the table will be opened in mysql_load    
    if (table_rules_on && !tables_ok(thd, &tables))
    {
      // TODO: this is a bug - this needs to be moved to the I/O thread
      if (net)
        skip_load_data_infile(net);
    }
    else
    {
      char llbuff[22];
unknown's avatar
unknown committed
2632
      char *end;
unknown's avatar
unknown committed
2633
      enum enum_duplicates handle_dup;
2634
      bool ignore= 0;
unknown's avatar
unknown committed
2635 2636
      char *load_data_query;

unknown's avatar
unknown committed
2637
      /*
unknown's avatar
unknown committed
2638 2639
        Forge LOAD DATA INFILE query which will be used in SHOW PROCESS LIST
        and written to slave's binlog if binlogging is on.
unknown's avatar
unknown committed
2640
      */
unknown's avatar
unknown committed
2641
      if (!(load_data_query= (char *)thd->alloc(get_query_buffer_length() + 1)))
unknown's avatar
unknown committed
2642
      {
unknown's avatar
unknown committed
2643 2644 2645 2646 2647
        /*
          This will set thd->fatal_error in case of OOM. So we surely will notice
          that something is wrong.
        */
        goto error;
unknown's avatar
unknown committed
2648
      }
unknown's avatar
unknown committed
2649 2650 2651 2652 2653 2654 2655

      print_query(FALSE, load_data_query, &end, (char **)&thd->lex->fname_start,
                  (char **)&thd->lex->fname_end);
      *end= 0;
      thd->query_length= end - load_data_query;
      thd->query= load_data_query;

unknown's avatar
unknown committed
2656 2657
      if (sql_ex.opt_flags & REPLACE_FLAG)
	handle_dup= DUP_REPLACE;
unknown's avatar
unknown committed
2658
      else if (sql_ex.opt_flags & IGNORE_FLAG)
2659 2660 2661 2662
      {
        ignore= 1;
        handle_dup= DUP_ERROR;
      }
unknown's avatar
unknown committed
2663
      else
unknown's avatar
unknown committed
2664
      {
unknown's avatar
unknown committed
2665
        /*
unknown's avatar
unknown committed
2666
	  When replication is running fine, if it was DUP_ERROR on the
2667
          master then we could choose IGNORE here, because if DUP_ERROR
unknown's avatar
unknown committed
2668
          suceeded on master, and data is identical on the master and slave,
2669
          then there should be no uniqueness errors on slave, so IGNORE is
unknown's avatar
unknown committed
2670
          the same as DUP_ERROR. But in the unlikely case of uniqueness errors
unknown's avatar
unknown committed
2671 2672 2673
          (because the data on the master and slave happen to be different
	  (user error or bug), we want LOAD DATA to print an error message on
	  the slave to discover the problem.
unknown's avatar
unknown committed
2674 2675

          If reading from net (a 3.23 master), mysql_load() will change this
2676
          to IGNORE.
unknown's avatar
unknown committed
2677 2678
        */
        handle_dup= DUP_ERROR;
unknown's avatar
unknown committed
2679
      }
unknown's avatar
unknown committed
2680

unknown's avatar
unknown committed
2681
      sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
2682 2683 2684 2685 2686
      String field_term(sql_ex.field_term,sql_ex.field_term_len,log_cs);
      String enclosed(sql_ex.enclosed,sql_ex.enclosed_len,log_cs);
      String line_term(sql_ex.line_term,sql_ex.line_term_len,log_cs);
      String line_start(sql_ex.line_start,sql_ex.line_start_len,log_cs);
      String escaped(sql_ex.escaped,sql_ex.escaped_len, log_cs);
unknown's avatar
unknown committed
2687 2688 2689 2690 2691
      ex.field_term= &field_term;
      ex.enclosed= &enclosed;
      ex.line_term= &line_term;
      ex.line_start= &line_start;
      ex.escaped= &escaped;
2692 2693 2694 2695 2696 2697

      ex.opt_enclosed = (sql_ex.opt_flags & OPT_ENCLOSED_FLAG);
      if (sql_ex.empty_flags & FIELD_TERM_EMPTY)
	ex.field_term->length(0);

      ex.skip_lines = skip_lines;
unknown's avatar
unknown committed
2698
      List<Item> field_list;
unknown's avatar
unknown committed
2699
      set_fields(thd->db,field_list);
unknown's avatar
unknown committed
2700
      thd->variables.pseudo_thread_id= thread_id;
unknown's avatar
unknown committed
2701
      List<Item> set_fields;
2702 2703 2704 2705 2706 2707 2708 2709 2710
      if (net)
      {
	// mysql_load will use thd->net to read the file
	thd->net.vio = net->vio;
	/*
	  Make sure the client does not get confused about the packet sequence
	*/
	thd->net.pkt_nr = net->pkt_nr;
      }
unknown's avatar
unknown committed
2711 2712 2713 2714 2715 2716 2717
      /*
        It is safe to use set_fields twice because we are not going to
        update it inside mysql_load().
      */
      if (mysql_load(thd, &ex, &tables, field_list, set_fields, set_fields,
                     handle_dup, ignore, net != 0, TL_WRITE))
        thd->query_error= 1;
2718
      if (thd->cuted_fields)
unknown's avatar
unknown committed
2719
      {
unknown's avatar
unknown committed
2720
	/* log_pos is the position of the LOAD event in the master log */
unknown's avatar
unknown committed
2721 2722 2723 2724 2725 2726 2727
        sql_print_warning("Slave: load data infile on table '%s' at "
                          "log position %s in log '%s' produced %ld "
                          "warning(s). Default database: '%s'",
                          (char*) table_name,
                          llstr(log_pos,llbuff), RPL_LOG_NAME, 
                          (ulong) thd->cuted_fields,
                          print_slave_db_safe(thd->db));
unknown's avatar
unknown committed
2728
      }
2729 2730 2731
      if (net)
        net->pkt_nr= thd->net.pkt_nr;
    }
2732 2733
  }
  else
2734 2735 2736 2737 2738 2739 2740 2741 2742
  {
    /*
      We will just ask the master to send us /dev/null if we do not
      want to load the data.
      TODO: this a bug - needs to be done in I/O thread
    */
    if (net)
      skip_load_data_infile(net);
  }
unknown's avatar
unknown committed
2743 2744

error:
2745
  thd->net.vio = 0; 
2746
  char *save_db= thd->db;
unknown's avatar
unknown committed
2747
  VOID(pthread_mutex_lock(&LOCK_thread_count));
2748
  thd->db= thd->catalog= 0;
unknown's avatar
unknown committed
2749
  thd->query= 0;
unknown's avatar
unknown committed
2750
  thd->query_length= thd->db_length= 0;
unknown's avatar
unknown committed
2751
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
2752 2753 2754
  close_thread_tables(thd);
  if (thd->query_error)
  {
2755
    /* this err/sql_errno code is copy-paste from net_send_error() */
unknown's avatar
unknown committed
2756 2757 2758 2759 2760 2761 2762 2763 2764
    const char *err;
    int sql_errno;
    if ((err=thd->net.last_error)[0])
      sql_errno=thd->net.last_errno;
    else
    {
      sql_errno=ER_UNKNOWN_ERROR;
      err=ER(sql_errno);       
    }
unknown's avatar
unknown committed
2765
    slave_print_error(rli,sql_errno,"\
unknown's avatar
unknown committed
2766
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
2767
		      err, (char*)table_name, print_slave_db_safe(save_db));
unknown's avatar
unknown committed
2768
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
2769 2770
    return 1;
  }
unknown's avatar
unknown committed
2771
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
2772
	    
2773
  if (thd->is_fatal_error)
2774
  {
unknown's avatar
unknown committed
2775 2776
    slave_print_error(rli,ER_UNKNOWN_ERROR, "\
Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
2777
		      (char*)table_name, print_slave_db_safe(save_db));
2778 2779 2780
    return 1;
  }

unknown's avatar
unknown committed
2781
  return ( use_rli_only_for_errors ? 0 : Log_event::exec_event(rli) ); 
2782
}
unknown's avatar
SCRUM  
unknown committed
2783
#endif
2784 2785


unknown's avatar
unknown committed
2786
/**************************************************************************
unknown's avatar
unknown committed
2787
  Rotate_log_event methods
unknown's avatar
unknown committed
2788
**************************************************************************/
2789

unknown's avatar
unknown committed
2790
/*
2791
  Rotate_log_event::pack_info()
unknown's avatar
unknown committed
2792
*/
2793

unknown's avatar
SCRUM  
unknown committed
2794
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2795
void Rotate_log_event::pack_info(Protocol *protocol)
2796
{
unknown's avatar
unknown committed
2797
  char buf1[256], buf[22];
unknown's avatar
unknown committed
2798
  String tmp(buf1, sizeof(buf1), log_cs);
2799
  tmp.length(0);
unknown's avatar
unknown committed
2800 2801 2802 2803
  tmp.append(new_log_ident, ident_len);
  tmp.append(";pos=");
  tmp.append(llstr(pos,buf));
  protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
2804
}
unknown's avatar
SCRUM  
unknown committed
2805
#endif
2806

2807

unknown's avatar
unknown committed
2808
/*
2809
  Rotate_log_event::print()
unknown's avatar
unknown committed
2810
*/
2811 2812

#ifdef MYSQL_CLIENT
2813
void Rotate_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
2814
{
2815
  char buf[22];
2816

unknown's avatar
unknown committed
2817
  if (short_form)
2818 2819
    return;
  print_header(file);
2820 2821 2822
  fprintf(file, "\tRotate to ");
  if (new_log_ident)
    my_fwrite(file, (byte*) new_log_ident, (uint)ident_len, 
2823
              MYF(MY_NABP | MY_WME));
2824
  fprintf(file, "  pos: %s", llstr(pos, buf));
2825
  fputc('\n', file);
2826
  fflush(file);
2827
}
unknown's avatar
unknown committed
2828
#endif /* MYSQL_CLIENT */
2829 2830


unknown's avatar
unknown committed
2831
/*
2832
  Rotate_log_event::Rotate_log_event()
unknown's avatar
unknown committed
2833
*/
2834

2835 2836 2837
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
                                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event) ,new_log_ident(NULL),alloced(0)
2838
{
2839
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
2840
  // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
2841 2842
  uint8 header_size= description_event->common_header_len;
  uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
2843 2844
  uint ident_offset;
  if (event_len < header_size)
unknown's avatar
unknown committed
2845
    DBUG_VOID_RETURN;
2846
  buf += header_size;
2847 2848 2849 2850
  pos = post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
  ident_len = (uint)(event_len -
                     (header_size+post_header_len)); 
  ident_offset = post_header_len; 
2851 2852 2853 2854 2855
  set_if_smaller(ident_len,FN_REFLEN-1);
  if (!(new_log_ident= my_strdup_with_length((byte*) buf +
					     ident_offset,
					     (uint) ident_len,
					     MYF(MY_WME))))
unknown's avatar
unknown committed
2856
    DBUG_VOID_RETURN;
2857
  alloced = 1;
unknown's avatar
unknown committed
2858
  DBUG_VOID_RETURN;
2859
}
2860 2861


unknown's avatar
unknown committed
2862
/*
2863
  Rotate_log_event::write()
unknown's avatar
unknown committed
2864
*/
2865

2866
bool Rotate_log_event::write(IO_CACHE* file)
2867
{
2868
  char buf[ROTATE_HEADER_LEN];
unknown's avatar
unknown committed
2869
  int8store(buf + R_POS_OFFSET, pos);
2870 2871 2872
  return (write_header(file, ROTATE_HEADER_LEN + ident_len) ||
          my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) ||
          my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len));
2873 2874
}

2875

unknown's avatar
unknown committed
2876
/*
2877 2878
  Rotate_log_event::exec_event()

2879
  Got a rotate log event from the master
2880

2881 2882 2883
  IMPLEMENTATION
    This is mainly used so that we can later figure out the logname and
    position for the master.
2884

2885
    We can't rotate the slave's BINlog as this will cause infinitive rotations
2886
    in a A -> B -> A setup.
2887
    The NOTES below is a wrong comment which will disappear when 4.1 is merged.
2888 2889 2890

  RETURN VALUES
    0	ok
unknown's avatar
unknown committed
2891
*/
2892

unknown's avatar
SCRUM  
unknown committed
2893
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2894
int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
2895
{
2896 2897 2898
  DBUG_ENTER("Rotate_log_event::exec_event");

  pthread_mutex_lock(&rli->data_lock);
2899
  rli->event_relay_log_pos= my_b_tell(rli->cur_log);
unknown's avatar
unknown committed
2900 2901 2902 2903 2904 2905 2906 2907 2908
  /*
    If we are in a transaction: the only normal case is when the I/O thread was
    copying a big transaction, then it was stopped and restarted: we have this
    in the relay log:
    BEGIN
    ...
    ROTATE (a fake one)
    ...
    COMMIT or ROLLBACK
unknown's avatar
unknown committed
2909 2910
    In that case, we don't want to touch the coordinates which correspond to
    the beginning of the transaction.
2911 2912
    Starting from 5.0.0, there also are some rotates from the slave itself, in
    the relay log.
unknown's avatar
unknown committed
2913
  */
unknown's avatar
unknown committed
2914
  if (!(thd->options & OPTION_BEGIN))
unknown's avatar
unknown committed
2915
  {
unknown's avatar
unknown committed
2916 2917
    memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
    rli->notify_group_master_log_name_update();
2918 2919 2920 2921 2922
    rli->group_master_log_pos= pos;
    rli->group_relay_log_pos= rli->event_relay_log_pos;
    DBUG_PRINT("info", ("group_master_log_name: '%s' group_master_log_pos:\
%lu",
                        rli->group_master_log_name,
unknown's avatar
unknown committed
2923
                        (ulong) rli->group_master_log_pos));
2924
    /*
2925 2926
      Reset thd->options and sql_mode etc, because this could be the signal of
      a master's downgrade from 5.0 to 4.0.
2927 2928 2929 2930 2931 2932
      However, no need to reset description_event_for_exec: indeed, if the next
      master is 5.0 (even 5.0.1) we will soon get a Format_desc; if the next
      master is 4.0 then the events are in the slave's format (conversion).
    */
    set_slave_thread_options(thd);
    thd->variables.sql_mode= global_system_variables.sql_mode;
2933 2934 2935 2936 2937 2938 2939 2940 2941 2942
    thd->variables.auto_increment_increment=
      thd->variables.auto_increment_offset= 1;
    thd->variables.character_set_client=
      global_system_variables.character_set_client;
    thd->variables.collation_connection=
      global_system_variables.collation_connection;
    thd->variables.collation_server=
      global_system_variables.collation_server;
    thd->update_charset();
    rli->cached_charset_invalidate();
unknown's avatar
unknown committed
2943
  }
2944 2945 2946 2947
  pthread_mutex_unlock(&rli->data_lock);
  pthread_cond_broadcast(&rli->data_cond);
  flush_relay_log_info(rli);
  DBUG_RETURN(0);
2948
}
unknown's avatar
SCRUM  
unknown committed
2949
#endif
2950 2951


unknown's avatar
unknown committed
2952
/**************************************************************************
unknown's avatar
unknown committed
2953
	Intvar_log_event methods
unknown's avatar
unknown committed
2954
**************************************************************************/
2955

unknown's avatar
unknown committed
2956
/*
2957
  Intvar_log_event::pack_info()
unknown's avatar
unknown committed
2958
*/
2959

unknown's avatar
SCRUM  
unknown committed
2960
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2961
void Intvar_log_event::pack_info(Protocol *protocol)
2962
{
unknown's avatar
unknown committed
2963 2964
  char buf[256], *pos;
  pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
unknown's avatar
unknown committed
2965
  *pos++= '=';
unknown's avatar
unknown committed
2966
  pos= longlong10_to_str(val, pos, -10);
unknown's avatar
unknown committed
2967
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
2968
}
unknown's avatar
SCRUM  
unknown committed
2969
#endif
2970

unknown's avatar
unknown committed
2971

unknown's avatar
unknown committed
2972
/*
2973
  Intvar_log_event::Intvar_log_event()
unknown's avatar
unknown committed
2974
*/
2975

2976 2977 2978
Intvar_log_event::Intvar_log_event(const char* buf,
                                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
2979
{
2980 2981 2982
  buf+= description_event->common_header_len;
  type= buf[I_TYPE_OFFSET];
  val= uint8korr(buf+I_VAL_OFFSET);
2983 2984
}

2985

unknown's avatar
unknown committed
2986
/*
2987
  Intvar_log_event::get_var_type_name()
unknown's avatar
unknown committed
2988
*/
2989 2990

const char* Intvar_log_event::get_var_type_name()
2991
{
2992 2993 2994 2995 2996
  switch(type) {
  case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID";
  case INSERT_ID_EVENT: return "INSERT_ID";
  default: /* impossible */ return "UNKNOWN";
  }
2997 2998
}

unknown's avatar
unknown committed
2999

unknown's avatar
unknown committed
3000
/*
3001
  Intvar_log_event::write()
unknown's avatar
unknown committed
3002
*/
3003

3004
bool Intvar_log_event::write(IO_CACHE* file)
3005
{
3006 3007
  byte buf[9];
  buf[I_TYPE_OFFSET]= (byte) type;
3008
  int8store(buf + I_VAL_OFFSET, val);
3009 3010
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3011 3012
}

3013

unknown's avatar
unknown committed
3014
/*
3015
  Intvar_log_event::print()
unknown's avatar
unknown committed
3016
*/
3017 3018

#ifdef MYSQL_CLIENT
3019 3020
void Intvar_log_event::print(FILE* file, bool short_form,
                             LAST_EVENT_INFO* last_event_info)
3021
{
3022 3023 3024
  char llbuff[22];
  const char *msg;
  LINT_INIT(msg);
3025

3026 3027 3028 3029 3030
  if (!short_form)
  {
    print_header(file);
    fprintf(file, "\tIntvar\n");
  }
3031

3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042
  fprintf(file, "SET ");
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    msg="LAST_INSERT_ID";
    break;
  case INSERT_ID_EVENT:
    msg="INSERT_ID";
    break;
  }
  fprintf(file, "%s=%s;\n", msg, llstr(val,llbuff));
  fflush(file);
3043
}
3044
#endif
3045

3046

unknown's avatar
unknown committed
3047
/*
3048
  Intvar_log_event::exec_event()
unknown's avatar
unknown committed
3049
*/
3050

unknown's avatar
SCRUM  
unknown committed
3051
#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
3052
int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
3053
{
3054 3055 3056 3057 3058 3059 3060 3061 3062
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    thd->last_insert_id_used = 1;
    thd->last_insert_id = val;
    break;
  case INSERT_ID_EVENT:
    thd->next_insert_id = val;
    break;
  }
3063
  rli->inc_event_relay_log_pos();
3064
  return 0;
3065
}
unknown's avatar
SCRUM  
unknown committed
3066
#endif
3067

3068

unknown's avatar
unknown committed
3069
/**************************************************************************
3070
  Rand_log_event methods
unknown's avatar
unknown committed
3071
**************************************************************************/
3072

unknown's avatar
SCRUM  
unknown committed
3073
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3074
void Rand_log_event::pack_info(Protocol *protocol)
3075
{
unknown's avatar
unknown committed
3076 3077 3078 3079 3080
  char buf1[256], *pos;
  pos= strmov(buf1,"rand_seed1=");
  pos= int10_to_str((long) seed1, pos, 10);
  pos= strmov(pos, ",rand_seed2=");
  pos= int10_to_str((long) seed2, pos, 10);
3081
  protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
3082
}
unknown's avatar
SCRUM  
unknown committed
3083
#endif
3084 3085


3086 3087 3088
Rand_log_event::Rand_log_event(const char* buf,
                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3089
{
3090 3091 3092
  buf+= description_event->common_header_len;
  seed1= uint8korr(buf+RAND_SEED1_OFFSET);
  seed2= uint8korr(buf+RAND_SEED2_OFFSET);
3093 3094
}

3095

3096
bool Rand_log_event::write(IO_CACHE* file)
3097
{
3098
  byte buf[16];
3099 3100
  int8store(buf + RAND_SEED1_OFFSET, seed1);
  int8store(buf + RAND_SEED2_OFFSET, seed2);
3101 3102
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3103
}
3104

3105 3106

#ifdef MYSQL_CLIENT
3107
void Rand_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
3108
{
unknown's avatar
unknown committed
3109
  char llbuff[22],llbuff2[22];
3110
  if (!short_form)
3111
  {
3112 3113
    print_header(file);
    fprintf(file, "\tRand\n");
3114
  }
unknown's avatar
unknown committed
3115
  fprintf(file, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s;\n",
unknown's avatar
unknown committed
3116
	  llstr(seed1, llbuff),llstr(seed2, llbuff2));
3117
  fflush(file);
3118
}
unknown's avatar
unknown committed
3119
#endif /* MYSQL_CLIENT */
3120

3121

unknown's avatar
SCRUM  
unknown committed
3122
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3123
int Rand_log_event::exec_event(struct st_relay_log_info* rli)
3124
{
unknown's avatar
unknown committed
3125 3126
  thd->rand.seed1= (ulong) seed1;
  thd->rand.seed2= (ulong) seed2;
3127
  rli->inc_event_relay_log_pos();
3128 3129
  return 0;
}
unknown's avatar
unknown committed
3130
#endif /* !MYSQL_CLIENT */
3131

unknown's avatar
unknown committed
3132

3133 3134 3135 3136 3137 3138 3139
/**************************************************************************
  Xid_log_event methods
**************************************************************************/

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Xid_log_event::pack_info(Protocol *protocol)
{
3140 3141
  char buf[128], *pos;
  pos= strmov(buf, "COMMIT /* xid=");
unknown's avatar
unknown committed
3142
  pos= longlong10_to_str(xid, pos, 10);
3143
  pos= strmov(pos, " */");
3144 3145 3146 3147
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
}
#endif

unknown's avatar
unknown committed
3148 3149 3150 3151 3152 3153 3154 3155
/*
  NOTE it's ok not to use int8store here,
  as long as xid_t::set(ulonglong) and
  xid_t::get_my_xid doesn't do it either

  we don't care about actual values of xids as long as
  identical numbers compare identically
*/
3156 3157 3158 3159

Xid_log_event::
Xid_log_event(const char* buf,
              const Format_description_log_event *description_event)
3160 3161 3162
  :Log_event(buf, description_event)
{
  buf+= description_event->common_header_len;
3163
  memcpy((char*) &xid, buf, sizeof(xid));
3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178
}


bool Xid_log_event::write(IO_CACHE* file)
{
  return write_header(file, sizeof(xid)) ||
         my_b_safe_write(file, (byte*) &xid, sizeof(xid));
}


#ifdef MYSQL_CLIENT
void Xid_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
  if (!short_form)
  {
unknown's avatar
unknown committed
3179 3180 3181
    char buf[64];
    longlong10_to_str(xid, buf, 10);

3182
    print_header(file);
unknown's avatar
unknown committed
3183 3184
    fprintf(file, "\tXid = %s\n", buf);
    fflush(file);
3185
  }
3186
  fprintf(file, "COMMIT;\n");
3187 3188 3189 3190 3191 3192 3193
}
#endif /* MYSQL_CLIENT */


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Xid_log_event::exec_event(struct st_relay_log_info* rli)
{
3194
  /* For a slave Xid_log_event is COMMIT */
3195
  mysql_log.write(thd,COM_QUERY,"COMMIT /* implicit, from Xid_log_event */");
3196
  return end_trans(thd, COMMIT) || Log_event::exec_event(rli);
3197 3198 3199 3200
}
#endif /* !MYSQL_CLIENT */


unknown's avatar
unknown committed
3201
/**************************************************************************
3202
  User_var_log_event methods
unknown's avatar
unknown committed
3203
**************************************************************************/
unknown's avatar
unknown committed
3204

unknown's avatar
unknown committed
3205
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3206 3207 3208
void User_var_log_event::pack_info(Protocol* protocol)
{
  char *buf= 0;
3209
  uint val_offset= 4 + name_len;
unknown's avatar
unknown committed
3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224
  uint event_len= val_offset;

  if (is_null)
  {
    buf= my_malloc(val_offset + 5, MYF(MY_WME));
    strmov(buf + val_offset, "NULL");
    event_len= val_offset + 4;
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
      buf= my_malloc(val_offset + FLOATING_POINT_BUFFER, MYF(MY_WME));
3225 3226
      event_len+= my_sprintf(buf + val_offset,
			     (buf + val_offset, "%.14g", real_val));
unknown's avatar
unknown committed
3227 3228 3229 3230 3231
      break;
    case INT_RESULT:
      buf= my_malloc(val_offset + 22, MYF(MY_WME));
      event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
      break;
unknown's avatar
unknown committed
3232 3233 3234 3235 3236 3237 3238 3239 3240 3241
    case DECIMAL_RESULT:
    {
      buf= my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH, MYF(MY_WME));
      String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin);
      my_decimal dec;
      binary2my_decimal(E_DEC_FATAL_ERROR, val+2, &dec, val[0], val[1]);
      my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
      event_len= str.length() + val_offset;
      break;
    } 
unknown's avatar
unknown committed
3242
    case STRING_RESULT:
3243 3244 3245 3246 3247 3248 3249 3250 3251 3252
      /* 15 is for 'COLLATE' and other chars */
      buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME));
      CHARSET_INFO *cs;
      if (!(cs= get_charset(charset_number, MYF(0))))
      {
        strmov(buf+val_offset, "???");
        event_len+= 3;
      }
      else
      {
3253 3254 3255
        char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
        p= str_to_hex(p, val, val_len);
        p= strxmov(p, " COLLATE ", cs->name, NullS);
3256 3257
        event_len= p-buf;
      }
unknown's avatar
unknown committed
3258
      break;
3259
    case ROW_RESULT:
unknown's avatar
unknown committed
3260
    default:
unknown's avatar
unknown committed
3261 3262 3263 3264 3265
      DBUG_ASSERT(1);
      return;
    }
  }
  buf[0]= '@';
3266 3267 3268 3269
  buf[1]= '`';
  buf[2+name_len]= '`';
  buf[3+name_len]= '=';
  memcpy(buf+2, name, name_len);
3270
  protocol->store(buf, event_len, &my_charset_bin);
unknown's avatar
unknown committed
3271 3272
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
unknown's avatar
unknown committed
3273
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
3274 3275


3276 3277 3278 3279
User_var_log_event::
User_var_log_event(const char* buf,
                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
unknown's avatar
unknown committed
3280
{
3281
  buf+= description_event->common_header_len;
unknown's avatar
unknown committed
3282 3283
  name_len= uint4korr(buf);
  name= (char *) buf + UV_NAME_LEN_SIZE;
3284 3285
  buf+= UV_NAME_LEN_SIZE + name_len;
  is_null= (bool) *buf;
unknown's avatar
unknown committed
3286 3287 3288
  if (is_null)
  {
    type= STRING_RESULT;
3289
    charset_number= my_charset_bin.number;
unknown's avatar
unknown committed
3290 3291 3292 3293 3294
    val_len= 0;
    val= 0;  
  }
  else
  {
3295 3296 3297
    type= (Item_result) buf[UV_VAL_IS_NULL];
    charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE);
    val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + 
unknown's avatar
unknown committed
3298
		       UV_CHARSET_NUMBER_SIZE);
3299 3300
    val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
		   UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
unknown's avatar
unknown committed
3301 3302 3303 3304
  }
}


3305
bool User_var_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
3306 3307 3308 3309
{
  char buf[UV_NAME_LEN_SIZE];
  char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + 
	    UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
unknown's avatar
unknown committed
3310
  char buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
3311
  uint buf1_length;
3312
  ulong event_length;
3313

unknown's avatar
unknown committed
3314
  int4store(buf, name_len);
3315 3316 3317 3318 3319 3320 3321
  
  if ((buf1[0]= is_null))
  {
    buf1_length= 1;
    val_len= 0;
  }    
  else
unknown's avatar
unknown committed
3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332
  {
    buf1[1]= type;
    int4store(buf1 + 2, charset_number);

    switch (type) {
    case REAL_RESULT:
      float8store(buf2, *(double*) val);
      break;
    case INT_RESULT:
      int8store(buf2, *(longlong*) val);
      break;
unknown's avatar
unknown committed
3333 3334 3335 3336 3337 3338 3339 3340 3341 3342
    case DECIMAL_RESULT:
    {
      my_decimal *dec= (my_decimal *)val;
      dec->fix_buffer_pointer();
      buf2[0]= (char)(dec->intg + dec->frac);
      buf2[1]= (char)dec->frac;
      decimal2bin((decimal*)val, buf2+2, buf2[0], buf2[1]);
      val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
      break;
    }
unknown's avatar
unknown committed
3343 3344 3345
    case STRING_RESULT:
      pos= val;
      break;
3346
    case ROW_RESULT:
unknown's avatar
unknown committed
3347
    default:
unknown's avatar
unknown committed
3348 3349 3350
      DBUG_ASSERT(1);
      return 0;
    }
unknown's avatar
unknown committed
3351 3352
    int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
    buf1_length= 10;
unknown's avatar
unknown committed
3353
  }
3354 3355 3356 3357 3358 3359

  /* Length of the whole event */
  event_length= sizeof(buf)+ name_len + buf1_length + val_len;

  return (write_header(file, event_length) ||
          my_b_safe_write(file, (byte*) buf, sizeof(buf))   ||
3360 3361 3362
	  my_b_safe_write(file, (byte*) name, name_len)     ||
	  my_b_safe_write(file, (byte*) buf1, buf1_length) ||
	  my_b_safe_write(file, (byte*) pos, val_len));
unknown's avatar
unknown committed
3363 3364
}

3365

unknown's avatar
unknown committed
3366
/*
unknown's avatar
unknown committed
3367
  User_var_log_event::print()
unknown's avatar
unknown committed
3368
*/
unknown's avatar
unknown committed
3369 3370

#ifdef MYSQL_CLIENT
3371
void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
unknown's avatar
unknown committed
3372 3373 3374 3375 3376 3377 3378
{
  if (!short_form)
  {
    print_header(file);
    fprintf(file, "\tUser_var\n");
  }

3379
  fprintf(file, "SET @`");
unknown's avatar
unknown committed
3380
  my_fwrite(file, (byte*) name, (uint) (name_len), MYF(MY_NABP | MY_WME));
3381
  fprintf(file, "`");
unknown's avatar
unknown committed
3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399

  if (is_null)
  {
    fprintf(file, ":=NULL;\n");
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
      fprintf(file, ":=%.14g;\n", real_val);
      break;
    case INT_RESULT:
      char int_buf[22];
      longlong10_to_str(uint8korr(val), int_buf, -10);
      fprintf(file, ":=%s;\n", int_buf);
      break;
unknown's avatar
unknown committed
3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413
    case DECIMAL_RESULT:
    {
      char str_buf[200];
      int str_len= sizeof(str_buf) - 1;
      int precision= (int)val[0];
      int scale= (int)val[1];
      decimal_digit dec_buf[10];
      decimal dec;
      dec.len= 10;
      dec.buf= dec_buf;

      bin2decimal(val+2, &dec, precision, scale);
      decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
      str_buf[str_len]= 0;
3414
      fprintf(file, ":=%s;\n",str_buf);
unknown's avatar
unknown committed
3415 3416
      break;
    }
unknown's avatar
unknown committed
3417
    case STRING_RESULT:
3418
    {
3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432
      /*
        Let's express the string in hex. That's the most robust way. If we
        print it in character form instead, we need to escape it with
        character_set_client which we don't know (we will know it in 5.0, but
        in 4.1 we don't know it easily when we are printing
        User_var_log_event). Explanation why we would need to bother with
        character_set_client (quoting Bar):
        > Note, the parser doesn't switch to another unescaping mode after
        > it has met a character set introducer.
        > For example, if an SJIS client says something like:
        > SET @a= _ucs2 \0a\0b'
        > the string constant is still unescaped according to SJIS, not
        > according to UCS2.
      */
3433 3434 3435 3436
      char *hex_str;
      CHARSET_INFO *cs;

      if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte
3437
        break; // no error, as we are 'void'
3438
      str_to_hex(hex_str, val, val_len);
3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451
      /*
        For proper behaviour when mysqlbinlog|mysql, we need to explicitely
        specify the variable's collation. It will however cause problems when
        people want to mysqlbinlog|mysql into another server not supporting the
        character set. But there's not much to do about this and it's unlikely.
      */
      if (!(cs= get_charset(charset_number, MYF(0))))
        /*
          Generate an unusable command (=> syntax error) is probably the best
          thing we can do here.
        */
        fprintf(file, ":=???;\n");
      else
3452
        fprintf(file, ":=_%s %s COLLATE `%s`;\n", cs->csname, hex_str, cs->name);
3453
      my_afree(hex_str);
3454
    }
unknown's avatar
unknown committed
3455
      break;
3456
    case ROW_RESULT:
unknown's avatar
unknown committed
3457
    default:
unknown's avatar
unknown committed
3458
      DBUG_ASSERT(1);
unknown's avatar
unknown committed
3459 3460 3461 3462 3463
      return;
    }
  }
  fflush(file);
}
unknown's avatar
SCRUM  
unknown committed
3464
#endif
3465

unknown's avatar
unknown committed
3466

unknown's avatar
unknown committed
3467
/*
unknown's avatar
unknown committed
3468
  User_var_log_event::exec_event()
unknown's avatar
unknown committed
3469
*/
unknown's avatar
unknown committed
3470

unknown's avatar
unknown committed
3471
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3472 3473 3474
int User_var_log_event::exec_event(struct st_relay_log_info* rli)
{
  Item *it= 0;
3475 3476 3477
  CHARSET_INFO *charset;
  if (!(charset= get_charset(charset_number, MYF(MY_WME))))
    return 1;
unknown's avatar
unknown committed
3478 3479 3480
  LEX_STRING user_var_name;
  user_var_name.str= name;
  user_var_name.length= name_len;
3481 3482
  double real_val;
  longlong int_val;
unknown's avatar
unknown committed
3483 3484 3485 3486 3487 3488 3489 3490 3491 3492

  if (is_null)
  {
    it= new Item_null();
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      float8get(real_val, val);
unknown's avatar
unknown committed
3493
      it= new Item_float(real_val);
3494
      val= (char*) &real_val;		// Pointer to value in native format
3495
      val_len= 8;
unknown's avatar
unknown committed
3496 3497
      break;
    case INT_RESULT:
3498 3499 3500
      int_val= (longlong) uint8korr(val);
      it= new Item_int(int_val);
      val= (char*) &int_val;		// Pointer to value in native format
3501
      val_len= 8;
unknown's avatar
unknown committed
3502
      break;
unknown's avatar
unknown committed
3503 3504 3505 3506 3507 3508 3509 3510
    case DECIMAL_RESULT:
    {
      Item_decimal *dec= new Item_decimal(val+2, val[0], val[1]);
      it= dec;
      val= (char *)dec->val_decimal(NULL);
      val_len= sizeof(my_decimal);
      break;
    }
unknown's avatar
unknown committed
3511 3512 3513
    case STRING_RESULT:
      it= new Item_string(val, val_len, charset);
      break;
3514
    case ROW_RESULT:
unknown's avatar
unknown committed
3515
    default:
unknown's avatar
unknown committed
3516 3517 3518 3519 3520
      DBUG_ASSERT(1);
      return 0;
    }
  }
  Item_func_set_user_var e(user_var_name, it);
3521 3522 3523 3524
  /*
    Item_func_set_user_var can't substitute something else on its place =>
    0 can be passed as last argument (reference on item)
  */
unknown's avatar
unknown committed
3525
  e.fix_fields(thd, 0, 0);
3526 3527 3528 3529 3530
  /*
    A variable can just be considered as a table with
    a single record and with a single column. Thus, like
    a column value, it could always have IMPLICIT derivation.
   */
3531
  e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT);
unknown's avatar
unknown committed
3532
  free_root(thd->mem_root,0);
unknown's avatar
unknown committed
3533

3534
  rli->inc_event_relay_log_pos();
unknown's avatar
unknown committed
3535 3536
  return 0;
}
unknown's avatar
unknown committed
3537
#endif /* !MYSQL_CLIENT */
3538 3539


unknown's avatar
unknown committed
3540
/**************************************************************************
3541
  Slave_log_event methods
unknown's avatar
unknown committed
3542
**************************************************************************/
unknown's avatar
unknown committed
3543

unknown's avatar
SCRUM  
unknown committed
3544
#ifdef HAVE_REPLICATION
3545
#ifdef MYSQL_CLIENT
3546
void Unknown_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
3547 3548 3549 3550 3551 3552 3553 3554
{
  if (short_form)
    return;
  print_header(file);
  fputc('\n', file);
  fprintf(file, "# %s", "Unknown event\n");
}
#endif  
3555

3556
#ifndef MYSQL_CLIENT
3557
void Slave_log_event::pack_info(Protocol *protocol)
3558
{
unknown's avatar
unknown committed
3559
  char buf[256+HOSTNAME_LENGTH], *pos;
3560 3561 3562 3563 3564 3565 3566
  pos= strmov(buf, "host=");
  pos= strnmov(pos, master_host, HOSTNAME_LENGTH);
  pos= strmov(pos, ",port=");
  pos= int10_to_str((long) master_port, pos, 10);
  pos= strmov(pos, ",log=");
  pos= strmov(pos, master_log);
  pos= strmov(pos, ",pos=");
unknown's avatar
unknown committed
3567
  pos= longlong10_to_str(master_pos, pos, 10);
3568
  protocol->store(buf, pos-buf, &my_charset_bin);
3569
}
unknown's avatar
unknown committed
3570
#endif /* !MYSQL_CLIENT */
3571 3572 3573 3574


#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
unknown's avatar
unknown committed
3575
				 struct st_relay_log_info* rli)
3576
  :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
3577 3578 3579 3580
{
  DBUG_ENTER("Slave_log_event");
  if (!rli->inited)				// QQ When can this happen ?
    DBUG_VOID_RETURN;
3581

3582 3583 3584 3585 3586
  MASTER_INFO* mi = rli->mi;
  // TODO: re-write this better without holding both locks at the same time
  pthread_mutex_lock(&mi->data_lock);
  pthread_mutex_lock(&rli->data_lock);
  master_host_len = strlen(mi->host);
3587
  master_log_len = strlen(rli->group_master_log_name);
3588 3589 3590
  // on OOM, just do not initialize the structure and print the error
  if ((mem_pool = (char*)my_malloc(get_data_size() + 1,
				   MYF(MY_WME))))
3591
  {
3592 3593 3594
    master_host = mem_pool + SL_MASTER_HOST_OFFSET ;
    memcpy(master_host, mi->host, master_host_len + 1);
    master_log = master_host + master_host_len + 1;
3595
    memcpy(master_log, rli->group_master_log_name, master_log_len + 1);
3596
    master_port = mi->port;
3597
    master_pos = rli->group_master_log_pos;
3598 3599
    DBUG_PRINT("info", ("master_log: %s  pos: %d", master_log,
			(ulong) master_pos));
3600
  }
3601 3602 3603 3604 3605 3606
  else
    sql_print_error("Out of memory while recording slave event");
  pthread_mutex_unlock(&rli->data_lock);
  pthread_mutex_unlock(&mi->data_lock);
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
3607
#endif /* !MYSQL_CLIENT */
3608 3609 3610 3611 3612 3613 3614 3615 3616


Slave_log_event::~Slave_log_event()
{
  my_free(mem_pool, MYF(MY_ALLOW_ZERO_PTR));
}


#ifdef MYSQL_CLIENT
3617
void Slave_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
3618 3619 3620 3621 3622 3623
{
  char llbuff[22];
  if (short_form)
    return;
  print_header(file);
  fputc('\n', file);
unknown's avatar
unknown committed
3624 3625
  fprintf(file, "\
Slave: master_host: '%s'  master_port: %d  master_log: '%s'  master_pos: %s\n",
3626 3627
	  master_host, master_port, master_log, llstr(master_pos, llbuff));
}
unknown's avatar
unknown committed
3628
#endif /* MYSQL_CLIENT */
3629 3630 3631 3632 3633 3634 3635 3636


int Slave_log_event::get_data_size()
{
  return master_host_len + master_log_len + 1 + SL_MASTER_HOST_OFFSET;
}


3637
bool Slave_log_event::write(IO_CACHE* file)
3638
{
3639
  ulong event_length= get_data_size();
3640 3641 3642
  int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
  int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
  // log and host are already there
3643 3644 3645

  return (write_header(file, event_length) ||
          my_b_safe_write(file, (byte*) mem_pool, event_length));
3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657
}


void Slave_log_event::init_from_mem_pool(int data_size)
{
  master_pos = uint8korr(mem_pool + SL_MASTER_POS_OFFSET);
  master_port = uint2korr(mem_pool + SL_MASTER_PORT_OFFSET);
  master_host = mem_pool + SL_MASTER_HOST_OFFSET;
  master_host_len = strlen(master_host);
  // safety
  master_log = master_host + master_host_len + 1;
  if (master_log > mem_pool + data_size)
3658
  {
3659 3660
    master_host = 0;
    return;
3661
  }
3662 3663
  master_log_len = strlen(master_log);
}
3664

3665

3666 3667 3668
/* This code is not used, so has not been updated to be format-tolerant */
Slave_log_event::Slave_log_event(const char* buf, uint event_len)
  :Log_event(buf,0) /*unused event*/ ,mem_pool(0),master_host(0)
3669
{
3670
  if (event_len < LOG_EVENT_HEADER_LEN)
3671
    return;
3672
  event_len -= LOG_EVENT_HEADER_LEN;
3673 3674 3675 3676 3677
  if (!(mem_pool = (char*) my_malloc(event_len + 1, MYF(MY_WME))))
    return;
  memcpy(mem_pool, buf + LOG_EVENT_HEADER_LEN, event_len);
  mem_pool[event_len] = 0;
  init_from_mem_pool(event_len);
3678 3679
}

3680

3681 3682 3683 3684 3685 3686 3687
#ifndef MYSQL_CLIENT
int Slave_log_event::exec_event(struct st_relay_log_info* rli)
{
  if (mysql_bin_log.is_open())
    mysql_bin_log.write(this);
  return Log_event::exec_event(rli);
}
unknown's avatar
unknown committed
3688
#endif /* !MYSQL_CLIENT */
3689 3690


unknown's avatar
unknown committed
3691
/**************************************************************************
unknown's avatar
unknown committed
3692
	Stop_log_event methods
unknown's avatar
unknown committed
3693
**************************************************************************/
3694

unknown's avatar
unknown committed
3695
/*
3696
  Stop_log_event::print()
3697
*/
3698 3699

#ifdef MYSQL_CLIENT
3700
void Stop_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
3701 3702 3703 3704 3705 3706 3707
{
  if (short_form)
    return;

  print_header(file);
  fprintf(file, "\tStop\n");
  fflush(file);
3708
}
unknown's avatar
unknown committed
3709
#endif /* MYSQL_CLIENT */
3710

3711

3712
/*
3713
  Stop_log_event::exec_event()
3714

3715
  The master stopped.
unknown's avatar
unknown committed
3716
  We used to clean up all temporary tables but this is useless as, as the
3717 3718
  master has shut down properly, it has written all DROP TEMPORARY TABLE
  (prepared statements' deletion is TODO only when we binlog prep stmts).
3719 3720 3721
  We used to clean up slave_load_tmpdir, but this is useless as it has been
  cleared at the end of LOAD DATA INFILE.
  So we have nothing to do here.
3722
  The place were we must do this cleaning is in Start_log_event_v3::exec_event(),
3723
  not here. Because if we come here, the master was sane.
3724 3725
*/

3726
#ifndef MYSQL_CLIENT
3727
int Stop_log_event::exec_event(struct st_relay_log_info* rli)
3728
{
unknown's avatar
unknown committed
3729 3730
  /*
    We do not want to update master_log pos because we get a rotate event
3731
    before stop, so by now group_master_log_name is set to the next log.
3732
    If we updated it, we will have incorrect master coordinates and this
unknown's avatar
unknown committed
3733
    could give false triggers in MASTER_POS_WAIT() that we have reached
3734
    the target position when in fact we have not.
unknown's avatar
unknown committed
3735
  */
3736 3737 3738 3739 3740 3741 3742
  if (thd->options & OPTION_BEGIN)
    rli->inc_event_relay_log_pos();
  else
  {
    rli->inc_group_relay_log_pos(0);
    flush_relay_log_info(rli);
  }
3743 3744
  return 0;
}
unknown's avatar
unknown committed
3745
#endif /* !MYSQL_CLIENT */
unknown's avatar
SCRUM  
unknown committed
3746
#endif /* HAVE_REPLICATION */
3747

3748

unknown's avatar
unknown committed
3749
/**************************************************************************
unknown's avatar
unknown committed
3750
	Create_file_log_event methods
unknown's avatar
unknown committed
3751
**************************************************************************/
3752 3753

/*
3754
  Create_file_log_event ctor
unknown's avatar
unknown committed
3755
*/
3756 3757

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
3758 3759 3760 3761
Create_file_log_event::
Create_file_log_event(THD* thd_arg, sql_exchange* ex,
		      const char* db_arg, const char* table_name_arg,
		      List<Item>& fields_arg, enum enum_duplicates handle_dup,
3762
                      bool ignore,
unknown's avatar
unknown committed
3763
		      char* block_arg, uint block_len_arg, bool using_trans)
3764
  :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
unknown's avatar
unknown committed
3765
		  using_trans),
3766
   fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
3767
   file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
3768
{
unknown's avatar
unknown committed
3769
  DBUG_ENTER("Create_file_log_event");
3770
  sql_ex.force_new_format();
unknown's avatar
unknown committed
3771
  DBUG_VOID_RETURN;
3772
}
unknown's avatar
unknown committed
3773
#endif /* !MYSQL_CLIENT */
3774

3775

unknown's avatar
unknown committed
3776
/*
3777
  Create_file_log_event::write_data_body()
unknown's avatar
unknown committed
3778
*/
3779

3780
bool Create_file_log_event::write_data_body(IO_CACHE* file)
3781
{
3782 3783
  bool res;
  if ((res= Load_log_event::write_data_body(file)) || fake_base)
3784 3785
    return res;
  return (my_b_safe_write(file, (byte*) "", 1) ||
3786
          my_b_safe_write(file, (byte*) block, block_len));
3787 3788
}

3789

unknown's avatar
unknown committed
3790
/*
3791
  Create_file_log_event::write_data_header()
unknown's avatar
unknown committed
3792
*/
unknown's avatar
unknown committed
3793

3794
bool Create_file_log_event::write_data_header(IO_CACHE* file)
3795
{
3796
  bool res;
3797
  byte buf[CREATE_FILE_HEADER_LEN];
3798 3799
  if ((res= Load_log_event::write_data_header(file)) || fake_base)
    return res;
3800
  int4store(buf + CF_FILE_ID_OFFSET, file_id);
3801
  return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN) != 0;
3802 3803 3804
}


unknown's avatar
unknown committed
3805
/*
3806
  Create_file_log_event::write_base()
unknown's avatar
unknown committed
3807
*/
3808

3809
bool Create_file_log_event::write_base(IO_CACHE* file)
3810
{
3811 3812 3813 3814
  bool res;
  fake_base= 1;                                 // pretend we are Load event
  res= write(file);
  fake_base= 0;
3815 3816 3817 3818
  return res;
}


unknown's avatar
unknown committed
3819
/*
3820
  Create_file_log_event ctor
unknown's avatar
unknown committed
3821
*/
3822

3823 3824 3825
Create_file_log_event::Create_file_log_event(const char* buf, uint len,
                                             const Format_description_log_event* description_event)
  :Load_log_event(buf,0,description_event),fake_base(0),block(0),inited_from_old(0)
3826
{
3827 3828 3829 3830 3831
  DBUG_ENTER("Create_file_log_event::Create_file_log_event(char*,...)");
  uint block_offset;
  uint header_len= description_event->common_header_len;
  uint8 load_header_len= description_event->post_header_len[LOAD_EVENT-1];
  uint8 create_file_header_len= description_event->post_header_len[CREATE_FILE_EVENT-1];
unknown's avatar
unknown committed
3832
  if (!(event_buf= my_memdup((byte*) buf, len, MYF(MY_WME))) ||
3833 3834 3835 3836 3837 3838 3839
      copy_log_event(event_buf,len,
                     ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
                      load_header_len + header_len :
                      (fake_base ? (header_len+load_header_len) :
                       (header_len+load_header_len) +
                       create_file_header_len)),
                     description_event))
unknown's avatar
unknown committed
3840
    DBUG_VOID_RETURN;
3841
  if (description_event->binlog_version!=1)
3842
  {
3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859
    file_id= uint4korr(buf + 
                       header_len +
		       load_header_len + CF_FILE_ID_OFFSET);
    /*
      Note that it's ok to use get_data_size() below, because it is computed
      with values we have already read from this event (because we called
      copy_log_event()); we are not using slave's format info to decode
      master's format, we are really using master's format info.
      Anyway, both formats should be identical (except the common_header_len)
      as these Load events are not changed between 4.0 and 5.0 (as logging of
      LOAD DATA INFILE does not use Load_log_event in 5.0).

      The + 1 is for \0 terminating fname  
    */
    block_offset= (description_event->common_header_len +
                   Load_log_event::get_data_size() +
                   create_file_header_len + 1);
3860 3861 3862 3863 3864 3865 3866 3867 3868
    if (len < block_offset)
      return;
    block = (char*)buf + block_offset;
    block_len = len - block_offset;
  }
  else
  {
    sql_ex.force_new_format();
    inited_from_old = 1;
3869
  }
unknown's avatar
unknown committed
3870
  DBUG_VOID_RETURN;
3871 3872
}

3873

unknown's avatar
unknown committed
3874
/*
3875
  Create_file_log_event::print()
unknown's avatar
unknown committed
3876
*/
3877 3878

#ifdef MYSQL_CLIENT
3879
void Create_file_log_event::print(FILE* file, bool short_form, 
3880
				  LAST_EVENT_INFO* last_event_info, bool enable_local)
unknown's avatar
unknown committed
3881
{
3882
  if (short_form)
3883 3884
  {
    if (enable_local && check_fname_outside_temp_buf())
3885
      Load_log_event::print(file, 1, last_event_info);
3886
    return;
3887 3888 3889 3890
  }

  if (enable_local)
  {
unknown's avatar
unknown committed
3891
    Load_log_event::print(file, short_form, last_event_info, !check_fname_outside_temp_buf());
unknown's avatar
unknown committed
3892 3893 3894 3895 3896
    /* 
       That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
       SHOW BINLOG EVENTS we don't.
    */
    fprintf(file, "#"); 
3897 3898
  }

3899
  fprintf(file, " file_id: %d  block_len: %d\n", file_id, block_len);
unknown's avatar
unknown committed
3900
}
3901

unknown's avatar
unknown committed
3902

3903
void Create_file_log_event::print(FILE* file, bool short_form,
3904
				  LAST_EVENT_INFO* last_event_info)
3905
{
3906
  print(file,short_form,last_event_info,0);
3907
}
unknown's avatar
unknown committed
3908
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
3909

3910

unknown's avatar
unknown committed
3911
/*
3912
  Create_file_log_event::pack_info()
unknown's avatar
unknown committed
3913
*/
3914

unknown's avatar
SCRUM  
unknown committed
3915
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3916
void Create_file_log_event::pack_info(Protocol *protocol)
3917
{
3918 3919 3920
  char buf[NAME_LEN*2 + 30 + 21*2], *pos;
  pos= strmov(buf, "db=");
  memcpy(pos, db, db_len);
unknown's avatar
unknown committed
3921
  pos= strmov(pos + db_len, ";table=");
3922
  memcpy(pos, table_name, table_name_len);
unknown's avatar
unknown committed
3923
  pos= strmov(pos + table_name_len, ";file_id=");
3924 3925 3926
  pos= int10_to_str((long) file_id, pos, 10);
  pos= strmov(pos, ";block_len=");
  pos= int10_to_str((long) block_len, pos, 10);
unknown's avatar
unknown committed
3927
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
3928
}
unknown's avatar
unknown committed
3929
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
3930 3931


unknown's avatar
unknown committed
3932
/*
3933
  Create_file_log_event::exec_event()
unknown's avatar
unknown committed
3934
*/
3935

unknown's avatar
SCRUM  
unknown committed
3936
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3937
int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
3938
{
3939
  char proc_info[17+FN_REFLEN+10], *fname_buf= proc_info+17;
unknown's avatar
unknown committed
3940
  char *p;
3941 3942 3943
  int fd = -1;
  IO_CACHE file;
  int error = 1;
unknown's avatar
unknown committed
3944

3945
  bzero((char*)&file, sizeof(file));
unknown's avatar
unknown committed
3946 3947
  p = slave_load_file_stem(fname_buf, file_id, server_id);
  strmov(p, ".info");			// strmov takes less code than memcpy
3948 3949
  strnmov(proc_info, "Making temp file ", 17); // no end 0
  thd->proc_info= proc_info;
3950 3951 3952 3953 3954
  if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC,
		    MYF(MY_WME))) < 0 ||
      init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
unknown's avatar
unknown committed
3955
    slave_print_error(rli,my_errno, "Error in Create_file event: could not open file '%s'", fname_buf);
3956 3957 3958 3959
    goto err;
  }
  
  // a trick to avoid allocating another buffer
unknown's avatar
unknown committed
3960
  strmov(p, ".data");
3961 3962 3963 3964
  fname = fname_buf;
  fname_len = (uint)(p-fname) + 5;
  if (write_base(&file))
  {
unknown's avatar
unknown committed
3965
    strmov(p, ".info"); // to have it right in the error message
unknown's avatar
unknown committed
3966 3967 3968
    slave_print_error(rli,my_errno,
		      "Error in Create_file event: could not write to file '%s'",
		      fname_buf);
3969 3970 3971 3972 3973 3974 3975 3976 3977
    goto err;
  }
  end_io_cache(&file);
  my_close(fd, MYF(0));
  
  // fname_buf now already has .data, not .info, because we did our trick
  if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC,
		    MYF(MY_WME))) < 0)
  {
unknown's avatar
unknown committed
3978
    slave_print_error(rli,my_errno, "Error in Create_file event: could not open file '%s'", fname_buf);
3979 3980
    goto err;
  }
unknown's avatar
unknown committed
3981
  if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
3982
  {
unknown's avatar
unknown committed
3983
    slave_print_error(rli,my_errno, "Error in Create_file event: write to '%s' failed", fname_buf);
3984 3985
    goto err;
  }
3986 3987
  error=0;					// Everything is ok

3988 3989 3990 3991 3992
err:
  if (error)
    end_io_cache(&file);
  if (fd >= 0)
    my_close(fd, MYF(0));
unknown's avatar
unknown committed
3993
  thd->proc_info= 0;
3994
  return error ? 1 : Log_event::exec_event(rli);
3995
}
unknown's avatar
unknown committed
3996
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
3997

3998

unknown's avatar
unknown committed
3999
/**************************************************************************
unknown's avatar
unknown committed
4000
	Append_block_log_event methods
unknown's avatar
unknown committed
4001
**************************************************************************/
4002

unknown's avatar
unknown committed
4003
/*
4004
  Append_block_log_event ctor
unknown's avatar
unknown committed
4005
*/
4006 4007

#ifndef MYSQL_CLIENT  
unknown's avatar
unknown committed
4008 4009
Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg,
					       char* block_arg,
unknown's avatar
unknown committed
4010 4011 4012
					       uint block_len_arg,
					       bool using_trans)
  :Log_event(thd_arg,0, using_trans), block(block_arg),
unknown's avatar
unknown committed
4013
   block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
4014 4015
{
}
unknown's avatar
unknown committed
4016
#endif
4017 4018


unknown's avatar
unknown committed
4019
/*
4020
  Append_block_log_event ctor
unknown's avatar
unknown committed
4021
*/
4022

4023 4024 4025
Append_block_log_event::Append_block_log_event(const char* buf, uint len,
                                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event),block(0)
4026
{
4027 4028 4029 4030 4031 4032
  DBUG_ENTER("Append_block_log_event::Append_block_log_event(char*,...)");
  uint8 common_header_len= description_event->common_header_len; 
  uint8 append_block_header_len=
    description_event->post_header_len[APPEND_BLOCK_EVENT-1];
  uint total_header_len= common_header_len+append_block_header_len;
  if (len < total_header_len)
unknown's avatar
unknown committed
4033
    DBUG_VOID_RETURN;
4034 4035 4036
  file_id= uint4korr(buf + common_header_len + AB_FILE_ID_OFFSET);
  block= (char*)buf + total_header_len;
  block_len= len - total_header_len;
unknown's avatar
unknown committed
4037
  DBUG_VOID_RETURN;
4038 4039 4040
}


unknown's avatar
unknown committed
4041
/*
4042
  Append_block_log_event::write()
unknown's avatar
unknown committed
4043
*/
4044

4045
bool Append_block_log_event::write(IO_CACHE* file)
4046 4047 4048
{
  byte buf[APPEND_BLOCK_HEADER_LEN];
  int4store(buf + AB_FILE_ID_OFFSET, file_id);
4049 4050
  return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
          my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
4051 4052 4053 4054
	  my_b_safe_write(file, (byte*) block, block_len));
}


unknown's avatar
unknown committed
4055
/*
4056
  Append_block_log_event::print()
unknown's avatar
unknown committed
4057
*/
4058 4059 4060

#ifdef MYSQL_CLIENT  
void Append_block_log_event::print(FILE* file, bool short_form,
4061
				   LAST_EVENT_INFO* last_event_info)
4062 4063 4064 4065 4066
{
  if (short_form)
    return;
  print_header(file);
  fputc('\n', file);
unknown's avatar
unknown committed
4067 4068
  fprintf(file, "#%s: file_id: %d  block_len: %d\n",
	  get_type_str(), file_id, block_len);
4069
}
unknown's avatar
unknown committed
4070
#endif /* MYSQL_CLIENT */
4071 4072


unknown's avatar
unknown committed
4073
/*
4074
  Append_block_log_event::pack_info()
unknown's avatar
unknown committed
4075
*/
4076

unknown's avatar
SCRUM  
unknown committed
4077
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4078
void Append_block_log_event::pack_info(Protocol *protocol)
4079 4080 4081 4082 4083 4084
{
  char buf[256];
  uint length;
  length= (uint) my_sprintf(buf,
			    (buf, ";file_id=%u;block_len=%u", file_id,
			     block_len));
unknown's avatar
unknown committed
4085
  protocol->store(buf, length, &my_charset_bin);
4086 4087 4088
}


unknown's avatar
unknown committed
4089 4090 4091 4092 4093 4094 4095 4096 4097
/*
  Append_block_log_event::get_open_mode()
*/

int Append_block_log_event::get_open_mode() const
{
  return O_WRONLY | O_APPEND | O_BINARY;
}

unknown's avatar
unknown committed
4098
/*
4099
  Append_block_log_event::exec_event()
unknown's avatar
unknown committed
4100
*/
4101

4102
int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
4103
{
4104
  char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
4105 4106
  char *p= slave_load_file_stem(fname, file_id, server_id);
  int fd;
4107
  int error = 1;
unknown's avatar
unknown committed
4108
  DBUG_ENTER("Append_block_log_event::exec_event");
4109

4110
  memcpy(p, ".data", 6);
4111 4112
  strnmov(proc_info, "Making temp file ", 17); // no end 0
  thd->proc_info= proc_info;
unknown's avatar
unknown committed
4113
  if ((fd = my_open(fname, get_open_mode(), MYF(MY_WME))) < 0)
4114
  {
unknown's avatar
unknown committed
4115 4116 4117
    slave_print_error(rli, my_errno,
                      "Error in %s event: could not open file '%s'",
                      get_type_str(), fname);
4118 4119
    goto err;
  }
unknown's avatar
unknown committed
4120
  if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
4121
  {
unknown's avatar
unknown committed
4122 4123 4124
    slave_print_error(rli, my_errno,
                      "Error in %s event: write to '%s' failed",
                      get_type_str(), fname);
4125 4126 4127
    goto err;
  }
  error=0;
4128

4129 4130 4131
err:
  if (fd >= 0)
    my_close(fd, MYF(0));
4132
  thd->proc_info= 0;
unknown's avatar
unknown committed
4133
  DBUG_RETURN(error ? error : Log_event::exec_event(rli));
4134
}
unknown's avatar
SCRUM  
unknown committed
4135
#endif
4136 4137


unknown's avatar
unknown committed
4138
/**************************************************************************
unknown's avatar
unknown committed
4139
	Delete_file_log_event methods
unknown's avatar
unknown committed
4140
**************************************************************************/
4141

unknown's avatar
unknown committed
4142
/*
4143
  Delete_file_log_event ctor
unknown's avatar
unknown committed
4144
*/
4145 4146

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4147 4148 4149
Delete_file_log_event::Delete_file_log_event(THD *thd_arg, const char* db_arg,
					     bool using_trans)
  :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
4150 4151
{
}
unknown's avatar
unknown committed
4152
#endif
4153

unknown's avatar
unknown committed
4154
/*
4155
  Delete_file_log_event ctor
unknown's avatar
unknown committed
4156
*/
4157

4158 4159 4160
Delete_file_log_event::Delete_file_log_event(const char* buf, uint len,
                                             const Format_description_log_event* description_event)
  :Log_event(buf, description_event),file_id(0)
4161
{
4162 4163 4164
  uint8 common_header_len= description_event->common_header_len;
  uint8 delete_file_header_len= description_event->post_header_len[DELETE_FILE_EVENT-1];
  if (len < (uint)(common_header_len + delete_file_header_len))
4165
    return;
4166
  file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
4167 4168 4169
}


unknown's avatar
unknown committed
4170
/*
4171
  Delete_file_log_event::write()
unknown's avatar
unknown committed
4172
*/
4173

4174
bool Delete_file_log_event::write(IO_CACHE* file)
4175 4176 4177
{
 byte buf[DELETE_FILE_HEADER_LEN];
 int4store(buf + DF_FILE_ID_OFFSET, file_id);
4178 4179
 return (write_header(file, sizeof(buf)) ||
         my_b_safe_write(file, buf, sizeof(buf)));
4180 4181 4182
}


unknown's avatar
unknown committed
4183
/*
4184
  Delete_file_log_event::print()
unknown's avatar
unknown committed
4185
*/
4186 4187 4188

#ifdef MYSQL_CLIENT  
void Delete_file_log_event::print(FILE* file, bool short_form,
4189
				  LAST_EVENT_INFO* last_event_info)
4190 4191 4192 4193 4194 4195 4196
{
  if (short_form)
    return;
  print_header(file);
  fputc('\n', file);
  fprintf(file, "#Delete_file: file_id=%u\n", file_id);
}
unknown's avatar
unknown committed
4197
#endif /* MYSQL_CLIENT */
4198

unknown's avatar
unknown committed
4199
/*
4200
  Delete_file_log_event::pack_info()
unknown's avatar
unknown committed
4201
*/
4202

unknown's avatar
SCRUM  
unknown committed
4203
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4204
void Delete_file_log_event::pack_info(Protocol *protocol)
4205 4206 4207 4208
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
4209
  protocol->store(buf, (int32) length, &my_charset_bin);
4210
}
unknown's avatar
SCRUM  
unknown committed
4211
#endif
4212

unknown's avatar
unknown committed
4213
/*
4214
  Delete_file_log_event::exec_event()
unknown's avatar
unknown committed
4215
*/
4216

unknown's avatar
SCRUM  
unknown committed
4217
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4218 4219 4220 4221 4222 4223 4224 4225 4226 4227
int Delete_file_log_event::exec_event(struct st_relay_log_info* rli)
{
  char fname[FN_REFLEN+10];
  char *p= slave_load_file_stem(fname, file_id, server_id);
  memcpy(p, ".data", 6);
  (void) my_delete(fname, MYF(MY_WME));
  memcpy(p, ".info", 6);
  (void) my_delete(fname, MYF(MY_WME));
  return Log_event::exec_event(rli);
}
unknown's avatar
unknown committed
4228
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4229 4230


unknown's avatar
unknown committed
4231
/**************************************************************************
unknown's avatar
unknown committed
4232
	Execute_load_log_event methods
unknown's avatar
unknown committed
4233
**************************************************************************/
4234

unknown's avatar
unknown committed
4235
/*
4236
  Execute_load_log_event ctor
unknown's avatar
unknown committed
4237
*/
4238 4239

#ifndef MYSQL_CLIENT  
unknown's avatar
unknown committed
4240 4241 4242
Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg,
					       bool using_trans)
  :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
4243 4244
{
}
unknown's avatar
unknown committed
4245
#endif
4246 4247
  

unknown's avatar
unknown committed
4248
/*
4249
  Execute_load_log_event ctor
unknown's avatar
unknown committed
4250
*/
4251

4252 4253 4254
Execute_load_log_event::Execute_load_log_event(const char* buf, uint len,
                                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event), file_id(0)
4255
{
4256 4257 4258
  uint8 common_header_len= description_event->common_header_len;
  uint8 exec_load_header_len= description_event->post_header_len[EXEC_LOAD_EVENT-1];
  if (len < (uint)(common_header_len+exec_load_header_len))
4259
    return;
4260
  file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
4261 4262 4263
}


unknown's avatar
unknown committed
4264
/*
4265
  Execute_load_log_event::write()
unknown's avatar
unknown committed
4266
*/
4267

4268
bool Execute_load_log_event::write(IO_CACHE* file)
4269 4270 4271
{
  byte buf[EXEC_LOAD_HEADER_LEN];
  int4store(buf + EL_FILE_ID_OFFSET, file_id);
4272 4273
  return (write_header(file, sizeof(buf)) || 
          my_b_safe_write(file, buf, sizeof(buf)));
4274 4275 4276
}


unknown's avatar
unknown committed
4277
/*
4278
  Execute_load_log_event::print()
unknown's avatar
unknown committed
4279
*/
4280 4281 4282

#ifdef MYSQL_CLIENT  
void Execute_load_log_event::print(FILE* file, bool short_form,
4283
				   LAST_EVENT_INFO* last_event_info)
4284 4285 4286 4287 4288 4289 4290 4291
{
  if (short_form)
    return;
  print_header(file);
  fputc('\n', file);
  fprintf(file, "#Exec_load: file_id=%d\n",
	  file_id);
}
unknown's avatar
unknown committed
4292
#endif
4293

unknown's avatar
unknown committed
4294
/*
4295
  Execute_load_log_event::pack_info()
unknown's avatar
unknown committed
4296
*/
4297

unknown's avatar
SCRUM  
unknown committed
4298
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4299
void Execute_load_log_event::pack_info(Protocol *protocol)
4300 4301 4302 4303
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
4304
  protocol->store(buf, (int32) length, &my_charset_bin);
4305 4306 4307
}


unknown's avatar
unknown committed
4308
/*
4309
  Execute_load_log_event::exec_event()
unknown's avatar
unknown committed
4310
*/
unknown's avatar
SCRUM  
unknown committed
4311

4312
int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
4313 4314
{
  char fname[FN_REFLEN+10];
4315 4316
  char *p= slave_load_file_stem(fname, file_id, server_id);
  int fd;
4317 4318 4319
  int error = 1;
  IO_CACHE file;
  Load_log_event* lev = 0;
4320

4321 4322 4323 4324 4325
  memcpy(p, ".info", 6);
  if ((fd = my_open(fname, O_RDONLY|O_BINARY, MYF(MY_WME))) < 0 ||
      init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
unknown's avatar
unknown committed
4326
    slave_print_error(rli,my_errno, "Error in Exec_load event: could not open file '%s'", fname);
4327 4328
    goto err;
  }
4329
  if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
4330 4331
                                                         (pthread_mutex_t*)0,
                                                         rli->relay_log.description_event_for_exec)) ||
4332
      lev->get_type_code() != NEW_LOAD_EVENT)
4333
  {
unknown's avatar
unknown committed
4334
    slave_print_error(rli,0, "Error in Exec_load event: file '%s' appears corrupted", fname);
4335 4336
    goto err;
  }
unknown's avatar
unknown committed
4337

4338
  lev->thd = thd;
4339 4340
  /*
    lev->exec_event should use rli only for errors
unknown's avatar
unknown committed
4341 4342 4343
    i.e. should not advance rli's position.
    lev->exec_event is the place where the table is loaded (it calls
    mysql_load()).
4344
  */
unknown's avatar
unknown committed
4345

4346
  rli->future_group_master_log_pos= log_pos;
4347
  if (lev->exec_event(0,rli,1)) 
4348
  {
4349 4350 4351 4352 4353 4354 4355 4356 4357
    /*
      We want to indicate the name of the file that could not be loaded
      (SQL_LOADxxx).
      But as we are here we are sure the error is in rli->last_slave_error and
      rli->last_slave_errno (example of error: duplicate entry for key), so we
      don't want to overwrite it with the filename.
      What we want instead is add the filename to the current error message.
    */
    char *tmp= my_strdup(rli->last_slave_error,MYF(MY_WME));
4358 4359 4360 4361 4362 4363 4364 4365
    if (tmp)
    {
      slave_print_error(rli,
			rli->last_slave_errno, /* ok to re-use error code */
			"%s. Failed executing load from '%s'", 
			tmp, fname);
      my_free(tmp,MYF(0));
    }
4366 4367
    goto err;
  }
unknown's avatar
unknown committed
4368 4369 4370 4371 4372 4373 4374 4375 4376 4377
  /*
    We have an open file descriptor to the .info file; we need to close it
    or Windows will refuse to delete the file in my_delete().
  */
  if (fd >= 0)
  {
    my_close(fd, MYF(0));
    end_io_cache(&file);
    fd= -1;
  }
4378
  (void) my_delete(fname, MYF(MY_WME));
4379
  memcpy(p, ".data", 6);
4380
  (void) my_delete(fname, MYF(MY_WME));
4381
  error = 0;
4382

4383 4384 4385
err:
  delete lev;
  if (fd >= 0)
4386
  {
4387
    my_close(fd, MYF(0));
4388 4389
    end_io_cache(&file);
  }
4390
  return error ? error : Log_event::exec_event(rli);
4391
}
unknown's avatar
SCRUM  
unknown committed
4392

unknown's avatar
unknown committed
4393
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4394 4395


unknown's avatar
unknown committed
4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605
/**************************************************************************
	Begin_load_query_log_event methods
**************************************************************************/

#ifndef MYSQL_CLIENT
Begin_load_query_log_event::
Begin_load_query_log_event(THD* thd_arg, const char* db_arg, char* block_arg,
                           uint block_len_arg, bool using_trans)
  :Append_block_log_event(thd_arg, db_arg, block_arg, block_len_arg,
                          using_trans)
{
   file_id= thd_arg->file_id= mysql_bin_log.next_file_id();
}
#endif


Begin_load_query_log_event::
Begin_load_query_log_event(const char* buf, uint len,
                           const Format_description_log_event* desc_event)
  :Append_block_log_event(buf, len, desc_event)
{
}


#if defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Begin_load_query_log_event::get_open_mode() const
{
  return O_CREAT | O_WRONLY | O_BINARY | O_TRUNC;
}
#endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */


/**************************************************************************
	Execute_load_query_log_event methods
**************************************************************************/


#ifndef MYSQL_CLIENT
Execute_load_query_log_event::
Execute_load_query_log_event(THD* thd_arg, const char* query_arg,
                     ulong query_length_arg, uint fn_pos_start_arg,
                     uint fn_pos_end_arg,
                     enum_load_dup_handling dup_handling_arg,
                     bool using_trans, bool suppress_use):
  Query_log_event(thd_arg, query_arg, query_length_arg, using_trans,
                  suppress_use),
  file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg),
  fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg)
{
}
#endif /* !MYSQL_CLIENT */


Execute_load_query_log_event::
Execute_load_query_log_event(const char* buf, uint event_len,
                             const Format_description_log_event* desc_event):
  Query_log_event(buf, event_len, desc_event, EXECUTE_LOAD_QUERY_EVENT),
  file_id(0), fn_pos_start(0), fn_pos_end(0)
{
  if (!Query_log_event::is_valid())
    return;

  buf+= desc_event->common_header_len;

  fn_pos_start= uint4korr(buf + ELQ_FN_POS_START_OFFSET);
  fn_pos_end= uint4korr(buf + ELQ_FN_POS_END_OFFSET);
  dup_handling= (enum_load_dup_handling)(*(buf + ELQ_DUP_HANDLING_OFFSET));

  if (fn_pos_start > q_len || fn_pos_end > q_len ||
      dup_handling > LOAD_DUP_REPLACE)
    return;

  file_id= uint4korr(buf + ELQ_FILE_ID_OFFSET);
}


ulong Execute_load_query_log_event::get_post_header_size_for_derived()
{
  return EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN;
}


bool
Execute_load_query_log_event::write_post_header_for_derived(IO_CACHE* file)
{
  char buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
  int4store(buf, file_id);
  int4store(buf + 4, fn_pos_start);
  int4store(buf + 4 + 4, fn_pos_end);
  *(buf + 4 + 4 + 4)= (char)dup_handling;
  return my_b_safe_write(file, (byte*) buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
}


#ifdef MYSQL_CLIENT
void Execute_load_query_log_event::print(FILE* file, bool short_form,
                                         LAST_EVENT_INFO* last_event_info)
{
  print(file, short_form, last_event_info, 0);
}


void Execute_load_query_log_event::print(FILE* file, bool short_form,
                                         LAST_EVENT_INFO* last_event_info,
                                         const char *local_fname)
{
  print_query_header(file, short_form, last_event_info);

  if (local_fname)
  {
    my_fwrite(file, (byte*) query, fn_pos_start, MYF(MY_NABP | MY_WME));
    fprintf(file, " LOCAL INFILE \'");
    fprintf(file, local_fname);
    fprintf(file, "\'");
    if (dup_handling == LOAD_DUP_REPLACE)
      fprintf(file, " REPLACE");
    fprintf(file, " INTO");
    my_fwrite(file, (byte*) query + fn_pos_end, q_len-fn_pos_end,
        MYF(MY_NABP | MY_WME));
    fprintf(file, ";\n");
  }
  else
  {
    my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
    fprintf(file, ";\n");
  }

  if (!short_form)
    fprintf(file, "# file_id: %d \n", file_id);
}
#endif


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Execute_load_query_log_event::pack_info(Protocol *protocol)
{
  char *buf, *pos;
  if (!(buf= my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME))))
    return;
  pos= buf;
  if (db && db_len)
  {
    pos= strmov(buf, "use `");
    memcpy(pos, db, db_len);
    pos= strmov(pos+db_len, "`; ");
  }
  if (query && q_len)
  {
    memcpy(pos, query, q_len);
    pos+= q_len;
  }
  pos= strmov(pos, " ;file_id=");
  pos= int10_to_str((long) file_id, pos, 10);
  protocol->store(buf, pos-buf, &my_charset_bin);
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}


int
Execute_load_query_log_event::exec_event(struct st_relay_log_info* rli)
{
  char *p;
  char *buf;
  char *fname;
  char *fname_end;
  int error;

  /* Replace filename and LOCAL keyword in query before executing it */
  if (!(buf = my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
                        (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME))))
  {
    slave_print_error(rli, my_errno, "Not enough memory");
    return 1;
  }

  p= buf;
  memcpy(p, query, fn_pos_start);
  p+= fn_pos_start;
  fname= (p= strmake(p, " INFILE \'", 9));
  p= slave_load_file_stem(p, file_id, server_id);
  fname_end= (p= strmake(p, ".data", 5));
  *(p++)='\'';
  switch (dup_handling)
  {
  case LOAD_DUP_IGNORE:
    p= strmake(p, " IGNORE", 7);
    break;
  case LOAD_DUP_REPLACE:
    p= strmake(p, " REPLACE", 8);
    break;
  default:
    /* Ordinary load data */
    break;
  }
  p= strmake(p, " INTO", 5);
  p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);

  error= Query_log_event::exec_event(rli, buf, p-buf);

  /* Forging file name for deletion in same buffer */
  *fname_end= 0;

  (void) my_delete(fname, MYF(MY_WME));

  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
  return error;
}
#endif


unknown's avatar
unknown committed
4606
/**************************************************************************
unknown's avatar
unknown committed
4607
	sql_ex_info methods
unknown's avatar
unknown committed
4608
**************************************************************************/
4609

unknown's avatar
unknown committed
4610
/*
4611
  sql_ex_info::write_data()
unknown's avatar
unknown committed
4612
*/
4613

4614
bool sql_ex_info::write_data(IO_CACHE* file)
4615 4616 4617
{
  if (new_format())
  {
4618 4619 4620 4621 4622
    return (write_str(file, field_term, (uint) field_term_len) ||
	    write_str(file, enclosed,   (uint) enclosed_len) ||
	    write_str(file, line_term,  (uint) line_term_len) ||
	    write_str(file, line_start, (uint) line_start_len) ||
	    write_str(file, escaped,    (uint) escaped_len) ||
4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634
	    my_b_safe_write(file,(byte*) &opt_flags,1));
  }
  else
  {
    old_sql_ex old_ex;
    old_ex.field_term= *field_term;
    old_ex.enclosed=   *enclosed;
    old_ex.line_term=  *line_term;
    old_ex.line_start= *line_start;
    old_ex.escaped=    *escaped;
    old_ex.opt_flags=  opt_flags;
    old_ex.empty_flags=empty_flags;
4635
    return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)) != 0;
4636 4637 4638 4639
  }
}


unknown's avatar
unknown committed
4640
/*
4641
  sql_ex_info::init()
unknown's avatar
unknown committed
4642
*/
4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656

char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format)
{
  cached_new_format = use_new_format;
  if (use_new_format)
  {
    empty_flags=0;
    /*
      The code below assumes that buf will not disappear from
      under our feet during the lifetime of the event. This assumption
      holds true in the slave thread if the log is in new format, but is not
      the case when we have old format because we will be reusing net buffer
      to read the actual file before we write out the Create_file event.
    */
4657 4658 4659 4660 4661
    if (read_str(&buf, buf_end, &field_term, &field_term_len) ||
	read_str(&buf, buf_end, &enclosed,   &enclosed_len) ||
	read_str(&buf, buf_end, &line_term,  &line_term_len) ||
	read_str(&buf, buf_end, &line_start, &line_start_len) ||
	read_str(&buf, buf_end, &escaped,    &escaped_len))
4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687
      return 0;
    opt_flags = *buf++;
  }
  else
  {
    field_term_len= enclosed_len= line_term_len= line_start_len= escaped_len=1;
    field_term = buf++;			// Use first byte in string
    enclosed=	 buf++;
    line_term=   buf++;
    line_start=  buf++;
    escaped=     buf++;
    opt_flags =  *buf++;
    empty_flags= *buf++;
    if (empty_flags & FIELD_TERM_EMPTY)
      field_term_len=0;
    if (empty_flags & ENCLOSED_EMPTY)
      enclosed_len=0;
    if (empty_flags & LINE_TERM_EMPTY)
      line_term_len=0;
    if (empty_flags & LINE_START_EMPTY)
      line_start_len=0;
    if (empty_flags & ESCAPED_EMPTY)
      escaped_len=0;
  }
  return buf;
}