log_event.cc 247 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 150 151 152 153 154 155 156
static void clear_all_errors(THD *thd, struct st_relay_log_info *rli)
{
  thd->query_error = 0;
  thd->clear_error();
  *rli->last_slave_error = 0;
  rli->last_slave_errno = 0;
}

unknown's avatar
unknown committed
157

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

162 163
inline int ignored_error_code(int err_code)
{
unknown's avatar
unknown committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177
#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
Fix for  
unknown committed
178 179
  return ((err_code == ER_SLAVE_IGNORED_TABLE) ||
          (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code)));
180
}
unknown's avatar
SCRUM  
unknown committed
181
#endif
182

unknown's avatar
unknown committed
183

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

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

unknown's avatar
unknown committed
215

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

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

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

unknown's avatar
unknown committed
249

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

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

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

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

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

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

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


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

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


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

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

324

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

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

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

373

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

379
#ifdef MYSQL_CLIENT
380

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

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

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

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

433

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

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


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

455
Log_event::Log_event()
456
  :temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
457 458
   thd(0)
{
unknown's avatar
unknown committed
459 460 461
  server_id=	::server_id;
  when=		time(NULL);
  log_pos=	0;
462
}
unknown's avatar
unknown committed
463
#endif /* !MYSQL_CLIENT */
464 465


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

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

#ifndef MYSQL_CLIENT
unknown's avatar
SCRUM  
unknown committed
538
#ifdef HAVE_REPLICATION
539

540
int Log_event::do_update_pos(RELAY_LOG_INFO *rli)
541
{
542
  /*
543
    rli is null when (as far as I (Guilhem) know) the caller is
544 545 546 547
    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.
548 549 550
    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.
551 552

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

557 558
  return 0;                                   // Cannot fail currently
}
559

560 561

Log_event::enum_skip_reason
562 563 564 565 566 567 568 569 570 571
Log_event::do_shall_skip(RELAY_LOG_INFO *rli)
{
  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;
572 573 574
  else if (rli->slave_skip_counter > 0)
    return EVENT_SKIP_COUNT;
  else
575
    return EVENT_SKIP_NOT;
576
}
unknown's avatar
unknown committed
577

578

unknown's avatar
unknown committed
579
/*
580
  Log_event::pack_info()
unknown's avatar
unknown committed
581
*/
582

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


unknown's avatar
unknown committed
589
/*
590
  Log_event::net_send()
unknown's avatar
unknown committed
591

592
  Only called by SHOW BINLOG EVENTS
unknown's avatar
unknown committed
593
*/
unknown's avatar
SCRUM  
unknown committed
594

unknown's avatar
unknown committed
595
int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
596
{
unknown's avatar
unknown committed
597 598
  const char *p= strrchr(log_name, FN_LIBCHAR);
  const char *event_type;
599 600 601
  if (p)
    log_name = p + 1;
  
unknown's avatar
unknown committed
602
  protocol->prepare_for_resend();
603
  protocol->store(log_name, &my_charset_bin);
unknown's avatar
unknown committed
604
  protocol->store((ulonglong) pos);
605
  event_type = get_type_str();
606
  protocol->store(event_type, strlen(event_type), &my_charset_bin);
unknown's avatar
unknown committed
607 608 609 610
  protocol->store((uint32) server_id);
  protocol->store((ulonglong) log_pos);
  pack_info(protocol);
  return protocol->write();
611
}
unknown's avatar
SCRUM  
unknown committed
612 613 614
#endif /* HAVE_REPLICATION */


unknown's avatar
unknown committed
615
/*
unknown's avatar
SCRUM  
unknown committed
616
  Log_event::init_show_field_list()
unknown's avatar
unknown committed
617
*/
unknown's avatar
SCRUM  
unknown committed
618 619 620 621

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

633

unknown's avatar
unknown committed
634
/*
635
  Log_event::write()
unknown's avatar
unknown committed
636
*/
637

638
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
unknown's avatar
unknown committed
639
{
640 641
  byte header[LOG_EVENT_HEADER_LEN];
  DBUG_ENTER("Log_event::write_header");
unknown's avatar
unknown committed
642

643 644
  /* Store number of bytes that will be written by this event */
  data_written= event_data_length + sizeof(header);
645

646 647 648 649
  /*
    log_pos != 0 if this is relay-log event. In this case we should not
    change the position
  */
650

651 652 653 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 695 696 697 698 699 700 701 702 703 704 705
  if (is_artificial_event())
  {
    /*
      We should not do any cleanup on slave when reading this. We
      mark this by setting log_pos to 0.  Start_log_event_v3() will
      detect this on reading and set artificial_event=1 for the event.
    */
    log_pos= 0;
  }
  else  if (!log_pos)
  {
    /*
      Calculate position of end of event

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

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

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

    log_pos= my_b_safe_tell(file)+data_written;
  }

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

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

  DBUG_RETURN(my_b_safe_write(file, header, sizeof(header)) != 0);
unknown's avatar
unknown committed
706 707 708
}


unknown's avatar
unknown committed
709
/*
710
  Log_event::read_log_event()
711 712 713 714

  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
715
*/
716

717
int Log_event::read_log_event(IO_CACHE* file, String* packet,
718
			      pthread_mutex_t* log_lock)
unknown's avatar
unknown committed
719 720
{
  ulong data_len;
721
  int result=0;
722
  char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
723
  DBUG_ENTER("Log_event::read_log_event");
724

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

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

end:
  if (log_lock)
    pthread_mutex_unlock(log_lock);
785
  DBUG_RETURN(result);
unknown's avatar
unknown committed
786
}
unknown's avatar
unknown committed
787
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
788

unknown's avatar
unknown committed
789
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
790 791
#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock);
#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock);
792
#else
793
#define UNLOCK_MUTEX
794 795 796
#define LOCK_MUTEX
#endif

unknown's avatar
unknown committed
797
/*
798 799
  Log_event::read_log_event()

unknown's avatar
unknown committed
800
  NOTE:
801
    Allocates memory;  The caller is responsible for clean-up.
unknown's avatar
unknown committed
802
*/
803

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

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

849
  if (data_len > max_allowed_packet)
unknown's avatar
unknown committed
850
  {
851 852
    error = "Event too big";
    goto err;
unknown's avatar
unknown committed
853 854
  }

855
  if (data_len < header_size)
unknown's avatar
unknown committed
856
  {
857 858
    error = "Event too small";
    goto err;
unknown's avatar
unknown committed
859
  }
860 861 862

  // some events use the extra byte to null-terminate strings
  if (!(buf = my_malloc(data_len+1, MYF(MY_WME))))
863 864 865
  {
    error = "Out of memory";
    goto err;
unknown's avatar
unknown committed
866
  }
867
  buf[data_len] = 0;
868
  memcpy(buf, head, header_size);
869
  if (my_b_read(file, (byte*) buf + header_size, data_len - header_size))
870 871 872 873
  {
    error = "read error";
    goto err;
  }
unknown's avatar
unknown committed
874
  if ((res= read_log_event(buf, data_len, &error, description_event)))
875
    res->register_temp_buf(buf);
876

877
err:
unknown's avatar
merged  
unknown committed
878
  UNLOCK_MUTEX;
879
  if (!res)
880
  {
881
    DBUG_ASSERT(error != 0);
882 883
    sql_print_error("Error in Log_event::read_log_event(): "
                    "'%s', data_len: %d, event_type: %d",
884
		    error,data_len,head[EVENT_TYPE_OFFSET]);
885
    my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
886 887 888 889 890 891 892 893 894
    /*
      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;
895
  }
896
  DBUG_RETURN(res);
unknown's avatar
unknown committed
897 898
}

899

unknown's avatar
unknown committed
900
/*
901
  Log_event::read_log_event()
902 903
  Binlog format tolerance is in (buf, event_len, description_event)
  constructors.
unknown's avatar
unknown committed
904
*/
905

906 907 908
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
909
{
910 911
  Log_event* ev;
  DBUG_ENTER("Log_event::read_log_event(char*,...)");
912
  DBUG_ASSERT(description_event != 0);
913
  DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
914
  if (event_len < EVENT_LEN_OFFSET ||
915 916 917
      (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
  {
    *error="Sanity check failed";		// Needed to free buffer
unknown's avatar
unknown committed
918
    DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
919
  }
unknown's avatar
unknown committed
920

921 922 923
  /* To check the integrity of the Log_event_type enumeration */
  DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] < ENUM_END_EVENT);

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

1014 1015 1016 1017
  DBUG_PRINT("read_event", ("%s(type_code: %d; event_len: %d)",
                            ev ? ev->get_type_str() : "<unknown>",
                            buf[EVENT_TYPE_OFFSET],
                            event_len));
1018
  /*
1019 1020 1021 1022 1023 1024 1025
    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'.
1026
  */
1027
  if (!ev || !ev->is_valid())
1028
  {
1029 1030
    DBUG_PRINT("error",("Found invalid event in binary log"));

1031
    delete ev;
1032
#ifdef MYSQL_CLIENT
1033
    if (!force_opt) /* then mysqlbinlog dies */
1034 1035
    {
      *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
1036
      DBUG_RETURN(0);
1037
    }
1038
    ev= new Unknown_log_event(buf, description_event);
1039 1040
#else
    *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
1041
    DBUG_RETURN(0);
1042
#endif
1043
  }
unknown's avatar
unknown committed
1044
  DBUG_RETURN(ev);  
unknown's avatar
unknown committed
1045 1046
}

1047
#ifdef MYSQL_CLIENT
1048

unknown's avatar
unknown committed
1049
/*
1050
  Log_event::print_header()
unknown's avatar
unknown committed
1051
*/
1052

1053 1054 1055
void Log_event::print_header(IO_CACHE* file,
                             PRINT_EVENT_INFO* print_event_info,
                             bool is_more __attribute__((unused)))
1056
{
1057
  char llbuff[22];
unknown's avatar
unknown committed
1058
  my_off_t hexdump_from= print_event_info->hexdump_from;
1059
  DBUG_ENTER("Log_event::print_header");
unknown's avatar
unknown committed
1060

1061
  my_b_printf(file, "#");
1062
  print_timestamp(file);
1063 1064
  my_b_printf(file, " server id %d  end_log_pos %s ", server_id,
              llstr(log_pos,llbuff));
1065

1066
  /* mysqlbinlog --hexdump */
unknown's avatar
unknown committed
1067
  if (print_event_info->hexdump_from)
1068
  {
1069
    my_b_printf(file, "\n");
1070 1071 1072
    uchar *ptr= (uchar*)temp_buf;
    my_off_t size=
      uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN;
1073 1074
    my_off_t i;

1075 1076 1077 1078
    /* 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
1079
    /* Pretty-print event common header if header is exactly 19 bytes */
unknown's avatar
unknown committed
1080
    if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
unknown's avatar
unknown committed
1081
    {
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
      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);
      DBUG_ASSERT(static_cast<my_size_t>(bytes_written) < sizeof(emit_buf));
1096
      my_b_write(file, (byte*) emit_buf, bytes_written);
unknown's avatar
unknown committed
1097 1098 1099
      ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
      hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
    }
1100 1101 1102 1103 1104

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

1109
      *c++= my_isalnum(&my_charset_bin, *ptr) ? *ptr : '.';
1110 1111 1112

      if (i % 16 == 15)
      {
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
        /*
          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);
        DBUG_ASSERT(static_cast<my_size_t>(bytes_written) < sizeof(emit_buf));
1127
	my_b_write(file, (byte*) emit_buf, bytes_written);
1128 1129 1130 1131
	hex_string[0]= 0;
	char_string[0]= 0;
	c= char_string;
	h= hex_string;
1132
      }
1133
      else if (i % 8 == 7) *h++ = ' ';
1134
    }
1135
    *c= '\0';
1136

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

1159

1160 1161 1162
void Log_event::print_base64(IO_CACHE* file,
                             PRINT_EVENT_INFO* print_event_info,
                             bool more)
1163
{
1164
  const uchar *ptr= (const uchar *)temp_buf;
1165
  uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
1166 1167
  DBUG_ENTER("Log_event::print_base64");

1168
  size_t const tmp_str_sz= base64_needed_encoded_length((int) size);
1169
  char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME));
1170 1171 1172
  if (!tmp_str) {
    fprintf(stderr, "\nError: Out of memory. "
            "Could not print correct binlog event.\n");
1173
    DBUG_VOID_RETURN;
1174
  }
1175

unknown's avatar
unknown committed
1176 1177 1178 1179
  if (base64_encode(ptr, (size_t) size, tmp_str))
  {
    DBUG_ASSERT(0);
  }
1180 1181 1182 1183 1184 1185 1186

  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
1187
    my_b_printf(file, "'%s\n", print_event_info->delimiter);
1188

1189
  my_free(tmp_str, MYF(0));
1190
  DBUG_VOID_RETURN;
1191 1192 1193
}


unknown's avatar
unknown committed
1194
/*
1195
  Log_event::print_timestamp()
unknown's avatar
unknown committed
1196
*/
1197

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

1211 1212 1213 1214 1215 1216 1217 1218
  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
1219 1220
}

unknown's avatar
unknown committed
1221
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
1222 1223


unknown's avatar
unknown committed
1224
/**************************************************************************
unknown's avatar
unknown committed
1225
	Query_log_event methods
unknown's avatar
unknown committed
1226
**************************************************************************/
1227

unknown's avatar
SCRUM  
unknown committed
1228
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1229

unknown's avatar
unknown committed
1230
/*
1231
  Query_log_event::pack_info()
1232 1233 1234 1235
  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
1236
*/
1237

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

1262
#ifndef MYSQL_CLIENT
1263

1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
/* 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
1276
/*
1277
  Query_log_event::write()
unknown's avatar
unknown committed
1278

1279 1280 1281 1282
  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
1283
*/
1284

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

1299
  if (!query)
1300 1301
    return 1;                                   // Something wrong with event

unknown's avatar
unknown committed
1302 1303 1304 1305 1306
  /*
    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.
1307
    Now imagine we (write()) are called by the slave SQL thread (we are
unknown's avatar
unknown committed
1308 1309 1310 1311 1312 1313 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
    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);
1340 1341 1342 1343
  int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
  buf[Q_DB_LEN_OFFSET] = (char) db_len;
  int2store(buf + Q_ERR_CODE_OFFSET, error_code);

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

  return (write_header(file, event_length) ||
unknown's avatar
unknown committed
1443 1444 1445 1446
          my_b_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) ||
          write_post_header_for_derived(file) ||
          my_b_safe_write(file, (byte*) start_of_status,
                          (uint) (start-start_of_status)) ||
unknown's avatar
unknown committed
1447 1448
          my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
          my_b_safe_write(file, (byte*) query, q_len)) ? 1 : 0;
unknown's avatar
unknown committed
1449 1450
}

1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462
/*
  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)
{
}

1463

unknown's avatar
unknown committed
1464
/*
1465
  Query_log_event::Query_log_event()
unknown's avatar
unknown committed
1466
*/
1467
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
1468 1469
				 ulong query_length, bool using_trans,
				 bool suppress_use)
unknown's avatar
unknown committed
1470
  :Log_event(thd_arg,
1471 1472 1473
	     ((thd_arg->tmp_table_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0)
	      | (suppress_use          ? LOG_EVENT_SUPPRESS_USE_F    : 0)),
	     using_trans),
1474
   data_buf(0), query(query_arg), catalog(thd_arg->catalog),
1475
   db(thd_arg->db), q_len((uint32) query_length),
1476
   error_code((thd_arg->killed != THD::NOT_KILLED) ?
1477
              ((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ?
1478
               0 : thd->killed_errno()) : thd_arg->net.last_errno),
unknown's avatar
unknown committed
1479 1480
   thread_id(thd_arg->thread_id),
   /* save the original thread id; we already know the server id */
1481
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
1482
   flags2_inited(1), sql_mode_inited(1), charset_inited(1),
1483 1484
   sql_mode(thd_arg->variables.sql_mode),
   auto_increment_increment(thd_arg->variables.auto_increment_increment),
1485
   auto_increment_offset(thd_arg->variables.auto_increment_offset),
1486 1487
   lc_time_names_number(thd_arg->variables.lc_time_names->number),
   charset_database_number(0)
1488 1489 1490 1491
{
  time_t end_time;
  time(&end_time);
  exec_time = (ulong) (end_time  - thd->start_time);
1492
  catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
1493
  /* status_vars_len is set just before writing the event */
1494
  db_len = (db) ? (uint32) strlen(db) : 0;
1495 1496 1497
  if (thd_arg->variables.collation_database != thd_arg->db_charset)
    charset_database_number= thd_arg->variables.collation_database->number;
  
1498 1499 1500 1501 1502 1503 1504
  /*
    If we don't use flags2 for anything else than options contained in
    thd->options, it would be more efficient to flags2=thd_arg->options
    (OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time).
    But it's likely that we don't want to use 32 bits for 3 bits; in the future
    we will probably want to reclaim the 29 bits. So we need the &.
  */
unknown's avatar
unknown committed
1505
  flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG);
1506 1507 1508 1509 1510 1511
  DBUG_ASSERT(thd->variables.character_set_client->number < 256*256);
  DBUG_ASSERT(thd->variables.collation_connection->number < 256*256);
  DBUG_ASSERT(thd->variables.collation_server->number < 256*256);
  int2store(charset, thd_arg->variables.character_set_client->number);
  int2store(charset+2, thd_arg->variables.collation_connection->number);
  int2store(charset+4, thd_arg->variables.collation_server->number);
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523
  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
1524 1525
  DBUG_PRINT("info",("Query_log_event has flags2: %lu  sql_mode: %lu",
                     (ulong) flags2, sql_mode));
1526
}
unknown's avatar
unknown committed
1527
#endif /* MYSQL_CLIENT */
1528

unknown's avatar
unknown committed
1529

1530 1531
/* 2 utility functions for the next method */

1532 1533 1534 1535 1536 1537 1538 1539
/* 
  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)
1540 1541
{
  if ((*len= **src))
1542 1543
    *dst= (char *)*src + 1;                          // Will be copied later
  (*src)+= *len + 1;
1544 1545
}

1546 1547 1548
static void copy_str_and_move(const char **src, 
                              Log_event::Byte **dst, 
                              uint len)
1549 1550
{
  memcpy(*dst, *src, len);
1551
  *src= (const char *)*dst;
1552 1553 1554 1555
  (*dst)+= len;
  *(*dst)++= 0;
}

unknown's avatar
unknown committed
1556
/*
1557
  Query_log_event::Query_log_event()
1558
  This is used by the SQL slave thread to prepare the event before execution.
unknown's avatar
unknown committed
1559
*/
1560

1561
Query_log_event::Query_log_event(const char* buf, uint event_len,
unknown's avatar
unknown committed
1562 1563
                                 const Format_description_log_event *description_event,
                                 Log_event_type event_type)
1564
  :Log_event(buf, description_event), data_buf(0), query(NullS),
1565
   db(NullS), catalog_len(0), status_vars_len(0),
1566
   flags2_inited(0), sql_mode_inited(0), charset_inited(0),
1567
   auto_increment_increment(1), auto_increment_offset(1),
1568
   time_zone_len(0), lc_time_names_number(0), charset_database_number(0)
unknown's avatar
unknown committed
1569 1570
{
  ulong data_len;
1571 1572
  uint32 tmp;
  uint8 common_header_len, post_header_len;
1573 1574
  Log_event::Byte *start;
  const Log_event::Byte *end;
1575
  bool catalog_nz= 1;
1576 1577 1578
  DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");

  common_header_len= description_event->common_header_len;
unknown's avatar
unknown committed
1579
  post_header_len= description_event->post_header_len[event_type-1];
unknown's avatar
unknown committed
1580
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594
                     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);
1595
  db_len = (uint)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
1596 1597 1598 1599 1600 1601 1602 1603 1604
  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)
1605
  {
1606 1607 1608 1609 1610
    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;
1611
  }
unknown's avatar
unknown committed
1612 1613 1614 1615 1616 1617
  /*
    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...)
  */

1618 1619
  /* variable-part: the status vars; only in MySQL 5.0  */
  
1620 1621 1622
  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;)
1623
  {
1624 1625 1626 1627
    switch (*pos++) {
    case Q_FLAGS2_CODE:
      flags2_inited= 1;
      flags2= uint4korr(pos);
unknown's avatar
unknown committed
1628
      DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", (ulong) flags2));
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
      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;
    }
1643
    case Q_CATALOG_NZ_CODE:
1644
      get_str_len_and_pointer(&pos, &catalog, &catalog_len);
1645 1646 1647 1648 1649 1650
      break;
    case Q_AUTO_INCREMENT:
      auto_increment_increment= uint2korr(pos);
      auto_increment_offset=    uint2korr(pos+2);
      pos+= 4;
      break;
1651 1652 1653 1654 1655 1656 1657
    case Q_CHARSET_CODE:
    {
      charset_inited= 1;
      memcpy(charset, pos, 6);
      pos+= 6;
      break;
    }
1658 1659
    case Q_TIME_ZONE_CODE:
    {
1660
      get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len);
1661 1662
      break;
    }
1663 1664 1665 1666 1667 1668
    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;
1669 1670 1671 1672
    case Q_LC_TIME_NAMES_CODE:
      lc_time_names_number= uint2korr(pos);
      pos+= 2;
      break;
1673 1674 1675 1676
    case Q_CHARSET_DATABASE_CODE:
      charset_database_number= uint2korr(pos);
      pos+= 2;
      break;
1677 1678 1679 1680
    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)));
1681
      pos= (const uchar*) end;                         // Break loop
1682
    }
1683
  }
1684
  
1685
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
1686 1687 1688 1689 1690 1691
  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))))
1692
#else
1693 1694 1695 1696
  if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
                                             time_zone_len + 1 +
                                             data_len + 1,
                                             MYF(MY_WME))))
1697
#endif
unknown's avatar
unknown committed
1698
      DBUG_VOID_RETURN;
1699
  if (catalog_len)                                  // If catalog is given
1700
  {
1701
    if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3.
1702
      copy_str_and_move(&catalog, &start, catalog_len);
1703 1704 1705
    else
    {
      memcpy(start, catalog, catalog_len+1); // copy end 0
1706
      catalog= (const char *)start;
1707 1708
      start+= catalog_len+1;
    }
1709
  }
1710
  if (time_zone_len)
1711
    copy_str_and_move(&time_zone_str, &start, time_zone_len);
1712

1713
  /* A 2nd variable part; this is common to all versions */ 
1714
  memcpy((char*) start, end, data_len);          // Copy db and query
