log_event.cc 254 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

23
#include "mysql_priv.h"
24
#include "slave.h"
25 26
#include "rpl_rli.h"
#include "rpl_mi.h"
unknown's avatar
unknown committed
27
#include "rpl_filter.h"
28
#include "rpl_utility.h"
unknown's avatar
unknown committed
29
#include "rpl_record.h"
30
#include <my_dir.h>
unknown's avatar
unknown committed
31
#endif /* MYSQL_CLIENT */
32 33
#include <base64.h>
#include <my_bitmap.h>
unknown's avatar
unknown committed
34

unknown's avatar
unknown committed
35
#define log_cs	&my_charset_latin1
36

37 38
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
/*
  Cache that will automatically be written to a dedicated file on
  destruction.

  DESCRIPTION

 */
class Write_on_release_cache
{
public:
  enum flag
  {
    FLUSH_F
  };

  typedef unsigned short flag_set;

  /*
    Constructor.

    SYNOPSIS
      Write_on_release_cache
      cache  Pointer to cache to use
      file   File to write cache to upon destruction
      flags  Flags for the cache

    DESCRIPTION

      Class used to guarantee copy of cache to file before exiting the
      current block.  On successful copy of the cache, the cache will
      be reinited as a WRITE_CACHE.

      Currently, a pointer to the cache is provided in the
      constructor, but it would be possible to create a subclass
      holding the IO_CACHE itself.
   */
  Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0)
    : m_cache(cache), m_file(file), m_flags(flags)
  {
    reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
  }

  ~Write_on_release_cache()
  {
83
    copy_event_cache_to_file_and_reinit(m_cache, m_file);
84 85 86 87 88 89 90 91 92 93 94
    if (m_flags | FLUSH_F)
      fflush(m_file);
  }

  /*
    Return a pointer to the internal IO_CACHE.

    SYNOPSIS
      operator&()

    DESCRIPTION
95 96 97 98

      Function to return a pointer to the internal cache, so that the
      object can be treated as a IO_CACHE and used with the my_b_*
      IO_CACHE functions
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

    RETURN VALUE
      A pointer to the internal IO_CACHE.
   */
  IO_CACHE *operator&()
  {
    return m_cache;
  }

private:
  // Hidden, to prevent usage.
  Write_on_release_cache(Write_on_release_cache const&);

  IO_CACHE *m_cache;
  FILE *m_file;
  flag_set m_flags;
};


unknown's avatar
unknown committed
118
/*
119
  pretty_print_str()
unknown's avatar
unknown committed
120
*/
121

122
#ifdef MYSQL_CLIENT
123
static void pretty_print_str(IO_CACHE* cache, char* str, int len)
unknown's avatar
unknown committed
124
{
125
  char* end = str + len;
126
  my_b_printf(cache, "\'");
127 128
  while (str < end)
  {
unknown's avatar
unknown committed
129
    char c;
130
    switch ((c=*str++)) {
131 132 133 134 135 136 137
    case '\n': my_b_printf(cache, "\\n"); break;
    case '\r': my_b_printf(cache, "\\r"); break;
    case '\\': my_b_printf(cache, "\\\\"); break;
    case '\b': my_b_printf(cache, "\\b"); break;
    case '\t': my_b_printf(cache, "\\t"); break;
    case '\'': my_b_printf(cache, "\\'"); break;
    case 0   : my_b_printf(cache, "\\0"); break;
138
    default:
139
      my_b_printf(cache, "%c", c);
140 141
      break;
    }
142
  }
143
  my_b_printf(cache, "\'");
unknown's avatar
unknown committed
144
}
unknown's avatar
unknown committed
145
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
146

unknown's avatar
unknown committed
147
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
148

149
static void clear_all_errors(THD *thd, Relay_log_info *rli)
150 151 152
{
  thd->query_error = 0;
  thd->clear_error();
153
  rli->clear_error();
154 155
}

unknown's avatar
unknown committed
156

unknown's avatar
unknown committed
157
/*
unknown's avatar
unknown committed
158
  Ignore error code specified on command line
unknown's avatar
unknown committed
159
*/
160

161 162
inline int ignored_error_code(int err_code)
{
unknown's avatar
unknown committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176
#ifdef HAVE_NDB_BINLOG
  /*
    The following error codes are hard-coded and will always be ignored.
  */
  switch (err_code)
  {
  case ER_DB_CREATE_EXISTS:
  case ER_DB_DROP_EXISTS:
    return 1;
  default:
    /* Nothing to do */
    break;
  }
#endif
unknown's avatar
unknown committed
177 178
  return ((err_code == ER_SLAVE_IGNORED_TABLE) ||
          (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code)));
179
}
unknown's avatar
SCRUM  
unknown committed
180
#endif
181

unknown's avatar
unknown committed
182

unknown's avatar
unknown committed
183
/*
184
  pretty_print_str()
unknown's avatar
unknown committed
185
*/
unknown's avatar
unknown committed
186

unknown's avatar
unknown committed
187
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
188
static char *pretty_print_str(char *packet, char *str, int len)
unknown's avatar
unknown committed
189
{
unknown's avatar
unknown committed
190 191
  char *end= str + len;
  char *pos= packet;
192
  *pos++= '\'';
193 194 195
  while (str < end)
  {
    char c;
196
    switch ((c=*str++)) {
unknown's avatar
unknown committed
197 198 199 200 201 202 203
    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;
204
    default:
unknown's avatar
unknown committed
205
      *pos++= c;
206 207
      break;
    }
unknown's avatar
unknown committed
208
  }
209 210
  *pos++= '\'';
  return pos;
unknown's avatar
unknown committed
211
}
unknown's avatar
unknown committed
212
#endif /* !MYSQL_CLIENT */
213

unknown's avatar
unknown committed
214

unknown's avatar
unknown committed
215
/*
unknown's avatar
unknown committed
216 217 218 219 220 221 222 223 224 225 226
  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
227
*/
unknown's avatar
unknown committed
228

unknown's avatar
SCRUM  
unknown committed
229
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
230 231
static char *slave_load_file_stem(char *buf, uint file_id,
                                  int event_server_id, const char *ext)
232
{
unknown's avatar
unknown committed
233
  char *res;
234 235 236
  fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", MY_UNPACK_FILENAME);
  to_unix_path(buf);

237 238 239 240 241
  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
242 243 244
  res= int10_to_str(file_id, buf, 10);
  strmov(res, ext);                             // Add extension last
  return res;                                   // Pointer to extension
245
}
unknown's avatar
SCRUM  
unknown committed
246
#endif
247

unknown's avatar
unknown committed
248

unknown's avatar
unknown committed
249
/*
250 251
  Delete all temporary files used for SQL_LOAD.

unknown's avatar
unknown committed
252 253
  SYNOPSIS
    cleanup_load_tmpdir()
unknown's avatar
unknown committed
254
*/
255

unknown's avatar
SCRUM  
unknown committed
256
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
257 258 259 260 261
static void cleanup_load_tmpdir()
{
  MY_DIR *dirp;
  FILEINFO *file;
  uint i;
unknown's avatar
unknown committed
262
  char fname[FN_REFLEN], prefbuf[31], *p;
unknown's avatar
unknown committed
263

264 265 266
  if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME))))
    return;

267 268 269 270 271 272 273 274
  /* 
     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.
  */
275
  p= strmake(prefbuf, STRING_WITH_LEN("SQL_LOAD-"));
276 277 278 279
  p= int10_to_str(::server_id, p, 10);
  *(p++)= '-';
  *p= 0;

280 281 282
  for (i=0 ; i < (uint)dirp->number_off_files; i++)
  {
    file=dirp->dir_entry+i;
283
    if (is_prefix(file->name, prefbuf))
unknown's avatar
unknown committed
284 285 286 287
    {
      fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
      my_delete(fname, MYF(0));
    }
288 289 290 291
  }

  my_dirend(dirp);
}
unknown's avatar
SCRUM  
unknown committed
292
#endif
293 294


unknown's avatar
unknown committed
295
/*
296
  write_str()
unknown's avatar
unknown committed
297
*/
298

299
static bool write_str(IO_CACHE *file, char *str, uint length)
300
{
301 302
  uchar tmp[1];
  tmp[0]= (uchar) length;
303
  return (my_b_safe_write(file, tmp, sizeof(tmp)) ||
304
	  my_b_safe_write(file, (uchar*) str, length));
305 306 307
}


unknown's avatar
unknown committed
308
/*
309
  read_str()
unknown's avatar
unknown committed
310
*/
311

312 313
static inline int read_str(const char **buf, const char *buf_end,
                           const char **str, uint8 *len)
314
{
315
  if (*buf + ((uint) (uchar) **buf) >= buf_end)
316
    return 1;
317 318 319
  *len= (uint8) **buf;
  *str= (*buf)+1;
  (*buf)+= (uint) *len+1;
320 321 322
  return 0;
}

323

324 325 326
/*
  Transforms a string into "" or its expression in 0x... form.
*/
unknown's avatar
unknown committed
327

328
char *str_to_hex(char *to, const char *from, uint len)
329 330 331
{
  if (len)
  {
unknown's avatar
unknown committed
332 333 334
    *to++= '0';
    *to++= 'x';
    to= octet2hex(to, from, len);
335 336
  }
  else
unknown's avatar
unknown committed
337 338
    to= strmov(to, "\"\"");
  return to;                               // pointer to end 0 of 'to'
339 340
}

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
/*
  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++= '\'';
363
    ptr+= escape_string_for_mysql(csinfo, ptr, 0,
364 365 366 367 368 369 370 371
                                  from->ptr(), from->length());
    *ptr++='\'';
  }
  to->length(orig_len + ptr - beg);
  return 0;
}
#endif

372

373 374 375 376 377
/*
  Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
  commands just before it prints a query.
*/

378
#ifdef MYSQL_CLIENT
379

380 381 382
static void print_set_option(IO_CACHE* file, uint32 bits_changed,
                             uint32 option, uint32 flags, const char* name,
                             bool* need_comma)
383 384 385 386
{
  if (bits_changed & option)
  {
    if (*need_comma)
387 388
      my_b_printf(file,", ");
    my_b_printf(file,"%s=%d", name, test(flags & option));
389 390 391
    *need_comma= 1;
  }
}
392
#endif
393

unknown's avatar
unknown committed
394
/**************************************************************************
395
	Log_event methods (= the parent class of all events)
unknown's avatar
unknown committed
396
**************************************************************************/
397

unknown's avatar
unknown committed
398
/*
399
  Log_event::get_type_str()
unknown's avatar
unknown committed
400
*/
401

unknown's avatar
unknown committed
402 403
const char* Log_event::get_type_str()
{
404
  switch(get_type_code()) {
405
  case START_EVENT_V3:  return "Start_v3";
unknown's avatar
unknown committed
406 407 408 409 410
  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";
411
  case NEW_LOAD_EVENT:   return "New_load";
unknown's avatar
unknown committed
412
  case SLAVE_EVENT:  return "Slave";
413 414 415 416
  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";
417
  case RAND_EVENT: return "RAND";
418
  case XID_EVENT: return "Xid";
unknown's avatar
unknown committed
419
  case USER_VAR_EVENT: return "User var";
420
  case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
421 422 423 424
  case TABLE_MAP_EVENT: return "Table_map";
  case WRITE_ROWS_EVENT: return "Write_rows";
  case UPDATE_ROWS_EVENT: return "Update_rows";
  case DELETE_ROWS_EVENT: return "Delete_rows";
unknown's avatar
unknown committed
425 426
  case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
  case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
427
  case INCIDENT_EVENT: return "Incident";
428
  default: return "Unknown";				/* impossible */
unknown's avatar
unknown committed
429 430 431
  }
}

432

unknown's avatar
unknown committed
433
/*
434
  Log_event::Log_event()
unknown's avatar
unknown committed
435
*/
436

unknown's avatar
unknown committed
437
#ifndef MYSQL_CLIENT
438
Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
439
  :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg)
440
{
unknown's avatar
unknown committed
441 442
  server_id=	thd->server_id;
  when=		thd->start_time;
443
  cache_stmt=	using_trans;
444 445 446
}


unknown's avatar
unknown committed
447
/*
448 449 450 451
  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
452 453
*/

454
Log_event::Log_event()
455
  :temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
456 457
   thd(0)
{
unknown's avatar
unknown committed
458
  server_id=	::server_id;
459 460 461 462 463
  /*
    We can't call my_time() here as this would cause a call before
    my_init() is called
  */
  when=		0;
unknown's avatar
unknown committed
464
  log_pos=	0;
465
}
unknown's avatar
unknown committed
466
#endif /* !MYSQL_CLIENT */
467 468


unknown's avatar
unknown committed
469
/*
470
  Log_event::Log_event()
471
*/
472

473
Log_event::Log_event(const char* buf,
unknown's avatar
unknown committed
474
                     const Format_description_log_event* description_event)
475
  :temp_buf(0), cache_stmt(0)
476
{
477 478
#ifndef MYSQL_CLIENT
  thd = 0;
unknown's avatar
unknown committed
479
#endif
480 481
  when = uint4korr(buf);
  server_id = uint4korr(buf + SERVER_ID_OFFSET);
482
  if (description_event->binlog_version==1)
483
  {
484 485 486
    log_pos= 0;
    flags= 0;
    return;
487
  }
488 489 490
  /* 4.0 or newer */
  log_pos= uint4korr(buf + LOG_POS_OFFSET);
  /*
491 492 493 494 495 496 497 498
    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).
499 500 501
  */
  if (description_event->binlog_version==3 &&
      buf[EVENT_TYPE_OFFSET]<FORMAT_DESCRIPTION_EVENT && log_pos)
502
  {
503 504 505
      /*
        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
506 507
        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
508
        not have its log_pos (which is 0) changed or it will modify
509 510 511 512
        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).
513 514
      */
    log_pos+= uint4korr(buf + EVENT_LEN_OFFSET);
515
  }
516 517 518 519 520 521 522
  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))
  {
    /*
523 524
      These events always have a header which stops here (i.e. their
      header is FROZEN).
525 526
    */
    /*
527 528 529 530 531 532 533
      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.
534 535 536 537
    */
    return;
  }
  /* otherwise, go on with reading the header from buf (nothing now) */
538 539 540
}

#ifndef MYSQL_CLIENT
unknown's avatar
SCRUM  
unknown committed
541
#ifdef HAVE_REPLICATION
542

543
int Log_event::do_update_pos(Relay_log_info *rli)
544
{
545
  /*
546
    rli is null when (as far as I (Guilhem) know) the caller is
547 548 549 550
    Load_log_event::do_apply_event *and* that one is called from
    Execute_load_log_event::do_apply_event.  In this case, we don't
    do anything here ; Execute_load_log_event::do_apply_event will
    call Log_event::do_apply_event again later with the proper rli.
551 552 553
    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.
554 555

    Matz: I don't think we will need this check with this refactoring.
556
  */
557
  if (rli)
558
    rli->stmt_done(log_pos, when);
unknown's avatar
unknown committed
559

560 561
  return 0;                                   // Cannot fail currently
}
562

563 564

Log_event::enum_skip_reason
565
Log_event::do_shall_skip(Relay_log_info *rli)
566 567 568 569 570 571 572 573 574
{
  DBUG_PRINT("info", ("ev->server_id=%lu, ::server_id=%lu,"
                      " rli->replicate_same_server_id=%d,"
                      " rli->slave_skip_counter=%d",
                      (ulong) server_id, (ulong) ::server_id,
                      rli->replicate_same_server_id,
                      rli->slave_skip_counter));
  if (server_id == ::server_id && !rli->replicate_same_server_id)
    return EVENT_SKIP_IGNORE;
575 576 577
  else if (rli->slave_skip_counter > 0)
    return EVENT_SKIP_COUNT;
  else
578
    return EVENT_SKIP_NOT;
579
}
unknown's avatar
unknown committed
580

581

unknown's avatar
unknown committed
582
/*
583
  Log_event::pack_info()
unknown's avatar
unknown committed
584
*/
585

586
void Log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
587
{
588
  protocol->store("", &my_charset_bin);
unknown's avatar
unknown committed
589 590 591
}


unknown's avatar
unknown committed
592
/*
593
  Log_event::net_send()
unknown's avatar
unknown committed
594

595
  Only called by SHOW BINLOG EVENTS
unknown's avatar
unknown committed
596
*/
unknown's avatar
SCRUM  
unknown committed
597

598
int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
599
{
unknown's avatar
unknown committed
600 601
  const char *p= strrchr(log_name, FN_LIBCHAR);
  const char *event_type;
602 603
  if (p)
    log_name = p + 1;
604

605
  protocol->prepare_for_resend();
606
  protocol->store(log_name, &my_charset_bin);
607
  protocol->store((ulonglong) pos);
608
  event_type = get_type_str();
609
  protocol->store(event_type, strlen(event_type), &my_charset_bin);
610 611 612 613
  protocol->store((uint32) server_id);
  protocol->store((ulonglong) log_pos);
  pack_info(protocol);
  return protocol->write();
614
}
unknown's avatar
SCRUM  
unknown committed
615 616 617
#endif /* HAVE_REPLICATION */


unknown's avatar
unknown committed
618
/*
unknown's avatar
SCRUM  
unknown committed
619
  Log_event::init_show_field_list()
unknown's avatar
unknown committed
620
*/
unknown's avatar
SCRUM  
unknown committed
621 622 623 624

void Log_event::init_show_field_list(List<Item>* field_list)
{
  field_list->push_back(new Item_empty_string("Log_name", 20));
625
  field_list->push_back(new Item_return_int("Pos", MY_INT32_NUM_DECIMAL_DIGITS,
unknown's avatar
SCRUM  
unknown committed
626 627 628 629
					    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));
630 631
  field_list->push_back(new Item_return_int("End_log_pos",
                                            MY_INT32_NUM_DECIMAL_DIGITS,
unknown's avatar
SCRUM  
unknown committed
632 633 634 635
					    MYSQL_TYPE_LONGLONG));
  field_list->push_back(new Item_empty_string("Info", 20));
}

636

unknown's avatar
unknown committed
637
/*
638
  Log_event::write()
unknown's avatar
unknown committed
639
*/
640

641
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
unknown's avatar
unknown committed
642
{
643
  uchar header[LOG_EVENT_HEADER_LEN];
644
  ulong now;
645
  DBUG_ENTER("Log_event::write_header");
unknown's avatar
unknown committed
646

647 648
  /* Store number of bytes that will be written by this event */
  data_written= event_data_length + sizeof(header);
649

650 651 652 653
  /*
    log_pos != 0 if this is relay-log event. In this case we should not
    change the position
  */
654

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
  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;
  }

unknown's avatar
unknown committed
695
  now= (ulong) get_time();                              // Query start time
696

697 698 699 700 701 702 703
  /*
    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).
  */

704
  int4store(header, now);              // timestamp
705 706 707 708 709 710 711
  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
712 713 714
}


unknown's avatar
unknown committed
715
/*
716
  Log_event::read_log_event()
717 718 719 720

  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
721
*/
722

723
int Log_event::read_log_event(IO_CACHE* file, String* packet,
724
			      pthread_mutex_t* log_lock)
unknown's avatar
unknown committed
725 726
{
  ulong data_len;
727
  int result=0;
728
  char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
729
  DBUG_ENTER("Log_event::read_log_event");
730

731
  if (log_lock)
732
    pthread_mutex_lock(log_lock);
733
  if (my_b_read(file, (uchar*) buf, sizeof(buf)))
734
  {
735 736 737 738 739
    /*
      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.
    */
740
    DBUG_PRINT("error",("file->error: %d", file->error));
741 742 743
    if (!file->error)
      result= LOG_READ_EOF;
    else
744
      result= (file->error > 0 ? LOG_READ_TRUNC : LOG_READ_IO);
745
    goto end;
746
  }
747
  data_len= uint4korr(buf + EVENT_LEN_OFFSET);
748
  if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
unknown's avatar
unknown committed
749
      data_len > current_thd->variables.max_allowed_packet)
750
  {
751
    DBUG_PRINT("error",("data_len: %ld", data_len));
752
    result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
753 754
	     LOG_READ_TOO_LARGE);
    goto end;
755
  }
756 757 758 759 760 761 762 763

  /* Append the log event header to packet */
  if (packet->append(buf, sizeof(buf)))
  {
    /* Failed to allocate packet */
    result= LOG_READ_MEM;
    goto end;
  }
764
  data_len-= LOG_EVENT_MINIMAL_HEADER_LEN;
765 766
  if (data_len)
  {
767
    /* Append rest of event, read directly from file into packet */
768
    if (packet->append(file, data_len))
769
    {
770
      /*
771 772 773 774 775 776 777 778 779 780
        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)
781
      */
782 783
      result= (my_errno == ENOMEM ? LOG_READ_MEM :
               (file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO));
784
      /* Implicit goto end; */
785
    }
786
  }
787 788 789 790

end:
  if (log_lock)
    pthread_mutex_unlock(log_lock);
791
  DBUG_RETURN(result);
unknown's avatar
unknown committed
792
}
unknown's avatar
unknown committed
793
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
794

unknown's avatar
unknown committed
795
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
796 797
#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock);
#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock);
798
#else
799
#define UNLOCK_MUTEX
800 801 802
#define LOCK_MUTEX
#endif

unknown's avatar
unknown committed
803
/*
804 805
  Log_event::read_log_event()

unknown's avatar
unknown committed
806
  NOTE:
807
    Allocates memory;  The caller is responsible for clean-up.
unknown's avatar
unknown committed
808
*/
809

unknown's avatar
unknown committed
810
#ifndef MYSQL_CLIENT
811 812
Log_event* Log_event::read_log_event(IO_CACHE* file,
				     pthread_mutex_t* log_lock,
813 814
                                     const Format_description_log_event
                                     *description_event)
unknown's avatar
unknown committed
815
#else
816
Log_event* Log_event::read_log_event(IO_CACHE* file,
817 818
                                     const Format_description_log_event
                                     *description_event)
819
#endif
unknown's avatar
unknown committed
820
{
821
  DBUG_ENTER("Log_event::read_log_event");
822
  DBUG_ASSERT(description_event != 0);
823 824 825 826 827
  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
828 829
    of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's
    "minimal" over the set {MySQL >=4.0}).
830 831 832
  */
  uint header_size= min(description_event->common_header_len,
                        LOG_EVENT_MINIMAL_HEADER_LEN);
833

834
  LOCK_MUTEX;
unknown's avatar
unknown committed
835
  DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file)));
836
  if (my_b_read(file, (uchar *) head, header_size))
837
  {
838 839
    DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
failed my_b_read"));
unknown's avatar
unknown committed
840
    UNLOCK_MUTEX;
841
    /*
842 843 844
      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.
845
    */
846
    DBUG_RETURN(0);
847
  }
848
  uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
849 850 851
  char *buf= 0;
  const char *error= 0;
  Log_event *res=  0;
852 853
#ifndef max_allowed_packet
  THD *thd=current_thd;
unknown's avatar
unknown committed
854
  uint max_allowed_packet= thd ? thd->variables.max_allowed_packet : ~(ulong)0;
855
#endif
unknown's avatar
unknown committed
856

857
  if (data_len > max_allowed_packet)
unknown's avatar
unknown committed
858
  {
859 860
    error = "Event too big";
    goto err;
unknown's avatar
unknown committed
861 862
  }

863
  if (data_len < header_size)
unknown's avatar
unknown committed
864
  {
865 866
    error = "Event too small";
    goto err;
unknown's avatar
unknown committed
867
  }
868 869

  // some events use the extra byte to null-terminate strings
