log_event.cc 166 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2004 MySQL AB
2

unknown's avatar
unknown committed
3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
6

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

unknown's avatar
unknown committed
12 13 14 15 16 17
   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
unknown's avatar
unknown committed
18

19
#ifdef USE_PRAGMA_IMPLEMENTATION
unknown's avatar
unknown committed
20 21
#pragma implementation				// gcc: Class implementation
#endif
22

unknown's avatar
unknown committed
23
#include  "mysql_priv.h"
24
#include "slave.h"
25
#include <my_dir.h>
unknown's avatar
unknown committed
26 27
#endif /* MYSQL_CLIENT */

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

30 31 32 33
#ifndef DBUG_OFF
uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
#endif

unknown's avatar
unknown committed
34
/*
35
  pretty_print_str()
unknown's avatar
unknown committed
36
*/
37

38
#ifdef MYSQL_CLIENT
39
static void pretty_print_str(FILE* file, char* str, int len)
unknown's avatar
unknown committed
40
{
41
  char* end = str + len;
unknown's avatar
unknown committed
42
  fputc('\'', file);
43 44
  while (str < end)
  {
unknown's avatar
unknown committed
45
    char c;
46 47 48 49 50 51 52 53 54 55 56 57
    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;
    }
58 59
  }
  fputc('\'', file);
unknown's avatar
unknown committed
60
}
unknown's avatar
unknown committed
61
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
62

unknown's avatar
unknown committed
63

unknown's avatar
unknown committed
64
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
65

66 67 68 69 70 71 72 73
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
74

unknown's avatar
unknown committed
75
/*
unknown's avatar
unknown committed
76
  Ignore error code specified on command line
unknown's avatar
unknown committed
77
*/
78

79 80
inline int ignored_error_code(int err_code)
{
unknown's avatar
unknown committed
81 82
  return ((err_code == ER_SLAVE_IGNORED_TABLE) ||
          (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code)));
83
}
unknown's avatar
SCRUM  
unknown committed
84
#endif
85

unknown's avatar
unknown committed
86

unknown's avatar
unknown committed
87
/*
88
  pretty_print_str()
unknown's avatar
unknown committed
89
*/
unknown's avatar
unknown committed
90

unknown's avatar
unknown committed
91
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
92
static char *pretty_print_str(char *packet, char *str, int len)
unknown's avatar
unknown committed
93
{
unknown's avatar
unknown committed
94 95
  char *end= str + len;
  char *pos= packet;
96
  *pos++= '\'';
97 98 99
  while (str < end)
  {
    char c;
100
    switch ((c=*str++)) {
unknown's avatar
unknown committed
101 102 103 104 105 106 107
    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;
108
    default:
unknown's avatar
unknown committed
109
      *pos++= c;
110 111
      break;
    }
unknown's avatar
unknown committed
112
  }
113 114
  *pos++= '\'';
  return pos;
unknown's avatar
unknown committed
115
}
unknown's avatar
unknown committed
116
#endif /* !MYSQL_CLIENT */
117

unknown's avatar
unknown committed
118

unknown's avatar
unknown committed
119
/*
unknown's avatar
unknown committed
120 121 122 123 124 125 126 127 128 129 130
  Creates a temporary name for load data infile:

  SYNOPSIS
    slave_load_file_stem()
    buf		      Store new filename here
    file_id	      File_id (part of file name)
    event_server_id   Event_id (part of file name)
    ext		      Extension for file name

  RETURN
    Pointer to start of extension
unknown's avatar
unknown committed
131
*/
unknown's avatar
unknown committed
132

unknown's avatar
SCRUM  
unknown committed
133
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
134 135
static char *slave_load_file_stem(char *buf, uint file_id,
                                  int event_server_id, const char *ext)
136
{
unknown's avatar
unknown committed
137
  char *res;
138 139 140
  fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", MY_UNPACK_FILENAME);
  to_unix_path(buf);

141 142 143 144 145
  buf = strend(buf);
  buf = int10_to_str(::server_id, buf, 10);
  *buf++ = '-';
  buf = int10_to_str(event_server_id, buf, 10);
  *buf++ = '-';
unknown's avatar
unknown committed
146 147 148
  res= int10_to_str(file_id, buf, 10);
  strmov(res, ext);                             // Add extension last
  return res;                                   // Pointer to extension
149
}
unknown's avatar
SCRUM  
unknown committed
150
#endif
151

unknown's avatar
unknown committed
152

unknown's avatar
unknown committed
153
/*
154 155
  Delete all temporary files used for SQL_LOAD.

unknown's avatar
unknown committed
156 157
  SYNOPSIS
    cleanup_load_tmpdir()
unknown's avatar
unknown committed
158
*/
159

unknown's avatar
SCRUM  
unknown committed
160
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
161 162 163 164 165
static void cleanup_load_tmpdir()
{
  MY_DIR *dirp;
  FILEINFO *file;
  uint i;
unknown's avatar
unknown committed
166
  char fname[FN_REFLEN], prefbuf[31], *p;
unknown's avatar
unknown committed
167

168 169 170
  if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME))))
    return;

171 172 173 174 175 176 177 178
  /* 
     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.
  */
179
  p= strmake(prefbuf, STRING_WITH_LEN("SQL_LOAD-"));
180 181 182 183
  p= int10_to_str(::server_id, p, 10);
  *(p++)= '-';
  *p= 0;

184 185 186
  for (i=0 ; i < (uint)dirp->number_off_files; i++)
  {
    file=dirp->dir_entry+i;
187
    if (is_prefix(file->name, prefbuf))
unknown's avatar
unknown committed
188 189 190 191
    {
      fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
      my_delete(fname, MYF(0));
    }
192 193 194 195
  }

  my_dirend(dirp);
}
unknown's avatar
SCRUM  
unknown committed
196
#endif
197 198


unknown's avatar
unknown committed
199
/*
200
  write_str()
unknown's avatar
unknown committed
201
*/
202

203
static bool write_str(IO_CACHE *file, char *str, uint length)
204
{
205 206 207 208
  byte tmp[1];
  tmp[0]= (byte) length;
  return (my_b_safe_write(file, tmp, sizeof(tmp)) ||
	  my_b_safe_write(file, (byte*) str, length));
209 210 211
}


unknown's avatar
unknown committed
212
/*
213
  read_str()
unknown's avatar
unknown committed
214
*/
215

216 217
static inline int read_str(char **buf, char *buf_end, char **str,
			   uint8 *len)
218
{
219
  if (*buf + ((uint) (uchar) **buf) >= buf_end)
220
    return 1;
221 222 223
  *len= (uint8) **buf;
  *str= (*buf)+1;
  (*buf)+= (uint) *len+1;
224 225 226
  return 0;
}

227

228 229 230
/*
  Transforms a string into "" or its expression in 0x... form.
*/
unknown's avatar
unknown committed
231

232
char *str_to_hex(char *to, const char *from, uint len)
233 234 235
{
  if (len)
  {
unknown's avatar
unknown committed
236 237 238
    *to++= '0';
    *to++= 'x';
    to= octet2hex(to, from, len);
239 240
  }
  else
unknown's avatar
unknown committed
241 242
    to= strmov(to, "\"\"");
  return to;                               // pointer to end 0 of 'to'
243 244
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
/*
  Append a version of the 'from' string suitable for use in a query to
  the 'to' string.  To generate a correct escaping, the character set
  information in 'csinfo' is used.
 */
#ifndef MYSQL_CLIENT
int
append_query_string(CHARSET_INFO *csinfo,
                    String const *from, String *to)
{
  char *beg, *ptr;
  uint32 const orig_len= to->length();
  if (to->reserve(orig_len + from->length()*2+3))
    return 1;

  beg= to->c_ptr_quick() + to->length();
  ptr= beg;
  if (csinfo->escape_with_backslash_is_dangerous)
    ptr= str_to_hex(ptr, from->ptr(), from->length());
  else
  {
    *ptr++= '\'';
267
    ptr+= escape_string_for_mysql(csinfo, ptr, 0,
268 269 270 271 272 273 274 275
                                  from->ptr(), from->length());
    *ptr++='\'';
  }
  to->length(orig_len + ptr - beg);
  return 0;
}
#endif

276

277 278 279 280 281
/*
  Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
  commands just before it prints a query.
*/

282 283
#ifdef MYSQL_CLIENT

284 285 286 287 288 289 290
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,", ");
291
    fprintf(file,"%s=%d", name, test(flags & option));
292 293 294
    *need_comma= 1;
  }
}
295
#endif
296

unknown's avatar
unknown committed
297
/**************************************************************************
298
	Log_event methods (= the parent class of all events)
unknown's avatar
unknown committed
299
**************************************************************************/
300

unknown's avatar
unknown committed
301
/*
302
  Log_event::get_type_str()
unknown's avatar
unknown committed
303
*/
304

unknown's avatar
unknown committed
305 306
const char* Log_event::get_type_str()
{
307
  switch(get_type_code()) {
308
  case START_EVENT_V3:  return "Start_v3";
unknown's avatar
unknown committed
309 310 311 312 313
  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";
314
  case NEW_LOAD_EVENT:   return "New_load";
unknown's avatar
unknown committed
315
  case SLAVE_EVENT:  return "Slave";
316 317 318 319
  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";
320
  case RAND_EVENT: return "RAND";
321
  case XID_EVENT: return "Xid";
unknown's avatar
unknown committed
322
  case USER_VAR_EVENT: return "User var";
323
  case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
unknown's avatar
unknown committed
324 325
  case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
  case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
326
  default: return "Unknown";				/* impossible */
unknown's avatar
unknown committed
327 328 329
  }
}

330

unknown's avatar
unknown committed
331
/*
332
  Log_event::Log_event()
unknown's avatar
unknown committed
333
*/
334

unknown's avatar
unknown committed
335
#ifndef MYSQL_CLIENT
336
Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
337
  :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg)
338
{
unknown's avatar
unknown committed
339 340
  server_id=	thd->server_id;
  when=		thd->start_time;
341
  cache_stmt=	using_trans;
342 343 344
}


unknown's avatar
unknown committed
345
/*
346 347 348 349
  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
350 351
*/

352
Log_event::Log_event()
353
  :temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
354 355
   thd(0)
{
unknown's avatar
unknown committed
356 357 358
  server_id=	::server_id;
  when=		time(NULL);
  log_pos=	0;
359
}
unknown's avatar
unknown committed
360
#endif /* !MYSQL_CLIENT */
361 362


unknown's avatar
unknown committed
363
/*
364
  Log_event::Log_event()
365
*/
366

367
Log_event::Log_event(const char* buf,
unknown's avatar
unknown committed
368
                     const Format_description_log_event* description_event)
369
  :temp_buf(0), cache_stmt(0)
370
{
371 372
#ifndef MYSQL_CLIENT
  thd = 0;
unknown's avatar
unknown committed
373
#endif
374 375
  when = uint4korr(buf);
  server_id = uint4korr(buf + SERVER_ID_OFFSET);
376
  data_written= uint4korr(buf + EVENT_LEN_OFFSET);
377
  if (description_event->binlog_version==1)
378
  {
379 380 381
    log_pos= 0;
    flags= 0;
    return;
382
  }
383 384 385
  /* 4.0 or newer */
  log_pos= uint4korr(buf + LOG_POS_OFFSET);
  /*
386 387 388 389 390 391 392 393
    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).
394 395 396
  */
  if (description_event->binlog_version==3 &&
      buf[EVENT_TYPE_OFFSET]<FORMAT_DESCRIPTION_EVENT && log_pos)
397
  {
398 399 400
      /*
        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
401 402
        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
403
        not have its log_pos (which is 0) changed or it will modify
404 405 406 407
        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).
408
      */
unknown's avatar
unknown committed
409
    log_pos+= data_written; /* purecov: inspected */
410
  }
411 412 413 414 415 416 417
  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))
  {
    /*
418 419
      These events always have a header which stops here (i.e. their
      header is FROZEN).
420 421
    */
    /*
422 423 424 425 426 427 428
      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.
429 430 431 432
    */
    return;
  }
  /* otherwise, go on with reading the header from buf (nothing now) */
433 434 435
}

#ifndef MYSQL_CLIENT
unknown's avatar
SCRUM  
unknown committed
436
#ifdef HAVE_REPLICATION
437

unknown's avatar
unknown committed
438
/*
439
  Log_event::exec_event()
unknown's avatar
unknown committed
440
*/
441

442
int Log_event::exec_event(struct st_relay_log_info* rli)
443
{
unknown's avatar
unknown committed
444 445
  DBUG_ENTER("Log_event::exec_event");

446 447 448 449 450 451 452 453 454 455 456 457
  /*
    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.
  */
458
  if (rli)
459
  {
460
    /*
461 462 463 464
      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= .
465

466
      CAUTION: opt_using_transactions means
467
      innodb || bdb ; suppose the master supports InnoDB and BDB,
468
      but the slave supports only BDB, problems
469
      will arise:
470 471
      - suppose an InnoDB table is created on the master,
      - then it will be MyISAM on the slave
472 473 474 475 476 477 478 479 480 481 482 483
      - 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.
484 485
    */
    if ((thd->options & OPTION_BEGIN) && opt_using_transactions)
486
      rli->inc_event_relay_log_pos();
487 488
    else
    {
489 490 491 492 493 494 495 496 497 498 499 500
      /*
        bug#29309 simulation: resetting the flag to force
        wrong behaviour of artificial event to update
        rli->last_master_timestamp for only one time -
        the first FLUSH LOGS in the test.
      */
      DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
                      if (debug_not_change_ts_if_art_event == 1
                          && is_artificial_event())
                      {
                        debug_not_change_ts_if_art_event= 0;
                      });
501
      rli->inc_group_relay_log_pos(log_pos);
502
      flush_relay_log_info(rli);
503
      /* 
504 505
         Note that Rotate_log_event::exec_event() does not call this
         function, so there is no chance that a fake rotate event resets
506
         last_master_timestamp.
507 508 509
         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).
510
      */
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
#ifndef DBUG_OFF
      if (!(is_artificial_event() && debug_not_change_ts_if_art_event > 0))
#else
      if (!is_artificial_event())
#endif
        rli->last_master_timestamp= when;
      /*
        The flag is set back to be positive so that 
        any further FLUSH LOGS will be handled as prescribed.
      */
      DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
                      if (debug_not_change_ts_if_art_event == 0)
                      {
                        debug_not_change_ts_if_art_event= 2;
                      });
526
    }
527
  }
unknown's avatar
unknown committed
528
  DBUG_RETURN(0);
529
}
unknown's avatar
unknown committed
530

531

unknown's avatar
unknown committed
532
/*
533
  Log_event::pack_info()
unknown's avatar
unknown committed
534
*/
535

536
void Log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
537
{
538
  protocol->store("", &my_charset_bin);
unknown's avatar
unknown committed
539 540 541
}


unknown's avatar
unknown committed
542
/*
543
  Log_event::net_send()
unknown's avatar
unknown committed
544

545
  Only called by SHOW BINLOG EVENTS
unknown's avatar
unknown committed
546
*/
unknown's avatar
SCRUM  
unknown committed
547

548
int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
549
{
unknown's avatar
unknown committed
550 551
  const char *p= strrchr(log_name, FN_LIBCHAR);
  const char *event_type;
552 553 554
  if (p)
    log_name = p + 1;
  
555
  protocol->prepare_for_resend();
556
  protocol->store(log_name, &my_charset_bin);
557
  protocol->store((ulonglong) pos);
558
  event_type = get_type_str();
559
  protocol->store(event_type, strlen(event_type), &my_charset_bin);
560 561 562 563
  protocol->store((uint32) server_id);
  protocol->store((ulonglong) log_pos);
  pack_info(protocol);
  return protocol->write();
564
}
unknown's avatar
SCRUM  
unknown committed
565 566 567
#endif /* HAVE_REPLICATION */


unknown's avatar
unknown committed
568
/*
unknown's avatar
SCRUM  
unknown committed
569
  Log_event::init_show_field_list()
unknown's avatar
unknown committed
570
*/
unknown's avatar
SCRUM  
unknown committed
571 572 573 574

void Log_event::init_show_field_list(List<Item>* field_list)
{
  field_list->push_back(new Item_empty_string("Log_name", 20));
575
  field_list->push_back(new Item_return_int("Pos", MY_INT32_NUM_DECIMAL_DIGITS,
unknown's avatar
SCRUM  
unknown committed
576 577 578 579
					    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));
580 581
  field_list->push_back(new Item_return_int("End_log_pos",
                                            MY_INT32_NUM_DECIMAL_DIGITS,
unknown's avatar
SCRUM  
unknown committed
582 583 584 585
					    MYSQL_TYPE_LONGLONG));
  field_list->push_back(new Item_empty_string("Info", 20));
}

586

unknown's avatar
unknown committed
587
/*
588
  Log_event::write()
unknown's avatar
unknown committed
589
*/
590

591
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
unknown's avatar
unknown committed
592
{
593 594
  byte header[LOG_EVENT_HEADER_LEN];
  DBUG_ENTER("Log_event::write_header");
unknown's avatar
unknown committed
595

596 597
  /* Store number of bytes that will be written by this event */
  data_written= event_data_length + sizeof(header);
598

599 600 601 602
  /*
    log_pos != 0 if this is relay-log event. In this case we should not
    change the position
  */
603

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
  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
659 660 661
}


unknown's avatar
unknown committed
662
/*
663
  Log_event::read_log_event()
664 665 666 667

  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
668
*/
669

670
int Log_event::read_log_event(IO_CACHE* file, String* packet,
671
			      pthread_mutex_t* log_lock)
unknown's avatar
unknown committed
672 673
{
  ulong data_len;
674
  int result=0;
675
  char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
676
  DBUG_ENTER("read_log_event");
677

678
  if (log_lock)
679
    pthread_mutex_lock(log_lock);
680 681
  if (my_b_read(file, (byte*) buf, sizeof(buf)))
  {
682 683 684 685 686
    /*
      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.
    */
687
    DBUG_PRINT("error",("file->error: %d", file->error));
688 689 690
    if (!file->error)
      result= LOG_READ_EOF;
    else
691
      result= (file->error > 0 ? LOG_READ_TRUNC : LOG_READ_IO);
692
    goto end;
693
  }
694
  data_len= uint4korr(buf + EVENT_LEN_OFFSET);
695
  if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
unknown's avatar
unknown committed
696
      data_len > current_thd->variables.max_allowed_packet)
697
  {
698
    DBUG_PRINT("error",("data_len: %ld", data_len));
699
    result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
700 701
	     LOG_READ_TOO_LARGE);
    goto end;
702
  }
703 704 705 706 707 708 709 710

  /* Append the log event header to packet */
  if (packet->append(buf, sizeof(buf)))
  {
    /* Failed to allocate packet */
    result= LOG_READ_MEM;
    goto end;
  }
711
  data_len-= LOG_EVENT_MINIMAL_HEADER_LEN;
712 713
  if (data_len)
  {
714
    /* Append rest of event, read directly from file into packet */
715
    if (packet->append(file, data_len))
716
    {
717
      /*
718 719 720 721 722 723 724 725 726 727
        Fatal error occured when appending rest of the event
        to packet, possible failures:
	1. EOF occured when reading from file, it's really an error
           as data_len is >=0 there's supposed to be more bytes available.
           file->error will have been set to number of bytes left to read
        2. Read was interrupted, file->error would normally be set to -1
        3. Failed to allocate memory for packet, my_errno
           will be ENOMEM(file->error shuold be 0, but since the
           memory allocation occurs before the call to read it might
           be uninitialized)
728
      */
729 730
      result= (my_errno == ENOMEM ? LOG_READ_MEM :
               (file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO));
731
      /* Implicit goto end; */
732
    }
733
  }
734 735 736 737

end:
  if (log_lock)
    pthread_mutex_unlock(log_lock);
738
  DBUG_RETURN(result);