1715
  start[data_len]= '\0';              // End query with \0 (For safetly)
1716 1717
  db= (char *)start;
  query= (char *)(start + db_len + 1);
1718 1719
  q_len= data_len - db_len -1;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1720 1721
}

1722

unknown's avatar
unknown committed
1723
/*
1724
  Query_log_event::print()
unknown's avatar
unknown committed
1725
*/
1726

1727
#ifdef MYSQL_CLIENT
1728
void Query_log_event::print_query_header(IO_CACHE* file,
unknown's avatar
unknown committed
1729
					 PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
1730
{
1731
  // TODO: print the catalog ??
1732
  char buff[40],*end;				// Enough for SET TIMESTAMP
1733 1734 1735
  bool different_db= 1;
  uint32 tmp;

unknown's avatar
unknown committed
1736
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
1737
  {
1738 1739 1740 1741
    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
1742 1743
  }

unknown's avatar
unknown committed
1744
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db)
1745
  {
unknown's avatar
unknown committed
1746 1747
    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
1748
    if (db[0] && different_db) 
unknown's avatar
unknown committed
1749
      my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter);
1750
  }
1751

1752
  end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
1753
  end= strmov(end, print_event_info->delimiter);
1754
  *end++='\n';
1755
  my_b_write(file, (byte*) buff, (uint) (end-buff));
unknown's avatar
unknown committed
1756
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
unknown's avatar
unknown committed
1757 1758
    my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
                (ulong)thread_id, print_event_info->delimiter);
1759

1760
  /*
1761 1762 1763
    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).
1764 1765 1766 1767
  */
  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
1768
    if (likely(print_event_info->flags2_inited)) 
1769
      /* All bits which have changed */
unknown's avatar
unknown committed
1770
      tmp= (print_event_info->flags2) ^ flags2;
1771 1772
    else /* that's the first Query event we read */
    {
unknown's avatar
unknown committed
1773
      print_event_info->flags2_inited= 1;
1774 1775 1776 1777 1778 1779
      tmp= ~((uint32)0); /* all bits have changed */
    }

    if (unlikely(tmp)) /* some bits have changed */
    {
      bool need_comma= 0;
1780
      my_b_printf(file, "SET ");
1781 1782 1783 1784 1785 1786
      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
1787
      my_b_printf(file,"%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
1788
      print_event_info->flags2= flags2;
1789 1790 1791 1792
    }
  }

  /*
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802
    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.
1803 1804 1805 1806
  */

  if (likely(sql_mode_inited))
  {
unknown's avatar
unknown committed
1807
    if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */
1808
    {
unknown's avatar
unknown committed
1809
      print_event_info->sql_mode_inited= 1;
1810
      /* force a difference to force write */
unknown's avatar
unknown committed
1811
      print_event_info->sql_mode= ~sql_mode;
1812
    }
unknown's avatar
unknown committed
1813
    if (unlikely(print_event_info->sql_mode != sql_mode))
1814
    {
unknown's avatar
unknown committed
1815 1816
      my_b_printf(file,"SET @@session.sql_mode=%lu%s\n",
                  (ulong)sql_mode, print_event_info->delimiter);
unknown's avatar
unknown committed
1817
      print_event_info->sql_mode= sql_mode;
1818 1819
    }
  }
unknown's avatar
unknown committed
1820 1821
  if (print_event_info->auto_increment_increment != auto_increment_increment ||
      print_event_info->auto_increment_offset != auto_increment_offset)
1822
  {
unknown's avatar
unknown committed
1823 1824 1825
    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
1826 1827
    print_event_info->auto_increment_increment= auto_increment_increment;
    print_event_info->auto_increment_offset=    auto_increment_offset;
1828 1829
  }

1830 1831
  /* TODO: print the catalog when we feature SET CATALOG */

1832 1833
  if (likely(charset_inited))
  {
unknown's avatar
unknown committed
1834
    if (unlikely(!print_event_info->charset_inited)) /* first Query event */
1835
    {
unknown's avatar
unknown committed
1836 1837
      print_event_info->charset_inited= 1;
      print_event_info->charset[0]= ~charset[0]; // force a difference to force write
1838
    }
unknown's avatar
unknown committed
1839
    if (unlikely(bcmp(print_event_info->charset, charset, 6)))
1840
    {
1841 1842 1843
      CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
      if (cs_info)
      {
1844
        /* for mysql client */
unknown's avatar
unknown committed
1845 1846
        my_b_printf(file, "/*!\\C %s */%s\n",
                    cs_info->csname, print_event_info->delimiter);
1847
      }
1848 1849 1850 1851
      my_b_printf(file,"SET "
                  "@@session.character_set_client=%d,"
                  "@@session.collation_connection=%d,"
                  "@@session.collation_server=%d"
unknown's avatar
unknown committed
1852
                  "%s\n",
1853 1854
                  uint2korr(charset),
                  uint2korr(charset+2),
unknown's avatar
unknown committed
1855 1856
                  uint2korr(charset+4),
                  print_event_info->delimiter);
unknown's avatar
unknown committed
1857
      memcpy(print_event_info->charset, charset, 6);
1858 1859
    }
  }
1860 1861
  if (time_zone_len)
  {
unknown's avatar
unknown committed
1862
    if (bcmp(print_event_info->time_zone_str, time_zone_str, time_zone_len+1))
1863
    {
unknown's avatar
unknown committed
1864 1865
      my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
                  time_zone_str, print_event_info->delimiter);
unknown's avatar
unknown committed
1866
      memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
1867 1868
    }
  }
1869 1870
  if (lc_time_names_number != print_event_info->lc_time_names_number)
  {
unknown's avatar
unknown committed
1871 1872
    my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
                lc_time_names_number, print_event_info->delimiter);
1873 1874
    print_event_info->lc_time_names_number= lc_time_names_number;
  }
1875 1876 1877
  if (charset_database_number != print_event_info->charset_database_number)
  {
    if (charset_database_number)
unknown's avatar
unknown committed
1878 1879
      my_b_printf(file, "SET @@session.collation_database=%d%s\n",
                  charset_database_number, print_event_info->delimiter);
1880
    else
unknown's avatar
unknown committed
1881 1882
      my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
                  print_event_info->delimiter);
1883 1884
    print_event_info->charset_database_number= charset_database_number;
  }
unknown's avatar
unknown committed
1885 1886
}

1887

unknown's avatar
unknown committed
1888
void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
1889
{
1890 1891 1892 1893
  Write_on_release_cache cache(&print_event_info->head_cache, file);

  print_query_header(&cache, print_event_info);
  my_b_write(&cache, (byte*) query, q_len);
unknown's avatar
unknown committed
1894
  my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
1895
}
unknown's avatar
unknown committed
1896
#endif /* MYSQL_CLIENT */
1897

unknown's avatar
unknown committed
1898

unknown's avatar
unknown committed
1899
/*
1900
  Query_log_event::do_apply_event()
unknown's avatar
unknown committed
1901
*/
unknown's avatar
unknown committed
1902

unknown's avatar
SCRUM  
unknown committed
1903
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1904

1905
int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
unknown's avatar
unknown committed
1906
{
1907
  return do_apply_event(rli, query, q_len);
unknown's avatar
unknown committed
1908 1909 1910
}


1911
int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli,
1912
                                      const char *query_arg, uint32 q_len_arg)
unknown's avatar
unknown committed
1913
{
1914
  LEX_STRING new_db;
unknown's avatar
unknown committed
1915
  int expected_error,actual_error= 0;
1916
  /*
1917 1918 1919
    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
1920
    Query_log_event::do_apply_event()). Same for thd->db.  Thank
1921
    you.
1922
  */
1923
  thd->catalog= catalog_len ? (char *) catalog : (char *)"";
1924 1925 1926
  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' */
1927 1928
  thd->variables.auto_increment_increment= auto_increment_increment;
  thd->variables.auto_increment_offset=    auto_increment_offset;
unknown's avatar
unknown committed
1929

1930
  /*
1931 1932 1933 1934 1935 1936 1937 1938
    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.
1939
  */
1940
  const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
1941 1942
  DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));

1943 1944
  clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli));
  const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
1945

unknown's avatar
unknown committed
1946 1947 1948 1949 1950 1951 1952
  /*
    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
1953
            ::do_apply_event(), then the companion SET also have so
1954
            we don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
1955
  */
1956
  if (rpl_filter->db_ok(thd->db))
1957 1958
  {
    thd->set_time((time_t)when);
unknown's avatar
unknown committed
1959 1960
    thd->query_length= q_len_arg;
    thd->query= (char*)query_arg;
unknown's avatar
unknown committed
1961
    VOID(pthread_mutex_lock(&LOCK_thread_count));
unknown's avatar
unknown committed
1962
    thd->query_id = next_query_id();
1963
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
1964
    thd->variables.pseudo_thread_id= thread_id;		// for temp tables
unknown's avatar
unknown committed
1965
    DBUG_PRINT("query",("%s",thd->query));
1966

unknown's avatar
unknown committed
1967
    if (ignored_error_code((expected_error= error_code)) ||
1968
	!check_expected_error(thd,rli,expected_error))
1969 1970 1971
    {
      if (flags2_inited)
        /*
1972 1973
          all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
          must take their value from flags2.
1974
        */
1975
        thd->options= flags2|(thd->options & ~OPTIONS_WRITTEN_TO_BIN_LOG);
1976 1977
      /*
        else, we are in a 3.23/4.0 binlog; we previously received a
1978 1979
        Rotate_log_event which reset thd->options and sql_mode etc, so
        nothing to do.
1980 1981 1982 1983 1984
      */
      /*
        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
1985 1986 1987
        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).
1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005
      */
      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))))
          {
            /*
2006 2007 2008 2009
              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).
2010
            */
2011
            set_slave_thread_default_charset(thd, rli);
2012 2013 2014 2015 2016
            goto compare_errors;
          }
          thd->update_charset(); // for the charset change to take effect
        }
      }
2017 2018 2019
      if (time_zone_len)
      {
        String tmp(time_zone_str, time_zone_len, &my_charset_bin);
2020
        if (!(thd->variables.time_zone= my_tz_find(thd, &tmp)))
2021 2022 2023 2024 2025 2026
        {
          my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
          thd->variables.time_zone= global_system_variables.time_zone;
          goto compare_errors;
        }
      }
2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
      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;
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054
      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;
      
2055
      /* Execute the query (note that we bypass dispatch_command()) */
unknown's avatar
unknown committed
2056
      mysql_parse(thd, thd->query, thd->query_length);
2057 2058

    }
unknown's avatar
unknown committed
2059 2060
    else
    {
unknown's avatar
unknown committed
2061
      /*
unknown's avatar
unknown committed
2062 2063 2064 2065 2066
        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
2067
      */
unknown's avatar
unknown committed
2068
      if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
2069
        clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli)); /* Can ignore query */
unknown's avatar
unknown committed
2070
      else
2071
      {
2072
        slave_print_msg(ERROR_LEVEL, rli, expected_error, 
unknown's avatar
unknown committed
2073
                          "\
unknown's avatar
unknown committed
2074
Query partially completed on the master (error on master: %d) \
unknown's avatar
unknown committed
2075 2076 2077
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
2078
START SLAVE; . Query: '%s'", expected_error, thd->query);
unknown's avatar
unknown committed
2079 2080 2081 2082
        thd->query_error= 1;
      }
      goto end;
    }
2083

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

2088
compare_errors:
unknown's avatar
unknown committed
2089 2090

     /*
unknown's avatar
unknown committed
2091 2092 2093 2094
      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",
2095
 		       expected_error, thd->net.last_errno));
unknown's avatar
unknown committed
2096
    if ((expected_error != (actual_error= thd->net.last_errno)) &&
2097 2098 2099
 	expected_error &&
 	!ignored_error_code(actual_error) &&
 	!ignored_error_code(expected_error))
unknown's avatar
unknown committed
2100
    {
2101 2102 2103
      slave_print_msg(ERROR_LEVEL, rli, 0,
                      "\
Query caused different errors on master and slave.     \
unknown's avatar
unknown committed
2104
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
unknown's avatar
unknown committed
2105
Default database: '%s'. Query: '%s'",
2106 2107 2108 2109 2110
                      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
2111 2112 2113 2114 2115 2116
      thd->query_error= 1;
    }
    /*
      If we get the same error code as expected, or they should be ignored. 
    */
    else if (expected_error == actual_error ||
2117
 	     ignored_error_code(actual_error))
unknown's avatar
unknown committed
2118 2119
    {
      DBUG_PRINT("info",("error ignored"));
2120
      clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli));
unknown's avatar
unknown committed
2121 2122 2123
    }
    /*
      Other cases: mostly we expected no error and get one.
unknown's avatar
unknown committed
2124
    */
unknown's avatar
unknown committed
2125 2126
    else if (thd->query_error || thd->is_fatal_error)
    {
2127 2128 2129 2130 2131
      slave_print_msg(ERROR_LEVEL, rli, actual_error,
                      "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
2132 2133
      thd->query_error= 1;
    }
2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147

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

2149 2150 2151 2152 2153 2154 2155
      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
2156 2157
  } /* End of if (db_ok(... */

unknown's avatar
unknown committed
2158
end:
2159
  VOID(pthread_mutex_lock(&LOCK_thread_count));
2160 2161 2162 2163 2164 2165
  /*
    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
2166 2167 2168
    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.
2169
  */
unknown's avatar
unknown committed
2170
  thd->catalog= 0;
2171
  thd->set_db(NULL, 0);                 /* will free the current database */
2172
  thd->query= 0;			// just to be sure
unknown's avatar
unknown committed
2173
  thd->query_length= 0;
2174
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
2175
  close_thread_tables(thd);      
2176 2177 2178 2179 2180 2181 2182 2183 2184 2185
  /*
    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
2186
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
2187 2188 2189 2190 2191
  return thd->query_error;
}

int Query_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
2192
  /*
2193 2194 2195
    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.
2196
  */
2197 2198 2199 2200 2201 2202 2203
  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
2204
}
2205 2206


unknown's avatar
SCRUM  
unknown committed
2207
#endif
unknown's avatar
unknown committed
2208

unknown's avatar
unknown committed
2209

2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224
/**************************************************************************
	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
2225
/**************************************************************************
2226
	Start_log_event_v3 methods
unknown's avatar
unknown committed
2227
**************************************************************************/
2228

2229 2230 2231 2232 2233 2234 2235 2236
#ifndef MYSQL_CLIENT
Start_log_event_v3::Start_log_event_v3() :Log_event(), binlog_version(BINLOG_VERSION), artificial_event(0)
{
  created= when;
  memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
#endif

unknown's avatar
unknown committed
2237
/*
2238
  Start_log_event_v3::pack_info()
unknown's avatar
unknown committed
2239
*/
2240

unknown's avatar
SCRUM  
unknown committed
2241
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2242
void Start_log_event_v3::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
2243
{
2244 2245 2246 2247
  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
2248 2249
  pos= int10_to_str(binlog_version, pos, 10);
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
unknown's avatar
unknown committed
2250
}
unknown's avatar
SCRUM  
unknown committed
2251
#endif
2252 2253


unknown's avatar
unknown committed
2254
/*
2255
  Start_log_event_v3::print()
unknown's avatar
unknown committed
2256
*/
unknown's avatar
unknown committed
2257 2258

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
2259
void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2260
{
2261 2262 2263 2264 2265
  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
2266
  if (!print_event_info->short_form)
2267
  {
2268 2269 2270 2271
    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);
2272
    if (created)
2273 2274
      my_b_printf(&cache," at startup");
    my_b_printf(&cache, "\n");
2275
    if (flags & LOG_EVENT_BINLOG_IN_USE_F)
2276 2277
      my_b_printf(&cache, "# Warning: this binlog was not closed properly. "
                  "Most probably mysqld crashed writing it.\n");
2278
  }
2279 2280
  if (!artificial_event && created)
  {
2281
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
2282 2283 2284 2285 2286 2287
    /*
      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
2288
    my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter);
2289
#else
unknown's avatar
unknown committed
2290
    my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter);
2291
#endif
2292
  }
2293
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2294
}
unknown's avatar
unknown committed
2295
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
2296

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

2301 2302 2303
Start_log_event_v3::Start_log_event_v3(const char* buf,
                                       const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
2304
{
2305 2306
  buf+= description_event->common_header_len;
  binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
2307 2308
  memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
	 ST_SERVER_VER_LEN);
unknown's avatar
unknown committed
2309 2310
  // prevent overrun if log is corrupted on disk
  server_version[ST_SERVER_VER_LEN-1]= 0;
2311 2312 2313
  created= uint4korr(buf+ST_CREATED_OFFSET);
  /* We use log_pos to mark if this was an artificial event or not */
  artificial_event= (log_pos == 0);
unknown's avatar
unknown committed
2314 2315
}

2316

unknown's avatar
unknown committed
2317
/*
2318
  Start_log_event_v3::write()
unknown's avatar
unknown committed
2319
*/
2320

2321
#ifndef MYSQL_CLIENT
2322
bool Start_log_event_v3::write(IO_CACHE* file)
2323
{
2324
  char buff[START_V3_HEADER_LEN];
2325 2326 2327
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
  int4store(buff + ST_CREATED_OFFSET,created);
2328 2329
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, (byte*) buff, sizeof(buff)));
2330
}
2331
#endif
2332

2333

unknown's avatar
unknown committed
2334
/*
2335
  Start_log_event_v3::do_apply_event()
2336 2337 2338 2339

  The master started

  IMPLEMENTATION
unknown's avatar
unknown committed
2340 2341 2342 2343
    - 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).
2344 2345

  TODO
2346 2347 2348 2349 2350
    - 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
2351 2352
*/

unknown's avatar
SCRUM  
unknown committed
2353
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2354
int Start_log_event_v3::do_apply_event(RELAY_LOG_INFO const *rli)
2355
{
2356
  DBUG_ENTER("Start_log_event_v3::do_apply_event");
2357
  switch (binlog_version)
2358 2359 2360 2361 2362 2363 2364 2365
  {
  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
2366
    */
2367 2368 2369 2370 2371
    if (created)
    {
      close_temporary_tables(thd);
      cleanup_load_tmpdir();
    }
unknown's avatar
unknown committed
2372 2373
    break;

2374
    /*
unknown's avatar
unknown committed
2375 2376
       Now the older formats; in that case load_tmpdir is cleaned up by the I/O
       thread.
unknown's avatar
unknown committed
2377
    */
2378
  case 1:
2379
    if (strncmp(rli->relay_log.description_event_for_exec->server_version,
2380 2381 2382 2383 2384 2385 2386 2387
                "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
2388
    /*
2389 2390 2391
      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
2392 2393 2394 2395
    */
    break;
  default:
    /* this case is impossible */
2396
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2397
  }
2398
  DBUG_RETURN(0);
2399
}
unknown's avatar
unknown committed
2400
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2401

2402 2403 2404 2405 2406 2407 2408 2409 2410
/***************************************************************************
       Format_description_log_event methods
****************************************************************************/

/*
  Format_description_log_event 1st ctor.

  SYNOPSIS
    Format_description_log_event::Format_description_log_event
2411
      binlog_version              the binlog version for which we want to build
2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
                                  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::
unknown's avatar
unknown committed
2426
Format_description_log_event(uint8 binlog_ver, const char* server_ver)
2427 2428 2429 2430 2431 2432 2433
  :Start_log_event_v3()
{
  created= when;
  binlog_version= binlog_ver;
  switch (binlog_ver) {
  case 4: /* MySQL 5.0 */
    memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
unknown's avatar
unknown committed
2434 2435
    DBUG_EXECUTE_IF("pretend_version_50034_in_binlog",
                    strmov(server_version, "5.0.34"););
2436 2437 2438 2439
    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),
unknown's avatar
unknown committed
2440
                                       MYF(MY_ZEROFILL));
2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457
    /*
      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;
2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476
      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
2477 2478
      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;
2479
      post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493
    }
    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 :
2494
      LOG_EVENT_MINIMAL_HEADER_LEN;
2495 2496 2497 2498 2499 2500 2501 2502 2503
    /*
      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),
2504
                                       MYF(0));
2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526
    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
2527
  calc_server_version_split();
2528 2529 2530 2531 2532 2533 2534 2535
}


/*
  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
2536
  post-header starts.
2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551
  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*
2552
                             description_event)
2553 2554 2555 2556 2557 2558 2559 2560 2561
  :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",
2562
                      common_header_len, number_of_event_types));
2563 2564 2565
  /* If alloc fails, we'll detect it in is_valid() */
  post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
                                      number_of_event_types*
unknown's avatar
unknown committed
2566
                                      sizeof(*post_header_len), MYF(0));
unknown's avatar
unknown committed
2567
  calc_server_version_split();
2568 2569 2570
  DBUG_VOID_RETURN;
}

2571
#ifndef MYSQL_CLIENT
2572 2573 2574 2575 2576 2577 2578 2579 2580 2581
bool Format_description_log_event::write(IO_CACHE* file)
{
  /*
    We don't call Start_log_event_v3::write() because this would make 2
    my_b_safe_write().
  */
  byte buff[FORMAT_DESCRIPTION_HEADER_LEN];
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
  int4store(buff + ST_CREATED_OFFSET,created);
2582
  buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
2583 2584 2585 2586 2587
  memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (byte*) post_header_len,
         LOG_EVENT_TYPES);
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, buff, sizeof(buff)));
}
2588
#endif
2589

2590
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2591
int Format_description_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
2592
{
2593
  DBUG_ENTER("Format_description_log_event::do_apply_event");
2594

unknown's avatar
unknown committed
2595
#ifdef USING_TRANSACTIONS
2596 2597 2598 2599
  /*
    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
2600 2601
    flushing the binlog cache to the binlog. XA guarantees that master has
    rolled back. So we roll back.
2602 2603 2604 2605 2606 2607 2608
    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)
  {
2609 2610 2611 2612 2613 2614
    /* This is not an error (XA is safe), just an information */
    slave_print_msg(INFORMATION_LEVEL, rli, 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."); 
2615
    const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, 1);
2616
  }
unknown's avatar
unknown committed
2617
#endif
2618
  /*
2619
    If this event comes from ourselves, there is no cleaning task to
2620
    perform, we don't call Start_log_event_v3::do_apply_event()
2621
    (this was just to update the log's description event).
2622
  */
2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644
  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);
}

int Format_description_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
  /* save the information describing this binlog */
  delete rli->relay_log.description_event_for_exec;
  rli->relay_log.description_event_for_exec= this;