870
  if (!(buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
871 872 873
  {
    error = "Out of memory";
    goto err;
unknown's avatar
unknown committed
874
  }
875
  buf[data_len] = 0;
876
  memcpy(buf, head, header_size);
877
  if (my_b_read(file, (uchar*) buf + header_size, data_len - header_size))
878 879 880 881
  {
    error = "read error";
    goto err;
  }
882
  if ((res= read_log_event(buf, data_len, &error, description_event)))
883
    res->register_temp_buf(buf);
884

885
err:
unknown's avatar
unknown committed
886
  UNLOCK_MUTEX;
887
  if (!res)
888
  {
889
    DBUG_ASSERT(error != 0);
890 891
    sql_print_error("Error in Log_event::read_log_event(): "
                    "'%s', data_len: %d, event_type: %d",
892
		    error,data_len,head[EVENT_TYPE_OFFSET]);
893
    my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
894 895 896 897 898 899 900 901 902
    /*
      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;
903
  }
904
  DBUG_RETURN(res);
unknown's avatar
unknown committed
905 906
}

907

unknown's avatar
unknown committed
908
/*
909
  Log_event::read_log_event()
910 911
  Binlog format tolerance is in (buf, event_len, description_event)
  constructors.
unknown's avatar
unknown committed
912
*/
913

914 915 916
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
917
{
918 919
  Log_event* ev;
  DBUG_ENTER("Log_event::read_log_event(char*,...)");
920
  DBUG_ASSERT(description_event != 0);
921
  DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
922
  /* Check the integrity */
923
  if (event_len < EVENT_LEN_OFFSET ||
924
      buf[EVENT_TYPE_OFFSET] >= ENUM_END_EVENT ||
925 926 927
      (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
  {
    *error="Sanity check failed";		// Needed to free buffer
unknown's avatar
unknown committed
928
    DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
929
  }
930

931
  switch(buf[EVENT_TYPE_OFFSET]) {
unknown's avatar
unknown committed
932
  case QUERY_EVENT:
unknown's avatar
unknown committed
933
    ev  = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
934
    break;
unknown's avatar
unknown committed
935
  case LOAD_EVENT:
unknown's avatar
unknown committed
936
    ev = new Load_log_event(buf, event_len, description_event);
unknown's avatar
unknown committed
937
    break;
938
  case NEW_LOAD_EVENT:
939
    ev = new Load_log_event(buf, event_len, description_event);
940
    break;
unknown's avatar
unknown committed
941
  case ROTATE_EVENT:
942
    ev = new Rotate_log_event(buf, event_len, description_event);
943
    break;
unknown's avatar
SCRUM  
unknown committed
944
#ifdef HAVE_REPLICATION
945
  case SLAVE_EVENT: /* can never happen (unused event) */
946 947
    ev = new Slave_log_event(buf, event_len);
    break;
unknown's avatar
SCRUM  
unknown committed
948
#endif /* HAVE_REPLICATION */
949
  case CREATE_FILE_EVENT:
950
    ev = new Create_file_log_event(buf, event_len, description_event);
951 952
    break;
  case APPEND_BLOCK_EVENT:
953
    ev = new Append_block_log_event(buf, event_len, description_event);
954 955
    break;
  case DELETE_FILE_EVENT:
956
    ev = new Delete_file_log_event(buf, event_len, description_event);
957 958
    break;
  case EXEC_LOAD_EVENT:
959
    ev = new Execute_load_log_event(buf, event_len, description_event);
960
    break;
961 962
  case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
    ev = new Start_log_event_v3(buf, description_event);
963 964
    break;
  case STOP_EVENT:
965
    ev = new Stop_log_event(buf, description_event);
966 967
    break;
  case INTVAR_EVENT:
968
    ev = new Intvar_log_event(buf, description_event);
969
    break;
970 971 972
  case XID_EVENT:
    ev = new Xid_log_event(buf, description_event);
    break;
unknown's avatar
unknown committed
973
  case RAND_EVENT:
974
    ev = new Rand_log_event(buf, description_event);
unknown's avatar
unknown committed
975
    break;
unknown's avatar
unknown committed
976
  case USER_VAR_EVENT:
977 978 979 980
    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
981
    break;
982
#if defined(HAVE_REPLICATION) 
983 984 985 986 987 988 989 990 991
  case PRE_GA_WRITE_ROWS_EVENT:
    ev = new Write_rows_log_event_old(buf, event_len, description_event);
    break;
  case PRE_GA_UPDATE_ROWS_EVENT:
    ev = new Update_rows_log_event_old(buf, event_len, description_event);
    break;
  case PRE_GA_DELETE_ROWS_EVENT:
    ev = new Delete_rows_log_event_old(buf, event_len, description_event);
    break;
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
  case WRITE_ROWS_EVENT:
    ev = new Write_rows_log_event(buf, event_len, description_event);
    break;
  case UPDATE_ROWS_EVENT:
    ev = new Update_rows_log_event(buf, event_len, description_event);
    break;
  case DELETE_ROWS_EVENT:
    ev = new Delete_rows_log_event(buf, event_len, description_event);
    break;
  case TABLE_MAP_EVENT:
    ev = new Table_map_log_event(buf, event_len, description_event);
    break;
#endif
unknown's avatar
unknown committed
1005 1006 1007 1008
  case BEGIN_LOAD_QUERY_EVENT:
    ev = new Begin_load_query_log_event(buf, event_len, description_event);
    break;
  case EXECUTE_LOAD_QUERY_EVENT:
1009 1010 1011 1012
    ev= new Execute_load_query_log_event(buf, event_len, description_event);
    break;
  case INCIDENT_EVENT:
    ev = new Incident_log_event(buf, event_len, description_event);
unknown's avatar
unknown committed
1013
    break;
1014
  default:
1015 1016
    DBUG_PRINT("error",("Unknown event code: %d",
                        (int) buf[EVENT_TYPE_OFFSET]));
1017
    ev= NULL;
1018
    break;
unknown's avatar
unknown committed
1019
  }
1020

1021 1022 1023 1024
  DBUG_PRINT("read_event", ("%s(type_code: %d; event_len: %d)",
                            ev ? ev->get_type_str() : "<unknown>",
                            buf[EVENT_TYPE_OFFSET],
                            event_len));
1025
  /*
1026 1027 1028 1029 1030 1031 1032
    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'.
1033
  */
1034
  if (!ev || !ev->is_valid())
1035
  {
1036 1037
    DBUG_PRINT("error",("Found invalid event in binary log"));

1038
    delete ev;
1039
#ifdef MYSQL_CLIENT
1040
    if (!force_opt) /* then mysqlbinlog dies */
1041 1042
    {
      *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
1043
      DBUG_RETURN(0);
1044
    }
1045
    ev= new Unknown_log_event(buf, description_event);
1046 1047
#else
    *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
1048
    DBUG_RETURN(0);
1049
#endif
1050
  }
unknown's avatar
unknown committed
1051
  DBUG_RETURN(ev);  
unknown's avatar
unknown committed
1052 1053
}

1054
#ifdef MYSQL_CLIENT
1055

unknown's avatar
unknown committed
1056
/*
1057
  Log_event::print_header()
unknown's avatar
unknown committed
1058
*/
1059

1060 1061 1062
void Log_event::print_header(IO_CACHE* file,
                             PRINT_EVENT_INFO* print_event_info,
                             bool is_more __attribute__((unused)))
1063
{
1064
  char llbuff[22];
unknown's avatar
unknown committed
1065
  my_off_t hexdump_from= print_event_info->hexdump_from;
1066
  DBUG_ENTER("Log_event::print_header");
unknown's avatar
unknown committed
1067

1068
  my_b_printf(file, "#");
1069
  print_timestamp(file);
1070 1071
  my_b_printf(file, " server id %d  end_log_pos %s ", server_id,
              llstr(log_pos,llbuff));
1072

1073
  /* mysqlbinlog --hexdump */
unknown's avatar
unknown committed
1074
  if (print_event_info->hexdump_from)
1075
  {
1076
    my_b_printf(file, "\n");
1077 1078 1079
    uchar *ptr= (uchar*)temp_buf;
    my_off_t size=
      uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN;
1080 1081
    my_off_t i;

1082 1083 1084 1085
    /* 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
1086
    /* Pretty-print event common header if header is exactly 19 bytes */
unknown's avatar
unknown committed
1087
    if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
unknown's avatar
unknown committed
1088
    {
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
      char emit_buf[256];               // Enough for storing one line
      my_b_printf(file, "# Position  Timestamp   Type   Master ID        "
                  "Size      Master Pos    Flags \n");
      int const bytes_written=
        my_snprintf(emit_buf, sizeof(emit_buf),
                    "# %8.8lx %02x %02x %02x %02x   %02x   "
                    "%02x %02x %02x %02x   %02x %02x %02x %02x   "
                    "%02x %02x %02x %02x   %02x %02x\n",
                    (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]);
      DBUG_ASSERT(bytes_written >= 0);
1102 1103
      DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
      my_b_write(file, (uchar*) emit_buf, bytes_written);
unknown's avatar
unknown committed
1104 1105 1106
      ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
      hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
    }
1107 1108 1109 1110 1111

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

1116
      *c++= my_isalnum(&my_charset_bin, *ptr) ? *ptr : '.';
1117 1118 1119

      if (i % 16 == 15)
      {
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
        /*
          my_b_printf() does not support full printf() formats, so we
          have to do it this way.

          TODO: Rewrite my_b_printf() to support full printf() syntax.
         */
        char emit_buf[256];
        int const bytes_written=
          my_snprintf(emit_buf, sizeof(emit_buf),
                      "# %8.8lx %-48.48s |%16s|\n",
                      (unsigned long) (hexdump_from + (i & 0xfffffff0)),
                      hex_string, char_string);
        DBUG_ASSERT(bytes_written >= 0);
1133 1134
        DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
	my_b_write(file, (uchar*) emit_buf, bytes_written);
1135 1136 1137 1138
	hex_string[0]= 0;
	char_string[0]= 0;
	c= char_string;
	h= hex_string;
1139
      }
1140
      else if (i % 8 == 7) *h++ = ' ';
1141
    }
1142
    *c= '\0';
1143

unknown's avatar
unknown committed
1144
    if (hex_string[0])
1145 1146 1147 1148
    {
      char emit_buf[256];
      int const bytes_written=
        my_snprintf(emit_buf, sizeof(emit_buf),
1149
                    "# %8.8lx %-48.48s |%s|\n",
1150 1151 1152
                    (unsigned long) (hexdump_from + (i & 0xfffffff0)),
                    hex_string, char_string);
      DBUG_ASSERT(bytes_written >= 0);
1153 1154
      DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
      my_b_write(file, (uchar*) emit_buf, bytes_written);
1155
    }
1156 1157 1158 1159
    /*
      need a # to prefix the rest of printouts for example those of
      Rows_log_event::print_helper().
    */
1160
    my_b_write(file, reinterpret_cast<const uchar*>("# "), 2);
1161
  }
1162
  DBUG_VOID_RETURN;
1163 1164
}

1165

1166 1167 1168
void Log_event::print_base64(IO_CACHE* file,
                             PRINT_EVENT_INFO* print_event_info,
                             bool more)
1169
{
1170
  const uchar *ptr= (const uchar *)temp_buf;
1171
  uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
1172 1173
  DBUG_ENTER("Log_event::print_base64");

1174
  size_t const tmp_str_sz= base64_needed_encoded_length((int) size);
1175
  char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME));
1176 1177 1178
  if (!tmp_str) {
    fprintf(stderr, "\nError: Out of memory. "
            "Could not print correct binlog event.\n");
1179
    DBUG_VOID_RETURN;
1180
  }
1181

unknown's avatar
unknown committed
1182 1183 1184 1185
  if (base64_encode(ptr, (size_t) size, tmp_str))
  {
    DBUG_ASSERT(0);
  }
1186 1187 1188 1189 1190 1191 1192

  if (my_b_tell(file) == 0)
    my_b_printf(file, "\nBINLOG '\n");

  my_b_printf(file, "%s\n", tmp_str);

  if (!more)
unknown's avatar
unknown committed
1193
    my_b_printf(file, "'%s\n", print_event_info->delimiter);
1194

1195
  my_free(tmp_str, MYF(0));
1196
  DBUG_VOID_RETURN;
1197 1198 1199
}


unknown's avatar
unknown committed
1200
/*
1201
  Log_event::print_timestamp()
unknown's avatar
unknown committed
1202
*/
1203

1204
void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
unknown's avatar
unknown committed
1205
{
unknown's avatar
unknown committed
1206
  struct tm *res;
1207
  DBUG_ENTER("Log_event::print_timestamp");
1208 1209
  if (!ts)
    ts = &when;
1210 1211
#ifdef MYSQL_SERVER				// This is always false
  struct tm tm_tmp;
unknown's avatar
unknown committed
1212
  localtime_r(ts,(res= &tm_tmp));
unknown's avatar
unknown committed
1213
#else
1214
  res=localtime(ts);
unknown's avatar
unknown committed
1215
#endif
1216

1217 1218 1219 1220 1221 1222 1223 1224
  my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d",
              res->tm_year % 100,
              res->tm_mon+1,
              res->tm_mday,
              res->tm_hour,
              res->tm_min,
              res->tm_sec);
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1225 1226
}

unknown's avatar
unknown committed
1227
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
1228 1229


unknown's avatar
unknown committed
1230
/**************************************************************************
unknown's avatar
unknown committed
1231
	Query_log_event methods
unknown's avatar
unknown committed
1232
**************************************************************************/
1233

unknown's avatar
SCRUM  
unknown committed
1234
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1235

unknown's avatar
unknown committed
1236
/*
1237
  Query_log_event::pack_info()
1238 1239 1240 1241
  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
1242
*/
1243

1244
void Query_log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
1245
{
1246
  // TODO: show the catalog ??
1247
  char *buf, *pos;
1248
  if (!(buf= (char*) my_malloc(9 + db_len + q_len, MYF(MY_WME))))
1249
    return;
1250 1251
  pos= buf;
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
1252
      && db && db_len)
1253
  {
1254 1255
    pos= strmov(buf, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
1256
    pos= strmov(pos+db_len, "`; ");
1257
  }
1258
  if (query && q_len)
1259 1260 1261 1262
  {
    memcpy(pos, query, q_len);
    pos+= q_len;
  }
1263
  protocol->store(buf, pos-buf, &my_charset_bin);
1264
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
1265
}
unknown's avatar
SCRUM  
unknown committed
1266
#endif
1267

1268
#ifndef MYSQL_CLIENT
1269

1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
/* 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
1282
/*
1283
  Query_log_event::write()
unknown's avatar
unknown committed
1284

1285 1286 1287 1288
  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
1289
*/
1290

1291
bool Query_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
1292
{
1293 1294 1295 1296 1297
  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
1298
            1+6+           // code of charset and charset
1299
            1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name
1300 1301
            1+2+           // code of lc_time_names and lc_time_names_number
            1+2            // code of charset_database and charset_database_number
1302
            ], *start, *start_of_status;
1303
  ulong event_length;
unknown's avatar
unknown committed
1304

1305
  if (!query)
1306 1307
    return 1;                                   // Something wrong with event

unknown's avatar
unknown committed
1308 1309 1310 1311 1312
  /*
    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.
1313
    Now imagine we (write()) are called by the slave SQL thread (we are
unknown's avatar
unknown committed
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
    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);
1346 1347 1348 1349
  int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
  buf[Q_DB_LEN_OFFSET] = (char) db_len;
  int2store(buf + Q_ERR_CODE_OFFSET, error_code);

1350 1351 1352 1353 1354 1355 1356 1357
  /*
    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)
  {
1358
    *start++= Q_FLAGS2_CODE;
1359 1360 1361 1362 1363
    int4store(start, flags2);
    start+= 4;
  }
  if (sql_mode_inited)
  {
1364
    *start++= Q_SQL_MODE_CODE;
unknown's avatar
unknown committed
1365
    int8store(start, (ulonglong)sql_mode);
1366 1367
    start+= 8;
  }
1368
  if (catalog_len) // i.e. this var is inited (false for 4.0 events)
1369
  {
1370 1371
    write_str_with_code_and_len((char **)(&start),
                                catalog, catalog_len, Q_CATALOG_NZ_CODE);
1372
    /*
1373 1374 1375 1376 1377 1378
      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
1379 1380 1381 1382 1383
      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
1384 1385
      Q_AUTO_INCREMENT*, Q_CHARSET and so replication will fail silently in
      various ways. Documented that you should not mix alpha/beta versions if
1386 1387 1388
      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.
1389 1390 1391 1392 1393 1394 1395 1396 1397
    */
  }
  if (auto_increment_increment != 1)
  {
    *start++= Q_AUTO_INCREMENT;
    int2store(start, auto_increment_increment);
    int2store(start+2, auto_increment_offset);
    start+= 4;
  }
1398 1399
  if (charset_inited)
  {
1400
    *start++= Q_CHARSET_CODE;
1401 1402 1403
    memcpy(start, charset, 6);
    start+= 6;
  }
1404 1405 1406 1407 1408 1409 1410 1411 1412
  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;
  }
1413 1414 1415 1416 1417 1418 1419
  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;
  }
1420 1421 1422 1423 1424 1425 1426
  if (charset_database_number)
  {
    DBUG_ASSERT(charset_database_number <= 0xFFFF);
    *start++= Q_CHARSET_DATABASE_CODE;
    int2store(start, charset_database_number);
    start+= 2;
  }
1427 1428
  /*
    Here there could be code like
1429
    if (command-line-option-which-says-"log_this_variable" && inited)
1430
    {
1431
    *start++= Q_THIS_VARIABLE_CODE;
1432 1433 1434 1435 1436 1437 1438
    int4store(start, this_variable);
    start+= 4;
    }
  */
  
  /* Store length of status variables */
  status_vars_len= (uint) (start-start_of_status);
1439
  DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS);
1440 1441 1442 1443 1444 1445
  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
1446
  event_length= (uint) (start-buf) + get_post_header_size_for_derived() + db_len + 1 + q_len;
1447 1448

  return (write_header(file, event_length) ||
1449
          my_b_safe_write(file, (uchar*) buf, QUERY_HEADER_LEN) ||
unknown's avatar
unknown committed
1450
          write_post_header_for_derived(file) ||
1451
          my_b_safe_write(file, (uchar*) start_of_status,
unknown's avatar
unknown committed
1452
                          (uint) (start-start_of_status)) ||
1453 1454
          my_b_safe_write(file, (db) ? (uchar*) db : (uchar*)"", db_len + 1) ||
          my_b_safe_write(file, (uchar*) query, q_len)) ? 1 : 0;
unknown's avatar
unknown committed
1455 1456
}

1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
/*
  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)
{
}

1469

unknown's avatar
unknown committed
1470
/*
1471 1472
  SYNOPSIS
    Query_log_event::Query_log_event()
1473
      thd_arg           - thread handle
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
      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
1486
*/
1487
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
1488
				 ulong query_length, bool using_trans,
1489 1490
				 bool suppress_use,
                                 THD::killed_state killed_status_arg)
1491
  :Log_event(thd_arg,
1492 1493 1494
             (thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F :
              0) |
             (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
1495
	     using_trans),
1496
   data_buf(0), query(query_arg), catalog(thd_arg->catalog),
1497
   db(thd_arg->db), q_len((uint32) query_length),
unknown's avatar
unknown committed
1498 1499
   thread_id(thd_arg->thread_id),
   /* save the original thread id; we already know the server id */
1500
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
1501
   flags2_inited(1), sql_mode_inited(1), charset_inited(1),
1502 1503
   sql_mode(thd_arg->variables.sql_mode),
   auto_increment_increment(thd_arg->variables.auto_increment_increment),
1504
   auto_increment_offset(thd_arg->variables.auto_increment_offset),
1505 1506
   lc_time_names_number(thd_arg->variables.lc_time_names->number),
   charset_database_number(0)
1507 1508
{
  time_t end_time;
1509 1510 1511 1512 1513 1514

  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 :
1515
     thd_arg->killed_errno());
1516
  
1517
  time(&end_time);
1518
  exec_time = (ulong) (end_time  - thd_arg->start_time);
1519
  catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
1520
  /* status_vars_len is set just before writing the event */
1521
  db_len = (db) ? (uint32) strlen(db) : 0;
1522 1523 1524
  if (thd_arg->variables.collation_database != thd_arg->db_charset)
    charset_database_number= thd_arg->variables.collation_database->number;
  
1525 1526
  /*
    If we don't use flags2 for anything else than options contained in
1527
    thd_arg->options, it would be more efficient to flags2=thd_arg->options
1528 1529 1530 1531
    (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
1532
  flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG);
1533 1534 1535
  DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256);
  DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256);
  DBUG_ASSERT(thd_arg->variables.collation_server->number < 256*256);
1536 1537 1538
  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);
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550
  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
1551 1552
  DBUG_PRINT("info",("Query_log_event has flags2: %lu  sql_mode: %lu",
                     (ulong) flags2, sql_mode));
1553
}
unknown's avatar
unknown committed
1554
#endif /* MYSQL_CLIENT */
1555

unknown's avatar
unknown committed
1556

1557 1558
/* 2 utility functions for the next method */

1559 1560 1561 1562 1563 1564 1565 1566
/* 
  Get the pointer for a string (src) that contains the length in
  the first byte. Set the output string (dst) to the string value
  and place the length of the string in the byte after the string.
*/
static void get_str_len_and_pointer(const Log_event::Byte **src, 
                                    const char **dst, 
                                    uint *len)
1567 1568
{
  if ((*len= **src))
1569 1570
    *dst= (char *)*src + 1;                          // Will be copied later
  (*src)+= *len + 1;
1571 1572
}

1573 1574 1575
static void copy_str_and_move(const char **src, 
                              Log_event::Byte **dst, 
                              uint len)
1576 1577
{
  memcpy(*dst, *src, len);
1578
  *src= (const char *)*dst;
1579 1580 1581 1582
  (*dst)+= len;
  *(*dst)++= 0;
}

unknown's avatar
unknown committed
1583
/*
1584
  Query_log_event::Query_log_event()
1585
  This is used by the SQL slave thread to prepare the event before execution.
unknown's avatar
unknown committed
1586
*/
1587

1588
Query_log_event::Query_log_event(const char* buf, uint event_len,
1589 1590
                                 const Format_description_log_event
                                 *description_event,
unknown's avatar
unknown committed
1591
                                 Log_event_type event_type)
1592
  :Log_event(buf, description_event), data_buf(0), query(NullS),
1593
   db(NullS), catalog_len(0), status_vars_len(0),
1594
   flags2_inited(0), sql_mode_inited(0), charset_inited(0),
1595
   auto_increment_increment(1), auto_increment_offset(1),
1596
   time_zone_len(0), lc_time_names_number(0), charset_database_number(0)
unknown's avatar
unknown committed
1597 1598
{
  ulong data_len;
1599 1600
  uint32 tmp;
  uint8 common_header_len, post_header_len;
1601 1602
  Log_event::Byte *start;
  const Log_event::Byte *end;
1603
  bool catalog_nz= 1;
1604 1605 1606
  DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");

  common_header_len= description_event->common_header_len;
unknown's avatar
unknown committed
1607
  post_header_len= description_event->post_header_len[event_type-1];
unknown's avatar
unknown committed
1608
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
                     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);
1623
  db_len = (uint)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
1624 1625 1626 1627 1628 1629 1630 1631 1632
  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)
1633
  {
1634 1635 1636 1637 1638
    status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET);
    data_len-= status_vars_len;
    DBUG_PRINT("info", ("Query_log_event has status_vars_len: %u",
                        (uint) status_vars_len));
    tmp-= 2;
1639
  }
unknown's avatar
unknown committed
1640 1641 1642 1643 1644 1645
  /*
    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...)
  */

1646 1647
  /* variable-part: the status vars; only in MySQL 5.0  */
  
1648 1649 1650
  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;)
1651
  {
1652 1653 1654 1655
    switch (*pos++) {
    case Q_FLAGS2_CODE:
      flags2_inited= 1;
      flags2= uint4korr(pos);
unknown's avatar
unknown committed
1656
      DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", (ulong) flags2));
1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
      pos+= 4;
      break;
    case Q_SQL_MODE_CODE:
    {
#ifndef DBUG_OFF
      char buff[22];
#endif
      sql_mode_inited= 1;
      sql_mode= (ulong) uint8korr(pos); // QQ: Fix when sql_mode is ulonglong
      DBUG_PRINT("info",("In Query_log_event, read sql_mode: %s",
			 llstr(sql_mode, buff)));
      pos+= 8;
      break;
    }
1671
    case Q_CATALOG_NZ_CODE:
1672
      get_str_len_and_pointer(&pos, &catalog, &catalog_len);
1673 1674 1675 1676 1677 1678
      break;
    case Q_AUTO_INCREMENT:
      auto_increment_increment= uint2korr(pos);
      auto_increment_offset=    uint2korr(pos+2);
      pos+= 4;
      break;
1679 1680 1681 1682 1683 1684 1685
    case Q_CHARSET_CODE:
    {
      charset_inited= 1;
      memcpy(charset, pos, 6);
      pos+= 6;
      break;
    }
1686 1687
    case Q_TIME_ZONE_CODE:
    {
1688
      get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len);
1689 1690
      break;
    }
1691 1692 1693 1694 1695 1696
    case Q_CATALOG_CODE: /* for 5.0.x where 0<=x<=3 masters */
      if ((catalog_len= *pos))
        catalog= (char*) pos+1;                           // Will be copied later
      pos+= catalog_len+2; // leap over end 0
      catalog_nz= 0; // catalog has end 0 in event
      break;
1697 1698 1699 1700
    case Q_LC_TIME_NAMES_CODE:
      lc_time_names_number= uint2korr(pos);
      pos+= 2;
      break;
1701 1702 1703 1704
    case Q_CHARSET_DATABASE_CODE:
      charset_database_number= uint2korr(pos);
      pos+= 2;
      break;
1705 1706 1707 1708
    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)));
1709
      pos= (const uchar*) end;                         // Break loop
1710
    }
1711
  }
1712
  
1713
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
1714 1715 1716 1717 1718 1719
  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))))
1720
#else
1721 1722 1723 1724
  if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
                                             time_zone_len + 1 +
                                             data_len + 1,
                                             MYF(MY_WME))))
1725
#endif
unknown's avatar
unknown committed
1726
      DBUG_VOID_RETURN;
1727
  if (catalog_len)                                  // If catalog is given
1728
  {
1729
    if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3.
1730
      copy_str_and_move(&catalog, &start, catalog_len);
1731 1732 1733
    else
    {
      memcpy(start, catalog, catalog_len+1); // copy end 0
1734
      catalog= (const char *)start;
1735 1736
      start+= catalog_len+1;
    }
1737
  }
1738
  if (time_zone_len)
1739
    copy_str_and_move(&time_zone_str, &start, time_zone_len);
1740

1741
  /* A 2nd variable part; this is common to all versions */ 
1742
  memcpy((char*) start, end, data_len);          // Copy db and query
1743
  start[data_len]= '\0';              // End query with \0 (For safetly)
1744 1745
  db= (char *)start;
  query= (char *)(start + db_len + 1);
1746 1747
  q_len= data_len - db_len -1;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1748 1749
}

1750

unknown's avatar
unknown committed
1751
/*
1752
  Query_log_event::print()
unknown's avatar
unknown committed
1753
*/
1754

1755
#ifdef MYSQL_CLIENT
1756
void Query_log_event::print_query_header(IO_CACHE* file,
unknown's avatar
unknown committed
1757
					 PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
1758
{
1759
  // TODO: print the catalog ??
1760
  char buff[40],*end;				// Enough for SET TIMESTAMP
1761 1762 1763
  bool different_db= 1;
  uint32 tmp;

unknown's avatar
unknown committed
1764
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
1765
  {
1766 1767 1768 1769
    print_header(file, print_event_info, FALSE);
    my_b_printf(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
1770 1771
  }

unknown's avatar
unknown committed
1772
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db)
1773
  {
unknown's avatar
unknown committed
1774 1775
    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
1776
    if (db[0] && different_db) 
unknown's avatar
unknown committed
1777
      my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter);
1778
  }
1779

1780
  end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
1781
  end= strmov(end, print_event_info->delimiter);
1782
  *end++='\n';
1783
  my_b_write(file, (uchar*) buff, (uint) (end-buff));
unknown's avatar
unknown committed
1784
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
unknown's avatar
unknown committed
1785 1786
    my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
                (ulong)thread_id, print_event_info->delimiter);
1787

1788
  /*
1789 1790 1791
    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).
1792 1793 1794 1795
  */
  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
1796
    if (likely(print_event_info->flags2_inited)) 
1797
      /* All bits which have changed */
unknown's avatar
unknown committed
1798
      tmp= (print_event_info->flags2) ^ flags2;
1799 1800
    else /* that's the first Query event we read */
    {
unknown's avatar
unknown committed
1801
      print_event_info->flags2_inited= 1;
1802 1803 1804 1805 1806 1807
      tmp= ~((uint32)0); /* all bits have changed */
    }

    if (unlikely(tmp)) /* some bits have changed */
    {
      bool need_comma= 0;
1808
      my_b_printf(file, "SET ");
1809 1810 1811 1812 1813 1814
      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);
unknown's avatar
unknown committed
1815
      my_b_printf(file,"%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
1816
      print_event_info->flags2= flags2;
1817 1818 1819 1820
    }
  }

  /*
1821 1822 1823 1824 1825 1826 1827 1828 1829 1830
    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.
1831 1832 1833 1834
  */

  if (likely(sql_mode_inited))
  {
unknown's avatar
unknown committed
1835
    if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */
1836
    {
unknown's avatar
unknown committed
1837
      print_event_info->sql_mode_inited= 1;
1838
      /* force a difference to force write */
unknown's avatar
unknown committed
1839
      print_event_info->sql_mode= ~sql_mode;
1840
    }
unknown's avatar
unknown committed
1841
    if (unlikely(print_event_info->sql_mode != sql_mode))
1842
    {
unknown's avatar
unknown committed
1843 1844
      my_b_printf(file,"SET @@session.sql_mode=%lu%s\n",
                  (ulong)sql_mode, print_event_info->delimiter);
unknown's avatar
unknown committed
1845
      print_event_info->sql_mode= sql_mode;
1846 1847
    }
  }
unknown's avatar
unknown committed
1848 1849
  if (print_event_info->auto_increment_increment != auto_increment_increment ||
      print_event_info->auto_increment_offset != auto_increment_offset)
1850
  {
unknown's avatar
unknown committed
1851 1852 1853
    my_b_printf(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
1854 1855
    print_event_info->auto_increment_increment= auto_increment_increment;
    print_event_info->auto_increment_offset=    auto_increment_offset;
1856 1857
  }

1858 1859
  /* TODO: print the catalog when we feature SET CATALOG */

1860 1861
  if (likely(charset_inited))
  {
unknown's avatar
unknown committed
1862
    if (unlikely(!print_event_info->charset_inited)) /* first Query event */
1863
    {
unknown's avatar
unknown committed
1864 1865
      print_event_info->charset_inited= 1;
      print_event_info->charset[0]= ~charset[0]; // force a difference to force write
1866
    }
1867
    if (unlikely(bcmp((uchar*) print_event_info->charset, (uchar*) charset, 6)))
1868
    {
1869 1870 1871
      CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
      if (cs_info)
      {
1872
        /* for mysql client */
unknown's avatar
unknown committed
1873 1874
        my_b_printf(file, "/*!\\C %s */%s\n",
                    cs_info->csname, print_event_info->delimiter);
1875
      }
1876 1877 1878 1879
      my_b_printf(file,"SET "
                  "@@session.character_set_client=%d,"
                  "@@session.collation_connection=%d,"
                  "@@session.collation_server=%d"
unknown's avatar
unknown committed
1880
                  "%s\n",
1881 1882
                  uint2korr(charset),
                  uint2korr(charset+2),
unknown's avatar
unknown committed
1883 1884
                  uint2korr(charset+4),
                  print_event_info->delimiter);
unknown's avatar
unknown committed
1885
      memcpy(print_event_info->charset, charset, 6);
1886 1887
    }
  }
1888 1889
  if (time_zone_len)
  {
1890 1891
    if (bcmp((uchar*) print_event_info->time_zone_str,
             (uchar*) time_zone_str, time_zone_len+1))
1892
    {
unknown's avatar
unknown committed
1893 1894
      my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
                  time_zone_str, print_event_info->delimiter);
unknown's avatar
unknown committed
1895
      memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
1896 1897
    }
  }
1898 1899
  if (lc_time_names_number != print_event_info->lc_time_names_number)
  {
unknown's avatar
unknown committed
1900 1901
    my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
                lc_time_names_number, print_event_info->delimiter);
1902 1903
    print_event_info->lc_time_names_number= lc_time_names_number;
  }
1904 1905 1906
  if (charset_database_number != print_event_info->charset_database_number)
  {
    if (charset_database_number)
unknown's avatar
unknown committed
1907 1908
      my_b_printf(file, "SET @@session.collation_database=%d%s\n",
                  charset_database_number, print_event_info->delimiter);
1909
    else
unknown's avatar
unknown committed
1910 1911
      my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
                  print_event_info->delimiter);
1912 1913
    print_event_info->charset_database_number= charset_database_number;
  }
unknown's avatar
unknown committed
1914 1915
}

1916

unknown's avatar
unknown committed
1917
void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
1918
{
1919 1920 1921
  Write_on_release_cache cache(&print_event_info->head_cache, file);

  print_query_header(&cache, print_event_info);
1922
  my_b_write(&cache, (uchar*) query, q_len);
unknown's avatar
unknown committed
1923
  my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
1924
}
unknown's avatar
unknown committed
1925
#endif /* MYSQL_CLIENT */
1926

unknown's avatar
unknown committed
1927

unknown's avatar
unknown committed
1928
/*
1929
  Query_log_event::do_apply_event()
unknown's avatar
unknown committed
1930
*/
unknown's avatar
unknown committed
1931

unknown's avatar
SCRUM  
unknown committed
1932
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1933

1934
int Query_log_event::do_apply_event(Relay_log_info const *rli)
unknown's avatar
unknown committed
1935
{
1936
  return do_apply_event(rli, query, q_len);
unknown's avatar
unknown committed
1937 1938 1939
}


1940
int Query_log_event::do_apply_event(Relay_log_info const *rli,
1941
                                      const char *query_arg, uint32 q_len_arg)
unknown's avatar
unknown committed
1942
{
1943
  LEX_STRING new_db;
unknown's avatar
unknown committed
1944
  int expected_error,actual_error= 0;
1945
  /*
1946 1947 1948
    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
1949
    Query_log_event::do_apply_event()). Same for thd->db.  Thank
1950
    you.
1951
  */
1952
  thd->catalog= catalog_len ? (char *) catalog : (char *)"";
1953 1954 1955
  new_db.length= db_len;
  new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
  thd->set_db(new_db.str, new_db.length);       /* allocates a copy of 'db' */
1956 1957
  thd->variables.auto_increment_increment= auto_increment_increment;
  thd->variables.auto_increment_offset=    auto_increment_offset;
unknown's avatar
unknown committed
1958

1959
  /*
1960 1961 1962 1963 1964 1965 1966 1967
    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.
1968
  */
1969
  const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
1970 1971
  DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));

1972 1973
  clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
  const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
1974

unknown's avatar
unknown committed
1975 1976 1977 1978 1979 1980 1981
  /*
    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
1982
            ::do_apply_event(), then the companion SET also have so
1983
            we don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
1984
  */
1985
  if (rpl_filter->db_ok(thd->db))
1986 1987
  {
    thd->set_time((time_t)when);
unknown's avatar
unknown committed
1988 1989
    thd->query_length= q_len_arg;
    thd->query= (char*)query_arg;
unknown's avatar
unknown committed
1990
    VOID(pthread_mutex_lock(&LOCK_thread_count));
1991
    thd->query_id = next_query_id();
1992
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
1993
    thd->variables.pseudo_thread_id= thread_id;		// for temp tables
unknown's avatar
unknown committed
1994
    DBUG_PRINT("query",("%s",thd->query));
1995

unknown's avatar
unknown committed
1996
    if (ignored_error_code((expected_error= error_code)) ||
1997
	!check_expected_error(thd,rli,expected_error))
1998 1999 2000
    {
      if (flags2_inited)
        /*
2001 2002
          all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
          must take their value from flags2.
2003
        */
2004
        thd->options= flags2|(thd->options & ~OPTIONS_WRITTEN_TO_BIN_LOG);
2005 2006
      /*
        else, we are in a 3.23/4.0 binlog; we previously received a
2007 2008
        Rotate_log_event which reset thd->options and sql_mode etc, so
        nothing to do.
2009 2010 2011 2012 2013
      */
      /*
        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
2014 2015 2016
        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).
2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034
      */
      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))))
          {
            /*
2035 2036 2037 2038
              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).
2039
            */
2040
            set_slave_thread_default_charset(thd, rli);
2041 2042 2043 2044 2045
            goto compare_errors;
          }
          thd->update_charset(); // for the charset change to take effect
        }
      }
2046 2047 2048
      if (time_zone_len)
      {
        String tmp(time_zone_str, time_zone_len, &my_charset_bin);
2049
        if (!(thd->variables.time_zone= my_tz_find(thd, &tmp)))
2050 2051 2052 2053 2054 2055
        {
          my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
          thd->variables.time_zone= global_system_variables.time_zone;
          goto compare_errors;
        }
      }
2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068
      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;
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083
      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;
      
2084
      /* Execute the query (note that we bypass dispatch_command()) */
2085 2086
      const char* found_semicolon= NULL;
      mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
2087
      log_slow_statement(thd);
2088
    }