unknown's avatar
unknown committed
739
}
unknown's avatar
unknown committed
740
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
741

unknown's avatar
unknown committed
742
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
743 744
#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock);
#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock);
745
#else
746
#define UNLOCK_MUTEX
747 748 749
#define LOCK_MUTEX
#endif

unknown's avatar
unknown committed
750
/*
751 752
  Log_event::read_log_event()

unknown's avatar
unknown committed
753
  NOTE:
754
    Allocates memory;  The caller is responsible for clean-up.
unknown's avatar
unknown committed
755
*/
756

unknown's avatar
unknown committed
757
#ifndef MYSQL_CLIENT
758 759
Log_event* Log_event::read_log_event(IO_CACHE* file,
				     pthread_mutex_t* log_lock,
760
                                     const Format_description_log_event *description_event)
unknown's avatar
unknown committed
761
#else
762 763
Log_event* Log_event::read_log_event(IO_CACHE* file,
                                     const Format_description_log_event *description_event)
764
#endif
unknown's avatar
unknown committed
765
{
766
  DBUG_ENTER("Log_event::read_log_event(IO_CACHE *, Format_description_log_event *");
767
  DBUG_ASSERT(description_event != 0);
768 769 770 771 772
  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
773 774
    of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's
    "minimal" over the set {MySQL >=4.0}).
775 776 777
  */
  uint header_size= min(description_event->common_header_len,
                        LOG_EVENT_MINIMAL_HEADER_LEN);
778

779
  LOCK_MUTEX;
unknown's avatar
unknown committed
780
  DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file)));
781
  if (my_b_read(file, (byte *) head, header_size))
782
  {
783 784
    DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
failed my_b_read"));
unknown's avatar
unknown committed
785
    UNLOCK_MUTEX;
786
    /*
787 788 789
      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.
790
    */
791
    DBUG_RETURN(0);
792
  }
793
  uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
794 795 796
  char *buf= 0;
  const char *error= 0;
  Log_event *res=  0;
797 798
#ifndef max_allowed_packet
  THD *thd=current_thd;
unknown's avatar
unknown committed
799
  uint max_allowed_packet= thd ? thd->variables.max_allowed_packet : ~(ulong)0;
800
#endif
unknown's avatar
unknown committed
801

802
  if (data_len > max_allowed_packet)
unknown's avatar
unknown committed
803
  {
804 805
    error = "Event too big";
    goto err;
unknown's avatar
unknown committed
806 807
  }

808
  if (data_len < header_size)
unknown's avatar
unknown committed
809
  {
810 811
    error = "Event too small";
    goto err;
unknown's avatar
unknown committed
812
  }
813 814 815

  // some events use the extra byte to null-terminate strings
  if (!(buf = my_malloc(data_len+1, MYF(MY_WME))))
816 817 818
  {
    error = "Out of memory";
    goto err;
unknown's avatar
unknown committed
819
  }
820
  buf[data_len] = 0;
821
  memcpy(buf, head, header_size);
822
  if (my_b_read(file, (byte*) buf + header_size, data_len - header_size))
823 824 825 826
  {
    error = "read error";
    goto err;
  }
827
  if ((res= read_log_event(buf, data_len, &error, description_event)))
828
    res->register_temp_buf(buf);
829

830
err:
unknown's avatar
unknown committed
831
  UNLOCK_MUTEX;
832
  if (!res)
833
  {
834
    DBUG_ASSERT(error != 0);
835 836
    sql_print_error("Error in Log_event::read_log_event(): "
                    "'%s', data_len: %d, event_type: %d",
837
		    error,data_len,head[EVENT_TYPE_OFFSET]);
838
    my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
839 840 841 842 843 844 845 846 847
    /*
      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;
848
  }
849
  DBUG_RETURN(res);
unknown's avatar
unknown committed
850 851
}

852

unknown's avatar
unknown committed
853
/*
854
  Log_event::read_log_event()
855 856
  Binlog format tolerance is in (buf, event_len, description_event)
  constructors.
unknown's avatar
unknown committed
857
*/
858

859 860 861
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
862
{
863 864
  Log_event* ev;
  DBUG_ENTER("Log_event::read_log_event(char*,...)");
865
  DBUG_ASSERT(description_event != 0);
866
  DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
867
  if (event_len < EVENT_LEN_OFFSET ||
868 869 870
      (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
  {
    *error="Sanity check failed";		// Needed to free buffer
unknown's avatar
unknown committed
871
    DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
872
  }
873

874
  switch(buf[EVENT_TYPE_OFFSET]) {
unknown's avatar
unknown committed
875
  case QUERY_EVENT:
unknown's avatar
unknown committed
876
    ev  = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
877
    break;
unknown's avatar
unknown committed
878
  case LOAD_EVENT:
unknown's avatar
unknown committed
879
    ev = new Load_log_event(buf, event_len, description_event);
unknown's avatar
unknown committed
880
    break;
881
  case NEW_LOAD_EVENT:
882
    ev = new Load_log_event(buf, event_len, description_event);
883
    break;
unknown's avatar
unknown committed
884
  case ROTATE_EVENT:
885
    ev = new Rotate_log_event(buf, event_len, description_event);
886
    break;
unknown's avatar
SCRUM  
unknown committed
887
#ifdef HAVE_REPLICATION
888
  case SLAVE_EVENT: /* can never happen (unused event) */
889 890
    ev = new Slave_log_event(buf, event_len);
    break;
unknown's avatar
SCRUM  
unknown committed
891
#endif /* HAVE_REPLICATION */
892
  case CREATE_FILE_EVENT:
893
    ev = new Create_file_log_event(buf, event_len, description_event);
894 895
    break;
  case APPEND_BLOCK_EVENT:
896
    ev = new Append_block_log_event(buf, event_len, description_event);
897 898
    break;
  case DELETE_FILE_EVENT:
899
    ev = new Delete_file_log_event(buf, event_len, description_event);
900 901
    break;
  case EXEC_LOAD_EVENT:
902
    ev = new Execute_load_log_event(buf, event_len, description_event);
903
    break;
904 905
  case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
    ev = new Start_log_event_v3(buf, description_event);
906 907
    break;
  case STOP_EVENT:
908
    ev = new Stop_log_event(buf, description_event);
909 910
    break;
  case INTVAR_EVENT:
911
    ev = new Intvar_log_event(buf, description_event);
912
    break;
913 914 915
  case XID_EVENT:
    ev = new Xid_log_event(buf, description_event);
    break;
unknown's avatar
unknown committed
916
  case RAND_EVENT:
917
    ev = new Rand_log_event(buf, description_event);
unknown's avatar
unknown committed
918
    break;
unknown's avatar
unknown committed
919
  case USER_VAR_EVENT:
920 921 922 923
    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
924
    break;
unknown's avatar
unknown committed
925 926 927 928 929 930
  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;
931
  default:
932 933
    DBUG_PRINT("error",("Unknown evernt code: %d",(int) buf[EVENT_TYPE_OFFSET]));
    ev= NULL;
934
    break;
unknown's avatar
unknown committed
935
  }
936

937
  /*
938 939 940 941 942 943 944
    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'.
945
  */
946
  if (!ev || !ev->is_valid())
947
  {
948 949
    DBUG_PRINT("error",("Found invalid event in binary log"));

950
    delete ev;
951
#ifdef MYSQL_CLIENT
952
    if (!force_opt) /* then mysqlbinlog dies */
953 954
    {
      *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
955
      DBUG_RETURN(0);
956
    }
957
    ev= new Unknown_log_event(buf, description_event);
958 959
#else
    *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
960
    DBUG_RETURN(0);
961
#endif
962
  }
unknown's avatar
unknown committed
963
  DBUG_RETURN(ev);  
unknown's avatar
unknown committed
964 965
}

966
#ifdef MYSQL_CLIENT
967

unknown's avatar
unknown committed
968
/*
969
  Log_event::print_header()
unknown's avatar
unknown committed
970
*/
971

unknown's avatar
unknown committed
972
void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info)
973
{
974
  char llbuff[22];
unknown's avatar
unknown committed
975
  my_off_t hexdump_from= print_event_info->hexdump_from;
unknown's avatar
unknown committed
976

977 978
  fputc('#', file);
  print_timestamp(file);
979
  fprintf(file, " server id %lu  end_log_pos %s ", server_id,
980
	  llstr(log_pos,llbuff));
981

982
  /* mysqlbinlog --hexdump */
unknown's avatar
unknown committed
983
  if (print_event_info->hexdump_from)
984 985
  {
    fprintf(file, "\n");
986 987 988
    uchar *ptr= (uchar*)temp_buf;
    my_off_t size=
      uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN;
989 990
    my_off_t i;

991 992 993 994
    /* Header len * 4 >= header len * (2 chars + space + extra space) */
    char *h, hex_string[LOG_EVENT_MINIMAL_HEADER_LEN*4]= {0};
    char *c, char_string[16+1]= {0};

unknown's avatar
unknown committed
995
    /* Pretty-print event common header if header is exactly 19 bytes */
unknown's avatar
unknown committed
996
    if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
unknown's avatar
unknown committed
997 998 999 1000 1001 1002
    {
      fprintf(file, "# Position  Timestamp   Type   Master ID        "
	      "Size      Master Pos    Flags \n");
      fprintf(file, "# %8.8lx %02x %02x %02x %02x   %02x   "
	      "%02x %02x %02x %02x   %02x %02x %02x %02x   "
	      "%02x %02x %02x %02x   %02x %02x\n",
1003 1004 1005 1006
	      (unsigned long) hexdump_from,
              ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
              ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13],
              ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]);
unknown's avatar
unknown committed
1007 1008 1009
      ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
      hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
    }
1010 1011 1012 1013 1014

    /* Rest of event (without common header) */
    for (i= 0, c= char_string, h=hex_string;
	 i < size;
	 i++, ptr++)
1015
    {
1016 1017
      my_snprintf(h, 4, "%02x ", *ptr);
      h += 3;
1018

1019
      *c++= my_isalnum(&my_charset_bin, *ptr) ? *ptr : '.';
1020 1021 1022

      if (i % 16 == 15)
      {
1023
	fprintf(file, "# %8.8lx %-48.48s |%16s|\n",
1024 1025
		(unsigned long) (hexdump_from + (i & 0xfffffff0)),
                hex_string, char_string);
1026 1027 1028 1029
	hex_string[0]= 0;
	char_string[0]= 0;
	c= char_string;
	h= hex_string;
1030
      }
1031
      else if (i % 8 == 7) *h++ = ' ';
1032
    }
1033
    *c= '\0';
1034

unknown's avatar
unknown committed
1035
    if (hex_string[0])
unknown's avatar
unknown committed
1036 1037
    {
      /* Non-full last line */
1038 1039 1040
      fprintf(file, "# %8.8lx %-48.48s |%s|\n# ",
	     (unsigned long) (hexdump_from + (i & 0xfffffff0)),
             hex_string, char_string);
unknown's avatar
unknown committed
1041 1042 1043
    }
    else
      fprintf(file, "# ");
1044
  }
1045 1046
}

1047

unknown's avatar
unknown committed
1048
/*
1049
  Log_event::print_timestamp()
unknown's avatar
unknown committed
1050
*/
1051

1052
void Log_event::print_timestamp(FILE* file, time_t* ts)
unknown's avatar
unknown committed
1053
{
unknown's avatar
unknown committed
1054
  struct tm *res;
1055 1056
  if (!ts)
    ts = &when;
1057 1058
#ifdef MYSQL_SERVER				// This is always false
  struct tm tm_tmp;
unknown's avatar
unknown committed
1059
  localtime_r(ts,(res= &tm_tmp));
unknown's avatar
unknown committed
1060
#else
1061
  res=localtime(ts);
unknown's avatar
unknown committed
1062
#endif
1063 1064

  fprintf(file,"%02d%02d%02d %2d:%02d:%02d",
1065 1066 1067 1068 1069 1070
	  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
1071 1072
}

unknown's avatar
unknown committed
1073
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
1074 1075


unknown's avatar
unknown committed
1076
/**************************************************************************
unknown's avatar
unknown committed
1077
	Query_log_event methods
unknown's avatar
unknown committed
1078
**************************************************************************/
1079

unknown's avatar
SCRUM  
unknown committed
1080
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1081

unknown's avatar
unknown committed
1082
/*
1083
  Query_log_event::pack_info()
1084 1085 1086 1087
  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
1088
*/
1089

1090
void Query_log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
1091
{
1092
  // TODO: show the catalog ??
1093 1094 1095
  char *buf, *pos;
  if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME))))
    return;
1096 1097
  pos= buf;
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
1098
      && db && db_len)
1099
  {
1100 1101
    pos= strmov(buf, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
1102
    pos= strmov(pos+db_len, "`; ");
1103
  }
1104
  if (query && q_len)
1105 1106 1107 1108
  {
    memcpy(pos, query, q_len);
    pos+= q_len;
  }
1109
  protocol->store(buf, pos-buf, &my_charset_bin);
1110
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
1111
}
unknown's avatar
SCRUM  
unknown committed
1112
#endif
1113

1114
#ifndef MYSQL_CLIENT
1115

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
/* Utility function for the next method */
static void write_str_with_code_and_len(char **dst, const char *src,
                                        int len, uint code)
{
  DBUG_ASSERT(src);
  *((*dst)++)= code;
  *((*dst)++)= (uchar) len;
  bmove(*dst, src, len);
  (*dst)+= len;
}


unknown's avatar
unknown committed
1128
/*
1129
  Query_log_event::write()
unknown's avatar
unknown committed
1130

1131 1132 1133 1134
  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
1135
*/
1136

1137
bool Query_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
1138
{
1139 1140 1141 1142 1143
  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
1144
            1+6+           // code of charset and charset
1145
            1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name
1146 1147
            1+2+           // code of lc_time_names and lc_time_names_number
            1+2            // code of charset_database and charset_database_number
1148
            ], *start, *start_of_status;
1149
  ulong event_length;
unknown's avatar
unknown committed
1150

1151
  if (!query)
1152 1153
    return 1;                                   // Something wrong with event

unknown's avatar
unknown committed
1154 1155 1156 1157 1158
  /*
    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.
1159
    Now imagine we (write()) are called by the slave SQL thread (we are
unknown's avatar
unknown committed
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
    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);
1192 1193 1194 1195
  int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
  buf[Q_DB_LEN_OFFSET] = (char) db_len;
  int2store(buf + Q_ERR_CODE_OFFSET, error_code);

1196 1197 1198 1199 1200 1201 1202 1203
  /*
    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)
  {
1204
    *start++= Q_FLAGS2_CODE;
1205 1206 1207 1208 1209
    int4store(start, flags2);
    start+= 4;
  }
  if (sql_mode_inited)
  {
1210
    *start++= Q_SQL_MODE_CODE;
unknown's avatar
unknown committed
1211
    int8store(start, (ulonglong)sql_mode);
1212 1213
    start+= 8;
  }
1214
  if (catalog_len) // i.e. this var is inited (false for 4.0 events)
1215
  {
1216 1217
    write_str_with_code_and_len((char **)(&start),
                                catalog, catalog_len, Q_CATALOG_NZ_CODE);
1218
    /*
1219 1220 1221 1222 1223 1224
      In 5.0.x where x<4 masters we used to store the end zero here. This was
      a waste of one byte so we don't do it in x>=4 masters. We change code to
      Q_CATALOG_NZ_CODE, because re-using the old code would make x<4 slaves
      of this x>=4 master segfault (expecting a zero when there is
      none). Remaining compatibility problems are: the older slave will not
      find the catalog; but it is will not crash, and it's not an issue
1225 1226 1227 1228 1229
      that it does not find the catalog as catalogs were not used in these
      older MySQL versions (we store it in binlog and read it from relay log
      but do nothing useful with it). What is an issue is that the older slave
      will stop processing the Q_* blocks (and jumps to the db/query) as soon
      as it sees unknown Q_CATALOG_NZ_CODE; so it will not be able to read
1230 1231
      Q_AUTO_INCREMENT*, Q_CHARSET and so replication will fail silently in
      various ways. Documented that you should not mix alpha/beta versions if
1232 1233 1234
      they are not exactly the same version, with example of 5.0.3->5.0.2 and
      5.0.4->5.0.3. If replication is from older to new, the new will
      recognize Q_CATALOG_CODE and have no problem.
1235
    */
1236 1237 1238 1239 1240 1241 1242 1243
  }
  if (auto_increment_increment != 1)
  {
    *start++= Q_AUTO_INCREMENT;
    int2store(start, auto_increment_increment);
    int2store(start+2, auto_increment_offset);
    start+= 4;
  }
1244 1245
  if (charset_inited)
  {
1246
    *start++= Q_CHARSET_CODE;
1247 1248 1249
    memcpy(start, charset, 6);
    start+= 6;
  }
1250 1251 1252 1253 1254 1255 1256 1257 1258
  if (time_zone_len)
  {
    /* In the TZ sys table, column Name is of length 64 so this should be ok */
    DBUG_ASSERT(time_zone_len <= MAX_TIME_ZONE_NAME_LENGTH);
    *start++= Q_TIME_ZONE_CODE;
    *start++= time_zone_len;
    memcpy(start, time_zone_str, time_zone_len);
    start+= time_zone_len;
  }
1259 1260 1261 1262 1263 1264 1265
  if (lc_time_names_number)
  {
    DBUG_ASSERT(lc_time_names_number <= 0xFFFF);
    *start++= Q_LC_TIME_NAMES_CODE;
    int2store(start, lc_time_names_number);
    start+= 2;
  }
1266 1267 1268 1269 1270 1271 1272
  if (charset_database_number)
  {
    DBUG_ASSERT(charset_database_number <= 0xFFFF);
    *start++= Q_CHARSET_DATABASE_CODE;
    int2store(start, charset_database_number);
    start+= 2;
  }
1273 1274
  /*
    Here there could be code like
1275
    if (command-line-option-which-says-"log_this_variable" && inited)
1276
    {
1277
    *start++= Q_THIS_VARIABLE_CODE;
1278 1279 1280 1281 1282 1283 1284
    int4store(start, this_variable);
    start+= 4;
    }
  */
  
  /* Store length of status variables */
  status_vars_len= (uint) (start-start_of_status);
1285
  DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS);
1286 1287 1288 1289 1290 1291
  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
1292
  event_length= (uint) (start-buf) + get_post_header_size_for_derived() + db_len + 1 + q_len;
1293 1294

  return (write_header(file, event_length) ||
unknown's avatar
unknown committed
1295 1296 1297 1298
          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)) ||
1299 1300
          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
1301 1302
}

1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314
/*
  Query_log_event::Query_log_event()
 
  The simplest constructor that could possibly work.  This is used for
  creating static objects that have a special meaning and are invisible
  to the log.  
*/
Query_log_event::Query_log_event()
  :Log_event(), data_buf(0)
{
}

1315

unknown's avatar
unknown committed
1316
/*
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
  SYNOPSIS
    Query_log_event::Query_log_event()
      thd               - thread handle
      query_arg         - array of char representing the query
      query_length      - size of the  `query_arg' array
      using_trans       - there is a modified transactional table
      suppress_use      - suppress the generation of 'USE' statements
      killed_status_arg - an optional with default to THD::KILLED_NO_VALUE
                          if the value is different from the default, the arg
                          is set to the current thd->killed value.
                          A caller might need to masquerade thd->killed with
                          THD::NOT_KILLED.
  DESCRIPTION
  Creates an event for binlogging
  The value for local `killed_status' can be supplied by caller.
unknown's avatar
unknown committed
1332
*/
1333
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
1334
				 ulong query_length, bool using_trans,
1335
				 bool suppress_use, THD::killed_state killed_status_arg)