2645 2646 2647
  if (server_id == (uint32) ::server_id)
  {
    /*
2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660
      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();
2661
    return 0;
2662
  }
2663 2664 2665
  else
  {
    return Log_event::do_update_pos(rli);
2666 2667 2668
  }
}

2669
Log_event::enum_skip_reason
2670
Format_description_log_event::do_shall_skip(RELAY_LOG_INFO *rli)
2671
{
2672
  return Log_event::EVENT_SKIP_NOT;
2673
}
2674

2675 2676
#endif

unknown's avatar
unknown committed
2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707

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


2708
  /**************************************************************************
2709
        Load_log_event methods
2710
   General note about Load_log_event: the binlogging of LOAD DATA INFILE is
2711
   going to be changed in 5.0 (or maybe in 5.1; not decided yet).
2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723
   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).
  **************************************************************************/
2724

unknown's avatar
unknown committed
2725
/*
2726
  Load_log_event::pack_info()
unknown's avatar
unknown committed
2727
*/
2728

unknown's avatar
SCRUM  
unknown committed
2729
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
2730
uint Load_log_event::get_query_buffer_length()
2731
{
unknown's avatar
unknown committed
2732
  return
2733 2734
    5 + db_len + 3 +                        // "use DB; "
    18 + fname_len + 2 +                    // "LOAD DATA INFILE 'file''"
unknown's avatar
unknown committed
2735
    7 +					    // LOCAL
2736
    9 +                                     // " REPLACE or IGNORE "
2737
    13 + table_name_len*2 +                 // "INTO TABLE `table`"
2738 2739 2740 2741
    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'"
2742 2743
    19 + sql_ex.line_start_len*4 + 2 +      // " LINES STARTING BY 'str'"
    15 + 22 +                               // " IGNORE xxx  LINES"
2744
    3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
unknown's avatar
unknown committed
2745
}
2746

unknown's avatar
unknown committed
2747 2748 2749 2750 2751 2752 2753

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)
2754
  {
2755 2756
    pos= strmov(pos, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
2757
    pos= strmov(pos+db_len, "`; ");
2758
  }
2759

unknown's avatar
unknown committed
2760
  pos= strmov(pos, "LOAD DATA ");
unknown's avatar
unknown committed
2761 2762 2763 2764

  if (fn_start)
    *fn_start= pos;

unknown's avatar
unknown committed
2765 2766 2767
  if (check_fname_outside_temp_buf())
    pos= strmov(pos, "LOCAL ");
  pos= strmov(pos, "INFILE '");
2768
  memcpy(pos, fname, fname_len);
unknown's avatar
unknown committed
2769
  pos= strmov(pos+fname_len, "' ");
2770

unknown's avatar
unknown committed
2771
  if (sql_ex.opt_flags & REPLACE_FLAG)
2772
    pos= strmov(pos, " REPLACE ");
unknown's avatar
unknown committed
2773
  else if (sql_ex.opt_flags & IGNORE_FLAG)
2774 2775
    pos= strmov(pos, " IGNORE ");

unknown's avatar
unknown committed
2776 2777 2778 2779 2780 2781
  pos= strmov(pos ,"INTO");

  if (fn_end)
    *fn_end= pos;

  pos= strmov(pos ," TABLE `");
2782 2783 2784
  memcpy(pos, table_name, table_name_len);
  pos+= table_name_len;

unknown's avatar
unknown committed
2785
  /* We have to create all optinal fields as the default is not empty */
2786
  pos= strmov(pos, "` FIELDS TERMINATED BY ");
unknown's avatar
unknown committed
2787 2788 2789 2790 2791
  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);
2792

unknown's avatar
unknown committed
2793 2794
  pos= strmov(pos, " ESCAPED BY ");
  pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len);
2795

unknown's avatar
unknown committed
2796 2797
  pos= strmov(pos, " LINES TERMINATED BY ");
  pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len);
2798 2799
  if (sql_ex.line_start_len)
  {
2800
    pos= strmov(pos, " STARTING BY ");
2801
    pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len);
2802
  }
2803

unknown's avatar
unknown committed
2804
  if ((long) skip_lines > 0)
2805 2806
  {
    pos= strmov(pos, " IGNORE ");
unknown's avatar
unknown committed
2807
    pos= longlong10_to_str((longlong) skip_lines, pos, 10);
2808 2809
    pos= strmov(pos," LINES ");    
  }
2810 2811 2812 2813

  if (num_fields)
  {
    uint i;
unknown's avatar
unknown committed
2814
    const char *field= fields;
2815
    pos= strmov(pos, " (");
2816 2817 2818
    for (i = 0; i < num_fields; i++)
    {
      if (i)
unknown's avatar
unknown committed
2819 2820 2821 2822
      {
        *pos++= ' ';
        *pos++= ',';
      }
2823
      memcpy(pos, field, field_lens[i]);
unknown's avatar
unknown committed
2824 2825
      pos+=   field_lens[i];
      field+= field_lens[i]  + 1;
2826
    }
2827
    *pos++= ')';
2828
  }
2829

unknown's avatar
unknown committed
2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841
  *end= pos;
}


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

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

2846

2847 2848
#ifndef MYSQL_CLIENT

unknown's avatar
unknown committed
2849
/*
2850
  Load_log_event::write_data_header()
unknown's avatar
unknown committed
2851
*/
2852

2853
bool Load_log_event::write_data_header(IO_CACHE* file)
2854
{
2855
  char buf[LOAD_HEADER_LEN];
unknown's avatar
unknown committed
2856
  int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
2857 2858 2859 2860 2861
  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);
2862
  return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN) != 0;
2863
}
2864

2865

unknown's avatar
unknown committed
2866
/*
2867
  Load_log_event::write_data_body()
unknown's avatar
unknown committed
2868
*/
2869

2870
bool Load_log_event::write_data_body(IO_CACHE* file)
2871
{
2872 2873 2874
  if (sql_ex.write_data(file))
    return 1;
  if (num_fields && fields && field_lens)
2875
  {
2876 2877 2878
    if (my_b_safe_write(file, (byte*)field_lens, num_fields) ||
	my_b_safe_write(file, (byte*)fields, field_block_len))
      return 1;
2879
  }
2880 2881 2882
  return (my_b_safe_write(file, (byte*)table_name, table_name_len + 1) ||
	  my_b_safe_write(file, (byte*)db, db_len + 1) ||
	  my_b_safe_write(file, (byte*)fname, fname_len));
2883 2884
}

2885

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

unknown's avatar
unknown committed
2890 2891 2892
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
2893
			       enum enum_duplicates handle_dup,
2894
			       bool ignore, bool using_trans)
2895 2896 2897
  :Log_event(thd_arg, !thd_arg->tmp_table_used ?
	     0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
   thread_id(thd_arg->thread_id),
2898
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
unknown's avatar
unknown committed
2899 2900
   num_fields(0),fields(0),
   field_lens(0),field_block_len(0),
unknown's avatar
unknown committed
2901
   table_name(table_name_arg ? table_name_arg : ""),
2902
   db(db_arg), fname(ex->file_name), local_fname(FALSE)
unknown's avatar
unknown committed
2903 2904 2905
{
  time_t end_time;
  time(&end_time);
2906
  exec_time = (ulong) (end_time  - thd_arg->start_time);
2907 2908 2909
  /* 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
2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922
  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;
2923
    
unknown's avatar
unknown committed
2924
  if (ex->dumpfile)
unknown's avatar
unknown committed
2925
    sql_ex.opt_flags|= DUMPFILE_FLAG;
unknown's avatar
unknown committed
2926
  if (ex->opt_enclosed)
unknown's avatar
unknown committed
2927
    sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
2928

unknown's avatar
unknown committed
2929
  sql_ex.empty_flags= 0;
2930

2931
  switch (handle_dup) {
unknown's avatar
unknown committed
2932
  case DUP_REPLACE:
unknown's avatar
unknown committed
2933
    sql_ex.opt_flags|= REPLACE_FLAG;
unknown's avatar
unknown committed
2934 2935 2936 2937
    break;
  case DUP_UPDATE:				// Impossible here
  case DUP_ERROR:
    break;	
unknown's avatar
unknown committed
2938
  }
2939 2940
  if (ignore)
    sql_ex.opt_flags|= IGNORE_FLAG;
2941

unknown's avatar
unknown committed
2942 2943 2944 2945 2946 2947 2948 2949 2950 2951
  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;
2952
    
unknown's avatar
unknown committed
2953
  skip_lines = ex->skip_lines;
2954

unknown's avatar
unknown committed
2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965
  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);
2966 2967
  }

unknown's avatar
unknown committed
2968 2969 2970
  field_lens = (const uchar*)field_lens_buf.ptr();
  fields = fields_buf.ptr();
}
unknown's avatar
unknown committed
2971
#endif /* !MYSQL_CLIENT */
2972

2973

2974
/*
2975
  Load_log_event::Load_log_event()
2976

unknown's avatar
unknown committed
2977 2978 2979
  NOTE
    The caller must do buf[event_len] = 0 before he starts using the
    constructed event.
2980 2981
*/

2982 2983 2984 2985
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
2986
   table_name(0), db(0), fname(0), local_fname(FALSE)
unknown's avatar
unknown committed
2987
{
unknown's avatar
unknown committed
2988
  DBUG_ENTER("Load_log_event");
2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000
  /*
    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
3001
  DBUG_VOID_RETURN;
3002 3003
}

3004

unknown's avatar
unknown committed
3005
/*
3006
  Load_log_event::copy_log_event()
unknown's avatar
unknown committed
3007
*/
3008

3009
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
3010 3011
                                   int body_offset,
                                   const Format_description_log_event *description_event)
3012
{
3013
  DBUG_ENTER("Load_log_event::copy_log_event");
3014
  uint data_len;
3015
  char* buf_end = (char*)buf + event_len;
3016 3017
  /* this is the beginning of the post-header */
  const char* data_head = buf + description_event->common_header_len;
unknown's avatar
unknown committed
3018
  slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
3019 3020 3021 3022 3023
  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
3024
	  
unknown's avatar
unknown committed
3025
  if ((int) event_len < body_offset)
unknown's avatar
unknown committed
3026
    DBUG_RETURN(1);
3027 3028 3029 3030
  /*
    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.
  */
3031 3032 3033
  if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
                                        buf_end,
                                        buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
unknown's avatar
unknown committed
3034
    DBUG_RETURN(1);
3035
  
3036
  data_len = event_len - body_offset;
3037
  if (num_fields > data_len) // simple sanity check against corruption
unknown's avatar
unknown committed
3038
    DBUG_RETURN(1);
3039
  for (uint i = 0; i < num_fields; i++)
3040
    field_block_len += (uint)field_lens[i] + 1;
3041

unknown's avatar
unknown committed
3042 3043 3044 3045
  fields = (char*)field_lens + num_fields;
  table_name  = fields + field_block_len;
  db = table_name + table_name_len + 1;
  fname = db + db_len + 1;
3046 3047
  fname_len = strlen(fname);
  // null termination is accomplished by the caller doing buf[event_len]=0
3048

unknown's avatar
unknown committed
3049
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3050 3051 3052
}


unknown's avatar
unknown committed
3053
/*
3054
  Load_log_event::print()
unknown's avatar
unknown committed
3055
*/
3056 3057

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3058
void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
3059
{
unknown's avatar
unknown committed
3060
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
3061 3062
}

unknown's avatar
unknown committed
3063

3064
void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
3065
			   bool commented)
unknown's avatar
unknown committed
3066
{
3067 3068
  Write_on_release_cache cache(&print_event_info->head_cache, file_arg);

unknown's avatar
unknown committed
3069
  DBUG_ENTER("Load_log_event::print");
unknown's avatar
unknown committed
3070
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
3071
  {
3072 3073 3074
    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
3075 3076
  }

3077
  bool different_db= 1;
3078
  if (db)
unknown's avatar
unknown committed
3079
  {
3080 3081 3082 3083 3084 3085
    /*
      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
3086
    if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) &&
3087
        !commented)
unknown's avatar
unknown committed
3088
      memcpy(print_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
3089
  }
3090
  
3091
  if (db && db[0] && different_db)
unknown's avatar
unknown committed
3092
    my_b_printf(&cache, "%suse %s%s\n", 
unknown's avatar
unknown committed
3093
            commented ? "# " : "",
3094
            db, print_event_info->delimiter);
unknown's avatar
unknown committed
3095

3096
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
unknown's avatar
unknown committed
3097
    my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
3098 3099
            commented ? "# " : "", (ulong)thread_id,
            print_event_info->delimiter);
3100
  my_b_printf(&cache, "%sLOAD DATA ",
unknown's avatar
unknown committed
3101
              commented ? "# " : "");
3102
  if (check_fname_outside_temp_buf())
3103 3104
    my_b_printf(&cache, "LOCAL ");
  my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
unknown's avatar
unknown committed
3105

unknown's avatar
unknown committed
3106
  if (sql_ex.opt_flags & REPLACE_FLAG)
3107
    my_b_printf(&cache," REPLACE ");
unknown's avatar
unknown committed
3108
  else if (sql_ex.opt_flags & IGNORE_FLAG)
3109
    my_b_printf(&cache," IGNORE ");
unknown's avatar
unknown committed
3110
  
3111 3112 3113
  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
3114

unknown's avatar
unknown committed
3115
  if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
3116 3117 3118
    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
3119
     
3120 3121
  my_b_printf(&cache, " ESCAPED BY ");
  pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len);
unknown's avatar
unknown committed
3122
     
3123 3124
  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
3125

unknown's avatar
unknown committed
3126

3127
  if (sql_ex.line_start)
unknown's avatar
unknown committed
3128
  {
3129 3130
    my_b_printf(&cache," STARTING BY ");
    pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len);
unknown's avatar
unknown committed
3131
  }
3132
  if ((long) skip_lines > 0)
3133
    my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines);
unknown's avatar
unknown committed
3134

3135 3136 3137 3138
  if (num_fields)
  {
    uint i;
    const char* field = fields;
3139
    my_b_printf(&cache, " (");
3140
    for (i = 0; i < num_fields; i++)
unknown's avatar
unknown committed
3141
    {
unknown's avatar
unknown committed
3142
      if (i)
3143 3144
	my_b_printf(&cache, ",");
      my_b_printf(&cache, field);
unknown's avatar
unknown committed
3145
	  
3146
      field += field_lens[i]  + 1;
unknown's avatar
unknown committed
3147
    }
3148
    my_b_printf(&cache, ")");
3149
  }
unknown's avatar
unknown committed
3150

unknown's avatar
unknown committed
3151
  my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
3152
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3153
}
unknown's avatar
unknown committed
3154
#endif /* MYSQL_CLIENT */
3155

3156

unknown's avatar
unknown committed
3157
/*
3158
  Load_log_event::set_fields()
unknown's avatar
unknown committed
3159 3160 3161 3162 3163

  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
3164
*/
3165

3166
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
3167
void Load_log_event::set_fields(const char* affected_db, 
3168 3169
				List<Item> &field_list,
                                Name_resolution_context *context)
unknown's avatar
unknown committed
3170 3171
{
  uint i;
unknown's avatar
unknown committed
3172
  const char* field = fields;
3173
  for (i= 0; i < num_fields; i++)
unknown's avatar
unknown committed
3174
  {
3175 3176
    field_list.push_back(new Item_field(context,
                                        affected_db, table_name, field));
3177
    field+= field_lens[i]  + 1;
unknown's avatar
unknown committed
3178
  }
unknown's avatar
unknown committed
3179
}
unknown's avatar
unknown committed
3180
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
3181 3182


unknown's avatar
SCRUM  
unknown committed
3183
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3184 3185
/*
  Does the data loading job when executing a LOAD DATA on the slave
3186

unknown's avatar
unknown committed
3187
  SYNOPSIS
3188
    Load_log_event::do_apply_event
3189 3190 3191
      net
      rli
      use_rli_only_for_errors	  - if set to 1, rli is provided to
3192
                                  Load_log_event::do_apply_event
3193 3194 3195 3196 3197 3198
                                  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
3199
                                  Execute_load_log_event::do_apply_event).
3200 3201 3202
                                  - if set to 0, rli is provided for
                                  full use, i.e. for error reports and
                                  position advancing.
3203

unknown's avatar
unknown committed
3204 3205
  DESCRIPTION
    Does the data loading job when executing a LOAD DATA on the slave
3206

unknown's avatar
unknown committed
3207
  RETURN VALUE
3208
    0           Success
unknown's avatar
unknown committed
3209 3210
    1    	Failure
*/
3211

3212
int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli,
3213
                                   bool use_rli_only_for_errors)
3214
{
3215 3216 3217 3218
  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);
3219
  DBUG_ASSERT(thd->query == 0);
unknown's avatar
unknown committed
3220
  thd->query_length= 0;                         // Should not be needed
unknown's avatar
unknown committed
3221
  thd->query_error= 0;
3222
  clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli));
3223

3224
  /* see Query_log_event::do_apply_event() and BUG#13360 */
3225
  DBUG_ASSERT(!rli->m_table_map.count());
3226 3227 3228 3229
  /*
    Usually mysql_init_query() is called by mysql_parse(), but we need it here
    as the present method does not call mysql_parse().
  */
unknown's avatar
unknown committed
3230
  mysql_init_query(thd, 0, 0);
unknown's avatar
unknown committed
3231
  if (!use_rli_only_for_errors)
3232
  {
3233 3234
    /*
      Saved for InnoDB, see comment in
3235
      Query_log_event::do_apply_event()
3236
    */
3237
    const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
3238
    DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
3239
  }
3240 3241
 
   /*
3242 3243 3244 3245 3246 3247 3248
    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
3249
    done by testing rules in Create_file_log_event::do_apply_event()
3250 3251 3252
    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
3253 3254 3255 3256 3257 3258 3259 3260


    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
3261
            ::do_apply_event(), then the companion SET also have so
3262
            we don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
3263
  */
3264
  if (rpl_filter->db_ok(thd->db))
3265
  {
3266 3267
    thd->set_time((time_t)when);
    VOID(pthread_mutex_lock(&LOCK_thread_count));
unknown's avatar
unknown committed
3268
    thd->query_id = next_query_id();
3269
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
3270 3271 3272 3273 3274 3275
    /*
      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
3276
    mysql_reset_errors(thd, 0);
3277 3278 3279

    TABLE_LIST tables;
    bzero((char*) &tables,sizeof(tables));
unknown's avatar
unknown committed
3280
    tables.db= thd->strmake(thd->db, thd->db_length);
3281
    tables.alias = tables.table_name = (char*) table_name;
3282
    tables.lock_type = TL_WRITE;
unknown's avatar
unknown committed
3283
    tables.updating= 1;
unknown's avatar
unknown committed
3284

3285
    // the table will be opened in mysql_load    
3286
    if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db, &tables))
3287 3288 3289 3290 3291 3292 3293 3294
    {
      // 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
3295
      char *end;
unknown's avatar
unknown committed
3296
      enum enum_duplicates handle_dup;
3297
      bool ignore= 0;
unknown's avatar
unknown committed
3298 3299
      char *load_data_query;

unknown's avatar
unknown committed
3300
      /*
unknown's avatar
unknown committed
3301 3302
        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
3303
      */
unknown's avatar
unknown committed
3304
      if (!(load_data_query= (char *)thd->alloc(get_query_buffer_length() + 1)))
unknown's avatar
unknown committed
3305
      {
unknown's avatar
unknown committed
3306 3307 3308 3309 3310
        /*
          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
3311
      }
unknown's avatar
unknown committed
3312 3313 3314 3315 3316 3317 3318

      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
3319
      if (sql_ex.opt_flags & REPLACE_FLAG)
3320
      {
unknown's avatar
unknown committed
3321
	handle_dup= DUP_REPLACE;
3322
      }
unknown's avatar
unknown committed
3323
      else if (sql_ex.opt_flags & IGNORE_FLAG)
3324 3325 3326 3327
      {
        ignore= 1;
        handle_dup= DUP_ERROR;
      }
unknown's avatar
unknown committed
3328
      else
unknown's avatar
unknown committed
3329
      {
unknown's avatar
unknown committed
3330
        /*
unknown's avatar
unknown committed
3331
	  When replication is running fine, if it was DUP_ERROR on the
3332
          master then we could choose IGNORE here, because if DUP_ERROR
unknown's avatar
unknown committed
3333
          suceeded on master, and data is identical on the master and slave,
3334
          then there should be no uniqueness errors on slave, so IGNORE is
unknown's avatar
unknown committed
3335
          the same as DUP_ERROR. But in the unlikely case of uniqueness errors
unknown's avatar
unknown committed
3336 3337 3338
          (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
3339 3340

          If reading from net (a 3.23 master), mysql_load() will change this
3341
          to IGNORE.
unknown's avatar
unknown committed
3342 3343
        */
        handle_dup= DUP_ERROR;
unknown's avatar
unknown committed
3344
      }
3345 3346 3347 3348 3349 3350 3351 3352 3353 3354
      /*
        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
3355

unknown's avatar
unknown committed
3356
      sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
3357 3358 3359 3360 3361
      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
3362 3363 3364 3365 3366
      ex.field_term= &field_term;
      ex.enclosed= &enclosed;
      ex.line_term= &line_term;
      ex.line_start= &line_start;
      ex.escaped= &escaped;
3367 3368 3369 3370 3371 3372

      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
3373
      List<Item> field_list;
3374 3375
      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
3376
      thd->variables.pseudo_thread_id= thread_id;
3377 3378 3379 3380 3381 3382 3383 3384 3385
      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
3386
      /*
3387
        It is safe to use tmp_list twice because we are not going to
unknown's avatar
unknown committed
3388 3389
        update it inside mysql_load().
      */
3390 3391
      List<Item> tmp_list;
      if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
3392
                     handle_dup, ignore, net != 0))
unknown's avatar
unknown committed
3393
        thd->query_error= 1;
3394
      if (thd->cuted_fields)
unknown's avatar
unknown committed
3395
      {
unknown's avatar
unknown committed
3396
	/* log_pos is the position of the LOAD event in the master log */
unknown's avatar
unknown committed
3397 3398 3399 3400 3401 3402 3403
        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
3404
      }
3405 3406 3407
      if (net)
        net->pkt_nr= thd->net.pkt_nr;
    }
3408 3409
  }
  else