unknown's avatar
unknown committed
2089 2090
    else
    {
unknown's avatar
unknown committed
2091
      /*
unknown's avatar
unknown committed
2092 2093 2094 2095 2096
        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
2097
      */
unknown's avatar
unknown committed
2098
      if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
2099
        clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* Can ignore query */
unknown's avatar
unknown committed
2100
      else
2101
      {
2102
        rli->report(ERROR_LEVEL, expected_error, 
unknown's avatar
unknown committed
2103
                          "\
unknown's avatar
unknown committed
2104
Query partially completed on the master (error on master: %d) \
unknown's avatar
unknown committed
2105 2106 2107
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
2108
START SLAVE; . Query: '%s'", expected_error, thd->query);
unknown's avatar
unknown committed
2109 2110 2111 2112
        thd->query_error= 1;
      }
      goto end;
    }
2113

2114 2115
    /* If the query was not ignored, it is printed to the general log */
    if (thd->net.last_errno != ER_SLAVE_IGNORED_TABLE)
2116
      general_log_print(thd, COM_QUERY, "%s", thd->query);
2117

2118
compare_errors:
unknown's avatar
unknown committed
2119 2120

     /*
unknown's avatar
unknown committed
2121 2122 2123 2124
      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",
2125
 		       expected_error, thd->net.last_errno));
unknown's avatar
unknown committed
2126
    if ((expected_error != (actual_error= thd->net.last_errno)) &&
2127 2128 2129
 	expected_error &&
 	!ignored_error_code(actual_error) &&
 	!ignored_error_code(expected_error))
unknown's avatar
unknown committed
2130
    {
2131
      rli->report(ERROR_LEVEL, 0,
2132 2133
                      "\
Query caused different errors on master and slave.     \
unknown's avatar
unknown committed
2134
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
unknown's avatar
unknown committed
2135
Default database: '%s'. Query: '%s'",
2136 2137 2138 2139 2140
                      ER_SAFE(expected_error),
                      expected_error,
                      actual_error ? thd->net.last_error: "no error",
                      actual_error,
                      print_slave_db_safe(db), query_arg);
unknown's avatar
unknown committed
2141 2142 2143 2144 2145 2146
      thd->query_error= 1;
    }
    /*
      If we get the same error code as expected, or they should be ignored. 
    */
    else if (expected_error == actual_error ||
2147
 	     ignored_error_code(actual_error))
unknown's avatar
unknown committed
2148 2149
    {
      DBUG_PRINT("info",("error ignored"));
2150
      clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
2151
      thd->killed= THD::NOT_KILLED;
unknown's avatar
unknown committed
2152 2153 2154
    }
    /*
      Other cases: mostly we expected no error and get one.
unknown's avatar
unknown committed
2155
    */
unknown's avatar
unknown committed
2156 2157
    else if (thd->query_error || thd->is_fatal_error)
    {
2158
      rli->report(ERROR_LEVEL, actual_error,
2159 2160 2161 2162
                      "Error '%s' on query. Default database: '%s'. Query: '%s'",
                      (actual_error ? thd->net.last_error :
                       "unexpected success or fatal error"),
                      print_slave_db_safe(thd->db), query_arg);
unknown's avatar
unknown committed
2163 2164
      thd->query_error= 1;
    }
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178

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

2180 2181 2182 2183 2184 2185 2186
      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
2187 2188
  } /* End of if (db_ok(... */

unknown's avatar
unknown committed
2189
end:
2190
  VOID(pthread_mutex_lock(&LOCK_thread_count));
2191 2192 2193 2194 2195 2196
  /*
    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
2197 2198 2199
    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.
2200
  */
unknown's avatar
unknown committed
2201
  thd->catalog= 0;
2202
  thd->set_db(NULL, 0);                 /* will free the current database */
2203
  thd->query= 0;			// just to be sure
unknown's avatar
unknown committed
2204
  thd->query_length= 0;
2205
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
2206
  close_thread_tables(thd);      
2207 2208 2209 2210 2211 2212 2213 2214 2215 2216
  /*
    As a disk space optimization, future masters will not log an event for
    LAST_INSERT_ID() if that function returned 0 (and thus they will be able
    to replace the THD::stmt_depends_on_first_successful_insert_id_in_prev_stmt
    variable by (THD->first_successful_insert_id_in_prev_stmt > 0) ; with the
    resetting below we are ready to support that.
  */
  thd->first_successful_insert_id_in_prev_stmt_for_binlog= 0;
  thd->first_successful_insert_id_in_prev_stmt= 0;
  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
unknown's avatar
unknown committed
2217
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
2218 2219 2220
  return thd->query_error;
}

2221
int Query_log_event::do_update_pos(Relay_log_info *rli)
2222
{
2223
  /*
2224 2225 2226
    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.
2227
  */
2228 2229 2230 2231 2232 2233 2234
  if (thd->one_shot_set)
  {
    rli->inc_event_relay_log_pos();
    return 0;
  }
  else
    return Log_event::do_update_pos(rli);
unknown's avatar
unknown committed
2235
}
2236 2237


unknown's avatar
SCRUM  
unknown committed
2238
#endif
unknown's avatar
unknown committed
2239

unknown's avatar
unknown committed
2240

2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255
/**************************************************************************
	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
2256
/**************************************************************************
2257
	Start_log_event_v3 methods
unknown's avatar
unknown committed
2258
**************************************************************************/
2259

2260
#ifndef MYSQL_CLIENT
2261 2262 2263
Start_log_event_v3::Start_log_event_v3()
  :Log_event(), created(0), binlog_version(BINLOG_VERSION),
   artificial_event(0), dont_set_created(0)
2264 2265 2266 2267 2268
{
  memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
#endif

unknown's avatar
unknown committed
2269
/*
2270
  Start_log_event_v3::pack_info()
unknown's avatar
unknown committed
2271
*/
2272

unknown's avatar
SCRUM  
unknown committed
2273
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2274
void Start_log_event_v3::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
2275
{
2276 2277 2278 2279
  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
2280 2281
  pos= int10_to_str(binlog_version, pos, 10);
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
unknown's avatar
unknown committed
2282
}
unknown's avatar
SCRUM  
unknown committed
2283
#endif
2284 2285


unknown's avatar
unknown committed
2286
/*
2287
  Start_log_event_v3::print()
unknown's avatar
unknown committed
2288
*/
unknown's avatar
unknown committed
2289 2290

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
2291
void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2292
{
2293 2294 2295 2296 2297
  DBUG_ENTER("Start_log_event_v3::print");

  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
2298
  if (!print_event_info->short_form)
2299
  {
2300 2301 2302 2303
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ",
                binlog_version, server_version);
    print_timestamp(&cache);
2304
    if (created)
2305 2306
      my_b_printf(&cache," at startup");
    my_b_printf(&cache, "\n");
2307
    if (flags & LOG_EVENT_BINLOG_IN_USE_F)
2308 2309
      my_b_printf(&cache, "# Warning: this binlog was not closed properly. "
                  "Most probably mysqld crashed writing it.\n");
2310
  }
2311 2312
  if (!artificial_event && created)
  {
2313
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
2314 2315 2316 2317 2318 2319
    /*
      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).
    */
unknown's avatar
unknown committed
2320
    my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter);
2321
#else
unknown's avatar
unknown committed
2322
    my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter);
2323
#endif
2324
  }
2325
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2326
}
unknown's avatar
unknown committed
2327
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
2328

unknown's avatar
unknown committed
2329
/*
2330
  Start_log_event_v3::Start_log_event_v3()
unknown's avatar
unknown committed
2331
*/
2332

2333
Start_log_event_v3::Start_log_event_v3(const char* buf,
2334 2335
                                       const Format_description_log_event
                                       *description_event)
2336
  :Log_event(buf, description_event)
2337
{
2338 2339
  buf+= description_event->common_header_len;
  binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
2340 2341
  memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
	 ST_SERVER_VER_LEN);
unknown's avatar
unknown committed
2342 2343
  // prevent overrun if log is corrupted on disk
  server_version[ST_SERVER_VER_LEN-1]= 0;
2344 2345 2346
  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);
2347
  dont_set_created= 1;
unknown's avatar
unknown committed
2348 2349
}

2350

unknown's avatar
unknown committed
2351
/*
2352
  Start_log_event_v3::write()
unknown's avatar
unknown committed
2353
*/
2354

2355
#ifndef MYSQL_CLIENT
2356
bool Start_log_event_v3::write(IO_CACHE* file)
2357
{
2358
  char buff[START_V3_HEADER_LEN];
2359 2360
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
2361 2362
  if (!dont_set_created)
    created= when= get_time();
2363
  int4store(buff + ST_CREATED_OFFSET,created);
2364
  return (write_header(file, sizeof(buff)) ||
2365
          my_b_safe_write(file, (uchar*) buff, sizeof(buff)));
2366
}
2367
#endif
2368

2369

unknown's avatar
unknown committed
2370
/*
2371
  Start_log_event_v3::do_apply_event()
2372 2373 2374 2375

  The master started

  IMPLEMENTATION
unknown's avatar
unknown committed
2376 2377 2378 2379
    - 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).
2380 2381

  TODO
2382 2383 2384 2385 2386
    - 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
2387 2388
*/

unknown's avatar
SCRUM  
unknown committed
2389
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2390
int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
2391
{
2392
  DBUG_ENTER("Start_log_event_v3::do_apply_event");
2393
  switch (binlog_version) {
2394 2395 2396 2397 2398 2399 2400
  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
2401
    */
2402 2403 2404 2405 2406
    if (created)
    {
      close_temporary_tables(thd);
      cleanup_load_tmpdir();
    }
unknown's avatar
unknown committed
2407 2408
    break;

2409
    /*
unknown's avatar
unknown committed
2410 2411
       Now the older formats; in that case load_tmpdir is cleaned up by the I/O
       thread.
unknown's avatar
unknown committed
2412
    */
2413
  case 1:
2414
    if (strncmp(rli->relay_log.description_event_for_exec->server_version,
2415 2416 2417 2418 2419 2420 2421 2422
                "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
2423
    /*
2424 2425 2426
      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
2427 2428 2429 2430
    */
    break;
  default:
    /* this case is impossible */
2431
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2432
  }
2433
  DBUG_RETURN(0);
2434
}
unknown's avatar
unknown committed
2435
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2436

2437 2438 2439 2440 2441 2442 2443 2444 2445
/***************************************************************************
       Format_description_log_event methods
****************************************************************************/

/*
  Format_description_log_event 1st ctor.

  SYNOPSIS
    Format_description_log_event::Format_description_log_event
2446
      binlog_version              the binlog version for which we want to build
2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460
                                  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::
2461
Format_description_log_event(uint8 binlog_ver, const char* server_ver)
2462 2463 2464 2465 2466 2467
  :Start_log_event_v3()
{
  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
2468 2469
    DBUG_EXECUTE_IF("pretend_version_50034_in_binlog",
                    strmov(server_version, "5.0.34"););
2470 2471 2472 2473
    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),
2474
                                       MYF(MY_ZEROFILL));
2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491
    /*
      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;
2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
      post_header_len[TABLE_MAP_EVENT-1]=    TABLE_MAP_HEADER_LEN;
      post_header_len[WRITE_ROWS_EVENT-1]=   ROWS_HEADER_LEN;
      post_header_len[UPDATE_ROWS_EVENT-1]=  ROWS_HEADER_LEN;
      post_header_len[DELETE_ROWS_EVENT-1]=  ROWS_HEADER_LEN;
      /*
        We here have the possibility to simulate a master of before we changed
        the table map id to be stored in 6 bytes: when it was stored in 4
        bytes (=> post_header_len was 6). This is used to test backward
        compatibility.
        This code can be removed after a few months (today is Dec 21st 2005),
        when we know that the 4-byte masters are not deployed anymore (check
        with Tomas Ulin first!), and the accompanying test (rpl_row_4_bytes)
        too.
      */
      DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
                      post_header_len[TABLE_MAP_EVENT-1]=
                      post_header_len[WRITE_ROWS_EVENT-1]=
                      post_header_len[UPDATE_ROWS_EVENT-1]=
                      post_header_len[DELETE_ROWS_EVENT-1]= 6;);
unknown's avatar
unknown committed
2511 2512
      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;
2513
      post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
    }
    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 :
2528
      LOG_EVENT_MINIMAL_HEADER_LEN;
2529 2530 2531 2532 2533 2534 2535 2536 2537
    /*
      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),
2538
                                       MYF(0));
2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560
    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
2561
  calc_server_version_split();
2562 2563 2564 2565 2566 2567 2568 2569
}


/*
  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
2570
  post-header starts.
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585
  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*
2586
                             description_event)
2587 2588 2589 2590 2591 2592 2593 2594 2595
  :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",
2596
                      common_header_len, number_of_event_types));
2597
  /* If alloc fails, we'll detect it in is_valid() */
2598
  post_header_len= (uint8*) my_memdup((uchar*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
2599
                                      number_of_event_types*
2600
                                      sizeof(*post_header_len), MYF(0));
unknown's avatar
unknown committed
2601
  calc_server_version_split();
2602 2603 2604
  DBUG_VOID_RETURN;
}

2605
#ifndef MYSQL_CLIENT
2606 2607 2608 2609 2610 2611
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().
  */
2612
  uchar buff[FORMAT_DESCRIPTION_HEADER_LEN];
2613 2614
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
2615 2616
  if (!dont_set_created)
    created= when= get_time();
2617
  int4store(buff + ST_CREATED_OFFSET,created);
2618
  buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
2619
  memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (uchar*) post_header_len,
2620 2621 2622 2623
         LOG_EVENT_TYPES);
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, buff, sizeof(buff)));
}
2624
#endif
2625

2626
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2627
int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
2628
{
2629
  DBUG_ENTER("Format_description_log_event::do_apply_event");
2630

unknown's avatar
unknown committed
2631
#ifdef USING_TRANSACTIONS
2632 2633 2634 2635
  /*
    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
2636 2637
    flushing the binlog cache to the binlog. XA guarantees that master has
    rolled back. So we roll back.
2638 2639 2640 2641 2642 2643 2644
    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)
  {
2645
    /* This is not an error (XA is safe), just an information */
2646 2647 2648 2649 2650
    rli->report(INFORMATION_LEVEL, 0,
                "Rolling back unfinished transaction (no COMMIT "
                "or ROLLBACK in relay log). A probable cause is that "
                "the master died while writing the transaction to "
                "its binary log, thus rolled back too."); 
2651
    const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 1);
2652
  }
unknown's avatar
unknown committed
2653
#endif
2654
  /*
2655
    If this event comes from ourselves, there is no cleaning task to
2656
    perform, we don't call Start_log_event_v3::do_apply_event()
2657
    (this was just to update the log's description event).
2658
  */
2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674
  if (server_id != (uint32) ::server_id)
  {
    /*
      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 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.
    */
    DBUG_RETURN(Start_log_event_v3::do_apply_event(rli));
  }
  DBUG_RETURN(0);
}

2675
int Format_description_log_event::do_update_pos(Relay_log_info *rli)
2676 2677 2678 2679 2680
{
  /* save the information describing this binlog */
  delete rli->relay_log.description_event_for_exec;
  rli->relay_log.description_event_for_exec= this;

2681 2682 2683
  if (server_id == (uint32) ::server_id)
  {
    /*
2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696
      We only increase the relay log position if we are skipping
      events and do not touch any group_* variables, nor flush the
      relay log info.  If there is a crash, we will have to re-skip
      the events again, but that is a minor issue.

      If we do not skip stepping the group log position (and the
      server id was changed when restarting the server), it might well
      be that we start executing at a position that is invalid, e.g.,
      at a Rows_log_event or a Query_log_event preceeded by a
      Intvar_log_event instead of starting at a Table_map_log_event or
      the Intvar_log_event respectively.
     */
    rli->inc_event_relay_log_pos();
2697
    return 0;
2698
  }
2699 2700 2701
  else
  {
    return Log_event::do_update_pos(rli);
2702 2703 2704
  }
}

2705
Log_event::enum_skip_reason
2706
Format_description_log_event::do_shall_skip(Relay_log_info *rli)
2707
{
2708
  return Log_event::EVENT_SKIP_NOT;
2709
}
2710

2711 2712
#endif

unknown's avatar
unknown committed
2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743

/**
   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]));
}


2744
  /**************************************************************************
2745
        Load_log_event methods
2746
   General note about Load_log_event: the binlogging of LOAD DATA INFILE is
2747
   going to be changed in 5.0 (or maybe in 5.1; not decided yet).
2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759
   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).
  **************************************************************************/
2760

unknown's avatar
unknown committed
2761
/*
2762
  Load_log_event::pack_info()
unknown's avatar
unknown committed
2763
*/
2764

unknown's avatar
SCRUM  
unknown committed
2765
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
2766
uint Load_log_event::get_query_buffer_length()
2767
{
unknown's avatar
unknown committed
2768
  return
2769 2770
    5 + db_len + 3 +                        // "use DB; "
    18 + fname_len + 2 +                    // "LOAD DATA INFILE 'file''"
unknown's avatar
unknown committed
2771
    7 +					    // LOCAL
2772
    9 +                                     // " REPLACE or IGNORE "
2773
    13 + table_name_len*2 +                 // "INTO TABLE `table`"
2774 2775 2776 2777
    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'"
2778 2779
    19 + sql_ex.line_start_len*4 + 2 +      // " LINES STARTING BY 'str'"
    15 + 22 +                               // " IGNORE xxx  LINES"
2780
    3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
unknown's avatar
unknown committed
2781
}
2782

unknown's avatar
unknown committed
2783 2784 2785 2786 2787 2788 2789

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)
2790
  {
2791 2792
    pos= strmov(pos, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
2793
    pos= strmov(pos+db_len, "`; ");
2794
  }
2795

unknown's avatar
unknown committed
2796
  pos= strmov(pos, "LOAD DATA ");
unknown's avatar
unknown committed
2797 2798 2799 2800

  if (fn_start)
    *fn_start= pos;

unknown's avatar
unknown committed
2801 2802 2803
  if (check_fname_outside_temp_buf())
    pos= strmov(pos, "LOCAL ");
  pos= strmov(pos, "INFILE '");
2804
  memcpy(pos, fname, fname_len);
unknown's avatar
unknown committed
2805
  pos= strmov(pos+fname_len, "' ");
2806

unknown's avatar
unknown committed
2807
  if (sql_ex.opt_flags & REPLACE_FLAG)
2808
    pos= strmov(pos, " REPLACE ");
unknown's avatar
unknown committed
2809
  else if (sql_ex.opt_flags & IGNORE_FLAG)
2810 2811
    pos= strmov(pos, " IGNORE ");

unknown's avatar
unknown committed
2812 2813 2814 2815 2816 2817
  pos= strmov(pos ,"INTO");

  if (fn_end)
    *fn_end= pos;

  pos= strmov(pos ," TABLE `");
2818 2819 2820
  memcpy(pos, table_name, table_name_len);
  pos+= table_name_len;

unknown's avatar
unknown committed
2821
  /* We have to create all optinal fields as the default is not empty */
2822
  pos= strmov(pos, "` FIELDS TERMINATED BY ");
unknown's avatar
unknown committed
2823 2824 2825 2826 2827
  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);
2828

unknown's avatar
unknown committed
2829 2830
  pos= strmov(pos, " ESCAPED BY ");
  pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len);
2831

unknown's avatar
unknown committed
2832 2833
  pos= strmov(pos, " LINES TERMINATED BY ");
  pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len);
2834 2835
  if (sql_ex.line_start_len)
  {
2836
    pos= strmov(pos, " STARTING BY ");
2837
    pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len);
2838
  }
2839

unknown's avatar
unknown committed
2840
  if ((long) skip_lines > 0)
2841 2842
  {
    pos= strmov(pos, " IGNORE ");
unknown's avatar
unknown committed
2843
    pos= longlong10_to_str((longlong) skip_lines, pos, 10);
2844 2845
    pos= strmov(pos," LINES ");    
  }
2846 2847 2848 2849

  if (num_fields)
  {
    uint i;
unknown's avatar
unknown committed
2850
    const char *field= fields;
2851
    pos= strmov(pos, " (");
2852 2853 2854
    for (i = 0; i < num_fields; i++)
    {
      if (i)
unknown's avatar
unknown committed
2855 2856 2857 2858
      {
        *pos++= ' ';
        *pos++= ',';
      }
2859
      memcpy(pos, field, field_lens[i]);
unknown's avatar
unknown committed
2860 2861
      pos+=   field_lens[i];
      field+= field_lens[i]  + 1;
2862
    }
2863
    *pos++= ')';
2864
  }
2865

unknown's avatar
unknown committed
2866 2867 2868 2869 2870 2871 2872 2873
  *end= pos;
}


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

2874
  if (!(buf= (char*) my_malloc(get_query_buffer_length(), MYF(MY_WME))))
unknown's avatar
unknown committed
2875 2876 2877
    return;
  print_query(TRUE, buf, &end, 0, 0);
  protocol->store(buf, end-buf, &my_charset_bin);
unknown's avatar
unknown committed
2878
  my_free(buf, MYF(0));
2879
}
unknown's avatar
unknown committed
2880
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2881

2882

2883 2884
#ifndef MYSQL_CLIENT

unknown's avatar
unknown committed
2885
/*
2886
  Load_log_event::write_data_header()
unknown's avatar
unknown committed
2887
*/
2888

2889
bool Load_log_event::write_data_header(IO_CACHE* file)
2890
{
2891
  char buf[LOAD_HEADER_LEN];
unknown's avatar
unknown committed
2892
  int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
2893 2894 2895 2896 2897
  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);
2898
  return my_b_safe_write(file, (uchar*)buf, LOAD_HEADER_LEN) != 0;
2899
}
2900

2901

unknown's avatar
unknown committed
2902
/*
2903
  Load_log_event::write_data_body()
unknown's avatar
unknown committed
2904
*/
2905

2906
bool Load_log_event::write_data_body(IO_CACHE* file)
2907
{
2908 2909 2910
  if (sql_ex.write_data(file))
    return 1;
  if (num_fields && fields && field_lens)
2911
  {
2912 2913
    if (my_b_safe_write(file, (uchar*)field_lens, num_fields) ||
	my_b_safe_write(file, (uchar*)fields, field_block_len))
2914
      return 1;
2915
  }
2916 2917 2918
  return (my_b_safe_write(file, (uchar*)table_name, table_name_len + 1) ||
	  my_b_safe_write(file, (uchar*)db, db_len + 1) ||
	  my_b_safe_write(file, (uchar*)fname, fname_len));
2919 2920
}

2921

unknown's avatar
unknown committed
2922
/*
2923
  Load_log_event::Load_log_event()
unknown's avatar
unknown committed
2924
*/
2925

unknown's avatar
unknown committed
2926 2927 2928
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
2929
			       enum enum_duplicates handle_dup,
2930
			       bool ignore, bool using_trans)
2931
  :Log_event(thd_arg,
unknown's avatar
unknown committed
2932
             thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0,
2933
             using_trans),
2934
   thread_id(thd_arg->thread_id),
2935
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
unknown's avatar
unknown committed
2936 2937
   num_fields(0),fields(0),
   field_lens(0),field_block_len(0),
unknown's avatar
unknown committed
2938
   table_name(table_name_arg ? table_name_arg : ""),
2939
   db(db_arg), fname(ex->file_name), local_fname(FALSE)
unknown's avatar
unknown committed
2940 2941 2942
{
  time_t end_time;
  time(&end_time);
2943
  exec_time = (ulong) (end_time  - thd_arg->start_time);
2944 2945 2946
  /* 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
2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959
  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;
2960
    
unknown's avatar
unknown committed
2961
  if (ex->dumpfile)
unknown's avatar
unknown committed
2962
    sql_ex.opt_flags|= DUMPFILE_FLAG;
unknown's avatar
unknown committed
2963
  if (ex->opt_enclosed)
unknown's avatar
unknown committed
2964
    sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
2965

unknown's avatar
unknown committed
2966
  sql_ex.empty_flags= 0;
2967

2968
  switch (handle_dup) {
unknown's avatar
unknown committed
2969
  case DUP_REPLACE:
unknown's avatar
unknown committed
2970
    sql_ex.opt_flags|= REPLACE_FLAG;
unknown's avatar
unknown committed
2971 2972 2973 2974
    break;
  case DUP_UPDATE:				// Impossible here
  case DUP_ERROR:
    break;	
unknown's avatar
unknown committed
2975
  }
2976 2977
  if (ignore)
    sql_ex.opt_flags|= IGNORE_FLAG;
2978

unknown's avatar
unknown committed
2979 2980 2981 2982 2983 2984 2985 2986 2987 2988
  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;
2989
    
unknown's avatar
unknown committed
2990
  skip_lines = ex->skip_lines;
2991

unknown's avatar
unknown committed
2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002
  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);
3003 3004
  }

unknown's avatar
unknown committed
3005 3006 3007
  field_lens = (const uchar*)field_lens_buf.ptr();
  fields = fields_buf.ptr();
}
unknown's avatar
unknown committed
3008
#endif /* !MYSQL_CLIENT */
3009

3010

3011
/*
3012
  Load_log_event::Load_log_event()
3013

unknown's avatar
unknown committed
3014 3015 3016
  NOTE
    The caller must do buf[event_len] = 0 before he starts using the
    constructed event.
3017 3018
*/

3019 3020 3021 3022
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
3023
   table_name(0), db(0), fname(0), local_fname(FALSE)
unknown's avatar
unknown committed
3024
{
unknown's avatar
unknown committed
3025
  DBUG_ENTER("Load_log_event");
3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037
  /*
    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
3038
  DBUG_VOID_RETURN;
3039 3040
}

3041

unknown's avatar
unknown committed
3042
/*
3043
  Load_log_event::copy_log_event()
unknown's avatar
unknown committed
3044
*/
3045

3046
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
3047 3048
                                   int body_offset,
                                   const Format_description_log_event *description_event)
3049
{
3050
  DBUG_ENTER("Load_log_event::copy_log_event");
3051
  uint data_len;
3052
  char* buf_end = (char*)buf + event_len;
3053 3054
  /* this is the beginning of the post-header */
  const char* data_head = buf + description_event->common_header_len;
unknown's avatar
unknown committed
3055
  slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
3056 3057 3058 3059 3060
  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
3061
	  
unknown's avatar
unknown committed
3062
  if ((int) event_len < body_offset)
unknown's avatar
unknown committed
3063
    DBUG_RETURN(1);
3064 3065 3066 3067
  /*
    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.
  */
3068 3069 3070
  if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
                                        buf_end,
                                        buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
unknown's avatar
unknown committed
3071
    DBUG_RETURN(1);
3072
  
3073
  data_len = event_len - body_offset;
3074
  if (num_fields > data_len) // simple sanity check against corruption
unknown's avatar
unknown committed
3075
    DBUG_RETURN(1);
3076
  for (uint i = 0; i < num_fields; i++)
3077
    field_block_len += (uint)field_lens[i] + 1;
3078

unknown's avatar
unknown committed
3079 3080 3081 3082
  fields = (char*)field_lens + num_fields;
  table_name  = fields + field_block_len;
  db = table_name + table_name_len + 1;
  fname = db + db_len + 1;
3083 3084
  fname_len = strlen(fname);
  // null termination is accomplished by the caller doing buf[event_len]=0
3085

unknown's avatar
unknown committed
3086
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3087 3088 3089
}


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

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3095
void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
3096
{
unknown's avatar
unknown committed
3097
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
3098 3099
}

unknown's avatar
unknown committed
3100

3101
void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
3102
			   bool commented)
unknown's avatar
unknown committed
3103
{
3104 3105
  Write_on_release_cache cache(&print_event_info->head_cache, file_arg);

unknown's avatar
unknown committed
3106
  DBUG_ENTER("Load_log_event::print");
unknown's avatar
unknown committed
3107
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
3108
  {
3109 3110 3111
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
                thread_id, exec_time);
unknown's avatar
unknown committed
3112 3113
  }

3114
  bool different_db= 1;
3115
  if (db)
unknown's avatar
unknown committed
3116
  {
3117 3118 3119 3120 3121 3122
    /*
      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
3123
    if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) &&
3124
        !commented)
unknown's avatar
unknown committed
3125
      memcpy(print_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
3126
  }
3127
  
3128
  if (db && db[0] && different_db)
unknown's avatar
unknown committed
3129
    my_b_printf(&cache, "%suse %s%s\n", 
unknown's avatar
unknown committed
3130
            commented ? "# " : "",
3131
            db, print_event_info->delimiter);
unknown's avatar
unknown committed
3132

3133
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
unknown's avatar
unknown committed
3134
    my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
3135 3136
            commented ? "# " : "", (ulong)thread_id,
            print_event_info->delimiter);
3137
  my_b_printf(&cache, "%sLOAD DATA ",
unknown's avatar
unknown committed
3138
              commented ? "# " : "");
3139
  if (check_fname_outside_temp_buf())
3140 3141
    my_b_printf(&cache, "LOCAL ");
  my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
unknown's avatar
unknown committed
3142

unknown's avatar
unknown committed
3143
  if (sql_ex.opt_flags & REPLACE_FLAG)
3144
    my_b_printf(&cache," REPLACE ");
unknown's avatar
unknown committed
3145
  else if (sql_ex.opt_flags & IGNORE_FLAG)
3146
    my_b_printf(&cache," IGNORE ");
unknown's avatar
unknown committed
3147
  
3148 3149 3150
  my_b_printf(&cache, "INTO TABLE `%s`", table_name);
  my_b_printf(&cache, " FIELDS TERMINATED BY ");
  pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len);
unknown's avatar
unknown committed
3151

unknown's avatar
unknown committed
3152
  if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
3153 3154 3155
    my_b_printf(&cache," OPTIONALLY ");
  my_b_printf(&cache, " ENCLOSED BY ");
  pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len);
unknown's avatar
unknown committed
3156
     
3157 3158
  my_b_printf(&cache, " ESCAPED BY ");
  pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len);
unknown's avatar
unknown committed
3159
     
3160 3161
  my_b_printf(&cache," LINES TERMINATED BY ");
  pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len);
unknown's avatar
unknown committed
3162

unknown's avatar
unknown committed
3163

3164
  if (sql_ex.line_start)
unknown's avatar
unknown committed
3165
  {
3166 3167
    my_b_printf(&cache," STARTING BY ");
    pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len);
unknown's avatar
unknown committed
3168
  }
3169
  if ((long) skip_lines > 0)
3170
    my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines);
unknown's avatar
unknown committed
3171

3172 3173 3174 3175
  if (num_fields)
  {
    uint i;
    const char* field = fields;
3176
    my_b_printf(&cache, " (");
3177
    for (i = 0; i < num_fields; i++)
unknown's avatar
unknown committed
3178
    {
unknown's avatar
unknown committed
3179
      if (i)
3180 3181
	my_b_printf(&cache, ",");
      my_b_printf(&cache, field);
unknown's avatar
unknown committed
3182
	  
3183
      field += field_lens[i]  + 1;
unknown's avatar
unknown committed
3184
    }
3185
    my_b_printf(&cache, ")");
3186
  }
unknown's avatar
unknown committed
3187

unknown's avatar
unknown committed
3188
  my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
3189
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3190
}
unknown's avatar
unknown committed
3191
#endif /* MYSQL_CLIENT */
3192

3193

unknown's avatar
unknown committed
3194
/*
3195
  Load_log_event::set_fields()
unknown's avatar
unknown committed
3196 3197 3198 3199 3200

  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
3201
*/
3202

3203
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
3204
void Load_log_event::set_fields(const char* affected_db, 
3205 3206
				List<Item> &field_list,
                                Name_resolution_context *context)
unknown's avatar
unknown committed
3207 3208
{
  uint i;
unknown's avatar
unknown committed
3209
  const char* field = fields;
3210
  for (i= 0; i < num_fields; i++)
unknown's avatar
unknown committed
3211
  {
3212 3213
    field_list.push_back(new Item_field(context,
                                        affected_db, table_name, field));
3214
    field+= field_lens[i]  + 1;
unknown's avatar
unknown committed
3215
  }
unknown's avatar
unknown committed
3216
}
unknown's avatar
unknown committed
3217
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
3218 3219


unknown's avatar
SCRUM  
unknown committed
3220
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3221 3222
/*
  Does the data loading job when executing a LOAD DATA on the slave
3223

unknown's avatar
unknown committed
3224
  SYNOPSIS
3225
    Load_log_event::do_apply_event
3226 3227 3228
      net
      rli
      use_rli_only_for_errors	  - if set to 1, rli is provided to
3229
                                  Load_log_event::do_apply_event
3230 3231 3232 3233 3234 3235
                                  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
3236
                                  Execute_load_log_event::do_apply_event).
3237 3238 3239
                                  - if set to 0, rli is provided for
                                  full use, i.e. for error reports and
                                  position advancing.
3240

unknown's avatar
unknown committed
3241 3242
  DESCRIPTION
    Does the data loading job when executing a LOAD DATA on the slave
3243

unknown's avatar
unknown committed
3244
  RETURN VALUE
3245
    0           Success
unknown's avatar
unknown committed
3246 3247
    1    	Failure
*/
3248

3249
int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
3250
                                   bool use_rli_only_for_errors)