1336
  :Log_event(thd_arg,
1337 1338 1339
             ((thd_arg->tmp_table_used || thd_arg->thread_specific_used) ? 
	        LOG_EVENT_THREAD_SPECIFIC_F : 0) |
	      (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
1340
	     using_trans),
1341
   data_buf(0), query(query_arg), catalog(thd_arg->catalog),
1342
   db(thd_arg->db), q_len((uint32) query_length),
unknown's avatar
unknown committed
1343 1344
   thread_id(thd_arg->thread_id),
   /* save the original thread id; we already know the server id */
1345
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
1346
   flags2_inited(1), sql_mode_inited(1), charset_inited(1),
1347 1348
   sql_mode(thd_arg->variables.sql_mode),
   auto_increment_increment(thd_arg->variables.auto_increment_increment),
1349
   auto_increment_offset(thd_arg->variables.auto_increment_offset),
1350 1351
   lc_time_names_number(thd_arg->variables.lc_time_names->number),
   charset_database_number(0)
1352 1353
{
  time_t end_time;
1354 1355 1356 1357 1358 1359 1360 1361

  if (killed_status_arg == THD::KILLED_NO_VALUE)
    killed_status_arg= thd_arg->killed;
  error_code=
    (killed_status_arg == THD::NOT_KILLED) ? thd_arg->net.last_errno :
    ((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ? 0 :
     thd->killed_errno());
  
1362 1363
  time(&end_time);
  exec_time = (ulong) (end_time  - thd->start_time);
1364
  catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
1365
  /* status_vars_len is set just before writing the event */
1366
  db_len = (db) ? (uint32) strlen(db) : 0;
1367 1368 1369
  if (thd_arg->variables.collation_database != thd_arg->db_charset)
    charset_database_number= thd_arg->variables.collation_database->number;
  
1370 1371 1372 1373 1374 1375 1376
  /*
    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 &.
  */
unknown's avatar
unknown committed
1377
  flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG);
1378 1379 1380 1381 1382 1383
  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);
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
  if (thd_arg->time_zone_used)
  {
    /*
      Note that our event becomes dependent on the Time_zone object
      representing the time zone. Fortunately such objects are never deleted
      or changed during mysqld's lifetime.
    */
    time_zone_len= thd_arg->variables.time_zone->get_name()->length();
    time_zone_str= thd_arg->variables.time_zone->get_name()->ptr();
  }
  else
    time_zone_len= 0;
unknown's avatar
unknown committed
1396 1397
  DBUG_PRINT("info",("Query_log_event has flags2: %lu  sql_mode: %lu",
                     (ulong) flags2, sql_mode));
1398
}
unknown's avatar
unknown committed
1399
#endif /* MYSQL_CLIENT */
1400

unknown's avatar
unknown committed
1401

1402 1403
/* 2 utility functions for the next method */

1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
/**
   Read a string with length from memory.

   This function reads the string-with-length stored at
   <code>src</code> and extract the length into <code>*len</code> and
   a pointer to the start of the string into <code>*dst</code>. The
   string can then be copied using <code>memcpy()</code> with the
   number of bytes given in <code>*len</code>.

   @param src Pointer to variable holding a pointer to the memory to
              read the string from.
   @param dst Pointer to variable holding a pointer where the actual
              string starts. Starting from this position, the string
              can be copied using @c memcpy().
   @param len Pointer to variable where the length will be stored.
   @param end One-past-the-end of the memory where the string is
              stored.

   @return    Zero if the entire string can be copied successfully,
              @c UINT_MAX if the length could not be read from memory
              (that is, if <code>*src >= end</code>), otherwise the
              number of bytes that are missing to read the full
              string, which happends <code>*dst + *len >= end</code>.
1427
*/
1428 1429 1430 1431 1432
static int
get_str_len_and_pointer(const Log_event::Byte **src,
                        const char **dst,
                        uint *len,
                        const Log_event::Byte *end)
1433
{
1434 1435 1436 1437 1438 1439
  if (*src >= end)
    return -1;       // Will be UINT_MAX in two-complement arithmetics
  uint length= **src;
  if (length > 0)
  {
    if (*src + length >= end)
1440
      return *src + length - end + 1;       // Number of bytes missing
1441 1442 1443
    *dst= (char *)*src + 1;                    // Will be copied later
  }
  *len= length;
1444 1445
  *src+= length + 1;
  return 0;
1446 1447
}

1448 1449 1450
static void copy_str_and_move(const char **src, 
                              Log_event::Byte **dst, 
                              uint len)
1451 1452
{
  memcpy(*dst, *src, len);
1453
  *src= (const char *)*dst;
1454 1455 1456 1457
  (*dst)+= len;
  *(*dst)++= 0;
}

1458

1459
#ifndef DBUG_OFF
unknown's avatar
unknown committed
1460 1461 1462 1463
static char const *
code_name(int code)
{
  static char buf[255];
1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
  switch (code) {
  case Q_FLAGS2_CODE: return "Q_FLAGS2_CODE";
  case Q_SQL_MODE_CODE: return "Q_SQL_MODE_CODE";
  case Q_CATALOG_CODE: return "Q_CATALOG_CODE";
  case Q_AUTO_INCREMENT: return "Q_AUTO_INCREMENT";
  case Q_CHARSET_CODE: return "Q_CHARSET_CODE";
  case Q_TIME_ZONE_CODE: return "Q_TIME_ZONE_CODE";
  case Q_CATALOG_NZ_CODE: return "Q_CATALOG_NZ_CODE";
  case Q_LC_TIME_NAMES_CODE: return "Q_LC_TIME_NAMES_CODE";
  case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE";
  }
  sprintf(buf, "CODE#%d", code);
  return buf;
}
1478
#endif
1479

1480 1481 1482 1483 1484 1485 1486
/**
   Macro to check that there is enough space to read from memory.

   @param PTR Pointer to memory
   @param END End of memory
   @param CNT Number of bytes that should be read.
 */
1487 1488 1489 1490 1491 1492 1493 1494 1495
#define CHECK_SPACE(PTR,END,CNT)                      \
  do {                                                \
    DBUG_PRINT("info", ("Read %s", code_name(pos[-1]))); \
    DBUG_ASSERT((PTR) + (CNT) <= (END));              \
    if ((PTR) + (CNT) > (END)) {                      \
      DBUG_PRINT("info", ("query= 0"));               \
      query= 0;                                       \
      DBUG_VOID_RETURN;                               \
    }                                                 \
1496 1497
  } while (0)

unknown's avatar
unknown committed
1498
/*
1499
  Query_log_event::Query_log_event()
1500
  This is used by the SQL slave thread to prepare the event before execution.
unknown's avatar
unknown committed
1501
*/
1502

1503
Query_log_event::Query_log_event(const char* buf, uint event_len,
unknown's avatar
unknown committed
1504 1505
                                 const Format_description_log_event *description_event,
                                 Log_event_type event_type)
1506
  :Log_event(buf, description_event), data_buf(0), query(NullS),
1507
   db(NullS), catalog_len(0), status_vars_len(0),
1508
   flags2_inited(0), sql_mode_inited(0), charset_inited(0),
1509
   auto_increment_increment(1), auto_increment_offset(1),
1510
   time_zone_len(0), lc_time_names_number(0), charset_database_number(0)
unknown's avatar
unknown committed
1511 1512
{
  ulong data_len;
1513 1514
  uint32 tmp;
  uint8 common_header_len, post_header_len;
1515 1516
  Log_event::Byte *start;
  const Log_event::Byte *end;
1517
  bool catalog_nz= 1;
1518 1519 1520
  DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");

  common_header_len= description_event->common_header_len;
unknown's avatar
unknown committed
1521
  post_header_len= description_event->post_header_len[event_type-1];
unknown's avatar
unknown committed
1522
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536
                     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);
1537
  db_len = (uint)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
1538 1539 1540 1541 1542 1543 1544 1545 1546
  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)
1547
  {
1548
    status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET);
1549 1550 1551 1552 1553 1554
    /*
      Check if status variable length is corrupt and will lead to very
      wrong data. We could be even more strict and require data_len to
      be even bigger, but this will suffice to catch most corruption
      errors that can lead to a crash.
    */
1555
    if (status_vars_len > min(data_len, MAX_SIZE_LOG_EVENT_STATUS))
1556
    {
unknown's avatar
unknown committed
1557
      DBUG_PRINT("info", ("status_vars_len (%u) > data_len (%lu); query= 0",
1558
                          status_vars_len, data_len));
1559 1560 1561
      query= 0;
      DBUG_VOID_RETURN;
    }
1562 1563 1564 1565
    data_len-= status_vars_len;
    DBUG_PRINT("info", ("Query_log_event has status_vars_len: %u",
                        (uint) status_vars_len));
    tmp-= 2;
1566
  }
unknown's avatar
unknown committed
1567 1568 1569 1570 1571 1572
  /*
    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...)
  */

1573 1574
  /* variable-part: the status vars; only in MySQL 5.0  */
  
1575 1576 1577
  start= (Log_event::Byte*) (buf+post_header_len);
  end= (const Log_event::Byte*) (start+status_vars_len);
  for (const Log_event::Byte* pos= start; pos < end;)
1578
  {
1579 1580
    switch (*pos++) {
    case Q_FLAGS2_CODE:
1581
      CHECK_SPACE(pos, end, 4);
1582 1583
      flags2_inited= 1;
      flags2= uint4korr(pos);
unknown's avatar
unknown committed
1584
      DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", (ulong) flags2));
1585 1586 1587 1588 1589 1590 1591
      pos+= 4;
      break;
    case Q_SQL_MODE_CODE:
    {
#ifndef DBUG_OFF
      char buff[22];
#endif
1592
      CHECK_SPACE(pos, end, 8);
1593 1594 1595 1596 1597 1598 1599
      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;
    }
1600
    case Q_CATALOG_NZ_CODE:
1601
      DBUG_PRINT("info", ("case Q_CATALOG_NZ_CODE; pos: 0x%lx; end: 0x%lx",
unknown's avatar
unknown committed
1602
                          (ulong) pos, (ulong) end));
1603 1604
      if (get_str_len_and_pointer(&pos, &catalog, &catalog_len, end))
      {
1605
        DBUG_PRINT("info", ("query= 0"));
1606 1607 1608
        query= 0;
        DBUG_VOID_RETURN;
      }
1609 1610
      break;
    case Q_AUTO_INCREMENT:
1611
      CHECK_SPACE(pos, end, 4);
1612 1613 1614 1615
      auto_increment_increment= uint2korr(pos);
      auto_increment_offset=    uint2korr(pos+2);
      pos+= 4;
      break;
1616 1617
    case Q_CHARSET_CODE:
    {
1618
      CHECK_SPACE(pos, end, 6);
1619 1620 1621 1622 1623
      charset_inited= 1;
      memcpy(charset, pos, 6);
      pos+= 6;
      break;
    }
1624 1625
    case Q_TIME_ZONE_CODE:
    {
1626 1627
      if (get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len, end))
      {
1628
        DBUG_PRINT("info", ("Q_TIME_ZONE_CODE: query= 0"));
1629 1630 1631
        query= 0;
        DBUG_VOID_RETURN;
      }
1632 1633
      break;
    }
1634
    case Q_CATALOG_CODE: /* for 5.0.x where 0<=x<=3 masters */
1635
      CHECK_SPACE(pos, end, 1);
1636 1637
      if ((catalog_len= *pos))
        catalog= (char*) pos+1;                           // Will be copied later
1638
      CHECK_SPACE(pos, end, catalog_len + 2);
1639 1640 1641
      pos+= catalog_len+2; // leap over end 0
      catalog_nz= 0; // catalog has end 0 in event
      break;
1642
    case Q_LC_TIME_NAMES_CODE:
1643
      CHECK_SPACE(pos, end, 2);
1644 1645 1646
      lc_time_names_number= uint2korr(pos);
      pos+= 2;
      break;
1647
    case Q_CHARSET_DATABASE_CODE:
1648
      CHECK_SPACE(pos, end, 2);
1649 1650 1651
      charset_database_number= uint2korr(pos);
      pos+= 2;
      break;
1652 1653 1654 1655
    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)));
1656
      pos= (const uchar*) end;                         // Break loop
1657
    }
1658
  }
1659
  
1660
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
1661 1662 1663 1664 1665 1666
  if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
                                              time_zone_len + 1 +
                                              data_len + 1 +
                                              QUERY_CACHE_FLAGS_SIZE +
                                              db_len + 1,
                                              MYF(MY_WME))))
1667
#else
1668 1669 1670 1671
  if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
                                             time_zone_len + 1 +
                                             data_len + 1,
                                             MYF(MY_WME))))
1672
#endif
unknown's avatar
unknown committed
1673
      DBUG_VOID_RETURN;
1674
  if (catalog_len)                                  // If catalog is given
1675
  {
1676
    if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3.
1677
      copy_str_and_move(&catalog, &start, catalog_len);
1678 1679 1680
    else
    {
      memcpy(start, catalog, catalog_len+1); // copy end 0
1681
      catalog= (const char *)start;
1682 1683
      start+= catalog_len+1;
    }
1684
  }
1685
  if (time_zone_len)
1686
    copy_str_and_move(&time_zone_str, &start, time_zone_len);
1687

1688
  /* A 2nd variable part; this is common to all versions */ 
1689
  memcpy((char*) start, end, data_len);          // Copy db and query
1690
  start[data_len]= '\0';              // End query with \0 (For safetly)
1691 1692
  db= (char *)start;
  query= (char *)(start + db_len + 1);
1693 1694
  q_len= data_len - db_len -1;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1695 1696
}

1697

unknown's avatar
unknown committed
1698
/*
1699
  Query_log_event::print()
unknown's avatar
unknown committed
1700
*/
1701

1702
#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
1703
void Query_log_event::print_query_header(FILE* file,
unknown's avatar
unknown committed
1704
					 PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
1705
{
1706
  // TODO: print the catalog ??
1707
  char buff[40],*end;				// Enough for SET TIMESTAMP
1708 1709 1710
  bool different_db= 1;
  uint32 tmp;

unknown's avatar
unknown committed
1711
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
1712
  {
unknown's avatar
unknown committed
1713
    print_header(file, print_event_info);
unknown's avatar
unknown committed
1714 1715
    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
1716 1717
  }

unknown's avatar
unknown committed
1718
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db)
1719
  {
unknown's avatar
unknown committed
1720 1721
    if (different_db= memcmp(print_event_info->db, db, db_len + 1))
      memcpy(print_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
1722
    if (db[0] && different_db) 
1723
      fprintf(file, "use %s%s\n", db, print_event_info->delimiter);
1724
  }
1725

1726
  end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
1727
  end= strmov(end, print_event_info->delimiter);
1728 1729
  *end++='\n';
  my_fwrite(file, (byte*) buff, (uint) (end-buff),MYF(MY_NABP | MY_WME));
unknown's avatar
unknown committed
1730
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
1731 1732
    fprintf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
            (ulong)thread_id, print_event_info->delimiter);
1733

1734
  /*
1735 1736 1737
    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).
1738 1739 1740 1741
  */
  if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */
  {
    /* tmp is a bitmask of bits which have changed. */
unknown's avatar
unknown committed
1742
    if (likely(print_event_info->flags2_inited)) 
1743
      /* All bits which have changed */
unknown's avatar
unknown committed
1744
      tmp= (print_event_info->flags2) ^ flags2;
1745 1746
    else /* that's the first Query event we read */
    {
unknown's avatar
unknown committed
1747
      print_event_info->flags2_inited= 1;
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
      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);
1761
      fprintf(file,"%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
1762
      print_event_info->flags2= flags2;
1763 1764 1765 1766
    }
  }

  /*
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
    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.
1777 1778 1779 1780
  */

  if (likely(sql_mode_inited))
  {
unknown's avatar
unknown committed
1781
    if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */
1782
    {
unknown's avatar
unknown committed
1783
      print_event_info->sql_mode_inited= 1;
1784
      /* force a difference to force write */
unknown's avatar
unknown committed
1785
      print_event_info->sql_mode= ~sql_mode;
1786
    }
unknown's avatar
unknown committed
1787
    if (unlikely(print_event_info->sql_mode != sql_mode))
1788
    {
1789 1790
      fprintf(file,"SET @@session.sql_mode=%lu%s\n",
              (ulong)sql_mode, print_event_info->delimiter);
unknown's avatar
unknown committed
1791
      print_event_info->sql_mode= sql_mode;
1792 1793
    }
  }
unknown's avatar
unknown committed
1794 1795
  if (print_event_info->auto_increment_increment != auto_increment_increment ||
      print_event_info->auto_increment_offset != auto_increment_offset)
1796
  {
1797 1798 1799
    fprintf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n",
            auto_increment_increment,auto_increment_offset,
            print_event_info->delimiter);
unknown's avatar
unknown committed
1800 1801
    print_event_info->auto_increment_increment= auto_increment_increment;
    print_event_info->auto_increment_offset=    auto_increment_offset;
1802 1803
  }

1804 1805
  /* TODO: print the catalog when we feature SET CATALOG */

1806 1807
  if (likely(charset_inited))
  {
unknown's avatar
unknown committed
1808
    if (unlikely(!print_event_info->charset_inited)) /* first Query event */
1809
    {
unknown's avatar
unknown committed
1810 1811
      print_event_info->charset_inited= 1;
      print_event_info->charset[0]= ~charset[0]; // force a difference to force write
1812
    }
unknown's avatar
unknown committed
1813
    if (unlikely(bcmp(print_event_info->charset, charset, 6)))
1814
    {
1815 1816 1817
      CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
      if (cs_info)
      {
1818 1819 1820
        /* for mysql client */
        fprintf(file, "/*!\\C %s */%s\n",
                cs_info->csname, print_event_info->delimiter);
1821
      }
1822 1823 1824 1825
      fprintf(file,"SET "
              "@@session.character_set_client=%d,"
              "@@session.collation_connection=%d,"
              "@@session.collation_server=%d"
1826
              "%s\n",
1827 1828
              uint2korr(charset),
              uint2korr(charset+2),
1829 1830
              uint2korr(charset+4),
              print_event_info->delimiter);
unknown's avatar
unknown committed
1831
      memcpy(print_event_info->charset, charset, 6);
1832 1833
    }
  }
1834 1835
  if (time_zone_len)
  {
unknown's avatar
unknown committed
1836
    if (bcmp(print_event_info->time_zone_str, time_zone_str, time_zone_len+1))
1837
    {
1838 1839
      fprintf(file,"SET @@session.time_zone='%s'%s\n",
              time_zone_str, print_event_info->delimiter);
unknown's avatar
unknown committed
1840
      memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
1841 1842
    }
  }
1843 1844
  if (lc_time_names_number != print_event_info->lc_time_names_number)
  {
unknown's avatar
unknown committed
1845 1846
    fprintf(file, "SET @@session.lc_time_names=%d%s\n",
            lc_time_names_number, print_event_info->delimiter);
1847 1848
    print_event_info->lc_time_names_number= lc_time_names_number;
  }
1849 1850 1851 1852 1853 1854 1855 1856 1857 1858
  if (charset_database_number != print_event_info->charset_database_number)
  {
    if (charset_database_number)
      fprintf(file, "SET @@session.collation_database=%d%s\n",
              charset_database_number, print_event_info->delimiter);
    else
      fprintf(file, "SET @@session.collation_database=DEFAULT%s\n",
              print_event_info->delimiter);
    print_event_info->charset_database_number= charset_database_number;
  }
unknown's avatar
unknown committed
1859 1860
}

1861