3410 3411 3412 3413 3414 3415 3416 3417 3418
  {
    /*
      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
3419 3420

error:
3421
  thd->net.vio = 0; 
unknown's avatar
unknown committed
3422
  const char *remember_db= thd->db;
unknown's avatar
unknown committed
3423
  VOID(pthread_mutex_lock(&LOCK_thread_count));
unknown's avatar
unknown committed
3424
  thd->catalog= 0;
3425
  thd->set_db(NULL, 0);                   /* will free the current database */
unknown's avatar
unknown committed
3426
  thd->query= 0;
unknown's avatar
unknown committed
3427
  thd->query_length= 0;
unknown's avatar
unknown committed
3428
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
3429 3430 3431
  close_thread_tables(thd);
  if (thd->query_error)
  {
3432
    /* this err/sql_errno code is copy-paste from net_send_error() */
unknown's avatar
unknown committed
3433 3434 3435 3436 3437 3438 3439 3440 3441
    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);       
    }
3442
    slave_print_msg(ERROR_LEVEL, rli, sql_errno,"\
unknown's avatar
unknown committed
3443
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
3444
                    err, (char*)table_name, print_slave_db_safe(remember_db));
unknown's avatar
unknown committed
3445
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3446 3447
    return 1;
  }
unknown's avatar
unknown committed
3448
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3449
	    
3450
  if (thd->is_fatal_error)
3451
  {
3452
    slave_print_msg(ERROR_LEVEL, rli, ER_UNKNOWN_ERROR, "\
unknown's avatar
unknown committed
3453
Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
3454
                    (char*)table_name, print_slave_db_safe(remember_db));
3455 3456 3457
    return 1;
  }

3458
  return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) ); 
3459
}
unknown's avatar
SCRUM  
unknown committed
3460
#endif
3461 3462


unknown's avatar
unknown committed
3463
/**************************************************************************
unknown's avatar
unknown committed
3464
  Rotate_log_event methods
unknown's avatar
unknown committed
3465
**************************************************************************/
3466

unknown's avatar
unknown committed
3467
/*
3468
  Rotate_log_event::pack_info()
unknown's avatar
unknown committed
3469
*/
3470

unknown's avatar
SCRUM  
unknown committed
3471
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3472
void Rotate_log_event::pack_info(Protocol *protocol)
3473
{
unknown's avatar
unknown committed
3474
  char buf1[256], buf[22];
unknown's avatar
unknown committed
3475
  String tmp(buf1, sizeof(buf1), log_cs);
3476
  tmp.length(0);
unknown's avatar
unknown committed
3477
  tmp.append(new_log_ident, ident_len);
3478
  tmp.append(STRING_WITH_LEN(";pos="));
unknown's avatar
unknown committed
3479 3480
  tmp.append(llstr(pos,buf));
  protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
3481
}
unknown's avatar
SCRUM  
unknown committed
3482
#endif
3483

3484

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

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3490
void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3491
{
3492
  char buf[22];
3493 3494
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);
3495

unknown's avatar
unknown committed
3496
  if (print_event_info->short_form)
3497
    return;
3498 3499
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\tRotate to ");
3500
  if (new_log_ident)
3501 3502
    my_b_write(&cache, (byte*) new_log_ident, (uint)ident_len);
  my_b_printf(&cache, "  pos: %s\n", llstr(pos, buf));
3503
}
unknown's avatar
unknown committed
3504
#endif /* MYSQL_CLIENT */
3505 3506


3507

unknown's avatar
unknown committed
3508
/*
3509
  Rotate_log_event::Rotate_log_event() (2 constructors)
unknown's avatar
unknown committed
3510
*/
3511

3512 3513

#ifndef MYSQL_CLIENT
3514
Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
3515 3516 3517 3518 3519 3520 3521 3522
                                   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];
3523
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
unknown's avatar
unknown committed
3524 3525
  DBUG_PRINT("enter",("new_log_ident: %s  pos: %s  flags: %lu", new_log_ident_arg,
                      llstr(pos_arg, buff), (ulong) flags));
3526 3527
#endif
  if (flags & DUP_NAME)
3528
    new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME));
3529 3530 3531 3532 3533
  DBUG_VOID_RETURN;
}
#endif


3534 3535
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
                                   const Format_description_log_event* description_event)
unknown's avatar
unknown committed
3536
  :Log_event(buf, description_event) ,new_log_ident(0), flags(DUP_NAME)
3537
{
3538
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
3539
  // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
3540 3541
  uint8 header_size= description_event->common_header_len;
  uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
3542 3543
  uint ident_offset;
  if (event_len < header_size)
unknown's avatar
unknown committed
3544
    DBUG_VOID_RETURN;
3545
  buf += header_size;
3546 3547 3548 3549
  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; 
3550
  set_if_smaller(ident_len,FN_REFLEN-1);
3551
  new_log_ident= my_strndup(buf + ident_offset, (uint) ident_len, MYF(MY_WME));
3552
  DBUG_PRINT("debug", ("new_log_ident: '%s'", new_log_ident));
unknown's avatar
unknown committed
3553
  DBUG_VOID_RETURN;
3554
}
3555 3556


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

3561
#ifndef MYSQL_CLIENT
3562
bool Rotate_log_event::write(IO_CACHE* file)
3563
{
3564
  char buf[ROTATE_HEADER_LEN];
unknown's avatar
unknown committed
3565
  int8store(buf + R_POS_OFFSET, pos);
3566 3567 3568
  return (write_header(file, ROTATE_HEADER_LEN + ident_len) ||
          my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) ||
          my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len));
3569
}
3570
#endif
3571

3572

unknown's avatar
unknown committed
3573
/*
3574
  Rotate_log_event::do_apply_event()
3575

3576
  Got a rotate log event from the master
3577

3578 3579 3580
  IMPLEMENTATION
    This is mainly used so that we can later figure out the logname and
    position for the master.
3581

3582
    We can't rotate the slave's BINlog as this will cause infinitive rotations
3583
    in a A -> B -> A setup.
3584
    The NOTES below is a wrong comment which will disappear when 4.1 is merged.
3585 3586 3587

  RETURN VALUES
    0	ok
unknown's avatar
unknown committed
3588
*/
3589

unknown's avatar
SCRUM  
unknown committed
3590
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3591
int Rotate_log_event::do_update_pos(RELAY_LOG_INFO *rli)
3592
{
3593
  DBUG_ENTER("Rotate_log_event::do_update_pos");
3594 3595 3596 3597
#ifndef DBUG_OFF
  char buf[32];
#endif

3598 3599
  DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
                      (ulong) this->server_id, (ulong) ::server_id));
3600 3601
  DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
  DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
3602 3603

  pthread_mutex_lock(&rli->data_lock);
3604
  rli->event_relay_log_pos= my_b_tell(rli->cur_log);
unknown's avatar
unknown committed
3605
  /*
3606 3607 3608 3609
    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
3610 3611 3612 3613 3614
    BEGIN
    ...
    ROTATE (a fake one)
    ...
    COMMIT or ROLLBACK
3615 3616 3617 3618

    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
3619
    relay log, which shall not change the group positions.
unknown's avatar
unknown committed
3620
  */
3621
  if ((server_id != ::server_id || rli->replicate_same_server_id) &&
3622
      !rli->is_in_group())
unknown's avatar
unknown committed
3623
  {
3624 3625 3626 3627
    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
3628 3629
    memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
    rli->notify_group_master_log_name_update();
3630
    rli->group_master_log_pos= pos;
3631 3632 3633
    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();
3634
    rli->group_relay_log_pos= rli->event_relay_log_pos;
3635 3636
    DBUG_PRINT("info", ("new group_master_log_name: '%s'  "
                        "new group_master_log_pos: %lu",
3637
                        rli->group_master_log_name,
unknown's avatar
unknown committed
3638
                        (ulong) rli->group_master_log_pos));
3639
    /*
3640 3641
      Reset thd->options and sql_mode etc, because this could be the signal of
      a master's downgrade from 5.0 to 4.0.
3642 3643 3644 3645 3646
      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);
3647
    set_slave_thread_default_charset(thd, rli);
3648
    thd->variables.sql_mode= global_system_variables.sql_mode;
3649 3650
    thd->variables.auto_increment_increment=
      thd->variables.auto_increment_offset= 1;
unknown's avatar
unknown committed
3651
  }
3652 3653 3654
  pthread_mutex_unlock(&rli->data_lock);
  pthread_cond_broadcast(&rli->data_cond);
  flush_relay_log_info(rli);
3655

3656
  DBUG_RETURN(0);
3657
}
3658

3659 3660

Log_event::enum_skip_reason
3661
Rotate_log_event::do_shall_skip(RELAY_LOG_INFO *rli)
3662
{
3663
  enum_skip_reason reason= Log_event::do_shall_skip(rli);
3664 3665

  switch (reason) {
3666
  case Log_event::EVENT_SKIP_NOT:
3667
  case Log_event::EVENT_SKIP_COUNT:
3668
    return Log_event::EVENT_SKIP_NOT;
3669

3670 3671
  case Log_event::EVENT_SKIP_IGNORE:
    return Log_event::EVENT_SKIP_IGNORE;
3672 3673
  }
  DBUG_ASSERT(0);
unknown's avatar
unknown committed
3674
  return Log_event::EVENT_SKIP_NOT;             // To keep compiler happy
3675 3676
}

unknown's avatar
SCRUM  
unknown committed
3677
#endif
3678 3679


unknown's avatar
unknown committed
3680
/**************************************************************************
unknown's avatar
unknown committed
3681
	Intvar_log_event methods
unknown's avatar
unknown committed
3682
**************************************************************************/
3683

unknown's avatar
unknown committed
3684
/*
3685
  Intvar_log_event::pack_info()
unknown's avatar
unknown committed
3686
*/
3687

unknown's avatar
SCRUM  
unknown committed
3688
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3689
void Intvar_log_event::pack_info(Protocol *protocol)
3690
{
unknown's avatar
unknown committed
3691 3692
  char buf[256], *pos;
  pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
unknown's avatar
unknown committed
3693
  *pos++= '=';
unknown's avatar
unknown committed
3694
  pos= longlong10_to_str(val, pos, -10);
unknown's avatar
unknown committed
3695
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
3696
}
unknown's avatar
SCRUM  
unknown committed
3697
#endif
3698

unknown's avatar
unknown committed
3699

unknown's avatar
unknown committed
3700
/*
3701
  Intvar_log_event::Intvar_log_event()
unknown's avatar
unknown committed
3702
*/
3703

3704 3705 3706
Intvar_log_event::Intvar_log_event(const char* buf,
                                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3707
{
3708 3709 3710
  buf+= description_event->common_header_len;
  type= buf[I_TYPE_OFFSET];
  val= uint8korr(buf+I_VAL_OFFSET);
3711 3712
}

3713

unknown's avatar
unknown committed
3714
/*
3715
  Intvar_log_event::get_var_type_name()
unknown's avatar
unknown committed
3716
*/
3717 3718

const char* Intvar_log_event::get_var_type_name()
3719
{
3720 3721 3722 3723 3724
  switch(type) {
  case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID";
  case INSERT_ID_EVENT: return "INSERT_ID";
  default: /* impossible */ return "UNKNOWN";
  }
3725 3726
}

unknown's avatar
unknown committed
3727

unknown's avatar
unknown committed
3728
/*
3729
  Intvar_log_event::write()
unknown's avatar
unknown committed
3730
*/
3731

3732
#ifndef MYSQL_CLIENT
3733
bool Intvar_log_event::write(IO_CACHE* file)
3734
{
3735 3736
  byte buf[9];
  buf[I_TYPE_OFFSET]= (byte) type;
3737
  int8store(buf + I_VAL_OFFSET, val);
3738 3739
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3740
}
3741
#endif
3742

3743

unknown's avatar
unknown committed
3744
/*
3745
  Intvar_log_event::print()
unknown's avatar
unknown committed
3746
*/
3747 3748

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3749
void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3750
{
3751 3752 3753
  char llbuff[22];
  const char *msg;
  LINT_INIT(msg);
3754 3755
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);
3756

unknown's avatar
unknown committed
3757
  if (!print_event_info->short_form)
3758
  {
3759 3760
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tIntvar\n");
3761
  }
3762

3763
  my_b_printf(&cache, "SET ");
3764 3765 3766 3767 3768 3769 3770
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    msg="LAST_INSERT_ID";
    break;
  case INSERT_ID_EVENT:
    msg="INSERT_ID";
    break;
3771 3772 3773 3774
  case INVALID_INT_EVENT:
  default: // cannot happen
    msg="INVALID_INT";
    break;
3775
  }
unknown's avatar
unknown committed
3776 3777
  my_b_printf(&cache, "%s=%s%s\n",
              msg, llstr(val,llbuff), print_event_info->delimiter);
3778
}
3779
#endif
3780

3781

unknown's avatar
unknown committed
3782
/*
3783
  Intvar_log_event::do_apply_event()
unknown's avatar
unknown committed
3784
*/
3785

unknown's avatar
SCRUM  
unknown committed
3786
#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
3787
int Intvar_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
3788
{
3789 3790 3791 3792 3793 3794
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
  const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT);

3795 3796
  switch (type) {
  case LAST_INSERT_ID_EVENT:
3797 3798
    thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
    thd->first_successful_insert_id_in_prev_stmt= val;
3799 3800
    break;
  case INSERT_ID_EVENT:
3801
    thd->force_one_auto_inc_interval(val);
3802 3803
    break;
  }
3804 3805 3806 3807 3808
  return 0;
}

int Intvar_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
3809
  rli->inc_event_relay_log_pos();
3810
  return 0;
3811
}
3812 3813 3814


Log_event::enum_skip_reason
3815
Intvar_log_event::do_shall_skip(RELAY_LOG_INFO *rli)
3816 3817
{
  /*
3818 3819 3820 3821 3822 3823
    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.
3824 3825
  */
  if (rli->slave_skip_counter == 1)
3826
    return Log_event::EVENT_SKIP_IGNORE;
3827
  else
3828
    return Log_event::do_shall_skip(rli);
3829 3830
}

unknown's avatar
SCRUM  
unknown committed
3831
#endif
3832

3833

unknown's avatar
unknown committed
3834
/**************************************************************************
3835
  Rand_log_event methods
unknown's avatar
unknown committed
3836
**************************************************************************/
3837

unknown's avatar
SCRUM  
unknown committed
3838
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3839
void Rand_log_event::pack_info(Protocol *protocol)
3840
{
unknown's avatar
unknown committed
3841 3842 3843 3844 3845
  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);
3846
  protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
3847
}
unknown's avatar
SCRUM  
unknown committed
3848
#endif
3849 3850


3851 3852 3853
Rand_log_event::Rand_log_event(const char* buf,
                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
3854
{
3855 3856 3857
  buf+= description_event->common_header_len;
  seed1= uint8korr(buf+RAND_SEED1_OFFSET);
  seed2= uint8korr(buf+RAND_SEED2_OFFSET);
3858 3859
}

3860

3861
#ifndef MYSQL_CLIENT
3862
bool Rand_log_event::write(IO_CACHE* file)
3863
{
3864
  byte buf[16];
3865 3866
  int8store(buf + RAND_SEED1_OFFSET, seed1);
  int8store(buf + RAND_SEED2_OFFSET, seed2);
3867 3868
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
3869
}
3870
#endif
3871

3872 3873

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3874
void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3875
{
3876 3877 3878
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
3879
  char llbuff[22],llbuff2[22];
unknown's avatar
unknown committed
3880
  if (!print_event_info->short_form)
3881
  {
3882 3883
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tRand\n");
3884
  }
unknown's avatar
unknown committed
3885 3886 3887
  my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
              llstr(seed1, llbuff),llstr(seed2, llbuff2),
              print_event_info->delimiter);
3888
}
unknown's avatar
unknown committed
3889
#endif /* MYSQL_CLIENT */
3890

3891

unknown's avatar
SCRUM  
unknown committed
3892
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3893
int Rand_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
3894
{
3895 3896 3897 3898 3899 3900
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
  const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT);

unknown's avatar
unknown committed
3901 3902
  thd->rand.seed1= (ulong) seed1;
  thd->rand.seed2= (ulong) seed2;
3903 3904 3905 3906 3907
  return 0;
}

int Rand_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
3908
  rli->inc_event_relay_log_pos();
3909 3910
  return 0;
}
3911

3912 3913

Log_event::enum_skip_reason
3914
Rand_log_event::do_shall_skip(RELAY_LOG_INFO *rli)
3915 3916
{
  /*
3917 3918 3919 3920 3921 3922
    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.
3923 3924
  */
  if (rli->slave_skip_counter == 1)
3925
    return Log_event::EVENT_SKIP_IGNORE;
3926
  else
3927
    return Log_event::do_shall_skip(rli);
3928 3929
}

unknown's avatar
unknown committed
3930
#endif /* !MYSQL_CLIENT */
3931

unknown's avatar
unknown committed
3932

unknown's avatar
unknown committed
3933 3934 3935 3936 3937 3938 3939
/**************************************************************************
  Xid_log_event methods
**************************************************************************/

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Xid_log_event::pack_info(Protocol *protocol)
{
3940 3941
  char buf[128], *pos;
  pos= strmov(buf, "COMMIT /* xid=");
3942
  pos= longlong10_to_str(xid, pos, 10);
3943
  pos= strmov(pos, " */");
unknown's avatar
unknown committed
3944 3945 3946 3947
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
}
#endif

unknown's avatar
unknown committed
3948 3949 3950 3951 3952 3953 3954 3955
/*
  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
*/
3956 3957 3958 3959

Xid_log_event::
Xid_log_event(const char* buf,
              const Format_description_log_event *description_event)
unknown's avatar
unknown committed
3960 3961 3962
  :Log_event(buf, description_event)
{
  buf+= description_event->common_header_len;
3963
  memcpy((char*) &xid, buf, sizeof(xid));
unknown's avatar
unknown committed
3964 3965 3966
}


3967
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
3968 3969 3970 3971 3972
bool Xid_log_event::write(IO_CACHE* file)
{
  return write_header(file, sizeof(xid)) ||
         my_b_safe_write(file, (byte*) &xid, sizeof(xid));
}
3973
#endif
unknown's avatar
unknown committed
3974 3975 3976


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3977
void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
3978
{
3979 3980 3981
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
3982
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
3983
  {
unknown's avatar
unknown committed
3984 3985 3986
    char buf[64];
    longlong10_to_str(xid, buf, 10);

3987 3988
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tXid = %s\n", buf);
unknown's avatar
unknown committed
3989
  }
unknown's avatar
unknown committed
3990
  my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
3991 3992 3993 3994 3995
}
#endif /* MYSQL_CLIENT */


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3996
int Xid_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
unknown's avatar
unknown committed
3997
{
3998
  /* For a slave Xid_log_event is COMMIT */
3999 4000
  general_log_print(thd, COM_QUERY,
                    "COMMIT /* implicit, from Xid_log_event */");
4001
  return end_trans(thd, COMMIT);
unknown's avatar
unknown committed
4002 4003 4004 4005
}
#endif /* !MYSQL_CLIENT */


unknown's avatar
unknown committed
4006
/**************************************************************************
4007
  User_var_log_event methods
unknown's avatar
unknown committed
4008
**************************************************************************/
unknown's avatar
unknown committed
4009

unknown's avatar
unknown committed
4010
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
4011 4012 4013
void User_var_log_event::pack_info(Protocol* protocol)
{
  char *buf= 0;
4014
  uint val_offset= 4 + name_len;
unknown's avatar
unknown committed
4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029
  uint event_len= val_offset;

  if (is_null)
  {
    buf= my_malloc(val_offset + 5, MYF(MY_WME));
    strmov(buf + val_offset, "NULL");
    event_len= val_offset + 4;
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
      buf= my_malloc(val_offset + FLOATING_POINT_BUFFER, MYF(MY_WME));
4030 4031
      event_len+= my_sprintf(buf + val_offset,
			     (buf + val_offset, "%.14g", real_val));
unknown's avatar
unknown committed
4032 4033 4034 4035 4036
      break;
    case INT_RESULT:
      buf= my_malloc(val_offset + 22, MYF(MY_WME));
      event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
      break;
unknown's avatar
unknown committed
4037 4038 4039 4040 4041 4042 4043 4044 4045 4046
    case DECIMAL_RESULT:
    {
      buf= my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH, MYF(MY_WME));
      String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin);
      my_decimal dec;
      binary2my_decimal(E_DEC_FATAL_ERROR, val+2, &dec, val[0], val[1]);
      my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
      event_len= str.length() + val_offset;
      break;
    } 
unknown's avatar
unknown committed
4047
    case STRING_RESULT:
4048 4049 4050 4051 4052 4053 4054 4055 4056 4057
      /* 15 is for 'COLLATE' and other chars */
      buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME));
      CHARSET_INFO *cs;
      if (!(cs= get_charset(charset_number, MYF(0))))
      {
        strmov(buf+val_offset, "???");
        event_len+= 3;
      }
      else
      {
4058 4059 4060
        char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
        p= str_to_hex(p, val, val_len);
        p= strxmov(p, " COLLATE ", cs->name, NullS);
4061 4062
        event_len= p-buf;
      }
unknown's avatar
unknown committed
4063
      break;
4064
    case ROW_RESULT:
unknown's avatar
unknown committed
4065
    default:
unknown's avatar
unknown committed
4066 4067 4068 4069 4070
      DBUG_ASSERT(1);
      return;
    }
  }
  buf[0]= '@';
4071 4072 4073 4074
  buf[1]= '`';
  buf[2+name_len]= '`';
  buf[3+name_len]= '=';
  memcpy(buf+2, name, name_len);
4075
  protocol->store(buf, event_len, &my_charset_bin);
unknown's avatar
unknown committed
4076 4077
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
unknown's avatar
unknown committed
4078
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
4079 4080


4081 4082 4083 4084
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
4085
{
4086
  buf+= description_event->common_header_len;
unknown's avatar
unknown committed
4087 4088
  name_len= uint4korr(buf);
  name= (char *) buf + UV_NAME_LEN_SIZE;
4089 4090
  buf+= UV_NAME_LEN_SIZE + name_len;
  is_null= (bool) *buf;
unknown's avatar
unknown committed
4091 4092 4093
  if (is_null)
  {
    type= STRING_RESULT;
4094
    charset_number= my_charset_bin.number;
unknown's avatar
unknown committed
4095 4096 4097 4098 4099
    val_len= 0;
    val= 0;  
  }
  else
  {
4100 4101 4102
    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
4103
		       UV_CHARSET_NUMBER_SIZE);
4104 4105
    val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
		   UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
unknown's avatar
unknown committed
4106 4107 4108 4109
  }
}