3251
{
3252 3253 3254 3255
  LEX_STRING new_db;
  new_db.length= db_len;
  new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
  thd->set_db(new_db.str, new_db.length);
3256
  DBUG_ASSERT(thd->query == 0);
unknown's avatar
unknown committed
3257
  thd->query_length= 0;                         // Should not be needed
unknown's avatar
unknown committed
3258
  thd->query_error= 0;
3259
  clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
3260

3261
  /* see Query_log_event::do_apply_event() and BUG#13360 */
3262
  DBUG_ASSERT(!rli->m_table_map.count());
3263
  /*
3264
    Usually lex_start() is called by mysql_parse(), but we need it here
3265 3266
    as the present method does not call mysql_parse().
  */
3267 3268 3269
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);

unknown's avatar
unknown committed
3270
  if (!use_rli_only_for_errors)
3271
  {
3272 3273
    /*
      Saved for InnoDB, see comment in
3274
      Query_log_event::do_apply_event()
3275
    */
3276
    const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
3277
    DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
3278
  }
3279 3280
 
   /*
3281 3282 3283 3284 3285 3286 3287
    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
3288
    done by testing rules in Create_file_log_event::do_apply_event()
3289 3290 3291
    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
3292 3293 3294 3295 3296 3297 3298 3299


    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
3300
            ::do_apply_event(), then the companion SET also have so
3301
            we don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
3302
  */
3303
  if (rpl_filter->db_ok(thd->db))
3304
  {
3305 3306
    thd->set_time((time_t)when);
    VOID(pthread_mutex_lock(&LOCK_thread_count));
3307
    thd->query_id = next_query_id();
3308
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
3309 3310 3311 3312 3313 3314
    /*
      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
3315
    mysql_reset_errors(thd, 0);
3316 3317 3318

    TABLE_LIST tables;
    bzero((char*) &tables,sizeof(tables));
unknown's avatar
unknown committed
3319
    tables.db= thd->strmake(thd->db, thd->db_length);
3320
    tables.alias = tables.table_name = (char*) table_name;
3321
    tables.lock_type = TL_WRITE;
unknown's avatar
unknown committed
3322
    tables.updating= 1;
unknown's avatar
unknown committed
3323

3324
    // the table will be opened in mysql_load    
3325
    if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db, &tables))
3326 3327 3328 3329 3330 3331 3332 3333
    {
      // 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
3334
      char *end;
unknown's avatar
unknown committed
3335
      enum enum_duplicates handle_dup;
3336
      bool ignore= 0;
unknown's avatar
unknown committed
3337 3338
      char *load_data_query;

unknown's avatar
unknown committed
3339
      /*
unknown's avatar
unknown committed
3340 3341
        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
3342
      */
unknown's avatar
unknown committed
3343
      if (!(load_data_query= (char *)thd->alloc(get_query_buffer_length() + 1)))
unknown's avatar
unknown committed
3344
      {
unknown's avatar
unknown committed
3345 3346 3347 3348 3349
        /*
          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
3350
      }
unknown's avatar
unknown committed
3351 3352 3353 3354 3355 3356 3357

      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
3358
      if (sql_ex.opt_flags & REPLACE_FLAG)
3359
      {
unknown's avatar
unknown committed
3360
	handle_dup= DUP_REPLACE;
3361
      }
unknown's avatar
unknown committed
3362
      else if (sql_ex.opt_flags & IGNORE_FLAG)
3363 3364 3365 3366
      {
        ignore= 1;
        handle_dup= DUP_ERROR;
      }
unknown's avatar
unknown committed
3367
      else
unknown's avatar
unknown committed
3368
      {
unknown's avatar
unknown committed
3369
        /*
unknown's avatar
unknown committed
3370
	  When replication is running fine, if it was DUP_ERROR on the
3371
          master then we could choose IGNORE here, because if DUP_ERROR
unknown's avatar
unknown committed
3372
          suceeded on master, and data is identical on the master and slave,
3373
          then there should be no uniqueness errors on slave, so IGNORE is
unknown's avatar
unknown committed
3374
          the same as DUP_ERROR. But in the unlikely case of uniqueness errors
unknown's avatar
unknown committed
3375 3376 3377
          (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
3378 3379

          If reading from net (a 3.23 master), mysql_load() will change this
3380
          to IGNORE.
unknown's avatar
unknown committed
3381 3382
        */
        handle_dup= DUP_ERROR;
unknown's avatar
unknown committed
3383
      }
3384 3385 3386 3387 3388 3389 3390 3391 3392 3393
      /*
        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
3394

unknown's avatar
unknown committed
3395
      sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
3396 3397 3398 3399 3400
      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
3401 3402 3403 3404 3405
      ex.field_term= &field_term;
      ex.enclosed= &enclosed;
      ex.line_term= &line_term;
      ex.line_start= &line_start;
      ex.escaped= &escaped;
3406 3407 3408 3409 3410 3411

      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
3412
      List<Item> field_list;
3413 3414
      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
3415
      thd->variables.pseudo_thread_id= thread_id;
3416 3417 3418 3419 3420 3421 3422 3423 3424
      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
3425
      /*
3426
        It is safe to use tmp_list twice because we are not going to
unknown's avatar
unknown committed
3427 3428
        update it inside mysql_load().
      */
3429 3430
      List<Item> tmp_list;
      if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
3431
                     handle_dup, ignore, net != 0))
unknown's avatar
unknown committed
3432
        thd->query_error= 1;
3433
      if (thd->cuted_fields)
unknown's avatar
unknown committed
3434
      {
unknown's avatar
unknown committed
3435
	/* log_pos is the position of the LOAD event in the master log */
unknown's avatar
unknown committed
3436 3437 3438 3439 3440 3441 3442
        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
3443
      }
3444 3445 3446
      if (net)
        net->pkt_nr= thd->net.pkt_nr;
    }
3447 3448
  }
  else
3449 3450 3451 3452 3453 3454 3455 3456 3457
  {
    /*
      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
3458 3459

error:
3460
  thd->net.vio = 0; 
unknown's avatar
unknown committed
3461
  const char *remember_db= thd->db;
unknown's avatar
unknown committed
3462
  VOID(pthread_mutex_lock(&LOCK_thread_count));
unknown's avatar
unknown committed
3463
  thd->catalog= 0;
3464
  thd->set_db(NULL, 0);                   /* will free the current database */
unknown's avatar
unknown committed
3465
  thd->query= 0;
unknown's avatar
unknown committed
3466
  thd->query_length= 0;
unknown's avatar
unknown committed
3467
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
3468
  close_thread_tables(thd);
3469 3470 3471 3472

  DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
                  thd->query_error= 0; thd->is_fatal_error= 1;);

3473 3474
  if (thd->query_error)
  {
3475
    /* this err/sql_errno code is copy-paste from net_send_error() */
unknown's avatar
unknown committed
3476 3477 3478 3479 3480 3481 3482 3483 3484
    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);       
    }
3485
    rli->report(ERROR_LEVEL, sql_errno,"\
unknown's avatar
unknown committed
3486
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
3487
                    err, (char*)table_name, print_slave_db_safe(remember_db));
unknown's avatar
unknown committed
3488
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3489 3490
    return 1;
  }
unknown's avatar
unknown committed
3491
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3492

3493
  if (thd->is_fatal_error)
3494
  {
3495 3496 3497 3498 3499 3500 3501 3502 3503
    char buf[256];
    my_snprintf(buf, sizeof(buf),
                "Running LOAD DATA INFILE on table '%-.64s'."
                " Default database: '%-.64s'",
                (char*)table_name,
                print_slave_db_safe(remember_db));

    rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
                ER(ER_SLAVE_FATAL_ERROR), buf);
3504 3505 3506
    return 1;
  }

3507
  return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) ); 
3508
}
unknown's avatar
SCRUM  
unknown committed
3509
#endif
3510 3511


unknown's avatar
unknown committed
3512
/**************************************************************************
unknown's avatar
unknown committed
3513
  Rotate_log_event methods
unknown's avatar
unknown committed
3514
**************************************************************************/
3515

unknown's avatar
unknown committed
3516
/*
3517
  Rotate_log_event::pack_info()
unknown's avatar
unknown committed
3518
*/
3519

unknown's avatar
SCRUM  
unknown committed
3520
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3521
void Rotate_log_event::pack_info(Protocol *protocol)
3522
{
unknown's avatar
unknown committed
3523
  char buf1[256], buf[22];
unknown's avatar
unknown committed
3524
  String tmp(buf1, sizeof(buf1), log_cs);
3525
  tmp.length(0);
unknown's avatar
unknown committed
3526
  tmp.append(new_log_ident, ident_len);
3527
  tmp.append(STRING_WITH_LEN(";pos="));
unknown's avatar
unknown committed
3528 3529
  tmp.append(llstr(pos,buf));
  protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
3530
}
unknown's avatar
SCRUM  
unknown committed
3531
#endif
3532

3533

unknown's avatar
unknown committed
3534
/*
3535
  Rotate_log_event::print()
unknown's avatar
unknown committed
3536
*/
3537 3538

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3539
void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3540
{
3541
  char buf[22];
3542 3543
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);
3544

unknown's avatar
unknown committed
3545
  if (print_event_info->short_form)
3546
    return;
3547 3548
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\tRotate to ");
3549
  if (new_log_ident)
3550
    my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len);
3551
  my_b_printf(&cache, "  pos: %s\n", llstr(pos, buf));
3552
}
unknown's avatar
unknown committed
3553
#endif /* MYSQL_CLIENT */
3554 3555


3556

unknown's avatar
unknown committed
3557
/*
3558
  Rotate_log_event::Rotate_log_event() (2 constructors)
unknown's avatar
unknown committed
3559
*/
3560

3561 3562

#ifndef MYSQL_CLIENT
3563
Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
3564 3565 3566 3567 3568 3569 3570 3571
                                   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];
3572
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
unknown's avatar
unknown committed
3573 3574
  DBUG_PRINT("enter",("new_log_ident: %s  pos: %s  flags: %lu", new_log_ident_arg,
                      llstr(pos_arg, buff), (ulong) flags));
3575 3576
#endif
  if (flags & DUP_NAME)
3577
    new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME));
3578 3579 3580 3581 3582
  DBUG_VOID_RETURN;
}
#endif


3583 3584
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
                                   const Format_description_log_event* description_event)
unknown's avatar
unknown committed
3585
  :Log_event(buf, description_event) ,new_log_ident(0), flags(DUP_NAME)
3586
{
3587
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
3588
  // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
3589 3590
  uint8 header_size= description_event->common_header_len;
  uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
3591 3592
  uint ident_offset;
  if (event_len < header_size)
unknown's avatar
unknown committed
3593
    DBUG_VOID_RETURN;
3594
  buf += header_size;
3595 3596 3597 3598
  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; 
3599
  set_if_smaller(ident_len,FN_REFLEN-1);
3600
  new_log_ident= my_strndup(buf + ident_offset, (uint) ident_len, MYF(MY_WME));
3601
  DBUG_PRINT("debug", ("new_log_ident: '%s'", new_log_ident));
unknown's avatar
unknown committed
3602
  DBUG_VOID_RETURN;
3603
}
3604 3605


unknown's avatar
unknown committed
3606
/*
3607
  Rotate_log_event::write()
unknown's avatar
unknown committed
3608
*/
3609

3610
#ifndef MYSQL_CLIENT
3611
bool Rotate_log_event::write(IO_CACHE* file)
3612
{
3613
  char buf[ROTATE_HEADER_LEN];
unknown's avatar
unknown committed
3614
  int8store(buf + R_POS_OFFSET, pos);
3615
  return (write_header(file, ROTATE_HEADER_LEN + ident_len) ||
3616 3617
          my_b_safe_write(file, (uchar*)buf, ROTATE_HEADER_LEN) ||
          my_b_safe_write(file, (uchar*)new_log_ident, (uint) ident_len));
3618
}
3619
#endif
3620

3621

unknown's avatar
unknown committed
3622
/*
3623
  Rotate_log_event::do_apply_event()
3624

3625
  Got a rotate log event from the master
3626

3627 3628 3629
  IMPLEMENTATION
    This is mainly used so that we can later figure out the logname and
    position for the master.
3630

3631
    We can't rotate the slave's BINlog as this will cause infinitive rotations
3632
    in a A -> B -> A setup.
3633
    The NOTES below is a wrong comment which will disappear when 4.1 is merged.
3634 3635 3636

  RETURN VALUES
    0	ok
unknown's avatar
unknown committed
3637
*/
3638

unknown's avatar
SCRUM  
unknown committed
3639
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3640
int Rotate_log_event::do_update_pos(Relay_log_info *rli)
3641
{
3642
  DBUG_ENTER("Rotate_log_event::do_update_pos");
3643 3644 3645 3646
#ifndef DBUG_OFF
  char buf[32];
#endif

3647 3648
  DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
                      (ulong) this->server_id, (ulong) ::server_id));
3649 3650
  DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
  DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
3651 3652

  pthread_mutex_lock(&rli->data_lock);
3653
  rli->event_relay_log_pos= my_b_tell(rli->cur_log);
unknown's avatar
unknown committed
3654
  /*
3655 3656 3657 3658
    If we are in a transaction or in a group: 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:

unknown's avatar
unknown committed
3659 3660 3661 3662 3663
    BEGIN
    ...
    ROTATE (a fake one)
    ...
    COMMIT or ROLLBACK
3664 3665 3666 3667

    In that case, we don't want to touch the coordinates which
    correspond to the beginning of the transaction.  Starting from
    5.0.0, there also are some rotates from the slave itself, in the
3668
    relay log, which shall not change the group positions.
unknown's avatar
unknown committed
3669
  */
3670
  if ((server_id != ::server_id || rli->replicate_same_server_id) &&
3671
      !rli->is_in_group())
unknown's avatar
unknown committed
3672
  {
3673 3674 3675 3676
    DBUG_PRINT("info", ("old group_master_log_name: '%s'  "
                        "old group_master_log_pos: %lu",
                        rli->group_master_log_name,
                        (ulong) rli->group_master_log_pos));
unknown's avatar
unknown committed
3677 3678
    memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
    rli->notify_group_master_log_name_update();
3679
    rli->group_master_log_pos= pos;
3680 3681 3682
    strmake(rli->group_relay_log_name, rli->event_relay_log_name,
            sizeof(rli->group_relay_log_name) - 1);
    rli->notify_group_relay_log_name_update();
3683
    rli->group_relay_log_pos= rli->event_relay_log_pos;
3684 3685
    DBUG_PRINT("info", ("new group_master_log_name: '%s'  "
                        "new group_master_log_pos: %lu",
3686
                        rli->group_master_log_name,
unknown's avatar
unknown committed
3687
                        (ulong) rli->group_master_log_pos));
3688
    /*
3689 3690
      Reset thd->options and sql_mode etc, because this could be the signal of
      a master's downgrade from 5.0 to 4.0.
3691 3692 3693 3694 3695
      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);
3696
    set_slave_thread_default_charset(thd, rli);
3697
    thd->variables.sql_mode= global_system_variables.sql_mode;
3698 3699
    thd->variables.auto_increment_increment=
      thd->variables.auto_increment_offset= 1;
unknown's avatar
unknown committed
3700
  }
3701 3702 3703
  pthread_mutex_unlock(&rli->data_lock);
  pthread_cond_broadcast(&rli->data_cond);
  flush_relay_log_info(rli);
3704

3705
  DBUG_RETURN(0);
3706
}
3707

3708 3709

Log_event::enum_skip_reason
3710
Rotate_log_event::do_shall_skip(Relay_log_info *rli)
3711
{
3712
  enum_skip_reason reason= Log_event::do_shall_skip(rli);
3713 3714

  switch (reason) {
3715
  case Log_event::EVENT_SKIP_NOT:
3716
  case Log_event::EVENT_SKIP_COUNT:
3717
    return Log_event::EVENT_SKIP_NOT;
3718

3719 3720
  case Log_event::EVENT_SKIP_IGNORE:
    return Log_event::EVENT_SKIP_IGNORE;
3721 3722
  }
  DBUG_ASSERT(0);
3723
  return Log_event::EVENT_SKIP_NOT;             // To keep compiler happy
3724 3725
}

unknown's avatar
SCRUM  
unknown committed
3726
#endif
3727 3728


unknown's avatar
unknown committed
3729
/**************************************************************************
unknown's avatar
unknown committed
3730
	Intvar_log_event methods
unknown's avatar
unknown committed
3731
**************************************************************************/
3732

unknown's avatar
unknown committed
3733
/*
3734
  Intvar_log_event::pack_info()
unknown's avatar
unknown committed
3735
*/
3736

unknown's avatar
SCRUM  
unknown committed
3737
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3738
void Intvar_log_event::pack_info(Protocol *protocol)
3739
{
unknown's avatar
unknown committed
3740 3741
  char buf[256], *pos;
  pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
unknown's avatar
unknown committed
3742
  *pos++= '=';
unknown's avatar
unknown committed
3743
  pos= longlong10_to_str(val, pos, -10);
unknown's avatar
unknown committed
3744
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
3745
}
unknown's avatar
SCRUM  
unknown committed
3746
#endif
3747

unknown's avatar
unknown committed
3748

unknown's avatar
unknown committed
3749
/*
3750
  Intvar_log_event::Intvar_log_event()
unknown's avatar
unknown committed
3751
*/
3752

3753 3754 3755
Intvar_log_event::Intvar_log_event(const char* buf,
                                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3756
{
3757 3758 3759
  buf+= description_event->common_header_len;
  type= buf[I_TYPE_OFFSET];
  val= uint8korr(buf+I_VAL_OFFSET);
3760 3761
}

3762

unknown's avatar
unknown committed
3763
/*
3764
  Intvar_log_event::get_var_type_name()
unknown's avatar
unknown committed
3765
*/
3766 3767

const char* Intvar_log_event::get_var_type_name()
3768
{
3769 3770 3771 3772 3773
  switch(type) {
  case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID";
  case INSERT_ID_EVENT: return "INSERT_ID";
  default: /* impossible */ return "UNKNOWN";
  }
3774 3775
}

unknown's avatar
unknown committed
3776

unknown's avatar
unknown committed
3777
/*
3778
  Intvar_log_event::write()
unknown's avatar
unknown committed
3779
*/
3780

3781
#ifndef MYSQL_CLIENT
3782
bool Intvar_log_event::write(IO_CACHE* file)
3783
{
3784 3785
  uchar buf[9];
  buf[I_TYPE_OFFSET]= (uchar) type;
3786
  int8store(buf + I_VAL_OFFSET, val);
3787 3788
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3789
}
3790
#endif
3791

3792

unknown's avatar
unknown committed
3793
/*
3794
  Intvar_log_event::print()
unknown's avatar
unknown committed
3795
*/
3796 3797

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3798
void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3799
{
3800 3801 3802
  char llbuff[22];
  const char *msg;
  LINT_INIT(msg);
3803 3804
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);
3805

unknown's avatar
unknown committed
3806
  if (!print_event_info->short_form)
3807
  {
3808 3809
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tIntvar\n");
3810
  }
3811

3812
  my_b_printf(&cache, "SET ");
3813 3814 3815 3816 3817 3818 3819
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    msg="LAST_INSERT_ID";
    break;
  case INSERT_ID_EVENT:
    msg="INSERT_ID";
    break;
3820 3821 3822 3823
  case INVALID_INT_EVENT:
  default: // cannot happen
    msg="INVALID_INT";
    break;
3824
  }
unknown's avatar
unknown committed
3825 3826
  my_b_printf(&cache, "%s=%s%s\n",
              msg, llstr(val,llbuff), print_event_info->delimiter);
3827
}
3828
#endif
3829

3830

unknown's avatar
unknown committed
3831
/*
3832
  Intvar_log_event::do_apply_event()
unknown's avatar
unknown committed
3833
*/
3834

unknown's avatar
SCRUM  
unknown committed
3835
#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
3836
int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
3837
{
3838 3839 3840 3841
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
3842
  const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
3843

3844 3845
  switch (type) {
  case LAST_INSERT_ID_EVENT:
3846 3847
    thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
    thd->first_successful_insert_id_in_prev_stmt= val;
3848 3849
    break;
  case INSERT_ID_EVENT:
3850
    thd->force_one_auto_inc_interval(val);
3851 3852
    break;
  }
3853 3854 3855
  return 0;
}

3856
int Intvar_log_event::do_update_pos(Relay_log_info *rli)
3857
{
3858
  rli->inc_event_relay_log_pos();
3859
  return 0;
3860
}
3861 3862 3863


Log_event::enum_skip_reason
3864
Intvar_log_event::do_shall_skip(Relay_log_info *rli)
3865 3866
{
  /*
3867 3868 3869 3870 3871 3872
    It is a common error to set the slave skip counter to 1 instead of
    2 when recovering from an insert which used a auto increment,
    rand, or user var.  Therefore, if the slave skip counter is 1, we
    just say that this event should be skipped by ignoring it, meaning
    that we do not change the value of the slave skip counter since it
    will be decreased by the following insert event.
3873 3874
  */
  if (rli->slave_skip_counter == 1)
3875
    return Log_event::EVENT_SKIP_IGNORE;
3876
  else
3877
    return Log_event::do_shall_skip(rli);
3878 3879
}

unknown's avatar
SCRUM  
unknown committed
3880
#endif
3881

3882

unknown's avatar
unknown committed
3883
/**************************************************************************
3884
  Rand_log_event methods
unknown's avatar
unknown committed
3885
**************************************************************************/
3886

unknown's avatar
SCRUM  
unknown committed
3887
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3888
void Rand_log_event::pack_info(Protocol *protocol)
3889
{
unknown's avatar
unknown committed
3890 3891 3892 3893 3894
  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);
3895
  protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
3896
}
unknown's avatar
SCRUM  
unknown committed
3897
#endif
3898 3899


3900 3901 3902
Rand_log_event::Rand_log_event(const char* buf,
                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3903
{
3904 3905 3906
  buf+= description_event->common_header_len;
  seed1= uint8korr(buf+RAND_SEED1_OFFSET);
  seed2= uint8korr(buf+RAND_SEED2_OFFSET);
3907 3908
}

3909

3910
#ifndef MYSQL_CLIENT
3911
bool Rand_log_event::write(IO_CACHE* file)
3912
{
3913
  uchar buf[16];
3914 3915
  int8store(buf + RAND_SEED1_OFFSET, seed1);
  int8store(buf + RAND_SEED2_OFFSET, seed2);
3916 3917
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3918
}
3919
#endif
3920

3921 3922

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3923
void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3924
{
3925 3926 3927
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
3928
  char llbuff[22],llbuff2[22];
unknown's avatar
unknown committed
3929
  if (!print_event_info->short_form)
3930
  {
3931 3932
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tRand\n");
3933
  }
unknown's avatar
unknown committed
3934 3935 3936
  my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
              llstr(seed1, llbuff),llstr(seed2, llbuff2),
              print_event_info->delimiter);
3937
}
unknown's avatar
unknown committed
3938
#endif /* MYSQL_CLIENT */
3939

3940

unknown's avatar
SCRUM  
unknown committed
3941
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3942
int Rand_log_event::do_apply_event(Relay_log_info const *rli)
3943
{
3944 3945 3946 3947
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
3948
  const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
3949

unknown's avatar
unknown committed
3950 3951
  thd->rand.seed1= (ulong) seed1;
  thd->rand.seed2= (ulong) seed2;
3952 3953 3954
  return 0;
}

3955
int Rand_log_event::do_update_pos(Relay_log_info *rli)
3956
{
3957
  rli->inc_event_relay_log_pos();
3958 3959
  return 0;
}
3960

3961 3962

Log_event::enum_skip_reason
3963
Rand_log_event::do_shall_skip(Relay_log_info *rli)
3964 3965
{
  /*
3966 3967 3968 3969 3970 3971
    It is a common error to set the slave skip counter to 1 instead of
    2 when recovering from an insert which used a auto increment,
    rand, or user var.  Therefore, if the slave skip counter is 1, we
    just say that this event should be skipped by ignoring it, meaning
    that we do not change the value of the slave skip counter since it
    will be decreased by the following insert event.
3972 3973
  */
  if (rli->slave_skip_counter == 1)
3974
    return Log_event::EVENT_SKIP_IGNORE;
3975
  else
3976
    return Log_event::do_shall_skip(rli);
3977 3978
}

unknown's avatar
unknown committed
3979
#endif /* !MYSQL_CLIENT */
3980

unknown's avatar
unknown committed
3981

3982 3983 3984 3985 3986 3987 3988
/**************************************************************************
  Xid_log_event methods
**************************************************************************/

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Xid_log_event::pack_info(Protocol *protocol)
{
3989 3990
  char buf[128], *pos;
  pos= strmov(buf, "COMMIT /* xid=");
3991
  pos= longlong10_to_str(xid, pos, 10);
3992
  pos= strmov(pos, " */");
3993 3994 3995 3996
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
}
#endif

unknown's avatar
unknown committed
3997 3998 3999 4000 4001 4002 4003 4004
/*
  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
*/
4005 4006 4007 4008

Xid_log_event::
Xid_log_event(const char* buf,
              const Format_description_log_event *description_event)
4009 4010 4011
  :Log_event(buf, description_event)
{
  buf+= description_event->common_header_len;
4012
  memcpy((char*) &xid, buf, sizeof(xid));
4013 4014 4015
}


4016
#ifndef MYSQL_CLIENT
4017 4018 4019
bool Xid_log_event::write(IO_CACHE* file)
{
  return write_header(file, sizeof(xid)) ||
4020
         my_b_safe_write(file, (uchar*) &xid, sizeof(xid));
4021
}
4022
#endif
4023 4024 4025


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4026
void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4027
{
4028 4029 4030
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4031
  if (!print_event_info->short_form)
4032
  {
unknown's avatar
unknown committed
4033 4034 4035
    char buf[64];
    longlong10_to_str(xid, buf, 10);

4036 4037
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tXid = %s\n", buf);
4038
  }
unknown's avatar
unknown committed
4039
  my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
4040 4041 4042 4043 4044
}
#endif /* MYSQL_CLIENT */


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4045
int Xid_log_event::do_apply_event(Relay_log_info const *rli)
4046
{
4047
  /* For a slave Xid_log_event is COMMIT */
4048 4049
  general_log_print(thd, COM_QUERY,
                    "COMMIT /* implicit, from Xid_log_event */");
4050
  return end_trans(thd, COMMIT);
4051 4052 4053 4054
}
#endif /* !MYSQL_CLIENT */


unknown's avatar
unknown committed
4055
/**************************************************************************
4056
  User_var_log_event methods
unknown's avatar
unknown committed
4057
**************************************************************************/
unknown's avatar
unknown committed
4058

unknown's avatar
unknown committed
4059
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
4060 4061 4062
void User_var_log_event::pack_info(Protocol* protocol)
{
  char *buf= 0;
4063
  uint val_offset= 4 + name_len;
unknown's avatar
unknown committed
4064 4065 4066 4067
  uint event_len= val_offset;

  if (is_null)
  {
4068 4069
    if (!(buf= (char*) my_malloc(val_offset + 5, MYF(MY_WME))))
      return;
unknown's avatar
unknown committed
4070 4071 4072 4073 4074 4075 4076 4077 4078
    strmov(buf + val_offset, "NULL");
    event_len= val_offset + 4;
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
4079 4080 4081
      if (!(buf= (char*) my_malloc(val_offset + FLOATING_POINT_BUFFER,
                                   MYF(MY_WME))))
        return;
4082 4083
      event_len+= my_sprintf(buf + val_offset,
			     (buf + val_offset, "%.14g", real_val));
unknown's avatar
unknown committed
4084 4085
      break;
    case INT_RESULT:
4086 4087
      if (!(buf= (char*) my_malloc(val_offset + 22, MYF(MY_WME))))
        return;
unknown's avatar
unknown committed
4088 4089
      event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
      break;
unknown's avatar
unknown committed
4090 4091
    case DECIMAL_RESULT:
    {
4092 4093 4094
      if (!(buf= (char*) my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH,
                                   MYF(MY_WME))))
        return;
unknown's avatar
unknown committed
4095 4096
      String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin);
      my_decimal dec;
4097 4098
      binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) (val+2), &dec, val[0],
                        val[1]);
unknown's avatar
unknown committed
4099 4100 4101 4102
      my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
      event_len= str.length() + val_offset;
      break;
    } 
unknown's avatar
unknown committed
4103
    case STRING_RESULT:
4104
      /* 15 is for 'COLLATE' and other chars */
4105 4106
      buf= (char*) my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15,
                             MYF(MY_WME));
4107
      CHARSET_INFO *cs;
4108 4109
      if (!buf)
        return;
4110 4111 4112 4113 4114 4115 4116
      if (!(cs= get_charset(charset_number, MYF(0))))
      {
        strmov(buf+val_offset, "???");
        event_len+= 3;
      }
      else
      {
4117 4118 4119
        char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
        p= str_to_hex(p, val, val_len);
        p= strxmov(p, " COLLATE ", cs->name, NullS);
4120 4121
        event_len= p-buf;
      }
unknown's avatar
unknown committed
4122
      break;
4123
    case ROW_RESULT:
unknown's avatar
unknown committed
4124
    default:
unknown's avatar
unknown committed
4125 4126 4127 4128 4129
      DBUG_ASSERT(1);
      return;
    }
  }
  buf[0]= '@';
4130
  buf[1]= '`';
4131
  memcpy(buf+2, name, name_len);
4132 4133
  buf[2+name_len]= '`';
  buf[3+name_len]= '=';
4134
  protocol->store(buf, event_len, &my_charset_bin);
4135
  my_free(buf, MYF(0));
unknown's avatar
unknown committed
4136
}
unknown's avatar
unknown committed
4137
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
4138 4139


4140 4141 4142 4143
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
4144
{
4145
  buf+= description_event->common_header_len;
unknown's avatar
unknown committed
4146 4147
  name_len= uint4korr(buf);
  name= (char *) buf + UV_NAME_LEN_SIZE;
4148 4149
  buf+= UV_NAME_LEN_SIZE + name_len;
  is_null= (bool) *buf;
unknown's avatar
unknown committed
4150 4151 4152
  if (is_null)
  {
    type= STRING_RESULT;
4153
    charset_number= my_charset_bin.number;
unknown's avatar
unknown committed
4154 4155 4156 4157 4158
    val_len= 0;
    val= 0;  
  }
  else
  {
4159 4160 4161
    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
4162
		       UV_CHARSET_NUMBER_SIZE);
4163 4164
    val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
		   UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
unknown's avatar
unknown committed
4165 4166 4167 4168
  }
}


4169
#ifndef MYSQL_CLIENT
4170
bool User_var_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
4171 4172 4173 4174
{
  char buf[UV_NAME_LEN_SIZE];
  char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + 
	    UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
4175
  uchar buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
4176
  uint buf1_length;
4177
  ulong event_length;
4178

unknown's avatar
unknown committed
4179
  int4store(buf, name_len);
4180 4181 4182 4183
  
  if ((buf1[0]= is_null))
  {
    buf1_length= 1;
4184
    val_len= 0;                                 // Length of 'pos'
4185 4186
  }    
  else
unknown's avatar
unknown committed
4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197
  {
    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
4198 4199 4200 4201 4202 4203
    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;
4204
      decimal2bin((decimal_t*)val, buf2+2, buf2[0], buf2[1]);
unknown's avatar
unknown committed
4205 4206 4207
      val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
      break;
    }
unknown's avatar
unknown committed
4208
    case STRING_RESULT:
4209
      pos= (uchar*) val;
unknown's avatar
unknown committed
4210
      break;
4211
    case ROW_RESULT:
unknown's avatar
unknown committed
4212
    default:
unknown's avatar
unknown committed
4213 4214 4215
      DBUG_ASSERT(1);
      return 0;
    }
unknown's avatar
unknown committed
4216 4217
    int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
    buf1_length= 10;
unknown's avatar
unknown committed
4218
  }
4219 4220 4221 4222 4223

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

  return (write_header(file, event_length) ||
4224 4225 4226 4227
          my_b_safe_write(file, (uchar*) buf, sizeof(buf))   ||
	  my_b_safe_write(file, (uchar*) name, name_len)     ||
	  my_b_safe_write(file, (uchar*) buf1, buf1_length) ||
	  my_b_safe_write(file, pos, val_len));
unknown's avatar
unknown committed
4228
}
4229
#endif
unknown's avatar
unknown committed
4230

4231

unknown's avatar
unknown committed
4232
/*
unknown's avatar
unknown committed
4233
  User_var_log_event::print()
unknown's avatar
unknown committed
4234
*/
unknown's avatar
unknown committed
4235 4236

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4237
void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
4238
{
4239 4240 4241
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4242
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
4243
  {
4244 4245
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tUser_var\n");
unknown's avatar
unknown committed
4246 4247
  }

4248
  my_b_printf(&cache, "SET @`");
4249
  my_b_write(&cache, (uchar*) name, (uint) (name_len));
4250
  my_b_printf(&cache, "`");
unknown's avatar
unknown committed
4251 4252 4253

  if (is_null)
  {
unknown's avatar
unknown committed
4254
    my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
4255 4256 4257 4258 4259 4260 4261
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
unknown's avatar
unknown committed
4262
      my_b_printf(&cache, ":=%.14g%s\n", real_val, print_event_info->delimiter);
unknown's avatar
unknown committed
4263 4264 4265 4266
      break;
    case INT_RESULT:
      char int_buf[22];
      longlong10_to_str(uint8korr(val), int_buf, -10);
unknown's avatar
unknown committed
4267
      my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4268
      break;
unknown's avatar
unknown committed
4269 4270 4271 4272 4273 4274
    case DECIMAL_RESULT:
    {
      char str_buf[200];
      int str_len= sizeof(str_buf) - 1;
      int precision= (int)val[0];
      int scale= (int)val[1];
4275 4276
      decimal_digit_t dec_buf[10];
      decimal_t dec;
unknown's avatar
unknown committed
4277 4278 4279
      dec.len= 10;
      dec.buf= dec_buf;

4280
      bin2decimal((uchar*) val+2, &dec, precision, scale);
unknown's avatar
unknown committed
4281 4282
      decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
      str_buf[str_len]= 0;
unknown's avatar
unknown committed
4283
      my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4284 4285
      break;
    }
unknown's avatar
unknown committed
4286
    case STRING_RESULT:
4287
    {
4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301
      /*
        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.
      */
4302 4303 4304 4305
      char *hex_str;
      CHARSET_INFO *cs;

      if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte
4306
        break; // no error, as we are 'void'
4307
      str_to_hex(hex_str, val, val_len);
4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318
      /*
        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.
        */
unknown's avatar
unknown committed
4319
        my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
4320
      else
unknown's avatar
unknown committed
4321 4322 4323
        my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
                    cs->csname, hex_str, cs->name,
                    print_event_info->delimiter);
4324
      my_afree(hex_str);
4325
    }
unknown's avatar
unknown committed
4326
      break;
4327
    case ROW_RESULT:
unknown's avatar
unknown committed
4328
    default:
unknown's avatar
unknown committed
4329
      DBUG_ASSERT(1);
unknown's avatar
unknown committed
4330 4331 4332 4333
      return;
    }
  }
}
unknown's avatar
SCRUM  
unknown committed
4334
#endif
4335

unknown's avatar
unknown committed
4336

unknown's avatar
unknown committed
4337
/*
4338
  User_var_log_event::do_apply_event()
unknown's avatar
unknown committed
4339
*/
unknown's avatar
unknown committed
4340

unknown's avatar
unknown committed
4341
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4342
int User_var_log_event::do_apply_event(Relay_log_info const *rli)
unknown's avatar
unknown committed
4343 4344
{
  Item *it= 0;
4345 4346 4347
  CHARSET_INFO *charset;
  if (!(charset= get_charset(charset_number, MYF(MY_WME))))
    return 1;
unknown's avatar
unknown committed
4348 4349 4350
  LEX_STRING user_var_name;
  user_var_name.str= name;
  user_var_name.length= name_len;
4351 4352
  double real_val;
  longlong int_val;
unknown's avatar
unknown committed
4353

4354 4355 4356 4357
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
4358
  const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
4359

unknown's avatar
unknown committed
4360 4361 4362 4363 4364 4365 4366 4367 4368
  if (is_null)
  {
    it= new Item_null();
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      float8get(real_val, val);
4369
      it= new Item_float(real_val, 0);
4370
      val= (char*) &real_val;		// Pointer to value in native format
4371
      val_len= 8;
unknown's avatar
unknown committed
4372 4373
      break;
    case INT_RESULT:
4374 4375 4376
      int_val= (longlong) uint8korr(val);
      it= new Item_int(int_val);
      val= (char*) &int_val;		// Pointer to value in native format
4377
      val_len= 8;
unknown's avatar
unknown committed
4378
      break;
unknown's avatar
unknown committed
4379 4380
    case DECIMAL_RESULT:
    {
4381
      Item_decimal *dec= new Item_decimal((uchar*) val+2, val[0], val[1]);
unknown's avatar
unknown committed
4382 4383 4384 4385 4386
      it= dec;
      val= (char *)dec->val_decimal(NULL);
      val_len= sizeof(my_decimal);
      break;
    }
unknown's avatar
unknown committed
4387 4388 4389
    case STRING_RESULT:
      it= new Item_string(val, val_len, charset);
      break;
4390
    case ROW_RESULT:
unknown's avatar
unknown committed
4391
    default:
unknown's avatar
unknown committed
4392 4393 4394 4395 4396
      DBUG_ASSERT(1);
      return 0;
    }
  }
  Item_func_set_user_var e(user_var_name, it);
4397 4398 4399 4400
  /*
    Item_func_set_user_var can't substitute something else on its place =>
    0 can be passed as last argument (reference on item)
  */
4401
  e.fix_fields(thd, 0);
4402 4403 4404 4405 4406
  /*
    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
4407
  e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
unknown's avatar
unknown committed
4408
  free_root(thd->mem_root,0);
unknown's avatar
unknown committed
4409

4410 4411 4412
  return 0;
}

4413
int User_var_log_event::do_update_pos(Relay_log_info *rli)
4414
{
4415
  rli->inc_event_relay_log_pos();
unknown's avatar
unknown committed
4416 4417
  return 0;
}
4418

4419
Log_event::enum_skip_reason
4420
User_var_log_event::do_shall_skip(Relay_log_info *rli)
4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434
{
  /*
    It is a common error to set the slave skip counter to 1 instead
    of 2 when recovering from an insert which used a auto increment,
    rand, or user var.  Therefore, if the slave skip counter is 1, we
    just say that this event should be skipped by ignoring it, meaning
    that we do not change the value of the slave skip counter since it
    will be decreased by the following insert event.
  */
  if (rli->slave_skip_counter == 1)
    return Log_event::EVENT_SKIP_IGNORE;
  else
    return Log_event::do_shall_skip(rli);
}
unknown's avatar
unknown committed
4435
#endif /* !MYSQL_CLIENT */
4436 4437


unknown's avatar
unknown committed
4438
/**************************************************************************
4439
  Slave_log_event methods
unknown's avatar
unknown committed
4440
**************************************************************************/
unknown's avatar
unknown committed
4441

unknown's avatar
SCRUM  
unknown committed
4442
#ifdef HAVE_REPLICATION
4443
#ifdef MYSQL_CLIENT
4444
void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
4445
{
4446 4447
  Write_on_release_cache cache(&print_event_info->head_cache, file_arg);

unknown's avatar
unknown committed
4448
  if (print_event_info->short_form)
4449
    return;
4450 4451
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n# %s", "Unknown event\n");
4452 4453
}
#endif  
4454

4455
#ifndef MYSQL_CLIENT
4456
void Slave_log_event::pack_info(Protocol *protocol)
4457
{
unknown's avatar
unknown committed
4458
  char buf[256+HOSTNAME_LENGTH], *pos;
4459 4460 4461 4462 4463 4464 4465
  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
4466
  pos= longlong10_to_str(master_pos, pos, 10);
4467
  protocol->store(buf, pos-buf, &my_charset_bin);
4468
}
unknown's avatar
unknown committed
4469
#endif /* !MYSQL_CLIENT */
4470 4471 4472 4473


#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
4474
				 Relay_log_info* rli)
4475
  :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
4476 4477 4478 4479
{
  DBUG_ENTER("Slave_log_event");
  if (!rli->inited)				// QQ When can this happen ?
    DBUG_VOID_RETURN;
4480

4481
  Master_info* mi = rli->mi;
4482 4483 4484 4485
  // 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);
4486
  master_log_len = strlen(rli->group_master_log_name);
4487 4488 4489
  // 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))))
4490
  {
4491 4492 4493
    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;
4494
    memcpy(master_log, rli->group_master_log_name, master_log_len + 1);
4495
    master_port = mi->port;
4496
    master_pos = rli->group_master_log_pos;
unknown's avatar
unknown committed
4497
    DBUG_PRINT("info", ("master_log: %s  pos: %lu", master_log,
4498
			(ulong) master_pos));
4499
  }
4500 4501 4502 4503 4504 4505
  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
4506
#endif /* !MYSQL_CLIENT */
4507 4508 4509 4510 4511 4512 4513 4514 4515


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


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4516
void Slave_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4517
{
4518 4519
  Write_on_release_cache cache(&print_event_info->head_cache, file);

4520
  char llbuff[22];
unknown's avatar
unknown committed
4521
  if (print_event_info->short_form)
4522
    return;
4523 4524
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n\
unknown's avatar
unknown committed
4525
Slave: master_host: '%s'  master_port: %d  master_log: '%s'  master_pos: %s\n",
4526 4527
	  master_host, master_port, master_log, llstr(master_pos, llbuff));
}
unknown's avatar
unknown committed
4528
#endif /* MYSQL_CLIENT */
4529 4530 4531 4532 4533 4534 4535 4536


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


4537
#ifndef MYSQL_CLIENT
4538
bool Slave_log_event::write(IO_CACHE* file)
4539
{
4540
  ulong event_length= get_data_size();
4541 4542 4543
  int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
  int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
  // log and host are already there
4544 4545

  return (write_header(file, event_length) ||
4546
          my_b_safe_write(file, (uchar*) mem_pool, event_length));
4547
}
4548
#endif
4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559


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)
4560
  {
4561 4562
    master_host = 0;
    return;
4563
  }
4564 4565
  master_log_len = strlen(master_log);
}
4566

4567

4568 4569 4570
/* 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)
4571
{
4572
  if (event_len < LOG_EVENT_HEADER_LEN)
4573
    return;
4574
  event_len -= LOG_EVENT_HEADER_LEN;
4575 4576 4577 4578 4579
  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);
4580 4581
}

4582

4583
#ifndef MYSQL_CLIENT
4584
int Slave_log_event::do_apply_event(Relay_log_info const *rli)
4585 4586 4587
{
  if (mysql_bin_log.is_open())
    mysql_bin_log.write(this);
4588
  return 0;
4589
}
unknown's avatar
unknown committed
4590
#endif /* !MYSQL_CLIENT */
4591 4592


unknown's avatar
unknown committed
4593
/**************************************************************************
unknown's avatar
unknown committed
4594
	Stop_log_event methods
unknown's avatar
unknown committed
4595
**************************************************************************/
4596

unknown's avatar
unknown committed
4597
/*
4598
  Stop_log_event::print()
4599
*/
4600 4601

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4602
void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4603
{
4604 4605 4606
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4607
  if (print_event_info->short_form)
4608 4609
    return;

4610 4611
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\tStop\n");
4612
}
unknown's avatar
unknown committed
4613
#endif /* MYSQL_CLIENT */
4614

4615

4616
/*
4617
  Stop_log_event::do_apply_event()
4618 4619 4620 4621 4622 4623 4624 4625

  The master stopped.  We used to clean up all temporary tables but
  this is useless as, as the master has shut down properly, it has
  written all DROP TEMPORARY TABLE (prepared statements' deletion is
  TODO only when we binlog prep stmts).  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.  The place
  were we must do this cleaning is in
4626
  Start_log_event_v3::do_apply_event(), not here. Because if we come
4627
  here, the master was sane.
4628 4629
*/

4630
#ifndef MYSQL_CLIENT
4631
int Stop_log_event::do_update_pos(Relay_log_info *rli)
4632
{
unknown's avatar
unknown committed
4633 4634
  /*
    We do not want to update master_log pos because we get a rotate event
4635
    before stop, so by now group_master_log_name is set to the next log.
4636
    If we updated it, we will have incorrect master coordinates and this
unknown's avatar
unknown committed
4637
    could give false triggers in MASTER_POS_WAIT() that we have reached
4638
    the target position when in fact we have not.
unknown's avatar
unknown committed
4639
  */
4640 4641 4642 4643 4644 4645 4646
  if (thd->options & OPTION_BEGIN)
    rli->inc_event_relay_log_pos();
  else
  {
    rli->inc_group_relay_log_pos(0);
    flush_relay_log_info(rli);
  }
4647 4648
  return 0;
}
4649

unknown's avatar
unknown committed
4650
#endif /* !MYSQL_CLIENT */
unknown's avatar
SCRUM  
unknown committed
4651
#endif /* HAVE_REPLICATION */
4652

4653

unknown's avatar
unknown committed
4654
/**************************************************************************
unknown's avatar
unknown committed
4655
	Create_file_log_event methods
unknown's avatar
unknown committed
4656
**************************************************************************/
4657 4658

/*
4659
  Create_file_log_event ctor
unknown's avatar
unknown committed
4660
*/
4661 4662

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4663 4664 4665 4666
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,
4667
                      bool ignore,
unknown's avatar
unknown committed
4668
		      char* block_arg, uint block_len_arg, bool using_trans)
4669
  :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
unknown's avatar
unknown committed
4670
		  using_trans),
4671
   fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
4672
   file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
4673
{
unknown's avatar
unknown committed
4674
  DBUG_ENTER("Create_file_log_event");
4675
  sql_ex.force_new_format();
unknown's avatar
unknown committed
4676
  DBUG_VOID_RETURN;
4677
}
4678

4679

unknown's avatar
unknown committed
4680
/*
4681
  Create_file_log_event::write_data_body()
unknown's avatar
unknown committed
4682
*/
4683

4684
bool Create_file_log_event::write_data_body(IO_CACHE* file)
4685
{
4686 4687
  bool res;
  if ((res= Load_log_event::write_data_body(file)) || fake_base)
4688
    return res;
4689 4690
  return (my_b_safe_write(file, (uchar*) "", 1) ||
          my_b_safe_write(file, (uchar*) block, block_len));
4691 4692
}

4693

unknown's avatar
unknown committed
4694
/*
4695
  Create_file_log_event::write_data_header()
unknown's avatar
unknown committed
4696
*/
unknown's avatar
unknown committed
4697

4698
bool Create_file_log_event::write_data_header(IO_CACHE* file)
4699
{
4700
  bool res;
4701
  uchar buf[CREATE_FILE_HEADER_LEN];
4702 4703
  if ((res= Load_log_event::write_data_header(file)) || fake_base)
    return res;
4704
  int4store(buf + CF_FILE_ID_OFFSET, file_id);
4705
  return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN) != 0;
4706 4707 4708
}


unknown's avatar
unknown committed
4709
/*
4710
  Create_file_log_event::write_base()
unknown's avatar
unknown committed
4711
*/
4712

4713
bool Create_file_log_event::write_base(IO_CACHE* file)
4714
{
4715 4716 4717 4718
  bool res;
  fake_base= 1;                                 // pretend we are Load event
  res= write(file);
  fake_base= 0;
4719 4720 4721
  return res;
}

4722
#endif /* !MYSQL_CLIENT */
4723

unknown's avatar
unknown committed
4724
/*
4725
  Create_file_log_event ctor
unknown's avatar
unknown committed
4726
*/
4727

4728 4729 4730
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)
4731
{
4732 4733 4734 4735 4736
  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];
4737
  if (!(event_buf= (char*) my_memdup(buf, len, MYF(MY_WME))) ||
4738 4739 4740 4741 4742 4743 4744
      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
4745
    DBUG_VOID_RETURN;
4746
  if (description_event->binlog_version!=1)
4747
  {
4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764
    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);
4765 4766 4767 4768 4769 4770 4771 4772 4773
    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;
4774
  }
unknown's avatar
unknown committed
4775
  DBUG_VOID_RETURN;
4776 4777
}

4778

unknown's avatar
unknown committed
4779
/*
4780
  Create_file_log_event::print()
unknown's avatar
unknown committed
4781
*/
4782 4783

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4784
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
4785
				  bool enable_local)
unknown's avatar
unknown committed
4786
{
4787 4788
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
4789
  if (print_event_info->short_form)
4790 4791
  {
    if (enable_local && check_fname_outside_temp_buf())
unknown's avatar
unknown committed
4792
      Load_log_event::print(file, print_event_info);
4793
    return;
4794 4795 4796 4797
  }

  if (enable_local)
  {
unknown's avatar
unknown committed
4798
    Load_log_event::print(file, print_event_info,
4799
			  !check_fname_outside_temp_buf());
unknown's avatar
unknown committed
4800 4801 4802 4803
    /* 
       That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
       SHOW BINLOG EVENTS we don't.
    */
4804
    my_b_printf(&cache, "#"); 
4805 4806
  }

4807
  my_b_printf(&cache, " file_id: %d  block_len: %d\n", file_id, block_len);
unknown's avatar
unknown committed
4808
}
4809

unknown's avatar
unknown committed
4810

unknown's avatar
unknown committed
4811
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4812
{
unknown's avatar
unknown committed
4813
  print(file, print_event_info, 0);
4814
}
unknown's avatar
unknown committed
4815
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
4816

4817

unknown's avatar
unknown committed
4818
/*
4819
  Create_file_log_event::pack_info()
unknown's avatar
unknown committed
4820
*/
4821

unknown's avatar
SCRUM  
unknown committed
4822
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4823
void Create_file_log_event::pack_info(Protocol *protocol)
4824
{
4825 4826 4827
  char buf[NAME_LEN*2 + 30 + 21*2], *pos;
  pos= strmov(buf, "db=");
  memcpy(pos, db, db_len);
unknown's avatar
unknown committed
4828
  pos= strmov(pos + db_len, ";table=");
4829
  memcpy(pos, table_name, table_name_len);
unknown's avatar
unknown committed
4830
  pos= strmov(pos + table_name_len, ";file_id=");
4831 4832 4833
  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
4834
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
4835
}
unknown's avatar
unknown committed
4836
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4837 4838


unknown's avatar
unknown committed
4839
/*
4840
  Create_file_log_event::do_apply_event()
unknown's avatar
unknown committed
4841
*/
4842

unknown's avatar
SCRUM  
unknown committed
4843
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4844
int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
4845
{
unknown's avatar
unknown committed
4846 4847
  char proc_info[17+FN_REFLEN+10], *fname_buf;
  char *ext;
4848 4849 4850
  int fd = -1;
  IO_CACHE file;
  int error = 1;
unknown's avatar
unknown committed
4851

4852
  bzero((char*)&file, sizeof(file));
unknown's avatar
unknown committed
4853 4854
  fname_buf= strmov(proc_info, "Making temp file ");
  ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
4855
  thd->proc_info= proc_info;
4856 4857 4858 4859
  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 ||
4860 4861 4862
      init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
4863 4864 4865
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: could not open file '%s'",
                fname_buf);
4866 4867 4868 4869
    goto err;
  }
  
  // a trick to avoid allocating another buffer
unknown's avatar
unknown committed
4870 4871
  fname= fname_buf;
  fname_len= (uint) (strmov(ext, ".data") - fname);
4872 4873
  if (write_base(&file))
  {
unknown's avatar
unknown committed
4874
    strmov(ext, ".info"); // to have it right in the error message
4875 4876 4877
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: could not write to file '%s'",
                fname_buf);
4878 4879 4880 4881 4882 4883
    goto err;
  }
  end_io_cache(&file);
  my_close(fd, MYF(0));
  
  // fname_buf now already has .data, not .info, because we did our trick
4884 4885 4886 4887
  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)
4888
  {
4889 4890 4891
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: could not open file '%s'",
                fname_buf);
4892 4893
    goto err;
  }
4894
  if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
4895
  {
4896 4897 4898
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: write to '%s' failed",
                fname_buf);
4899 4900
    goto err;
  }
4901 4902
  error=0;					// Everything is ok

4903 4904 4905 4906 4907
err:
  if (error)
    end_io_cache(&file);
  if (fd >= 0)
    my_close(fd, MYF(0));
unknown's avatar
unknown committed
4908
  thd->proc_info= 0;
4909
  return error == 0;
4910
}
unknown's avatar
unknown committed
4911
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4912

4913

unknown's avatar
unknown committed
4914
/**************************************************************************
unknown's avatar
unknown committed
4915
	Append_block_log_event methods
unknown's avatar
unknown committed
4916
**************************************************************************/
4917

unknown's avatar
unknown committed
4918
/*
4919
  Append_block_log_event ctor
unknown's avatar
unknown committed
4920
*/
4921 4922

#ifndef MYSQL_CLIENT  
4923 4924 4925
Append_block_log_event::Append_block_log_event(THD *thd_arg,
                                               const char *db_arg,
					       char *block_arg,
unknown's avatar
unknown committed
4926 4927 4928
					       uint block_len_arg,
					       bool using_trans)
  :Log_event(thd_arg,0, using_trans), block(block_arg),
unknown's avatar
unknown committed
4929
   block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
4930 4931
{
}
unknown's avatar
unknown committed
4932
#endif
4933 4934


unknown's avatar
unknown committed
4935
/*
4936
  Append_block_log_event ctor
unknown's avatar
unknown committed
4937
*/
4938

4939 4940 4941
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)
4942
{
4943 4944 4945 4946 4947 4948
  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
4949
    DBUG_VOID_RETURN;
4950 4951 4952
  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
4953
  DBUG_VOID_RETURN;
4954 4955 4956
}


unknown's avatar
unknown committed
4957
/*
4958
  Append_block_log_event::write()
unknown's avatar
unknown committed
4959
*/
4960

4961
#ifndef MYSQL_CLIENT
4962
bool Append_block_log_event::write(IO_CACHE* file)
4963
{
4964
  uchar buf[APPEND_BLOCK_HEADER_LEN];
4965
  int4store(buf + AB_FILE_ID_OFFSET, file_id);
4966 4967
  return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
          my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
4968
	  my_b_safe_write(file, (uchar*) block, block_len));
4969
}
4970
#endif
4971 4972


unknown's avatar
unknown committed
4973
/*
4974
  Append_block_log_event::print()
unknown's avatar
unknown committed
4975
*/
4976 4977

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
4978
void Append_block_log_event::print(FILE* file,
unknown's avatar
unknown committed
4979
				   PRINT_EVENT_INFO* print_event_info)
4980
{
4981 4982
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
4983
  if (print_event_info->short_form)
4984
    return;
4985 4986 4987
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#%s: file_id: %d  block_len: %d\n",
              get_type_str(), file_id, block_len);
4988
}
unknown's avatar
unknown committed
4989
#endif /* MYSQL_CLIENT */
4990 4991


unknown's avatar
unknown committed
4992
/*
4993
  Append_block_log_event::pack_info()
unknown's avatar
unknown committed
4994
*/
4995

unknown's avatar
SCRUM  
unknown committed
4996
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4997
void Append_block_log_event::pack_info(Protocol *protocol)
4998 4999 5000 5001 5002 5003
{
  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
5004
  protocol->store(buf, length, &my_charset_bin);
5005 5006 5007
}


unknown's avatar
unknown committed
5008
/*
5009
  Append_block_log_event::get_create_or_append()
unknown's avatar
unknown committed
5010 5011
*/

5012
int Append_block_log_event::get_create_or_append() const
unknown's avatar
unknown committed
5013
{
5014
  return 0; /* append to the file, fail if not exists */
unknown's avatar
unknown committed
5015 5016
}

unknown's avatar
unknown committed
5017
/*
5018
  Append_block_log_event::do_apply_event()
unknown's avatar
unknown committed
5019
*/
5020

5021
int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
5022
{
5023
  char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
5024
  int fd;
5025
  int error = 1;
5026
  DBUG_ENTER("Append_block_log_event::do_apply_event");
5027

unknown's avatar
unknown committed
5028 5029
  fname= strmov(proc_info, "Making temp file ");
  slave_load_file_stem(fname, file_id, server_id, ".data");
5030
  thd->proc_info= proc_info;
5031 5032 5033 5034 5035 5036 5037
  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)
    {
5038 5039 5040
      rli->report(ERROR_LEVEL, my_errno,
                  "Error in %s event: could not create file '%s'",
                  get_type_str(), fname);
5041 5042 5043
      goto err;
    }
  }
5044 5045
  else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
                         MYF(MY_WME))) < 0)
5046
  {
5047 5048 5049
    rli->report(ERROR_LEVEL, my_errno,
                "Error in %s event: could not open file '%s'",
                get_type_str(), fname);
5050 5051
    goto err;
  }
5052
  if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
5053
  {
5054 5055 5056
    rli->report(ERROR_LEVEL, my_errno,
                "Error in %s event: write to '%s' failed",
                get_type_str(), fname);
5057 5058 5059
    goto err;
  }
  error=0;
5060

5061 5062 5063
err:
  if (fd >= 0)
    my_close(fd, MYF(0));
5064
  thd->proc_info= 0;
5065
  DBUG_RETURN(error);
5066
}
unknown's avatar
SCRUM  
unknown committed
5067
#endif
5068 5069


unknown's avatar
unknown committed
5070
/**************************************************************************
unknown's avatar
unknown committed
5071
	Delete_file_log_event methods
unknown's avatar
unknown committed
5072
**************************************************************************/
5073

unknown's avatar
unknown committed
5074
/*
5075
  Delete_file_log_event ctor
unknown's avatar
unknown committed
5076
*/
5077 5078

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5079 5080 5081
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)
5082 5083
{
}
unknown's avatar
unknown committed
5084
#endif
5085

unknown's avatar
unknown committed
5086
/*
5087
  Delete_file_log_event ctor
unknown's avatar
unknown committed
5088
*/
5089

5090 5091 5092
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)
5093
{
5094 5095 5096
  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))
5097
    return;
5098
  file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
5099 5100 5101
}


unknown's avatar
unknown committed
5102
/*
5103
  Delete_file_log_event::write()
unknown's avatar
unknown committed
5104
*/
5105

5106
#ifndef MYSQL_CLIENT
5107
bool Delete_file_log_event::write(IO_CACHE* file)
5108
{
5109
 uchar buf[DELETE_FILE_HEADER_LEN];
5110
 int4store(buf + DF_FILE_ID_OFFSET, file_id);
5111 5112
 return (write_header(file, sizeof(buf)) ||
         my_b_safe_write(file, buf, sizeof(buf)));
5113
}
5114
#endif
5115 5116


unknown's avatar
unknown committed
5117
/*
5118
  Delete_file_log_event::print()
unknown's avatar
unknown committed
5119
*/
5120 5121

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5122
void Delete_file_log_event::print(FILE* file,
unknown's avatar
unknown committed
5123
				  PRINT_EVENT_INFO* print_event_info)
5124
{
5125 5126
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5127
  if (print_event_info->short_form)
5128
    return;
5129 5130
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id);
5131
}
unknown's avatar
unknown committed
5132
#endif /* MYSQL_CLIENT */
5133

unknown's avatar
unknown committed
5134
/*
5135
  Delete_file_log_event::pack_info()
unknown's avatar
unknown committed
5136
*/
5137

unknown's avatar
SCRUM  
unknown committed
5138
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5139
void Delete_file_log_event::pack_info(Protocol *protocol)
5140 5141 5142 5143
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
5144
  protocol->store(buf, (int32) length, &my_charset_bin);
5145
}
unknown's avatar
SCRUM  
unknown committed
5146
#endif
5147

unknown's avatar
unknown committed
5148
/*
5149
  Delete_file_log_event::do_apply_event()
unknown's avatar
unknown committed
5150
*/
5151