unknown's avatar
unknown committed
1862
void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
1863
{
unknown's avatar
unknown committed
1864
  print_query_header(file, print_event_info);
unknown's avatar
unknown committed
1865
  my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
1866
  fprintf(file, "\n%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
1867
}
unknown's avatar
unknown committed
1868
#endif /* MYSQL_CLIENT */
1869

unknown's avatar
unknown committed
1870

unknown's avatar
unknown committed
1871
/*
1872
  Query_log_event::exec_event()
unknown's avatar
unknown committed
1873
*/
unknown's avatar
unknown committed
1874

unknown's avatar
SCRUM  
unknown committed
1875
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892

static const char *rewrite_db(const char *db)
{
  if (replicate_rewrite_db.is_empty() || db == NULL)
    return db;
  I_List_iterator<i_string_pair> it(replicate_rewrite_db);
  i_string_pair* tmp;

  while ((tmp=it++))
  {
    if (strcmp(tmp->key, db) == 0)
      return tmp->val;
  }
  return db;
}


1893
int Query_log_event::exec_event(struct st_relay_log_info* rli)
unknown's avatar
unknown committed
1894 1895 1896 1897 1898
{
  return exec_event(rli, query, q_len);
}


1899 1900
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
1901
{
1902
  const char *new_db= rewrite_db(db);
unknown's avatar
unknown committed
1903
  int expected_error,actual_error= 0;
1904 1905 1906 1907 1908 1909
  /*
    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.
  */
1910
  thd->catalog= catalog_len ? (char *) catalog : (char *)"";
1911
  thd->set_db(new_db, strlen(new_db));          /* allocates a copy of 'db' */
1912 1913
  thd->variables.auto_increment_increment= auto_increment_increment;
  thd->variables.auto_increment_offset=    auto_increment_offset;
unknown's avatar
unknown committed
1914

1915
  /*
1916 1917 1918 1919 1920 1921 1922 1923
    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.
1924
  */
unknown's avatar
unknown committed
1925
  rli->future_group_master_log_pos= log_pos;
1926 1927
  DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));

unknown's avatar
unknown committed
1928
  clear_all_errors(thd, rli);
1929

unknown's avatar
unknown committed
1930 1931 1932 1933 1934 1935 1936 1937 1938 1939
  /*
    Note:   We do not need to execute reset_one_shot_variables() if this
            db_ok() test fails.
    Reason: The db stored in binlog events is the same for SET and for
            its companion query.  If the SET is ignored because of
            db_ok(), the companion query will also be ignored, and if
            the companion query is ignored in the db_ok() test of
            ::exec_event(), then the companion SET also have so we
            don't need to reset_one_shot_variables().
  */
1940 1941 1942
  if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
  {
    thd->set_time((time_t)when);
unknown's avatar
unknown committed
1943 1944
    thd->query_length= q_len_arg;
    thd->query= (char*)query_arg;
unknown's avatar
unknown committed
1945
    VOID(pthread_mutex_lock(&LOCK_thread_count));
1946
    thd->query_id = next_query_id();
1947
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
1948
    thd->variables.pseudo_thread_id= thread_id;		// for temp tables
unknown's avatar
unknown committed
1949
    DBUG_PRINT("query",("%s",thd->query));
1950

unknown's avatar
unknown committed
1951
    if (ignored_error_code((expected_error= error_code)) ||
1952
	!check_expected_error(thd,rli,expected_error))
1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988
    {
      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))))
          {
            /*
1989 1990 1991 1992
              We updated the thd->variables with nonsensical values (0). Let's
              set them to something safe (i.e. which avoids crash), and we'll
              stop with EE_UNKNOWN_CHARSET in compare_errors (unless set to
              ignore this error).
1993
            */
1994
            set_slave_thread_default_charset(thd, rli);
1995 1996 1997 1998 1999
            goto compare_errors;
          }
          thd->update_charset(); // for the charset change to take effect
        }
      }
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
      if (time_zone_len)
      {
        String tmp(time_zone_str, time_zone_len, &my_charset_bin);
        if (!(thd->variables.time_zone=
              my_tz_find_with_opening_tz_tables(thd, &tmp)))
        {
          my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
          thd->variables.time_zone= global_system_variables.time_zone;
          goto compare_errors;
        }
      }
2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023
      if (lc_time_names_number)
      {
        if (!(thd->variables.lc_time_names=
              my_locale_by_number(lc_time_names_number)))
        {
          my_printf_error(ER_UNKNOWN_ERROR,
                      "Unknown locale: '%d'", MYF(0), lc_time_names_number);
          thd->variables.lc_time_names= &my_locale_en_US;
          goto compare_errors;
        }
      }
      else
        thd->variables.lc_time_names= &my_locale_en_US;
2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038
      if (charset_database_number)
      {
        CHARSET_INFO *cs;
        if (!(cs= get_charset(charset_database_number, MYF(0))))
        {
          char buf[20];
          int10_to_str((int) charset_database_number, buf, -10);
          my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);
          goto compare_errors;
        }
        thd->variables.collation_database= cs;
      }
      else
        thd->variables.collation_database= thd->db_charset;
      
2039
      /* Execute the query (note that we bypass dispatch_command()) */
2040 2041
      const char* found_semicolon= NULL;
      mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
2042 2043

    }
unknown's avatar
unknown committed
2044 2045
    else
    {
unknown's avatar
unknown committed
2046
      /*
unknown's avatar
unknown committed
2047 2048 2049 2050 2051
        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
2052
      */
unknown's avatar
unknown committed
2053
      if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
unknown's avatar
unknown committed
2054 2055
        clear_all_errors(thd, rli);        /* Can ignore query */
      else
2056
      {
unknown's avatar
unknown committed
2057
        slave_print_error(rli,expected_error, 
unknown's avatar
unknown committed
2058
                          "\
unknown's avatar
unknown committed
2059
Query partially completed on the master (error on master: %d) \
unknown's avatar
unknown committed
2060 2061 2062
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
2063
START SLAVE; . Query: '%s'", expected_error, thd->query);
unknown's avatar
unknown committed
2064 2065 2066 2067
        thd->query_error= 1;
      }
      goto end;
    }
2068

2069 2070 2071 2072
    /* 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);

2073
compare_errors:
unknown's avatar
unknown committed
2074 2075

     /*
unknown's avatar
unknown committed
2076 2077 2078 2079
      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",
2080
 		       expected_error, thd->net.last_errno));
unknown's avatar
unknown committed
2081
    if ((expected_error != (actual_error= thd->net.last_errno)) &&
2082 2083 2084
 	expected_error &&
 	!ignored_error_code(actual_error) &&
 	!ignored_error_code(expected_error))
unknown's avatar
unknown committed
2085 2086
    {
      slave_print_error(rli, 0,
2087
 			"\
unknown's avatar
unknown committed
2088
Query caused different errors on master and slave. \
unknown's avatar
unknown committed
2089
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
unknown's avatar
unknown committed
2090
Default database: '%s'. Query: '%s'",
unknown's avatar
unknown committed
2091 2092 2093 2094
			ER_SAFE(expected_error),
			expected_error,
			actual_error ? thd->net.last_error: "no error",
			actual_error,
unknown's avatar
unknown committed
2095
			print_slave_db_safe(db), query_arg);
unknown's avatar
unknown committed
2096 2097 2098 2099 2100 2101
      thd->query_error= 1;
    }
    /*
      If we get the same error code as expected, or they should be ignored. 
    */
    else if (expected_error == actual_error ||
2102
 	     ignored_error_code(actual_error))
unknown's avatar
unknown committed
2103 2104 2105
    {
      DBUG_PRINT("info",("error ignored"));
      clear_all_errors(thd, rli);
2106
      thd->killed= THD::NOT_KILLED;
unknown's avatar
unknown committed
2107 2108 2109
    }
    /*
      Other cases: mostly we expected no error and get one.
unknown's avatar
unknown committed
2110
    */
unknown's avatar
unknown committed
2111 2112 2113
    else if (thd->query_error || thd->is_fatal_error)
    {
      slave_print_error(rli,actual_error,
unknown's avatar
unknown committed
2114
			"Error '%s' on query. Default database: '%s'. Query: '%s'",
unknown's avatar
unknown committed
2115 2116
			(actual_error ? thd->net.last_error :
			 "unexpected success or fatal error"),
unknown's avatar
unknown committed
2117
			print_slave_db_safe(thd->db), query_arg);
unknown's avatar
unknown committed
2118 2119
      thd->query_error= 1;
    }
2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133

    /*
      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...
2134

2135 2136 2137 2138 2139 2140 2141
      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
2142 2143
  } /* End of if (db_ok(... */

unknown's avatar
unknown committed
2144
end:
2145
  VOID(pthread_mutex_lock(&LOCK_thread_count));
2146 2147 2148 2149 2150 2151
  /*
    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
2152 2153 2154
    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.
2155
  */
unknown's avatar
unknown committed
2156
  thd->catalog= 0;
2157
  thd->set_db(NULL, 0);                 /* will free the current database */
2158
  DBUG_PRINT("info", ("end: query= 0"));
2159
  thd->query= 0;			// just to be sure
unknown's avatar
unknown committed
2160
  thd->query_length= 0;
2161
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
2162
  close_thread_tables(thd);      
unknown's avatar
unknown committed
2163
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
2164 2165 2166 2167 2168 2169 2170
  /*
    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 : 
2171
          (thd->one_shot_set ? (rli->inc_event_relay_log_pos(),0) :
2172
           Log_event::exec_event(rli))); 
unknown's avatar
unknown committed
2173
}
unknown's avatar
SCRUM  
unknown committed
2174
#endif
unknown's avatar
unknown committed
2175

unknown's avatar
unknown committed
2176

2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191
/**************************************************************************
	Muted_query_log_event methods
**************************************************************************/

#ifndef MYSQL_CLIENT
/*
  Muted_query_log_event::Muted_query_log_event()
*/
Muted_query_log_event::Muted_query_log_event()
  :Query_log_event()
{
}
#endif


unknown's avatar
unknown committed
2192
/**************************************************************************
2193
	Start_log_event_v3 methods
unknown's avatar
unknown committed
2194
**************************************************************************/
2195

2196 2197 2198 2199 2200 2201 2202 2203
#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
2204
/*
2205
  Start_log_event_v3::pack_info()
unknown's avatar
unknown committed
2206
*/
2207

unknown's avatar
SCRUM  
unknown committed
2208
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2209
void Start_log_event_v3::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
2210
{
2211 2212 2213 2214
  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
2215 2216
  pos= int10_to_str(binlog_version, pos, 10);
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
unknown's avatar
unknown committed
2217
}
unknown's avatar
SCRUM  
unknown committed
2218
#endif
2219 2220


unknown's avatar
unknown committed
2221
/*
2222
  Start_log_event_v3::print()
unknown's avatar
unknown committed
2223
*/
unknown's avatar
unknown committed
2224 2225

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
2226
void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2227
{
unknown's avatar
unknown committed
2228
  if (!print_event_info->short_form)
2229
  {
unknown's avatar
unknown committed
2230
    print_header(file, print_event_info);
2231 2232 2233 2234 2235 2236
    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);
2237 2238 2239
    if (flags & LOG_EVENT_BINLOG_IN_USE_F)
      fprintf(file, "# Warning: this binlog was not closed properly. "
              "Most probably mysqld crashed writing it.\n");
2240
  }
2241 2242
  if (!artificial_event && created)
  {
2243
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
2244 2245 2246 2247 2248 2249
    /*
      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).
    */
2250
    fprintf(file,"RESET CONNECTION%s\n", print_event_info->delimiter);
2251
#else
2252
    fprintf(file,"ROLLBACK%s\n", print_event_info->delimiter);
2253
#endif
2254
  }
unknown's avatar
unknown committed
2255 2256
  fflush(file);
}
unknown's avatar
unknown committed
2257
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
2258

unknown's avatar
unknown committed
2259
/*
2260
  Start_log_event_v3::Start_log_event_v3()
unknown's avatar
unknown committed
2261
*/
2262

2263 2264 2265
Start_log_event_v3::Start_log_event_v3(const char* buf,
                                       const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
2266
{
2267 2268
  buf+= description_event->common_header_len;
  binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
2269 2270
  memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
	 ST_SERVER_VER_LEN);
unknown's avatar
unknown committed
2271 2272
  // prevent overrun if log is corrupted on disk
  server_version[ST_SERVER_VER_LEN-1]= 0;
2273 2274 2275
  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
2276 2277
}

2278

unknown's avatar
unknown committed
2279
/*
2280
  Start_log_event_v3::write()
unknown's avatar
unknown committed
2281
*/
2282

2283
#ifndef MYSQL_CLIENT
2284
bool Start_log_event_v3::write(IO_CACHE* file)
2285
{
2286
  char buff[START_V3_HEADER_LEN];
2287 2288 2289
  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);
2290 2291
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, (byte*) buff, sizeof(buff)));
2292
}
2293
#endif
2294

2295

unknown's avatar
unknown committed
2296
/*
2297
  Start_log_event_v3::exec_event()
2298 2299 2300 2301

  The master started

  IMPLEMENTATION
unknown's avatar
unknown committed
2302 2303 2304 2305
    - 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).
2306 2307

  TODO
2308 2309 2310 2311 2312
    - 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
2313 2314
*/

unknown's avatar
SCRUM  
unknown committed
2315
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2316
int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
2317
{
2318
  DBUG_ENTER("Start_log_event_v3::exec_event");
2319
  switch (binlog_version)
2320 2321 2322 2323 2324 2325 2326 2327
  {
  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
2328
    */
2329 2330 2331 2332 2333
    if (created)
    {
      close_temporary_tables(thd);
      cleanup_load_tmpdir();
    }
unknown's avatar
unknown committed
2334 2335
    break;

2336
    /*
unknown's avatar
unknown committed
2337 2338
       Now the older formats; in that case load_tmpdir is cleaned up by the I/O
       thread.
unknown's avatar
unknown committed
2339
    */
2340
  case 1:
2341
    if (strncmp(rli->relay_log.description_event_for_exec->server_version,
2342 2343 2344 2345 2346 2347 2348 2349
                "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
2350
    /*
2351 2352 2353
      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
2354 2355 2356 2357
    */
    break;
  default:
    /* this case is impossible */
2358
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2359
  }
2360
  DBUG_RETURN(Log_event::exec_event(rli));
2361
}
unknown's avatar
unknown committed
2362
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2363

2364 2365 2366 2367 2368 2369 2370 2371 2372
/***************************************************************************
       Format_description_log_event methods
****************************************************************************/

/*
  Format_description_log_event 1st ctor.

  SYNOPSIS
    Format_description_log_event::Format_description_log_event
2373
      binlog_version              the binlog version for which we want to build
2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387
                                  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::
2388
Format_description_log_event(uint8 binlog_ver, const char* server_ver)
2389 2390 2391 2392 2393 2394 2395
  :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);
unknown's avatar
unknown committed
2396 2397
    DBUG_EXECUTE_IF("pretend_version_50034_in_binlog",
                    strmov(server_version, "5.0.34"););
2398 2399 2400 2401
    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),
2402
                                       MYF(MY_ZEROFILL));
2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419
    /*
      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
2420 2421
      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;
2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435
    }
    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 :
2436
      LOG_EVENT_MINIMAL_HEADER_LEN;
2437 2438 2439 2440 2441 2442 2443 2444 2445
    /*
      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),
2446
                                       MYF(0));
2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
    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;
  }
unknown's avatar
unknown committed
2469
  calc_server_version_split();
2470 2471 2472 2473 2474 2475 2476 2477
}


/*
  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
2478
  post-header starts.
2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493
  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*
2494
                             description_event)
2495 2496 2497 2498 2499 2500 2501 2502 2503
  :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",
2504
                      common_header_len, number_of_event_types));
2505 2506 2507
  /* 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*
2508
                                      sizeof(*post_header_len), MYF(0));
unknown's avatar
unknown committed
2509
  calc_server_version_split();
2510 2511 2512
  DBUG_VOID_RETURN;
}

2513
#ifndef MYSQL_CLIENT
2514 2515 2516 2517 2518 2519 2520 2521 2522 2523
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);
2524
  buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
2525 2526 2527 2528 2529
  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)));
}
2530
#endif
2531

2532 2533 2534
/*
  SYNOPSIS
    Format_description_log_event::exec_event()
2535

2536 2537 2538 2539 2540 2541 2542 2543 2544 2545
  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");
2546 2547

  /* save the information describing this binlog */
2548 2549 2550
  delete rli->relay_log.description_event_for_exec;
  rli->relay_log.description_event_for_exec= this;

unknown's avatar
unknown committed
2551
#ifdef USING_TRANSACTIONS
2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572
  /*
    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);
  }
unknown's avatar
unknown committed
2573
#endif
2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601
  /*
    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
2602 2603 2604
    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.
2605 2606 2607 2608 2609
  */
  DBUG_RETURN(Start_log_event_v3::exec_event(rli));
}
#endif

unknown's avatar
unknown committed
2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640

/**
   Splits the event's 'server_version' string into three numeric pieces stored
   into 'server_version_split':
   X.Y.Zabc (X,Y,Z numbers, a not a digit) -> {X,Y,Z}
   X.Yabc -> {X,Y,0}
   Xabc -> {X,0,0}
   'server_version_split' is then used for lookups to find if the server which
   created this event has some known bug.
*/
void Format_description_log_event::calc_server_version_split()
{
  char *p= server_version, *r;
  ulong number;
  for (uint i= 0; i<=2; i++)
  {
    number= strtoul(p, &r, 10);
    server_version_split[i]= (uchar)number;
    DBUG_ASSERT(number < 256); // fit in uchar
    p= r;
    DBUG_ASSERT(!((i == 0) && (*r != '.'))); // should be true in practice
    if (*r == '.')
      p++; // skip the dot
  }
  DBUG_PRINT("info",("Format_description_log_event::server_version_split:"
                     " '%s' %d %d %d", server_version,
                     server_version_split[0],
                     server_version_split[1], server_version_split[2]));
}


2641
  /**************************************************************************
2642
        Load_log_event methods
2643
   General note about Load_log_event: the binlogging of LOAD DATA INFILE is
2644
   going to be changed in 5.0 (or maybe in 5.1; not decided yet).
2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656
   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).
  **************************************************************************/
2657

unknown's avatar
unknown committed
2658
/*
2659
  Load_log_event::pack_info()
unknown's avatar
unknown committed
2660
*/
2661

unknown's avatar
SCRUM  
unknown committed
2662
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
2663
uint Load_log_event::get_query_buffer_length()
2664
{
unknown's avatar
unknown committed
2665
  return
2666 2667
    5 + db_len + 3 +                        // "use DB; "
    18 + fname_len + 2 +                    // "LOAD DATA INFILE 'file''"
unknown's avatar
unknown committed
2668
    7 +					    // LOCAL
2669
    9 +                                     // " REPLACE or IGNORE "
2670
    13 + table_name_len*2 +                 // "INTO TABLE `table`"
2671 2672 2673 2674
    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'"
2675 2676
    19 + sql_ex.line_start_len*4 + 2 +      // " LINES STARTING BY 'str'"
    15 + 22 +                               // " IGNORE xxx  LINES"
2677
    3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
unknown's avatar
unknown committed
2678
}
2679

unknown's avatar
unknown committed
2680 2681 2682 2683 2684 2685 2686

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)
2687
  {
2688 2689
    pos= strmov(pos, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
2690
    pos= strmov(pos+db_len, "`; ");
2691
  }
2692

unknown's avatar
unknown committed
2693
  pos= strmov(pos, "LOAD DATA ");
unknown's avatar
unknown committed
2694 2695 2696 2697

  if (fn_start)
    *fn_start= pos;

unknown's avatar
unknown committed
2698 2699 2700
  if (check_fname_outside_temp_buf())
    pos= strmov(pos, "LOCAL ");
  pos= strmov(pos, "INFILE '");
2701
  memcpy(pos, fname, fname_len);
unknown's avatar
unknown committed
2702
  pos= strmov(pos+fname_len, "' ");
2703

unknown's avatar
unknown committed
2704
  if (sql_ex.opt_flags & REPLACE_FLAG)
2705
    pos= strmov(pos, " REPLACE ");
unknown's avatar
unknown committed
2706
  else if (sql_ex.opt_flags & IGNORE_FLAG)
2707 2708
    pos= strmov(pos, " IGNORE ");

unknown's avatar
unknown committed
2709 2710 2711 2712 2713 2714
  pos= strmov(pos ,"INTO");

  if (fn_end)
    *fn_end= pos;

  pos= strmov(pos ," TABLE `");
2715 2716 2717
  memcpy(pos, table_name, table_name_len);
  pos+= table_name_len;

unknown's avatar
unknown committed
2718
  /* We have to create all optinal fields as the default is not empty */
2719
  pos= strmov(pos, "` FIELDS TERMINATED BY ");
unknown's avatar
unknown committed
2720 2721 2722 2723 2724
  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);