4110
#ifndef MYSQL_CLIENT
4111
bool User_var_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
4112 4113 4114 4115
{
  char buf[UV_NAME_LEN_SIZE];
  char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + 
	    UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
unknown's avatar
unknown committed
4116
  char buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
4117
  uint buf1_length;
4118
  ulong event_length;
4119

unknown's avatar
unknown committed
4120
  int4store(buf, name_len);
4121 4122 4123 4124 4125 4126 4127
  
  if ((buf1[0]= is_null))
  {
    buf1_length= 1;
    val_len= 0;
  }    
  else
unknown's avatar
unknown committed
4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138
  {
    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
4139 4140 4141 4142 4143 4144
    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;
4145
      decimal2bin((decimal_t*)val, buf2+2, buf2[0], buf2[1]);
unknown's avatar
unknown committed
4146 4147 4148
      val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
      break;
    }
unknown's avatar
unknown committed
4149 4150 4151
    case STRING_RESULT:
      pos= val;
      break;
4152
    case ROW_RESULT:
unknown's avatar
unknown committed
4153
    default:
unknown's avatar
unknown committed
4154 4155 4156
      DBUG_ASSERT(1);
      return 0;
    }
unknown's avatar
unknown committed
4157 4158
    int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
    buf1_length= 10;
unknown's avatar
unknown committed
4159
  }
4160 4161 4162 4163 4164 4165

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

  return (write_header(file, event_length) ||
          my_b_safe_write(file, (byte*) buf, sizeof(buf))   ||
4166 4167 4168
	  my_b_safe_write(file, (byte*) name, name_len)     ||
	  my_b_safe_write(file, (byte*) buf1, buf1_length) ||
	  my_b_safe_write(file, (byte*) pos, val_len));
unknown's avatar
unknown committed
4169
}
4170
#endif
unknown's avatar
unknown committed
4171

4172

unknown's avatar
unknown committed
4173
/*
unknown's avatar
unknown committed
4174
  User_var_log_event::print()
unknown's avatar
unknown committed
4175
*/
unknown's avatar
unknown committed
4176 4177

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4178
void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
4179
{
4180 4181 4182
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4183
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
4184
  {
4185 4186
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tUser_var\n");
unknown's avatar
unknown committed
4187 4188
  }

4189 4190 4191
  my_b_printf(&cache, "SET @`");
  my_b_write(&cache, (byte*) name, (uint) (name_len));
  my_b_printf(&cache, "`");
unknown's avatar
unknown committed
4192 4193 4194

  if (is_null)
  {
unknown's avatar
unknown committed
4195
    my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
4196 4197 4198 4199 4200 4201 4202
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
unknown's avatar
unknown committed
4203
      my_b_printf(&cache, ":=%.14g%s\n", real_val, print_event_info->delimiter);
unknown's avatar
unknown committed
4204 4205 4206 4207
      break;
    case INT_RESULT:
      char int_buf[22];
      longlong10_to_str(uint8korr(val), int_buf, -10);
unknown's avatar
unknown committed
4208
      my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4209
      break;
unknown's avatar
unknown committed
4210 4211 4212 4213 4214 4215
    case DECIMAL_RESULT:
    {
      char str_buf[200];
      int str_len= sizeof(str_buf) - 1;
      int precision= (int)val[0];
      int scale= (int)val[1];
4216 4217
      decimal_digit_t dec_buf[10];
      decimal_t dec;
unknown's avatar
unknown committed
4218 4219 4220 4221 4222 4223
      dec.len= 10;
      dec.buf= dec_buf;

      bin2decimal(val+2, &dec, precision, scale);
      decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
      str_buf[str_len]= 0;
unknown's avatar
unknown committed
4224
      my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4225 4226
      break;
    }
unknown's avatar
unknown committed
4227
    case STRING_RESULT:
4228
    {
4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242
      /*
        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.
      */
4243 4244 4245 4246
      char *hex_str;
      CHARSET_INFO *cs;

      if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte
4247
        break; // no error, as we are 'void'
4248
      str_to_hex(hex_str, val, val_len);
4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259
      /*
        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
4260
        my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
4261
      else
unknown's avatar
unknown committed
4262 4263 4264
        my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
                    cs->csname, hex_str, cs->name,
                    print_event_info->delimiter);
4265
      my_afree(hex_str);
4266
    }
unknown's avatar
unknown committed
4267
      break;
4268
    case ROW_RESULT:
unknown's avatar
unknown committed
4269
    default:
unknown's avatar
unknown committed
4270
      DBUG_ASSERT(1);
unknown's avatar
unknown committed
4271 4272 4273 4274
      return;
    }
  }
}
unknown's avatar
SCRUM  
unknown committed
4275
#endif
4276

unknown's avatar
unknown committed
4277

unknown's avatar
unknown committed
4278
/*
4279
  User_var_log_event::do_apply_event()
unknown's avatar
unknown committed
4280
*/
unknown's avatar
unknown committed
4281

unknown's avatar
unknown committed
4282
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4283
int User_var_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
unknown's avatar
unknown committed
4284 4285
{
  Item *it= 0;
4286 4287 4288
  CHARSET_INFO *charset;
  if (!(charset= get_charset(charset_number, MYF(MY_WME))))
    return 1;
unknown's avatar
unknown committed
4289 4290 4291
  LEX_STRING user_var_name;
  user_var_name.str= name;
  user_var_name.length= name_len;
4292 4293
  double real_val;
  longlong int_val;
unknown's avatar
unknown committed
4294

4295 4296 4297 4298 4299 4300
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
  const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT);

unknown's avatar
unknown committed
4301 4302 4303 4304 4305 4306 4307 4308 4309
  if (is_null)
  {
    it= new Item_null();
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      float8get(real_val, val);
unknown's avatar
unknown committed
4310
      it= new Item_float(real_val);
4311
      val= (char*) &real_val;		// Pointer to value in native format
4312
      val_len= 8;
unknown's avatar
unknown committed
4313 4314
      break;
    case INT_RESULT:
4315 4316 4317
      int_val= (longlong) uint8korr(val);
      it= new Item_int(int_val);
      val= (char*) &int_val;		// Pointer to value in native format
4318
      val_len= 8;
unknown's avatar
unknown committed
4319
      break;
unknown's avatar
unknown committed
4320 4321 4322 4323 4324 4325 4326 4327
    case DECIMAL_RESULT:
    {
      Item_decimal *dec= new Item_decimal(val+2, val[0], val[1]);
      it= dec;
      val= (char *)dec->val_decimal(NULL);
      val_len= sizeof(my_decimal);
      break;
    }
unknown's avatar
unknown committed
4328 4329 4330
    case STRING_RESULT:
      it= new Item_string(val, val_len, charset);
      break;
4331
    case ROW_RESULT:
unknown's avatar
unknown committed
4332
    default:
unknown's avatar
unknown committed
4333 4334 4335 4336 4337
      DBUG_ASSERT(1);
      return 0;
    }
  }
  Item_func_set_user_var e(user_var_name, it);
4338 4339 4340 4341
  /*
    Item_func_set_user_var can't substitute something else on its place =>
    0 can be passed as last argument (reference on item)
  */
4342
  e.fix_fields(thd, 0);
unknown's avatar
unknown committed
4343 4344 4345 4346 4347
  /*
    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
4348
  e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
unknown's avatar
unknown committed
4349
  free_root(thd->mem_root,0);
unknown's avatar
unknown committed
4350

4351 4352 4353 4354 4355
  return 0;
}

int User_var_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
4356
  rli->inc_event_relay_log_pos();
unknown's avatar
unknown committed
4357 4358
  return 0;
}
4359

4360
Log_event::enum_skip_reason
4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375
User_var_log_event::do_shall_skip(RELAY_LOG_INFO *rli)
{
  /*
    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
4376
#endif /* !MYSQL_CLIENT */
4377 4378


unknown's avatar
unknown committed
4379
/**************************************************************************
4380
  Slave_log_event methods
unknown's avatar
unknown committed
4381
**************************************************************************/
unknown's avatar
unknown committed
4382

unknown's avatar
SCRUM  
unknown committed
4383
#ifdef HAVE_REPLICATION
4384
#ifdef MYSQL_CLIENT
4385
void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
4386
{
4387 4388
  Write_on_release_cache cache(&print_event_info->head_cache, file_arg);

unknown's avatar
unknown committed
4389
  if (print_event_info->short_form)
4390
    return;
4391 4392
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n# %s", "Unknown event\n");
4393 4394
}
#endif  
4395

4396
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4397
void Slave_log_event::pack_info(Protocol *protocol)
4398
{
unknown's avatar
unknown committed
4399
  char buf[256+HOSTNAME_LENGTH], *pos;
4400 4401 4402 4403 4404 4405 4406
  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
4407
  pos= longlong10_to_str(master_pos, pos, 10);
4408
  protocol->store(buf, pos-buf, &my_charset_bin);
4409
}
unknown's avatar
unknown committed
4410
#endif /* !MYSQL_CLIENT */
4411 4412 4413 4414


#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
4415
				 RELAY_LOG_INFO* rli)
4416
  :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
4417 4418 4419 4420
{
  DBUG_ENTER("Slave_log_event");
  if (!rli->inited)				// QQ When can this happen ?
    DBUG_VOID_RETURN;
4421

4422 4423 4424 4425 4426
  MASTER_INFO* mi = rli->mi;
  // TODO: re-write this better without holding both locks at the same time
  pthread_mutex_lock(&mi->data_lock);
  pthread_mutex_lock(&rli->data_lock);
  master_host_len = strlen(mi->host);
4427
  master_log_len = strlen(rli->group_master_log_name);
4428 4429 4430
  // 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))))
4431
  {
4432 4433 4434
    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;
4435
    memcpy(master_log, rli->group_master_log_name, master_log_len + 1);
4436
    master_port = mi->port;
4437
    master_pos = rli->group_master_log_pos;
unknown's avatar
unknown committed
4438
    DBUG_PRINT("info", ("master_log: %s  pos: %lu", master_log,
4439
			(ulong) master_pos));
4440
  }
4441 4442 4443 4444 4445 4446
  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
4447
#endif /* !MYSQL_CLIENT */
4448 4449 4450 4451 4452 4453 4454 4455 4456


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


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4457
void Slave_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4458
{
4459 4460
  Write_on_release_cache cache(&print_event_info->head_cache, file);

4461
  char llbuff[22];
unknown's avatar
unknown committed
4462
  if (print_event_info->short_form)
4463
    return;
4464 4465
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n\
unknown's avatar
unknown committed
4466
Slave: master_host: '%s'  master_port: %d  master_log: '%s'  master_pos: %s\n",
4467 4468
	  master_host, master_port, master_log, llstr(master_pos, llbuff));
}
unknown's avatar
unknown committed
4469
#endif /* MYSQL_CLIENT */
4470 4471 4472 4473 4474 4475 4476 4477


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


4478
#ifndef MYSQL_CLIENT
4479
bool Slave_log_event::write(IO_CACHE* file)
4480
{
4481
  ulong event_length= get_data_size();
4482 4483 4484
  int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
  int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
  // log and host are already there
4485 4486 4487

  return (write_header(file, event_length) ||
          my_b_safe_write(file, (byte*) mem_pool, event_length));
4488
}
4489
#endif
4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500


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)
4501
  {
4502 4503
    master_host = 0;
    return;
4504
  }
4505 4506
  master_log_len = strlen(master_log);
}
4507

4508

4509 4510 4511
/* 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)
4512
{
4513
  if (event_len < LOG_EVENT_HEADER_LEN)
4514
    return;
4515
  event_len -= LOG_EVENT_HEADER_LEN;
4516 4517 4518 4519 4520
  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);
4521 4522
}

4523

4524
#ifndef MYSQL_CLIENT
4525
int Slave_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
4526 4527 4528
{
  if (mysql_bin_log.is_open())
    mysql_bin_log.write(this);
4529
  return 0;
4530
}
unknown's avatar
unknown committed
4531
#endif /* !MYSQL_CLIENT */
4532 4533


unknown's avatar
unknown committed
4534
/**************************************************************************
unknown's avatar
unknown committed
4535
	Stop_log_event methods
unknown's avatar
unknown committed
4536
**************************************************************************/
4537

unknown's avatar
unknown committed
4538
/*
4539
  Stop_log_event::print()
4540
*/
4541 4542

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4543
void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4544
{
4545 4546 4547
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4548
  if (print_event_info->short_form)
4549 4550
    return;

4551 4552
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\tStop\n");
4553
}
unknown's avatar
unknown committed
4554
#endif /* MYSQL_CLIENT */
4555

4556

4557
/*
4558
  Stop_log_event::do_apply_event()
4559 4560 4561 4562 4563 4564 4565 4566

  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
4567
  Start_log_event_v3::do_apply_event(), not here. Because if we come
4568
  here, the master was sane.
4569 4570
*/

4571
#ifndef MYSQL_CLIENT
4572
int Stop_log_event::do_update_pos(RELAY_LOG_INFO *rli)
4573
{
unknown's avatar
unknown committed
4574 4575
  /*
    We do not want to update master_log pos because we get a rotate event
4576
    before stop, so by now group_master_log_name is set to the next log.
4577
    If we updated it, we will have incorrect master coordinates and this
unknown's avatar
unknown committed
4578
    could give false triggers in MASTER_POS_WAIT() that we have reached
4579
    the target position when in fact we have not.
unknown's avatar
unknown committed
4580
  */
4581 4582 4583 4584 4585 4586 4587
  if (thd->options & OPTION_BEGIN)
    rli->inc_event_relay_log_pos();
  else
  {
    rli->inc_group_relay_log_pos(0);
    flush_relay_log_info(rli);
  }
4588 4589
  return 0;
}
4590

unknown's avatar
unknown committed
4591
#endif /* !MYSQL_CLIENT */
unknown's avatar
SCRUM  
unknown committed
4592
#endif /* HAVE_REPLICATION */
4593

4594

unknown's avatar
unknown committed
4595
/**************************************************************************
unknown's avatar
unknown committed
4596
	Create_file_log_event methods
unknown's avatar
unknown committed
4597
**************************************************************************/
4598 4599

/*
4600
  Create_file_log_event ctor
unknown's avatar
unknown committed
4601
*/
4602 4603

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4604 4605 4606 4607
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,
4608
                      bool ignore,
unknown's avatar
unknown committed
4609
		      char* block_arg, uint block_len_arg, bool using_trans)
4610
  :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
unknown's avatar
unknown committed
4611
		  using_trans),
4612
   fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
4613
   file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
4614
{
unknown's avatar
unknown committed
4615
  DBUG_ENTER("Create_file_log_event");
4616
  sql_ex.force_new_format();
unknown's avatar
unknown committed
4617
  DBUG_VOID_RETURN;
4618
}
4619

4620

unknown's avatar
unknown committed
4621
/*
4622
  Create_file_log_event::write_data_body()
unknown's avatar
unknown committed
4623
*/
4624

4625
bool Create_file_log_event::write_data_body(IO_CACHE* file)
4626
{
4627 4628
  bool res;
  if ((res= Load_log_event::write_data_body(file)) || fake_base)
4629 4630
    return res;
  return (my_b_safe_write(file, (byte*) "", 1) ||
4631
          my_b_safe_write(file, (byte*) block, block_len));
4632 4633
}

4634

unknown's avatar
unknown committed
4635
/*
4636
  Create_file_log_event::write_data_header()
unknown's avatar
unknown committed
4637
*/
unknown's avatar
unknown committed
4638

4639
bool Create_file_log_event::write_data_header(IO_CACHE* file)
4640
{
4641
  bool res;
4642
  byte buf[CREATE_FILE_HEADER_LEN];
4643 4644
  if ((res= Load_log_event::write_data_header(file)) || fake_base)
    return res;
4645
  int4store(buf + CF_FILE_ID_OFFSET, file_id);
4646
  return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN) != 0;
4647 4648 4649
}


unknown's avatar
unknown committed
4650
/*
4651
  Create_file_log_event::write_base()
unknown's avatar
unknown committed
4652
*/
4653

4654
bool Create_file_log_event::write_base(IO_CACHE* file)
4655
{
4656 4657 4658 4659
  bool res;
  fake_base= 1;                                 // pretend we are Load event
  res= write(file);
  fake_base= 0;
4660 4661 4662
  return res;
}

4663
#endif /* !MYSQL_CLIENT */
4664

unknown's avatar
unknown committed
4665
/*
4666
  Create_file_log_event ctor
unknown's avatar
unknown committed
4667
*/
4668

4669 4670 4671
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)
4672
{
4673 4674 4675 4676 4677
  DBUG_ENTER("Create_file_log_event::Create_file_log_event(char*,...)");
  uint block_offset;
  uint header_len= description_event->common_header_len;
  uint8 load_header_len= description_event->post_header_len[LOAD_EVENT-1];
  uint8 create_file_header_len= description_event->post_header_len[CREATE_FILE_EVENT-1];
unknown's avatar
unknown committed
4678
  if (!(event_buf= my_memdup((byte*) buf, len, MYF(MY_WME))) ||
4679 4680 4681 4682 4683 4684 4685
      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
4686
    DBUG_VOID_RETURN;
4687
  if (description_event->binlog_version!=1)
4688
  {
4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705
    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);
4706 4707 4708 4709 4710 4711 4712 4713 4714
    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;
4715
  }
unknown's avatar
unknown committed
4716
  DBUG_VOID_RETURN;
4717 4718
}

4719

unknown's avatar
unknown committed
4720
/*
4721
  Create_file_log_event::print()
unknown's avatar
unknown committed
4722
*/
4723 4724

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4725
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
4726
				  bool enable_local)
unknown's avatar
unknown committed
4727
{
4728 4729
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
4730
  if (print_event_info->short_form)
4731 4732
  {
    if (enable_local && check_fname_outside_temp_buf())
unknown's avatar
unknown committed
4733
      Load_log_event::print(file, print_event_info);
4734
    return;
4735 4736 4737 4738
  }

  if (enable_local)
  {
unknown's avatar
unknown committed
4739
    Load_log_event::print(file, print_event_info,
4740
			  !check_fname_outside_temp_buf());
unknown's avatar
unknown committed
4741 4742 4743 4744
    /* 
       That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
       SHOW BINLOG EVENTS we don't.
    */
4745
    my_b_printf(&cache, "#"); 
4746 4747
  }

4748
  my_b_printf(&cache, " file_id: %d  block_len: %d\n", file_id, block_len);
unknown's avatar
unknown committed
4749
}
4750

unknown's avatar
unknown committed
4751

unknown's avatar
unknown committed
4752
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4753
{
unknown's avatar
unknown committed
4754
  print(file, print_event_info, 0);
4755
}
unknown's avatar
unknown committed
4756
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
4757

4758

unknown's avatar
unknown committed
4759
/*
4760
  Create_file_log_event::pack_info()
unknown's avatar
unknown committed
4761
*/
4762

unknown's avatar
SCRUM  
unknown committed
4763
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
4764
void Create_file_log_event::pack_info(Protocol *protocol)
4765
{
4766 4767 4768
  char buf[NAME_LEN*2 + 30 + 21*2], *pos;
  pos= strmov(buf, "db=");
  memcpy(pos, db, db_len);
unknown's avatar
unknown committed
4769
  pos= strmov(pos + db_len, ";table=");
4770
  memcpy(pos, table_name, table_name_len);
unknown's avatar
unknown committed
4771
  pos= strmov(pos + table_name_len, ";file_id=");
4772 4773 4774
  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
4775
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
4776
}
unknown's avatar
unknown committed
4777
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4778 4779


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

unknown's avatar
SCRUM  
unknown committed
4784
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4785
int Create_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
4786
{
unknown's avatar
unknown committed
4787 4788
  char proc_info[17+FN_REFLEN+10], *fname_buf;
  char *ext;
4789 4790 4791
  int fd = -1;
  IO_CACHE file;
  int error = 1;
unknown's avatar
unknown committed
4792

4793
  bzero((char*)&file, sizeof(file));
unknown's avatar
unknown committed
4794 4795
  fname_buf= strmov(proc_info, "Making temp file ");
  ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
4796
  thd->proc_info= proc_info;
4797 4798 4799 4800
  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 ||
4801 4802 4803
      init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
4804 4805
    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in Create_file event: "
                    "could not open file '%s'", fname_buf);
4806 4807 4808 4809
    goto err;
  }
  
  // a trick to avoid allocating another buffer
unknown's avatar
unknown committed
4810 4811
  fname= fname_buf;
  fname_len= (uint) (strmov(ext, ".data") - fname);
4812 4813
  if (write_base(&file))
  {
unknown's avatar
unknown committed
4814
    strmov(ext, ".info"); // to have it right in the error message
4815 4816 4817
    slave_print_msg(ERROR_LEVEL, rli, my_errno,
                    "Error in Create_file event: could not write to file '%s'",
                    fname_buf);
4818 4819 4820 4821 4822 4823
    goto err;
  }
  end_io_cache(&file);
  my_close(fd, MYF(0));
  
  // fname_buf now already has .data, not .info, because we did our trick
4824 4825 4826 4827
  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)
4828
  {
4829 4830
    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in Create_file event: "
                    "could not open file '%s'", fname_buf);
4831 4832
    goto err;
  }
unknown's avatar
unknown committed
4833
  if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
4834
  {
4835 4836
    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in Create_file event: "
                    "write to '%s' failed", fname_buf);
4837 4838
    goto err;
  }
4839 4840
  error=0;					// Everything is ok

4841 4842 4843 4844 4845
err:
  if (error)
    end_io_cache(&file);
  if (fd >= 0)
    my_close(fd, MYF(0));
unknown's avatar
unknown committed
4846
  thd->proc_info= 0;
4847
  return error == 0;
4848
}
unknown's avatar
unknown committed
4849
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
4850

4851

unknown's avatar
unknown committed
4852
/**************************************************************************
unknown's avatar
unknown committed
4853
	Append_block_log_event methods
unknown's avatar
unknown committed
4854
**************************************************************************/
4855

unknown's avatar
unknown committed
4856
/*
4857
  Append_block_log_event ctor
unknown's avatar
unknown committed
4858
*/
4859 4860

#ifndef MYSQL_CLIENT  
unknown's avatar
unknown committed
4861 4862
Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg,
					       char* block_arg,
unknown's avatar
unknown committed
4863 4864 4865
					       uint block_len_arg,
					       bool using_trans)
  :Log_event(thd_arg,0, using_trans), block(block_arg),
unknown's avatar
unknown committed
4866
   block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
4867 4868
{
}
unknown's avatar
unknown committed
4869
#endif
4870 4871


unknown's avatar
unknown committed
4872
/*
4873
  Append_block_log_event ctor
unknown's avatar
unknown committed
4874
*/
4875