unknown's avatar
SCRUM  
unknown committed
5152
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5153
int Delete_file_log_event::do_apply_event(Relay_log_info const *rli)
5154 5155
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
5156
  char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
5157
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5158
  strmov(ext, ".info");
5159
  (void) my_delete(fname, MYF(MY_WME));
5160
  return 0;
5161
}
unknown's avatar
unknown committed
5162
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5163 5164


unknown's avatar
unknown committed
5165
/**************************************************************************
unknown's avatar
unknown committed
5166
	Execute_load_log_event methods
unknown's avatar
unknown committed
5167
**************************************************************************/
5168

unknown's avatar
unknown committed
5169
/*
5170
  Execute_load_log_event ctor
unknown's avatar
unknown committed
5171
*/
5172 5173

#ifndef MYSQL_CLIENT  
5174 5175
Execute_load_log_event::Execute_load_log_event(THD *thd_arg,
                                               const char* db_arg,
unknown's avatar
unknown committed
5176 5177
					       bool using_trans)
  :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
5178 5179
{
}
unknown's avatar
unknown committed
5180
#endif
5181 5182
  

unknown's avatar
unknown committed
5183
/*
5184
  Execute_load_log_event ctor
unknown's avatar
unknown committed
5185
*/
5186

5187 5188 5189
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)
5190
{
5191 5192 5193
  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))
5194
    return;
5195
  file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
5196 5197 5198
}


unknown's avatar
unknown committed
5199
/*
5200
  Execute_load_log_event::write()
unknown's avatar
unknown committed
5201
*/
5202

5203
#ifndef MYSQL_CLIENT
5204
bool Execute_load_log_event::write(IO_CACHE* file)
5205
{
5206
  uchar buf[EXEC_LOAD_HEADER_LEN];
5207
  int4store(buf + EL_FILE_ID_OFFSET, file_id);
5208 5209
  return (write_header(file, sizeof(buf)) || 
          my_b_safe_write(file, buf, sizeof(buf)));
5210
}
5211
#endif
5212 5213


unknown's avatar
unknown committed
5214
/*
5215
  Execute_load_log_event::print()
unknown's avatar
unknown committed
5216
*/
5217 5218

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5219
void Execute_load_log_event::print(FILE* file,
unknown's avatar
unknown committed
5220
				   PRINT_EVENT_INFO* print_event_info)
5221
{
5222 5223
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5224
  if (print_event_info->short_form)
5225
    return;
5226 5227 5228
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
              file_id);
5229
}
unknown's avatar
unknown committed
5230
#endif
5231

unknown's avatar
unknown committed
5232
/*
5233
  Execute_load_log_event::pack_info()
unknown's avatar
unknown committed
5234
*/
5235

unknown's avatar
SCRUM  
unknown committed
5236
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5237
void Execute_load_log_event::pack_info(Protocol *protocol)
5238 5239 5240 5241
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
5242
  protocol->store(buf, (int32) length, &my_charset_bin);
5243 5244 5245
}


unknown's avatar
unknown committed
5246
/*
5247
  Execute_load_log_event::do_apply_event()
unknown's avatar
unknown committed
5248
*/
unknown's avatar
SCRUM  
unknown committed
5249

5250
int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
5251 5252
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
5253
  char *ext;
5254
  int fd;
unknown's avatar
unknown committed
5255
  int error= 1;
5256
  IO_CACHE file;
unknown's avatar
unknown committed
5257
  Load_log_event *lev= 0;
5258

unknown's avatar
unknown committed
5259
  ext= slave_load_file_stem(fname, file_id, server_id, ".info");
5260 5261
  if ((fd = my_open(fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
                    MYF(MY_WME))) < 0 ||
5262 5263 5264
      init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
5265 5266 5267
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Exec_load event: could not open file '%s'",
                fname);
5268 5269
    goto err;
  }
5270
  if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
5271 5272
                                                         (pthread_mutex_t*)0,
                                                         rli->relay_log.description_event_for_exec)) ||
5273
      lev->get_type_code() != NEW_LOAD_EVENT)
5274
  {
5275
    rli->report(ERROR_LEVEL, 0, "Error in Exec_load event: "
5276
                    "file '%s' appears corrupted", fname);
5277 5278
    goto err;
  }
unknown's avatar
unknown committed
5279

5280
  lev->thd = thd;
5281
  /*
5282
    lev->do_apply_event should use rli only for errors i.e. should
5283 5284
    not advance rli's position.

5285
    lev->do_apply_event is the place where the table is loaded (it
5286
    calls mysql_load()).
5287
  */
unknown's avatar
unknown committed
5288

5289
  const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
5290
  if (lev->do_apply_event(0,rli,1)) 
5291
  {
5292 5293 5294 5295 5296 5297 5298 5299
    /*
      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.
    */
5300
    char *tmp= my_strdup(rli->last_error().message, MYF(MY_WME));
5301 5302
    if (tmp)
    {
5303
      rli->report(ERROR_LEVEL, rli->last_error().number,
5304
                  "%s. Failed executing load from '%s'", tmp, fname);
5305 5306
      my_free(tmp,MYF(0));
    }
5307 5308
    goto err;
  }
unknown's avatar
unknown committed
5309 5310 5311 5312 5313 5314 5315 5316 5317 5318
  /*
    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;
  }
5319
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5320
  memcpy(ext, ".data", 6);
5321
  (void) my_delete(fname, MYF(MY_WME));
5322
  error = 0;
5323

5324 5325 5326
err:
  delete lev;
  if (fd >= 0)
5327
  {
5328
    my_close(fd, MYF(0));
5329 5330
    end_io_cache(&file);
  }
5331
  return error;
5332
}
unknown's avatar
SCRUM  
unknown committed
5333

unknown's avatar
unknown committed
5334
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5335 5336


unknown's avatar
unknown committed
5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361
/**************************************************************************
	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)
5362
int Begin_load_query_log_event::get_create_or_append() const
unknown's avatar
unknown committed
5363
{
5364
  return 1; /* create the file */
unknown's avatar
unknown committed
5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375
}
#endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */


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


#ifndef MYSQL_CLIENT
Execute_load_query_log_event::
5376
Execute_load_query_log_event(THD *thd_arg, const char* query_arg,
unknown's avatar
unknown committed
5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418
                     ulong query_length_arg, uint fn_pos_start_arg,
                     uint fn_pos_end_arg,
                     enum_load_dup_handling dup_handling_arg,
                     bool using_trans, bool suppress_use):
  Query_log_event(thd_arg, query_arg, query_length_arg, using_trans,
                  suppress_use),
  file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg),
  fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg)
{
}
#endif /* !MYSQL_CLIENT */


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

  buf+= desc_event->common_header_len;

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

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

  file_id= uint4korr(buf + ELQ_FILE_ID_OFFSET);
}


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


5419
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5420 5421 5422
bool
Execute_load_query_log_event::write_post_header_for_derived(IO_CACHE* file)
{
5423
  uchar buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
unknown's avatar
unknown committed
5424 5425 5426
  int4store(buf, file_id);
  int4store(buf + 4, fn_pos_start);
  int4store(buf + 4 + 4, fn_pos_end);
5427 5428
  *(buf + 4 + 4 + 4)= (uchar) dup_handling;
  return my_b_safe_write(file, buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
unknown's avatar
unknown committed
5429
}
5430
#endif
unknown's avatar
unknown committed
5431 5432 5433


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
5434
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5435
                                         PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
5436
{
unknown's avatar
unknown committed
5437
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
5438 5439 5440
}


unknown's avatar
unknown committed
5441
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5442
                                         PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
5443 5444
                                         const char *local_fname)
{
5445 5446 5447
  Write_on_release_cache cache(&print_event_info->head_cache, file);

  print_query_header(&cache, print_event_info);
unknown's avatar
unknown committed
5448 5449 5450

  if (local_fname)
  {
5451
    my_b_write(&cache, (uchar*) query, fn_pos_start);
5452 5453 5454
    my_b_printf(&cache, " LOCAL INFILE \'");
    my_b_printf(&cache, local_fname);
    my_b_printf(&cache, "\'");
unknown's avatar
unknown committed
5455
    if (dup_handling == LOAD_DUP_REPLACE)
5456 5457
      my_b_printf(&cache, " REPLACE");
    my_b_printf(&cache, " INTO");
5458
    my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end);
unknown's avatar
unknown committed
5459
    my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5460 5461 5462
  }
  else
  {
5463
    my_b_write(&cache, (uchar*) query, q_len);
unknown's avatar
unknown committed
5464
    my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5465 5466
  }

unknown's avatar
unknown committed
5467
  if (!print_event_info->short_form)
5468
    my_b_printf(&cache, "# file_id: %d \n", file_id);
unknown's avatar
unknown committed
5469 5470 5471 5472 5473 5474 5475 5476
}
#endif


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Execute_load_query_log_event::pack_info(Protocol *protocol)
{
  char *buf, *pos;
5477
  if (!(buf= (char*) my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME))))
unknown's avatar
unknown committed
5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498
    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
5499
Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
unknown's avatar
unknown committed
5500 5501 5502 5503 5504 5505 5506
{
  char *p;
  char *buf;
  char *fname;
  char *fname_end;
  int error;

5507 5508
  buf= (char*) my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
                         (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME));
5509

unknown's avatar
unknown committed
5510
  DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", my_free(buf, MYF(0)); buf= NULL;);
5511

unknown's avatar
unknown committed
5512
  /* Replace filename and LOCAL keyword in query before executing it */
5513
  if (buf == NULL)
unknown's avatar
unknown committed
5514
  {
5515 5516
    rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
                ER(ER_SLAVE_FATAL_ERROR), "Not enough memory");
unknown's avatar
unknown committed
5517 5518 5519 5520 5521 5522
    return 1;
  }

  p= buf;
  memcpy(p, query, fn_pos_start);
  p+= fn_pos_start;
5523
  fname= (p= strmake(p, STRING_WITH_LEN(" INFILE \'")));
unknown's avatar
unknown committed
5524 5525
  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
5526
  *(p++)='\'';
unknown's avatar
unknown committed
5527
  switch (dup_handling) {
unknown's avatar
unknown committed
5528
  case LOAD_DUP_IGNORE:
5529
    p= strmake(p, STRING_WITH_LEN(" IGNORE"));
unknown's avatar
unknown committed
5530 5531
    break;
  case LOAD_DUP_REPLACE:
5532
    p= strmake(p, STRING_WITH_LEN(" REPLACE"));
unknown's avatar
unknown committed
5533 5534 5535 5536 5537
    break;
  default:
    /* Ordinary load data */
    break;
  }
5538
  p= strmake(p, STRING_WITH_LEN(" INTO"));
unknown's avatar
unknown committed
5539 5540
  p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);

5541
  error= Query_log_event::do_apply_event(rli, buf, p-buf);
unknown's avatar
unknown committed
5542 5543 5544 5545

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

5546 5547 5548 5549 5550 5551
  /*
    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
5552 5553 5554 5555 5556 5557 5558

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


unknown's avatar
unknown committed
5559
/**************************************************************************
unknown's avatar
unknown committed
5560
	sql_ex_info methods
unknown's avatar
unknown committed
5561
**************************************************************************/
5562

unknown's avatar
unknown committed
5563
/*
5564
  sql_ex_info::write_data()
unknown's avatar
unknown committed
5565
*/
5566

5567
bool sql_ex_info::write_data(IO_CACHE* file)
5568 5569 5570
{
  if (new_format())
  {
5571 5572 5573 5574 5575
    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) ||
5576
	    my_b_safe_write(file,(uchar*) &opt_flags,1));
5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587
  }
  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;
5588
    return my_b_safe_write(file, (uchar*) &old_ex, sizeof(old_ex)) != 0;
5589 5590 5591 5592
  }
}


unknown's avatar
unknown committed
5593
/*
5594
  sql_ex_info::init()
unknown's avatar
unknown committed
5595
*/
5596

5597
char *sql_ex_info::init(char *buf, char *buf_end, bool use_new_format)
5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609
{
  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.
    */
5610 5611 5612 5613 5614 5615
    const char *ptr= buf;
    if (read_str(&ptr, buf_end, (const char **) &field_term, &field_term_len) ||
	read_str(&ptr, buf_end, (const char **) &enclosed,   &enclosed_len) ||
	read_str(&ptr, buf_end, (const char **) &line_term,  &line_term_len) ||
	read_str(&ptr, buf_end, (const char **) &line_start, &line_start_len) ||
	read_str(&ptr, buf_end, (const char **) &escaped,    &escaped_len))
5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641
      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;
}
5642 5643 5644 5645 5646 5647 5648 5649 5650 5651


/**************************************************************************
	Rows_log_event member functions
**************************************************************************/

#ifndef MYSQL_CLIENT
Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
                               MY_BITMAP const *cols, bool is_transactional)
  : Log_event(thd_arg, 0, is_transactional),
5652
    m_row_count(0),
5653 5654
    m_table(tbl_arg),
    m_table_id(tid),
5655
    m_width(tbl_arg ? tbl_arg->s->fields : 1),
5656 5657 5658 5659
    m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0) 
#ifdef HAVE_REPLICATION
    ,m_key(NULL), m_curr_row(NULL), m_curr_row_end(NULL)
#endif
5660
{
5661 5662
  /*
    We allow a special form of dummy event when the table, and cols
5663
    are null and the table id is ~0UL.  This is a temporary
5664
    solution, to be able to terminate a started statement in the
5665
    binary log: the extraneous events will be removed in the future.
5666
   */
5667 5668
  DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
              !tbl_arg && !cols && tid == ~0UL);
5669 5670 5671 5672 5673

  if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
      set_flags(NO_FOREIGN_KEY_CHECKS_F);
  if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
      set_flags(RELAXED_UNIQUE_CHECKS_F);
5674
  /* if bitmap_init fails, caught in is_valid() */
5675 5676
  if (likely(!bitmap_init(&m_cols,
                          m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
5677
                          m_width,
5678
                          false)))
5679 5680 5681
  {
    /* Cols can be zero if this is a dummy binrows event */
    if (likely(cols != NULL))
5682
    {
5683
      memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
5684 5685
      create_last_word_mask(&m_cols);
    }
5686
  }
5687
  else
5688 5689 5690 5691
  {
    // Needed because bitmap_init() does not set it to null on failure
    m_cols.bitmap= 0;
  }
5692 5693 5694 5695 5696 5697 5698 5699
}
#endif

Rows_log_event::Rows_log_event(const char *buf, uint event_len,
                               Log_event_type event_type,
                               const Format_description_log_event
                               *description_event)
  : Log_event(buf, description_event),
5700
    m_row_count(0),
5701
#ifndef MYSQL_CLIENT
5702
    m_table(NULL),
5703 5704 5705 5706 5707
#endif
    m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
    ,m_key(NULL), m_curr_row(NULL), m_curr_row_end(NULL)
#endif
5708 5709 5710 5711 5712
{
  DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
  uint8 const common_header_len= description_event->common_header_len;
  uint8 const post_header_len= description_event->post_header_len[event_type-1];

unknown's avatar
unknown committed
5713 5714
  DBUG_PRINT("enter",("event_len: %u  common_header_len: %d  "
		      "post_header_len: %d",
5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727
		      event_len, common_header_len,
		      post_header_len));

  const char *post_start= buf + common_header_len;
  post_start+= RW_MAPID_OFFSET;
  if (post_header_len == 6)
  {
    /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
    m_table_id= uint4korr(post_start);
    post_start+= 4;
  }
  else
  {
5728
    m_table_id= (ulong) uint6korr(post_start);
5729 5730 5731 5732 5733
    post_start+= RW_FLAGS_OFFSET;
  }

  m_flags= uint2korr(post_start);

5734 5735 5736
  uchar const *const var_start=
    (const uchar *)buf + common_header_len + post_header_len;
  uchar const *const ptr_width= var_start;
5737
  uchar *ptr_after_width= (uchar*) ptr_width;
5738
  DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
5739
  m_width = net_field_length(&ptr_after_width);
5740 5741 5742 5743
  DBUG_PRINT("debug", ("m_width=%lu", m_width));
  /* if bitmap_init fails, catched in is_valid() */
  if (likely(!bitmap_init(&m_cols,
                          m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
5744
                          m_width,
5745 5746 5747 5748
                          false)))
  {
    DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
    memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8);
5749
    create_last_word_mask(&m_cols);
5750
    ptr_after_width+= (m_width + 7) / 8;
5751
    DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
5752 5753 5754 5755 5756 5757 5758
  }
  else
  {
    // Needed because bitmap_init() does not set it to null on failure
    m_cols.bitmap= NULL;
    DBUG_VOID_RETURN;
  }
5759

5760 5761 5762 5763 5764 5765
  m_cols_ai.bitmap= m_cols.bitmap; /* See explanation in is_valid() */

  if (event_type == UPDATE_ROWS_EVENT)
  {
    DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));

5766
    /* if bitmap_init fails, caught in is_valid() */
5767 5768
    if (likely(!bitmap_init(&m_cols_ai,
                            m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
5769
                            m_width,
5770 5771 5772 5773
                            false)))
    {
      DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
      memcpy(m_cols_ai.bitmap, ptr_after_width, (m_width + 7) / 8);
5774
      create_last_word_mask(&m_cols_ai);
5775
      ptr_after_width+= (m_width + 7) / 8;
5776 5777
      DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
                no_bytes_in_map(&m_cols_ai));
5778 5779 5780 5781 5782 5783 5784 5785 5786
    }
    else
    {
      // Needed because bitmap_init() does not set it to null on failure
      m_cols_ai.bitmap= 0;
      DBUG_VOID_RETURN;
    }
  }

5787
  const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
5788

5789
  size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf);
unknown's avatar
unknown committed
5790
  DBUG_PRINT("info",("m_table_id: %lu  m_flags: %d  m_width: %lu  data_size: %lu",
5791
                     m_table_id, m_flags, m_width, (ulong) data_size));
5792

5793
  m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
5794 5795
  if (likely((bool)m_rows_buf))
  {
5796
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
5797
    m_curr_row= m_rows_buf;
5798
#endif
5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813
    m_rows_end= m_rows_buf + data_size;
    m_rows_cur= m_rows_end;
    memcpy(m_rows_buf, ptr_rows_data, data_size);
  }
  else
    m_cols.bitmap= 0; // to not free it

  DBUG_VOID_RETURN;
}

Rows_log_event::~Rows_log_event()
{
  if (m_cols.bitmap == m_bitbuf) // no my_malloc happened
    m_cols.bitmap= 0; // so no my_free in bitmap_free
  bitmap_free(&m_cols); // To pair with bitmap_init().
5814
  my_free((uchar*)m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
5815 5816
}

5817 5818 5819 5820
int Rows_log_event::get_data_size()
{
  int const type_code= get_type_code();

5821 5822
  uchar buf[sizeof(m_width)+1];
  uchar *end= net_store_length(buf, (m_width + 7) / 8);
5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839

  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
                  return 6 + no_bytes_in_map(&m_cols) + (end - buf) +
                  (type_code == UPDATE_ROWS_EVENT ? no_bytes_in_map(&m_cols_ai) : 0) +
                  (m_rows_cur - m_rows_buf););
  int data_size= ROWS_HEADER_LEN;
  data_size+= no_bytes_in_map(&m_cols);
  data_size+= end - buf;

  if (type_code == UPDATE_ROWS_EVENT)
    data_size+= no_bytes_in_map(&m_cols_ai);

  data_size+= (m_rows_cur - m_rows_buf);
  return data_size; 
}


5840
#ifndef MYSQL_CLIENT
5841
int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
5842 5843 5844 5845 5846 5847
{
  /*
    When the table has a primary key, we would probably want, by default, to
    log only the primary key value instead of the entire "before image". This
    would save binlog space. TODO
  */
5848
  DBUG_ENTER("Rows_log_event::do_add_row_data");
unknown's avatar
unknown committed
5849 5850
  DBUG_PRINT("enter", ("row_data: 0x%lx  length: %lu", (ulong) row_data,
                       (ulong) length));
5851 5852 5853 5854 5855
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
5856
  DBUG_DUMP("row_data", row_data, min(length, 32));
5857
#endif
5858 5859

  DBUG_ASSERT(m_rows_buf <= m_rows_cur);
5860
  DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
5861 5862 5863
  DBUG_ASSERT(m_rows_cur <= m_rows_end);

  /* The cast will always work since m_rows_cur <= m_rows_end */
5864
  if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
5865
  {
5866
    size_t const block_size= 1024;
5867
    my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
5868
    my_ptrdiff_t const new_alloc= 
5869
        block_size * ((cur_size + length + block_size - 1) / block_size);
5870

5871
    uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc,
5872
                                           MYF(MY_ALLOW_ZERO_PTR|MY_WME));
5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889
    if (unlikely(!new_buf))
      DBUG_RETURN(HA_ERR_OUT_OF_MEM);

    /* If the memory moved, we need to move the pointers */
    if (new_buf != m_rows_buf)
    {
      m_rows_buf= new_buf;
      m_rows_cur= m_rows_buf + cur_size;
    }

    /*
       The end pointer should always be changed to point to the end of
       the allocated memory.
    */
    m_rows_end= m_rows_buf + new_alloc;
  }

5890
  DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
5891 5892
  memcpy(m_rows_cur, row_data, length);
  m_rows_cur+= length;
5893
  m_row_count++;
5894 5895 5896 5897 5898
  DBUG_RETURN(0);
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
5899
int Rows_log_event::do_apply_event(Relay_log_info const *rli)
5900
{
5901
  DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
5902
  int error= 0;
5903 5904

  /*
5905 5906 5907
    If m_table_id == ~0UL, then we have a dummy event that does not
    contain any data.  In that case, we just remove all tables in the
    tables_to_lock list, close the thread tables, and return with
5908
    success.
5909
   */
5910
  if (m_table_id == ~0UL)
5911 5912 5913 5914 5915 5916 5917
  {
    /*
       This one is supposed to be set: just an extra check so that
       nothing strange has happened.
     */
    DBUG_ASSERT(get_flags(STMT_END_F));

5918
    const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
5919
    close_thread_tables(thd);
5920 5921 5922
    thd->clear_error();
    DBUG_RETURN(0);
  }
5923 5924 5925

  /*
    'thd' has been set by exec_relay_log_event(), just before calling
5926
    do_apply_event(). We still check here to prevent future coding
5927
    errors.
5928 5929 5930 5931
  */
  DBUG_ASSERT(rli->sql_thd == thd);

  /*
5932 5933 5934 5935
    If there is no locks taken, this is the first binrow event seen
    after the table map events.  We should then lock all the tables
    used in the transaction and proceed with execution of the actual
    event.
5936
  */
5937
  if (!thd->lock)
5938
  {
5939 5940
    bool need_reopen= 1; /* To execute the first lap of the loop below */

5941
    /*
5942
      lock_tables() reads the contents of thd->lex, so they must be
5943
      initialized. Contrary to in
5944
      Table_map_log_event::do_apply_event() we don't call
5945
      mysql_init_query() as that may reset the binlog format.
5946
    */
5947
    lex_start(thd);
5948

5949 5950 5951
    while ((error= lock_tables(thd, rli->tables_to_lock,
                               rli->tables_to_lock_count, &need_reopen)))
    {
5952 5953
      if (!need_reopen)
      {
5954 5955 5956 5957 5958 5959 5960
        if (thd->query_error || thd->is_fatal_error)
        {
          /*
            Error reporting borrowed from Query_log_event with many excessive
            simplifications (we don't honour --slave-skip-errors)
          */
          uint actual_error= thd->net.last_errno;
5961 5962 5963 5964 5965
          rli->report(ERROR_LEVEL, actual_error,
                      "Error '%s' in %s event: when locking tables",
                      (actual_error ? thd->net.last_error :
                       "unexpected success or fatal error"),
                      get_type_str());
5966 5967 5968 5969
          thd->is_fatal_error= 1;
        }
        else
        {
5970 5971 5972
          rli->report(ERROR_LEVEL, error,
                      "Error in %s event: when locking tables",
                      get_type_str());
5973
        }
5974
        const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
5975 5976
        DBUG_RETURN(error);
      }
5977

5978
      /*
5979
        So we need to reopen the tables.
5980

5981 5982
        We need to flush the pending RBR event, since it keeps a
        pointer to an open table.
5983

5984 5985 5986
        ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
        the pending RBR event and reset the table pointer after the
        tables has been reopened.
5987

5988 5989
        NOTE: For this new scheme there should be no pending event:
        need to add code to assert that is the case.
5990 5991
       */
      thd->binlog_flush_pending_rows_event(false);
5992 5993
      TABLE_LIST *tables= rli->tables_to_lock;
      close_tables_for_reopen(thd, &tables);
5994

5995 5996
      uint tables_count= rli->tables_to_lock_count;
      if ((error= open_tables(thd, &tables, &tables_count, 0)))
5997 5998 5999 6000 6001 6002 6003 6004
      {
        if (thd->query_error || thd->is_fatal_error)
        {
          /*
            Error reporting borrowed from Query_log_event with many excessive
            simplifications (we don't honour --slave-skip-errors)
          */
          uint actual_error= thd->net.last_errno;
6005 6006 6007 6008
          rli->report(ERROR_LEVEL, actual_error,
                      "Error '%s' on reopening tables",
                      (actual_error ? thd->net.last_error :
                       "unexpected success or fatal error"));
6009 6010
          thd->query_error= 1;
        }
6011
        const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6012
        DBUG_RETURN(error);
6013
      }
6014
    }
6015 6016 6017 6018 6019 6020 6021 6022 6023 6024

    /*
      When the open and locking succeeded, we check all tables to
      ensure that they still have the correct type.

      We can use a down cast here since we know that every table added
      to the tables_to_lock is a RPL_TABLE_LIST.
    */

    {
6025
      RPL_TABLE_LIST *ptr= rli->tables_to_lock;
6026 6027 6028 6029 6030 6031 6032
      for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
      {
        if (ptr->m_tabledef.compatible_with(rli, ptr->table))
        {
          mysql_unlock_tables(thd, thd->lock);
          thd->lock= 0;
          thd->query_error= 1;
6033
          const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6034 6035 6036 6037 6038
          DBUG_RETURN(ERR_BAD_TABLE_DEF);
        }
      }
    }

6039
    /*
6040 6041
      ... and then we add all the tables to the table map and remove
      them from tables to lock.
6042 6043 6044

      We also invalidate the query cache for all the tables, since
      they will now be changed.
6045 6046 6047 6048 6049 6050 6051

      TODO [/Matz]: Maybe the query cache should not be invalidated
      here? It might be that a table is not changed, even though it
      was locked for the statement.  We do know that each
      Rows_log_event contain at least one row, so after processing one
      Rows_log_event, we can invalidate the query cache for the
      associated table.
6052
     */
6053
    for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
6054
    {
6055
      const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
6056
    }
6057 6058 6059
#ifdef HAVE_QUERY_CACHE
    query_cache.invalidate_locked_for_write(rli->tables_to_lock);
#endif
6060 6061
  }

6062 6063
  TABLE* 
    table= 
6064
    m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
6065 6066 6067 6068 6069

  if (table)
  {
    /*
      table == NULL means that this table should not be replicated
6070
      (this was set up by Table_map_log_event::do_apply_event()
6071
      which tested replicate-* rules).
6072
    */
6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099

    /*
      It's not needed to set_time() but
      1) it continues the property that "Time" in SHOW PROCESSLIST shows how
      much slave is behind
      2) it will be needed when we allow replication from a table with no
      TIMESTAMP column to a table with one.
      So we call set_time(), like in SBR. Presently it changes nothing.
    */
    thd->set_time((time_t)when);
    /*
      There are a few flags that are replicated with each row event.
      Make sure to set/clear them before executing the main body of
      the event.
    */
    if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
        thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
    else
        thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;

    if (get_flags(RELAXED_UNIQUE_CHECKS_F))
        thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
    else
        thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
    /* A small test to verify that objects have consistent types */
    DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));

6100 6101 6102 6103 6104 6105 6106 6107 6108
    /*
      Now we are in a statement and will stay in a statement until we
      see a STMT_END_F.

      We set this flag here, before actually applying any rows, in
      case the SQL thread is stopped and we need to detect that we're
      inside a statement and halting abruptly might cause problems
      when restarting.
     */
6109
    const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
6110

6111 6112
     if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
      set_flags(COMPLETE_ROWS_F);
6113

6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129
    /* 
      Set tables write and read sets.
      
      Read_set contains all slave columns (in case we are going to fetch
      a complete record from slave)
      
      Write_set equals the m_cols bitmap sent from master but it can be 
      longer if slave has extra columns. 
     */ 

    DBUG_PRINT_BITSET("debug", "Setting table's write_set from: %s", &m_cols);
    
    bitmap_set_all(table->read_set);
    bitmap_set_all(table->write_set);
    if (!get_flags(COMPLETE_ROWS_F))
      bitmap_intersect(table->write_set,&m_cols);
6130

6131 6132 6133
    // Do event specific preparations 
    
    error= do_before_row_operations(rli);
6134

6135
    // row processing loop
6136

6137 6138
    while (error == 0 && m_curr_row < m_rows_end)
    {
6139 6140 6141 6142
      /* in_use can have been set to NULL in close_tables_for_reopen */
      THD* old_thd= table->in_use;
      if (!table->in_use)
        table->in_use= thd;
6143 6144 6145

      error= do_exec_row(rli);

6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157
      table->in_use = old_thd;
      switch (error)
      {
        /* Some recoverable errors */
      case HA_ERR_RECORD_CHANGED:
      case HA_ERR_KEY_NOT_FOUND:	/* Idempotency support: OK if
                                           tuple does not exist */
	error= 0;
      case 0:
	break;

      default:
6158
	rli->report(ERROR_LEVEL, thd->net.last_errno,
6159 6160 6161
                    "Error in %s event: row application failed. %s",
                    get_type_str(),
                    thd->net.last_error ? thd->net.last_error : "");
6162 6163 6164 6165
	thd->query_error= 1;
	break;
      }

6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184
      /*
       If m_curr_row_end  was not set during event execution (e.g., because
       of errors) we can't proceed to the next row. If the error is transient
       (i.e., error==0 at this point) we must call unpack_current_row() to set 
       m_curr_row_end.
      */ 
   
      if (!m_curr_row_end && !error)
        unpack_current_row(rli);
  
      // at this moment m_curr_row_end should be set
      DBUG_ASSERT(error || m_curr_row_end != NULL); 
      DBUG_ASSERT(error || m_curr_row < m_curr_row_end);
      DBUG_ASSERT(error || m_curr_row_end <= m_rows_end);
  
      m_curr_row= m_curr_row_end;
 
    } // row processing loop

6185
    DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
6186
                    const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
6187
    error= do_after_row_operations(rli, error);
6188
    if (!cache_stmt)
6189 6190 6191 6192
    {
      DBUG_PRINT("info", ("Marked that we need to keep log"));
      thd->options|= OPTION_KEEP_LOG;
    }