2725

unknown's avatar
unknown committed
2726 2727
  pos= strmov(pos, " ESCAPED BY ");
  pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len);
2728

unknown's avatar
unknown committed
2729 2730
  pos= strmov(pos, " LINES TERMINATED BY ");
  pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len);
2731 2732
  if (sql_ex.line_start_len)
  {
2733
    pos= strmov(pos, " STARTING BY ");
2734
    pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len);
2735
  }
2736

unknown's avatar
unknown committed
2737
  if ((long) skip_lines > 0)
2738 2739
  {
    pos= strmov(pos, " IGNORE ");
unknown's avatar
unknown committed
2740
    pos= longlong10_to_str((longlong) skip_lines, pos, 10);
2741 2742
    pos= strmov(pos," LINES ");    
  }
2743 2744 2745 2746

  if (num_fields)
  {
    uint i;
unknown's avatar
unknown committed
2747
    const char *field= fields;
2748
    pos= strmov(pos, " (");
2749 2750 2751
    for (i = 0; i < num_fields; i++)
    {
      if (i)
unknown's avatar
unknown committed
2752 2753 2754 2755
      {
        *pos++= ' ';
        *pos++= ',';
      }
2756
      memcpy(pos, field, field_lens[i]);
unknown's avatar
unknown committed
2757 2758
      pos+=   field_lens[i];
      field+= field_lens[i]  + 1;
2759
    }
2760
    *pos++= ')';
2761
  }
2762

unknown's avatar
unknown committed
2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774
  *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
2775
  my_free(buf, MYF(0));
2776
}
unknown's avatar
unknown committed
2777
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2778

2779

2780 2781
#ifndef MYSQL_CLIENT

unknown's avatar
unknown committed
2782
/*
2783
  Load_log_event::write_data_header()
unknown's avatar
unknown committed
2784
*/
2785

2786
bool Load_log_event::write_data_header(IO_CACHE* file)
2787
{
2788
  char buf[LOAD_HEADER_LEN];
unknown's avatar
unknown committed
2789
  int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
2790 2791 2792 2793 2794
  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);
2795
  return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN) != 0;
2796
}
2797

2798

unknown's avatar
unknown committed
2799
/*
2800
  Load_log_event::write_data_body()
unknown's avatar
unknown committed
2801
*/
2802

2803
bool Load_log_event::write_data_body(IO_CACHE* file)
2804
{
2805 2806 2807
  if (sql_ex.write_data(file))
    return 1;
  if (num_fields && fields && field_lens)
2808
  {
2809 2810 2811
    if (my_b_safe_write(file, (byte*)field_lens, num_fields) ||
	my_b_safe_write(file, (byte*)fields, field_block_len))
      return 1;
2812
  }
2813 2814 2815
  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));
2816 2817
}

2818

unknown's avatar
unknown committed
2819
/*
2820
  Load_log_event::Load_log_event()
unknown's avatar
unknown committed
2821
*/
2822

unknown's avatar
unknown committed
2823 2824 2825
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
2826
			       enum enum_duplicates handle_dup,
2827
			       bool ignore, bool using_trans)
2828 2829 2830 2831
  :Log_event(thd_arg,
             (thd_arg->tmp_table_used || thd_arg->thread_specific_used) ?
               LOG_EVENT_THREAD_SPECIFIC_F : 0,
             using_trans),
2832
   thread_id(thd_arg->thread_id),
2833
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
unknown's avatar
unknown committed
2834 2835
   num_fields(0),fields(0),
   field_lens(0),field_block_len(0),
unknown's avatar
unknown committed
2836
   table_name(table_name_arg ? table_name_arg : ""),
2837
   db(db_arg), fname(ex->file_name), local_fname(FALSE)
unknown's avatar
unknown committed
2838 2839 2840
{
  time_t end_time;
  time(&end_time);
2841
  exec_time = (ulong) (end_time  - thd_arg->start_time);
2842 2843 2844
  /* 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
2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857
  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;
2858
    
unknown's avatar
unknown committed
2859
  if (ex->dumpfile)
unknown's avatar
unknown committed
2860
    sql_ex.opt_flags|= DUMPFILE_FLAG;
unknown's avatar
unknown committed
2861
  if (ex->opt_enclosed)
unknown's avatar
unknown committed
2862
    sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
2863

unknown's avatar
unknown committed
2864
  sql_ex.empty_flags= 0;
2865

2866
  switch (handle_dup) {
unknown's avatar
unknown committed
2867
  case DUP_REPLACE:
unknown's avatar
unknown committed
2868
    sql_ex.opt_flags|= REPLACE_FLAG;
unknown's avatar
unknown committed
2869 2870 2871 2872
    break;
  case DUP_UPDATE:				// Impossible here
  case DUP_ERROR:
    break;	
unknown's avatar
unknown committed
2873
  }
2874 2875
  if (ignore)
    sql_ex.opt_flags|= IGNORE_FLAG;
2876

unknown's avatar
unknown committed
2877 2878 2879 2880 2881 2882 2883 2884 2885 2886
  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;
2887
    
unknown's avatar
unknown committed
2888
  skip_lines = ex->skip_lines;
2889

unknown's avatar
unknown committed
2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900
  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);
2901 2902
  }

unknown's avatar
unknown committed
2903 2904 2905
  field_lens = (const uchar*)field_lens_buf.ptr();
  fields = fields_buf.ptr();
}
unknown's avatar
unknown committed
2906
#endif /* !MYSQL_CLIENT */
2907

2908

2909
/*
2910
  Load_log_event::Load_log_event()
2911

unknown's avatar
unknown committed
2912 2913 2914
  NOTE
    The caller must do buf[event_len] = 0 before he starts using the
    constructed event.
2915 2916
*/

2917 2918 2919 2920
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
2921
   table_name(0), db(0), fname(0), local_fname(FALSE)
unknown's avatar
unknown committed
2922
{
unknown's avatar
unknown committed
2923
  DBUG_ENTER("Load_log_event");
2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935
  /*
    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
2936
  DBUG_VOID_RETURN;
2937 2938
}

2939

unknown's avatar
unknown committed
2940
/*
2941
  Load_log_event::copy_log_event()
unknown's avatar
unknown committed
2942
*/
2943

2944
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
2945 2946
                                   int body_offset,
                                   const Format_description_log_event *description_event)
2947
{
2948
  DBUG_ENTER("Load_log_event::copy_log_event");
2949
  uint data_len;
2950
  char* buf_end = (char*)buf + event_len;
2951 2952
  /* this is the beginning of the post-header */
  const char* data_head = buf + description_event->common_header_len;
unknown's avatar
unknown committed
2953
  slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
2954 2955 2956 2957 2958
  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
2959
	  
unknown's avatar
unknown committed
2960
  if ((int) event_len < body_offset)
unknown's avatar
unknown committed
2961
    DBUG_RETURN(1);
2962 2963 2964 2965
  /*
    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.
  */
2966 2967 2968
  if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
                                        buf_end,
                                        buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
unknown's avatar
unknown committed
2969
    DBUG_RETURN(1);
2970
  
2971
  data_len = event_len - body_offset;
2972
  if (num_fields > data_len) // simple sanity check against corruption
unknown's avatar
unknown committed
2973
    DBUG_RETURN(1);
2974
  for (uint i = 0; i < num_fields; i++)
2975
    field_block_len += (uint)field_lens[i] + 1;
2976

unknown's avatar
unknown committed
2977 2978 2979 2980
  fields = (char*)field_lens + num_fields;
  table_name  = fields + field_block_len;
  db = table_name + table_name_len + 1;
  fname = db + db_len + 1;
2981 2982
  fname_len = strlen(fname);
  // null termination is accomplished by the caller doing buf[event_len]=0
2983

unknown's avatar
unknown committed
2984
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2985 2986 2987
}


unknown's avatar
unknown committed
2988
/*
2989
  Load_log_event::print()
unknown's avatar
unknown committed
2990
*/
2991 2992

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
2993
void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2994
{
unknown's avatar
unknown committed
2995
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
2996 2997
}

unknown's avatar
unknown committed
2998

unknown's avatar
unknown committed
2999
void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
3000
			   bool commented)
unknown's avatar
unknown committed
3001
{
unknown's avatar
unknown committed
3002
  DBUG_ENTER("Load_log_event::print");
unknown's avatar
unknown committed
3003
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
3004
  {
unknown's avatar
unknown committed
3005
    print_header(file, print_event_info);
3006
    fprintf(file, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
unknown's avatar
unknown committed
3007 3008 3009
	    thread_id, exec_time);
  }

3010
  bool different_db= 1;
3011
  if (db)
unknown's avatar
unknown committed
3012
  {
3013 3014 3015 3016 3017 3018
    /*
      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.
    */
unknown's avatar
unknown committed
3019
    if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) &&
3020
        !commented)
unknown's avatar
unknown committed
3021
      memcpy(print_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
3022
  }
3023
  
3024
  if (db && db[0] && different_db)
3025
    fprintf(file, "%suse %s%s\n", 
unknown's avatar
unknown committed
3026
            commented ? "# " : "",
3027
            db, print_event_info->delimiter);
unknown's avatar
unknown committed
3028

3029
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
3030 3031 3032
    fprintf(file,"%sSET @@session.pseudo_thread_id=%lu%s\n",
            commented ? "# " : "", (ulong)thread_id,
            print_event_info->delimiter);
unknown's avatar
unknown committed
3033 3034
  fprintf(file, "%sLOAD DATA ",
          commented ? "# " : "");
3035 3036
  if (check_fname_outside_temp_buf())
    fprintf(file, "LOCAL ");
3037
  fprintf(file, "INFILE '%-*s' ", fname_len, fname);
unknown's avatar
unknown committed
3038

unknown's avatar
unknown committed
3039
  if (sql_ex.opt_flags & REPLACE_FLAG)
unknown's avatar
unknown committed
3040
    fprintf(file," REPLACE ");
unknown's avatar
unknown committed
3041
  else if (sql_ex.opt_flags & IGNORE_FLAG)
unknown's avatar
unknown committed
3042 3043
    fprintf(file," IGNORE ");
  
3044
  fprintf(file, "INTO TABLE `%s`", table_name);
unknown's avatar
unknown committed
3045 3046
  fprintf(file, " FIELDS TERMINATED BY ");
  pretty_print_str(file, sql_ex.field_term, sql_ex.field_term_len);
unknown's avatar
unknown committed
3047

unknown's avatar
unknown committed
3048 3049 3050 3051
  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
3052
     
unknown's avatar
unknown committed
3053 3054
  fprintf(file, " ESCAPED BY ");
  pretty_print_str(file, sql_ex.escaped, sql_ex.escaped_len);
unknown's avatar
unknown committed
3055
     
unknown's avatar
unknown committed
3056 3057 3058
  fprintf(file," LINES TERMINATED BY ");
  pretty_print_str(file, sql_ex.line_term, sql_ex.line_term_len);

unknown's avatar
unknown committed
3059

3060
  if (sql_ex.line_start)
unknown's avatar
unknown committed
3061
  {
3062
    fprintf(file," STARTING BY ");
3063
    pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len);
unknown's avatar
unknown committed
3064
  }
3065 3066
  if ((long) skip_lines > 0)
    fprintf(file, " IGNORE %ld LINES", (long) skip_lines);
unknown's avatar
unknown committed
3067

3068 3069 3070 3071
  if (num_fields)
  {
    uint i;
    const char* field = fields;
3072 3073
    fprintf(file, " (");
    for (i = 0; i < num_fields; i++)
unknown's avatar
unknown committed
3074
    {
unknown's avatar
unknown committed
3075
      if (i)
3076 3077
	fputc(',', file);
      fprintf(file, field);
unknown's avatar
unknown committed
3078
	  
3079
      field += field_lens[i]  + 1;
unknown's avatar
unknown committed
3080
    }
3081 3082
    fputc(')', file);
  }
unknown's avatar
unknown committed
3083

3084
  fprintf(file, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
3085
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3086
}
unknown's avatar
unknown committed
3087
#endif /* MYSQL_CLIENT */
3088

3089

unknown's avatar
unknown committed
3090
/*
3091
  Load_log_event::set_fields()
unknown's avatar
unknown committed
3092 3093 3094 3095 3096

  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
3097
*/
3098

3099
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
3100
void Load_log_event::set_fields(const char* affected_db, 
3101 3102
				List<Item> &field_list,
                                Name_resolution_context *context)
unknown's avatar
unknown committed
3103 3104
{
  uint i;
unknown's avatar
unknown committed
3105
  const char* field = fields;
3106
  for (i= 0; i < num_fields; i++)
unknown's avatar
unknown committed
3107
  {
3108 3109
    field_list.push_back(new Item_field(context,
                                        affected_db, table_name, field));
3110
    field+= field_lens[i]  + 1;
unknown's avatar
unknown committed
3111
  }
unknown's avatar
unknown committed
3112
}
unknown's avatar
unknown committed
3113
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
3114 3115


unknown's avatar
SCRUM  
unknown committed
3116
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3117 3118
/*
  Does the data loading job when executing a LOAD DATA on the slave
3119

unknown's avatar
unknown committed
3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133
  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.
3134

unknown's avatar
unknown committed
3135 3136 3137 3138 3139 3140 3141
  DESCRIPTION
    Does the data loading job when executing a LOAD DATA on the slave
 
  RETURN VALUE
    0           Success                                                 
    1    	Failure
*/
3142

unknown's avatar
unknown committed
3143 3144
int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, 
			       bool use_rli_only_for_errors)
3145
{
3146 3147
  const char *new_db= rewrite_db(db);
  thd->set_db(new_db, strlen(new_db));
3148
  DBUG_ASSERT(thd->query == 0);
unknown's avatar
unknown committed
3149
  thd->query_length= 0;                         // Should not be needed
unknown's avatar
unknown committed
3150
  thd->query_error= 0;
unknown's avatar
unknown committed
3151
  clear_all_errors(thd, rli);
3152
  /*
3153
    Usually lex_start() is called by mysql_parse(), but we need it here
3154 3155
    as the present method does not call mysql_parse().
  */
3156 3157 3158
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);

unknown's avatar
unknown committed
3159
  if (!use_rli_only_for_errors)
3160
  {
3161
    /* Saved for InnoDB, see comment in Query_log_event::exec_event() */
unknown's avatar
unknown committed
3162
    rli->future_group_master_log_pos= log_pos;
3163
    DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
3164
  }
3165 3166
 
   /*
unknown's avatar
unknown committed
3167 3168 3169 3170 3171 3172 3173 3174 3175 3176
    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).
unknown's avatar
unknown committed
3177 3178 3179 3180 3181 3182 3183 3184 3185 3186


    Note:   We do not need to execute reset_one_shot_variables() if this
            db_ok() test fails.
    Reason: The db stored in binlog events is the same for SET and for
            its companion query.  If the SET is ignored because of
            db_ok(), the companion query will also be ignored, and if
            the companion query is ignored in the db_ok() test of
            ::exec_event(), then the companion SET also have so we
            don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
3187
  */
3188
  if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
3189
  {
3190 3191
    thd->set_time((time_t)when);
    VOID(pthread_mutex_lock(&LOCK_thread_count));
3192
    thd->query_id = next_query_id();
3193
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
3194 3195 3196 3197 3198 3199
    /*
      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.
    */
unknown's avatar
unknown committed
3200
    mysql_reset_errors(thd, 0);
3201 3202 3203

    TABLE_LIST tables;
    bzero((char*) &tables,sizeof(tables));
unknown's avatar
unknown committed
3204
    tables.db= thd->strmake(thd->db, thd->db_length);
3205
    tables.alias = tables.table_name = (char*) table_name;
3206
    tables.lock_type = TL_WRITE;
unknown's avatar
unknown committed
3207
    tables.updating= 1;
unknown's avatar
unknown committed
3208

3209 3210 3211 3212 3213 3214 3215 3216 3217 3218
    // 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
3219
      char *end;
unknown's avatar
unknown committed
3220
      enum enum_duplicates handle_dup;
3221
      bool ignore= 0;
unknown's avatar
unknown committed
3222 3223
      char *load_data_query;

unknown's avatar
unknown committed
3224
      /*
unknown's avatar
unknown committed
3225 3226
        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
3227
      */
unknown's avatar
unknown committed
3228
      if (!(load_data_query= (char *)thd->alloc(get_query_buffer_length() + 1)))
unknown's avatar
unknown committed
3229
      {
unknown's avatar
unknown committed
3230 3231 3232 3233 3234
        /*
          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
3235
      }
unknown's avatar
unknown committed
3236 3237 3238 3239 3240 3241 3242

      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
3243
      if (sql_ex.opt_flags & REPLACE_FLAG)
3244
      {
unknown's avatar
unknown committed
3245
	handle_dup= DUP_REPLACE;
3246
      }
unknown's avatar
unknown committed
3247
      else if (sql_ex.opt_flags & IGNORE_FLAG)
3248 3249 3250 3251
      {
        ignore= 1;
        handle_dup= DUP_ERROR;
      }
unknown's avatar
unknown committed
3252
      else
unknown's avatar
unknown committed
3253
      {
unknown's avatar
unknown committed
3254
        /*
unknown's avatar
unknown committed
3255
	  When replication is running fine, if it was DUP_ERROR on the
3256
          master then we could choose IGNORE here, because if DUP_ERROR
unknown's avatar
unknown committed
3257
          suceeded on master, and data is identical on the master and slave,
3258
          then there should be no uniqueness errors on slave, so IGNORE is
unknown's avatar
unknown committed
3259
          the same as DUP_ERROR. But in the unlikely case of uniqueness errors
unknown's avatar
unknown committed
3260 3261 3262
          (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
3263 3264

          If reading from net (a 3.23 master), mysql_load() will change this
3265
          to IGNORE.
unknown's avatar
unknown committed
3266 3267
        */
        handle_dup= DUP_ERROR;
unknown's avatar
unknown committed
3268
      }
3269 3270 3271 3272 3273 3274 3275 3276 3277 3278
      /*
        We need to set thd->lex->sql_command and thd->lex->duplicates
        since InnoDB tests these variables to decide if this is a LOAD
        DATA ... REPLACE INTO ... statement even though mysql_parse()
        is not called.  This is not needed in 5.0 since there the LOAD
        DATA ... statement is replicated using mysql_parse(), which
        sets the thd->lex fields correctly.
      */
      thd->lex->sql_command= SQLCOM_LOAD;
      thd->lex->duplicates= handle_dup;
unknown's avatar
unknown committed
3279

unknown's avatar
unknown committed
3280
      sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
3281 3282 3283 3284 3285
      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
3286 3287 3288 3289 3290
      ex.field_term= &field_term;
      ex.enclosed= &enclosed;
      ex.line_term= &line_term;
      ex.line_start= &line_start;
      ex.escaped= &escaped;
3291 3292 3293 3294 3295 3296

      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
3297
      List<Item> field_list;
3298 3299
      thd->lex->select_lex.context.resolve_in_table_list_only(&tables);
      set_fields(tables.db, field_list, &thd->lex->select_lex.context);
unknown's avatar
unknown committed
3300
      thd->variables.pseudo_thread_id= thread_id;
3301 3302 3303 3304 3305 3306 3307 3308 3309
      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
3310
      /*
3311
        It is safe to use tmp_list twice because we are not going to
unknown's avatar
unknown committed
3312 3313
        update it inside mysql_load().
      */
3314 3315
      List<Item> tmp_list;
      if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
3316
                     handle_dup, ignore, net != 0))