4876 4877 4878
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)
4879
{
4880 4881 4882 4883 4884 4885
  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
4886
    DBUG_VOID_RETURN;
4887 4888 4889
  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
4890
  DBUG_VOID_RETURN;
4891 4892 4893
}


unknown's avatar
unknown committed
4894
/*
4895
  Append_block_log_event::write()
unknown's avatar
unknown committed
4896
*/
4897

4898
#ifndef MYSQL_CLIENT
4899
bool Append_block_log_event::write(IO_CACHE* file)
4900 4901 4902
{
  byte buf[APPEND_BLOCK_HEADER_LEN];
  int4store(buf + AB_FILE_ID_OFFSET, file_id);
4903 4904
  return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
          my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
4905 4906
	  my_b_safe_write(file, (byte*) block, block_len));
}
4907
#endif
4908 4909


unknown's avatar
unknown committed
4910
/*
4911
  Append_block_log_event::print()
unknown's avatar
unknown committed
4912
*/
4913 4914

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
4915
void Append_block_log_event::print(FILE* file,
unknown's avatar
unknown committed
4916
				   PRINT_EVENT_INFO* print_event_info)
4917
{
4918 4919
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
4920
  if (print_event_info->short_form)
4921
    return;
4922 4923 4924
  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);
4925
}
unknown's avatar
unknown committed
4926
#endif /* MYSQL_CLIENT */
4927 4928


unknown's avatar
unknown committed
4929
/*
4930
  Append_block_log_event::pack_info()
unknown's avatar
unknown committed
4931
*/
4932

unknown's avatar
SCRUM  
unknown committed
4933
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
4934
void Append_block_log_event::pack_info(Protocol *protocol)
4935 4936 4937 4938 4939 4940
{
  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
4941
  protocol->store(buf, length, &my_charset_bin);
4942 4943 4944
}


unknown's avatar
unknown committed
4945
/*
4946
  Append_block_log_event::get_create_or_append()
unknown's avatar
unknown committed
4947 4948
*/

4949
int Append_block_log_event::get_create_or_append() const
unknown's avatar
unknown committed
4950
{
4951
  return 0; /* append to the file, fail if not exists */
unknown's avatar
unknown committed
4952 4953
}

unknown's avatar
unknown committed
4954
/*
4955
  Append_block_log_event::do_apply_event()
unknown's avatar
unknown committed
4956
*/
4957

4958
int Append_block_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
4959
{
4960
  char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
4961
  int fd;
4962
  int error = 1;
4963
  DBUG_ENTER("Append_block_log_event::do_apply_event");
4964

unknown's avatar
unknown committed
4965 4966
  fname= strmov(proc_info, "Making temp file ");
  slave_load_file_stem(fname, file_id, server_id, ".data");
4967
  thd->proc_info= proc_info;
4968 4969 4970 4971 4972 4973 4974
  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)
    {
4975 4976 4977
      slave_print_msg(ERROR_LEVEL, rli, my_errno,
                      "Error in %s event: could not create file '%s'",
                      get_type_str(), fname);
4978 4979 4980
      goto err;
    }
  }
4981 4982
  else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
                         MYF(MY_WME))) < 0)
4983
  {
4984 4985 4986
    slave_print_msg(ERROR_LEVEL, rli, my_errno,
                    "Error in %s event: could not open file '%s'",
                    get_type_str(), fname);
4987 4988
    goto err;
  }
unknown's avatar
unknown committed
4989
  if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
4990
  {
4991 4992 4993
    slave_print_msg(ERROR_LEVEL, rli, my_errno,
                    "Error in %s event: write to '%s' failed",
                    get_type_str(), fname);
4994 4995 4996
    goto err;
  }
  error=0;
4997

4998 4999 5000
err:
  if (fd >= 0)
    my_close(fd, MYF(0));
5001
  thd->proc_info= 0;
5002
  DBUG_RETURN(error);
5003
}
unknown's avatar
SCRUM  
unknown committed
5004
#endif
5005 5006


unknown's avatar
unknown committed
5007
/**************************************************************************
unknown's avatar
unknown committed
5008
	Delete_file_log_event methods
unknown's avatar
unknown committed
5009
**************************************************************************/
5010

unknown's avatar
unknown committed
5011
/*
5012
  Delete_file_log_event ctor
unknown's avatar
unknown committed
5013
*/
5014 5015

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5016 5017 5018
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)
5019 5020
{
}
unknown's avatar
unknown committed
5021
#endif
5022

unknown's avatar
unknown committed
5023
/*
5024
  Delete_file_log_event ctor
unknown's avatar
unknown committed
5025
*/
5026

5027 5028 5029
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)
5030
{
5031 5032 5033
  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))
5034
    return;
5035
  file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
5036 5037 5038
}


unknown's avatar
unknown committed
5039
/*
5040
  Delete_file_log_event::write()
unknown's avatar
unknown committed
5041
*/
5042

5043
#ifndef MYSQL_CLIENT
5044
bool Delete_file_log_event::write(IO_CACHE* file)
5045 5046 5047
{
 byte buf[DELETE_FILE_HEADER_LEN];
 int4store(buf + DF_FILE_ID_OFFSET, file_id);
5048 5049
 return (write_header(file, sizeof(buf)) ||
         my_b_safe_write(file, buf, sizeof(buf)));
5050
}
5051
#endif
5052 5053


unknown's avatar
unknown committed
5054
/*
5055
  Delete_file_log_event::print()
unknown's avatar
unknown committed
5056
*/
5057 5058

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5059
void Delete_file_log_event::print(FILE* file,
unknown's avatar
unknown committed
5060
				  PRINT_EVENT_INFO* print_event_info)
5061
{
5062 5063
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5064
  if (print_event_info->short_form)
5065
    return;
5066 5067
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id);
5068
}
unknown's avatar
unknown committed
5069
#endif /* MYSQL_CLIENT */
5070

unknown's avatar
unknown committed
5071
/*
5072
  Delete_file_log_event::pack_info()
unknown's avatar
unknown committed
5073
*/
5074

unknown's avatar
SCRUM  
unknown committed
5075
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
5076
void Delete_file_log_event::pack_info(Protocol *protocol)
5077 5078 5079 5080
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
5081
  protocol->store(buf, (int32) length, &my_charset_bin);
5082
}
unknown's avatar
SCRUM  
unknown committed
5083
#endif
5084

unknown's avatar
unknown committed
5085
/*
5086
  Delete_file_log_event::do_apply_event()
unknown's avatar
unknown committed
5087
*/
5088

unknown's avatar
SCRUM  
unknown committed
5089
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5090
int Delete_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
5091 5092
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
5093
  char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
5094
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5095
  strmov(ext, ".info");
5096
  (void) my_delete(fname, MYF(MY_WME));
5097
  return 0;
5098
}
unknown's avatar
unknown committed
5099
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5100 5101


unknown's avatar
unknown committed
5102
/**************************************************************************
unknown's avatar
unknown committed
5103
	Execute_load_log_event methods
unknown's avatar
unknown committed
5104
**************************************************************************/
5105

unknown's avatar
unknown committed
5106
/*
5107
  Execute_load_log_event ctor
unknown's avatar
unknown committed
5108
*/
5109 5110

#ifndef MYSQL_CLIENT  
unknown's avatar
unknown committed
5111 5112 5113
Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg,
					       bool using_trans)
  :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
5114 5115
{
}
unknown's avatar
unknown committed
5116
#endif
5117 5118
  

unknown's avatar
unknown committed
5119
/*
5120
  Execute_load_log_event ctor
unknown's avatar
unknown committed
5121
*/
5122

5123 5124 5125
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)
5126
{
5127 5128 5129
  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))
5130
    return;
5131
  file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
5132 5133 5134
}


unknown's avatar
unknown committed
5135
/*
5136
  Execute_load_log_event::write()
unknown's avatar
unknown committed
5137
*/
5138

5139
#ifndef MYSQL_CLIENT
5140
bool Execute_load_log_event::write(IO_CACHE* file)
5141 5142 5143
{
  byte buf[EXEC_LOAD_HEADER_LEN];
  int4store(buf + EL_FILE_ID_OFFSET, file_id);
5144 5145
  return (write_header(file, sizeof(buf)) || 
          my_b_safe_write(file, buf, sizeof(buf)));
5146
}
5147
#endif
5148 5149


unknown's avatar
unknown committed
5150
/*
5151
  Execute_load_log_event::print()
unknown's avatar
unknown committed
5152
*/
5153 5154

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5155
void Execute_load_log_event::print(FILE* file,
unknown's avatar
unknown committed
5156
				   PRINT_EVENT_INFO* print_event_info)
5157
{
5158 5159
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5160
  if (print_event_info->short_form)
5161
    return;
5162 5163 5164
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
              file_id);
5165
}
unknown's avatar
unknown committed
5166
#endif
5167

unknown's avatar
unknown committed
5168
/*
5169
  Execute_load_log_event::pack_info()
unknown's avatar
unknown committed
5170
*/
5171

unknown's avatar
SCRUM  
unknown committed
5172
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
5173
void Execute_load_log_event::pack_info(Protocol *protocol)
5174 5175 5176 5177
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
5178
  protocol->store(buf, (int32) length, &my_charset_bin);
5179 5180 5181
}


unknown's avatar
unknown committed
5182
/*
5183
  Execute_load_log_event::do_apply_event()
unknown's avatar
unknown committed
5184
*/
unknown's avatar
SCRUM  
unknown committed
5185

5186
int Execute_load_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
5187 5188
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
5189
  char *ext;
5190
  int fd;
unknown's avatar
unknown committed
5191
  int error= 1;
5192
  IO_CACHE file;
unknown's avatar
unknown committed
5193
  Load_log_event *lev= 0;
5194

unknown's avatar
unknown committed
5195
  ext= slave_load_file_stem(fname, file_id, server_id, ".info");
5196 5197
  if ((fd = my_open(fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
                    MYF(MY_WME))) < 0 ||
5198 5199 5200
      init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
5201 5202
    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Error in Exec_load event: "
                    "could not open file '%s'", fname);
5203 5204
    goto err;
  }
5205
  if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
5206 5207
                                                         (pthread_mutex_t*)0,
                                                         rli->relay_log.description_event_for_exec)) ||
5208
      lev->get_type_code() != NEW_LOAD_EVENT)
5209
  {
5210 5211
    slave_print_msg(ERROR_LEVEL, rli, 0, "Error in Exec_load event: "
                    "file '%s' appears corrupted", fname);
5212 5213
    goto err;
  }
unknown's avatar
unknown committed
5214

5215
  lev->thd = thd;
5216
  /*
5217
    lev->do_apply_event should use rli only for errors i.e. should
5218 5219
    not advance rli's position.

5220
    lev->do_apply_event is the place where the table is loaded (it
5221
    calls mysql_load()).
5222
  */
unknown's avatar
unknown committed
5223

5224 5225
  const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
  if (lev->do_apply_event(0,rli,1)) 
5226
  {
5227 5228 5229 5230 5231 5232 5233 5234 5235
    /*
      We want to indicate the name of the file that could not be loaded
      (SQL_LOADxxx).
      But as we are here we are sure the error is in rli->last_slave_error and
      rli->last_slave_errno (example of error: duplicate entry for key), so we
      don't want to overwrite it with the filename.
      What we want instead is add the filename to the current error message.
    */
    char *tmp= my_strdup(rli->last_slave_error,MYF(MY_WME));
5236 5237
    if (tmp)
    {
5238 5239 5240 5241
      slave_print_msg(ERROR_LEVEL, rli,
                      rli->last_slave_errno, /* ok to re-use error code */
                      "%s. Failed executing load from '%s'", 
                      tmp, fname);
5242 5243
      my_free(tmp,MYF(0));
    }
5244 5245
    goto err;
  }
unknown's avatar
Fix for  
unknown committed
5246 5247 5248 5249 5250 5251 5252 5253 5254 5255
  /*
    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;
  }
5256
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5257
  memcpy(ext, ".data", 6);
5258
  (void) my_delete(fname, MYF(MY_WME));
5259
  error = 0;
5260

5261 5262 5263
err:
  delete lev;
  if (fd >= 0)
5264
  {
5265
    my_close(fd, MYF(0));
5266 5267
    end_io_cache(&file);
  }
5268
  return error;
5269
}
unknown's avatar
SCRUM  
unknown committed
5270

unknown's avatar
unknown committed
5271
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5272 5273


unknown's avatar
unknown committed
5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298
/**************************************************************************
	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)
5299
int Begin_load_query_log_event::get_create_or_append() const
unknown's avatar
unknown committed
5300
{
5301
  return 1; /* create the file */
unknown's avatar
unknown committed
5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355
}
#endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */


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


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


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

  buf+= desc_event->common_header_len;

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

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

  file_id= uint4korr(buf + ELQ_FILE_ID_OFFSET);
}


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


5356
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5357 5358 5359 5360 5361 5362 5363 5364 5365 5366
bool
Execute_load_query_log_event::write_post_header_for_derived(IO_CACHE* file)
{
  char buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
  int4store(buf, file_id);
  int4store(buf + 4, fn_pos_start);
  int4store(buf + 4 + 4, fn_pos_end);
  *(buf + 4 + 4 + 4)= (char)dup_handling;
  return my_b_safe_write(file, (byte*) buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
}
5367
#endif
unknown's avatar
unknown committed
5368 5369 5370


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
5371
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5372
                                         PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
5373
{
unknown's avatar
unknown committed
5374
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
5375 5376 5377
}


unknown's avatar
unknown committed
5378
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5379
                                         PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
5380 5381
                                         const char *local_fname)
{
5382 5383 5384
  Write_on_release_cache cache(&print_event_info->head_cache, file);

  print_query_header(&cache, print_event_info);
unknown's avatar
unknown committed
5385 5386 5387

  if (local_fname)
  {
5388 5389 5390 5391
    my_b_write(&cache, (byte*) query, fn_pos_start);
    my_b_printf(&cache, " LOCAL INFILE \'");
    my_b_printf(&cache, local_fname);
    my_b_printf(&cache, "\'");
unknown's avatar
unknown committed
5392
    if (dup_handling == LOAD_DUP_REPLACE)
5393 5394 5395
      my_b_printf(&cache, " REPLACE");
    my_b_printf(&cache, " INTO");
    my_b_write(&cache, (byte*) query + fn_pos_end, q_len-fn_pos_end);
unknown's avatar
unknown committed
5396
    my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5397 5398 5399
  }
  else
  {
5400
    my_b_write(&cache, (byte*) query, q_len);
unknown's avatar
unknown committed
5401
    my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5402 5403
  }

unknown's avatar
unknown committed
5404
  if (!print_event_info->short_form)
5405
    my_b_printf(&cache, "# file_id: %d \n", file_id);
unknown's avatar
unknown committed
5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435
}
#endif


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


int
5436
Execute_load_query_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
unknown's avatar
unknown committed
5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447
{
  char *p;
  char *buf;
  char *fname;
  char *fname_end;
  int error;

  /* Replace filename and LOCAL keyword in query before executing it */
  if (!(buf = my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
                        (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME))))
  {
5448
    slave_print_msg(ERROR_LEVEL, rli, my_errno, "Not enough memory");
unknown's avatar
unknown committed
5449 5450 5451 5452 5453 5454
    return 1;
  }

  p= buf;
  memcpy(p, query, fn_pos_start);
  p+= fn_pos_start;
5455
  fname= (p= strmake(p, STRING_WITH_LEN(" INFILE \'")));
unknown's avatar
unknown committed
5456 5457
  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
5458
  *(p++)='\'';
unknown's avatar
unknown committed
5459
  switch (dup_handling) {
unknown's avatar
unknown committed
5460
  case LOAD_DUP_IGNORE:
5461
    p= strmake(p, STRING_WITH_LEN(" IGNORE"));
unknown's avatar
unknown committed
5462 5463
    break;
  case LOAD_DUP_REPLACE:
5464
    p= strmake(p, STRING_WITH_LEN(" REPLACE"));
unknown's avatar
unknown committed
5465 5466 5467 5468 5469
    break;
  default:
    /* Ordinary load data */
    break;
  }
5470
  p= strmake(p, STRING_WITH_LEN(" INTO"));
unknown's avatar
unknown committed
5471 5472
  p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);

5473
  error= Query_log_event::do_apply_event(rli, buf, p-buf);
unknown's avatar
unknown committed
5474 5475 5476 5477

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

5478 5479 5480 5481 5482 5483
  /*
    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
5484 5485 5486 5487 5488 5489 5490

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


unknown's avatar
unknown committed
5491
/**************************************************************************
unknown's avatar
unknown committed
5492
	sql_ex_info methods
unknown's avatar
unknown committed
5493
**************************************************************************/
5494

unknown's avatar
unknown committed
5495
/*
5496
  sql_ex_info::write_data()
unknown's avatar
unknown committed
5497
*/
5498

5499
bool sql_ex_info::write_data(IO_CACHE* file)
5500 5501 5502
{
  if (new_format())
  {
5503 5504 5505 5506 5507
    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) ||
5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519
	    my_b_safe_write(file,(byte*) &opt_flags,1));
  }
  else
  {
    old_sql_ex old_ex;
    old_ex.field_term= *field_term;
    old_ex.enclosed=   *enclosed;
    old_ex.line_term=  *line_term;
    old_ex.line_start= *line_start;
    old_ex.escaped=    *escaped;
    old_ex.opt_flags=  opt_flags;
    old_ex.empty_flags=empty_flags;
5520
    return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)) != 0;
5521 5522 5523 5524
  }
}


unknown's avatar
unknown committed
5525
/*
5526
  sql_ex_info::init()
unknown's avatar
unknown committed
5527
*/
5528

5529
char *sql_ex_info::init(char *buf, char *buf_end, bool use_new_format)
5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541
{
  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.
    */
5542 5543 5544 5545 5546 5547
    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))
5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573
      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;
}
5574 5575 5576 5577 5578 5579 5580 5581 5582 5583


/**************************************************************************
	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),
5584
    m_row_count(0),
5585 5586
    m_table(tbl_arg),
    m_table_id(tid),
5587 5588
    m_width(tbl_arg ? tbl_arg->s->fields : 1),
    m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
5589 5590
    m_flags(0)
{
5591 5592
  /*
    We allow a special form of dummy event when the table, and cols
5593
    are null and the table id is ~0UL.  This is a temporary
5594 5595
    solution, to be able to terminate a started statement in the
    binary log: the extreneous events will be removed in the future.
5596
   */
5597 5598
  DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
              !tbl_arg && !cols && tid == ~0UL);
5599 5600 5601 5602 5603 5604 5605 5606 5607 5608

  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);
  /* if bitmap_init fails, catched in is_valid() */
  if (likely(!bitmap_init(&m_cols,
                          m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
                          (m_width + 7) & ~7UL,
                          false)))
5609 5610 5611 5612 5613
  {
    /* Cols can be zero if this is a dummy binrows event */
    if (likely(cols != NULL))
      memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
  }
5614
  else
5615 5616 5617 5618
  {
    // Needed because bitmap_init() does not set it to null on failure
    m_cols.bitmap= 0;
  }
5619 5620 5621 5622 5623 5624 5625 5626
}
#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),
5627
    m_row_count(0),
5628 5629 5630 5631 5632 5633
    m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
{
  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
5634 5635
  DBUG_PRINT("enter",("event_len: %u  common_header_len: %d  "
		      "post_header_len: %d",
5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648
		      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
  {
5649
    m_table_id= (ulong) uint6korr(post_start);
5650 5651 5652 5653 5654
    post_start+= RW_FLAGS_OFFSET;
  }

  m_flags= uint2korr(post_start);

5655 5656
  byte const *const var_start=
    (const byte *)buf + common_header_len + post_header_len;
5657
  byte const *const ptr_width= var_start;
5658
  uchar *ptr_after_width= (uchar*) ptr_width;
5659
  DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
5660
  m_width = net_field_length(&ptr_after_width);
5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678
  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,
                          (m_width + 7) & ~7UL,
                          false)))
  {
    DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
    memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8);
    ptr_after_width+= (m_width + 7) / 8;
    DBUG_DUMP("m_cols", (char*) m_cols.bitmap, no_bytes_in_map(&m_cols));
  }
  else
  {
    // Needed because bitmap_init() does not set it to null on failure
    m_cols.bitmap= NULL;
    DBUG_VOID_RETURN;
  }
5679

5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705
  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));

    /* if bitmap_init fails, catched in is_valid() */
    if (likely(!bitmap_init(&m_cols_ai,
                            m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
                            (m_width + 7) & ~7UL,
                            false)))
    {
      DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
      memcpy(m_cols_ai.bitmap, ptr_after_width, (m_width + 7) / 8);
      ptr_after_width+= (m_width + 7) / 8;
      DBUG_DUMP("m_cols_ai", (char*) m_cols_ai.bitmap, no_bytes_in_map(&m_cols_ai));
    }
    else
    {
      // Needed because bitmap_init() does not set it to null on failure
      m_cols_ai.bitmap= 0;
      DBUG_VOID_RETURN;
    }
  }

  const byte* const ptr_rows_data= (const byte*) ptr_after_width;
5706

5707
  my_size_t const data_size= event_len - (ptr_rows_data - (const byte *) buf);
unknown's avatar
unknown committed
5708
  DBUG_PRINT("info",("m_table_id: %lu  m_flags: %d  m_width: %lu  data_size: %lu",
5709 5710
                     m_table_id, m_flags, m_width, data_size));

5711
  m_rows_buf= (byte*)my_malloc(data_size, MYF(MY_WME));