6193
  } // if (table)
6194

6195
  /*
6196 6197
    We need to delay this clear until here bacause unpack_current_row() uses
    master-side table definitions stored in rli.
6198 6199
  */
  if (rli->tables_to_lock && get_flags(STMT_END_F))
6200
    const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6201

6202 6203
  if (error)
  {                     /* error has occured during the transaction */
6204
    rli->report(ERROR_LEVEL, thd->net.last_errno,
6205
                "Error in %s event: error during transaction execution "
6206
                "on table %s.%s. %s",
6207
                get_type_str(), table->s->db.str,
6208 6209
                table->s->table_name.str,
                thd->net.last_error ? thd->net.last_error : "");
6210

6211
    /*
6212 6213 6214 6215 6216 6217 6218 6219
      If one day we honour --skip-slave-errors in row-based replication, and
      the error should be skipped, then we would clear mappings, rollback,
      close tables, but the slave SQL thread would not stop and then may
      assume the mapping is still available, the tables are still open...
      So then we should clear mappings/rollback/close here only if this is a
      STMT_END_F.
      For now we code, knowing that error is not skippable and so slave SQL
      thread is certainly going to stop.
unknown's avatar
unknown committed
6220
      rollback at the caller along with sbr.
6221
    */
unknown's avatar
unknown committed
6222
    thd->reset_current_stmt_binlog_row_based();
6223
    const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
6224 6225 6226 6227
    thd->query_error= 1;
    DBUG_RETURN(error);
  }

6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249
  /*
    This code would ideally be placed in do_update_pos() instead, but
    since we have no access to table there, we do the setting of
    last_event_start_time here instead.
  */
  if (table && (table->s->primary_key == MAX_KEY) &&
      !cache_stmt && get_flags(STMT_END_F) == RLE_NO_FLAGS)
  {
    /*
      ------------ Temporary fix until WL#2975 is implemented ---------

      This event is not the last one (no STMT_END_F). If we stop now
      (in case of terminate_slave_thread()), how will we restart? We
      have to restart from Table_map_log_event, but as this table is
      not transactional, the rows already inserted will still be
      present, and idempotency is not guaranteed (no PK) so we risk
      that repeating leads to double insert. So we desperately try to
      continue, hope we'll eventually leave this buggy situation (by
      executing the final Rows_log_event). If we are in a hopeless
      wait (reached end of last relay log and nothing gets appended
      there), we timeout after one minute, and notify DBA about the
      problem.  When WL#2975 is implemented, just remove the member
6250
      Relay_log_info::last_event_start_time and all its occurrences.
6251
    */
6252
    const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
6253 6254 6255 6256 6257
  }

  DBUG_RETURN(0);
}

6258
Log_event::enum_skip_reason
6259
Rows_log_event::do_shall_skip(Relay_log_info *rli)
6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271
{
  /*
    If the slave skip counter is 1 and this event does not end a
    statement, then we should not start executing on the next event.
    Otherwise, we defer the decision to the normal skipping logic.
  */
  if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
    return Log_event::EVENT_SKIP_IGNORE;
  else
    return Log_event::do_shall_skip(rli);
}

6272
int
6273
Rows_log_event::do_update_pos(Relay_log_info *rli)
6274 6275 6276 6277 6278 6279 6280
{
  DBUG_ENTER("Rows_log_event::do_update_pos");
  int error= 0;

  DBUG_PRINT("info", ("flags: %s",
                      get_flags(STMT_END_F) ? "STMT_END_F " : ""));

6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298
  if (get_flags(STMT_END_F))
  {
    /*
      This is the end of a statement or transaction, so close (and
      unlock) the tables we opened when processing the
      Table_map_log_event starting the statement.

      OBSERVER.  This will clear *all* mappings, not only those that
      are open for the table. There is not good handle for on-close
      actions for tables.

      NOTE. Even if we have no table ('table' == 0) we still need to be
      here, so that we increase the group relay log position. If we didn't, we
      could have a group relay log position which lags behind "forever"
      (assume the last master's transaction is ignored by the slave because of
      replicate-ignore rules).
    */
    thd->binlog_flush_pending_rows_event(true);
6299

6300 6301 6302 6303 6304 6305 6306 6307 6308 6309
    /*
      If this event is not in a transaction, the call below will, if some
      transactional storage engines are involved, commit the statement into
      them and flush the pending event to binlog.
      If this event is in a transaction, the call will do nothing, but a
      Xid_log_event will come next which will, if some transactional engines
      are involved, commit the transaction and flush the pending event to the
      binlog.
    */
    error= ha_autocommit_or_rollback(thd, 0);
6310

6311 6312 6313 6314 6315 6316 6317 6318 6319 6320
    /*
      Now what if this is not a transactional engine? we still need to
      flush the pending event to the binlog; we did it with
      thd->binlog_flush_pending_rows_event(). Note that we imitate
      what is done for real queries: a call to
      ha_autocommit_or_rollback() (sometimes only if involves a
      transactional engine), and a call to be sure to have the pending
      event flushed.
    */

unknown's avatar
unknown committed
6321
    thd->reset_current_stmt_binlog_row_based();
6322
    rli->cleanup_context(thd, 0);
6323 6324
    if (error == 0)
    {
6325 6326 6327 6328 6329 6330 6331
      /*
        Indicate that a statement is finished.
        Step the group log position if we are not in a transaction,
        otherwise increase the event log position.
       */
      rli->stmt_done(log_pos, when);

6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342
      /*
        Clear any errors pushed in thd->net.last_err* if for example "no key
        found" (as this is allowed). This is a safety measure; apparently
        those errors (e.g. when executing a Delete_rows_log_event of a
        non-existing row, like in rpl_row_mystery22.test,
        thd->net.last_error = "Can't find record in 't1'" and last_errno=1032)
        do not become visible. We still prefer to wipe them out.
      */
      thd->clear_error();
    }
    else
6343 6344 6345
      rli->report(ERROR_LEVEL, error,
                  "Error in %s event: commit of row events failed, "
                  "table `%s`.`%s`",
6346 6347
                  get_type_str(), m_table->s->db.str,
                  m_table->s->table_name.str);
6348
  }
6349
  else
6350
  {
6351
    rli->inc_event_relay_log_pos();
6352 6353
  }

6354
  DBUG_RETURN(error);
6355
}
6356

6357 6358 6359 6360 6361
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
{
6362
  uchar buf[ROWS_HEADER_LEN];	// No need to init the buffer
6363
  DBUG_ASSERT(m_table_id != ~0UL);
6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380
  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
                  {
                    int4store(buf + 0, m_table_id);
                    int2store(buf + 4, m_flags);
                    return (my_b_safe_write(file, buf, 6));
                  });
  int6store(buf + RW_MAPID_OFFSET, (ulonglong)m_table_id);
  int2store(buf + RW_FLAGS_OFFSET, m_flags);
  return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
}

bool Rows_log_event::write_data_body(IO_CACHE*file)
{
  /*
     Note that this should be the number of *bits*, not the number of
     bytes.
  */
6381
  uchar sbuf[sizeof(m_width)];
6382
  my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
6383
  bool res= false;
6384 6385
  uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
  DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
6386

6387 6388
  DBUG_DUMP("m_width", sbuf, (size_t) (sbuf_end - sbuf));
  res= res || my_b_safe_write(file, sbuf, (size_t) (sbuf_end - sbuf));
6389

6390 6391
  DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
  res= res || my_b_safe_write(file, (uchar*) m_cols.bitmap,
6392 6393 6394 6395 6396 6397
                              no_bytes_in_map(&m_cols));
  /*
    TODO[refactor write]: Remove the "down cast" here (and elsewhere).
   */
  if (get_type_code() == UPDATE_ROWS_EVENT)
  {
6398 6399 6400
    DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
              no_bytes_in_map(&m_cols_ai));
    res= res || my_b_safe_write(file, (uchar*) m_cols_ai.bitmap,
6401 6402
                                no_bytes_in_map(&m_cols_ai));
  }
6403 6404
  DBUG_DUMP("rows", m_rows_buf, data_size);
  res= res || my_b_safe_write(file, m_rows_buf, (size_t) data_size);
6405 6406 6407

  return res;

6408 6409 6410
}
#endif

6411
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
6412 6413
void Rows_log_event::pack_info(Protocol *protocol)
{
6414 6415 6416
  char buf[256];
  char const *const flagstr=
    get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
6417
  size_t bytes= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
6418
                               "table_id: %lu%s", m_table_id, flagstr);
6419
  protocol->store(buf, bytes, &my_charset_bin);
6420 6421 6422
}
#endif

6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433
#ifdef MYSQL_CLIENT
void Rows_log_event::print_helper(FILE *file,
                                  PRINT_EVENT_INFO *print_event_info,
                                  char const *const name)
{
  IO_CACHE *const head= &print_event_info->head_cache;
  IO_CACHE *const body= &print_event_info->body_cache;
  if (!print_event_info->short_form)
  {
    bool const last_stmt_event= get_flags(STMT_END_F);
    print_header(head, print_event_info, !last_stmt_event);
6434 6435 6436
    my_b_printf(head, "\t%s: table id %lu%s\n",
                name, m_table_id,
                last_stmt_event ? " flags: STMT_END_F" : "");
6437 6438 6439 6440 6441
    print_base64(body, print_event_info, !last_stmt_event);
  }

  if (get_flags(STMT_END_F))
  {
6442 6443
    copy_event_cache_to_file_and_reinit(head, file);
    copy_event_cache_to_file_and_reinit(body, file);
6444 6445 6446 6447
  }
}
#endif

6448
/**************************************************************************
6449
	Table_map_log_event member functions and support functions
6450 6451
**************************************************************************/

6452 6453 6454 6455
/**
  @page How replication of field metadata works.
  
  When a table map is created, the master first calls 
6456
  Table_map_log_event::save_field_metadata() which calculates how many 
6457
  values will be in the field metadata. Only those fields that require the 
6458 6459 6460 6461 6462 6463 6464
  extra data are added. The method also loops through all of the fields in 
  the table calling the method Field::save_field_metadata() which returns the
  values for the field that will be saved in the metadata and replicated to
  the slave. Once all fields have been processed, the table map is written to
  the binlog adding the size of the field metadata and the field metadata to
  the end of the body of the table map.

6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502
  When a table map is read on the slave, the field metadata is read from the 
  table map and passed to the table_def class constructor which saves the 
  field metadata from the table map into an array based on the type of the 
  field. Field metadata values not present (those fields that do not use extra 
  data) in the table map are initialized as zero (0). The array size is the 
  same as the columns for the table on the slave.

*/

/**
  Save the field metadata based on the real_type of the field.
  The metadata saved depends on the type of the field. Some fields
  store a single byte for pack_length() while others store two bytes
  for field_length (max length).
  
  @retval 0 Ok.

  TODO: We may want to consider changing the encoding of the information.
  Currently, the code attempts to minimize the number of bytes written to 
  the tablemap. There are at least two other alternatives; 1) using 
  net_store_length() to store the data allowing it to choose the number of
  bytes that are appropriate thereby making the code much easier to 
  maintain (only 1 place to change the encoding), or 2) use a fixed number
  of bytes for each field. The problem with option 1 is that net_store_length()
  will use one byte if the value < 251, but 3 bytes if it is > 250. Thus,
  for fields like CHAR which can be no larger than 255 characters, the method
  will use 3 bytes when the value is > 250. Further, every value that is
  encoded using 2 parts (e.g., pack_length, field_length) will be numerically
  > 250 therefore will use 3 bytes for eah value. The problem with option 2
  is less wasteful for space but does waste 1 byte for every field that does
  not encode 2 parts. 
*/
#if !defined(MYSQL_CLIENT)
int Table_map_log_event::save_field_metadata()
{
  DBUG_ENTER("Table_map_log_event::save_field_metadata");
  int index= 0;
  for (unsigned int i= 0 ; i < m_table->s->fields ; i++)
6503 6504
    index+= m_table->s->field[i]->save_field_metadata(&m_field_metadata[index]);
  DBUG_RETURN(index);
6505 6506 6507
}
#endif /* !defined(MYSQL_CLIENT) */

6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521
/*
  Constructor used to build an event for writing to the binary log.
  Mats says tbl->s lives longer than this event so it's ok to copy pointers
  (tbl->s->db etc) and not pointer content.
 */
#if !defined(MYSQL_CLIENT)
Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
                                         bool is_transactional, uint16 flags)
  : Log_event(thd, 0, is_transactional),
    m_table(tbl),
    m_dbnam(tbl->s->db.str),
    m_dblen(m_dbnam ? tbl->s->db.length : 0),
    m_tblnam(tbl->s->table_name.str),
    m_tbllen(tbl->s->table_name.length),
6522
    m_colcnt(tbl->s->fields), m_field_metadata(0),
6523
    m_field_metadata_size(0), m_memory(NULL), m_meta_memory(NULL), m_data_size(0),
6524
    m_table_id(tid), m_null_bits(0), m_flags(flags)
6525
{
6526
  DBUG_ASSERT(m_table_id != ~0UL);
6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538
  /*
    In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
    table.cc / alloc_table_share():
      Use the fact the key is db/0/table_name/0
    As we rely on this let's assert it.
  */
  DBUG_ASSERT((tbl->s->db.str == 0) ||
              (tbl->s->db.str[tbl->s->db.length] == 0));
  DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);


  m_data_size=  TABLE_MAP_HEADER_LEN;
unknown's avatar
unknown committed
6539
  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;);
6540 6541 6542 6543
  m_data_size+= m_dblen + 2;	// Include length and terminating \0
  m_data_size+= m_tbllen + 2;	// Include length and terminating \0
  m_data_size+= 1 + m_colcnt;	// COLCNT and column types

6544
  /* If malloc fails, caught in is_valid() */
6545
  if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
6546
  {
unknown's avatar
unknown committed
6547
    m_coltype= reinterpret_cast<uchar*>(m_memory);
6548 6549 6550
    for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
      m_coltype[i]= m_table->field[i]->type();
  }
6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561

  /*
    Calculate a bitmap for the results of maybe_null() for all columns.
    The bitmap is used to determine when there is a column from the master
    that is not on the slave and is null and thus not in the row data during
    replication.
  */
  uint num_null_bytes= (m_table->s->fields + 7) / 8;
  m_data_size+= num_null_bytes;
  m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
                                 &m_null_bits, num_null_bytes,
6562
                                 &m_field_metadata, (m_colcnt * 2),
6563
                                 NULL);
6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581

  bzero(m_field_metadata, (m_colcnt * 2));

  /*
    Create an array for the field metadata and store it.
  */
  m_field_metadata_size= save_field_metadata();
  DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));

  /*
    Now set the size of the data to the size of the field metadata array
    plus one or two bytes for number of elements in the field metadata array.
  */
  if (m_field_metadata_size > 255)
    m_data_size+= m_field_metadata_size + 2; 
  else
    m_data_size+= m_field_metadata_size + 1; 

6582 6583 6584 6585 6586
  bzero(m_null_bits, num_null_bytes);
  for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
    if (m_table->field[i]->maybe_null())
      m_null_bits[(i / 8)]+= 1 << (i % 8);

6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599
}
#endif /* !defined(MYSQL_CLIENT) */

/*
  Constructor used by slave to read the event from the binary log.
 */
#if defined(HAVE_REPLICATION)
Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
                                         const Format_description_log_event
                                         *description_event)

  : Log_event(buf, description_event),
#ifndef MYSQL_CLIENT
6600
    m_table(NULL),
6601
#endif
6602 6603 6604 6605 6606
    m_dbnam(NULL), m_dblen(0), m_tblnam(NULL), m_tbllen(0),
    m_colcnt(0), m_coltype(0),
    m_memory(NULL), m_table_id(ULONG_MAX), m_flags(0),
    m_data_size(0), m_field_metadata(0), m_field_metadata_size(0),
    m_null_bits(0), m_meta_memory(NULL)
6607
{
6608
  unsigned int bytes_read= 0;
6609 6610 6611 6612
  DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");

  uint8 common_header_len= description_event->common_header_len;
  uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1];
unknown's avatar
unknown committed
6613
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
6614 6615
                     event_len, common_header_len, post_header_len));

6616 6617 6618 6619 6620
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
6621
  DBUG_DUMP("event buffer", (uchar*) buf, event_len);
6622
#endif
6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636

  /* Read the post-header */
  const char *post_start= buf + common_header_len;

  post_start+= TM_MAPID_OFFSET;
  if (post_header_len == 6)
  {
    /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
    m_table_id= uint4korr(post_start);
    post_start+= 4;
  }
  else
  {
    DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN);
6637
    m_table_id= (ulong) uint6korr(post_start);
6638 6639 6640
    post_start+= TM_FLAGS_OFFSET;
  }

6641
  DBUG_ASSERT(m_table_id != ~0UL);
6642 6643 6644 6645 6646 6647 6648

  m_flags= uint2korr(post_start);

  /* Read the variable part of the event */
  const char *const vpart= buf + common_header_len + post_header_len;

  /* Extract the length of the various parts from the buffer */
6649
  uchar const *const ptr_dblen= (uchar const*)vpart + 0;
unknown's avatar
unknown committed
6650
  m_dblen= *(uchar*) ptr_dblen;
6651 6652

  /* Length of database name + counter + terminating null */
6653
  uchar const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
unknown's avatar
unknown committed
6654
  m_tbllen= *(uchar*) ptr_tbllen;
6655 6656

  /* Length of table name + counter + terminating null */
6657
  uchar const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
6658 6659
  uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
  m_colcnt= net_field_length(&ptr_after_colcnt);
6660

unknown's avatar
unknown committed
6661
  DBUG_PRINT("info",("m_dblen: %lu  off: %ld  m_tbllen: %lu  off: %ld  m_colcnt: %lu  off: %ld",
6662 6663
                     (ulong) m_dblen, (long) (ptr_dblen-(const uchar*)vpart), 
                     (ulong) m_tbllen, (long) (ptr_tbllen-(const uchar*)vpart),
6664
                     m_colcnt, (long) (ptr_colcnt-(const uchar*)vpart)));
6665

6666
  /* Allocate mem for all fields in one go. If fails, caught in is_valid() */
6667 6668 6669 6670 6671
  m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
                                     &m_dbnam, (uint) m_dblen + 1,
                                     &m_tblnam, (uint) m_tbllen + 1,
                                     &m_coltype, (uint) m_colcnt,
                                     NullS);
6672 6673 6674 6675

  if (m_memory)
  {
    /* Copy the different parts into their memory */
6676 6677
    strncpy(const_cast<char*>(m_dbnam), (const char*)ptr_dblen  + 1, m_dblen + 1);
    strncpy(const_cast<char*>(m_tblnam), (const char*)ptr_tbllen + 1, m_tbllen + 1);
6678
    memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
6679 6680 6681 6682 6683 6684 6685

    ptr_after_colcnt= ptr_after_colcnt + m_colcnt;
    bytes_read= ptr_after_colcnt - (uchar *)buf;
    DBUG_PRINT("info", ("Bytes read: %d.\n", bytes_read));
    if (bytes_read < event_len)
    {
      m_field_metadata_size= net_field_length(&ptr_after_colcnt);
6686
      DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
6687 6688 6689 6690 6691 6692 6693 6694 6695
      uint num_null_bytes= (m_colcnt + 7) / 8;
      m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
                                     &m_null_bits, num_null_bytes,
                                     &m_field_metadata, m_field_metadata_size,
                                     NULL);
      memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
      ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
      memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
    }
6696 6697 6698 6699 6700 6701 6702 6703
  }

  DBUG_VOID_RETURN;
}
#endif

Table_map_log_event::~Table_map_log_event()
{
6704
  my_free(m_meta_memory, MYF(MY_ALLOW_ZERO_PTR));
6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719
  my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
}

/*
  Return value is an error code, one of:

      -1     Failure to open table   [from open_tables()]
       0     Success
       1     No room for more tables [from set_table()]
       2     Out of memory           [from set_table()]
       3     Wrong table definition
       4     Daisy-chaining RBR with SBR not possible
 */

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
6720
int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
6721
{
6722 6723 6724 6725
  RPL_TABLE_LIST *table_list;
  char *db_mem, *tname_mem;
  size_t dummy_len;
  void *memory;
6726
  DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
6727 6728 6729 6730 6731 6732 6733
  DBUG_ASSERT(rli->sql_thd == thd);

  /* Step the query id to mark what columns that are actually used. */
  pthread_mutex_lock(&LOCK_thread_count);
  thd->query_id= next_query_id();
  pthread_mutex_unlock(&LOCK_thread_count);

6734 6735 6736 6737 6738
  if (!(memory= my_multi_malloc(MYF(MY_WME),
                                &table_list, (uint) sizeof(RPL_TABLE_LIST),
                                &db_mem, (uint) NAME_LEN + 1,
                                &tname_mem, (uint) NAME_LEN + 1,
                                NullS)))
6739
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
6740 6741 6742 6743 6744 6745 6746 6747 6748 6749

  bzero(table_list, sizeof(*table_list));
  table_list->db = db_mem;
  table_list->alias= table_list->table_name = tname_mem;
  table_list->lock_type= TL_WRITE;
  table_list->next_global= table_list->next_local= 0;
  table_list->table_id= m_table_id;
  table_list->updating= 1;
  strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
  strmov(table_list->table_name, m_tblnam);
6750 6751 6752

  int error= 0;

6753 6754 6755
  if (!rpl_filter->db_ok(table_list->db) ||
      (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list)))
  {
6756
    my_free(memory, MYF(MY_WME));
6757 6758
  }
  else
6759
  {
6760 6761 6762 6763 6764
    /*
      open_tables() reads the contents of thd->lex, so they must be
      initialized, so we should call lex_start(); to be even safer, we
      call mysql_init_query() which does a more complete set of inits.
    */
6765 6766
    lex_start(thd);
    mysql_reset_thd_for_next_command(thd);
6767
    /*
unknown's avatar
unknown committed
6768 6769 6770
      Check if the slave is set to use SBR.  If so, it should switch
      to using RBR until the end of the "statement", i.e., next
      STMT_END_F or next error.
6771
    */
unknown's avatar
unknown committed
6772 6773
    if (!thd->current_stmt_binlog_row_based &&
        mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
6774
    {
unknown's avatar
unknown committed
6775
      thd->set_current_stmt_binlog_row_based();
6776 6777 6778
    }

    /*
6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794
      Open the table if it is not already open and add the table to
      table map.  Note that for any table that should not be
      replicated, a filter is needed.

      The creation of a new TABLE_LIST is used to up-cast the
      table_list consisting of RPL_TABLE_LIST items. This will work
      since the only case where the argument to open_tables() is
      changed, is when thd->lex->query_tables == table_list, i.e.,
      when the statement requires prelocking. Since this is not
      executed when a statement is executed, this case will not occur.
      As a precaution, an assertion is added to ensure that the bad
      case is not a fact.

      Either way, the memory in the list is *never* released
      internally in the open_tables() function, hence we take a copy
      of the pointer to make sure that it's not lost.
6795 6796
    */
    uint count;
6797 6798 6799
    DBUG_ASSERT(thd->lex->query_tables != table_list);
    TABLE_LIST *tmp_table_list= table_list;
    if ((error= open_tables(thd, &tmp_table_list, &count, 0)))
6800
    {
6801
      if (thd->query_error || thd->is_fatal_error)
6802
      {
6803 6804 6805 6806 6807
        /*
          Error reporting borrowed from Query_log_event with many excessive
          simplifications (we don't honour --slave-skip-errors)
        */
        uint actual_error= thd->net.last_errno;
6808 6809 6810 6811 6812
        rli->report(ERROR_LEVEL, actual_error,
                    "Error '%s' on opening table `%s`.`%s`",
                    (actual_error ? thd->net.last_error :
                     "unexpected success or fatal error"),
                    table_list->db, table_list->table_name);
6813
        thd->query_error= 1;
6814
      }
6815
      goto err;
6816 6817
    }

6818
    m_table= table_list->table;
6819 6820 6821 6822 6823 6824 6825

    /*
      This will fail later otherwise, the 'in_use' field should be
      set to the current thread.
    */
    DBUG_ASSERT(m_table->in_use);

6826 6827 6828
    /*
      Use placement new to construct the table_def instance in the
      memory allocated for it inside table_list.
6829 6830 6831

      The memory allocated by the table_def structure (i.e., not the
      memory allocated *for* the table_def structure) is released
6832
      inside Relay_log_info::clear_tables_to_lock() by calling the
6833
      table_def destructor explicitly.
6834
    */
6835 6836
    new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt, 
         m_field_metadata, m_field_metadata_size, m_null_bits);
6837
    table_list->m_tabledef_valid= TRUE;
6838 6839

    /*
6840
      We record in the slave's information that the table should be
unknown's avatar
unknown committed
6841
      locked by linking the table into the list of tables to lock.
6842
    */
6843
    table_list->next_global= table_list->next_local= rli->tables_to_lock;
6844 6845
    const_cast<Relay_log_info*>(rli)->tables_to_lock= table_list;
    const_cast<Relay_log_info*>(rli)->tables_to_lock_count++;
6846
    /* 'memory' is freed in clear_tables_to_lock */
6847 6848
  }

6849
  DBUG_RETURN(error);
6850

6851
err:
6852
  my_free(memory, MYF(MY_WME));
6853 6854
  DBUG_RETURN(error);
}
6855

6856
Log_event::enum_skip_reason
6857
Table_map_log_event::do_shall_skip(Relay_log_info *rli)
6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868
{
  /*
    If the slave skip counter is 1, then we should not start executing
    on the next event.
  */
  if (rli->slave_skip_counter == 1)
    return Log_event::EVENT_SKIP_IGNORE;
  else
    return Log_event::do_shall_skip(rli);
}

6869
int Table_map_log_event::do_update_pos(Relay_log_info *rli)
6870 6871 6872 6873 6874
{
  rli->inc_event_relay_log_pos();
  return 0;
}

6875 6876 6877 6878 6879
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifndef MYSQL_CLIENT
bool Table_map_log_event::write_data_header(IO_CACHE *file)
{
6880
  DBUG_ASSERT(m_table_id != ~0UL);
6881
  uchar buf[TABLE_MAP_HEADER_LEN];
6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900
  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
                  {
                    int4store(buf + 0, m_table_id);
                    int2store(buf + 4, m_flags);
                    return (my_b_safe_write(file, buf, 6));
                  });
  int6store(buf + TM_MAPID_OFFSET, (ulonglong)m_table_id);
  int2store(buf + TM_FLAGS_OFFSET, m_flags);
  return (my_b_safe_write(file, buf, TABLE_MAP_HEADER_LEN));
}

bool Table_map_log_event::write_data_body(IO_CACHE *file)
{
  DBUG_ASSERT(m_dbnam != NULL);
  DBUG_ASSERT(m_tblnam != NULL);
  /* We use only one byte per length for storage in event: */
  DBUG_ASSERT(m_dblen < 128);
  DBUG_ASSERT(m_tbllen < 128);

6901 6902
  uchar const dbuf[]= { (uchar) m_dblen };
  uchar const tbuf[]= { (uchar) m_tbllen };
6903

6904 6905 6906
  uchar cbuf[sizeof(m_colcnt)];
  uchar *const cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
  DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
6907

6908 6909 6910 6911
  /*
    Store the size of the field metadata.
  */
  uchar mbuf[sizeof(m_field_metadata_size)];
6912
  uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
6913

6914
  return (my_b_safe_write(file, dbuf,      sizeof(dbuf)) ||
6915
          my_b_safe_write(file, (const uchar*)m_dbnam,   m_dblen+1) ||
6916
          my_b_safe_write(file, tbuf,      sizeof(tbuf)) ||
6917 6918
          my_b_safe_write(file, (const uchar*)m_tblnam,  m_tbllen+1) ||
          my_b_safe_write(file, cbuf, (size_t) (cbuf_end - cbuf)) ||
6919 6920 6921 6922
          my_b_safe_write(file, m_coltype, m_colcnt) ||
          my_b_safe_write(file, mbuf, (size_t) (mbuf_end - mbuf)) ||
          my_b_safe_write(file, m_field_metadata, m_field_metadata_size),
          my_b_safe_write(file, m_null_bits, (m_colcnt + 7) / 8));
6923 6924 6925 6926 6927 6928 6929 6930 6931 6932
 }
#endif

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)

/*
  Print some useful information for the SHOW BINARY LOG information
  field.
 */

6933
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
6934 6935 6936
void Table_map_log_event::pack_info(Protocol *protocol)
{
    char buf[256];
6937
    size_t bytes= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
6938
                                 "table_id: %lu (%s.%s)",
6939
                              m_table_id, m_dbnam, m_tblnam);
6940 6941
    protocol->store(buf, bytes, &my_charset_bin);
}
6942 6943
#endif

6944 6945 6946 6947 6948 6949 6950 6951 6952

#endif


#ifdef MYSQL_CLIENT
void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
{
  if (!print_event_info->short_form)
  {
6953 6954 6955 6956 6957
    print_header(&print_event_info->head_cache, print_event_info, TRUE);
    my_b_printf(&print_event_info->head_cache,
                "\tTable_map: `%s`.`%s` mapped to number %lu\n",
                m_dbnam, m_tblnam, m_table_id);
    print_base64(&print_event_info->body_cache, print_event_info, TRUE);
6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991
  }
}
#endif

/**************************************************************************
	Write_rows_log_event member functions
**************************************************************************/

/*
  Constructor used to build an event for writing to the binary log.
 */
#if !defined(MYSQL_CLIENT)
Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
                                           ulong tid_arg,
                                           MY_BITMAP const *cols,
                                           bool is_transactional)
  : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
{
}
#endif

/*
  Constructor used by slave to read the event from the binary log.
 */
#ifdef HAVE_REPLICATION
Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
                                           const Format_description_log_event
                                           *description_event)
: Rows_log_event(buf, event_len, WRITE_ROWS_EVENT, description_event)
{
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
6992 6993
int 
Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011
{
  int error= 0;

  /*
    We are using REPLACE semantics and not INSERT IGNORE semantics
    when writing rows, that is: new rows replace old rows.  We need to
    inform the storage engine that it should use this behaviour.
  */

  /* Tell the storage engine that we are using REPLACE semantics. */
  thd->lex->duplicates= DUP_REPLACE;

  /*
    Pretend we're executing a REPLACE command: this is needed for
    InnoDB and NDB Cluster since they are not (properly) checking the
    lex->duplicates flag.
  */
  thd->lex->sql_command= SQLCOM_REPLACE;
7012 7013 7014
  /* 
     Do not raise the error flag in case of hitting to an unique attribute
  */
7015
  m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
7016 7017 7018 7019 7020 7021
  /* 
     NDB specific: update from ndb master wrapped as Write_rows
  */
  /*
    so that the event should be applied to replace slave's row
  */
7022
  m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
7023 7024 7025 7026 7027
  /* 
     NDB specific: if update from ndb master wrapped as Write_rows
     does not find the row it's assumed idempotent binlog applying
     is taking place; don't raise the error.
  */
7028
  m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
7029 7030 7031 7032 7033
  /*
    TODO: the cluster team (Tomas?) says that it's better if the engine knows
    how many rows are going to be inserted, then it can allocate needed memory
    from the start.
  */
7034
  m_table->file->ha_start_bulk_insert(0);
7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049
  /*
    We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
    any TIMESTAMP column with data from the row but instead will use
    the event's current time.
    As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
    columns, we know that all TIMESTAMP columns on slave will receive explicit
    data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
    When we allow a table without TIMESTAMP to be replicated to a table having
    more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
    column to be replicated into a BIGINT column and the slave's table has a
    TIMESTAMP column, then the slave's TIMESTAMP column will take its value
    from set_time() which we called earlier (consistent with SBR). And then in
    some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
    analyze if explicit data is provided for slave's TIMESTAMP columns).
  */
7050
  m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7051 7052 7053
  return error;
}

7054 7055 7056
int 
Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, 
                                              int error)