unknown's avatar
unknown committed
3317
        thd->query_error= 1;
3318
      if (thd->cuted_fields)
unknown's avatar
unknown committed
3319
      {
unknown's avatar
unknown committed
3320
	/* log_pos is the position of the LOAD event in the master log */
unknown's avatar
unknown committed
3321 3322 3323 3324 3325 3326 3327
        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
3328
      }
3329 3330 3331
      if (net)
        net->pkt_nr= thd->net.pkt_nr;
    }
3332 3333
  }
  else
3334 3335 3336 3337 3338 3339 3340 3341 3342
  {
    /*
      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
3343 3344

error:
3345
  thd->net.vio = 0; 
unknown's avatar
unknown committed
3346
  const char *remember_db= thd->db;
unknown's avatar
unknown committed
3347
  VOID(pthread_mutex_lock(&LOCK_thread_count));
unknown's avatar
unknown committed
3348
  thd->catalog= 0;
3349
  thd->set_db(NULL, 0);                   /* will free the current database */
unknown's avatar
unknown committed
3350
  thd->query= 0;
unknown's avatar
unknown committed
3351
  thd->query_length= 0;
unknown's avatar
unknown committed
3352
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
3353 3354 3355
  close_thread_tables(thd);
  if (thd->query_error)
  {
3356
    /* this err/sql_errno code is copy-paste from net_send_error() */
unknown's avatar
unknown committed
3357 3358 3359 3360 3361 3362 3363 3364 3365
    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
3366
    slave_print_error(rli,sql_errno,"\
unknown's avatar
unknown committed
3367
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
unknown's avatar
unknown committed
3368
		      err, (char*)table_name, print_slave_db_safe(remember_db));
unknown's avatar
unknown committed
3369
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3370 3371
    return 1;
  }
unknown's avatar
unknown committed
3372
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3373
	    
3374
  if (thd->is_fatal_error)
3375
  {
unknown's avatar
unknown committed
3376 3377
    slave_print_error(rli,ER_UNKNOWN_ERROR, "\
Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
unknown's avatar
unknown committed
3378
		      (char*)table_name, print_slave_db_safe(remember_db));
3379 3380 3381
    return 1;
  }

unknown's avatar
unknown committed
3382
  return ( use_rli_only_for_errors ? 0 : Log_event::exec_event(rli) ); 
3383
}
unknown's avatar
SCRUM  
unknown committed
3384
#endif
3385 3386


unknown's avatar
unknown committed
3387
/**************************************************************************
unknown's avatar
unknown committed
3388
  Rotate_log_event methods
unknown's avatar
unknown committed
3389
**************************************************************************/
3390

unknown's avatar
unknown committed
3391
/*
3392
  Rotate_log_event::pack_info()
unknown's avatar
unknown committed
3393
*/
3394

unknown's avatar
SCRUM  
unknown committed
3395
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3396
void Rotate_log_event::pack_info(Protocol *protocol)
3397
{
unknown's avatar
unknown committed
3398
  char buf1[256], buf[22];
unknown's avatar
unknown committed
3399
  String tmp(buf1, sizeof(buf1), log_cs);
3400
  tmp.length(0);
unknown's avatar
unknown committed
3401
  tmp.append(new_log_ident, ident_len);
3402
  tmp.append(STRING_WITH_LEN(";pos="));
unknown's avatar
unknown committed
3403 3404
  tmp.append(llstr(pos,buf));
  protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
3405
}
unknown's avatar
SCRUM  
unknown committed
3406
#endif
3407

3408

unknown's avatar
unknown committed
3409
/*
3410
  Rotate_log_event::print()
unknown's avatar
unknown committed
3411
*/
3412 3413

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3414
void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3415
{
3416
  char buf[22];
3417

unknown's avatar
unknown committed
3418
  if (print_event_info->short_form)
3419
    return;
unknown's avatar
unknown committed
3420
  print_header(file, print_event_info);
3421 3422 3423
  fprintf(file, "\tRotate to ");
  if (new_log_ident)
    my_fwrite(file, (byte*) new_log_ident, (uint)ident_len, 
3424
              MYF(MY_NABP | MY_WME));
3425
  fprintf(file, "  pos: %s", llstr(pos, buf));
3426
  fputc('\n', file);
3427
  fflush(file);
3428
}
unknown's avatar
unknown committed
3429
#endif /* MYSQL_CLIENT */
3430 3431


3432

unknown's avatar
unknown committed
3433
/*
3434
  Rotate_log_event::Rotate_log_event() (2 constructors)
unknown's avatar
unknown committed
3435
*/
3436

3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449

#ifndef MYSQL_CLIENT
Rotate_log_event::Rotate_log_event(THD* thd_arg,
                                   const char* new_log_ident_arg,
                                   uint ident_len_arg, ulonglong pos_arg,
                                   uint flags_arg)
  :Log_event(), new_log_ident(new_log_ident_arg),
   pos(pos_arg),ident_len(ident_len_arg ? ident_len_arg :
                          (uint) strlen(new_log_ident_arg)), flags(flags_arg)
{
#ifndef DBUG_OFF
  char buff[22];
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(THD*,...)");
unknown's avatar
unknown committed
3450 3451
  DBUG_PRINT("enter",("new_log_ident: %s  pos: %s  flags: %lu", new_log_ident_arg,
                      llstr(pos_arg, buff), (ulong) flags));
3452 3453
#endif
  if (flags & DUP_NAME)
3454
    new_log_ident= my_strdup_with_length(new_log_ident_arg,
unknown's avatar
unknown committed
3455
                                         ident_len, MYF(MY_WME));
3456 3457 3458 3459 3460
  DBUG_VOID_RETURN;
}
#endif


3461 3462
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
                                   const Format_description_log_event* description_event)
unknown's avatar
unknown committed
3463
  :Log_event(buf, description_event) ,new_log_ident(0), flags(DUP_NAME)
3464
{
3465
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
3466
  // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
3467 3468
  uint8 header_size= description_event->common_header_len;
  uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
3469 3470
  uint ident_offset;
  if (event_len < header_size)
unknown's avatar
unknown committed
3471
    DBUG_VOID_RETURN;
3472
  buf += header_size;
3473 3474 3475 3476
  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; 
3477
  set_if_smaller(ident_len,FN_REFLEN-1);
3478
  new_log_ident= my_strdup_with_length(buf + ident_offset,
3479 3480
                                       (uint) ident_len,
                                       MYF(MY_WME));
unknown's avatar
unknown committed
3481
  DBUG_VOID_RETURN;
3482
}
3483 3484


unknown's avatar
unknown committed
3485
/*
3486
  Rotate_log_event::write()
unknown's avatar
unknown committed
3487
*/
3488

3489
#ifndef MYSQL_CLIENT
3490
bool Rotate_log_event::write(IO_CACHE* file)
3491
{
3492
  char buf[ROTATE_HEADER_LEN];
unknown's avatar
unknown committed
3493
  int8store(buf + R_POS_OFFSET, pos);
3494 3495 3496
  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));
3497
}
3498
#endif
3499

unknown's avatar
unknown committed
3500
/*
3501 3502
  Rotate_log_event::exec_event()

3503
  Got a rotate log event from the master
3504

3505 3506 3507
  IMPLEMENTATION
    This is mainly used so that we can later figure out the logname and
    position for the master.
3508

3509
    We can't rotate the slave's BINlog as this will cause infinitive rotations
3510
    in a A -> B -> A setup.
3511
    The NOTES below is a wrong comment which will disappear when 4.1 is merged.
3512 3513 3514

  RETURN VALUES
    0	ok
unknown's avatar
unknown committed
3515
*/
3516

unknown's avatar
SCRUM  
unknown committed
3517
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3518
int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
3519
{
3520 3521 3522
  DBUG_ENTER("Rotate_log_event::exec_event");

  pthread_mutex_lock(&rli->data_lock);
3523
  rli->event_relay_log_pos= my_b_tell(rli->cur_log);
unknown's avatar
unknown committed
3524 3525 3526 3527 3528 3529 3530 3531 3532
  /*
    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
3533 3534
    In that case, we don't want to touch the coordinates which correspond to
    the beginning of the transaction.
3535 3536
    Starting from 5.0.0, there also are some rotates from the slave itself, in
    the relay log.
unknown's avatar
unknown committed
3537
  */
unknown's avatar
unknown committed
3538
  if (!(thd->options & OPTION_BEGIN))
unknown's avatar
unknown committed
3539
  {
unknown's avatar
unknown committed
3540 3541
    memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
    rli->notify_group_master_log_name_update();
3542
    rli->group_master_log_pos= pos;
3543 3544
    strmake(rli->group_relay_log_name, rli->event_relay_log_name,
            sizeof(rli->group_relay_log_name) - 1);
3545 3546 3547 3548
    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
3549
                        (ulong) rli->group_master_log_pos));
3550
    /*
3551 3552
      Reset thd->options and sql_mode etc, because this could be the signal of
      a master's downgrade from 5.0 to 4.0.
3553 3554 3555 3556 3557
      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);
3558
    set_slave_thread_default_charset(thd, rli);
3559
    thd->variables.sql_mode= global_system_variables.sql_mode;
3560 3561
    thd->variables.auto_increment_increment=
      thd->variables.auto_increment_offset= 1;
unknown's avatar
unknown committed
3562
  }
3563 3564 3565 3566
  pthread_mutex_unlock(&rli->data_lock);
  pthread_cond_broadcast(&rli->data_cond);
  flush_relay_log_info(rli);
  DBUG_RETURN(0);
3567
}
unknown's avatar
SCRUM  
unknown committed
3568
#endif
3569 3570


unknown's avatar
unknown committed
3571
/**************************************************************************
unknown's avatar
unknown committed
3572
	Intvar_log_event methods
unknown's avatar
unknown committed
3573
**************************************************************************/
3574

unknown's avatar
unknown committed
3575
/*
3576
  Intvar_log_event::pack_info()
unknown's avatar
unknown committed
3577
*/
3578

unknown's avatar
SCRUM  
unknown committed
3579
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3580
void Intvar_log_event::pack_info(Protocol *protocol)
3581
{
unknown's avatar
unknown committed
3582 3583
  char buf[256], *pos;
  pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
unknown's avatar
unknown committed
3584
  *pos++= '=';
unknown's avatar
unknown committed
3585
  pos= longlong10_to_str(val, pos, -10);
unknown's avatar
unknown committed
3586
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
3587
}
unknown's avatar
SCRUM  
unknown committed
3588
#endif
3589

unknown's avatar
unknown committed
3590

unknown's avatar
unknown committed
3591
/*
3592
  Intvar_log_event::Intvar_log_event()
unknown's avatar
unknown committed
3593
*/
3594

3595 3596 3597
Intvar_log_event::Intvar_log_event(const char* buf,
                                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3598
{
3599 3600 3601
  buf+= description_event->common_header_len;
  type= buf[I_TYPE_OFFSET];
  val= uint8korr(buf+I_VAL_OFFSET);
3602 3603
}

3604

unknown's avatar
unknown committed
3605
/*
3606
  Intvar_log_event::get_var_type_name()
unknown's avatar
unknown committed
3607
*/
3608 3609

const char* Intvar_log_event::get_var_type_name()
3610
{
3611 3612 3613 3614 3615
  switch(type) {
  case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID";
  case INSERT_ID_EVENT: return "INSERT_ID";
  default: /* impossible */ return "UNKNOWN";
  }
3616 3617
}

unknown's avatar
unknown committed
3618

unknown's avatar
unknown committed
3619
/*
3620
  Intvar_log_event::write()
unknown's avatar
unknown committed
3621
*/
3622

3623
#ifndef MYSQL_CLIENT
3624
bool Intvar_log_event::write(IO_CACHE* file)
3625
{
3626 3627
  byte buf[9];
  buf[I_TYPE_OFFSET]= (byte) type;
3628
  int8store(buf + I_VAL_OFFSET, val);
3629 3630
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3631
}
3632
#endif
3633

3634

unknown's avatar
unknown committed
3635
/*
3636
  Intvar_log_event::print()
unknown's avatar
unknown committed
3637
*/
3638 3639

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3640
void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3641
{
3642 3643 3644
  char llbuff[22];
  const char *msg;
  LINT_INIT(msg);
3645

unknown's avatar
unknown committed
3646
  if (!print_event_info->short_form)
3647
  {
unknown's avatar
unknown committed
3648
    print_header(file, print_event_info);
3649 3650
    fprintf(file, "\tIntvar\n");
  }
3651

3652 3653 3654 3655 3656 3657 3658 3659
  fprintf(file, "SET ");
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    msg="LAST_INSERT_ID";
    break;
  case INSERT_ID_EVENT:
    msg="INSERT_ID";
    break;
3660 3661 3662 3663
  case INVALID_INT_EVENT:
  default: // cannot happen
    msg="INVALID_INT";
    break;
3664
  }
3665 3666
  fprintf(file, "%s=%s%s\n",
          msg, llstr(val,llbuff), print_event_info->delimiter);
3667
  fflush(file);
3668
}
3669
#endif
3670

3671

unknown's avatar
unknown committed
3672
/*
3673
  Intvar_log_event::exec_event()
unknown's avatar
unknown committed
3674
*/
3675

unknown's avatar
SCRUM  
unknown committed
3676
#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
3677
int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
3678
{
3679 3680 3681 3682 3683 3684 3685 3686
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    thd->last_insert_id = val;
    break;
  case INSERT_ID_EVENT:
    thd->next_insert_id = val;
    break;
  }
3687
  rli->inc_event_relay_log_pos();
3688
  return 0;
3689
}
unknown's avatar
SCRUM  
unknown committed
3690
#endif
3691

3692

unknown's avatar
unknown committed
3693
/**************************************************************************
3694
  Rand_log_event methods
unknown's avatar
unknown committed
3695
**************************************************************************/
3696

unknown's avatar
SCRUM  
unknown committed
3697
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3698
void Rand_log_event::pack_info(Protocol *protocol)
3699
{
unknown's avatar
unknown committed
3700 3701 3702 3703 3704
  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);
3705
  protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
3706
}
unknown's avatar
SCRUM  
unknown committed
3707
#endif
3708 3709


3710 3711 3712
Rand_log_event::Rand_log_event(const char* buf,
                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3713
{
3714 3715 3716
  buf+= description_event->common_header_len;
  seed1= uint8korr(buf+RAND_SEED1_OFFSET);
  seed2= uint8korr(buf+RAND_SEED2_OFFSET);
3717 3718
}

3719

3720
#ifndef MYSQL_CLIENT
3721
bool Rand_log_event::write(IO_CACHE* file)
3722
{
3723
  byte buf[16];
3724 3725
  int8store(buf + RAND_SEED1_OFFSET, seed1);
  int8store(buf + RAND_SEED2_OFFSET, seed2);
3726 3727
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3728
}
3729
#endif
3730

3731 3732

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3733
void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3734
{
unknown's avatar
unknown committed
3735
  char llbuff[22],llbuff2[22];
unknown's avatar
unknown committed
3736
  if (!print_event_info->short_form)
3737
  {
unknown's avatar
unknown committed
3738
    print_header(file, print_event_info);
3739
    fprintf(file, "\tRand\n");
3740
  }
3741 3742 3743
  fprintf(file, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
	  llstr(seed1, llbuff),llstr(seed2, llbuff2),
          print_event_info->delimiter);
3744
  fflush(file);
3745
}
unknown's avatar
unknown committed
3746
#endif /* MYSQL_CLIENT */
3747

3748

unknown's avatar
SCRUM  
unknown committed
3749
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3750
int Rand_log_event::exec_event(struct st_relay_log_info* rli)
3751
{
unknown's avatar
unknown committed
3752 3753
  thd->rand.seed1= (ulong) seed1;
  thd->rand.seed2= (ulong) seed2;
3754
  rli->inc_event_relay_log_pos();
3755 3756
  return 0;
}
unknown's avatar
unknown committed
3757
#endif /* !MYSQL_CLIENT */
3758

unknown's avatar
unknown committed
3759

3760 3761 3762 3763 3764 3765 3766
/**************************************************************************
  Xid_log_event methods
**************************************************************************/

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Xid_log_event::pack_info(Protocol *protocol)
{
3767 3768
  char buf[128], *pos;
  pos= strmov(buf, "COMMIT /* xid=");
unknown's avatar
unknown committed
3769
  pos= longlong10_to_str(xid, pos, 10);
3770
  pos= strmov(pos, " */");
3771 3772 3773 3774
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
}
#endif

unknown's avatar
unknown committed
3775 3776 3777 3778 3779 3780 3781 3782
/*
  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
*/
3783 3784 3785 3786

Xid_log_event::
Xid_log_event(const char* buf,
              const Format_description_log_event *description_event)
3787 3788 3789
  :Log_event(buf, description_event)
{
  buf+= description_event->common_header_len;
3790
  memcpy((char*) &xid, buf, sizeof(xid));
3791 3792 3793
}


3794
#ifndef MYSQL_CLIENT
3795 3796
bool Xid_log_event::write(IO_CACHE* file)
{
3797
  DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
3798 3799 3800
  return write_header(file, sizeof(xid)) ||
         my_b_safe_write(file, (byte*) &xid, sizeof(xid));
}
3801
#endif
3802 3803 3804


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3805
void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3806
{
unknown's avatar
unknown committed
3807
  if (!print_event_info->short_form)
3808
  {
unknown's avatar
unknown committed
3809 3810 3811
    char buf[64];
    longlong10_to_str(xid, buf, 10);

unknown's avatar
unknown committed
3812
    print_header(file, print_event_info);
unknown's avatar
unknown committed
3813 3814
    fprintf(file, "\tXid = %s\n", buf);
    fflush(file);
3815
  }
3816
  fprintf(file, "COMMIT%s\n", print_event_info->delimiter);
3817 3818 3819 3820 3821 3822 3823
}
#endif /* MYSQL_CLIENT */


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Xid_log_event::exec_event(struct st_relay_log_info* rli)
{
3824
  /* For a slave Xid_log_event is COMMIT */
3825
  mysql_log.write(thd,COM_QUERY,"COMMIT /* implicit, from Xid_log_event */");
3826
  return end_trans(thd, COMMIT) || Log_event::exec_event(rli);
3827 3828 3829 3830
}
#endif /* !MYSQL_CLIENT */


unknown's avatar
unknown committed
3831
/**************************************************************************
3832
  User_var_log_event methods
unknown's avatar
unknown committed
3833
**************************************************************************/
unknown's avatar
unknown committed
3834

unknown's avatar
unknown committed
3835
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3836 3837 3838
void User_var_log_event::pack_info(Protocol* protocol)
{
  char *buf= 0;
3839
  uint val_offset= 4 + name_len;
unknown's avatar
unknown committed
3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854
  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));
3855 3856
      event_len+= my_sprintf(buf + val_offset,
			     (buf + val_offset, "%.14g", real_val));
unknown's avatar
unknown committed
3857 3858 3859 3860 3861
      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
3862 3863 3864 3865 3866 3867 3868 3869 3870 3871
    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
3872
    case STRING_RESULT:
3873 3874 3875 3876 3877 3878 3879 3880 3881 3882
      /* 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
      {
3883 3884 3885
        char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
        p= str_to_hex(p, val, val_len);
        p= strxmov(p, " COLLATE ", cs->name, NullS);
3886 3887
        event_len= p-buf;
      }
unknown's avatar
unknown committed
3888
      break;
3889
    case ROW_RESULT:
unknown's avatar
unknown committed
3890
    default:
unknown's avatar
unknown committed
3891 3892 3893 3894 3895
      DBUG_ASSERT(1);
      return;
    }
  }
  buf[0]= '@';
3896 3897 3898 3899
  buf[1]= '`';
  buf[2+name_len]= '`';
  buf[3+name_len]= '=';
  memcpy(buf+2, name, name_len);
3900
  protocol->store(buf, event_len, &my_charset_bin);
unknown's avatar
unknown committed
3901 3902
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
unknown's avatar
unknown committed
3903
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
3904 3905


3906 3907 3908 3909
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
3910
{
3911
  buf+= description_event->common_header_len;
unknown's avatar
unknown committed
3912 3913
  name_len= uint4korr(buf);
  name= (char *) buf + UV_NAME_LEN_SIZE;
3914 3915
  buf+= UV_NAME_LEN_SIZE + name_len;
  is_null= (bool) *buf;
unknown's avatar
unknown committed
3916 3917 3918
  if (is_null)
  {
    type= STRING_RESULT;
3919
    charset_number= my_charset_bin.number;
unknown's avatar
unknown committed
3920 3921 3922 3923 3924
    val_len= 0;
    val= 0;  
  }
  else
  {
3925 3926 3927
    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
3928
		       UV_CHARSET_NUMBER_SIZE);
3929 3930
    val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
		   UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
unknown's avatar
unknown committed
3931 3932 3933 3934
  }
}


3935
#ifndef MYSQL_CLIENT
3936
bool User_var_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
3937 3938 3939 3940
{
  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
3941
  char buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
3942
  uint buf1_length;
3943
  ulong event_length;
3944

unknown's avatar
unknown committed
3945
  int4store(buf, name_len);
3946 3947 3948 3949 3950 3951 3952
  
  if ((buf1[0]= is_null))
  {
    buf1_length= 1;
    val_len= 0;
  }    
  else
unknown's avatar
unknown committed
3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963
  {
    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
3964 3965 3966 3967 3968 3969
    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;
3970
      decimal2bin((decimal_t*)val, buf2+2, buf2[0], buf2[1]);
unknown's avatar
unknown committed
3971 3972 3973
      val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
      break;
    }
unknown's avatar
unknown committed
3974 3975 3976
    case STRING_RESULT:
      pos= val;
      break;
3977
    case ROW_RESULT:
unknown's avatar
unknown committed
3978
    default:
unknown's avatar
unknown committed
3979 3980 3981
      DBUG_ASSERT(1);
      return 0;
    }
unknown's avatar
unknown committed
3982 3983
    int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
    buf1_length= 10;
unknown's avatar
unknown committed
3984
  }
3985 3986 3987 3988 3989 3990

  /* 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))   ||
3991 3992 3993
	  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
3994
}
3995
#endif
unknown's avatar
unknown committed
3996

3997

unknown's avatar
unknown committed
3998
/*
unknown's avatar
unknown committed
3999
  User_var_log_event::print()
unknown's avatar
unknown committed
4000
*/
unknown's avatar
unknown committed
4001 4002

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4003
void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
4004
{
unknown's avatar
unknown committed
4005
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
4006
  {
unknown's avatar
unknown committed
4007
    print_header(file, print_event_info);
unknown's avatar
unknown committed
4008 4009 4010
    fprintf(file, "\tUser_var\n");
  }

4011
  fprintf(file, "SET @`");
unknown's avatar
unknown committed
4012
  my_fwrite(file, (byte*) name, (uint) (name_len), MYF(MY_NABP | MY_WME));
4013
  fprintf(file, "`");
unknown's avatar
unknown committed
4014 4015 4016

  if (is_null)
  {
4017
    fprintf(file, ":=NULL%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
4018 4019 4020 4021 4022 4023 4024
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
4025
      fprintf(file, ":=%.14g%s\n", real_val, print_event_info->delimiter);
unknown's avatar
unknown committed
4026 4027 4028 4029
      break;
    case INT_RESULT:
      char int_buf[22];
      longlong10_to_str(uint8korr(val), int_buf, -10);
4030
      fprintf(file, ":=%s%s\n", int_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4031
      break;
unknown's avatar
unknown committed
4032 4033 4034 4035 4036 4037
    case DECIMAL_RESULT:
    {
      char str_buf[200];
      int str_len= sizeof(str_buf) - 1;
      int precision= (int)val[0];
      int scale= (int)val[1];
4038 4039
      decimal_digit_t dec_buf[10];
      decimal_t dec;
unknown's avatar
unknown committed
4040 4041 4042 4043 4044 4045
      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;
4046
      fprintf(file, ":=%s%s\n",str_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4047 4048
      break;
    }
unknown's avatar
unknown committed
4049
    case STRING_RESULT:
4050
    {
4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064
      /*
        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.
      */
4065 4066 4067 4068
      char *hex_str;
      CHARSET_INFO *cs;

      if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte
4069
        break; // no error, as we are 'void'
4070
      str_to_hex(hex_str, val, val_len);
4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081
      /*
        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.
        */
4082
        fprintf(file, ":=???%s\n", print_event_info->delimiter);
4083
      else
4084 4085
        fprintf(file, ":=_%s %s COLLATE `%s`%s\n",
                cs->csname, hex_str, cs->name, print_event_info->delimiter);
4086
      my_afree(hex_str);
4087
    }
unknown's avatar
unknown committed
4088
      break;
4089
    case ROW_RESULT:
unknown's avatar
unknown committed
4090
    default:
unknown's avatar
unknown committed
4091
      DBUG_ASSERT(1);
unknown's avatar
unknown committed
4092 4093 4094 4095 4096
      return;
    }
  }
  fflush(file);
}
unknown's avatar
SCRUM  
unknown committed
4097
#endif
4098

unknown's avatar
unknown committed
4099

unknown's avatar
unknown committed
4100
/*
unknown's avatar
unknown committed
4101
  User_var_log_event::exec_event()
unknown's avatar
unknown committed
4102
*/
unknown's avatar
unknown committed
4103

unknown's avatar
unknown committed
4104
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
4105 4106 4107
int User_var_log_event::exec_event(struct st_relay_log_info* rli)
{
  Item *it= 0;
4108 4109 4110
  CHARSET_INFO *charset;
  if (!(charset= get_charset(charset_number, MYF(MY_WME))))
    return 1;
unknown's avatar
unknown committed
4111 4112 4113
  LEX_STRING user_var_name;
  user_var_name.str= name;
  user_var_name.length= name_len;
4114 4115
  double real_val;
  longlong int_val;
unknown's avatar
unknown committed
4116 4117 4118 4119 4120 4121 4122 4123 4124 4125

  if (is_null)
  {
    it= new Item_null();
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      float8get(real_val, val);
unknown's avatar
unknown committed
4126
      it= new Item_float(real_val);
4127
      val= (char*) &real_val;		// Pointer to value in native format
4128
      val_len= 8;
unknown's avatar
unknown committed
4129 4130
      break;
    case INT_RESULT:
4131 4132 4133
      int_val= (longlong) uint8korr(val);
      it= new Item_int(int_val);
      val= (char*) &int_val;		// Pointer to value in native format
4134
      val_len= 8;
unknown's avatar
unknown committed
4135
      break;
unknown's avatar
unknown committed
4136 4137 4138 4139 4140 4141 4142 4143
    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
4144 4145 4146
    case STRING_RESULT:
      it= new Item_string(val, val_len, charset);
      break;
4147
    case ROW_RESULT:
unknown's avatar
unknown committed
4148
    default:
unknown's avatar
unknown committed
4149 4150 4151 4152 4153
      DBUG_ASSERT(1);
      return 0;
    }
  }
  Item_func_set_user_var e(user_var_name, it);
4154 4155 4156
  /*
    Item_func_set_user_var can't substitute something else on its place =>
    0 can be passed as last argument (reference on item)
4157 4158 4159 4160

    Fix_fields() can fail, in which case a call of update_hash() might
    crash the server, so if fix fields fails, we just return with an
    error.
4161
  */
4162 4163 4164
  if (e.fix_fields(thd, 0))
    return 1;

4165 4166 4167 4168 4169
  /*
    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.
   */
unknown's avatar
unknown committed
4170
  e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
unknown's avatar
unknown committed
4171
  free_root(thd->mem_root,0);
unknown's avatar
unknown committed
4172

4173
  rli->inc_event_relay_log_pos();
unknown's avatar
unknown committed
4174 4175
  return 0;
}
unknown's avatar
unknown committed
4176
#endif /* !MYSQL_CLIENT */
4177 4178


unknown's avatar
unknown committed
4179
/**************************************************************************
4180
  Slave_log_event methods
unknown's avatar
unknown committed
4181
**************************************************************************/
unknown's avatar
unknown committed
4182

unknown's avatar
SCRUM  
unknown committed
4183
#ifdef HAVE_REPLICATION
4184
#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4185
void Unknown_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4186
{
unknown's avatar
unknown committed
4187
  if (print_event_info->short_form)
4188
    return;
unknown's avatar
unknown committed
4189
  print_header(file, print_event_info);
4190 4191 4192 4193
  fputc('\n', file);
  fprintf(file, "# %s", "Unknown event\n");
}
#endif  
4194

4195
#ifndef MYSQL_CLIENT
4196
void Slave_log_event::pack_info(Protocol *protocol)
4197
{
unknown's avatar
unknown committed
4198
  char buf[256+HOSTNAME_LENGTH], *pos;
4199 4200 4201 4202 4203 4204 4205
  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
4206
  pos= longlong10_to_str(master_pos, pos, 10);
4207
  protocol->store(buf, pos-buf, &my_charset_bin);
4208
}
unknown's avatar
unknown committed
4209
#endif /* !MYSQL_CLIENT */
4210 4211 4212 4213


#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
unknown's avatar
unknown committed
4214
				 struct st_relay_log_info* rli)
4215
  :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
4216 4217 4218 4219
{
  DBUG_ENTER("Slave_log_event");
  if (!rli->inited)				// QQ When can this happen ?
    DBUG_VOID_RETURN;
4220

4221 4222 4223 4224 4225
  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);
4226
  master_log_len = strlen(rli->group_master_log_name);
4227 4228 4229
  // 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))))
4230
  {
4231 4232 4233
    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;
4234
    memcpy(master_log, rli->group_master_log_name, master_log_len + 1);
4235
    master_port = mi->port;
4236
    master_pos = rli->group_master_log_pos;
unknown's avatar
unknown committed
4237
    DBUG_PRINT("info", ("master_log: %s  pos: %lu", master_log,
4238
			(ulong) master_pos));
4239
  }
4240 4241 4242 4243 4244 4245
  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
4246
#endif /* !MYSQL_CLIENT */
4247 4248 4249 4250 4251 4252 4253 4254 4255


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


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4256
void Slave_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4257 4258
{
  char llbuff[22];
unknown's avatar
unknown committed
4259
  if (print_event_info->short_form)
4260
    return;
unknown's avatar
unknown committed
4261
  print_header(file, print_event_info);
4262
  fputc('\n', file);
unknown's avatar
unknown committed
4263 4264
  fprintf(file, "\
Slave: master_host: '%s'  master_port: %d  master_log: '%s'  master_pos: %s\n",
4265 4266
	  master_host, master_port, master_log, llstr(master_pos, llbuff));
}
unknown's avatar
unknown committed
4267
#endif /* MYSQL_CLIENT */
4268 4269 4270 4271 4272 4273 4274 4275


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


4276
#ifndef MYSQL_CLIENT
4277
bool Slave_log_event::write(IO_CACHE* file)
4278
{
4279
  ulong event_length= get_data_size();
4280 4281 4282
  int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
  int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
  // log and host are already there
4283 4284 4285

  return (write_header(file, event_length) ||
          my_b_safe_write(file, (byte*) mem_pool, event_length));
4286
}
4287
#endif
4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298


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)
4299
  {
4300 4301
    master_host = 0;
    return;
4302
  }
4303 4304
  master_log_len = strlen(master_log);
}
4305

4306

4307 4308 4309
/* 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)
4310
{
4311
  if (event_len < LOG_EVENT_HEADER_LEN)
4312
    return;
4313
  event_len -= LOG_EVENT_HEADER_LEN;
4314 4315 4316 4317 4318
  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);
4319 4320
}

4321

4322 4323 4324 4325 4326 4327 4328
#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
4329
#endif /* !MYSQL_CLIENT */
4330 4331


unknown's avatar
unknown committed
4332
/**************************************************************************
unknown's avatar
unknown committed
4333
	Stop_log_event methods
unknown's avatar
unknown committed
4334
**************************************************************************/
4335

unknown's avatar
unknown committed
4336
/*
4337
  Stop_log_event::print()
4338
*/
4339 4340

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4341
void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4342
{
unknown's avatar
unknown committed
4343
  if (print_event_info->short_form)
4344 4345
    return;

unknown's avatar
unknown committed
4346
  print_header(file, print_event_info);
4347 4348
  fprintf(file, "\tStop\n");
  fflush(file);
4349
}
unknown's avatar
unknown committed
4350
#endif /* MYSQL_CLIENT */
4351

4352

4353
/*
4354
  Stop_log_event::exec_event()
4355

4356
  The master stopped.
unknown's avatar
unknown committed
4357
  We used to clean up all temporary tables but this is useless as, as the
4358 4359
  master has shut down properly, it has written all DROP TEMPORARY TABLE
  (prepared statements' deletion is TODO only when we binlog prep stmts).
4360 4361 4362
  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.
4363
  The place were we must do this cleaning is in Start_log_event_v3::exec_event(),
4364
  not here. Because if we come here, the master was sane.
4365 4366
*/

4367
#ifndef MYSQL_CLIENT
4368
int Stop_log_event::exec_event(struct st_relay_log_info* rli)
4369
{
unknown's avatar
unknown committed
4370 4371
  /*
    We do not want to update master_log pos because we get a rotate event
4372
    before stop, so by now group_master_log_name is set to the next log.
4373
    If we updated it, we will have incorrect master coordinates and this
unknown's avatar
unknown committed
4374
    could give false triggers in MASTER_POS_WAIT() that we have reached
4375
    the target position when in fact we have not.
unknown's avatar
unknown committed
4376
  */
4377 4378 4379 4380 4381 4382 4383
  if (thd->options & OPTION_BEGIN)
    rli->inc_event_relay_log_pos();
  else
  {
    rli->inc_group_relay_log_pos(0);
    flush_relay_log_info(rli);
  }
4384 4385
  return 0;
}
unknown's avatar
unknown committed
4386
#endif /* !MYSQL_CLIENT */
unknown's avatar
SCRUM  
unknown committed
4387
#endif /* HAVE_REPLICATION */
4388

4389

unknown's avatar
unknown committed
4390
/**************************************************************************
unknown's avatar
unknown committed
4391
	Create_file_log_event methods
unknown's avatar
unknown committed
4392
**************************************************************************/
4393 4394

/*
4395
  Create_file_log_event ctor
unknown's avatar
unknown committed
4396
*/
4397 4398

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4399 4400 4401 4402
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,
4403
                      bool ignore,
unknown's avatar
unknown committed
4404
		      char* block_arg, uint block_len_arg, bool using_trans)
4405
  :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
unknown's avatar
unknown committed
4406
		  using_trans),
4407
   fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
4408
   file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
4409
{
unknown's avatar
unknown committed
4410
  DBUG_ENTER("Create_file_log_event");
4411
  sql_ex.force_new_format();
unknown's avatar
unknown committed
4412
  DBUG_VOID_RETURN;
4413
}
4414

4415

unknown's avatar
unknown committed
4416
/*
4417
  Create_file_log_event::write_data_body()
unknown's avatar
unknown committed
4418
*/
4419

4420
bool Create_file_log_event::write_data_body(IO_CACHE* file)
4421
{
4422 4423
  bool res;
  if ((res= Load_log_event::write_data_body(file)) || fake_base)
4424 4425
    return res;
  return (my_b_safe_write(file, (byte*) "", 1) ||
4426
          my_b_safe_write(file, (byte*) block, block_len));
4427 4428
}

4429

unknown's avatar
unknown committed
4430
/*
4431
  Create_file_log_event::write_data_header()
unknown's avatar
unknown committed
4432
*/
unknown's avatar
unknown committed
4433

4434
bool Create_file_log_event::write_data_header(IO_CACHE* file)
4435
{
4436
  bool res;
4437
  byte buf[CREATE_FILE_HEADER_LEN];
4438 4439
  if ((res= Load_log_event::write_data_header(file)) || fake_base)
    return res;
4440
  int4store(buf + CF_FILE_ID_OFFSET, file_id);
4441
  return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN) != 0;
4442 4443 4444
}


unknown's avatar
unknown committed
4445
/*
4446
  Create_file_log_event::write_base()
unknown's avatar
unknown committed
4447
*/
4448

4449
bool Create_file_log_event::write_base(IO_CACHE* file)
4450
{
4451 4452 4453 4454
  bool res;
  fake_base= 1;                                 // pretend we are Load event
  res= write(file);
  fake_base= 0;
4455 4456 4457
  return res;
}

4458
#endif /* !MYSQL_CLIENT */
4459

unknown's avatar
unknown committed
4460
/*
4461
  Create_file_log_event ctor
unknown's avatar
unknown committed
4462
*/
4463

4464 4465 4466
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)
4467
{
4468 4469 4470 4471 4472
  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
4473
  if (!(event_buf= my_memdup((byte*) buf, len, MYF(MY_WME))) ||
4474 4475 4476 4477 4478 4479 4480
      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
4481
    DBUG_VOID_RETURN;
4482
  if (description_event->binlog_version!=1)
4483
  {
4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500
    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);
4501 4502 4503 4504 4505 4506 4507 4508 4509
    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;
4510
  }
unknown's avatar
unknown committed
4511
  DBUG_VOID_RETURN;
4512 4513
}

4514

unknown's avatar
unknown committed
4515
/*
4516
  Create_file_log_event::print()
unknown's avatar
unknown committed
4517
*/
4518 4519

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4520
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
4521
				  bool enable_local)
unknown's avatar
unknown committed
4522
{
unknown's avatar
unknown committed
4523
  if (print_event_info->short_form)
4524 4525
  {
    if (enable_local && check_fname_outside_temp_buf())
unknown's avatar
unknown committed
4526
      Load_log_event::print(file, print_event_info);
4527
    return;
4528 4529 4530 4531
  }

  if (enable_local)
  {
unknown's avatar
unknown committed
4532
    Load_log_event::print(file, print_event_info,
4533
			  !check_fname_outside_temp_buf());
unknown's avatar
unknown committed
4534 4535 4536 4537 4538
    /* 
       That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
       SHOW BINLOG EVENTS we don't.
    */
    fprintf(file, "#"); 
4539 4540
  }

4541
  fprintf(file, " file_id: %d  block_len: %d\n", file_id, block_len);
unknown's avatar
unknown committed
4542
}
4543

unknown's avatar
unknown committed
4544

unknown's avatar
unknown committed
4545
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4546
{
unknown's avatar
unknown committed
4547
  print(file, print_event_info, 0);
4548
}
unknown's avatar
unknown committed
4549
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
4550

4551

unknown's avatar
unknown committed
4552
/*
4553
  Create_file_log_event::pack_info()
unknown's avatar
unknown committed
4554
*/
4555

unknown's avatar
SCRUM  
unknown committed
4556
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4557
void Create_file_log_event::pack_info(Protocol *protocol)
4558
{
4559 4560 4561
  char buf[NAME_LEN*2 + 30 + 21*2], *pos;
  pos= strmov(buf, "db=");
  memcpy(pos, db, db_len);
unknown's avatar
unknown committed
4562
  pos= strmov(pos + db_len, ";table=");
4563
  memcpy(pos, table_name, table_name_len);
unknown's avatar
unknown committed
4564
  pos= strmov(pos + table_name_len, ";file_id=");
4565 4566 4567
  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
4568
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
4569
}
unknown's avatar
unknown committed
4570
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4571 4572


unknown's avatar
unknown committed
4573
/*
4574
  Create_file_log_event::exec_event()
unknown's avatar
unknown committed
4575
*/
4576