5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728
  if (likely((bool)m_rows_buf))
  {
    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().
5729
  my_free((gptr)m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
5730 5731
}

5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754
int Rows_log_event::get_data_size()
{
  int const type_code= get_type_code();

  char buf[sizeof(m_width)+1];
  char *end= net_store_length(buf, (m_width + 7) / 8);

  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; 
}


5755 5756 5757 5758 5759 5760 5761 5762 5763
#ifndef MYSQL_CLIENT
int Rows_log_event::do_add_row_data(byte *const row_data,
                                    my_size_t const length)
{
  /*
    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
  */
5764
  DBUG_ENTER("Rows_log_event::do_add_row_data");
unknown's avatar
unknown committed
5765 5766
  DBUG_PRINT("enter", ("row_data: 0x%lx  length: %lu", (ulong) row_data,
                       (ulong) length));
5767 5768 5769 5770 5771
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
5772
  DBUG_DUMP("row_data", (const char*)row_data, min(length, 32));
5773
#endif
5774 5775

  DBUG_ASSERT(m_rows_buf <= m_rows_cur);
5776
  DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
5777 5778 5779
  DBUG_ASSERT(m_rows_cur <= m_rows_end);

  /* The cast will always work since m_rows_cur <= m_rows_end */
5780
  if (static_cast<my_size_t>(m_rows_end - m_rows_cur) <= length)
5781 5782 5783
  {
    my_size_t const block_size= 1024;
    my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
5784
    my_ptrdiff_t const new_alloc= 
5785
        block_size * ((cur_size + length + block_size - 1) / block_size);
5786

unknown's avatar
unknown committed
5787
    byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, (uint) new_alloc,
5788
                                           MYF(MY_ALLOW_ZERO_PTR|MY_WME));
5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805
    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;
  }

5806
  DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
5807 5808
  memcpy(m_rows_cur, row_data, length);
  m_rows_cur+= length;
5809
  m_row_count++;
5810 5811 5812 5813 5814
  DBUG_RETURN(0);
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
5815
int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
5816
{
5817
  DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)");
5818
  int error= 0;
5819
  char const *row_start= (char const *)m_rows_buf;
5820 5821

  /*
5822 5823 5824
    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
5825
    success.
5826
   */
5827
  if (m_table_id == ~0UL)
5828 5829 5830 5831 5832 5833 5834
  {
    /*
       This one is supposed to be set: just an extra check so that
       nothing strange has happened.
     */
    DBUG_ASSERT(get_flags(STMT_END_F));

5835
    const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
5836
    close_thread_tables(thd);
5837 5838 5839
    thd->clear_error();
    DBUG_RETURN(0);
  }
5840 5841 5842

  /*
    'thd' has been set by exec_relay_log_event(), just before calling
5843
    do_apply_event(). We still check here to prevent future coding
5844
    errors.
5845 5846 5847 5848
  */
  DBUG_ASSERT(rli->sql_thd == thd);

  /*
5849 5850 5851 5852
    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.
5853
  */
5854
  if (!thd->lock)
5855
  {
5856 5857
    bool need_reopen= 1; /* To execute the first lap of the loop below */

5858
    /*
5859
      lock_tables() reads the contents of thd->lex, so they must be
5860
      initialized. Contrary to in
5861
      Table_map_log_event::do_apply_event() we don't call
5862
      mysql_init_query() as that may reset the binlog format.
5863
    */
5864
    lex_start(thd, NULL, 0);
5865

5866 5867 5868
    while ((error= lock_tables(thd, rli->tables_to_lock,
                               rli->tables_to_lock_count, &need_reopen)))
    {
5869 5870
      if (!need_reopen)
      {
5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890
        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;
          slave_print_msg(ERROR_LEVEL, rli, actual_error,
                          "Error '%s' in %s event: when locking tables",
                          (actual_error ? thd->net.last_error :
                           "unexpected success or fatal error"),
                          get_type_str());
          thd->is_fatal_error= 1;
        }
        else
        {
          slave_print_msg(ERROR_LEVEL, rli, error,
                         "Error in %s event: when locking tables",
                         get_type_str());
        }
5891
        const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
5892 5893
        DBUG_RETURN(error);
      }
5894

5895
      /*
5896
        So we need to reopen the tables.
5897

5898 5899
        We need to flush the pending RBR event, since it keeps a
        pointer to an open table.
5900

5901 5902 5903
        ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
        the pending RBR event and reset the table pointer after the
        tables has been reopened.
5904

5905 5906
        NOTE: For this new scheme there should be no pending event:
        need to add code to assert that is the case.
5907 5908
       */
      thd->binlog_flush_pending_rows_event(false);
5909 5910
      TABLE_LIST *tables= rli->tables_to_lock;
      close_tables_for_reopen(thd, &tables);
5911

5912 5913
      uint tables_count= rli->tables_to_lock_count;
      if ((error= open_tables(thd, &tables, &tables_count, 0)))
5914 5915 5916 5917 5918 5919 5920 5921 5922
      {
        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;
          slave_print_msg(ERROR_LEVEL, rli, actual_error,
5923
                          "Error '%s' on reopening tables",
5924
                          (actual_error ? thd->net.last_error :
5925
                           "unexpected success or fatal error"));
5926 5927
          thd->query_error= 1;
        }
5928
        const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
5929
        DBUG_RETURN(error);
5930
      }
5931
    }
5932 5933 5934 5935 5936 5937 5938 5939 5940 5941

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

    {
5942
      RPL_TABLE_LIST *ptr= rli->tables_to_lock;
5943 5944 5945 5946 5947 5948 5949
      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;
5950
          const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
5951 5952 5953 5954 5955
          DBUG_RETURN(ERR_BAD_TABLE_DEF);
        }
      }
    }

5956
    /*
5957 5958
      ... and then we add all the tables to the table map and remove
      them from tables to lock.
5959 5960 5961

      We also invalidate the query cache for all the tables, since
      they will now be changed.
5962 5963 5964 5965 5966 5967 5968

      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.
5969
     */
5970
    for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
5971
    {
5972
      const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
5973
    }
5974 5975 5976
#ifdef HAVE_QUERY_CACHE
    query_cache.invalidate_locked_for_write(rli->tables_to_lock);
#endif
5977
    const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
5978 5979
  }

5980 5981
  DBUG_ASSERT(rli->tables_to_lock == NULL && rli->tables_to_lock_count == 0);

5982
  TABLE* table= const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.get_table(m_table_id);
5983 5984 5985 5986 5987

  if (table)
  {
    /*
      table == NULL means that this table should not be replicated
5988
      (this was set up by Table_map_log_event::do_apply_event()
5989
      which tested replicate-* rules).
5990
    */
5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017

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

6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028
    /*
      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.
     */
    const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT);

6029
    error= do_before_row_operations(table);
6030 6031
    while (error == 0 && row_start < (const char*) m_rows_end)
    {
6032 6033
      char const *row_end= NULL;
      if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
6034 6035
        break; // We should perform the after-row operation even in
               // the case of error
6036

6037
      DBUG_ASSERT(row_end != NULL); // cannot happen
6038
      DBUG_ASSERT(row_end <= (const char*)m_rows_end);
6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056

      /* 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;
      error= do_exec_row(table);
      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:
6057
	slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
6058 6059 6060 6061 6062 6063 6064 6065 6066
                        "Error in %s event: row application failed",
                        get_type_str());
	thd->query_error= 1;
	break;
      }

      row_start= row_end;
    }
    DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
6067
                    const_cast<RELAY_LOG_INFO*>(rli)->abort_slave= 1;);
6068 6069
    error= do_after_row_operations(table, error);
    if (!cache_stmt)
6070 6071 6072 6073
    {
      DBUG_PRINT("info", ("Marked that we need to keep log"));
      thd->options|= OPTION_KEEP_LOG;
    }
6074 6075 6076 6077
  }

  if (error)
  {                     /* error has occured during the transaction */
6078
    slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
unknown's avatar
unknown committed
6079 6080 6081 6082
                    "Error in %s event: error during transaction execution "
                    "on table %s.%s",
                    get_type_str(), table->s->db.str, 
                    table->s->table_name.str);
6083

unknown's avatar
unknown committed
6084
     /*
6085 6086 6087 6088 6089 6090 6091 6092
      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
6093
      rollback at the caller along with sbr.
6094
    */
unknown's avatar
unknown committed
6095
    thd->reset_current_stmt_binlog_row_based();
6096
    const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, error);
6097 6098 6099 6100
    thd->query_error= 1;
    DBUG_RETURN(error);
  }

6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139
  /*
    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
      st_relay_log_info::last_event_start_time and all its occurences.
    */
    const_cast<RELAY_LOG_INFO*>(rli)->last_event_start_time= time(0);
  }

  DBUG_RETURN(0);
}

int
Rows_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
  DBUG_ENTER("Rows_log_event::do_update_pos");
  int error= 0;

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

6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157
  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);
6158

6159 6160 6161 6162 6163 6164 6165 6166 6167 6168
    /*
      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);
6169

6170 6171 6172 6173 6174 6175 6176 6177 6178 6179
    /*
      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
6180
    thd->reset_current_stmt_binlog_row_based();
6181
    rli->cleanup_context(thd, 0);
6182 6183
    if (error == 0)
    {
6184 6185 6186 6187 6188 6189 6190
      /*
        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);

6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202
      /*
        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
      slave_print_msg(ERROR_LEVEL, rli, error,
6203 6204
                      "Error in %s event: commit of row events failed",
                      get_type_str());
6205
  }
6206
  else
6207
  {
6208
    rli->inc_event_relay_log_pos();
6209 6210
  }

6211
  DBUG_RETURN(error);
6212
}
6213

6214 6215 6216 6217 6218 6219
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
{
  byte buf[ROWS_HEADER_LEN];	// No need to init the buffer
6220
  DBUG_ASSERT(m_table_id != ~0UL);
6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237
  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.
  */
unknown's avatar
unknown committed
6238
  char sbuf[sizeof(m_width)];
6239
  my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
6240
  bool res= false;
6241

unknown's avatar
unknown committed
6242
  char *const sbuf_end= net_store_length((char*) sbuf, (uint) m_width);
unknown's avatar
unknown committed
6243
  DBUG_ASSERT(static_cast<my_size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
6244

6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268
  DBUG_DUMP("m_width", sbuf, sbuf_end - sbuf);
  res= res || my_b_safe_write(file,
                              reinterpret_cast<byte*>(sbuf),
                              sbuf_end - sbuf);

  DBUG_DUMP("m_cols", (char*) m_cols.bitmap, no_bytes_in_map(&m_cols));
  res= res || my_b_safe_write(file,
                              reinterpret_cast<byte*>(m_cols.bitmap),
                              no_bytes_in_map(&m_cols));
  /*
    TODO[refactor write]: Remove the "down cast" here (and elsewhere).
   */
  if (get_type_code() == UPDATE_ROWS_EVENT)
  {
    DBUG_DUMP("m_cols_ai", (char*) m_cols_ai.bitmap, no_bytes_in_map(&m_cols_ai));
    res= res || my_b_safe_write(file,
                                reinterpret_cast<byte*>(m_cols_ai.bitmap),
                                no_bytes_in_map(&m_cols_ai));
  }
  DBUG_DUMP("rows", m_rows_buf, data_size);
  res= res || my_b_safe_write(file, m_rows_buf, (uint) data_size);

  return res;

6269 6270 6271
}
#endif

6272
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
6273 6274
void Rows_log_event::pack_info(Protocol *protocol)
{
6275 6276 6277
  char buf[256];
  char const *const flagstr=
    get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
unknown's avatar
unknown committed
6278 6279
  my_size_t bytes= my_snprintf(buf, sizeof(buf),
                               "table_id: %lu%s", m_table_id, flagstr);
6280
  protocol->store(buf, bytes, &my_charset_bin);
6281 6282 6283
}
#endif

6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294
#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);
6295 6296 6297
    my_b_printf(head, "\t%s: table id %lu%s\n",
                name, m_table_id,
                last_stmt_event ? " flags: STMT_END_F" : "");
6298 6299 6300 6301 6302
    print_base64(body, print_event_info, !last_stmt_event);
  }

  if (get_flags(STMT_END_F))
  {
6303 6304
    copy_event_cache_to_file_and_reinit(head, file);
    copy_event_cache_to_file_and_reinit(body, file);
6305 6306 6307 6308
  }
}
#endif

6309
/**************************************************************************
6310
	Table_map_log_event member functions and support functions
6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330
**************************************************************************/

/*
  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),
    m_colcnt(tbl->s->fields), m_coltype(0),
    m_table_id(tid),
    m_flags(flags)
{
6331
  DBUG_ASSERT(m_table_id != ~0UL);
6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343
  /*
    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
6344
  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;);
6345 6346 6347 6348 6349 6350 6351
  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

  /* If malloc fails, catched in is_valid() */
  if ((m_memory= my_malloc(m_colcnt, MYF(MY_WME))))
  {
unknown's avatar
unknown committed
6352
    m_coltype= reinterpret_cast<uchar*>(m_memory);
6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376
    for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
      m_coltype[i]= m_table->field[i]->type();
  }
}
#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
  m_table(NULL),
#endif
  m_memory(NULL)
{
  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
6377
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
6378 6379
                     event_len, common_header_len, post_header_len));

6380 6381 6382 6383 6384
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
6385
  DBUG_DUMP("event buffer", buf, event_len);
6386
#endif
6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400

  /* 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);
6401
    m_table_id= (ulong) uint6korr(post_start);
6402 6403 6404
    post_start+= TM_FLAGS_OFFSET;
  }

6405
  DBUG_ASSERT(m_table_id != ~0UL);
6406 6407 6408 6409 6410 6411 6412

  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 */
6413
  byte const *const ptr_dblen= (byte const*)vpart + 0;
unknown's avatar
unknown committed
6414
  m_dblen= *(uchar*) ptr_dblen;
6415 6416

  /* Length of database name + counter + terminating null */
6417
  byte const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
unknown's avatar
unknown committed
6418
  m_tbllen= *(uchar*) ptr_tbllen;
6419 6420

  /* Length of table name + counter + terminating null */
6421
  byte const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
6422 6423
  uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
  m_colcnt= net_field_length(&ptr_after_colcnt);
6424

unknown's avatar
unknown committed
6425
  DBUG_PRINT("info",("m_dblen: %lu  off: %ld  m_tbllen: %lu  off: %ld  m_colcnt: %lu  off: %ld",
unknown's avatar
unknown committed
6426 6427 6428
                     m_dblen, (long) (ptr_dblen-(const byte*)vpart), 
                     m_tbllen, (long) (ptr_tbllen-(const byte*)vpart),
                     m_colcnt, (long) (ptr_colcnt-(const byte*)vpart)));
6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439

  /* Allocate mem for all fields in one go. If fails, catched in is_valid() */
  m_memory= my_multi_malloc(MYF(MY_WME),
			    &m_dbnam, m_dblen + 1,
			    &m_tblnam, m_tbllen + 1,
			    &m_coltype, m_colcnt,
			    NULL);

  if (m_memory)
  {
    /* Copy the different parts into their memory */
6440 6441
    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);
6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465
    memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
  }

  DBUG_VOID_RETURN;
}
#endif

Table_map_log_event::~Table_map_log_event()
{
  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)
6466
int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
6467
{
6468
  DBUG_ENTER("Table_map_log_event::do_apply_event(st_relay_log_info*)");
6469 6470 6471 6472 6473 6474 6475 6476

  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);

6477
  RPL_TABLE_LIST *table_list;
6478 6479 6480
  char *db_mem, *tname_mem;
  void *const memory=
    my_multi_malloc(MYF(MY_WME),
6481
                    &table_list, sizeof(RPL_TABLE_LIST),
6482 6483 6484 6485
                    &db_mem, NAME_LEN + 1,
                    &tname_mem, NAME_LEN + 1,
                    NULL);

6486 6487
  if (memory == NULL)
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
6488

6489
  uint dummy_len;
6490 6491 6492 6493 6494 6495 6496 6497 6498
  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);
6499 6500 6501

  int error= 0;

6502 6503 6504 6505 6506 6507
  if (!rpl_filter->db_ok(table_list->db) ||
      (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list)))
  {
    my_free((gptr) memory, MYF(MY_WME));
  }
  else
6508
  {
6509 6510 6511 6512 6513 6514
    /*
      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.
    */
    mysql_init_query(thd, NULL, 0);
6515
    /*
unknown's avatar
unknown committed
6516 6517 6518
      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.
6519
    */
unknown's avatar
unknown committed
6520 6521
    if (!thd->current_stmt_binlog_row_based &&
        mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
6522
    {
unknown's avatar
unknown committed
6523
      thd->set_current_stmt_binlog_row_based();
6524 6525 6526
    }

    /*
6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542
      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.
6543 6544
    */
    uint count;
6545 6546 6547
    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)))
6548
    {
6549
      if (thd->query_error || thd->is_fatal_error)
6550
      {
6551 6552 6553 6554 6555
        /*
          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;
6556 6557 6558 6559 6560 6561
        slave_print_msg(ERROR_LEVEL, rli, 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);
        thd->query_error= 1;
6562
      }
6563
      goto err;
6564 6565
    }

6566
    m_table= table_list->table;
6567 6568 6569 6570 6571 6572 6573

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

6574 6575 6576
    /*
      Use placement new to construct the table_def instance in the
      memory allocated for it inside table_list.
6577 6578 6579 6580 6581

      The memory allocated by the table_def structure (i.e., not the
      memory allocated *for* the table_def structure) is released
      inside st_relay_log_info::clear_tables_to_lock() by calling the
      table_def destructor explicitly.
6582
    */
6583
    new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt);
6584
    table_list->m_tabledef_valid= TRUE;
6585 6586

    /*
6587
      We record in the slave's information that the table should be
unknown's avatar
unknown committed
6588
      locked by linking the table into the list of tables to lock.
6589
    */
6590
    table_list->next_global= table_list->next_local= rli->tables_to_lock;
6591 6592
    const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock= table_list;
    const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock_count++;
6593
    /* 'memory' is freed in clear_tables_to_lock */
6594 6595
  }

6596
  DBUG_RETURN(error);
6597

6598 6599
err:
  my_free((gptr) memory, MYF(MY_WME));
6600 6601
  DBUG_RETURN(error);
}
6602 6603 6604 6605 6606 6607 6608

int Table_map_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
  rli->inc_event_relay_log_pos();
  return 0;
}

6609 6610 6611 6612 6613
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifndef MYSQL_CLIENT
bool Table_map_log_event::write_data_header(IO_CACHE *file)
{
6614
  DBUG_ASSERT(m_table_id != ~0UL);
6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634
  byte buf[TABLE_MAP_HEADER_LEN];
  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);

unknown's avatar
unknown committed
6635 6636
  byte const dbuf[]= { (byte) m_dblen };
  byte const tbuf[]= { (byte) m_tbllen };
6637

unknown's avatar
unknown committed
6638
  char cbuf[sizeof(m_colcnt)];
unknown's avatar
unknown committed
6639
  char *const cbuf_end= net_store_length((char*) cbuf, (uint) m_colcnt);
unknown's avatar
unknown committed
6640
  DBUG_ASSERT(static_cast<my_size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
6641

6642
  return (my_b_safe_write(file, dbuf,      sizeof(dbuf)) ||
6643
          my_b_safe_write(file, (const byte*)m_dbnam,   m_dblen+1) ||
6644
          my_b_safe_write(file, tbuf,      sizeof(tbuf)) ||
6645
          my_b_safe_write(file, (const byte*)m_tblnam,  m_tbllen+1) ||
unknown's avatar
unknown committed
6646
          my_b_safe_write(file, reinterpret_cast<byte*>(cbuf),
unknown's avatar
Merge  
unknown committed
6647
                          cbuf_end - (char*) cbuf) ||
6648
          my_b_safe_write(file, reinterpret_cast<byte*>(m_coltype), m_colcnt));
6649 6650 6651 6652 6653 6654 6655 6656 6657 6658
 }
#endif

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

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

6659
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
6660 6661 6662
void Table_map_log_event::pack_info(Protocol *protocol)
{
    char buf[256];
unknown's avatar
unknown committed
6663 6664
    my_size_t bytes= my_snprintf(buf, sizeof(buf),
                                 "table_id: %lu (%s.%s)",
6665
                              m_table_id, m_dbnam, m_tblnam);
6666 6667
    protocol->store(buf, bytes, &my_charset_bin);
}
6668 6669
#endif

6670 6671 6672 6673 6674 6675 6676 6677 6678

#endif


#ifdef MYSQL_CLIENT
void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
{
  if (!print_event_info->short_form)
  {
6679 6680 6681 6682 6683
    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);
6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737
  }
}
#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)
int Write_rows_log_event::do_before_row_operations(TABLE *table)
{
  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;

unknown's avatar
unknown committed
6738
  table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);  // Needed for ndbcluster
6739
  table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);  // Needed for ndbcluster
unknown's avatar
unknown committed
6740
  table->file->extra(HA_EXTRA_IGNORE_NO_KEY);   // Needed for ndbcluster
6741 6742 6743 6744 6745
  /*
    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.
  */
6746
  table->file->ha_start_bulk_insert(0);
6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768
  /*
    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).
  */
  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
  return error;
}

int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
{
  if (error == 0)
6769
    error= table->file->ha_end_bulk_insert();
6770 6771 6772
  return error;
}

6773
int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
6774
                                         TABLE *table,
6775 6776
                                         char const *const row_start,
                                         char const **const row_end)
6777 6778
{
  DBUG_ASSERT(table != NULL);
6779 6780 6781
  DBUG_ASSERT(row_start && row_end);

  int error;
6782 6783
  error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
                    &m_master_reclength, table->write_set, WRITE_ROWS_EVENT);
6784
  bitmap_copy(table->read_set, table->write_set);
6785
  return error;
6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830
}

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

/* Anonymous namespace for template functions/classes */
namespace {

  /*
    Smart pointer that will automatically call my_afree (a macro) when
    the pointer goes out of scope.  This is used so that I do not have
    to remember to call my_afree() before each return.  There is no
    overhead associated with this, since all functions are inline.

    I (Matz) would prefer to use the free function as a template
    parameter, but that is not possible when the "function" is a
    macro.
  */
  template <class Obj>
  class auto_afree_ptr
  {
    Obj* m_ptr;
  public:
    auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { }
    ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); }
    void assign(Obj* ptr) {
      /* Only to be called if it hasn't been given a value before. */
      DBUG_ASSERT(m_ptr == NULL);
      m_ptr= ptr;
    }
    Obj* get() { return m_ptr; }
  };

}


6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843
/*
  Copy "extra" columns from record[1] to record[0].

  Copy the extra fields that are not present on the master but are
  present on the slave from record[1] to record[0].  This is used
  after fetching a record that are to be updated, either inside
  replace_record() or as part of executing an update_row().
 */