7057
{
7058
  int local_error= 0;
7059 7060
  m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
  m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
7061 7062 7063 7064 7065 7066
  /*
    reseting the extra with 
    table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY); 
    fires bug#27077
    todo: explain or fix
  */
7067
  if ((local_error= m_table->file->ha_end_bulk_insert()))
7068
  {
7069
    m_table->file->print_error(local_error, MYF(0));
7070 7071
  }
  return error? error : local_error;
7072 7073
}

7074
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087

/*
  Check if there are more UNIQUE keys after the given key.
*/
static int
last_uniq_key(TABLE *table, uint keyno)
{
  while (++keyno < table->s->keys)
    if (table->key_info[keyno].flags & HA_NOSAME)
      return 0;
  return 1;
}

7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112
/**
   Check if an error is a duplicate key error.

   This function is used to check if an error code is one of the
   duplicate key error, i.e., and error code for which it is sensible
   to do a <code>get_dup_key()</code> to retrieve the duplicate key.

   @param errcode The error code to check.

   @return <code>true</code> if the error code is such that
   <code>get_dup_key()</code> will return true, <code>false</code>
   otherwise.
 */
bool
is_duplicate_key_error(int errcode)
{
  switch (errcode)
  {
  case HA_ERR_FOUND_DUPP_KEY:
  case HA_ERR_FOUND_DUPP_UNIQUE:
    return true;
  }
  return false;
}

7113 7114
/**
  Write the current row into event's table.
7115

7116 7117 7118 7119 7120
  The row is located in the row buffer, pointed by @c m_curr_row member.
  Number of columns of the row is stored in @c m_width member (it can be 
  different from the number of columns in the table to which we insert). 
  Bitmap @c m_cols indicates which columns are present in the row. It is assumed 
  that event's table is already open and pointed by @c m_table.
7121

7122 7123 7124 7125 7126
  If the same record already exists in the table it can be either overwritten 
  or an error is reported depending on the value of @c overwrite flag 
  (error reporting not yet implemented). Note that the matching record can be
  different from the row we insert if we use primary keys to identify records in
  the table.
7127

7128 7129 7130 7131
  The row to be inserted can contain values only for selected columns. The 
  missing columns are filled with default values using @c prepare_record() 
  function. If a matching record is found in the table and @c overwritte is
  true, the missing columns are taken from it.
7132

7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148
  @param  rli   Relay log info (needed for row unpacking).
  @param  overwrite  
                Shall we overwrite if the row already exists or signal 
                error (currently ignored).

  @returns Error code on failure, 0 on success.

  This method, if successful, sets @c m_curr_row_end pointer to point at the
  next row in the rows buffer. This is done when unpacking the row to be 
  inserted.

  @note If a matching record is found, it is either updated using 
  @c ha_update_row() or first deleted and then new record written.
*/ 

int
7149
Rows_log_event::write_row(const Relay_log_info *const rli,
7150
                          const bool overwrite)
7151
{
7152 7153
  DBUG_ENTER("write_row");
  DBUG_ASSERT(m_table != NULL && thd != NULL);
7154

7155
  TABLE *table= m_table;  // pointer to event's table
7156 7157 7158 7159
  int error;
  int keynum;
  auto_afree_ptr<char> key(NULL);

7160 7161 7162 7163 7164 7165 7166 7167 7168
  /* fill table->record[0] with default values */

  if ((error= prepare_record(rli, table, m_width,
                             TRUE /* check if columns have def. values */)))
    DBUG_RETURN(error);
  
  /* unpack row into table->record[0] */
  error= unpack_current_row(rli); // TODO: how to handle errors?

7169
#ifndef DBUG_OFF
7170
  DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
7171 7172 7173 7174
  DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
  DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif

7175 7176 7177 7178 7179 7180 7181 7182
  /* 
    Try to write record. If a corresponding record already exists in the table,
    we try to change it using ha_update_row() if possible. Otherwise we delete
    it and repeat the whole process again. 

    TODO: Add safety measures against infinite looping. 
   */

7183 7184
  while ((error= table->file->ha_write_row(table->record[0])))
  {
unknown's avatar
unknown committed
7185 7186 7187 7188 7189
    if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
    {
      table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */
      DBUG_RETURN(error);
    }
7190 7191
    if ((keynum= table->file->get_dup_key(error)) < 0)
    {
7192
      DBUG_PRINT("info",("Can't locate duplicate key (get_dup_key returns %d)",keynum));
7193 7194 7195 7196 7197 7198
      table->file->print_error(error, MYF(0));
      /*
        We failed to retrieve the duplicate key
        - either because the error was not "duplicate key" error
        - or because the information which key is not available
      */
7199
      DBUG_RETURN(error);
7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211
    }

    /*
       We need to retrieve the old row into record[1] to be able to
       either update or delete the offending record.  We either:

       - use rnd_pos() with a row-id (available as dupp_row) to the
         offending row, if that is possible (MyISAM and Blackhole), or else

       - use index_read_idx() with the key that is duplicated, to
         retrieve the offending row.
     */
7212
    if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
7213
    {
7214
      DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
7215
      error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
7216
      if (error)
7217
      {
7218
        DBUG_PRINT("info",("rnd_pos() returns error %d",error));
7219
        table->file->print_error(error, MYF(0));
7220
        DBUG_RETURN(error);
7221
      }
7222 7223 7224
    }
    else
    {
7225 7226
      DBUG_PRINT("info",("Locating offending record using index_read_idx()"));

7227 7228
      if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
      {
7229
        DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
7230
        DBUG_RETURN(my_errno);
7231 7232 7233 7234 7235 7236
      }

      if (key.get() == NULL)
      {
        key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
        if (key.get() == NULL)
7237 7238
        {
          DBUG_PRINT("info",("Can't allocate key buffer"));
7239
          DBUG_RETURN(ENOMEM);
7240
        }
7241 7242
      }

7243 7244
      key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
               0);
7245 7246 7247 7248
      error= table->file->index_read_idx_map(table->record[1], keynum,
                                             (const uchar*)key.get(),
                                             HA_WHOLE_KEY,
                                             HA_READ_KEY_EXACT);
7249
      if (error)
7250
      {
7251
        DBUG_PRINT("info",("index_read_idx() returns error %d",error)); 
7252
        table->file->print_error(error, MYF(0));
7253
        DBUG_RETURN(error);
7254
      }
7255 7256 7257
    }

    /*
7258
       Now, record[1] should contain the offending row.  That
7259 7260
       will enable us to update it or, alternatively, delete it (so
       that we can insert the new row afterwards).
7261
     */
7262

7263 7264 7265
    /*
      If row is incomplete we will use the record found to fill 
      missing columns.  
7266
    */
7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277
    if (!get_flags(COMPLETE_ROWS_F))
    {
      restore_record(table,record[1]);
      error= unpack_current_row(rli);
    }

#ifndef DBUG_OFF
    DBUG_PRINT("debug",("preparing for update: before and after image"));
    DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
    DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
#endif
7278

7279
    /*
7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296
       REPLACE is defined as either INSERT or DELETE + INSERT.  If
       possible, we can replace it with an UPDATE, but that will not
       work on InnoDB if FOREIGN KEY checks are necessary.

       I (Matz) am not sure of the reason for the last_uniq_key()
       check as, but I'm guessing that it's something along the
       following lines.

       Suppose that we got the duplicate key to be a key that is not
       the last unique key for the table and we perform an update:
       then there might be another key for which the unique check will
       fail, so we're better off just deleting the row and inserting
       the correct row.
     */
    if (last_uniq_key(table, keynum) &&
        !table->file->referenced_by_foreign_key())
    {
7297
      DBUG_PRINT("info",("Updating row using ha_update_row()"));
7298 7299
      error=table->file->ha_update_row(table->record[1],
                                       table->record[0]);
7300 7301 7302 7303 7304
      switch (error) {
                
      case HA_ERR_RECORD_IS_THE_SAME:
        DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
                           " ha_update_row()"));
7305
        error= 0;
7306 7307 7308 7309 7310 7311 7312 7313 7314
      
      case 0:
        break;
        
      default:    
        DBUG_PRINT("info",("ha_update_row() returns error %d",error));
        table->file->print_error(error, MYF(0));
      }
      
7315
      DBUG_RETURN(error);
7316 7317 7318
    }
    else
    {
7319
      DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
7320
      if ((error= table->file->ha_delete_row(table->record[1])))
7321
      {
7322
        DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
7323
        table->file->print_error(error, MYF(0));
7324
        DBUG_RETURN(error);
7325
      }
7326 7327 7328
      /* Will retry ha_write_row() with the offending row removed. */
    }
  }
7329

7330
  DBUG_RETURN(error);
7331 7332
}

7333 7334 7335
#endif

int 
7336
Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
7337
{
7338 7339 7340 7341 7342 7343 7344
  DBUG_ASSERT(m_table != NULL);
  int error= write_row(rli, TRUE /* overwrite */);
  
  if (error && !thd->net.last_errno)
    thd->net.last_errno= error;
      
  return error; 
7345
}
7346

7347 7348 7349 7350 7351
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
{
7352
  Rows_log_event::print_helper(file, print_event_info, "Write_rows");
7353 7354 7355 7356 7357 7358 7359 7360
}
#endif

/**************************************************************************
	Delete_rows_log_event member functions
**************************************************************************/

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
7361 7362 7363 7364 7365 7366
/*
  Compares table->record[0] and table->record[1]

  Returns TRUE if different.
*/
static bool record_compare(TABLE *table)
7367
{
7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380
  /*
    Need to set the X bit and the filler bits in both records since
    there are engines that do not set it correctly.

    In addition, since MyISAM checks that one hasn't tampered with the
    record, it is necessary to restore the old bytes into the record
    after doing the comparison.

    TODO[record format ndb]: Remove it once NDB returns correct
    records. Check that the other engines also return correct records.
   */

  bool result= FALSE;
7381
  uchar saved_x[2], saved_filler[2];
7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394

  if (table->s->null_bytes > 0)
  {
    for (int i = 0 ; i < 2 ; ++i)
    {
      saved_x[i]= table->record[i][0];
      saved_filler[i]= table->record[i][table->s->null_bytes - 1];
      table->record[i][0]|= 1U;
      table->record[i][table->s->null_bytes - 1]|=
        256U - (1U << table->s->last_null_bit_pos);
    }
  }

7395
  if (table->s->blob_fields + table->s->varchar_fields == 0)
7396 7397 7398 7399 7400
  {
    result= cmp_record(table,record[1]);
    goto record_compare_exit;
  }

7401 7402 7403 7404
  /* Compare null bits */
  if (memcmp(table->null_flags,
	     table->null_flags+table->s->rec_buff_length,
	     table->s->null_bytes))
7405 7406 7407 7408 7409
  {
    result= TRUE;				// Diff in NULL value
    goto record_compare_exit;
  }

7410 7411
  /* Compare updated fields */
  for (Field **ptr=table->field ; *ptr ; ptr++)
7412
  {
7413
    if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
7414 7415 7416 7417
    {
      result= TRUE;
      goto record_compare_exit;
    }
7418
  }
7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433

record_compare_exit:
  /*
    Restore the saved bytes.

    TODO[record format ndb]: Remove this code once NDB returns the
    correct record format.
  */
  if (table->s->null_bytes > 0)
  {
    for (int i = 0 ; i < 2 ; ++i)
    {
      table->record[i][0]= saved_x[i];
      table->record[i][table->s->null_bytes - 1]= saved_filler[i];
    }
7434
  }
7435 7436

  return result;
7437 7438
}

7439 7440
/**
  Locate the current row in event's table.
7441

7442 7443 7444 7445
  The current row is pointed by @c m_curr_row. Member @c m_width tells how many 
  columns are there in the row (this can be differnet from the number of columns 
  in the table). It is assumed that event's table is already open and pointed 
  by @c m_table.
7446

7447 7448 7449
  If a corresponding record is found in the table it is stored in 
  @c m_table->record[0]. Note that when record is located based on a primary 
  key, it is possible that the record found differs from the row being located.
7450

7451 7452 7453 7454 7455
  If no key is specified or table does not have keys, a table scan is used to 
  find the row. In that case the row should be complete and contain values for
  all columns. However, it can still be shorter than the table, i.e. the table 
  can contain extra columns not present in the row. It is also possible that 
  the table has fewer columns than the row being located. 
7456

7457 7458 7459 7460
  @returns Error code on failure, 0 on success. 
  
  @post In case of success @c m_table->record[0] contains the record found. 
  Also, the internal "cursor" of the table is positioned at the record found.
7461

7462 7463
  @note If the engine allows random access of the records, a combination of
  @c position() and @c rnd_pos() will be used. 
7464
 */
7465

7466
int Rows_log_event::find_row(const Relay_log_info *rli)
7467
{
7468 7469 7470
  DBUG_ENTER("find_row");

  DBUG_ASSERT(m_table && m_table->in_use != NULL);
7471

7472 7473
  TABLE *table= m_table;
  int error;
7474

7475
  /* unpack row - missing fields get default values */
7476

7477 7478 7479 7480 7481 7482
  // TODO: shall we check and report errors here?
  prepare_record(NULL,table,m_width,FALSE /* don't check errors */); 
  error= unpack_current_row(rli); 

#ifndef DBUG_OFF
  DBUG_PRINT("info",("looking for the following record"));
7483
  DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
7484
#endif
7485

7486 7487
  if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
      table->s->primary_key < MAX_KEY)
7488 7489 7490 7491 7492 7493
  {
    /*
      Use a more efficient method to fetch the record given by
      table->record[0] if the engine allows it.  We first compute a
      row reference using the position() member function (it will be
      stored in table->file->ref) and the use rnd_pos() to position
7494
      the "cursor" (i.e., record[0] in this case) at the correct row.
7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505

      TODO: Add a check that the correct record has been fetched by
      comparing with the original record. Take into account that the
      record on the master and slave can be of different
      length. Something along these lines should work:

      ADD>>>  store_record(table,record[1]);
              int error= table->file->rnd_pos(table->record[0], table->file->ref);
      ADD>>>  DBUG_ASSERT(memcmp(table->record[1], table->record[0],
                                 table->s->reclength) == 0);

7506
    */
7507
    DBUG_PRINT("info",("locating record using primary key (position)"));
7508
    int error= table->file->rnd_pos_by_record(table->record[0]);
7509 7510 7511 7512 7513
    if (error)
    {
      DBUG_PRINT("info",("rnd_pos returns error %d",error));
      table->file->print_error(error, MYF(0));
    }
7514
    DBUG_RETURN(error);
7515 7516
  }

7517
  // We can't use position() - try other methods.
7518 7519 7520 7521 7522
  
  /* 
    We need to retrieve all fields
    TODO: Move this out from this function to main loop 
   */
7523
  table->use_all_columns();
unknown's avatar
unknown committed
7524

7525 7526 7527 7528 7529 7530
  /*
    Save copy of the record in table->record[1]. It might be needed 
    later if linear search is used to find exact match.
   */ 
  store_record(table,record[1]);    

7531 7532
  if (table->s->keys > 0)
  {
7533 7534
    DBUG_PRINT("info",("locating record using primary key (index_read)"));

7535
    /* We have a key: search the table using the index */
7536
    if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
7537 7538 7539
    {
      DBUG_PRINT("info",("ha_index_init returns error %d",error));
      table->file->print_error(error, MYF(0));
7540
      DBUG_RETURN(error);
7541
    }
7542

7543 7544 7545 7546 7547 7548 7549 7550 7551
    /* Fill key data for the row */

    DBUG_ASSERT(m_key);
    key_copy(m_key, table->record[0], table->key_info, 0);

    /*
      Don't print debug messages when running valgrind since they can
      trigger false warnings.
     */
7552
#ifndef HAVE_purify
7553
    DBUG_DUMP("key data", m_key, table->key_info->key_length);
7554 7555
#endif

7556 7557 7558 7559 7560 7561
    /*
      We need to set the null bytes to ensure that the filler bit are
      all set when returning.  There are storage engines that just set
      the necessary bits on the bytes and don't set the filler bits
      correctly.
    */
7562 7563
    my_ptrdiff_t const pos=
      table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
7564 7565 7566
    table->record[0][pos]= 0xFF;
    
    if ((error= table->file->index_read_map(table->record[0], m_key, 
7567
                                            HA_WHOLE_KEY,
7568
                                            HA_READ_KEY_EXACT)))
7569
    {
7570
      DBUG_PRINT("info",("no record matching the key found in the table"));
7571
      table->file->print_error(error, MYF(0));
7572
      table->file->ha_index_end();
7573 7574 7575
      DBUG_RETURN(error);
    }

7576 7577 7578 7579 7580
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
7581 7582
    DBUG_PRINT("info",("found first matching record")); 
    DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
7583
#endif
7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596
    /*
      Below is a minor "optimization".  If the key (i.e., key number
      0) has the HA_NOSAME flag set, we know that we have found the
      correct record (since there can be no duplicates); otherwise, we
      have to compare the record with the one found to see if it is
      the correct one.

      CAVEAT! This behaviour is essential for the replication of,
      e.g., the mysql.proc table since the correct record *shall* be
      found using the primary key *only*.  There shall be no
      comparison of non-PK columns to decide if the correct record is
      found.  I can see no scenario where it would be incorrect to
      chose the row to change only using a PK or an UNNI.
7597
    */
7598
    if (table->key_info->flags & HA_NOSAME)
7599 7600
    {
      table->file->ha_index_end();
7601
      DBUG_RETURN(0);
7602
    }
7603

7604 7605
    /*
      In case key is not unique, we still have to iterate over records found
7606 7607
      and find the one which is identical to the row given. A copy of the 
      record we are looking for is stored in record[1].
7608 7609 7610
     */ 
    DBUG_PRINT("info",("non-unique index, scanning it to find matching record")); 

7611
    while (record_compare(table))
7612
    {
7613 7614 7615 7616 7617
      /*
        We need to set the null bytes to ensure that the filler bit
        are all set when returning.  There are storage engines that
        just set the necessary bits on the bytes and don't set the
        filler bits correctly.
7618 7619 7620

        TODO[record format ndb]: Remove this code once NDB returns the
        correct record format.
7621
      */
7622 7623
      if (table->s->null_bytes > 0)
      {
7624
        table->record[0][table->s->null_bytes - 1]|=
7625 7626 7627
          256U - (1U << table->s->last_null_bit_pos);
      }

7628
      if ((error= table->file->index_next(table->record[0])))
7629
      {
7630 7631
        DBUG_PRINT("info",("no record matching the given row found"));
        table->file->print_error(error, MYF(0));
7632
        table->file->ha_index_end();
7633
        DBUG_RETURN(error);
7634 7635
      }
    }
7636 7637 7638 7639 7640

    /*
      Have to restart the scan to be able to fetch the next row.
    */
    table->file->ha_index_end();
7641 7642 7643
  }
  else
  {
7644 7645
    DBUG_PRINT("info",("locating record using table scan (rnd_next)"));

7646
    int restart_count= 0; // Number of times scanning has restarted from top
7647 7648 7649

    /* We don't have a key: search the table using rnd_next() */
    if ((error= table->file->ha_rnd_init(1)))
7650 7651 7652 7653 7654 7655
    {
      DBUG_PRINT("info",("error initializing table scan"
                         " (ha_rnd_init returns %d)",error));
      table->file->print_error(error, MYF(0));
      DBUG_RETURN(error);
    }
7656 7657

    /* Continue until we find the right record or have made a full loop */
7658 7659
    do
    {
7660
      error= table->file->rnd_next(table->record[0]);
7661

7662
      switch (error) {
7663

7664 7665
      case 0:
      case HA_ERR_RECORD_DELETED:
7666
        break;
7667 7668

      case HA_ERR_END_OF_FILE:
7669 7670 7671
        if (++restart_count < 2)
          table->file->ha_rnd_init(1);
        break;
7672 7673

      default:
7674 7675 7676
        DBUG_PRINT("info", ("Failed to get next record"
                            " (rnd_next returns %d)",error));
        table->file->print_error(error, MYF(0));
7677
        table->file->ha_rnd_end();
7678
        DBUG_RETURN(error);
7679 7680
      }
    }
7681
    while (restart_count < 2 && record_compare(table));
7682 7683 7684 7685 7686
    
    /* 
      Note: above record_compare will take into accout all record fields 
      which might be incorrect in case a partial row was given in the event
     */
7687

7688 7689 7690
    /*
      Have to restart the scan to be able to fetch the next row.
    */
7691 7692
    if (restart_count == 2)
      DBUG_PRINT("info", ("Record not found"));
7693 7694
    else
      DBUG_DUMP("record found", table->record[0], table->s->reclength);
7695 7696
    table->file->ha_rnd_end();

7697 7698 7699 7700 7701 7702
    DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
    DBUG_RETURN(error);
  }

  DBUG_RETURN(0);
}
7703

7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732
#endif

/*
  Constructor used to build an event for writing to the binary log.
 */

#ifndef MYSQL_CLIENT
Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
                                             ulong tid, MY_BITMAP const *cols,
                                             bool is_transactional)
  : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
{
}
#endif /* #if !defined(MYSQL_CLIENT) */

/*
  Constructor used by slave to read the event from the binary log.
 */
#ifdef HAVE_REPLICATION
Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
                                             const Format_description_log_event
                                             *description_event)
  : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
{
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)

7733 7734 7735 7736 7737
int 
Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
  if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
      m_table->s->primary_key < MAX_KEY)
7738 7739
  {
    /*
7740
      We don't need to allocate any memory for m_key since it is not used.
7741 7742 7743 7744
    */
    return 0;
  }

7745
  if (m_table->s->keys > 0)
7746
  {
7747 7748 7749 7750
    // Allocate buffer for key searches
    m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
    if (!m_key)
      return HA_ERR_OUT_OF_MEM;
7751
  }
7752
  return 0;
7753 7754
}

7755 7756 7757
int 
Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, 
                                               int error)
7758 7759
{
  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
7760 7761
  m_table->file->ha_index_or_rnd_end();
  my_free(m_key, MYF(MY_ALLOW_ZERO_PTR));
7762
  m_key= NULL;
7763 7764 7765 7766

  return error;
}

7767
int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
7768
{
7769
  int error;
7770
  DBUG_ASSERT(m_table != NULL);
7771

7772
  if (!(error= find_row(rli))) 
7773 7774
  { 
    /*
7775
      Delete the record found, located in record[0]
7776
    */
7777
    error= m_table->file->ha_delete_row(m_table->record[0]);
7778
  }
7779 7780 7781 7782 7783 7784 7785 7786 7787
  return error;
}

#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Delete_rows_log_event::print(FILE *file,
                                  PRINT_EVENT_INFO* print_event_info)
{
7788
  Rows_log_event::print_helper(file, print_event_info, "Delete_rows");
7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801
}
#endif


/**************************************************************************
	Update_rows_log_event member functions
**************************************************************************/

/*
  Constructor used to build an event for writing to the binary log.
 */
#if !defined(MYSQL_CLIENT)
Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813
                                             ulong tid,
                                             MY_BITMAP const *cols_bi,
                                             MY_BITMAP const *cols_ai,
                                             bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional)
{
  init(cols_ai);
}

Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
                                             ulong tid,
                                             MY_BITMAP const *cols,
7814 7815 7816
                                             bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
{
7817 7818 7819 7820 7821
  init(cols);
}

void Update_rows_log_event::init(MY_BITMAP const *cols)
{
7822
  /* if bitmap_init fails, caught in is_valid() */
7823 7824
  if (likely(!bitmap_init(&m_cols_ai,
                          m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
7825
                          m_width,
7826 7827 7828 7829
                          false)))
  {
    /* Cols can be zero if this is a dummy binrows event */
    if (likely(cols != NULL))
7830
    {
7831
      memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols));
7832 7833
      create_last_word_mask(&m_cols_ai);
    }
7834
  }
7835 7836 7837
}
#endif /* !defined(MYSQL_CLIENT) */

7838 7839 7840 7841 7842 7843 7844 7845 7846

Update_rows_log_event::~Update_rows_log_event()
{
  if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
    m_cols_ai.bitmap= 0; // so no my_free in bitmap_free
  bitmap_free(&m_cols_ai); // To pair with bitmap_init().
}


7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861
/*
  Constructor used by slave to read the event from the binary log.
 */
#ifdef HAVE_REPLICATION
Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
                                             const
                                             Format_description_log_event
                                             *description_event)
  : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
{
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)

7862 7863 7864 7865
int 
Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
  if (m_table->s->keys > 0)
7866
  {
7867 7868 7869 7870
    // Allocate buffer for key searches
    m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
    if (!m_key)
      return HA_ERR_OUT_OF_MEM;
7871 7872
  }

7873
  m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7874

7875
  return 0;
7876 7877
}

7878 7879 7880
int 
Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, 
                                               int error)
7881
{
7882 7883 7884
  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
  m_table->file->ha_index_or_rnd_end();
  my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
7885
  m_key= NULL;
7886 7887 7888 7889

  return error;
}

7890
int 
7891
Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
7892
{
7893
  DBUG_ASSERT(m_table != NULL);
7894

7895 7896
  int error= find_row(rli); 
  if (error)
7897
    return error;
7898

7899
  /*
7900
    This is the situation after locating BI:
7901

7902 7903 7904
    ===|=== before image ====|=== after image ===|===
       ^                     ^
       m_curr_row            m_curr_row_end
7905

7906 7907 7908
    BI found in the table is stored in record[0]. We copy it to record[1]
    and unpack AI to record[0].
   */
7909

7910
  store_record(m_table,record[1]);
7911

7912 7913
  m_curr_row= m_curr_row_end;
  error= unpack_current_row(rli); // this also updates m_curr_row_end
7914

7915
  /*
7916
    Now we have the right row to update.  The old row (the one we're
7917
    looking for) is in record[1] and the new row is in record[0].
7918
  */
7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929
#ifndef HAVE_purify
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
  DBUG_PRINT("info",("Updating row in table"));
  DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
  DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
#endif

  error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
7930 7931
  if (error == HA_ERR_RECORD_IS_THE_SAME)
    error= 0;
7932 7933 7934

  return error;
}
7935

7936 7937 7938 7939 7940 7941
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Update_rows_log_event::print(FILE *file,
				  PRINT_EVENT_INFO* print_event_info)
{
7942
  Rows_log_event::print_helper(file, print_event_info, "Update_rows");
7943 7944 7945
}
#endif

7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962

Incident_log_event::Incident_log_event(const char *buf, uint event_len,
                                       const Format_description_log_event *descr_event)
  : Log_event(buf, descr_event)
{
  DBUG_ENTER("Incident_log_event::Incident_log_event");
  uint8 const common_header_len=
    descr_event->common_header_len;
  uint8 const post_header_len=
    descr_event->post_header_len[INCIDENT_EVENT-1];

  DBUG_PRINT("info",("event_len: %u; common_header_len: %d; post_header_len: %d",
                     event_len, common_header_len, post_header_len));

  m_incident= static_cast<Incident>(uint2korr(buf + common_header_len));
  char const *ptr= buf + common_header_len + post_header_len;
  char const *const str_end= buf + event_len;
unknown's avatar
unknown committed
7963 7964
  uint8 len= 0;                   // Assignment to keep compiler happy
  const char *str= NULL;          // Assignment to keep compiler happy
7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988
  read_str(&ptr, str_end, &str, &len);
  m_message.str= const_cast<char*>(str);
  m_message.length= len;
  DBUG_PRINT("info", ("m_incident: %d", m_incident));
  DBUG_VOID_RETURN;
}


Incident_log_event::~Incident_log_event()
{
}


const char *
Incident_log_event::description() const
{
  static const char *const description[]= {
    "NOTHING",                                  // Not used
    "LOST_EVENTS"
  };

  DBUG_PRINT("info", ("m_incident: %d", m_incident));

  DBUG_ASSERT(0 <= m_incident);
7989
  DBUG_ASSERT((size_t) m_incident <= sizeof(description)/sizeof(*description));
7990 7991 7992 7993 7994 7995 7996 7997 7998

  return description[m_incident];
}


#ifndef MYSQL_CLIENT
void Incident_log_event::pack_info(Protocol *protocol)
{
  char buf[256];
7999
  size_t bytes;
8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026
  if (m_message.length > 0)
    bytes= my_snprintf(buf, sizeof(buf), "#%d (%s)",
                       m_incident, description());
  else
    bytes= my_snprintf(buf, sizeof(buf), "#%d (%s): %s",
                       m_incident, description(), m_message.str);
  protocol->store(buf, bytes, &my_charset_bin);
}
#endif


#ifdef MYSQL_CLIENT
void
Incident_log_event::print(FILE *file,
                          PRINT_EVENT_INFO *print_event_info)
{
  if (print_event_info->short_form)
    return;

  Write_on_release_cache cache(&print_event_info->head_cache, file);
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n# Incident: %s", description());
}
#endif

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int
8027
Incident_log_event::do_apply_event(Relay_log_info const *rli)
8028
{
8029
  DBUG_ENTER("Incident_log_event::do_apply_event");
8030 8031 8032 8033
  rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT,
              ER(ER_SLAVE_INCIDENT),
              description(),
              m_message.length > 0 ? m_message.str : "<none>");
8034 8035 8036 8037 8038 8039 8040 8041 8042
  DBUG_RETURN(1);
}
#endif

bool
Incident_log_event::write_data_header(IO_CACHE *file)
{
  DBUG_ENTER("Incident_log_event::write_data_header");
  DBUG_PRINT("enter", ("m_incident: %d", m_incident));
8043
  uchar buf[sizeof(int16)];
8044 8045 8046 8047 8048 8049 8050 8051 8052 8053
  int2store(buf, (int16) m_incident);
  DBUG_RETURN(my_b_safe_write(file, buf, sizeof(buf)));
}

bool
Incident_log_event::write_data_body(IO_CACHE *file)
{
  DBUG_ENTER("Incident_log_event::write_data_body");
  DBUG_RETURN(write_str(file, m_message.str, m_message.length));
}