unknown's avatar
SCRUM  
unknown committed
4577
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4578
int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
4579
{
unknown's avatar
unknown committed
4580 4581
  char proc_info[17+FN_REFLEN+10], *fname_buf;
  char *ext;
4582 4583 4584
  int fd = -1;
  IO_CACHE file;
  int error = 1;
unknown's avatar
unknown committed
4585

4586
  bzero((char*)&file, sizeof(file));
unknown's avatar
unknown committed
4587 4588
  fname_buf= strmov(proc_info, "Making temp file ");
  ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
4589
  thd->proc_info= proc_info;
4590 4591 4592 4593
  my_delete(fname_buf, MYF(0)); // old copy may exist already
  if ((fd= my_create(fname_buf, CREATE_MODE,
		     O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
		     MYF(MY_WME))) < 0 ||
4594 4595 4596
      init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
unknown's avatar
unknown committed
4597 4598 4599
    slave_print_error(rli,my_errno,
                      "Error in Create_file event: could not open file '%s'",
                      fname_buf);
4600 4601 4602 4603
    goto err;
  }
  
  // a trick to avoid allocating another buffer
unknown's avatar
unknown committed
4604 4605
  fname= fname_buf;
  fname_len= (uint) (strmov(ext, ".data") - fname);
4606 4607
  if (write_base(&file))
  {
unknown's avatar
unknown committed
4608
    strmov(ext, ".info"); // to have it right in the error message
unknown's avatar
unknown committed
4609
    slave_print_error(rli,my_errno,
unknown's avatar
unknown committed
4610 4611
		      "Error in Create_file event: could not write to file "
                      "'%s'",
unknown's avatar
unknown committed
4612
		      fname_buf);
4613 4614 4615 4616 4617 4618
    goto err;
  }
  end_io_cache(&file);
  my_close(fd, MYF(0));
  
  // fname_buf now already has .data, not .info, because we did our trick
4619 4620 4621 4622
  my_delete(fname_buf, MYF(0)); // old copy may exist already
  if ((fd= my_create(fname_buf, CREATE_MODE,
		     O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
		     MYF(MY_WME))) < 0)
4623
  {
unknown's avatar
unknown committed
4624 4625 4626
    slave_print_error(rli,my_errno,
                      "Error in Create_file event: could not open file '%s'",
                      fname_buf);
4627 4628
    goto err;
  }
unknown's avatar
unknown committed
4629
  if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
4630
  {
unknown's avatar
unknown committed
4631 4632 4633
    slave_print_error(rli,my_errno,
                      "Error in Create_file event: write to '%s' failed",
                      fname_buf);
4634 4635
    goto err;
  }
4636 4637
  error=0;					// Everything is ok

4638 4639 4640 4641 4642
err:
  if (error)
    end_io_cache(&file);
  if (fd >= 0)
    my_close(fd, MYF(0));
unknown's avatar
unknown committed
4643
  thd->proc_info= 0;
4644
  return error ? 1 : Log_event::exec_event(rli);
4645
}
unknown's avatar
unknown committed
4646
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4647

4648

unknown's avatar
unknown committed
4649
/**************************************************************************
unknown's avatar
unknown committed
4650
	Append_block_log_event methods
unknown's avatar
unknown committed
4651
**************************************************************************/
4652

unknown's avatar
unknown committed
4653
/*
4654
  Append_block_log_event ctor
unknown's avatar
unknown committed
4655
*/
4656 4657

#ifndef MYSQL_CLIENT  
unknown's avatar
unknown committed
4658 4659
Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg,
					       char* block_arg,
unknown's avatar
unknown committed
4660 4661 4662
					       uint block_len_arg,
					       bool using_trans)
  :Log_event(thd_arg,0, using_trans), block(block_arg),
unknown's avatar
unknown committed
4663
   block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
4664 4665
{
}
unknown's avatar
unknown committed
4666
#endif
4667 4668


unknown's avatar
unknown committed
4669
/*
4670
  Append_block_log_event ctor
unknown's avatar
unknown committed
4671
*/
4672

4673 4674 4675
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)
4676
{
4677 4678 4679 4680 4681 4682
  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
4683
    DBUG_VOID_RETURN;
4684 4685 4686
  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
4687
  DBUG_VOID_RETURN;
4688 4689 4690
}


unknown's avatar
unknown committed
4691
/*
4692
  Append_block_log_event::write()
unknown's avatar
unknown committed
4693
*/
4694

4695
#ifndef MYSQL_CLIENT
4696
bool Append_block_log_event::write(IO_CACHE* file)
4697 4698 4699
{
  byte buf[APPEND_BLOCK_HEADER_LEN];
  int4store(buf + AB_FILE_ID_OFFSET, file_id);
4700 4701
  return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
          my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
4702 4703
	  my_b_safe_write(file, (byte*) block, block_len));
}
4704
#endif
4705 4706


unknown's avatar
unknown committed
4707
/*
4708
  Append_block_log_event::print()
unknown's avatar
unknown committed
4709
*/
4710 4711

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
4712
void Append_block_log_event::print(FILE* file,
unknown's avatar
unknown committed
4713
				   PRINT_EVENT_INFO* print_event_info)
4714
{
unknown's avatar
unknown committed
4715
  if (print_event_info->short_form)
4716
    return;
unknown's avatar
unknown committed
4717
  print_header(file, print_event_info);
4718
  fputc('\n', file);
unknown's avatar
unknown committed
4719 4720
  fprintf(file, "#%s: file_id: %d  block_len: %d\n",
	  get_type_str(), file_id, block_len);
4721
}
unknown's avatar
unknown committed
4722
#endif /* MYSQL_CLIENT */
4723 4724


unknown's avatar
unknown committed
4725
/*
4726
  Append_block_log_event::pack_info()
unknown's avatar
unknown committed
4727
*/
4728

unknown's avatar
SCRUM  
unknown committed
4729
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4730
void Append_block_log_event::pack_info(Protocol *protocol)
4731 4732 4733 4734 4735 4736
{
  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
4737
  protocol->store(buf, length, &my_charset_bin);
4738 4739 4740
}


unknown's avatar
unknown committed
4741
/*
4742
  Append_block_log_event::get_create_or_append()
unknown's avatar
unknown committed
4743 4744
*/

4745
int Append_block_log_event::get_create_or_append() const
unknown's avatar
unknown committed
4746
{
4747
  return 0; /* append to the file, fail if not exists */
unknown's avatar
unknown committed
4748 4749
}

unknown's avatar
unknown committed
4750
/*
4751
  Append_block_log_event::exec_event()
unknown's avatar
unknown committed
4752
*/
4753

4754
int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
4755
{
4756
  char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
4757
  int fd;
4758
  int error = 1;
unknown's avatar
unknown committed
4759
  DBUG_ENTER("Append_block_log_event::exec_event");
4760

unknown's avatar
unknown committed
4761 4762
  fname= strmov(proc_info, "Making temp file ");
  slave_load_file_stem(fname, file_id, server_id, ".data");
4763
  thd->proc_info= proc_info;
4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776
  if (get_create_or_append())
  {
    my_delete(fname, MYF(0)); // old copy may exist already
    if ((fd= my_create(fname, CREATE_MODE,
		       O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
		       MYF(MY_WME))) < 0)
    {
      slave_print_error(rli, my_errno,
			"Error in %s event: could not create file '%s'",
			get_type_str(), fname);
      goto err;
    }
  }
4777 4778
  else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
                         MYF(MY_WME))) < 0)
4779
  {
unknown's avatar
unknown committed
4780 4781 4782
    slave_print_error(rli, my_errno,
                      "Error in %s event: could not open file '%s'",
                      get_type_str(), fname);
4783 4784
    goto err;
  }
unknown's avatar
unknown committed
4785
  if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
4786
  {
unknown's avatar
unknown committed
4787 4788 4789
    slave_print_error(rli, my_errno,
                      "Error in %s event: write to '%s' failed",
                      get_type_str(), fname);
4790 4791 4792
    goto err;
  }
  error=0;
4793

4794 4795 4796
err:
  if (fd >= 0)
    my_close(fd, MYF(0));
4797
  thd->proc_info= 0;
unknown's avatar
unknown committed
4798
  DBUG_RETURN(error ? error : Log_event::exec_event(rli));
4799
}
unknown's avatar
SCRUM  
unknown committed
4800
#endif
4801 4802


unknown's avatar
unknown committed
4803
/**************************************************************************
unknown's avatar
unknown committed
4804
	Delete_file_log_event methods
unknown's avatar
unknown committed
4805
**************************************************************************/
4806

unknown's avatar
unknown committed
4807
/*
4808
  Delete_file_log_event ctor
unknown's avatar
unknown committed
4809
*/
4810 4811

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4812 4813 4814
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)
4815 4816
{
}
unknown's avatar
unknown committed
4817
#endif
4818

unknown's avatar
unknown committed
4819
/*
4820
  Delete_file_log_event ctor
unknown's avatar
unknown committed
4821
*/
4822

4823 4824 4825
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)
4826
{
4827 4828 4829
  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))
4830
    return;
4831
  file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
4832 4833 4834
}


unknown's avatar
unknown committed
4835
/*
4836
  Delete_file_log_event::write()
unknown's avatar
unknown committed
4837
*/
4838

4839
#ifndef MYSQL_CLIENT
4840
bool Delete_file_log_event::write(IO_CACHE* file)
4841 4842 4843
{
 byte buf[DELETE_FILE_HEADER_LEN];
 int4store(buf + DF_FILE_ID_OFFSET, file_id);
4844 4845
 return (write_header(file, sizeof(buf)) ||
         my_b_safe_write(file, buf, sizeof(buf)));
4846
}
4847
#endif
4848 4849


unknown's avatar
unknown committed
4850
/*
4851
  Delete_file_log_event::print()
unknown's avatar
unknown committed
4852
*/
4853 4854

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
4855
void Delete_file_log_event::print(FILE* file,
unknown's avatar
unknown committed
4856
				  PRINT_EVENT_INFO* print_event_info)
4857
{
unknown's avatar
unknown committed
4858
  if (print_event_info->short_form)
4859
    return;
unknown's avatar
unknown committed
4860
  print_header(file, print_event_info);
4861 4862 4863
  fputc('\n', file);
  fprintf(file, "#Delete_file: file_id=%u\n", file_id);
}
unknown's avatar
unknown committed
4864
#endif /* MYSQL_CLIENT */
4865

unknown's avatar
unknown committed
4866
/*
4867
  Delete_file_log_event::pack_info()
unknown's avatar
unknown committed
4868
*/
4869

unknown's avatar
SCRUM  
unknown committed
4870
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4871
void Delete_file_log_event::pack_info(Protocol *protocol)
4872 4873 4874 4875
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
4876
  protocol->store(buf, (int32) length, &my_charset_bin);
4877
}
unknown's avatar
SCRUM  
unknown committed
4878
#endif
4879

unknown's avatar
unknown committed
4880
/*
4881
  Delete_file_log_event::exec_event()
unknown's avatar
unknown committed
4882
*/
4883

unknown's avatar
SCRUM  
unknown committed
4884
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4885 4886 4887
int Delete_file_log_event::exec_event(struct st_relay_log_info* rli)
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
4888
  char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
4889
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
4890
  strmov(ext, ".info");
4891 4892 4893
  (void) my_delete(fname, MYF(MY_WME));
  return Log_event::exec_event(rli);
}
unknown's avatar
unknown committed
4894
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4895 4896


unknown's avatar
unknown committed
4897
/**************************************************************************
unknown's avatar
unknown committed
4898
	Execute_load_log_event methods
unknown's avatar
unknown committed
4899
**************************************************************************/
4900

unknown's avatar
unknown committed
4901
/*
4902
  Execute_load_log_event ctor
unknown's avatar
unknown committed
4903
*/
4904 4905

#ifndef MYSQL_CLIENT  
unknown's avatar
unknown committed
4906 4907 4908
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)
4909 4910
{
}
unknown's avatar
unknown committed
4911
#endif
4912 4913
  

unknown's avatar
unknown committed
4914
/*
4915
  Execute_load_log_event ctor
unknown's avatar
unknown committed
4916
*/
4917

4918 4919 4920
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)
4921
{
4922 4923 4924
  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))
4925
    return;
4926
  file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
4927 4928 4929
}


unknown's avatar
unknown committed
4930
/*
4931
  Execute_load_log_event::write()
unknown's avatar
unknown committed
4932
*/
4933

4934
#ifndef MYSQL_CLIENT
4935
bool Execute_load_log_event::write(IO_CACHE* file)
4936 4937 4938
{
  byte buf[EXEC_LOAD_HEADER_LEN];
  int4store(buf + EL_FILE_ID_OFFSET, file_id);
4939 4940
  return (write_header(file, sizeof(buf)) || 
          my_b_safe_write(file, buf, sizeof(buf)));
4941
}
4942
#endif
4943 4944


unknown's avatar
unknown committed
4945
/*
4946
  Execute_load_log_event::print()
unknown's avatar
unknown committed
4947
*/
4948 4949

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
4950
void Execute_load_log_event::print(FILE* file,
unknown's avatar
unknown committed
4951
				   PRINT_EVENT_INFO* print_event_info)
4952
{
unknown's avatar
unknown committed
4953
  if (print_event_info->short_form)
4954
    return;
unknown's avatar
unknown committed
4955
  print_header(file, print_event_info);
4956 4957 4958 4959
  fputc('\n', file);
  fprintf(file, "#Exec_load: file_id=%d\n",
	  file_id);
}
unknown's avatar
unknown committed
4960
#endif
4961

unknown's avatar
unknown committed
4962
/*
4963
  Execute_load_log_event::pack_info()
unknown's avatar
unknown committed
4964
*/
4965

unknown's avatar
SCRUM  
unknown committed
4966
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4967
void Execute_load_log_event::pack_info(Protocol *protocol)
4968 4969 4970 4971
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
4972
  protocol->store(buf, (int32) length, &my_charset_bin);
4973 4974 4975
}


unknown's avatar
unknown committed
4976
/*
4977
  Execute_load_log_event::exec_event()
unknown's avatar
unknown committed
4978
*/
unknown's avatar
SCRUM  
unknown committed
4979

4980
int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
4981 4982
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
4983
  char *ext;
4984
  int fd;
unknown's avatar
unknown committed
4985
  int error= 1;
4986
  IO_CACHE file;
unknown's avatar
unknown committed
4987
  Load_log_event *lev= 0;
4988

unknown's avatar
unknown committed
4989
  ext= slave_load_file_stem(fname, file_id, server_id, ".info");
4990 4991
  if ((fd = my_open(fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
                    MYF(MY_WME))) < 0 ||
4992 4993 4994
      init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
unknown's avatar
unknown committed
4995 4996 4997
    slave_print_error(rli,my_errno,
                      "Error in Exec_load event: could not open file '%s'",
                      fname);
4998 4999
    goto err;
  }
5000
  if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
5001 5002
                                                         (pthread_mutex_t*)0,
                                                         rli->relay_log.description_event_for_exec)) ||
5003
      lev->get_type_code() != NEW_LOAD_EVENT)
5004
  {
unknown's avatar
unknown committed
5005 5006 5007
    slave_print_error(rli,0,
                      "Error in Exec_load event: file '%s' appears corrupted",
                      fname);
5008 5009
    goto err;
  }
unknown's avatar
unknown committed
5010

5011
  lev->thd = thd;
5012 5013
  /*
    lev->exec_event should use rli only for errors
unknown's avatar
unknown committed
5014 5015 5016
    i.e. should not advance rli's position.
    lev->exec_event is the place where the table is loaded (it calls
    mysql_load()).
5017
  */
unknown's avatar
unknown committed
5018

5019
  rli->future_group_master_log_pos= log_pos;
5020
  if (lev->exec_event(0,rli,1)) 
5021
  {
5022 5023 5024 5025 5026 5027 5028 5029 5030
    /*
      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));
5031 5032 5033 5034 5035 5036 5037 5038
    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));
    }
5039 5040
    goto err;
  }
unknown's avatar
unknown committed
5041 5042 5043 5044 5045 5046 5047 5048 5049 5050
  /*
    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;
  }
5051
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5052
  memcpy(ext, ".data", 6);
5053
  (void) my_delete(fname, MYF(MY_WME));
5054
  error = 0;
5055

5056 5057 5058
err:
  delete lev;
  if (fd >= 0)
5059
  {
5060
    my_close(fd, MYF(0));
5061 5062
    end_io_cache(&file);
  }
5063
  return error ? error : Log_event::exec_event(rli);
5064
}
unknown's avatar
SCRUM  
unknown committed
5065

unknown's avatar
unknown committed
5066
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5067 5068


unknown's avatar
unknown committed
5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093
/**************************************************************************
	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)
5094
int Begin_load_query_log_event::get_create_or_append() const
unknown's avatar
unknown committed
5095
{
5096
  return 1; /* create the file */
unknown's avatar
unknown committed
5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108
}
#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,
5109 5110 5111 5112 5113
                             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,
                             THD::killed_state killed_err_arg):
unknown's avatar
unknown committed
5114
  Query_log_event(thd_arg, query_arg, query_length_arg, using_trans,
5115
                  suppress_use, killed_err_arg),
unknown's avatar
unknown committed
5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151
  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;
}


5152
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5153 5154 5155 5156 5157 5158 5159 5160 5161 5162
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);
}
5163
#endif
unknown's avatar
unknown committed
5164 5165 5166


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
5167
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5168
                                         PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
5169
{
unknown's avatar
unknown committed
5170
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
5171 5172 5173
}


unknown's avatar
unknown committed
5174
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5175
                                         PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
5176 5177
                                         const char *local_fname)
{
unknown's avatar
unknown committed
5178
  print_query_header(file, print_event_info);
unknown's avatar
unknown committed
5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190

  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));
5191
    fprintf(file, "\n%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5192 5193 5194 5195
  }
  else
  {
    my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
5196
    fprintf(file, "\n%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5197 5198
  }

unknown's avatar
unknown committed
5199
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249
    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;
5250
  fname= (p= strmake(p, STRING_WITH_LEN(" INFILE \'")));
unknown's avatar
unknown committed
5251 5252
  p= slave_load_file_stem(p, file_id, server_id, ".data");
  fname_end= p= strend(p);                      // Safer than p=p+5
unknown's avatar
unknown committed
5253
  *(p++)='\'';
unknown's avatar
unknown committed
5254
  switch (dup_handling) {
unknown's avatar
unknown committed
5255
  case LOAD_DUP_IGNORE:
5256
    p= strmake(p, STRING_WITH_LEN(" IGNORE"));
unknown's avatar
unknown committed
5257 5258
    break;
  case LOAD_DUP_REPLACE:
5259
    p= strmake(p, STRING_WITH_LEN(" REPLACE"));
unknown's avatar
unknown committed
5260 5261 5262 5263 5264
    break;
  default:
    /* Ordinary load data */
    break;
  }
5265
  p= strmake(p, STRING_WITH_LEN(" INTO"));
unknown's avatar
unknown committed
5266 5267 5268 5269 5270 5271 5272
  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;

5273 5274 5275 5276 5277 5278
  /*
    If there was an error the slave is going to stop, leave the
    file so that we can re-execute this event at START SLAVE.
  */
  if (!error)
    (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5279 5280 5281 5282 5283 5284 5285

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


unknown's avatar
unknown committed
5286
/**************************************************************************
unknown's avatar
unknown committed
5287
	sql_ex_info methods
unknown's avatar
unknown committed
5288
**************************************************************************/
5289

unknown's avatar
unknown committed
5290
/*
5291
  sql_ex_info::write_data()
unknown's avatar
unknown committed
5292
*/
5293

5294
bool sql_ex_info::write_data(IO_CACHE* file)
5295 5296 5297
{
  if (new_format())
  {
5298 5299 5300 5301 5302
    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) ||
5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314
	    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;
5315
    return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)) != 0;
5316 5317 5318 5319
  }
}


unknown's avatar
unknown committed
5320
/*
5321
  sql_ex_info::init()
unknown's avatar
unknown committed
5322
*/
5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336

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.
    */
5337 5338 5339 5340 5341
    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))
5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367
      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;
}