static int
copy_extra_record_fields(TABLE *table,
                         my_size_t master_reclength,
                         my_ptrdiff_t master_fields)
{
unknown's avatar
unknown committed
6844
  DBUG_PRINT("info", ("Copying to 0x%lx "
unknown's avatar
unknown committed
6845
                      "from field %lu at offset %lu "
unknown's avatar
unknown committed
6846 6847
                      "to field %d at offset %lu",
                      (long) table->record[0],
unknown's avatar
unknown committed
6848
                      (ulong) master_fields, (ulong) master_reclength,
6849
                      table->s->fields, table->s->reclength));
6850 6851 6852 6853 6854
  /*
    Copying the extra fields of the slave that does not exist on
    master into record[0] (which are basically the default values).
  */
  DBUG_ASSERT(master_reclength <= table->s->reclength);
6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897
  if (master_reclength < table->s->reclength)
    bmove_align(table->record[0] + master_reclength,
                table->record[1] + master_reclength,
                table->s->reclength - master_reclength);
    
  /*
    Bit columns are special.  We iterate over all the remaining
    columns and copy the "extra" bits to the new record.  This is
    not a very good solution: it should be refactored on
    opportunity.

    REFACTORING SUGGESTION (Matz).  Introduce a member function
    similar to move_field_offset() called copy_field_offset() to
    copy field values and implement it for all Field subclasses. Use
    this function to copy data from the found record to the record
    that are going to be inserted.

    The copy_field_offset() function need to be a virtual function,
    which in this case will prevent copying an entire range of
    fields efficiently.
  */
  {
    Field **field_ptr= table->field + master_fields;
    for ( ; *field_ptr ; ++field_ptr)
    {
      /*
        Set the null bit according to the values in record[1]
       */
      if ((*field_ptr)->maybe_null() &&
          (*field_ptr)->is_null_in_record(reinterpret_cast<uchar*>(table->record[1])))
        (*field_ptr)->set_null();
      else
        (*field_ptr)->set_notnull();

      /*
        Do the extra work for special columns.
       */
      switch ((*field_ptr)->real_type())
      {
      default:
        /* Nothing to do */
        break;

6898
      case MYSQL_TYPE_BIT:
6899
        Field_bit *f= static_cast<Field_bit*>(*field_ptr);
6900 6901 6902 6903 6904 6905 6906
        if (f->bit_len > 0)
        {
          my_ptrdiff_t const offset= table->record[1] - table->record[0];
          uchar const bits=
            get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len);
          set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len);
        }
6907 6908 6909 6910 6911 6912 6913
        break;
      }
    }
  }
  return 0;                                     // All OK
}

6914 6915
#define DBUG_PRINT_BITSET(N,FRM,BS)                \
  do {                                             \
6916 6917 6918 6919 6920 6921 6922
    char buf[256];                                 \
    for (uint i = 0 ; i < (BS)->n_bits ; ++i)      \
      buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \
    buf[(BS)->n_bits] = '\0';                      \
    DBUG_PRINT((N), ((FRM), buf));                 \
  } while (0)

6923

6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949
/**
   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;
}


6950 6951 6952
/*
  Replace the provided record in the database.

6953 6954 6955 6956 6957 6958 6959 6960
  SYNOPSIS
      replace_record()
      thd    Thread context for writing the record.
      table  Table to which record should be written.
      master_reclength
             Offset to first column that is not present on the master,
             alternatively the length of the record on the master
             side.
6961

6962 6963
  RETURN VALUE
      Error code on failure, 0 on success.
6964

6965 6966 6967 6968
  DESCRIPTION
      Similar to how it is done in mysql_insert(), we first try to do
      a ha_write_row() and of that fails due to duplicated keys (or
      indices), we do an ha_update_row() or a ha_delete_row() instead.
6969 6970
 */
static int
6971 6972 6973
replace_record(THD *thd, TABLE *table,
               ulong const master_reclength,
               uint const master_fields)
6974
{
6975
  DBUG_ENTER("replace_record");
6976 6977 6978 6979 6980 6981
  DBUG_ASSERT(table != NULL && thd != NULL);

  int error;
  int keynum;
  auto_afree_ptr<char> key(NULL);

6982 6983 6984 6985 6986 6987
#ifndef DBUG_OFF
  DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
  DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
  DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif

6988 6989
  while ((error= table->file->ha_write_row(table->record[0])))
  {
unknown's avatar
unknown committed
6990 6991 6992 6993 6994
    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);
    }
6995 6996 6997
    if ((keynum= table->file->get_dup_key(error)) < 0)
    {
      /* We failed to retrieve the duplicate key */
6998
      DBUG_RETURN(error);
6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010
    }

    /*
       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.
     */
7011
    if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
7012
    {
7013
      error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
7014
      if (error)
7015 7016
      {
        table->file->print_error(error, MYF(0));
7017
        DBUG_RETURN(error);
7018
      }
7019 7020 7021 7022 7023
    }
    else
    {
      if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
      {
7024
        DBUG_RETURN(my_errno);
7025 7026 7027 7028 7029 7030
      }

      if (key.get() == NULL)
      {
        key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
        if (key.get() == NULL)
7031
          DBUG_RETURN(ENOMEM);
7032 7033
      }

7034
      key_copy((byte*)key.get(), table->record[0], table->key_info + keynum, 0);
7035
      error= table->file->index_read_idx(table->record[1], keynum,
7036
                                         (const byte*)key.get(),
7037
                                         HA_WHOLE_KEY,
7038 7039
                                         HA_READ_KEY_EXACT);
      if (error)
7040 7041
      {
        table->file->print_error(error, MYF(0));
7042
        DBUG_RETURN(error);
7043
      }
7044 7045 7046 7047 7048 7049 7050
    }

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

7051 7052 7053
       First we copy the columns into table->record[0] that are not
       present on the master from table->record[1], if there are any.
    */
7054
    copy_extra_record_fields(table, master_reclength, master_fields);
7055

7056
    /*
7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075
       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())
    {
      error=table->file->ha_update_row(table->record[1],
                                       table->record[0]);
7076 7077
      if (error)
        table->file->print_error(error, MYF(0));
7078
      DBUG_RETURN(error);
7079 7080 7081 7082
    }
    else
    {
      if ((error= table->file->ha_delete_row(table->record[1])))
7083 7084
      {
        table->file->print_error(error, MYF(0));
7085
        DBUG_RETURN(error);
7086
      }
7087 7088 7089
      /* Will retry ha_write_row() with the offending row removed. */
    }
  }
7090

7091
  DBUG_RETURN(error);
7092 7093 7094 7095 7096
}

int Write_rows_log_event::do_exec_row(TABLE *table)
{
  DBUG_ASSERT(table != NULL);
7097
  int error= replace_record(thd, table, m_master_reclength, m_width);
7098 7099 7100 7101 7102 7103 7104
  return error;
}
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
{
7105
  Rows_log_event::print_helper(file, print_event_info, "Write_rows");
7106 7107 7108 7109 7110 7111 7112 7113
}
#endif

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

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
7114 7115 7116 7117 7118 7119
/*
  Compares table->record[0] and table->record[1]

  Returns TRUE if different.
*/
static bool record_compare(TABLE *table)
7120
{
7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147
  /*
    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;
  byte saved_x[2], saved_filler[2];

  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);
    }
  }

7148
  if (table->s->blob_fields + table->s->varchar_fields == 0)
7149 7150 7151 7152 7153
  {
    result= cmp_record(table,record[1]);
    goto record_compare_exit;
  }

7154 7155 7156 7157
  /* Compare null bits */
  if (memcmp(table->null_flags,
	     table->null_flags+table->s->rec_buff_length,
	     table->s->null_bytes))
7158 7159 7160 7161 7162
  {
    result= TRUE;				// Diff in NULL value
    goto record_compare_exit;
  }

7163 7164
  /* Compare updated fields */
  for (Field **ptr=table->field ; *ptr ; ptr++)
7165
  {
7166
    if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
7167 7168 7169 7170
    {
      result= TRUE;
      goto record_compare_exit;
    }
7171
  }
7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186

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];
    }
7187
  }
7188 7189

  return result;
7190 7191 7192 7193 7194
}


/*
  Find the row given by 'key', if the table has keys, or else use a table scan
7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214
  to find (and fetch) the row.

  If the engine allows random access of the records, a combination of
  position() and rnd_pos() will be used.

  @param table Pointer to table to search
  @param key   Pointer to key to use for search, if table has key

  @pre <code>table->record[0]</code> shall contain the row to locate
  and <code>key</code> shall contain a key to use for searching, if
  the engine has a key.

  @post If the return value is zero, <code>table->record[1]</code>
  will contain the fetched row and the internal "cursor" will refer to
  the row. If the return value is non-zero,
  <code>table->record[1]</code> is undefined.  In either case,
  <code>table->record[0]</code> is undefined.

  @return Zero if the row was successfully fetched into
  <code>table->record[1]</code>, error code otherwise.
7215
 */
7216

7217
static int find_and_fetch_row(TABLE *table, byte *key)
7218 7219
{
  DBUG_ENTER("find_and_fetch_row(TABLE *table, byte *key, byte *record)");
7220 7221
  DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx  record: 0x%lx",
		       (long) table, (long) key, (long) table->record[1]));
7222 7223 7224

  DBUG_ASSERT(table->in_use != NULL);

7225 7226
  DBUG_DUMP("record[0]", table->record[0], table->s->reclength);

7227 7228
  if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
      table->s->primary_key < MAX_KEY)
7229 7230 7231 7232 7233 7234
  {
    /*
      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
7235
      the "cursor" (i.e., record[0] in this case) at the correct row.
7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246

      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);

7247 7248
    */
    table->file->position(table->record[0]);
7249 7250 7251 7252 7253 7254 7255
    int error= table->file->rnd_pos(table->record[0], table->file->ref);
    /*
      rnd_pos() returns the record in table->record[0], so we have to
      move it to table->record[1].
     */
    bmove_align(table->record[1], table->record[0], table->s->reclength);
    DBUG_RETURN(error);
7256 7257
  }

unknown's avatar
unknown committed
7258
  /* We need to retrieve all fields */
7259 7260
  /* TODO: Move this out from this function to main loop */
  table->use_all_columns();
unknown's avatar
unknown committed
7261

7262 7263 7264
  if (table->s->keys > 0)
  {
    int error;
7265
    /* We have a key: search the table using the index */
7266
    if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
7267
      DBUG_RETURN(error);
7268

7269 7270 7271 7272 7273
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
unknown's avatar
unknown committed
7274 7275
    DBUG_DUMP("table->record[0]", (const char *)table->record[0], table->s->reclength);
    DBUG_DUMP("table->record[1]", (const char *)table->record[1], table->s->reclength);
7276 7277
#endif

7278 7279 7280 7281 7282 7283
    /*
      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.
    */
7284 7285 7286
    my_ptrdiff_t const pos=
      table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
    table->record[1][pos]= 0xFF;
unknown's avatar
unknown committed
7287 7288
    if ((error= table->file->index_read(table->record[1], key, HA_WHOLE_KEY,
                                        HA_READ_KEY_EXACT)))
7289 7290
    {
      table->file->print_error(error, MYF(0));
7291
      table->file->ha_index_end();
7292 7293 7294
      DBUG_RETURN(error);
    }

7295 7296 7297 7298 7299
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
unknown's avatar
unknown committed
7300 7301
    DBUG_DUMP("table->record[0]", (const char *)table->record[0], table->s->reclength);
    DBUG_DUMP("table->record[1]", (const char *)table->record[1], table->s->reclength);
7302
#endif
7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315
    /*
      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.
7316
    */
7317
    if (table->key_info->flags & HA_NOSAME)
7318 7319
    {
      table->file->ha_index_end();
7320
      DBUG_RETURN(0);
7321
    }
7322

7323
    while (record_compare(table))
7324 7325
    {
      int error;
7326

7327 7328 7329 7330 7331
      /*
        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.
7332 7333 7334

        TODO[record format ndb]: Remove this code once NDB returns the
        correct record format.
7335
      */
7336 7337 7338 7339 7340 7341
      if (table->s->null_bytes > 0)
      {
        table->record[1][table->s->null_bytes - 1]|=
          256U - (1U << table->s->last_null_bit_pos);
      }

7342
      if ((error= table->file->index_next(table->record[1])))
7343 7344
      {
	table->file->print_error(error, MYF(0));
7345
        table->file->ha_index_end();
7346 7347 7348
	DBUG_RETURN(error);
      }
    }
7349 7350 7351 7352 7353

    /*
      Have to restart the scan to be able to fetch the next row.
    */
    table->file->ha_index_end();
7354 7355 7356 7357
  }
  else
  {
    int restart_count= 0; // Number of times scanning has restarted from top
7358 7359 7360 7361 7362 7363 7364
    int error;

    /* We don't have a key: search the table using rnd_next() */
    if ((error= table->file->ha_rnd_init(1)))
      return error;

    /* Continue until we find the right record or have made a full loop */
7365 7366
    do
    {
7367
      error= table->file->rnd_next(table->record[1]);
7368

7369 7370
      DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
      DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
7371

7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384
      switch (error)
      {
      case 0:
      case HA_ERR_RECORD_DELETED:
	break;

      case HA_ERR_END_OF_FILE:
	if (++restart_count < 2)
	  table->file->ha_rnd_init(1);
	break;

      default:
	table->file->print_error(error, MYF(0));
7385
        DBUG_PRINT("info", ("Record not found"));
7386
        table->file->ha_rnd_end();
7387 7388 7389
	DBUG_RETURN(error);
      }
    }
7390
    while (restart_count < 2 && record_compare(table));
7391

7392 7393 7394
    /*
      Have to restart the scan to be able to fetch the next row.
    */
7395
    DBUG_PRINT("info", ("Record %sfound", restart_count == 2 ? "not " : ""));
7396 7397
    table->file->ha_rnd_end();

7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415
    DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
    DBUG_RETURN(error);
  }

  DBUG_RETURN(0);
}
#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)
#ifdef HAVE_REPLICATION
7416
  ,m_memory(NULL), m_key(NULL), m_after_image(NULL)
7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432
#endif
{
}
#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)
#if defined(MYSQL_CLIENT)
  : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
#else
  : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event),
7433
    m_memory(NULL), m_key(NULL), m_after_image(NULL)
7434 7435 7436 7437 7438 7439 7440 7441 7442 7443
#endif
{
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int Delete_rows_log_event::do_before_row_operations(TABLE *table)
{
  DBUG_ASSERT(m_memory == NULL);

7444
  if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
7445 7446 7447
      table->s->primary_key < MAX_KEY)
  {
    /*
7448
      We don't need to allocate any memory for m_after_image and
7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459
      m_key since they are not used.
    */
    return 0;
  }

  int error= 0;

  if (table->s->keys > 0)
  {
    m_memory=
      my_multi_malloc(MYF(MY_WME),
7460
		      &m_after_image, table->s->reclength,
7461 7462 7463 7464 7465
		      &m_key, table->key_info->key_length,
		      NULL);
  }
  else
  {
7466 7467
    m_after_image= (byte*)my_malloc(table->s->reclength, MYF(MY_WME));
    m_memory= (gptr)m_after_image;
7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480
    m_key= NULL;
  }
  if (!m_memory)
    return HA_ERR_OUT_OF_MEM;

  return error;
}

int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error)
{
  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
  table->file->ha_index_or_rnd_end();
  my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
7481
  m_memory= NULL;
7482
  m_after_image= NULL;
7483
  m_key= NULL;
7484 7485 7486 7487

  return error;
}

7488
int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
7489
                                          TABLE *table,
7490 7491
                                          char const *const row_start,
                                          char const **const row_end)
7492
{
7493 7494
  int error;
  DBUG_ASSERT(row_start && row_end);
7495 7496 7497 7498 7499 7500
  /*
    This assertion actually checks that there is at least as many
    columns on the slave as on the master.
  */
  DBUG_ASSERT(table->s->fields >= m_width);

7501 7502
  error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
                    &m_master_reclength, table->read_set, DELETE_ROWS_EVENT);
7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513
  /*
    If we will access rows using the random access method, m_key will
    be set to NULL, so we do not need to make a key copy in that case.
   */
  if (m_key)
  {
    KEY *const key_info= table->key_info;

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

7514
  return error;
7515 7516 7517 7518
}

int Delete_rows_log_event::do_exec_row(TABLE *table)
{
7519
  int error;
7520 7521
  DBUG_ASSERT(table != NULL);

7522 7523 7524 7525 7526 7527 7528 7529
  if (!(error= find_and_fetch_row(table, m_key)))
  { 
    /*
      Now we should have the right row to delete.  We are using
      record[0] since it is guaranteed to point to a record with the
      correct value.
    */
    error= table->file->ha_delete_row(table->record[0]);
7530
  }
7531 7532 7533 7534 7535 7536 7537 7538 7539
  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)
{
7540
  Rows_log_event::print_helper(file, print_event_info, "Delete_rows");
7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553
}
#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,
7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569
                                             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)
#ifdef HAVE_REPLICATION
  , m_memory(NULL), m_key(NULL)

#endif
{
  init(cols_ai);
}

Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
                                             ulong tid,
                                             MY_BITMAP const *cols,
7570 7571 7572 7573 7574 7575
                                             bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
#ifdef HAVE_REPLICATION
  , m_memory(NULL), m_key(NULL)
#endif
{
7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590
  init(cols);
}

void Update_rows_log_event::init(MY_BITMAP const *cols)
{
  /* if bitmap_init fails, catched in is_valid() */
  if (likely(!bitmap_init(&m_cols_ai,
                          m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
                          (m_width + 7) & ~7UL,
                          false)))
  {
    /* Cols can be zero if this is a dummy binrows event */
    if (likely(cols != NULL))
      memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols));
  }
7591 7592 7593
}
#endif /* !defined(MYSQL_CLIENT) */

7594 7595 7596 7597 7598 7599 7600 7601 7602

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().
}


7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631
/*
  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)
#if defined(MYSQL_CLIENT)
  : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
#else
  : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event),
    m_memory(NULL), m_key(NULL)
#endif
{
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int Update_rows_log_event::do_before_row_operations(TABLE *table)
{
  DBUG_ASSERT(m_memory == NULL);

  int error= 0;

  if (table->s->keys > 0)
  {
    m_memory=
      my_multi_malloc(MYF(MY_WME),
7632
		      &m_after_image, table->s->reclength,
7633 7634 7635 7636 7637
		      &m_key, table->key_info->key_length,
		      NULL);
  }
  else
  {
7638 7639
    m_after_image= (byte*)my_malloc(table->s->reclength, MYF(MY_WME));
    m_memory= (gptr)m_after_image;
7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654
    m_key= NULL;
  }
  if (!m_memory)
    return HA_ERR_OUT_OF_MEM;

  table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;

  return error;
}

int Update_rows_log_event::do_after_row_operations(TABLE *table, int error)
{
  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
  table->file->ha_index_or_rnd_end();
  my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
7655
  m_memory= NULL;
7656
  m_after_image= NULL;
7657
  m_key= NULL;
7658 7659 7660 7661

  return error;
}

7662
int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
7663
                                          TABLE *table,
7664 7665
                                          char const *const row_start,
                                          char const **const row_end)
7666
{
7667 7668
  int error;
  DBUG_ASSERT(row_start && row_end);
7669 7670 7671 7672 7673 7674
  /*
    This assertion actually checks that there is at least as many
    columns on the slave as on the master.
  */
  DBUG_ASSERT(table->s->fields >= m_width);

7675 7676 7677 7678 7679 7680
  /*
    We need to perform some juggling below since unpack_row() always
    unpacks into table->record[0]. For more information, see the
    comments for unpack_row().
  */

7681
  /* record[0] is the before image for the update */
7682 7683 7684
  error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
                    &m_master_reclength, table->read_set, UPDATE_ROWS_EVENT);
  store_record(table, record[1]);
7685
  char const *next_start = *row_end;
7686
  /* m_after_image is the after image for the update */
7687
  error= unpack_row(rli, table, m_width, next_start, &m_cols_ai, row_end,
7688 7689 7690
                    &m_master_reclength, table->write_set, UPDATE_ROWS_EVENT);
  bmove_align(m_after_image, table->record[0], table->s->reclength);
  restore_record(table, record[1]);
7691

7692 7693 7694 7695 7696
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
7697 7698
  DBUG_DUMP("record[0]", (const char *)table->record[0], table->s->reclength);
  DBUG_DUMP("m_after_image", (const char *)m_after_image, table->s->reclength);
7699
#endif
7700

7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711
  /*
    If we will access rows using the random access method, m_key will
    be set to NULL, so we do not need to make a key copy in that case.
   */
  if (m_key)
  {
    KEY *const key_info= table->key_info;

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

7712
  return error;
7713 7714 7715 7716 7717 7718
}

int Update_rows_log_event::do_exec_row(TABLE *table)
{
  DBUG_ASSERT(table != NULL);

7719
  int error= find_and_fetch_row(table, m_key);
7720 7721 7722
  if (error)
    return error;

7723
  /*
7724 7725 7726 7727 7728 7729
    We have to ensure that the new record (i.e., the after image) is
    in record[0] and the old record (i.e., the before image) is in
    record[1].  This since some storage engines require this (for
    example, the partition engine).

    Since find_and_fetch_row() puts the fetched record (i.e., the old
7730 7731 7732 7733 7734
    record) in record[1], we can keep it there. We put the new record
    (i.e., the after image) into record[0], and copy the fields that
    are on the slave (i.e., in record[1]) into record[0], effectively
    overwriting the default values that where put there by the
    unpack_row() function.
7735
  */
7736
  bmove_align(table->record[0], m_after_image, table->s->reclength);
7737
  copy_extra_record_fields(table, m_master_reclength, m_width);
7738

7739
  /*
7740 7741 7742 7743
    Now we have the right row to update.  The old row (the one we're
    looking for) is in record[1] and the new row has is in record[0].
    We also have copied the original values already in the slave's
    database into the after image delivered from the master.
7744
  */
7745
  error= table->file->ha_update_row(table->record[1], table->record[0]);
7746 7747 7748 7749 7750 7751 7752 7753 7754

  return error;
}
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Update_rows_log_event::print(FILE *file,
				  PRINT_EVENT_INFO* print_event_info)
{
7755
  Rows_log_event::print_helper(file, print_event_info, "Update_rows");
7756 7757 7758
}
#endif

7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775

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
7776 7777
  uint8 len= 0;                   // Assignment to keep compiler happy
  const char *str= NULL;          // Assignment to keep compiler happy
7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839
  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);
  DBUG_ASSERT((my_size_t) m_incident <= sizeof(description)/sizeof(*description));

  return description[m_incident];
}


#ifndef MYSQL_CLIENT
void Incident_log_event::pack_info(Protocol *protocol)
{
  char buf[256];
  my_size_t bytes;
  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
7840
Incident_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
7841
{
7842
  DBUG_ENTER("Incident_log_event::do_apply_event");
7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868
  slave_print_msg(ERROR_LEVEL, rli, ER_SLAVE_INCIDENT,
                  ER(ER_SLAVE_INCIDENT),
                  description(),
                  m_message.length > 0 ? m_message.str : "<none>");
  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));
  byte buf[sizeof(int16)];
  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));
}