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

/*
  Size of buffer for printing a double in format %.<PREC>g

  optional '-' + optional zero + '.'  + PREC digits + 'e' + sign +
  exponent digits + '\0'
*/
#define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1)


49
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
static const char *HA_ERR(int i)
{
  switch (i) {
  case HA_ERR_KEY_NOT_FOUND: return "HA_ERR_KEY_NOT_FOUND";
  case HA_ERR_FOUND_DUPP_KEY: return "HA_ERR_FOUND_DUPP_KEY";
  case HA_ERR_RECORD_CHANGED: return "HA_ERR_RECORD_CHANGED";
  case HA_ERR_WRONG_INDEX: return "HA_ERR_WRONG_INDEX";
  case HA_ERR_CRASHED: return "HA_ERR_CRASHED";
  case HA_ERR_WRONG_IN_RECORD: return "HA_ERR_WRONG_IN_RECORD";
  case HA_ERR_OUT_OF_MEM: return "HA_ERR_OUT_OF_MEM";
  case HA_ERR_NOT_A_TABLE: return "HA_ERR_NOT_A_TABLE";
  case HA_ERR_WRONG_COMMAND: return "HA_ERR_WRONG_COMMAND";
  case HA_ERR_OLD_FILE: return "HA_ERR_OLD_FILE";
  case HA_ERR_NO_ACTIVE_RECORD: return "HA_ERR_NO_ACTIVE_RECORD";
  case HA_ERR_RECORD_DELETED: return "HA_ERR_RECORD_DELETED";
  case HA_ERR_RECORD_FILE_FULL: return "HA_ERR_RECORD_FILE_FULL";
  case HA_ERR_INDEX_FILE_FULL: return "HA_ERR_INDEX_FILE_FULL";
  case HA_ERR_END_OF_FILE: return "HA_ERR_END_OF_FILE";
  case HA_ERR_UNSUPPORTED: return "HA_ERR_UNSUPPORTED";
  case HA_ERR_TO_BIG_ROW: return "HA_ERR_TO_BIG_ROW";
  case HA_WRONG_CREATE_OPTION: return "HA_WRONG_CREATE_OPTION";
  case HA_ERR_FOUND_DUPP_UNIQUE: return "HA_ERR_FOUND_DUPP_UNIQUE";
  case HA_ERR_UNKNOWN_CHARSET: return "HA_ERR_UNKNOWN_CHARSET";
  case HA_ERR_WRONG_MRG_TABLE_DEF: return "HA_ERR_WRONG_MRG_TABLE_DEF";
  case HA_ERR_CRASHED_ON_REPAIR: return "HA_ERR_CRASHED_ON_REPAIR";
  case HA_ERR_CRASHED_ON_USAGE: return "HA_ERR_CRASHED_ON_USAGE";
  case HA_ERR_LOCK_WAIT_TIMEOUT: return "HA_ERR_LOCK_WAIT_TIMEOUT";
  case HA_ERR_LOCK_TABLE_FULL: return "HA_ERR_LOCK_TABLE_FULL";
  case HA_ERR_READ_ONLY_TRANSACTION: return "HA_ERR_READ_ONLY_TRANSACTION";
  case HA_ERR_LOCK_DEADLOCK: return "HA_ERR_LOCK_DEADLOCK";
  case HA_ERR_CANNOT_ADD_FOREIGN: return "HA_ERR_CANNOT_ADD_FOREIGN";
  case HA_ERR_NO_REFERENCED_ROW: return "HA_ERR_NO_REFERENCED_ROW";
  case HA_ERR_ROW_IS_REFERENCED: return "HA_ERR_ROW_IS_REFERENCED";
  case HA_ERR_NO_SAVEPOINT: return "HA_ERR_NO_SAVEPOINT";
  case HA_ERR_NON_UNIQUE_BLOCK_SIZE: return "HA_ERR_NON_UNIQUE_BLOCK_SIZE";
  case HA_ERR_NO_SUCH_TABLE: return "HA_ERR_NO_SUCH_TABLE";
  case HA_ERR_TABLE_EXIST: return "HA_ERR_TABLE_EXIST";
  case HA_ERR_NO_CONNECTION: return "HA_ERR_NO_CONNECTION";
  case HA_ERR_NULL_IN_SPATIAL: return "HA_ERR_NULL_IN_SPATIAL";
  case HA_ERR_TABLE_DEF_CHANGED: return "HA_ERR_TABLE_DEF_CHANGED";
  case HA_ERR_NO_PARTITION_FOUND: return "HA_ERR_NO_PARTITION_FOUND";
  case HA_ERR_RBR_LOGGING_FAILED: return "HA_ERR_RBR_LOGGING_FAILED";
  case HA_ERR_DROP_INDEX_FK: return "HA_ERR_DROP_INDEX_FK";
  case HA_ERR_FOREIGN_DUPLICATE_KEY: return "HA_ERR_FOREIGN_DUPLICATE_KEY";
  case HA_ERR_TABLE_NEEDS_UPGRADE: return "HA_ERR_TABLE_NEEDS_UPGRADE";
  case HA_ERR_TABLE_READONLY: return "HA_ERR_TABLE_READONLY";
  case HA_ERR_AUTOINC_READ_FAILED: return "HA_ERR_AUTOINC_READ_FAILED";
  case HA_ERR_AUTOINC_ERANGE: return "HA_ERR_AUTOINC_ERANGE";
  case HA_ERR_GENERIC: return "HA_ERR_GENERIC";
  case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME";
  case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE";
  case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT";
  }
103 104 105 106 107 108 109 110 111 112 113 114
  return 0;
}

/**
   macro to call from different branches of Rows_log_event::do_apply_event
*/
static void inline slave_rows_error_report(enum loglevel level, int ha_error,
                                           Relay_log_info const *rli, THD *thd,
                                           TABLE *table, const char * type,
                                           const char *log_name, ulong pos)
{
  const char *handler_error=  HA_ERR(ha_error);
115
  rli->report(level, thd->net.client_last_errno,
116 117 118 119 120
              "Could not execute %s event on table %s.%s;"
              "%s%s handler error %s; "
              "the event's master log %s, end_log_pos %lu",
              type, table->s->db.str,
              table->s->table_name.str,
121 122
              thd->net.client_last_error[0] != 0 ? thd->net.client_last_error : "",
              thd->net.client_last_error[0] != 0 ? ";" : "",
123 124
              handler_error == NULL? "<unknown>" : handler_error,
              log_name, pos);
125 126 127
}
#endif

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
/*
  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()
  {
172
    copy_event_cache_to_file_and_reinit(m_cache, m_file);
173 174 175 176 177 178 179 180 181 182 183
    if (m_flags | FLUSH_F)
      fflush(m_file);
  }

  /*
    Return a pointer to the internal IO_CACHE.

    SYNOPSIS
      operator&()

    DESCRIPTION
184 185 186 187

      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
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

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

206 207 208
#ifndef DBUG_OFF
uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
#endif
209

unknown's avatar
unknown committed
210
/*
211
  pretty_print_str()
unknown's avatar
unknown committed
212
*/
213

214
#ifdef MYSQL_CLIENT
215
static void pretty_print_str(IO_CACHE* cache, char* str, int len)
unknown's avatar
unknown committed
216
{
217
  char* end = str + len;
218
  my_b_printf(cache, "\'");
219 220
  while (str < end)
  {
unknown's avatar
unknown committed
221
    char c;
222
    switch ((c=*str++)) {
223 224 225 226 227 228 229
    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;
230
    default:
231
      my_b_printf(cache, "%c", c);
232 233
      break;
    }
234
  }
235
  my_b_printf(cache, "\'");
unknown's avatar
unknown committed
236
}
unknown's avatar
unknown committed
237
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
238

unknown's avatar
unknown committed
239
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
240

241
static void clear_all_errors(THD *thd, Relay_log_info *rli)
242
{
243
  thd->is_slave_error = 0;
244
  thd->clear_error();
245
  rli->clear_error();
246 247
}

unknown's avatar
unknown committed
248

unknown's avatar
unknown committed
249
/*
unknown's avatar
unknown committed
250
  Ignore error code specified on command line
unknown's avatar
unknown committed
251
*/
252

253 254
inline int ignored_error_code(int err_code)
{
unknown's avatar
unknown committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268
#ifdef HAVE_NDB_BINLOG
  /*
    The following error codes are hard-coded and will always be ignored.
  */
  switch (err_code)
  {
  case ER_DB_CREATE_EXISTS:
  case ER_DB_DROP_EXISTS:
    return 1;
  default:
    /* Nothing to do */
    break;
  }
#endif
unknown's avatar
unknown committed
269 270
  return ((err_code == ER_SLAVE_IGNORED_TABLE) ||
          (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code)));
271
}
unknown's avatar
SCRUM  
unknown committed
272
#endif
273

unknown's avatar
unknown committed
274

unknown's avatar
unknown committed
275
/*
276
  pretty_print_str()
unknown's avatar
unknown committed
277
*/
unknown's avatar
unknown committed
278

unknown's avatar
unknown committed
279
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
280
static char *pretty_print_str(char *packet, char *str, int len)
unknown's avatar
unknown committed
281
{
unknown's avatar
unknown committed
282 283
  char *end= str + len;
  char *pos= packet;
284
  *pos++= '\'';
285 286 287
  while (str < end)
  {
    char c;
288
    switch ((c=*str++)) {
unknown's avatar
unknown committed
289 290 291 292 293 294 295
    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;
296
    default:
unknown's avatar
unknown committed
297
      *pos++= c;
298 299
      break;
    }
unknown's avatar
unknown committed
300
  }
301 302
  *pos++= '\'';
  return pos;
unknown's avatar
unknown committed
303
}
unknown's avatar
unknown committed
304
#endif /* !MYSQL_CLIENT */
305

unknown's avatar
unknown committed
306

unknown's avatar
unknown committed
307
/*
unknown's avatar
unknown committed
308 309 310 311 312 313 314 315 316 317 318
  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
319
*/
unknown's avatar
unknown committed
320

unknown's avatar
SCRUM  
unknown committed
321
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
322 323
static char *slave_load_file_stem(char *buf, uint file_id,
                                  int event_server_id, const char *ext)
324
{
unknown's avatar
unknown committed
325
  char *res;
326 327 328
  fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", MY_UNPACK_FILENAME);
  to_unix_path(buf);

329 330 331 332 333
  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
334 335 336
  res= int10_to_str(file_id, buf, 10);
  strmov(res, ext);                             // Add extension last
  return res;                                   // Pointer to extension
337
}
unknown's avatar
SCRUM  
unknown committed
338
#endif
339

unknown's avatar
unknown committed
340

unknown's avatar
unknown committed
341
/*
342 343
  Delete all temporary files used for SQL_LOAD.

unknown's avatar
unknown committed
344 345
  SYNOPSIS
    cleanup_load_tmpdir()
unknown's avatar
unknown committed
346
*/
347

unknown's avatar
SCRUM  
unknown committed
348
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
349 350 351 352 353
static void cleanup_load_tmpdir()
{
  MY_DIR *dirp;
  FILEINFO *file;
  uint i;
unknown's avatar
unknown committed
354
  char fname[FN_REFLEN], prefbuf[31], *p;
unknown's avatar
unknown committed
355

356 357 358
  if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME))))
    return;

359 360 361 362 363 364 365 366
  /* 
     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.
  */
367
  p= strmake(prefbuf, STRING_WITH_LEN("SQL_LOAD-"));
368 369 370 371
  p= int10_to_str(::server_id, p, 10);
  *(p++)= '-';
  *p= 0;

372 373 374
  for (i=0 ; i < (uint)dirp->number_off_files; i++)
  {
    file=dirp->dir_entry+i;
375
    if (is_prefix(file->name, prefbuf))
unknown's avatar
unknown committed
376 377 378 379
    {
      fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
      my_delete(fname, MYF(0));
    }
380 381 382 383
  }

  my_dirend(dirp);
}
unknown's avatar
SCRUM  
unknown committed
384
#endif
385 386


unknown's avatar
unknown committed
387
/*
388
  write_str()
unknown's avatar
unknown committed
389
*/
390

391
static bool write_str(IO_CACHE *file, char *str, uint length)
392
{
393 394
  uchar tmp[1];
  tmp[0]= (uchar) length;
395
  return (my_b_safe_write(file, tmp, sizeof(tmp)) ||
396
	  my_b_safe_write(file, (uchar*) str, length));
397 398 399
}


unknown's avatar
unknown committed
400
/*
401
  read_str()
unknown's avatar
unknown committed
402
*/
403

404 405
static inline int read_str(const char **buf, const char *buf_end,
                           const char **str, uint8 *len)
406
{
407
  if (*buf + ((uint) (uchar) **buf) >= buf_end)
408
    return 1;
409 410 411
  *len= (uint8) **buf;
  *str= (*buf)+1;
  (*buf)+= (uint) *len+1;
412 413 414
  return 0;
}

415

416 417 418
/*
  Transforms a string into "" or its expression in 0x... form.
*/
unknown's avatar
unknown committed
419

420
char *str_to_hex(char *to, const char *from, uint len)
421 422 423
{
  if (len)
  {
unknown's avatar
unknown committed
424 425 426
    *to++= '0';
    *to++= 'x';
    to= octet2hex(to, from, len);
427 428
  }
  else
unknown's avatar
unknown committed
429 430
    to= strmov(to, "\"\"");
  return to;                               // pointer to end 0 of 'to'
431 432
}

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
/*
  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++= '\'';
455
    ptr+= escape_string_for_mysql(csinfo, ptr, 0,
456 457 458 459 460 461 462 463
                                  from->ptr(), from->length());
    *ptr++='\'';
  }
  to->length(orig_len + ptr - beg);
  return 0;
}
#endif

464

465 466 467 468 469
/*
  Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
  commands just before it prints a query.
*/

470
#ifdef MYSQL_CLIENT
471

472 473 474
static void print_set_option(IO_CACHE* file, uint32 bits_changed,
                             uint32 option, uint32 flags, const char* name,
                             bool* need_comma)
475 476 477 478
{
  if (bits_changed & option)
  {
    if (*need_comma)
479 480
      my_b_printf(file,", ");
    my_b_printf(file,"%s=%d", name, test(flags & option));
481 482 483
    *need_comma= 1;
  }
}
484
#endif
485

unknown's avatar
unknown committed
486
/**************************************************************************
487
	Log_event methods (= the parent class of all events)
unknown's avatar
unknown committed
488
**************************************************************************/
489

unknown's avatar
unknown committed
490
/*
491
  Log_event::get_type_str()
unknown's avatar
unknown committed
492
*/
493

494
const char* Log_event::get_type_str(Log_event_type type)
unknown's avatar
unknown committed
495
{
496
  switch(type) {
497
  case START_EVENT_V3:  return "Start_v3";
unknown's avatar
unknown committed
498 499 500 501 502
  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";
503
  case NEW_LOAD_EVENT:   return "New_load";
unknown's avatar
unknown committed
504
  case SLAVE_EVENT:  return "Slave";
505 506 507 508
  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";
509
  case RAND_EVENT: return "RAND";
510
  case XID_EVENT: return "Xid";
unknown's avatar
unknown committed
511
  case USER_VAR_EVENT: return "User var";
512
  case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
513
  case TABLE_MAP_EVENT: return "Table_map";
514 515 516
  case PRE_GA_WRITE_ROWS_EVENT: return "Write_rows_event_old";
  case PRE_GA_UPDATE_ROWS_EVENT: return "Update_rows_event_old";
  case PRE_GA_DELETE_ROWS_EVENT: return "Delete_rows_event_old";
517 518 519
  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
520 521
  case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
  case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
522
  case INCIDENT_EVENT: return "Incident";
523
  default: return "Unknown";				/* impossible */
unknown's avatar
unknown committed
524 525 526
  }
}

527 528 529 530 531
const char* Log_event::get_type_str()
{
  return get_type_str(get_type_code());
}

532

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

unknown's avatar
unknown committed
537
#ifndef MYSQL_CLIENT
538
Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
539
  :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg)
540
{
unknown's avatar
unknown committed
541 542
  server_id=	thd->server_id;
  when=		thd->start_time;
543
  cache_stmt=	using_trans;
544 545 546
}


unknown's avatar
unknown committed
547
/*
548 549 550 551
  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
552 553
*/

554
Log_event::Log_event()
555
  :temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
556 557
   thd(0)
{
unknown's avatar
unknown committed
558
  server_id=	::server_id;
559 560 561 562 563
  /*
    We can't call my_time() here as this would cause a call before
    my_init() is called
  */
  when=		0;
unknown's avatar
unknown committed
564
  log_pos=	0;
565
}
unknown's avatar
unknown committed
566
#endif /* !MYSQL_CLIENT */
567 568


unknown's avatar
unknown committed
569
/*
570
  Log_event::Log_event()
571
*/
572

573
Log_event::Log_event(const char* buf,
unknown's avatar
unknown committed
574
                     const Format_description_log_event* description_event)
575
  :temp_buf(0), cache_stmt(0)
576
{
577 578
#ifndef MYSQL_CLIENT
  thd = 0;
unknown's avatar
unknown committed
579
#endif
580 581
  when = uint4korr(buf);
  server_id = uint4korr(buf + SERVER_ID_OFFSET);
582
  if (description_event->binlog_version==1)
583
  {
584 585 586
    log_pos= 0;
    flags= 0;
    return;
587
  }
588 589 590
  /* 4.0 or newer */
  log_pos= uint4korr(buf + LOG_POS_OFFSET);
  /*
591 592 593 594 595 596 597 598
    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).
599 600 601
  */
  if (description_event->binlog_version==3 &&
      buf[EVENT_TYPE_OFFSET]<FORMAT_DESCRIPTION_EVENT && log_pos)
602
  {
603 604 605
      /*
        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
606 607
        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
608
        not have its log_pos (which is 0) changed or it will modify
609 610 611 612
        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).
613 614
      */
    log_pos+= uint4korr(buf + EVENT_LEN_OFFSET);
615
  }
616 617 618 619 620 621 622
  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))
  {
    /*
623 624
      These events always have a header which stops here (i.e. their
      header is FROZEN).
625 626
    */
    /*
627 628 629 630 631 632 633
      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.
634 635 636 637
    */
    return;
  }
  /* otherwise, go on with reading the header from buf (nothing now) */
638 639 640
}

#ifndef MYSQL_CLIENT
unknown's avatar
SCRUM  
unknown committed
641
#ifdef HAVE_REPLICATION
642

643
int Log_event::do_update_pos(Relay_log_info *rli)
644
{
645
  /*
646
    rli is null when (as far as I (Guilhem) know) the caller is
647 648 649 650
    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.
651 652 653
    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.
654 655

    Matz: I don't think we will need this check with this refactoring.
656
  */
657
  if (rli)
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
  {
    /*
      bug#29309 simulation: resetting the flag to force
      wrong behaviour of artificial event to update
      rli->last_master_timestamp for only one time -
      the first FLUSH LOGS in the test.
    */
    DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
                    if (debug_not_change_ts_if_art_event == 1
                        && is_artificial_event())
                    {
                      debug_not_change_ts_if_art_event= 0;
                    });
#ifndef DBUG_OFF
    rli->stmt_done(log_pos, 
                   is_artificial_event() &&
                   debug_not_change_ts_if_art_event > 0 ? 0 : when);
#else
    rli->stmt_done(log_pos, is_artificial_event()? 0 : when);
#endif
    DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
                    if (debug_not_change_ts_if_art_event == 0)
                    {
                      debug_not_change_ts_if_art_event= 2;
                    });
  }
684 685
  return 0;                                   // Cannot fail currently
}
686

687 688

Log_event::enum_skip_reason
689
Log_event::do_shall_skip(Relay_log_info *rli)
690 691 692 693 694 695 696
{
  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));
697 698
  if (server_id == ::server_id && !rli->replicate_same_server_id ||
      rli->slave_skip_counter == 1 && rli->is_in_group())
699
    return EVENT_SKIP_IGNORE;
700 701 702
  else if (rli->slave_skip_counter > 0)
    return EVENT_SKIP_COUNT;
  else
703
    return EVENT_SKIP_NOT;
704
}
unknown's avatar
unknown committed
705

706

unknown's avatar
unknown committed
707
/*
708
  Log_event::pack_info()
unknown's avatar
unknown committed
709
*/
710

711
void Log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
712
{
713
  protocol->store("", &my_charset_bin);
unknown's avatar
unknown committed
714 715 716
}


unknown's avatar
unknown committed
717
/*
718
  Log_event::net_send()
unknown's avatar
unknown committed
719

720
  Only called by SHOW BINLOG EVENTS
unknown's avatar
unknown committed
721
*/
unknown's avatar
SCRUM  
unknown committed
722

723
int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
724
{
unknown's avatar
unknown committed
725 726
  const char *p= strrchr(log_name, FN_LIBCHAR);
  const char *event_type;
727 728
  if (p)
    log_name = p + 1;
729

730
  protocol->prepare_for_resend();
731
  protocol->store(log_name, &my_charset_bin);
732
  protocol->store((ulonglong) pos);
733
  event_type = get_type_str();
734
  protocol->store(event_type, strlen(event_type), &my_charset_bin);
735 736 737 738
  protocol->store((uint32) server_id);
  protocol->store((ulonglong) log_pos);
  pack_info(protocol);
  return protocol->write();
739
}
unknown's avatar
SCRUM  
unknown committed
740 741 742
#endif /* HAVE_REPLICATION */


unknown's avatar
unknown committed
743
/*
unknown's avatar
SCRUM  
unknown committed
744
  Log_event::init_show_field_list()
unknown's avatar
unknown committed
745
*/
unknown's avatar
SCRUM  
unknown committed
746 747 748 749

void Log_event::init_show_field_list(List<Item>* field_list)
{
  field_list->push_back(new Item_empty_string("Log_name", 20));
750
  field_list->push_back(new Item_return_int("Pos", MY_INT32_NUM_DECIMAL_DIGITS,
unknown's avatar
SCRUM  
unknown committed
751 752 753 754
					    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));
755 756
  field_list->push_back(new Item_return_int("End_log_pos",
                                            MY_INT32_NUM_DECIMAL_DIGITS,
unknown's avatar
SCRUM  
unknown committed
757 758 759 760
					    MYSQL_TYPE_LONGLONG));
  field_list->push_back(new Item_empty_string("Info", 20));
}

761

unknown's avatar
unknown committed
762
/*
763
  Log_event::write()
unknown's avatar
unknown committed
764
*/
765

766
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
unknown's avatar
unknown committed
767
{
768
  uchar header[LOG_EVENT_HEADER_LEN];
769
  ulong now;
770
  DBUG_ENTER("Log_event::write_header");
unknown's avatar
unknown committed
771

772 773
  /* Store number of bytes that will be written by this event */
  data_written= event_data_length + sizeof(header);
774

775 776 777 778
  /*
    log_pos != 0 if this is relay-log event. In this case we should not
    change the position
  */
779

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
  if (is_artificial_event())
  {
    /*
      We should not do any cleanup on slave when reading this. We
      mark this by setting log_pos to 0.  Start_log_event_v3() will
      detect this on reading and set artificial_event=1 for the event.
    */
    log_pos= 0;
  }
  else  if (!log_pos)
  {
    /*
      Calculate position of end of event

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

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

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

    log_pos= my_b_safe_tell(file)+data_written;
  }

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

822 823 824 825 826 827 828
  /*
    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).
  */

829
  int4store(header, now);              // timestamp
830 831 832 833 834 835 836
  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
837 838 839
}


unknown's avatar
unknown committed
840
/*
841
  Log_event::read_log_event()
842 843 844 845

  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
846
*/
847

848
int Log_event::read_log_event(IO_CACHE* file, String* packet,
849
			      pthread_mutex_t* log_lock)
unknown's avatar
unknown committed
850 851
{
  ulong data_len;
852
  int result=0;
853
  char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
854
  DBUG_ENTER("Log_event::read_log_event");
855

856
  if (log_lock)
857
    pthread_mutex_lock(log_lock);
858
  if (my_b_read(file, (uchar*) buf, sizeof(buf)))
859
  {
860 861 862 863 864
    /*
      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.
    */
865
    DBUG_PRINT("error",("file->error: %d", file->error));
866 867 868
    if (!file->error)
      result= LOG_READ_EOF;
    else
869
      result= (file->error > 0 ? LOG_READ_TRUNC : LOG_READ_IO);
870
    goto end;
871
  }
872
  data_len= uint4korr(buf + EVENT_LEN_OFFSET);
873
  if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
unknown's avatar
unknown committed
874
      data_len > current_thd->variables.max_allowed_packet)
875
  {
876
    DBUG_PRINT("error",("data_len: %ld", data_len));
877
    result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
878 879
	     LOG_READ_TOO_LARGE);
    goto end;
880
  }
881 882 883 884 885 886 887 888

  /* Append the log event header to packet */
  if (packet->append(buf, sizeof(buf)))
  {
    /* Failed to allocate packet */
    result= LOG_READ_MEM;
    goto end;
  }
889
  data_len-= LOG_EVENT_MINIMAL_HEADER_LEN;
890 891
  if (data_len)
  {
892
    /* Append rest of event, read directly from file into packet */
893
    if (packet->append(file, data_len))
894
    {
895
      /*
896 897 898 899 900 901 902 903 904 905
        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)
906
      */
907 908
      result= (my_errno == ENOMEM ? LOG_READ_MEM :
               (file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO));
909
      /* Implicit goto end; */
910
    }
911
  }
912 913 914 915

end:
  if (log_lock)
    pthread_mutex_unlock(log_lock);
916
  DBUG_RETURN(result);
unknown's avatar
unknown committed
917
}
unknown's avatar
unknown committed
918
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
919

unknown's avatar
unknown committed
920
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
921 922
#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock);
#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock);
923
#else
924
#define UNLOCK_MUTEX
925 926 927
#define LOCK_MUTEX
#endif

unknown's avatar
unknown committed
928
/*
929 930
  Log_event::read_log_event()

unknown's avatar
unknown committed
931
  NOTE:
932
    Allocates memory;  The caller is responsible for clean-up.
unknown's avatar
unknown committed
933
*/
934

unknown's avatar
unknown committed
935
#ifndef MYSQL_CLIENT
936 937
Log_event* Log_event::read_log_event(IO_CACHE* file,
				     pthread_mutex_t* log_lock,
938 939
                                     const Format_description_log_event
                                     *description_event)
unknown's avatar
unknown committed
940
#else
941
Log_event* Log_event::read_log_event(IO_CACHE* file,
942 943
                                     const Format_description_log_event
                                     *description_event)
944
#endif
unknown's avatar
unknown committed
945
{
946
  DBUG_ENTER("Log_event::read_log_event");
947
  DBUG_ASSERT(description_event != 0);
948 949 950 951 952
  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
953 954
    of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's
    "minimal" over the set {MySQL >=4.0}).
955 956 957
  */
  uint header_size= min(description_event->common_header_len,
                        LOG_EVENT_MINIMAL_HEADER_LEN);
958

959
  LOCK_MUTEX;
unknown's avatar
unknown committed
960
  DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file)));
961
  if (my_b_read(file, (uchar *) head, header_size))
962
  {
963 964
    DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
failed my_b_read"));
unknown's avatar
unknown committed
965
    UNLOCK_MUTEX;
966
    /*
967 968 969
      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.
970
    */
971
    DBUG_RETURN(0);
972
  }
973
  uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
974 975 976
  char *buf= 0;
  const char *error= 0;
  Log_event *res=  0;
977 978
#ifndef max_allowed_packet
  THD *thd=current_thd;
unknown's avatar
unknown committed
979
  uint max_allowed_packet= thd ? thd->variables.max_allowed_packet : ~(ulong)0;
980
#endif
unknown's avatar
unknown committed
981

982
  if (data_len > max_allowed_packet)
unknown's avatar
unknown committed
983
  {
984 985
    error = "Event too big";
    goto err;
unknown's avatar
unknown committed
986 987
  }

988
  if (data_len < header_size)
unknown's avatar
unknown committed
989
  {
990 991
    error = "Event too small";
    goto err;
unknown's avatar
unknown committed
992
  }
993 994

  // some events use the extra byte to null-terminate strings
995
  if (!(buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
996 997 998
  {
    error = "Out of memory";
    goto err;
unknown's avatar
unknown committed
999
  }
1000
  buf[data_len] = 0;
1001
  memcpy(buf, head, header_size);
1002
  if (my_b_read(file, (uchar*) buf + header_size, data_len - header_size))
1003 1004 1005 1006
  {
    error = "read error";
    goto err;
  }
1007
  if ((res= read_log_event(buf, data_len, &error, description_event)))
1008
    res->register_temp_buf(buf);
1009

1010
err:
unknown's avatar
unknown committed
1011
  UNLOCK_MUTEX;
1012
  if (!res)
1013
  {
1014
    DBUG_ASSERT(error != 0);
1015 1016
    sql_print_error("Error in Log_event::read_log_event(): "
                    "'%s', data_len: %d, event_type: %d",
1017
		    error,data_len,head[EVENT_TYPE_OFFSET]);
1018
    my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
1019 1020 1021 1022 1023 1024 1025 1026 1027
    /*
      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;
1028
  }
1029
  DBUG_RETURN(res);
unknown's avatar
unknown committed
1030 1031
}

1032

unknown's avatar
unknown committed
1033
/*
1034
  Log_event::read_log_event()
1035 1036
  Binlog format tolerance is in (buf, event_len, description_event)
  constructors.
unknown's avatar
unknown committed
1037
*/
1038

1039 1040 1041
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
1042
{
1043 1044
  Log_event* ev;
  DBUG_ENTER("Log_event::read_log_event(char*,...)");
1045
  DBUG_ASSERT(description_event != 0);
1046
  DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
1047 1048
  DBUG_DUMP("data", (unsigned char*) buf, event_len);

1049
  /* Check the integrity */
1050
  if (event_len < EVENT_LEN_OFFSET ||
1051
      buf[EVENT_TYPE_OFFSET] >= ENUM_END_EVENT ||
1052 1053 1054
      (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
  {
    *error="Sanity check failed";		// Needed to free buffer
unknown's avatar
unknown committed
1055
    DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
1056
  }
1057

1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
  uint event_type= buf[EVENT_TYPE_OFFSET];
  if (event_type > description_event->number_of_event_types &&
      event_type != FORMAT_DESCRIPTION_EVENT)
  {
    /*
      It is unsafe to use the description_event if its post_header_len
      array does not include the event type.
    */
    DBUG_PRINT("error", ("event type %d found, but the current "
                         "Format_description_log_event supports only %d event "
                         "types", event_type,
                         description_event->number_of_event_types));
    ev= NULL;
  }
  else
  {
    switch(event_type) {
    case QUERY_EVENT:
      ev  = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
      break;
    case LOAD_EVENT:
      ev = new Load_log_event(buf, event_len, description_event);
      break;
    case NEW_LOAD_EVENT:
      ev = new Load_log_event(buf, event_len, description_event);
      break;
    case ROTATE_EVENT:
      ev = new Rotate_log_event(buf, event_len, description_event);
      break;
unknown's avatar
SCRUM  
unknown committed
1087
#ifdef HAVE_REPLICATION
1088 1089 1090
    case SLAVE_EVENT: /* can never happen (unused event) */
      ev = new Slave_log_event(buf, event_len);
      break;
unknown's avatar
SCRUM  
unknown committed
1091
#endif /* HAVE_REPLICATION */
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
    case CREATE_FILE_EVENT:
      ev = new Create_file_log_event(buf, event_len, description_event);
      break;
    case APPEND_BLOCK_EVENT:
      ev = new Append_block_log_event(buf, event_len, description_event);
      break;
    case DELETE_FILE_EVENT:
      ev = new Delete_file_log_event(buf, event_len, description_event);
      break;
    case EXEC_LOAD_EVENT:
      ev = new Execute_load_log_event(buf, event_len, description_event);
      break;
    case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
      ev = new Start_log_event_v3(buf, description_event);
      break;
    case STOP_EVENT:
      ev = new Stop_log_event(buf, description_event);
      break;
    case INTVAR_EVENT:
      ev = new Intvar_log_event(buf, description_event);
      break;
    case XID_EVENT:
      ev = new Xid_log_event(buf, description_event);
      break;
    case RAND_EVENT:
      ev = new Rand_log_event(buf, description_event);
      break;
    case USER_VAR_EVENT:
      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);
      break;
1125
#if defined(HAVE_REPLICATION) 
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
    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;
    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;
1147
#endif
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
    case BEGIN_LOAD_QUERY_EVENT:
      ev = new Begin_load_query_log_event(buf, event_len, description_event);
      break;
    case EXECUTE_LOAD_QUERY_EVENT:
      ev= new Execute_load_query_log_event(buf, event_len, description_event);
      break;
    case INCIDENT_EVENT:
      ev = new Incident_log_event(buf, event_len, description_event);
      break;
    default:
      DBUG_PRINT("error",("Unknown event code: %d",
                          (int) buf[EVENT_TYPE_OFFSET]));
      ev= NULL;
      break;
    }
unknown's avatar
unknown committed
1163
  }
1164

1165 1166 1167 1168
  DBUG_PRINT("read_event", ("%s(type_code: %d; event_len: %d)",
                            ev ? ev->get_type_str() : "<unknown>",
                            buf[EVENT_TYPE_OFFSET],
                            event_len));
1169
  /*
1170 1171 1172 1173 1174 1175 1176
    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'.
1177
  */
1178
  if (!ev || !ev->is_valid())
1179
  {
1180 1181
    DBUG_PRINT("error",("Found invalid event in binary log"));

1182
    delete ev;
1183
#ifdef MYSQL_CLIENT
1184
    if (!force_opt) /* then mysqlbinlog dies */
1185 1186
    {
      *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
1187
      DBUG_RETURN(0);
1188
    }
1189
    ev= new Unknown_log_event(buf, description_event);
1190 1191
#else
    *error= "Found invalid event in binary log";
unknown's avatar
unknown committed
1192
    DBUG_RETURN(0);
1193
#endif
1194
  }
unknown's avatar
unknown committed
1195
  DBUG_RETURN(ev);  
unknown's avatar
unknown committed
1196 1197
}

1198
#ifdef MYSQL_CLIENT
1199

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

1204 1205 1206
void Log_event::print_header(IO_CACHE* file,
                             PRINT_EVENT_INFO* print_event_info,
                             bool is_more __attribute__((unused)))
1207
{
1208
  char llbuff[22];
unknown's avatar
unknown committed
1209
  my_off_t hexdump_from= print_event_info->hexdump_from;
1210
  DBUG_ENTER("Log_event::print_header");
unknown's avatar
unknown committed
1211

1212
  my_b_printf(file, "#");
1213
  print_timestamp(file);
1214 1215
  my_b_printf(file, " server id %d  end_log_pos %s ", server_id,
              llstr(log_pos,llbuff));
1216

1217
  /* mysqlbinlog --hexdump */
unknown's avatar
unknown committed
1218
  if (print_event_info->hexdump_from)
1219
  {
1220
    my_b_printf(file, "\n");
1221 1222 1223
    uchar *ptr= (uchar*)temp_buf;
    my_off_t size=
      uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN;
1224 1225
    my_off_t i;

1226 1227 1228 1229
    /* 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
1230
    /* Pretty-print event common header if header is exactly 19 bytes */
unknown's avatar
unknown committed
1231
    if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
unknown's avatar
unknown committed
1232
    {
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
      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);
1246 1247
      DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
      my_b_write(file, (uchar*) emit_buf, bytes_written);
unknown's avatar
unknown committed
1248 1249 1250
      ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
      hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
    }
1251 1252 1253 1254 1255

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

1260
      *c++= my_isalnum(&my_charset_bin, *ptr) ? *ptr : '.';
1261 1262 1263

      if (i % 16 == 15)
      {
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
        /*
          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);
1277 1278
        DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
	my_b_write(file, (uchar*) emit_buf, bytes_written);
1279 1280 1281 1282
	hex_string[0]= 0;
	char_string[0]= 0;
	c= char_string;
	h= hex_string;
1283
      }
1284
      else if (i % 8 == 7) *h++ = ' ';
1285
    }
1286
    *c= '\0';
1287

unknown's avatar
unknown committed
1288
    if (hex_string[0])
1289 1290 1291 1292
    {
      char emit_buf[256];
      int const bytes_written=
        my_snprintf(emit_buf, sizeof(emit_buf),
1293
                    "# %8.8lx %-48.48s |%s|\n",
1294 1295 1296
                    (unsigned long) (hexdump_from + (i & 0xfffffff0)),
                    hex_string, char_string);
      DBUG_ASSERT(bytes_written >= 0);
1297 1298
      DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
      my_b_write(file, (uchar*) emit_buf, bytes_written);
1299
    }
1300 1301 1302 1303
    /*
      need a # to prefix the rest of printouts for example those of
      Rows_log_event::print_helper().
    */
1304
    my_b_write(file, reinterpret_cast<const uchar*>("# "), 2);
1305
  }
1306
  DBUG_VOID_RETURN;
1307 1308
}

1309

1310 1311 1312
void Log_event::print_base64(IO_CACHE* file,
                             PRINT_EVENT_INFO* print_event_info,
                             bool more)
1313
{
1314
  const uchar *ptr= (const uchar *)temp_buf;
1315
  uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
1316 1317
  DBUG_ENTER("Log_event::print_base64");

1318
  size_t const tmp_str_sz= base64_needed_encoded_length((int) size);
1319
  char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME));
1320 1321 1322
  if (!tmp_str) {
    fprintf(stderr, "\nError: Out of memory. "
            "Could not print correct binlog event.\n");
1323
    DBUG_VOID_RETURN;
1324
  }
1325

unknown's avatar
unknown committed
1326 1327 1328 1329
  if (base64_encode(ptr, (size_t) size, tmp_str))
  {
    DBUG_ASSERT(0);
  }
1330 1331 1332 1333 1334 1335 1336

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

1339
  my_free(tmp_str, MYF(0));
1340
  DBUG_VOID_RETURN;
1341 1342 1343
}


unknown's avatar
unknown committed
1344
/*
1345
  Log_event::print_timestamp()
unknown's avatar
unknown committed
1346
*/
1347

1348
void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
unknown's avatar
unknown committed
1349
{
unknown's avatar
unknown committed
1350
  struct tm *res;
1351
  DBUG_ENTER("Log_event::print_timestamp");
1352 1353
  if (!ts)
    ts = &when;
1354 1355
#ifdef MYSQL_SERVER				// This is always false
  struct tm tm_tmp;
unknown's avatar
unknown committed
1356
  localtime_r(ts,(res= &tm_tmp));
unknown's avatar
unknown committed
1357
#else
1358
  res=localtime(ts);
unknown's avatar
unknown committed
1359
#endif
1360

1361 1362 1363 1364 1365 1366 1367 1368
  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
1369 1370
}

unknown's avatar
unknown committed
1371
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
1372 1373


1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
inline Log_event::enum_skip_reason
Log_event::continue_group(Relay_log_info *rli)
{
  if (rli->slave_skip_counter == 1)
    return Log_event::EVENT_SKIP_IGNORE;
  return Log_event::do_shall_skip(rli);
}
#endif

unknown's avatar
unknown committed
1384
/**************************************************************************
unknown's avatar
unknown committed
1385
	Query_log_event methods
unknown's avatar
unknown committed
1386
**************************************************************************/
1387

unknown's avatar
SCRUM  
unknown committed
1388
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
1389

unknown's avatar
unknown committed
1390
/*
1391
  Query_log_event::pack_info()
1392 1393 1394 1395
  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
1396
*/
1397

1398
void Query_log_event::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
1399
{
1400
  // TODO: show the catalog ??
1401
  char *buf, *pos;
1402
  if (!(buf= (char*) my_malloc(9 + db_len + q_len, MYF(MY_WME))))
1403
    return;
1404 1405
  pos= buf;
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
1406
      && db && db_len)
1407
  {
1408 1409
    pos= strmov(buf, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
1410
    pos= strmov(pos+db_len, "`; ");
1411
  }
1412
  if (query && q_len)
1413 1414 1415 1416
  {
    memcpy(pos, query, q_len);
    pos+= q_len;
  }
1417
  protocol->store(buf, pos-buf, &my_charset_bin);
1418
  my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
1419
}
unknown's avatar
SCRUM  
unknown committed
1420
#endif
1421

1422
#ifndef MYSQL_CLIENT
1423

1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
/* 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
1436
/*
1437
  Query_log_event::write()
unknown's avatar
unknown committed
1438

1439 1440 1441 1442
  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
1443
*/
1444

1445
bool Query_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
1446
{
1447 1448 1449 1450 1451
  /**
    @todo if catalog can be of length FN_REFLEN==512, then we are not
    replicating it correctly, since the length is stored in a byte
    /sven
  */
1452 1453 1454 1455 1456
  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
1457
            1+6+           // code of charset and charset
1458
            1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name
1459 1460
            1+2+           // code of lc_time_names and lc_time_names_number
            1+2            // code of charset_database and charset_database_number
1461
            ], *start, *start_of_status;
1462
  ulong event_length;
unknown's avatar
unknown committed
1463

1464
  if (!query)
1465 1466
    return 1;                                   // Something wrong with event

unknown's avatar
unknown committed
1467 1468 1469 1470 1471
  /*
    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.
1472
    Now imagine we (write()) are called by the slave SQL thread (we are
unknown's avatar
unknown committed
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
    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);
1505 1506 1507 1508
  int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
  buf[Q_DB_LEN_OFFSET] = (char) db_len;
  int2store(buf + Q_ERR_CODE_OFFSET, error_code);

1509 1510 1511 1512 1513 1514 1515 1516
  /*
    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)
  {
1517
    *start++= Q_FLAGS2_CODE;
1518 1519 1520 1521 1522
    int4store(start, flags2);
    start+= 4;
  }
  if (sql_mode_inited)
  {
1523
    *start++= Q_SQL_MODE_CODE;
unknown's avatar
unknown committed
1524
    int8store(start, (ulonglong)sql_mode);
1525 1526
    start+= 8;
  }
1527
  if (catalog_len) // i.e. this var is inited (false for 4.0 events)
1528
  {
1529 1530
    write_str_with_code_and_len((char **)(&start),
                                catalog, catalog_len, Q_CATALOG_NZ_CODE);
1531
    /*
1532 1533 1534 1535 1536 1537
      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
1538 1539 1540 1541 1542
      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
1543 1544
      Q_AUTO_INCREMENT*, Q_CHARSET and so replication will fail silently in
      various ways. Documented that you should not mix alpha/beta versions if
1545 1546 1547
      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.
1548 1549 1550 1551 1552 1553 1554 1555 1556
    */
  }
  if (auto_increment_increment != 1)
  {
    *start++= Q_AUTO_INCREMENT;
    int2store(start, auto_increment_increment);
    int2store(start+2, auto_increment_offset);
    start+= 4;
  }
1557 1558
  if (charset_inited)
  {
1559
    *start++= Q_CHARSET_CODE;
1560 1561 1562
    memcpy(start, charset, 6);
    start+= 6;
  }
1563 1564 1565 1566 1567 1568 1569 1570 1571
  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;
  }
1572 1573 1574 1575 1576 1577 1578
  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;
  }
1579 1580 1581 1582 1583 1584 1585
  if (charset_database_number)
  {
    DBUG_ASSERT(charset_database_number <= 0xFFFF);
    *start++= Q_CHARSET_DATABASE_CODE;
    int2store(start, charset_database_number);
    start+= 2;
  }
1586 1587
  /*
    Here there could be code like
1588
    if (command-line-option-which-says-"log_this_variable" && inited)
1589
    {
1590
    *start++= Q_THIS_VARIABLE_CODE;
1591 1592 1593 1594 1595 1596 1597
    int4store(start, this_variable);
    start+= 4;
    }
  */
  
  /* Store length of status variables */
  status_vars_len= (uint) (start-start_of_status);
1598
  DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS);
1599 1600 1601 1602 1603 1604
  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
1605
  event_length= (uint) (start-buf) + get_post_header_size_for_derived() + db_len + 1 + q_len;
1606 1607

  return (write_header(file, event_length) ||
1608
          my_b_safe_write(file, (uchar*) buf, QUERY_HEADER_LEN) ||
unknown's avatar
unknown committed
1609
          write_post_header_for_derived(file) ||
1610
          my_b_safe_write(file, (uchar*) start_of_status,
unknown's avatar
unknown committed
1611
                          (uint) (start-start_of_status)) ||
1612 1613
          my_b_safe_write(file, (db) ? (uchar*) db : (uchar*)"", db_len + 1) ||
          my_b_safe_write(file, (uchar*) query, q_len)) ? 1 : 0;
unknown's avatar
unknown committed
1614 1615
}

1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
/*
  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)
{
}

1628

unknown's avatar
unknown committed
1629
/*
1630 1631
  SYNOPSIS
    Query_log_event::Query_log_event()
1632
      thd_arg           - thread handle
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644
      query_arg         - array of char representing the query
      query_length      - size of the  `query_arg' array
      using_trans       - there is a modified transactional table
      suppress_use      - suppress the generation of 'USE' statements
      killed_status_arg - an optional with default to THD::KILLED_NO_VALUE
                          if the value is different from the default, the arg
                          is set to the current thd->killed value.
                          A caller might need to masquerade thd->killed with
                          THD::NOT_KILLED.
  DESCRIPTION
  Creates an event for binlogging
  The value for local `killed_status' can be supplied by caller.
unknown's avatar
unknown committed
1645
*/
1646
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
1647
				 ulong query_length, bool using_trans,
1648 1649
				 bool suppress_use,
                                 THD::killed_state killed_status_arg)
1650
  :Log_event(thd_arg,
1651 1652 1653
             (thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F :
              0) |
             (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
1654
	     using_trans),
1655
   data_buf(0), query(query_arg), catalog(thd_arg->catalog),
1656
   db(thd_arg->db), q_len((uint32) query_length),
unknown's avatar
unknown committed
1657 1658
   thread_id(thd_arg->thread_id),
   /* save the original thread id; we already know the server id */
1659
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
1660
   flags2_inited(1), sql_mode_inited(1), charset_inited(1),
1661 1662
   sql_mode(thd_arg->variables.sql_mode),
   auto_increment_increment(thd_arg->variables.auto_increment_increment),
1663
   auto_increment_offset(thd_arg->variables.auto_increment_offset),
1664 1665
   lc_time_names_number(thd_arg->variables.lc_time_names->number),
   charset_database_number(0)
1666 1667
{
  time_t end_time;
1668 1669 1670 1671

  if (killed_status_arg == THD::KILLED_NO_VALUE)
    killed_status_arg= thd_arg->killed;
  error_code=
1672 1673
    (killed_status_arg == THD::NOT_KILLED) ?
    (thd_arg->is_error() ? thd_arg->main_da.sql_errno() : 0) :
1674
    ((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ? 0 :
1675
     thd_arg->killed_errno());
1676
  
1677
  time(&end_time);
1678
  exec_time = (ulong) (end_time  - thd_arg->start_time);
1679 1680 1681 1682
  /**
    @todo this means that if we have no catalog, then it is replicated
    as an existing catalog of length zero. is that safe? /sven
  */
1683
  catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
1684
  /* status_vars_len is set just before writing the event */
1685
  db_len = (db) ? (uint32) strlen(db) : 0;
1686 1687 1688
  if (thd_arg->variables.collation_database != thd_arg->db_charset)
    charset_database_number= thd_arg->variables.collation_database->number;
  
1689 1690
  /*
    If we don't use flags2 for anything else than options contained in
1691
    thd_arg->options, it would be more efficient to flags2=thd_arg->options
1692
    (OPTIONS_WRITTEN_TO_BIN_LOG would be used only at reading time).
1693 1694 1695
    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
1696
  flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG);
1697 1698 1699
  DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256);
  DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256);
  DBUG_ASSERT(thd_arg->variables.collation_server->number < 256*256);
1700
  DBUG_ASSERT(thd_arg->variables.character_set_client->mbminlen == 1);
1701 1702 1703
  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);
1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
  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
1716 1717
  DBUG_PRINT("info",("Query_log_event has flags2: %lu  sql_mode: %lu",
                     (ulong) flags2, sql_mode));
1718
}
unknown's avatar
unknown committed
1719
#endif /* MYSQL_CLIENT */
1720

unknown's avatar
unknown committed
1721

1722 1723
/* 2 utility functions for the next method */

1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746
/**
   Read a string with length from memory.

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

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

   @return    Zero if the entire string can be copied successfully,
              @c UINT_MAX if the length could not be read from memory
              (that is, if <code>*src >= end</code>), otherwise the
              number of bytes that are missing to read the full
              string, which happends <code>*dst + *len >= end</code>.
1747
*/
1748 1749 1750 1751 1752
static int
get_str_len_and_pointer(const Log_event::Byte **src,
                        const char **dst,
                        uint *len,
                        const Log_event::Byte *end)
1753
{
1754 1755 1756 1757 1758 1759
  if (*src >= end)
    return -1;       // Will be UINT_MAX in two-complement arithmetics
  uint length= **src;
  if (length > 0)
  {
    if (*src + length >= end)
1760
      return *src + length - end + 1;       // Number of bytes missing
1761 1762 1763
    *dst= (char *)*src + 1;                    // Will be copied later
  }
  *len= length;
1764 1765
  *src+= length + 1;
  return 0;
1766 1767
}

1768 1769 1770
static void copy_str_and_move(const char **src, 
                              Log_event::Byte **dst, 
                              uint len)
1771 1772
{
  memcpy(*dst, *src, len);
1773
  *src= (const char *)*dst;
1774 1775 1776 1777
  (*dst)+= len;
  *(*dst)++= 0;
}

1778

1779
#ifndef DBUG_OFF
unknown's avatar
unknown committed
1780 1781 1782 1783
static char const *
code_name(int code)
{
  static char buf[255];
1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797
  switch (code) {
  case Q_FLAGS2_CODE: return "Q_FLAGS2_CODE";
  case Q_SQL_MODE_CODE: return "Q_SQL_MODE_CODE";
  case Q_CATALOG_CODE: return "Q_CATALOG_CODE";
  case Q_AUTO_INCREMENT: return "Q_AUTO_INCREMENT";
  case Q_CHARSET_CODE: return "Q_CHARSET_CODE";
  case Q_TIME_ZONE_CODE: return "Q_TIME_ZONE_CODE";
  case Q_CATALOG_NZ_CODE: return "Q_CATALOG_NZ_CODE";
  case Q_LC_TIME_NAMES_CODE: return "Q_LC_TIME_NAMES_CODE";
  case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE";
  }
  sprintf(buf, "CODE#%d", code);
  return buf;
}
1798
#endif
1799

1800 1801 1802 1803 1804 1805 1806
/**
   Macro to check that there is enough space to read from memory.

   @param PTR Pointer to memory
   @param END End of memory
   @param CNT Number of bytes that should be read.
 */
1807 1808 1809 1810 1811 1812 1813 1814 1815
#define CHECK_SPACE(PTR,END,CNT)                      \
  do {                                                \
    DBUG_PRINT("info", ("Read %s", code_name(pos[-1]))); \
    DBUG_ASSERT((PTR) + (CNT) <= (END));              \
    if ((PTR) + (CNT) > (END)) {                      \
      DBUG_PRINT("info", ("query= 0"));               \
      query= 0;                                       \
      DBUG_VOID_RETURN;                               \
    }                                                 \
1816 1817
  } while (0)

unknown's avatar
unknown committed
1818
/*
1819
  Query_log_event::Query_log_event()
1820
  This is used by the SQL slave thread to prepare the event before execution.
unknown's avatar
unknown committed
1821
*/
1822

1823
Query_log_event::Query_log_event(const char* buf, uint event_len,
1824 1825
                                 const Format_description_log_event
                                 *description_event,
unknown's avatar
unknown committed
1826
                                 Log_event_type event_type)
1827
  :Log_event(buf, description_event), data_buf(0), query(NullS),
1828
   db(NullS), catalog_len(0), status_vars_len(0),
1829
   flags2_inited(0), sql_mode_inited(0), charset_inited(0),
1830
   auto_increment_increment(1), auto_increment_offset(1),
1831
   time_zone_len(0), lc_time_names_number(0), charset_database_number(0)
unknown's avatar
unknown committed
1832 1833
{
  ulong data_len;
1834 1835
  uint32 tmp;
  uint8 common_header_len, post_header_len;
1836 1837
  Log_event::Byte *start;
  const Log_event::Byte *end;
1838
  bool catalog_nz= 1;
1839 1840 1841
  DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");

  common_header_len= description_event->common_header_len;
unknown's avatar
unknown committed
1842
  post_header_len= description_event->post_header_len[event_type-1];
unknown's avatar
unknown committed
1843
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
                     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);
1858
  db_len = (uint)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
1859 1860 1861 1862 1863 1864 1865 1866 1867
  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)
1868
  {
1869
    status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET);
1870 1871 1872 1873 1874 1875
    /*
      Check if status variable length is corrupt and will lead to very
      wrong data. We could be even more strict and require data_len to
      be even bigger, but this will suffice to catch most corruption
      errors that can lead to a crash.
    */
1876
    if (status_vars_len > min(data_len, MAX_SIZE_LOG_EVENT_STATUS))
1877
    {
unknown's avatar
unknown committed
1878
      DBUG_PRINT("info", ("status_vars_len (%u) > data_len (%lu); query= 0",
1879
                          status_vars_len, data_len));
1880 1881 1882
      query= 0;
      DBUG_VOID_RETURN;
    }
1883 1884 1885 1886
    data_len-= status_vars_len;
    DBUG_PRINT("info", ("Query_log_event has status_vars_len: %u",
                        (uint) status_vars_len));
    tmp-= 2;
1887
  }
unknown's avatar
unknown committed
1888 1889 1890 1891 1892 1893
  /*
    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...)
  */

1894 1895
  /* variable-part: the status vars; only in MySQL 5.0  */
  
1896 1897 1898
  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;)
1899
  {
1900 1901
    switch (*pos++) {
    case Q_FLAGS2_CODE:
1902
      CHECK_SPACE(pos, end, 4);
1903 1904
      flags2_inited= 1;
      flags2= uint4korr(pos);
unknown's avatar
unknown committed
1905
      DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", (ulong) flags2));
1906 1907 1908 1909 1910 1911 1912
      pos+= 4;
      break;
    case Q_SQL_MODE_CODE:
    {
#ifndef DBUG_OFF
      char buff[22];
#endif
1913
      CHECK_SPACE(pos, end, 8);
1914 1915 1916 1917 1918 1919 1920
      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;
    }
1921
    case Q_CATALOG_NZ_CODE:
1922
      DBUG_PRINT("info", ("case Q_CATALOG_NZ_CODE; pos: 0x%lx; end: 0x%lx",
unknown's avatar
unknown committed
1923
                          (ulong) pos, (ulong) end));
1924 1925
      if (get_str_len_and_pointer(&pos, &catalog, &catalog_len, end))
      {
1926
        DBUG_PRINT("info", ("query= 0"));
1927 1928 1929
        query= 0;
        DBUG_VOID_RETURN;
      }
1930 1931
      break;
    case Q_AUTO_INCREMENT:
1932
      CHECK_SPACE(pos, end, 4);
1933 1934 1935 1936
      auto_increment_increment= uint2korr(pos);
      auto_increment_offset=    uint2korr(pos+2);
      pos+= 4;
      break;
1937 1938
    case Q_CHARSET_CODE:
    {
1939
      CHECK_SPACE(pos, end, 6);
1940 1941 1942 1943 1944
      charset_inited= 1;
      memcpy(charset, pos, 6);
      pos+= 6;
      break;
    }
1945 1946
    case Q_TIME_ZONE_CODE:
    {
1947 1948
      if (get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len, end))
      {
1949
        DBUG_PRINT("info", ("Q_TIME_ZONE_CODE: query= 0"));
1950 1951 1952
        query= 0;
        DBUG_VOID_RETURN;
      }
1953 1954
      break;
    }
1955
    case Q_CATALOG_CODE: /* for 5.0.x where 0<=x<=3 masters */
1956
      CHECK_SPACE(pos, end, 1);
1957 1958
      if ((catalog_len= *pos))
        catalog= (char*) pos+1;                           // Will be copied later
1959
      CHECK_SPACE(pos, end, catalog_len + 2);
1960 1961 1962
      pos+= catalog_len+2; // leap over end 0
      catalog_nz= 0; // catalog has end 0 in event
      break;
1963
    case Q_LC_TIME_NAMES_CODE:
1964
      CHECK_SPACE(pos, end, 2);
1965 1966 1967
      lc_time_names_number= uint2korr(pos);
      pos+= 2;
      break;
1968
    case Q_CHARSET_DATABASE_CODE:
1969
      CHECK_SPACE(pos, end, 2);
1970 1971 1972
      charset_database_number= uint2korr(pos);
      pos+= 2;
      break;
1973 1974 1975 1976
    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)));
1977
      pos= (const uchar*) end;                         // Break loop
1978
    }
1979
  }
1980
  
1981
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
1982 1983 1984 1985 1986 1987
  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))))
1988
#else
1989 1990 1991 1992
  if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
                                             time_zone_len + 1 +
                                             data_len + 1,
                                             MYF(MY_WME))))
1993
#endif
unknown's avatar
unknown committed
1994
      DBUG_VOID_RETURN;
1995
  if (catalog_len)                                  // If catalog is given
1996
  {
1997 1998 1999 2000 2001
    /**
      @todo we should clean up and do only copy_str_and_move; it
      works for both cases.  Then we can remove the catalog_nz
      flag. /sven
    */
2002
    if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3.
2003
      copy_str_and_move(&catalog, &start, catalog_len);
2004 2005 2006
    else
    {
      memcpy(start, catalog, catalog_len+1); // copy end 0
2007
      catalog= (const char *)start;
2008 2009
      start+= catalog_len+1;
    }
2010
  }
2011
  if (time_zone_len)
2012
    copy_str_and_move(&time_zone_str, &start, time_zone_len);
2013

2014 2015 2016 2017 2018 2019 2020
  /**
    if time_zone_len or catalog_len are 0, then time_zone and catalog
    are uninitialized at this point.  shouldn't they point to the
    zero-length null-terminated strings we allocated space for in the
    my_alloc call above? /sven
  */

2021
  /* A 2nd variable part; this is common to all versions */ 
2022
  memcpy((char*) start, end, data_len);          // Copy db and query
2023
  start[data_len]= '\0';              // End query with \0 (For safetly)
2024 2025
  db= (char *)start;
  query= (char *)(start + db_len + 1);
2026 2027
  q_len= data_len - db_len -1;
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2028 2029
}

2030

unknown's avatar
unknown committed
2031
/*
2032
  Query_log_event::print()
unknown's avatar
unknown committed
2033
*/
2034

2035
#ifdef MYSQL_CLIENT
2036
void Query_log_event::print_query_header(IO_CACHE* file,
unknown's avatar
unknown committed
2037
					 PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2038
{
2039
  // TODO: print the catalog ??
2040
  char buff[40],*end;				// Enough for SET TIMESTAMP
2041 2042 2043
  bool different_db= 1;
  uint32 tmp;

unknown's avatar
unknown committed
2044
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
2045
  {
2046 2047 2048 2049
    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
2050 2051
  }

unknown's avatar
unknown committed
2052
  if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db)
2053
  {
unknown's avatar
unknown committed
2054 2055
    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
2056
    if (db[0] && different_db) 
unknown's avatar
unknown committed
2057
      my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter);
2058
  }
2059

2060
  end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
2061
  end= strmov(end, print_event_info->delimiter);
2062
  *end++='\n';
2063
  my_b_write(file, (uchar*) buff, (uint) (end-buff));
unknown's avatar
unknown committed
2064
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
unknown's avatar
unknown committed
2065 2066
    my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
                (ulong)thread_id, print_event_info->delimiter);
2067

2068
  /*
2069 2070 2071
    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).
2072 2073 2074 2075
  */
  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
2076
    if (likely(print_event_info->flags2_inited)) 
2077
      /* All bits which have changed */
unknown's avatar
unknown committed
2078
      tmp= (print_event_info->flags2) ^ flags2;
2079 2080
    else /* that's the first Query event we read */
    {
unknown's avatar
unknown committed
2081
      print_event_info->flags2_inited= 1;
2082 2083 2084 2085 2086 2087
      tmp= ~((uint32)0); /* all bits have changed */
    }

    if (unlikely(tmp)) /* some bits have changed */
    {
      bool need_comma= 0;
2088
      my_b_printf(file, "SET ");
2089 2090 2091 2092 2093 2094
      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
2095
      my_b_printf(file,"%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
2096
      print_event_info->flags2= flags2;
2097 2098 2099 2100
    }
  }

  /*
2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
    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.
2111 2112 2113 2114
  */

  if (likely(sql_mode_inited))
  {
unknown's avatar
unknown committed
2115
    if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */
2116
    {
unknown's avatar
unknown committed
2117
      print_event_info->sql_mode_inited= 1;
2118
      /* force a difference to force write */
unknown's avatar
unknown committed
2119
      print_event_info->sql_mode= ~sql_mode;
2120
    }
unknown's avatar
unknown committed
2121
    if (unlikely(print_event_info->sql_mode != sql_mode))
2122
    {
unknown's avatar
unknown committed
2123 2124
      my_b_printf(file,"SET @@session.sql_mode=%lu%s\n",
                  (ulong)sql_mode, print_event_info->delimiter);
unknown's avatar
unknown committed
2125
      print_event_info->sql_mode= sql_mode;
2126 2127
    }
  }
unknown's avatar
unknown committed
2128 2129
  if (print_event_info->auto_increment_increment != auto_increment_increment ||
      print_event_info->auto_increment_offset != auto_increment_offset)
2130
  {
unknown's avatar
unknown committed
2131 2132 2133
    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
2134 2135
    print_event_info->auto_increment_increment= auto_increment_increment;
    print_event_info->auto_increment_offset=    auto_increment_offset;
2136 2137
  }

2138 2139
  /* TODO: print the catalog when we feature SET CATALOG */

2140 2141
  if (likely(charset_inited))
  {
unknown's avatar
unknown committed
2142
    if (unlikely(!print_event_info->charset_inited)) /* first Query event */
2143
    {
unknown's avatar
unknown committed
2144 2145
      print_event_info->charset_inited= 1;
      print_event_info->charset[0]= ~charset[0]; // force a difference to force write
2146
    }
2147
    if (unlikely(bcmp((uchar*) print_event_info->charset, (uchar*) charset, 6)))
2148
    {
2149 2150 2151
      CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
      if (cs_info)
      {
2152
        /* for mysql client */
unknown's avatar
unknown committed
2153 2154
        my_b_printf(file, "/*!\\C %s */%s\n",
                    cs_info->csname, print_event_info->delimiter);
2155
      }
2156 2157 2158 2159
      my_b_printf(file,"SET "
                  "@@session.character_set_client=%d,"
                  "@@session.collation_connection=%d,"
                  "@@session.collation_server=%d"
unknown's avatar
unknown committed
2160
                  "%s\n",
2161 2162
                  uint2korr(charset),
                  uint2korr(charset+2),
unknown's avatar
unknown committed
2163 2164
                  uint2korr(charset+4),
                  print_event_info->delimiter);
unknown's avatar
unknown committed
2165
      memcpy(print_event_info->charset, charset, 6);
2166 2167
    }
  }
2168 2169
  if (time_zone_len)
  {
2170 2171
    if (bcmp((uchar*) print_event_info->time_zone_str,
             (uchar*) time_zone_str, time_zone_len+1))
2172
    {
unknown's avatar
unknown committed
2173 2174
      my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
                  time_zone_str, print_event_info->delimiter);
unknown's avatar
unknown committed
2175
      memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
2176 2177
    }
  }
2178 2179
  if (lc_time_names_number != print_event_info->lc_time_names_number)
  {
unknown's avatar
unknown committed
2180 2181
    my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
                lc_time_names_number, print_event_info->delimiter);
2182 2183
    print_event_info->lc_time_names_number= lc_time_names_number;
  }
2184 2185 2186
  if (charset_database_number != print_event_info->charset_database_number)
  {
    if (charset_database_number)
unknown's avatar
unknown committed
2187 2188
      my_b_printf(file, "SET @@session.collation_database=%d%s\n",
                  charset_database_number, print_event_info->delimiter);
2189
    else
unknown's avatar
unknown committed
2190 2191
      my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
                  print_event_info->delimiter);
2192 2193
    print_event_info->charset_database_number= charset_database_number;
  }
unknown's avatar
unknown committed
2194 2195
}

2196

unknown's avatar
unknown committed
2197
void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2198
{
2199 2200 2201
  Write_on_release_cache cache(&print_event_info->head_cache, file);

  print_query_header(&cache, print_event_info);
2202
  my_b_write(&cache, (uchar*) query, q_len);
2203
  my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
2204
}
unknown's avatar
unknown committed
2205
#endif /* MYSQL_CLIENT */
2206

unknown's avatar
unknown committed
2207

unknown's avatar
unknown committed
2208
/*
2209
  Query_log_event::do_apply_event()
unknown's avatar
unknown committed
2210
*/
unknown's avatar
unknown committed
2211

unknown's avatar
SCRUM  
unknown committed
2212
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2213

2214
int Query_log_event::do_apply_event(Relay_log_info const *rli)
unknown's avatar
unknown committed
2215
{
2216
  return do_apply_event(rli, query, q_len);
unknown's avatar
unknown committed
2217 2218 2219
}


2220
int Query_log_event::do_apply_event(Relay_log_info const *rli,
2221
                                      const char *query_arg, uint32 q_len_arg)
unknown's avatar
unknown committed
2222
{
2223
  LEX_STRING new_db;
unknown's avatar
unknown committed
2224
  int expected_error,actual_error= 0;
2225
  /*
2226 2227 2228
    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
2229
    Query_log_event::do_apply_event()). Same for thd->db.  Thank
2230
    you.
2231
  */
2232
  thd->catalog= catalog_len ? (char *) catalog : (char *)"";
2233 2234 2235
  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' */
2236 2237
  thd->variables.auto_increment_increment= auto_increment_increment;
  thd->variables.auto_increment_offset=    auto_increment_offset;
unknown's avatar
unknown committed
2238

2239
  /*
2240 2241 2242 2243 2244 2245 2246 2247
    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.
2248
  */
2249
  const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
2250 2251
  DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));

2252 2253
  clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
  const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
2254

unknown's avatar
unknown committed
2255 2256 2257 2258 2259 2260 2261
  /*
    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
2262
            ::do_apply_event(), then the companion SET also have so
2263
            we don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
2264
  */
2265
  if (rpl_filter->db_ok(thd->db))
2266 2267
  {
    thd->set_time((time_t)when);
unknown's avatar
unknown committed
2268 2269
    thd->query_length= q_len_arg;
    thd->query= (char*)query_arg;
unknown's avatar
unknown committed
2270
    VOID(pthread_mutex_lock(&LOCK_thread_count));
2271
    thd->query_id = next_query_id();
2272
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
2273
    thd->variables.pseudo_thread_id= thread_id;		// for temp tables
unknown's avatar
unknown committed
2274
    DBUG_PRINT("query",("%s",thd->query));
2275

unknown's avatar
unknown committed
2276
    if (ignored_error_code((expected_error= error_code)) ||
2277
	!check_expected_error(thd,rli,expected_error))
2278 2279 2280
    {
      if (flags2_inited)
        /*
2281 2282
          all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
          must take their value from flags2.
2283
        */
2284
        thd->options= flags2|(thd->options & ~OPTIONS_WRITTEN_TO_BIN_LOG);
2285 2286
      /*
        else, we are in a 3.23/4.0 binlog; we previously received a
2287 2288
        Rotate_log_event which reset thd->options and sql_mode etc, so
        nothing to do.
2289 2290 2291 2292 2293
      */
      /*
        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
2294 2295 2296
        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).
2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314
      */
      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))))
          {
            /*
2315 2316 2317 2318
              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).
2319
            */
2320
            set_slave_thread_default_charset(thd, rli);
2321 2322 2323 2324 2325
            goto compare_errors;
          }
          thd->update_charset(); // for the charset change to take effect
        }
      }
2326 2327 2328
      if (time_zone_len)
      {
        String tmp(time_zone_str, time_zone_len, &my_charset_bin);
2329
        if (!(thd->variables.time_zone= my_tz_find(thd, &tmp)))
2330 2331 2332 2333 2334 2335
        {
          my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
          thd->variables.time_zone= global_system_variables.time_zone;
          goto compare_errors;
        }
      }
2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348
      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;
2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363
      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;
      
2364
      /* Execute the query (note that we bypass dispatch_command()) */
2365 2366
      const char* found_semicolon= NULL;
      mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
2367
      log_slow_statement(thd);
2368
    }
unknown's avatar
unknown committed
2369 2370
    else
    {
unknown's avatar
unknown committed
2371
      /*
unknown's avatar
unknown committed
2372 2373 2374 2375 2376
        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
2377
      */
unknown's avatar
unknown committed
2378
      if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
2379
        clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* Can ignore query */
unknown's avatar
unknown committed
2380
      else
2381
      {
2382
        rli->report(ERROR_LEVEL, expected_error, 
unknown's avatar
unknown committed
2383
                          "\
unknown's avatar
unknown committed
2384
Query partially completed on the master (error on master: %d) \
unknown's avatar
unknown committed
2385 2386 2387
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
2388
START SLAVE; . Query: '%s'", expected_error, thd->query);
2389
        thd->is_slave_error= 1;
unknown's avatar
unknown committed
2390 2391 2392
      }
      goto end;
    }
2393

2394
    /* If the query was not ignored, it is printed to the general log */
2395
    if (!thd->is_error() || thd->main_da.sql_errno() != ER_SLAVE_IGNORED_TABLE)
2396
      general_log_write(thd, COM_QUERY, thd->query, thd->query_length);
2397

2398
compare_errors:
unknown's avatar
unknown committed
2399 2400

     /*
unknown's avatar
unknown committed
2401 2402 2403
      If we expected a non-zero error code, and we don't get the same error
      code, and none of them should be ignored.
    */
2404 2405 2406 2407
    actual_error= thd->is_error() ? thd->main_da.sql_errno() : 0;
    DBUG_PRINT("info",("expected_error: %d  sql_errno: %d",
 		       expected_error, actual_error));
    if ((expected_error != actual_error) &&
2408 2409 2410
 	expected_error &&
 	!ignored_error_code(actual_error) &&
 	!ignored_error_code(expected_error))
unknown's avatar
unknown committed
2411
    {
2412
      rli->report(ERROR_LEVEL, 0,
2413 2414
                      "\
Query caused different errors on master and slave.     \
unknown's avatar
unknown committed
2415
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
unknown's avatar
unknown committed
2416
Default database: '%s'. Query: '%s'",
2417 2418
                      ER_SAFE(expected_error),
                      expected_error,
2419
                      actual_error ? thd->main_da.message() : "no error",
2420 2421
                      actual_error,
                      print_slave_db_safe(db), query_arg);
2422
      thd->is_slave_error= 1;
unknown's avatar
unknown committed
2423 2424 2425 2426 2427
    }
    /*
      If we get the same error code as expected, or they should be ignored. 
    */
    else if (expected_error == actual_error ||
2428
 	     ignored_error_code(actual_error))
unknown's avatar
unknown committed
2429 2430
    {
      DBUG_PRINT("info",("error ignored"));
2431
      clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
2432
      thd->killed= THD::NOT_KILLED;
unknown's avatar
unknown committed
2433 2434 2435
    }
    /*
      Other cases: mostly we expected no error and get one.
unknown's avatar
unknown committed
2436
    */
2437
    else if (thd->is_slave_error || thd->is_fatal_error)
unknown's avatar
unknown committed
2438
    {
2439
      rli->report(ERROR_LEVEL, actual_error,
2440
                      "Error '%s' on query. Default database: '%s'. Query: '%s'",
2441
                      (actual_error ? thd->main_da.message() :
2442 2443
                       "unexpected success or fatal error"),
                      print_slave_db_safe(thd->db), query_arg);
2444
      thd->is_slave_error= 1;
unknown's avatar
unknown committed
2445
    }
2446 2447 2448 2449 2450 2451 2452 2453 2454

    /*
      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, ...);
2455
      thd->is_slave_error = 1;
2456 2457 2458 2459
      }
      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...
2460

2461 2462 2463 2464 2465 2466 2467
      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
2468 2469
  } /* End of if (db_ok(... */

unknown's avatar
unknown committed
2470
end:
2471
  VOID(pthread_mutex_lock(&LOCK_thread_count));
2472 2473 2474 2475 2476 2477
  /*
    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
2478 2479 2480
    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.
2481
  */
unknown's avatar
unknown committed
2482
  thd->catalog= 0;
2483
  thd->set_db(NULL, 0);                 /* will free the current database */
2484
  DBUG_PRINT("info", ("end: query= 0"));
2485
  thd->query= 0;			// just to be sure
unknown's avatar
unknown committed
2486
  thd->query_length= 0;
2487
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
unknown's avatar
unknown committed
2488
  close_thread_tables(thd);      
2489 2490 2491 2492 2493 2494 2495 2496 2497 2498
  /*
    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
2499
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
2500
  return thd->is_slave_error;
2501 2502
}

2503
int Query_log_event::do_update_pos(Relay_log_info *rli)
2504
{
2505
  /*
2506 2507 2508
    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.
2509
  */
2510 2511 2512 2513 2514 2515 2516
  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
2517
}
2518 2519


2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543
Log_event::enum_skip_reason
Query_log_event::do_shall_skip(Relay_log_info *rli)
{
  DBUG_ENTER("Query_log_event::do_shall_skip");
  DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len));
  DBUG_ASSERT(query && q_len > 0);

  if (rli->slave_skip_counter > 0)
  {
    if (strcmp("BEGIN", query) == 0)
    {
      thd->options|= OPTION_BEGIN;
      DBUG_RETURN(Log_event::continue_group(rli));
    }

    if (strcmp("COMMIT", query) == 0 || strcmp("ROLLBACK", query) == 0)
    {
      thd->options&= ~OPTION_BEGIN;
      DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
    }
  }
  DBUG_RETURN(Log_event::do_shall_skip(rli));
}

unknown's avatar
SCRUM  
unknown committed
2544
#endif
unknown's avatar
unknown committed
2545

unknown's avatar
unknown committed
2546

2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561
/**************************************************************************
	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
2562
/**************************************************************************
2563
	Start_log_event_v3 methods
unknown's avatar
unknown committed
2564
**************************************************************************/
2565

2566
#ifndef MYSQL_CLIENT
2567 2568 2569
Start_log_event_v3::Start_log_event_v3()
  :Log_event(), created(0), binlog_version(BINLOG_VERSION),
   artificial_event(0), dont_set_created(0)
2570 2571 2572 2573 2574
{
  memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
#endif

unknown's avatar
unknown committed
2575
/*
2576
  Start_log_event_v3::pack_info()
unknown's avatar
unknown committed
2577
*/
2578

unknown's avatar
SCRUM  
unknown committed
2579
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2580
void Start_log_event_v3::pack_info(Protocol *protocol)
unknown's avatar
unknown committed
2581
{
2582 2583 2584 2585
  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
2586 2587
  pos= int10_to_str(binlog_version, pos, 10);
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
unknown's avatar
unknown committed
2588
}
unknown's avatar
SCRUM  
unknown committed
2589
#endif
2590 2591


unknown's avatar
unknown committed
2592
/*
2593
  Start_log_event_v3::print()
unknown's avatar
unknown committed
2594
*/
unknown's avatar
unknown committed
2595 2596

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
2597
void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
2598
{
2599 2600 2601 2602 2603
  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
2604
  if (!print_event_info->short_form)
2605
  {
2606 2607 2608 2609
    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);
2610
    if (created)
2611 2612
      my_b_printf(&cache," at startup");
    my_b_printf(&cache, "\n");
2613
    if (flags & LOG_EVENT_BINLOG_IN_USE_F)
2614 2615
      my_b_printf(&cache, "# Warning: this binlog was not closed properly. "
                  "Most probably mysqld crashed writing it.\n");
2616
  }
2617 2618
  if (!artificial_event && created)
  {
2619
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
2620 2621 2622 2623 2624 2625
    /*
      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
2626
    my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter);
2627
#else
unknown's avatar
unknown committed
2628
    my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter);
2629
#endif
2630
  }
2631 2632 2633 2634 2635 2636 2637 2638
  if (temp_buf &&
      print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
      !print_event_info->short_form)
  {
    my_b_printf(&cache, "BINLOG '\n");
    print_base64(&cache, print_event_info, FALSE);
    print_event_info->printed_fd_event= TRUE;
  }
2639
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2640
}
unknown's avatar
unknown committed
2641
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
2642

unknown's avatar
unknown committed
2643
/*
2644
  Start_log_event_v3::Start_log_event_v3()
unknown's avatar
unknown committed
2645
*/
2646

2647
Start_log_event_v3::Start_log_event_v3(const char* buf,
2648 2649
                                       const Format_description_log_event
                                       *description_event)
2650
  :Log_event(buf, description_event)
2651
{
2652 2653
  buf+= description_event->common_header_len;
  binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
2654 2655
  memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
	 ST_SERVER_VER_LEN);
unknown's avatar
unknown committed
2656 2657
  // prevent overrun if log is corrupted on disk
  server_version[ST_SERVER_VER_LEN-1]= 0;
2658 2659 2660
  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);
2661
  dont_set_created= 1;
unknown's avatar
unknown committed
2662 2663
}

2664

unknown's avatar
unknown committed
2665
/*
2666
  Start_log_event_v3::write()
unknown's avatar
unknown committed
2667
*/
2668

2669
#ifndef MYSQL_CLIENT
2670
bool Start_log_event_v3::write(IO_CACHE* file)
2671
{
2672
  char buff[START_V3_HEADER_LEN];
2673 2674
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
2675 2676
  if (!dont_set_created)
    created= when= get_time();
2677
  int4store(buff + ST_CREATED_OFFSET,created);
2678
  return (write_header(file, sizeof(buff)) ||
2679
          my_b_safe_write(file, (uchar*) buff, sizeof(buff)));
2680
}
2681
#endif
2682

2683

unknown's avatar
unknown committed
2684
/*
2685
  Start_log_event_v3::do_apply_event()
2686 2687 2688 2689

  The master started

  IMPLEMENTATION
unknown's avatar
unknown committed
2690 2691 2692 2693
    - 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).
2694 2695

  TODO
2696 2697 2698 2699 2700
    - 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
2701 2702
*/

unknown's avatar
SCRUM  
unknown committed
2703
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2704
int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
2705
{
2706
  DBUG_ENTER("Start_log_event_v3::do_apply_event");
2707
  switch (binlog_version) {
2708 2709 2710 2711 2712 2713 2714
  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
2715
    */
2716 2717 2718 2719 2720
    if (created)
    {
      close_temporary_tables(thd);
      cleanup_load_tmpdir();
    }
unknown's avatar
unknown committed
2721 2722
    break;

2723
    /*
unknown's avatar
unknown committed
2724 2725
       Now the older formats; in that case load_tmpdir is cleaned up by the I/O
       thread.
unknown's avatar
unknown committed
2726
    */
2727
  case 1:
2728
    if (strncmp(rli->relay_log.description_event_for_exec->server_version,
2729 2730 2731 2732 2733 2734 2735 2736
                "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
2737
    /*
2738 2739 2740
      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
2741 2742 2743 2744
    */
    break;
  default:
    /* this case is impossible */
2745
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2746
  }
2747
  DBUG_RETURN(0);
2748
}
unknown's avatar
unknown committed
2749
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
2750

2751 2752 2753 2754 2755 2756 2757 2758 2759
/***************************************************************************
       Format_description_log_event methods
****************************************************************************/

/*
  Format_description_log_event 1st ctor.

  SYNOPSIS
    Format_description_log_event::Format_description_log_event
2760
      binlog_version              the binlog version for which we want to build
2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774
                                  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::
2775
Format_description_log_event(uint8 binlog_ver, const char* server_ver)
2776 2777 2778 2779 2780 2781
  :Start_log_event_v3()
{
  binlog_version= binlog_ver;
  switch (binlog_ver) {
  case 4: /* MySQL 5.0 */
    memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
unknown's avatar
unknown committed
2782 2783
    DBUG_EXECUTE_IF("pretend_version_50034_in_binlog",
                    strmov(server_version, "5.0.34"););
2784 2785 2786 2787
    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),
2788
                                       MYF(MY_ZEROFILL));
2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805
    /*
      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;
2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824
      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
2825 2826
      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;
2827
      post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841
    }
    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 :
2842
      LOG_EVENT_MINIMAL_HEADER_LEN;
2843 2844 2845 2846 2847 2848 2849 2850 2851
    /*
      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),
2852
                                       MYF(0));
2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874
    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
2875
  calc_server_version_split();
2876 2877 2878 2879 2880 2881 2882 2883
}


/*
  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
2884
  post-header starts.
2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899
  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*
2900
                             description_event)
2901 2902 2903 2904 2905 2906 2907 2908 2909
  :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",
2910
                      common_header_len, number_of_event_types));
2911
  /* If alloc fails, we'll detect it in is_valid() */
2912
  post_header_len= (uint8*) my_memdup((uchar*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
2913
                                      number_of_event_types*
2914
                                      sizeof(*post_header_len), MYF(0));
unknown's avatar
unknown committed
2915
  calc_server_version_split();
2916 2917 2918
  DBUG_VOID_RETURN;
}

2919
#ifndef MYSQL_CLIENT
2920 2921 2922 2923 2924 2925
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().
  */
2926
  uchar buff[FORMAT_DESCRIPTION_HEADER_LEN];
2927 2928
  int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
  memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
2929 2930
  if (!dont_set_created)
    created= when= get_time();
2931
  int4store(buff + ST_CREATED_OFFSET,created);
2932
  buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
2933
  memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (uchar*) post_header_len,
2934 2935 2936 2937
         LOG_EVENT_TYPES);
  return (write_header(file, sizeof(buff)) ||
          my_b_safe_write(file, buff, sizeof(buff)));
}
2938
#endif
2939

2940
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
2941
int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
2942
{
2943
  DBUG_ENTER("Format_description_log_event::do_apply_event");
2944

unknown's avatar
unknown committed
2945
#ifdef USING_TRANSACTIONS
2946 2947 2948 2949
  /*
    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
2950 2951
    flushing the binlog cache to the binlog. XA guarantees that master has
    rolled back. So we roll back.
2952 2953 2954 2955 2956 2957 2958
    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)
  {
2959
    /* This is not an error (XA is safe), just an information */
2960 2961 2962 2963 2964
    rli->report(INFORMATION_LEVEL, 0,
                "Rolling back unfinished transaction (no COMMIT "
                "or ROLLBACK in relay log). A probable cause is that "
                "the master died while writing the transaction to "
                "its binary log, thus rolled back too."); 
2965
    const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 1);
2966
  }
unknown's avatar
unknown committed
2967
#endif
2968
  /*
2969
    If this event comes from ourselves, there is no cleaning task to
2970
    perform, we don't call Start_log_event_v3::do_apply_event()
2971
    (this was just to update the log's description event).
2972
  */
2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988
  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);
}

2989
int Format_description_log_event::do_update_pos(Relay_log_info *rli)
2990 2991 2992 2993 2994
{
  /* save the information describing this binlog */
  delete rli->relay_log.description_event_for_exec;
  rli->relay_log.description_event_for_exec= this;

2995 2996 2997
  if (server_id == (uint32) ::server_id)
  {
    /*
2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010
      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();
3011
    return 0;
3012
  }
3013 3014 3015
  else
  {
    return Log_event::do_update_pos(rli);
3016 3017 3018
  }
}

3019
Log_event::enum_skip_reason
3020
Format_description_log_event::do_shall_skip(Relay_log_info *rli)
3021
{
3022
  return Log_event::EVENT_SKIP_NOT;
3023
}
3024

3025 3026
#endif

unknown's avatar
unknown committed
3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057

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


3058
  /**************************************************************************
3059
        Load_log_event methods
3060
   General note about Load_log_event: the binlogging of LOAD DATA INFILE is
3061
   going to be changed in 5.0 (or maybe in 5.1; not decided yet).
3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073
   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).
  **************************************************************************/
3074

unknown's avatar
unknown committed
3075
/*
3076
  Load_log_event::pack_info()
unknown's avatar
unknown committed
3077
*/
3078

unknown's avatar
SCRUM  
unknown committed
3079
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3080
uint Load_log_event::get_query_buffer_length()
3081
{
unknown's avatar
unknown committed
3082
  return
3083 3084
    5 + db_len + 3 +                        // "use DB; "
    18 + fname_len + 2 +                    // "LOAD DATA INFILE 'file''"
unknown's avatar
unknown committed
3085
    7 +					    // LOCAL
3086
    9 +                                     // " REPLACE or IGNORE "
3087
    13 + table_name_len*2 +                 // "INTO TABLE `table`"
3088 3089 3090
    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'"
3091
    21 + sql_ex.line_term_len*4 + 2 +       // " LINES TERMINATED BY 'str'"
3092 3093
    19 + sql_ex.line_start_len*4 + 2 +      // " LINES STARTING BY 'str'"
    15 + 22 +                               // " IGNORE xxx  LINES"
3094
    3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
unknown's avatar
unknown committed
3095
}
3096

unknown's avatar
unknown committed
3097 3098 3099 3100 3101 3102 3103

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)
3104
  {
3105 3106
    pos= strmov(pos, "use `");
    memcpy(pos, db, db_len);
unknown's avatar
unknown committed
3107
    pos= strmov(pos+db_len, "`; ");
3108
  }
3109

unknown's avatar
unknown committed
3110
  pos= strmov(pos, "LOAD DATA ");
unknown's avatar
unknown committed
3111 3112 3113 3114

  if (fn_start)
    *fn_start= pos;

unknown's avatar
unknown committed
3115 3116 3117
  if (check_fname_outside_temp_buf())
    pos= strmov(pos, "LOCAL ");
  pos= strmov(pos, "INFILE '");
3118
  memcpy(pos, fname, fname_len);
unknown's avatar
unknown committed
3119
  pos= strmov(pos+fname_len, "' ");
3120

unknown's avatar
unknown committed
3121
  if (sql_ex.opt_flags & REPLACE_FLAG)
3122
    pos= strmov(pos, " REPLACE ");
unknown's avatar
unknown committed
3123
  else if (sql_ex.opt_flags & IGNORE_FLAG)
3124 3125
    pos= strmov(pos, " IGNORE ");

unknown's avatar
unknown committed
3126 3127 3128 3129 3130 3131
  pos= strmov(pos ,"INTO");

  if (fn_end)
    *fn_end= pos;

  pos= strmov(pos ," TABLE `");
3132 3133 3134
  memcpy(pos, table_name, table_name_len);
  pos+= table_name_len;

unknown's avatar
unknown committed
3135
  /* We have to create all optinal fields as the default is not empty */
3136
  pos= strmov(pos, "` FIELDS TERMINATED BY ");
unknown's avatar
unknown committed
3137 3138 3139 3140 3141
  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);
3142

unknown's avatar
unknown committed
3143 3144
  pos= strmov(pos, " ESCAPED BY ");
  pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len);
3145

unknown's avatar
unknown committed
3146 3147
  pos= strmov(pos, " LINES TERMINATED BY ");
  pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len);
3148 3149
  if (sql_ex.line_start_len)
  {
3150
    pos= strmov(pos, " STARTING BY ");
3151
    pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len);
3152
  }
3153

unknown's avatar
unknown committed
3154
  if ((long) skip_lines > 0)
3155 3156
  {
    pos= strmov(pos, " IGNORE ");
unknown's avatar
unknown committed
3157
    pos= longlong10_to_str((longlong) skip_lines, pos, 10);
3158 3159
    pos= strmov(pos," LINES ");    
  }
3160 3161 3162 3163

  if (num_fields)
  {
    uint i;
unknown's avatar
unknown committed
3164
    const char *field= fields;
3165
    pos= strmov(pos, " (");
3166 3167 3168
    for (i = 0; i < num_fields; i++)
    {
      if (i)
unknown's avatar
unknown committed
3169 3170 3171 3172
      {
        *pos++= ' ';
        *pos++= ',';
      }
3173
      memcpy(pos, field, field_lens[i]);
unknown's avatar
unknown committed
3174 3175
      pos+=   field_lens[i];
      field+= field_lens[i]  + 1;
3176
    }
3177
    *pos++= ')';
3178
  }
3179

unknown's avatar
unknown committed
3180 3181 3182 3183 3184 3185 3186 3187
  *end= pos;
}


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

3188
  if (!(buf= (char*) my_malloc(get_query_buffer_length(), MYF(MY_WME))))
unknown's avatar
unknown committed
3189 3190 3191
    return;
  print_query(TRUE, buf, &end, 0, 0);
  protocol->store(buf, end-buf, &my_charset_bin);
unknown's avatar
unknown committed
3192
  my_free(buf, MYF(0));
3193
}
unknown's avatar
unknown committed
3194
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
3195

3196

3197 3198
#ifndef MYSQL_CLIENT

unknown's avatar
unknown committed
3199
/*
3200
  Load_log_event::write_data_header()
unknown's avatar
unknown committed
3201
*/
3202

3203
bool Load_log_event::write_data_header(IO_CACHE* file)
3204
{
3205
  char buf[LOAD_HEADER_LEN];
unknown's avatar
unknown committed
3206
  int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
3207 3208 3209 3210 3211
  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);
3212
  return my_b_safe_write(file, (uchar*)buf, LOAD_HEADER_LEN) != 0;
3213
}
3214

3215

unknown's avatar
unknown committed
3216
/*
3217
  Load_log_event::write_data_body()
unknown's avatar
unknown committed
3218
*/
3219

3220
bool Load_log_event::write_data_body(IO_CACHE* file)
3221
{
3222 3223 3224
  if (sql_ex.write_data(file))
    return 1;
  if (num_fields && fields && field_lens)
3225
  {
3226 3227
    if (my_b_safe_write(file, (uchar*)field_lens, num_fields) ||
	my_b_safe_write(file, (uchar*)fields, field_block_len))
3228
      return 1;
3229
  }
3230 3231 3232
  return (my_b_safe_write(file, (uchar*)table_name, table_name_len + 1) ||
	  my_b_safe_write(file, (uchar*)db, db_len + 1) ||
	  my_b_safe_write(file, (uchar*)fname, fname_len));
3233 3234
}

3235

unknown's avatar
unknown committed
3236
/*
3237
  Load_log_event::Load_log_event()
unknown's avatar
unknown committed
3238
*/
3239

unknown's avatar
unknown committed
3240 3241 3242
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
3243
			       enum enum_duplicates handle_dup,
3244
			       bool ignore, bool using_trans)
3245
  :Log_event(thd_arg,
unknown's avatar
unknown committed
3246
             thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0,
3247
             using_trans),
3248
   thread_id(thd_arg->thread_id),
3249
   slave_proxy_id(thd_arg->variables.pseudo_thread_id),
unknown's avatar
unknown committed
3250 3251
   num_fields(0),fields(0),
   field_lens(0),field_block_len(0),
unknown's avatar
unknown committed
3252
   table_name(table_name_arg ? table_name_arg : ""),
3253
   db(db_arg), fname(ex->file_name), local_fname(FALSE)
unknown's avatar
unknown committed
3254 3255 3256
{
  time_t end_time;
  time(&end_time);
3257
  exec_time = (ulong) (end_time  - thd_arg->start_time);
3258 3259 3260
  /* 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
3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273
  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;
3274
    
unknown's avatar
unknown committed
3275
  if (ex->dumpfile)
unknown's avatar
unknown committed
3276
    sql_ex.opt_flags|= DUMPFILE_FLAG;
unknown's avatar
unknown committed
3277
  if (ex->opt_enclosed)
unknown's avatar
unknown committed
3278
    sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
3279

unknown's avatar
unknown committed
3280
  sql_ex.empty_flags= 0;
3281

3282
  switch (handle_dup) {
unknown's avatar
unknown committed
3283
  case DUP_REPLACE:
unknown's avatar
unknown committed
3284
    sql_ex.opt_flags|= REPLACE_FLAG;
unknown's avatar
unknown committed
3285 3286 3287 3288
    break;
  case DUP_UPDATE:				// Impossible here
  case DUP_ERROR:
    break;	
unknown's avatar
unknown committed
3289
  }
3290 3291
  if (ignore)
    sql_ex.opt_flags|= IGNORE_FLAG;
3292

unknown's avatar
unknown committed
3293 3294 3295 3296 3297 3298 3299 3300 3301 3302
  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;
3303
    
unknown's avatar
unknown committed
3304
  skip_lines = ex->skip_lines;
3305

unknown's avatar
unknown committed
3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316
  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);
3317 3318
  }

unknown's avatar
unknown committed
3319 3320 3321
  field_lens = (const uchar*)field_lens_buf.ptr();
  fields = fields_buf.ptr();
}
unknown's avatar
unknown committed
3322
#endif /* !MYSQL_CLIENT */
3323

3324

3325
/*
3326
  Load_log_event::Load_log_event()
3327

unknown's avatar
unknown committed
3328 3329 3330
  NOTE
    The caller must do buf[event_len] = 0 before he starts using the
    constructed event.
3331 3332
*/

3333 3334 3335 3336
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
3337
   table_name(0), db(0), fname(0), local_fname(FALSE)
unknown's avatar
unknown committed
3338
{
unknown's avatar
unknown committed
3339
  DBUG_ENTER("Load_log_event");
3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351
  /*
    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
3352
  DBUG_VOID_RETURN;
3353 3354
}

3355

unknown's avatar
unknown committed
3356
/*
3357
  Load_log_event::copy_log_event()
unknown's avatar
unknown committed
3358
*/
3359

3360
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
3361 3362
                                   int body_offset,
                                   const Format_description_log_event *description_event)
3363
{
3364
  DBUG_ENTER("Load_log_event::copy_log_event");
3365
  uint data_len;
3366
  char* buf_end = (char*)buf + event_len;
3367 3368
  /* this is the beginning of the post-header */
  const char* data_head = buf + description_event->common_header_len;
unknown's avatar
unknown committed
3369
  slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
3370 3371 3372 3373 3374
  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
3375
	  
unknown's avatar
unknown committed
3376
  if ((int) event_len < body_offset)
unknown's avatar
unknown committed
3377
    DBUG_RETURN(1);
3378 3379 3380 3381
  /*
    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.
  */
3382 3383 3384
  if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
                                        buf_end,
                                        buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
unknown's avatar
unknown committed
3385
    DBUG_RETURN(1);
3386
  
3387
  data_len = event_len - body_offset;
3388
  if (num_fields > data_len) // simple sanity check against corruption
unknown's avatar
unknown committed
3389
    DBUG_RETURN(1);
3390
  for (uint i = 0; i < num_fields; i++)
3391
    field_block_len += (uint)field_lens[i] + 1;
3392

unknown's avatar
unknown committed
3393 3394 3395 3396
  fields = (char*)field_lens + num_fields;
  table_name  = fields + field_block_len;
  db = table_name + table_name_len + 1;
  fname = db + db_len + 1;
3397 3398
  fname_len = strlen(fname);
  // null termination is accomplished by the caller doing buf[event_len]=0
3399

unknown's avatar
unknown committed
3400
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3401 3402 3403
}


unknown's avatar
unknown committed
3404
/*
3405
  Load_log_event::print()
unknown's avatar
unknown committed
3406
*/
3407 3408

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3409
void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
3410
{
unknown's avatar
unknown committed
3411
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
3412 3413
}

unknown's avatar
unknown committed
3414

3415
void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
3416
			   bool commented)
unknown's avatar
unknown committed
3417
{
3418 3419
  Write_on_release_cache cache(&print_event_info->head_cache, file_arg);

unknown's avatar
unknown committed
3420
  DBUG_ENTER("Load_log_event::print");
unknown's avatar
unknown committed
3421
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
3422
  {
3423 3424 3425
    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
3426 3427
  }

3428
  bool different_db= 1;
3429
  if (db)
unknown's avatar
unknown committed
3430
  {
3431 3432 3433 3434 3435 3436
    /*
      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
3437
    if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) &&
3438
        !commented)
unknown's avatar
unknown committed
3439
      memcpy(print_event_info->db, db, db_len + 1);
unknown's avatar
unknown committed
3440
  }
3441
  
3442
  if (db && db[0] && different_db)
unknown's avatar
unknown committed
3443
    my_b_printf(&cache, "%suse %s%s\n", 
unknown's avatar
unknown committed
3444
            commented ? "# " : "",
3445
            db, print_event_info->delimiter);
unknown's avatar
unknown committed
3446

3447
  if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
unknown's avatar
unknown committed
3448
    my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
3449 3450
            commented ? "# " : "", (ulong)thread_id,
            print_event_info->delimiter);
3451
  my_b_printf(&cache, "%sLOAD DATA ",
unknown's avatar
unknown committed
3452
              commented ? "# " : "");
3453
  if (check_fname_outside_temp_buf())
3454 3455
    my_b_printf(&cache, "LOCAL ");
  my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
unknown's avatar
unknown committed
3456

unknown's avatar
unknown committed
3457
  if (sql_ex.opt_flags & REPLACE_FLAG)
3458
    my_b_printf(&cache," REPLACE ");
unknown's avatar
unknown committed
3459
  else if (sql_ex.opt_flags & IGNORE_FLAG)
3460
    my_b_printf(&cache," IGNORE ");
unknown's avatar
unknown committed
3461
  
3462 3463 3464
  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
3465

unknown's avatar
unknown committed
3466
  if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
3467 3468 3469
    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
3470
     
3471 3472
  my_b_printf(&cache, " ESCAPED BY ");
  pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len);
unknown's avatar
unknown committed
3473
     
3474 3475
  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
3476

unknown's avatar
unknown committed
3477

3478
  if (sql_ex.line_start)
unknown's avatar
unknown committed
3479
  {
3480 3481
    my_b_printf(&cache," STARTING BY ");
    pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len);
unknown's avatar
unknown committed
3482
  }
3483
  if ((long) skip_lines > 0)
3484
    my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines);
unknown's avatar
unknown committed
3485

3486 3487 3488 3489
  if (num_fields)
  {
    uint i;
    const char* field = fields;
3490
    my_b_printf(&cache, " (");
3491
    for (i = 0; i < num_fields; i++)
unknown's avatar
unknown committed
3492
    {
unknown's avatar
unknown committed
3493
      if (i)
3494 3495
	my_b_printf(&cache, ",");
      my_b_printf(&cache, field);
unknown's avatar
unknown committed
3496
	  
3497
      field += field_lens[i]  + 1;
unknown's avatar
unknown committed
3498
    }
3499
    my_b_printf(&cache, ")");
3500
  }
unknown's avatar
unknown committed
3501

unknown's avatar
unknown committed
3502
  my_b_printf(&cache, "%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
3503
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
3504
}
unknown's avatar
unknown committed
3505
#endif /* MYSQL_CLIENT */
3506

3507

unknown's avatar
unknown committed
3508
/*
3509
  Load_log_event::set_fields()
unknown's avatar
unknown committed
3510 3511 3512 3513 3514

  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
3515
*/
3516

3517
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
3518
void Load_log_event::set_fields(const char* affected_db, 
3519 3520
				List<Item> &field_list,
                                Name_resolution_context *context)
unknown's avatar
unknown committed
3521 3522
{
  uint i;
unknown's avatar
unknown committed
3523
  const char* field = fields;
3524
  for (i= 0; i < num_fields; i++)
unknown's avatar
unknown committed
3525
  {
3526 3527
    field_list.push_back(new Item_field(context,
                                        affected_db, table_name, field));
3528
    field+= field_lens[i]  + 1;
unknown's avatar
unknown committed
3529
  }
unknown's avatar
unknown committed
3530
}
unknown's avatar
unknown committed
3531
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
3532 3533


unknown's avatar
SCRUM  
unknown committed
3534
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
3535 3536
/*
  Does the data loading job when executing a LOAD DATA on the slave
3537

unknown's avatar
unknown committed
3538
  SYNOPSIS
3539
    Load_log_event::do_apply_event
3540 3541 3542
      net
      rli
      use_rli_only_for_errors	  - if set to 1, rli is provided to
3543
                                  Load_log_event::do_apply_event
3544 3545 3546 3547 3548 3549
                                  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
3550
                                  Execute_load_log_event::do_apply_event).
3551 3552 3553
                                  - if set to 0, rli is provided for
                                  full use, i.e. for error reports and
                                  position advancing.
3554

unknown's avatar
unknown committed
3555 3556
  DESCRIPTION
    Does the data loading job when executing a LOAD DATA on the slave
3557

unknown's avatar
unknown committed
3558
  RETURN VALUE
3559
    0           Success
unknown's avatar
unknown committed
3560 3561
    1    	Failure
*/
3562

3563
int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
3564
                                   bool use_rli_only_for_errors)
3565
{
3566 3567 3568 3569
  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);
3570
  DBUG_ASSERT(thd->query == 0);
unknown's avatar
unknown committed
3571
  thd->query_length= 0;                         // Should not be needed
3572
  thd->is_slave_error= 0;
3573
  clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
3574

3575
  /* see Query_log_event::do_apply_event() and BUG#13360 */
3576
  DBUG_ASSERT(!rli->m_table_map.count());
3577
  /*
3578
    Usually lex_start() is called by mysql_parse(), but we need it here
3579 3580
    as the present method does not call mysql_parse().
  */
3581 3582 3583
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);

unknown's avatar
unknown committed
3584
  if (!use_rli_only_for_errors)
3585
  {
3586 3587
    /*
      Saved for InnoDB, see comment in
3588
      Query_log_event::do_apply_event()
3589
    */
3590
    const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
3591
    DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
3592
  }
3593 3594
 
   /*
3595 3596 3597 3598 3599 3600 3601
    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
3602
    done by testing rules in Create_file_log_event::do_apply_event()
3603 3604 3605
    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
3606 3607 3608 3609 3610 3611 3612 3613


    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
3614
            ::do_apply_event(), then the companion SET also have so
3615
            we don't need to reset_one_shot_variables().
unknown's avatar
unknown committed
3616
  */
3617
  if (rpl_filter->db_ok(thd->db))
3618
  {
3619 3620
    thd->set_time((time_t)when);
    VOID(pthread_mutex_lock(&LOCK_thread_count));
3621
    thd->query_id = next_query_id();
3622
    VOID(pthread_mutex_unlock(&LOCK_thread_count));
3623 3624 3625 3626 3627 3628
    /*
      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
3629
    mysql_reset_errors(thd, 0);
3630 3631 3632

    TABLE_LIST tables;
    bzero((char*) &tables,sizeof(tables));
unknown's avatar
unknown committed
3633
    tables.db= thd->strmake(thd->db, thd->db_length);
3634
    tables.alias = tables.table_name = (char*) table_name;
3635
    tables.lock_type = TL_WRITE;
unknown's avatar
unknown committed
3636
    tables.updating= 1;
unknown's avatar
unknown committed
3637

3638
    // the table will be opened in mysql_load    
3639
    if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db, &tables))
3640 3641 3642 3643 3644 3645 3646 3647
    {
      // 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
3648
      char *end;
unknown's avatar
unknown committed
3649
      enum enum_duplicates handle_dup;
3650
      bool ignore= 0;
unknown's avatar
unknown committed
3651 3652
      char *load_data_query;

unknown's avatar
unknown committed
3653
      /*
unknown's avatar
unknown committed
3654 3655
        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
3656
      */
unknown's avatar
unknown committed
3657
      if (!(load_data_query= (char *)thd->alloc(get_query_buffer_length() + 1)))
unknown's avatar
unknown committed
3658
      {
unknown's avatar
unknown committed
3659 3660 3661 3662 3663
        /*
          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
3664
      }
unknown's avatar
unknown committed
3665 3666 3667 3668 3669 3670 3671

      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
3672
      if (sql_ex.opt_flags & REPLACE_FLAG)
3673
      {
unknown's avatar
unknown committed
3674
	handle_dup= DUP_REPLACE;
3675
      }
unknown's avatar
unknown committed
3676
      else if (sql_ex.opt_flags & IGNORE_FLAG)
3677 3678 3679 3680
      {
        ignore= 1;
        handle_dup= DUP_ERROR;
      }
unknown's avatar
unknown committed
3681
      else
unknown's avatar
unknown committed
3682
      {
unknown's avatar
unknown committed
3683
        /*
unknown's avatar
unknown committed
3684
	  When replication is running fine, if it was DUP_ERROR on the
3685
          master then we could choose IGNORE here, because if DUP_ERROR
unknown's avatar
unknown committed
3686
          suceeded on master, and data is identical on the master and slave,
3687
          then there should be no uniqueness errors on slave, so IGNORE is
unknown's avatar
unknown committed
3688
          the same as DUP_ERROR. But in the unlikely case of uniqueness errors
unknown's avatar
unknown committed
3689 3690 3691
          (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
3692 3693

          If reading from net (a 3.23 master), mysql_load() will change this
3694
          to IGNORE.
unknown's avatar
unknown committed
3695 3696
        */
        handle_dup= DUP_ERROR;
unknown's avatar
unknown committed
3697
      }
3698 3699 3700 3701 3702 3703 3704 3705 3706 3707
      /*
        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
3708

unknown's avatar
unknown committed
3709
      sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
3710 3711 3712 3713 3714
      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
3715 3716 3717 3718 3719
      ex.field_term= &field_term;
      ex.enclosed= &enclosed;
      ex.line_term= &line_term;
      ex.line_start= &line_start;
      ex.escaped= &escaped;
3720 3721 3722 3723 3724 3725

      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
3726
      List<Item> field_list;
3727 3728
      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
3729
      thd->variables.pseudo_thread_id= thread_id;
3730 3731 3732 3733 3734 3735 3736 3737 3738
      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
3739
      /*
3740
        It is safe to use tmp_list twice because we are not going to
unknown's avatar
unknown committed
3741 3742
        update it inside mysql_load().
      */
3743 3744
      List<Item> tmp_list;
      if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
3745
                     handle_dup, ignore, net != 0))
3746
        thd->is_slave_error= 1;
3747
      if (thd->cuted_fields)
unknown's avatar
unknown committed
3748
      {
unknown's avatar
unknown committed
3749
	/* log_pos is the position of the LOAD event in the master log */
unknown's avatar
unknown committed
3750 3751 3752 3753 3754 3755 3756
        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
3757
      }
3758 3759 3760
      if (net)
        net->pkt_nr= thd->net.pkt_nr;
    }
3761 3762
  }
  else
3763 3764 3765 3766 3767 3768 3769 3770 3771
  {
    /*
      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
3772 3773

error:
3774
  thd->net.vio = 0; 
unknown's avatar
unknown committed
3775
  const char *remember_db= thd->db;
unknown's avatar
unknown committed
3776
  VOID(pthread_mutex_lock(&LOCK_thread_count));
unknown's avatar
unknown committed
3777
  thd->catalog= 0;
3778
  thd->set_db(NULL, 0);                   /* will free the current database */
unknown's avatar
unknown committed
3779
  thd->query= 0;
unknown's avatar
unknown committed
3780
  thd->query_length= 0;
unknown's avatar
unknown committed
3781
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
3782
  close_thread_tables(thd);
3783 3784

  DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
3785
                  thd->is_slave_error= 0; thd->is_fatal_error= 1;);
3786

3787
  if (thd->is_slave_error)
3788
  {
3789
    /* this err/sql_errno code is copy-paste from net_send_error() */
unknown's avatar
unknown committed
3790 3791
    const char *err;
    int sql_errno;
3792 3793 3794 3795 3796
    if (thd->is_error())
    {
      err= thd->main_da.message();
      sql_errno= thd->main_da.sql_errno();
    }
unknown's avatar
unknown committed
3797 3798 3799 3800 3801
    else
    {
      sql_errno=ER_UNKNOWN_ERROR;
      err=ER(sql_errno);       
    }
3802
    rli->report(ERROR_LEVEL, sql_errno,"\
unknown's avatar
unknown committed
3803
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
3804
                    err, (char*)table_name, print_slave_db_safe(remember_db));
unknown's avatar
unknown committed
3805
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3806 3807
    return 1;
  }
unknown's avatar
unknown committed
3808
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
3809

3810
  if (thd->is_fatal_error)
3811
  {
3812 3813 3814 3815 3816 3817 3818 3819 3820
    char buf[256];
    my_snprintf(buf, sizeof(buf),
                "Running LOAD DATA INFILE on table '%-.64s'."
                " Default database: '%-.64s'",
                (char*)table_name,
                print_slave_db_safe(remember_db));

    rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
                ER(ER_SLAVE_FATAL_ERROR), buf);
3821 3822 3823
    return 1;
  }

3824
  return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) ); 
3825
}
unknown's avatar
SCRUM  
unknown committed
3826
#endif
3827 3828


unknown's avatar
unknown committed
3829
/**************************************************************************
unknown's avatar
unknown committed
3830
  Rotate_log_event methods
unknown's avatar
unknown committed
3831
**************************************************************************/
3832

unknown's avatar
unknown committed
3833
/*
3834
  Rotate_log_event::pack_info()
unknown's avatar
unknown committed
3835
*/
3836

unknown's avatar
SCRUM  
unknown committed
3837
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3838
void Rotate_log_event::pack_info(Protocol *protocol)
3839
{
unknown's avatar
unknown committed
3840
  char buf1[256], buf[22];
unknown's avatar
unknown committed
3841
  String tmp(buf1, sizeof(buf1), log_cs);
3842
  tmp.length(0);
unknown's avatar
unknown committed
3843
  tmp.append(new_log_ident, ident_len);
3844
  tmp.append(STRING_WITH_LEN(";pos="));
unknown's avatar
unknown committed
3845 3846
  tmp.append(llstr(pos,buf));
  protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
3847
}
unknown's avatar
SCRUM  
unknown committed
3848
#endif
3849

3850

unknown's avatar
unknown committed
3851
/*
3852
  Rotate_log_event::print()
unknown's avatar
unknown committed
3853
*/
3854 3855

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
3856
void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
3857
{
3858
  char buf[22];
3859 3860
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);
3861

unknown's avatar
unknown committed
3862
  if (print_event_info->short_form)
3863
    return;
3864 3865
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\tRotate to ");
3866
  if (new_log_ident)
3867
    my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len);
3868
  my_b_printf(&cache, "  pos: %s\n", llstr(pos, buf));
3869
}
unknown's avatar
unknown committed
3870
#endif /* MYSQL_CLIENT */
3871 3872


3873

unknown's avatar
unknown committed
3874
/*
3875
  Rotate_log_event::Rotate_log_event() (2 constructors)
unknown's avatar
unknown committed
3876
*/
3877

3878 3879

#ifndef MYSQL_CLIENT
3880
Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
3881 3882 3883 3884 3885 3886 3887 3888
                                   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];
3889
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
unknown's avatar
unknown committed
3890 3891
  DBUG_PRINT("enter",("new_log_ident: %s  pos: %s  flags: %lu", new_log_ident_arg,
                      llstr(pos_arg, buff), (ulong) flags));
3892 3893
#endif
  if (flags & DUP_NAME)
3894
    new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME));
3895 3896 3897 3898 3899
  DBUG_VOID_RETURN;
}
#endif


3900 3901
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
                                   const Format_description_log_event* description_event)
unknown's avatar
unknown committed
3902
  :Log_event(buf, description_event) ,new_log_ident(0), flags(DUP_NAME)
3903
{
3904
  DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
3905
  // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
3906 3907
  uint8 header_size= description_event->common_header_len;
  uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
3908 3909
  uint ident_offset;
  if (event_len < header_size)
unknown's avatar
unknown committed
3910
    DBUG_VOID_RETURN;
3911
  buf += header_size;
3912 3913 3914 3915
  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; 
3916
  set_if_smaller(ident_len,FN_REFLEN-1);
3917
  new_log_ident= my_strndup(buf + ident_offset, (uint) ident_len, MYF(MY_WME));
3918
  DBUG_PRINT("debug", ("new_log_ident: '%s'", new_log_ident));
unknown's avatar
unknown committed
3919
  DBUG_VOID_RETURN;
3920
}
3921 3922


unknown's avatar
unknown committed
3923
/*
3924
  Rotate_log_event::write()
unknown's avatar
unknown committed
3925
*/
3926

3927
#ifndef MYSQL_CLIENT
3928
bool Rotate_log_event::write(IO_CACHE* file)
3929
{
3930
  char buf[ROTATE_HEADER_LEN];
unknown's avatar
unknown committed
3931
  int8store(buf + R_POS_OFFSET, pos);
3932
  return (write_header(file, ROTATE_HEADER_LEN + ident_len) ||
3933 3934
          my_b_safe_write(file, (uchar*)buf, ROTATE_HEADER_LEN) ||
          my_b_safe_write(file, (uchar*)new_log_ident, (uint) ident_len));
3935
}
3936
#endif
3937

3938

unknown's avatar
unknown committed
3939
/*
3940
  Rotate_log_event::do_apply_event()
3941

3942
  Got a rotate log event from the master
3943

3944 3945 3946
  IMPLEMENTATION
    This is mainly used so that we can later figure out the logname and
    position for the master.
3947

3948
    We can't rotate the slave's BINlog as this will cause infinitive rotations
3949
    in a A -> B -> A setup.
3950
    The NOTES below is a wrong comment which will disappear when 4.1 is merged.
3951 3952 3953

  RETURN VALUES
    0	ok
unknown's avatar
unknown committed
3954
*/
3955

unknown's avatar
SCRUM  
unknown committed
3956
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
3957
int Rotate_log_event::do_update_pos(Relay_log_info *rli)
3958
{
3959
  DBUG_ENTER("Rotate_log_event::do_update_pos");
3960 3961 3962 3963
#ifndef DBUG_OFF
  char buf[32];
#endif

3964 3965
  DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
                      (ulong) this->server_id, (ulong) ::server_id));
3966 3967
  DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
  DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
3968 3969

  pthread_mutex_lock(&rli->data_lock);
3970
  rli->event_relay_log_pos= my_b_tell(rli->cur_log);
unknown's avatar
unknown committed
3971
  /*
3972 3973 3974 3975
    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
3976 3977 3978 3979 3980
    BEGIN
    ...
    ROTATE (a fake one)
    ...
    COMMIT or ROLLBACK
3981 3982 3983 3984

    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
3985
    relay log, which shall not change the group positions.
unknown's avatar
unknown committed
3986
  */
3987
  if ((server_id != ::server_id || rli->replicate_same_server_id) &&
3988
      !rli->is_in_group())
unknown's avatar
unknown committed
3989
  {
3990 3991 3992 3993
    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
3994 3995
    memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
    rli->notify_group_master_log_name_update();
3996
    rli->group_master_log_pos= pos;
3997 3998 3999
    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();
4000
    rli->group_relay_log_pos= rli->event_relay_log_pos;
4001 4002
    DBUG_PRINT("info", ("new group_master_log_name: '%s'  "
                        "new group_master_log_pos: %lu",
4003
                        rli->group_master_log_name,
unknown's avatar
unknown committed
4004
                        (ulong) rli->group_master_log_pos));
4005
    /*
4006 4007
      Reset thd->options and sql_mode etc, because this could be the signal of
      a master's downgrade from 5.0 to 4.0.
4008 4009 4010 4011 4012
      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);
4013
    set_slave_thread_default_charset(thd, rli);
4014
    thd->variables.sql_mode= global_system_variables.sql_mode;
4015 4016
    thd->variables.auto_increment_increment=
      thd->variables.auto_increment_offset= 1;
unknown's avatar
unknown committed
4017
  }
4018 4019 4020
  pthread_mutex_unlock(&rli->data_lock);
  pthread_cond_broadcast(&rli->data_cond);
  flush_relay_log_info(rli);
4021

4022
  DBUG_RETURN(0);
4023
}
4024

4025 4026

Log_event::enum_skip_reason
4027
Rotate_log_event::do_shall_skip(Relay_log_info *rli)
4028
{
4029
  enum_skip_reason reason= Log_event::do_shall_skip(rli);
4030 4031

  switch (reason) {
4032
  case Log_event::EVENT_SKIP_NOT:
4033
  case Log_event::EVENT_SKIP_COUNT:
4034
    return Log_event::EVENT_SKIP_NOT;
4035

4036 4037
  case Log_event::EVENT_SKIP_IGNORE:
    return Log_event::EVENT_SKIP_IGNORE;
4038 4039
  }
  DBUG_ASSERT(0);
4040
  return Log_event::EVENT_SKIP_NOT;             // To keep compiler happy
4041 4042
}

unknown's avatar
SCRUM  
unknown committed
4043
#endif
4044 4045


unknown's avatar
unknown committed
4046
/**************************************************************************
unknown's avatar
unknown committed
4047
	Intvar_log_event methods
unknown's avatar
unknown committed
4048
**************************************************************************/
4049

unknown's avatar
unknown committed
4050
/*
4051
  Intvar_log_event::pack_info()
unknown's avatar
unknown committed
4052
*/
4053

unknown's avatar
SCRUM  
unknown committed
4054
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4055
void Intvar_log_event::pack_info(Protocol *protocol)
4056
{
unknown's avatar
unknown committed
4057 4058
  char buf[256], *pos;
  pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
unknown's avatar
unknown committed
4059
  *pos++= '=';
unknown's avatar
unknown committed
4060
  pos= longlong10_to_str(val, pos, -10);
unknown's avatar
unknown committed
4061
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
4062
}
unknown's avatar
SCRUM  
unknown committed
4063
#endif
4064

unknown's avatar
unknown committed
4065

unknown's avatar
unknown committed
4066
/*
4067
  Intvar_log_event::Intvar_log_event()
unknown's avatar
unknown committed
4068
*/
4069

4070 4071 4072
Intvar_log_event::Intvar_log_event(const char* buf,
                                   const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
4073
{
4074 4075 4076
  buf+= description_event->common_header_len;
  type= buf[I_TYPE_OFFSET];
  val= uint8korr(buf+I_VAL_OFFSET);
4077 4078
}

4079

unknown's avatar
unknown committed
4080
/*
4081
  Intvar_log_event::get_var_type_name()
unknown's avatar
unknown committed
4082
*/
4083 4084

const char* Intvar_log_event::get_var_type_name()
4085
{
4086 4087 4088 4089 4090
  switch(type) {
  case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID";
  case INSERT_ID_EVENT: return "INSERT_ID";
  default: /* impossible */ return "UNKNOWN";
  }
4091 4092
}

unknown's avatar
unknown committed
4093

unknown's avatar
unknown committed
4094
/*
4095
  Intvar_log_event::write()
unknown's avatar
unknown committed
4096
*/
4097

4098
#ifndef MYSQL_CLIENT
4099
bool Intvar_log_event::write(IO_CACHE* file)
4100
{
4101 4102
  uchar buf[9];
  buf[I_TYPE_OFFSET]= (uchar) type;
4103
  int8store(buf + I_VAL_OFFSET, val);
4104 4105
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
4106
}
4107
#endif
4108

4109

unknown's avatar
unknown committed
4110
/*
4111
  Intvar_log_event::print()
unknown's avatar
unknown committed
4112
*/
4113 4114

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4115
void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4116
{
4117 4118 4119
  char llbuff[22];
  const char *msg;
  LINT_INIT(msg);
4120 4121
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);
4122

unknown's avatar
unknown committed
4123
  if (!print_event_info->short_form)
4124
  {
4125 4126
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tIntvar\n");
4127
  }
4128

4129
  my_b_printf(&cache, "SET ");
4130 4131 4132 4133 4134 4135 4136
  switch (type) {
  case LAST_INSERT_ID_EVENT:
    msg="LAST_INSERT_ID";
    break;
  case INSERT_ID_EVENT:
    msg="INSERT_ID";
    break;
4137 4138 4139 4140
  case INVALID_INT_EVENT:
  default: // cannot happen
    msg="INVALID_INT";
    break;
4141
  }
unknown's avatar
unknown committed
4142 4143
  my_b_printf(&cache, "%s=%s%s\n",
              msg, llstr(val,llbuff), print_event_info->delimiter);
4144
}
4145
#endif
4146

4147

unknown's avatar
unknown committed
4148
/*
4149
  Intvar_log_event::do_apply_event()
unknown's avatar
unknown committed
4150
*/
4151

unknown's avatar
SCRUM  
unknown committed
4152
#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
4153
int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
4154
{
4155 4156 4157 4158
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
4159
  const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
4160

4161 4162
  switch (type) {
  case LAST_INSERT_ID_EVENT:
4163 4164
    thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
    thd->first_successful_insert_id_in_prev_stmt= val;
4165 4166
    break;
  case INSERT_ID_EVENT:
4167
    thd->force_one_auto_inc_interval(val);
4168 4169
    break;
  }
4170 4171 4172
  return 0;
}

4173
int Intvar_log_event::do_update_pos(Relay_log_info *rli)
4174
{
4175
  rli->inc_event_relay_log_pos();
4176
  return 0;
4177
}
4178 4179 4180


Log_event::enum_skip_reason
4181
Intvar_log_event::do_shall_skip(Relay_log_info *rli)
4182 4183
{
  /*
4184 4185 4186 4187 4188 4189
    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.
4190
  */
4191
  return continue_group(rli);
4192 4193
}

unknown's avatar
SCRUM  
unknown committed
4194
#endif
4195

4196

unknown's avatar
unknown committed
4197
/**************************************************************************
4198
  Rand_log_event methods
unknown's avatar
unknown committed
4199
**************************************************************************/
4200

unknown's avatar
SCRUM  
unknown committed
4201
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4202
void Rand_log_event::pack_info(Protocol *protocol)
4203
{
unknown's avatar
unknown committed
4204 4205 4206 4207 4208
  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);
4209
  protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
4210
}
unknown's avatar
SCRUM  
unknown committed
4211
#endif
4212 4213


4214 4215 4216
Rand_log_event::Rand_log_event(const char* buf,
                               const Format_description_log_event* description_event)
  :Log_event(buf, description_event)
4217
{
4218 4219 4220
  buf+= description_event->common_header_len;
  seed1= uint8korr(buf+RAND_SEED1_OFFSET);
  seed2= uint8korr(buf+RAND_SEED2_OFFSET);
4221 4222
}

4223

4224
#ifndef MYSQL_CLIENT
4225
bool Rand_log_event::write(IO_CACHE* file)
4226
{
4227
  uchar buf[16];
4228 4229
  int8store(buf + RAND_SEED1_OFFSET, seed1);
  int8store(buf + RAND_SEED2_OFFSET, seed2);
4230 4231
  return (write_header(file, sizeof(buf)) ||
          my_b_safe_write(file, buf, sizeof(buf)));
4232
}
4233
#endif
4234

4235 4236

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

unknown's avatar
unknown committed
4242
  char llbuff[22],llbuff2[22];
unknown's avatar
unknown committed
4243
  if (!print_event_info->short_form)
4244
  {
4245 4246
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tRand\n");
4247
  }
unknown's avatar
unknown committed
4248 4249 4250
  my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
              llstr(seed1, llbuff),llstr(seed2, llbuff2),
              print_event_info->delimiter);
4251
}
unknown's avatar
unknown committed
4252
#endif /* MYSQL_CLIENT */
4253

4254

unknown's avatar
SCRUM  
unknown committed
4255
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4256
int Rand_log_event::do_apply_event(Relay_log_info const *rli)
4257
{
4258 4259 4260 4261
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
4262
  const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
4263

unknown's avatar
unknown committed
4264 4265
  thd->rand.seed1= (ulong) seed1;
  thd->rand.seed2= (ulong) seed2;
4266 4267 4268
  return 0;
}

4269
int Rand_log_event::do_update_pos(Relay_log_info *rli)
4270
{
4271
  rli->inc_event_relay_log_pos();
4272 4273
  return 0;
}
4274

4275 4276

Log_event::enum_skip_reason
4277
Rand_log_event::do_shall_skip(Relay_log_info *rli)
4278 4279
{
  /*
4280 4281 4282 4283 4284 4285
    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.
4286
  */
4287
  return continue_group(rli);
4288 4289
}

unknown's avatar
unknown committed
4290
#endif /* !MYSQL_CLIENT */
4291

unknown's avatar
unknown committed
4292

4293 4294 4295 4296 4297 4298 4299
/**************************************************************************
  Xid_log_event methods
**************************************************************************/

#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Xid_log_event::pack_info(Protocol *protocol)
{
4300 4301
  char buf[128], *pos;
  pos= strmov(buf, "COMMIT /* xid=");
4302
  pos= longlong10_to_str(xid, pos, 10);
4303
  pos= strmov(pos, " */");
4304 4305 4306 4307
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
}
#endif

unknown's avatar
unknown committed
4308 4309 4310 4311 4312 4313 4314 4315
/*
  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
*/
4316 4317 4318 4319

Xid_log_event::
Xid_log_event(const char* buf,
              const Format_description_log_event *description_event)
4320 4321 4322
  :Log_event(buf, description_event)
{
  buf+= description_event->common_header_len;
4323
  memcpy((char*) &xid, buf, sizeof(xid));
4324 4325 4326
}


4327
#ifndef MYSQL_CLIENT
4328 4329
bool Xid_log_event::write(IO_CACHE* file)
{
4330
  DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
4331
  return write_header(file, sizeof(xid)) ||
4332
         my_b_safe_write(file, (uchar*) &xid, sizeof(xid));
4333
}
4334
#endif
4335 4336 4337


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4338
void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4339
{
4340 4341 4342
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4343
  if (!print_event_info->short_form)
4344
  {
unknown's avatar
unknown committed
4345 4346 4347
    char buf[64];
    longlong10_to_str(xid, buf, 10);

4348 4349
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tXid = %s\n", buf);
4350
  }
unknown's avatar
unknown committed
4351
  my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
4352 4353 4354 4355 4356
}
#endif /* MYSQL_CLIENT */


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4357
int Xid_log_event::do_apply_event(Relay_log_info const *rli)
4358
{
4359
  /* For a slave Xid_log_event is COMMIT */
4360 4361
  general_log_print(thd, COM_QUERY,
                    "COMMIT /* implicit, from Xid_log_event */");
4362
  return end_trans(thd, COMMIT);
4363
}
4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374

Log_event::enum_skip_reason
Xid_log_event::do_shall_skip(Relay_log_info *rli)
{
  DBUG_ENTER("Xid_log_event::do_shall_skip");
  if (rli->slave_skip_counter > 0) {
    thd->options&= ~OPTION_BEGIN;
    DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
  }
  DBUG_RETURN(Log_event::do_shall_skip(rli));
}
4375 4376 4377
#endif /* !MYSQL_CLIENT */


unknown's avatar
unknown committed
4378
/**************************************************************************
4379
  User_var_log_event methods
unknown's avatar
unknown committed
4380
**************************************************************************/
unknown's avatar
unknown committed
4381

unknown's avatar
unknown committed
4382
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
unknown's avatar
unknown committed
4383 4384 4385
void User_var_log_event::pack_info(Protocol* protocol)
{
  char *buf= 0;
4386
  uint val_offset= 4 + name_len;
unknown's avatar
unknown committed
4387 4388 4389 4390
  uint event_len= val_offset;

  if (is_null)
  {
4391 4392
    if (!(buf= (char*) my_malloc(val_offset + 5, MYF(MY_WME))))
      return;
unknown's avatar
unknown committed
4393 4394 4395 4396 4397 4398 4399 4400 4401
    strmov(buf + val_offset, "NULL");
    event_len= val_offset + 4;
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
      float8get(real_val, val);
4402 4403 4404
      if (!(buf= (char*) my_malloc(val_offset + FLOATING_POINT_BUFFER,
                                   MYF(MY_WME))))
        return;
4405 4406
      event_len+= my_sprintf(buf + val_offset,
			     (buf + val_offset, "%.14g", real_val));
unknown's avatar
unknown committed
4407 4408
      break;
    case INT_RESULT:
4409 4410
      if (!(buf= (char*) my_malloc(val_offset + 22, MYF(MY_WME))))
        return;
unknown's avatar
unknown committed
4411 4412
      event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
      break;
unknown's avatar
unknown committed
4413 4414
    case DECIMAL_RESULT:
    {
4415 4416 4417
      if (!(buf= (char*) my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH,
                                   MYF(MY_WME))))
        return;
unknown's avatar
unknown committed
4418 4419
      String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin);
      my_decimal dec;
4420 4421
      binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) (val+2), &dec, val[0],
                        val[1]);
unknown's avatar
unknown committed
4422 4423 4424 4425
      my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
      event_len= str.length() + val_offset;
      break;
    } 
unknown's avatar
unknown committed
4426
    case STRING_RESULT:
4427
      /* 15 is for 'COLLATE' and other chars */
4428 4429
      buf= (char*) my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15,
                             MYF(MY_WME));
4430
      CHARSET_INFO *cs;
4431 4432
      if (!buf)
        return;
4433 4434 4435 4436 4437 4438 4439
      if (!(cs= get_charset(charset_number, MYF(0))))
      {
        strmov(buf+val_offset, "???");
        event_len+= 3;
      }
      else
      {
4440 4441 4442
        char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
        p= str_to_hex(p, val, val_len);
        p= strxmov(p, " COLLATE ", cs->name, NullS);
4443 4444
        event_len= p-buf;
      }
unknown's avatar
unknown committed
4445
      break;
4446
    case ROW_RESULT:
unknown's avatar
unknown committed
4447
    default:
unknown's avatar
unknown committed
4448 4449 4450 4451 4452
      DBUG_ASSERT(1);
      return;
    }
  }
  buf[0]= '@';
4453
  buf[1]= '`';
4454
  memcpy(buf+2, name, name_len);
4455 4456
  buf[2+name_len]= '`';
  buf[3+name_len]= '=';
4457
  protocol->store(buf, event_len, &my_charset_bin);
4458
  my_free(buf, MYF(0));
unknown's avatar
unknown committed
4459
}
unknown's avatar
unknown committed
4460
#endif /* !MYSQL_CLIENT */
unknown's avatar
unknown committed
4461 4462


4463 4464 4465 4466
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
4467
{
4468
  buf+= description_event->common_header_len;
unknown's avatar
unknown committed
4469 4470
  name_len= uint4korr(buf);
  name= (char *) buf + UV_NAME_LEN_SIZE;
4471 4472
  buf+= UV_NAME_LEN_SIZE + name_len;
  is_null= (bool) *buf;
unknown's avatar
unknown committed
4473 4474 4475
  if (is_null)
  {
    type= STRING_RESULT;
4476
    charset_number= my_charset_bin.number;
unknown's avatar
unknown committed
4477 4478 4479 4480 4481
    val_len= 0;
    val= 0;  
  }
  else
  {
4482 4483 4484
    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
4485
		       UV_CHARSET_NUMBER_SIZE);
4486 4487
    val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
		   UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
unknown's avatar
unknown committed
4488 4489 4490 4491
  }
}


4492
#ifndef MYSQL_CLIENT
4493
bool User_var_log_event::write(IO_CACHE* file)
unknown's avatar
unknown committed
4494 4495 4496 4497
{
  char buf[UV_NAME_LEN_SIZE];
  char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + 
	    UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
4498
  uchar buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
4499
  uint buf1_length;
4500
  ulong event_length;
4501

unknown's avatar
unknown committed
4502
  int4store(buf, name_len);
4503 4504 4505 4506
  
  if ((buf1[0]= is_null))
  {
    buf1_length= 1;
4507
    val_len= 0;                                 // Length of 'pos'
4508 4509
  }    
  else
unknown's avatar
unknown committed
4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520
  {
    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
4521 4522 4523 4524 4525 4526
    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;
4527
      decimal2bin((decimal_t*)val, buf2+2, buf2[0], buf2[1]);
unknown's avatar
unknown committed
4528 4529 4530
      val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
      break;
    }
unknown's avatar
unknown committed
4531
    case STRING_RESULT:
4532
      pos= (uchar*) val;
unknown's avatar
unknown committed
4533
      break;
4534
    case ROW_RESULT:
unknown's avatar
unknown committed
4535
    default:
unknown's avatar
unknown committed
4536 4537 4538
      DBUG_ASSERT(1);
      return 0;
    }
unknown's avatar
unknown committed
4539 4540
    int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
    buf1_length= 10;
unknown's avatar
unknown committed
4541
  }
4542 4543 4544 4545 4546

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

  return (write_header(file, event_length) ||
4547 4548 4549 4550
          my_b_safe_write(file, (uchar*) buf, sizeof(buf))   ||
	  my_b_safe_write(file, (uchar*) name, name_len)     ||
	  my_b_safe_write(file, (uchar*) buf1, buf1_length) ||
	  my_b_safe_write(file, pos, val_len));
unknown's avatar
unknown committed
4551
}
4552
#endif
unknown's avatar
unknown committed
4553

4554

unknown's avatar
unknown committed
4555
/*
unknown's avatar
unknown committed
4556
  User_var_log_event::print()
unknown's avatar
unknown committed
4557
*/
unknown's avatar
unknown committed
4558 4559

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4560
void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
4561
{
4562 4563 4564
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4565
  if (!print_event_info->short_form)
unknown's avatar
unknown committed
4566
  {
4567 4568
    print_header(&cache, print_event_info, FALSE);
    my_b_printf(&cache, "\tUser_var\n");
unknown's avatar
unknown committed
4569 4570
  }

4571
  my_b_printf(&cache, "SET @`");
4572
  my_b_write(&cache, (uchar*) name, (uint) (name_len));
4573
  my_b_printf(&cache, "`");
unknown's avatar
unknown committed
4574 4575 4576

  if (is_null)
  {
unknown's avatar
unknown committed
4577
    my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
4578 4579 4580 4581 4582 4583
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      double real_val;
4584
      char real_buf[FMT_G_BUFSIZE(14)];
unknown's avatar
unknown committed
4585
      float8get(real_val, val);
4586 4587
      my_sprintf(real_buf, (real_buf, "%.14g", real_val));
      my_b_printf(&cache, ":=%s%s\n", real_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4588 4589 4590 4591
      break;
    case INT_RESULT:
      char int_buf[22];
      longlong10_to_str(uint8korr(val), int_buf, -10);
unknown's avatar
unknown committed
4592
      my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4593
      break;
unknown's avatar
unknown committed
4594 4595 4596 4597 4598 4599
    case DECIMAL_RESULT:
    {
      char str_buf[200];
      int str_len= sizeof(str_buf) - 1;
      int precision= (int)val[0];
      int scale= (int)val[1];
4600 4601
      decimal_digit_t dec_buf[10];
      decimal_t dec;
unknown's avatar
unknown committed
4602 4603 4604
      dec.len= 10;
      dec.buf= dec_buf;

4605
      bin2decimal((uchar*) val+2, &dec, precision, scale);
unknown's avatar
unknown committed
4606 4607
      decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
      str_buf[str_len]= 0;
unknown's avatar
unknown committed
4608
      my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter);
unknown's avatar
unknown committed
4609 4610
      break;
    }
unknown's avatar
unknown committed
4611
    case STRING_RESULT:
4612
    {
4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626
      /*
        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.
      */
4627 4628 4629 4630
      char *hex_str;
      CHARSET_INFO *cs;

      if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte
4631
        break; // no error, as we are 'void'
4632
      str_to_hex(hex_str, val, val_len);
4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643
      /*
        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
4644
        my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
4645
      else
unknown's avatar
unknown committed
4646 4647 4648
        my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
                    cs->csname, hex_str, cs->name,
                    print_event_info->delimiter);
4649
      my_afree(hex_str);
4650
    }
unknown's avatar
unknown committed
4651
      break;
4652
    case ROW_RESULT:
unknown's avatar
unknown committed
4653
    default:
unknown's avatar
unknown committed
4654
      DBUG_ASSERT(1);
unknown's avatar
unknown committed
4655 4656 4657 4658
      return;
    }
  }
}
unknown's avatar
SCRUM  
unknown committed
4659
#endif
4660

unknown's avatar
unknown committed
4661

unknown's avatar
unknown committed
4662
/*
4663
  User_var_log_event::do_apply_event()
unknown's avatar
unknown committed
4664
*/
unknown's avatar
unknown committed
4665

unknown's avatar
unknown committed
4666
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
4667
int User_var_log_event::do_apply_event(Relay_log_info const *rli)
unknown's avatar
unknown committed
4668 4669
{
  Item *it= 0;
4670 4671 4672
  CHARSET_INFO *charset;
  if (!(charset= get_charset(charset_number, MYF(MY_WME))))
    return 1;
unknown's avatar
unknown committed
4673 4674 4675
  LEX_STRING user_var_name;
  user_var_name.str= name;
  user_var_name.length= name_len;
4676 4677
  double real_val;
  longlong int_val;
unknown's avatar
unknown committed
4678

4679 4680 4681 4682
  /*
    We are now in a statement until the associated query log event has
    been processed.
   */
4683
  const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
4684

unknown's avatar
unknown committed
4685 4686 4687 4688 4689 4690 4691 4692 4693
  if (is_null)
  {
    it= new Item_null();
  }
  else
  {
    switch (type) {
    case REAL_RESULT:
      float8get(real_val, val);
4694
      it= new Item_float(real_val, 0);
4695
      val= (char*) &real_val;		// Pointer to value in native format
4696
      val_len= 8;
unknown's avatar
unknown committed
4697 4698
      break;
    case INT_RESULT:
4699 4700 4701
      int_val= (longlong) uint8korr(val);
      it= new Item_int(int_val);
      val= (char*) &int_val;		// Pointer to value in native format
4702
      val_len= 8;
unknown's avatar
unknown committed
4703
      break;
unknown's avatar
unknown committed
4704 4705
    case DECIMAL_RESULT:
    {
4706
      Item_decimal *dec= new Item_decimal((uchar*) val+2, val[0], val[1]);
unknown's avatar
unknown committed
4707 4708 4709 4710 4711
      it= dec;
      val= (char *)dec->val_decimal(NULL);
      val_len= sizeof(my_decimal);
      break;
    }
unknown's avatar
unknown committed
4712 4713 4714
    case STRING_RESULT:
      it= new Item_string(val, val_len, charset);
      break;
4715
    case ROW_RESULT:
unknown's avatar
unknown committed
4716
    default:
unknown's avatar
unknown committed
4717 4718 4719 4720 4721
      DBUG_ASSERT(1);
      return 0;
    }
  }
  Item_func_set_user_var e(user_var_name, it);
4722 4723 4724 4725
  /*
    Item_func_set_user_var can't substitute something else on its place =>
    0 can be passed as last argument (reference on item)
  */
4726
  e.fix_fields(thd, 0);
4727 4728 4729 4730 4731
  /*
    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
4732
  e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
unknown's avatar
unknown committed
4733
  free_root(thd->mem_root,0);
unknown's avatar
unknown committed
4734

4735 4736 4737
  return 0;
}

4738
int User_var_log_event::do_update_pos(Relay_log_info *rli)
4739
{
4740
  rli->inc_event_relay_log_pos();
unknown's avatar
unknown committed
4741 4742
  return 0;
}
4743

4744
Log_event::enum_skip_reason
4745
User_var_log_event::do_shall_skip(Relay_log_info *rli)
4746 4747 4748 4749 4750 4751 4752 4753 4754
{
  /*
    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.
  */
4755
  return continue_group(rli);
4756
}
unknown's avatar
unknown committed
4757
#endif /* !MYSQL_CLIENT */
4758 4759


unknown's avatar
unknown committed
4760
/**************************************************************************
4761
  Slave_log_event methods
unknown's avatar
unknown committed
4762
**************************************************************************/
unknown's avatar
unknown committed
4763

unknown's avatar
SCRUM  
unknown committed
4764
#ifdef HAVE_REPLICATION
4765
#ifdef MYSQL_CLIENT
4766
void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
4767
{
4768 4769
  Write_on_release_cache cache(&print_event_info->head_cache, file_arg);

unknown's avatar
unknown committed
4770
  if (print_event_info->short_form)
4771
    return;
4772 4773
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n# %s", "Unknown event\n");
4774 4775
}
#endif  
4776

4777
#ifndef MYSQL_CLIENT
4778
void Slave_log_event::pack_info(Protocol *protocol)
4779
{
unknown's avatar
unknown committed
4780
  char buf[256+HOSTNAME_LENGTH], *pos;
4781 4782 4783 4784 4785 4786 4787
  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
4788
  pos= longlong10_to_str(master_pos, pos, 10);
4789
  protocol->store(buf, pos-buf, &my_charset_bin);
4790
}
unknown's avatar
unknown committed
4791
#endif /* !MYSQL_CLIENT */
4792 4793 4794 4795


#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
4796
				 Relay_log_info* rli)
4797
  :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
4798 4799 4800 4801
{
  DBUG_ENTER("Slave_log_event");
  if (!rli->inited)				// QQ When can this happen ?
    DBUG_VOID_RETURN;
4802

4803
  Master_info* mi = rli->mi;
4804 4805 4806 4807
  // 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);
4808
  master_log_len = strlen(rli->group_master_log_name);
4809 4810 4811
  // 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))))
4812
  {
4813 4814 4815
    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;
4816
    memcpy(master_log, rli->group_master_log_name, master_log_len + 1);
4817
    master_port = mi->port;
4818
    master_pos = rli->group_master_log_pos;
unknown's avatar
unknown committed
4819
    DBUG_PRINT("info", ("master_log: %s  pos: %lu", master_log,
4820
			(ulong) master_pos));
4821
  }
4822 4823 4824 4825 4826 4827
  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
4828
#endif /* !MYSQL_CLIENT */
4829 4830 4831 4832 4833 4834 4835 4836 4837


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


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4838
void Slave_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4839
{
4840 4841
  Write_on_release_cache cache(&print_event_info->head_cache, file);

4842
  char llbuff[22];
unknown's avatar
unknown committed
4843
  if (print_event_info->short_form)
4844
    return;
4845 4846
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n\
unknown's avatar
unknown committed
4847
Slave: master_host: '%s'  master_port: %d  master_log: '%s'  master_pos: %s\n",
4848 4849
	  master_host, master_port, master_log, llstr(master_pos, llbuff));
}
unknown's avatar
unknown committed
4850
#endif /* MYSQL_CLIENT */
4851 4852 4853 4854 4855 4856 4857 4858


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


4859
#ifndef MYSQL_CLIENT
4860
bool Slave_log_event::write(IO_CACHE* file)
4861
{
4862
  ulong event_length= get_data_size();
4863 4864 4865
  int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
  int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
  // log and host are already there
4866 4867

  return (write_header(file, event_length) ||
4868
          my_b_safe_write(file, (uchar*) mem_pool, event_length));
4869
}
4870
#endif
4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881


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)
4882
  {
4883 4884
    master_host = 0;
    return;
4885
  }
4886 4887
  master_log_len = strlen(master_log);
}
4888

4889

4890 4891 4892
/* 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)
4893
{
4894
  if (event_len < LOG_EVENT_HEADER_LEN)
4895
    return;
4896
  event_len -= LOG_EVENT_HEADER_LEN;
4897 4898 4899 4900 4901
  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);
4902 4903
}

4904

4905
#ifndef MYSQL_CLIENT
4906
int Slave_log_event::do_apply_event(Relay_log_info const *rli)
4907 4908 4909
{
  if (mysql_bin_log.is_open())
    mysql_bin_log.write(this);
4910
  return 0;
4911
}
unknown's avatar
unknown committed
4912
#endif /* !MYSQL_CLIENT */
4913 4914


unknown's avatar
unknown committed
4915
/**************************************************************************
unknown's avatar
unknown committed
4916
	Stop_log_event methods
unknown's avatar
unknown committed
4917
**************************************************************************/
4918

unknown's avatar
unknown committed
4919
/*
4920
  Stop_log_event::print()
4921
*/
4922 4923

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
4924
void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
4925
{
4926 4927 4928
  Write_on_release_cache cache(&print_event_info->head_cache, file,
                               Write_on_release_cache::FLUSH_F);

unknown's avatar
unknown committed
4929
  if (print_event_info->short_form)
4930 4931
    return;

4932 4933
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\tStop\n");
4934
}
unknown's avatar
unknown committed
4935
#endif /* MYSQL_CLIENT */
4936

4937

4938
/*
4939
  Stop_log_event::do_apply_event()
4940 4941 4942 4943 4944 4945 4946 4947

  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
4948
  Start_log_event_v3::do_apply_event(), not here. Because if we come
4949
  here, the master was sane.
4950 4951
*/

4952
#ifndef MYSQL_CLIENT
4953
int Stop_log_event::do_update_pos(Relay_log_info *rli)
4954
{
unknown's avatar
unknown committed
4955 4956
  /*
    We do not want to update master_log pos because we get a rotate event
4957
    before stop, so by now group_master_log_name is set to the next log.
4958
    If we updated it, we will have incorrect master coordinates and this
unknown's avatar
unknown committed
4959
    could give false triggers in MASTER_POS_WAIT() that we have reached
4960
    the target position when in fact we have not.
unknown's avatar
unknown committed
4961
  */
4962 4963 4964 4965 4966 4967 4968
  if (thd->options & OPTION_BEGIN)
    rli->inc_event_relay_log_pos();
  else
  {
    rli->inc_group_relay_log_pos(0);
    flush_relay_log_info(rli);
  }
4969 4970
  return 0;
}
4971

unknown's avatar
unknown committed
4972
#endif /* !MYSQL_CLIENT */
unknown's avatar
SCRUM  
unknown committed
4973
#endif /* HAVE_REPLICATION */
4974

4975

unknown's avatar
unknown committed
4976
/**************************************************************************
unknown's avatar
unknown committed
4977
	Create_file_log_event methods
unknown's avatar
unknown committed
4978
**************************************************************************/
4979 4980

/*
4981
  Create_file_log_event ctor
unknown's avatar
unknown committed
4982
*/
4983 4984

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
4985 4986 4987 4988
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,
4989
                      bool ignore,
unknown's avatar
unknown committed
4990
		      char* block_arg, uint block_len_arg, bool using_trans)
4991
  :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
unknown's avatar
unknown committed
4992
		  using_trans),
4993
   fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
4994
   file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
4995
{
unknown's avatar
unknown committed
4996
  DBUG_ENTER("Create_file_log_event");
4997
  sql_ex.force_new_format();
unknown's avatar
unknown committed
4998
  DBUG_VOID_RETURN;
4999
}
5000

5001

unknown's avatar
unknown committed
5002
/*
5003
  Create_file_log_event::write_data_body()
unknown's avatar
unknown committed
5004
*/
5005

5006
bool Create_file_log_event::write_data_body(IO_CACHE* file)
5007
{
5008 5009
  bool res;
  if ((res= Load_log_event::write_data_body(file)) || fake_base)
5010
    return res;
5011 5012
  return (my_b_safe_write(file, (uchar*) "", 1) ||
          my_b_safe_write(file, (uchar*) block, block_len));
5013 5014
}

5015

unknown's avatar
unknown committed
5016
/*
5017
  Create_file_log_event::write_data_header()
unknown's avatar
unknown committed
5018
*/
unknown's avatar
unknown committed
5019

5020
bool Create_file_log_event::write_data_header(IO_CACHE* file)
5021
{
5022
  bool res;
5023
  uchar buf[CREATE_FILE_HEADER_LEN];
5024 5025
  if ((res= Load_log_event::write_data_header(file)) || fake_base)
    return res;
5026
  int4store(buf + CF_FILE_ID_OFFSET, file_id);
5027
  return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN) != 0;
5028 5029 5030
}


unknown's avatar
unknown committed
5031
/*
5032
  Create_file_log_event::write_base()
unknown's avatar
unknown committed
5033
*/
5034

5035
bool Create_file_log_event::write_base(IO_CACHE* file)
5036
{
5037 5038 5039 5040
  bool res;
  fake_base= 1;                                 // pretend we are Load event
  res= write(file);
  fake_base= 0;
5041 5042 5043
  return res;
}

5044
#endif /* !MYSQL_CLIENT */
5045

unknown's avatar
unknown committed
5046
/*
5047
  Create_file_log_event ctor
unknown's avatar
unknown committed
5048
*/
5049

5050 5051 5052
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)
5053
{
5054 5055 5056 5057 5058
  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];
5059
  if (!(event_buf= (char*) my_memdup(buf, len, MYF(MY_WME))) ||
5060 5061 5062 5063 5064 5065 5066
      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
5067
    DBUG_VOID_RETURN;
5068
  if (description_event->binlog_version!=1)
5069
  {
5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086
    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);
5087
    if (len < block_offset)
5088
      DBUG_VOID_RETURN;
5089 5090 5091 5092 5093 5094 5095
    block = (char*)buf + block_offset;
    block_len = len - block_offset;
  }
  else
  {
    sql_ex.force_new_format();
    inited_from_old = 1;
5096
  }
unknown's avatar
unknown committed
5097
  DBUG_VOID_RETURN;
5098 5099
}

5100

unknown's avatar
unknown committed
5101
/*
5102
  Create_file_log_event::print()
unknown's avatar
unknown committed
5103
*/
5104 5105

#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
5106
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
5107
				  bool enable_local)
unknown's avatar
unknown committed
5108
{
5109 5110
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5111
  if (print_event_info->short_form)
5112 5113
  {
    if (enable_local && check_fname_outside_temp_buf())
unknown's avatar
unknown committed
5114
      Load_log_event::print(file, print_event_info);
5115
    return;
5116 5117 5118 5119
  }

  if (enable_local)
  {
unknown's avatar
unknown committed
5120
    Load_log_event::print(file, print_event_info,
5121
			  !check_fname_outside_temp_buf());
unknown's avatar
unknown committed
5122 5123 5124 5125
    /* 
       That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
       SHOW BINLOG EVENTS we don't.
    */
5126
    my_b_printf(&cache, "#"); 
5127 5128
  }

5129
  my_b_printf(&cache, " file_id: %d  block_len: %d\n", file_id, block_len);
unknown's avatar
unknown committed
5130
}
5131

unknown's avatar
unknown committed
5132

unknown's avatar
unknown committed
5133
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
5134
{
unknown's avatar
unknown committed
5135
  print(file, print_event_info, 0);
5136
}
unknown's avatar
unknown committed
5137
#endif /* MYSQL_CLIENT */
unknown's avatar
unknown committed
5138

5139

unknown's avatar
unknown committed
5140
/*
5141
  Create_file_log_event::pack_info()
unknown's avatar
unknown committed
5142
*/
5143

unknown's avatar
SCRUM  
unknown committed
5144
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5145
void Create_file_log_event::pack_info(Protocol *protocol)
5146
{
5147 5148 5149
  char buf[NAME_LEN*2 + 30 + 21*2], *pos;
  pos= strmov(buf, "db=");
  memcpy(pos, db, db_len);
unknown's avatar
unknown committed
5150
  pos= strmov(pos + db_len, ";table=");
5151
  memcpy(pos, table_name, table_name_len);
unknown's avatar
unknown committed
5152
  pos= strmov(pos + table_name_len, ";file_id=");
5153 5154 5155
  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
5156
  protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
5157
}
unknown's avatar
unknown committed
5158
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5159 5160


unknown's avatar
unknown committed
5161
/*
5162
  Create_file_log_event::do_apply_event()
unknown's avatar
unknown committed
5163
*/
5164

unknown's avatar
SCRUM  
unknown committed
5165
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5166
int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
5167
{
unknown's avatar
unknown committed
5168 5169
  char proc_info[17+FN_REFLEN+10], *fname_buf;
  char *ext;
5170 5171 5172
  int fd = -1;
  IO_CACHE file;
  int error = 1;
unknown's avatar
unknown committed
5173

5174
  bzero((char*)&file, sizeof(file));
unknown's avatar
unknown committed
5175 5176
  fname_buf= strmov(proc_info, "Making temp file ");
  ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
5177
  thd->proc_info= proc_info;
5178 5179 5180 5181
  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 ||
5182 5183 5184
      init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
5185 5186 5187
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: could not open file '%s'",
                fname_buf);
5188 5189 5190 5191
    goto err;
  }
  
  // a trick to avoid allocating another buffer
unknown's avatar
unknown committed
5192 5193
  fname= fname_buf;
  fname_len= (uint) (strmov(ext, ".data") - fname);
5194 5195
  if (write_base(&file))
  {
unknown's avatar
unknown committed
5196
    strmov(ext, ".info"); // to have it right in the error message
5197 5198 5199
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: could not write to file '%s'",
                fname_buf);
5200 5201 5202 5203 5204 5205
    goto err;
  }
  end_io_cache(&file);
  my_close(fd, MYF(0));
  
  // fname_buf now already has .data, not .info, because we did our trick
5206 5207 5208 5209
  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)
5210
  {
5211 5212 5213
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: could not open file '%s'",
                fname_buf);
5214 5215
    goto err;
  }
5216
  if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
5217
  {
5218 5219 5220
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Create_file event: write to '%s' failed",
                fname_buf);
5221 5222
    goto err;
  }
5223 5224
  error=0;					// Everything is ok

5225 5226 5227 5228 5229
err:
  if (error)
    end_io_cache(&file);
  if (fd >= 0)
    my_close(fd, MYF(0));
unknown's avatar
unknown committed
5230
  thd->proc_info= 0;
5231
  return error == 0;
5232
}
unknown's avatar
unknown committed
5233
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5234

5235

unknown's avatar
unknown committed
5236
/**************************************************************************
unknown's avatar
unknown committed
5237
	Append_block_log_event methods
unknown's avatar
unknown committed
5238
**************************************************************************/
5239

unknown's avatar
unknown committed
5240
/*
5241
  Append_block_log_event ctor
unknown's avatar
unknown committed
5242
*/
5243 5244

#ifndef MYSQL_CLIENT  
5245 5246 5247
Append_block_log_event::Append_block_log_event(THD *thd_arg,
                                               const char *db_arg,
					       char *block_arg,
unknown's avatar
unknown committed
5248 5249 5250
					       uint block_len_arg,
					       bool using_trans)
  :Log_event(thd_arg,0, using_trans), block(block_arg),
unknown's avatar
unknown committed
5251
   block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
5252 5253
{
}
unknown's avatar
unknown committed
5254
#endif
5255 5256


unknown's avatar
unknown committed
5257
/*
5258
  Append_block_log_event ctor
unknown's avatar
unknown committed
5259
*/
5260

5261 5262 5263
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)
5264
{
5265 5266 5267 5268 5269 5270
  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
5271
    DBUG_VOID_RETURN;
5272 5273 5274
  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
5275
  DBUG_VOID_RETURN;
5276 5277 5278
}


unknown's avatar
unknown committed
5279
/*
5280
  Append_block_log_event::write()
unknown's avatar
unknown committed
5281
*/
5282

5283
#ifndef MYSQL_CLIENT
5284
bool Append_block_log_event::write(IO_CACHE* file)
5285
{
5286
  uchar buf[APPEND_BLOCK_HEADER_LEN];
5287
  int4store(buf + AB_FILE_ID_OFFSET, file_id);
5288 5289
  return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
          my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
5290
	  my_b_safe_write(file, (uchar*) block, block_len));
5291
}
5292
#endif
5293 5294


unknown's avatar
unknown committed
5295
/*
5296
  Append_block_log_event::print()
unknown's avatar
unknown committed
5297
*/
5298 5299

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5300
void Append_block_log_event::print(FILE* file,
unknown's avatar
unknown committed
5301
				   PRINT_EVENT_INFO* print_event_info)
5302
{
5303 5304
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5305
  if (print_event_info->short_form)
5306
    return;
5307 5308 5309
  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);
5310
}
unknown's avatar
unknown committed
5311
#endif /* MYSQL_CLIENT */
5312 5313


unknown's avatar
unknown committed
5314
/*
5315
  Append_block_log_event::pack_info()
unknown's avatar
unknown committed
5316
*/
5317

unknown's avatar
SCRUM  
unknown committed
5318
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5319
void Append_block_log_event::pack_info(Protocol *protocol)
5320 5321 5322 5323 5324 5325
{
  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
5326
  protocol->store(buf, length, &my_charset_bin);
5327 5328 5329
}


unknown's avatar
unknown committed
5330
/*
5331
  Append_block_log_event::get_create_or_append()
unknown's avatar
unknown committed
5332 5333
*/

5334
int Append_block_log_event::get_create_or_append() const
unknown's avatar
unknown committed
5335
{
5336
  return 0; /* append to the file, fail if not exists */
unknown's avatar
unknown committed
5337 5338
}

unknown's avatar
unknown committed
5339
/*
5340
  Append_block_log_event::do_apply_event()
unknown's avatar
unknown committed
5341
*/
5342

5343
int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
5344
{
5345
  char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
5346
  int fd;
5347
  int error = 1;
5348
  DBUG_ENTER("Append_block_log_event::do_apply_event");
5349

unknown's avatar
unknown committed
5350 5351
  fname= strmov(proc_info, "Making temp file ");
  slave_load_file_stem(fname, file_id, server_id, ".data");
5352
  thd->proc_info= proc_info;
5353 5354 5355 5356 5357 5358 5359
  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)
    {
5360 5361 5362
      rli->report(ERROR_LEVEL, my_errno,
                  "Error in %s event: could not create file '%s'",
                  get_type_str(), fname);
5363 5364 5365
      goto err;
    }
  }
5366 5367
  else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
                         MYF(MY_WME))) < 0)
5368
  {
5369 5370 5371
    rli->report(ERROR_LEVEL, my_errno,
                "Error in %s event: could not open file '%s'",
                get_type_str(), fname);
5372 5373
    goto err;
  }
5374
  if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
5375
  {
5376 5377 5378
    rli->report(ERROR_LEVEL, my_errno,
                "Error in %s event: write to '%s' failed",
                get_type_str(), fname);
5379 5380 5381
    goto err;
  }
  error=0;
5382

5383 5384 5385
err:
  if (fd >= 0)
    my_close(fd, MYF(0));
5386
  thd->proc_info= 0;
5387
  DBUG_RETURN(error);
5388
}
unknown's avatar
SCRUM  
unknown committed
5389
#endif
5390 5391


unknown's avatar
unknown committed
5392
/**************************************************************************
unknown's avatar
unknown committed
5393
	Delete_file_log_event methods
unknown's avatar
unknown committed
5394
**************************************************************************/
5395

unknown's avatar
unknown committed
5396
/*
5397
  Delete_file_log_event ctor
unknown's avatar
unknown committed
5398
*/
5399 5400

#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5401 5402 5403
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)
5404 5405
{
}
unknown's avatar
unknown committed
5406
#endif
5407

unknown's avatar
unknown committed
5408
/*
5409
  Delete_file_log_event ctor
unknown's avatar
unknown committed
5410
*/
5411

5412 5413 5414
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)
5415
{
5416 5417 5418
  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))
5419
    return;
5420
  file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
5421 5422 5423
}


unknown's avatar
unknown committed
5424
/*
5425
  Delete_file_log_event::write()
unknown's avatar
unknown committed
5426
*/
5427

5428
#ifndef MYSQL_CLIENT
5429
bool Delete_file_log_event::write(IO_CACHE* file)
5430
{
5431
 uchar buf[DELETE_FILE_HEADER_LEN];
5432
 int4store(buf + DF_FILE_ID_OFFSET, file_id);
5433 5434
 return (write_header(file, sizeof(buf)) ||
         my_b_safe_write(file, buf, sizeof(buf)));
5435
}
5436
#endif
5437 5438


unknown's avatar
unknown committed
5439
/*
5440
  Delete_file_log_event::print()
unknown's avatar
unknown committed
5441
*/
5442 5443

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5444
void Delete_file_log_event::print(FILE* file,
unknown's avatar
unknown committed
5445
				  PRINT_EVENT_INFO* print_event_info)
5446
{
5447 5448
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5449
  if (print_event_info->short_form)
5450
    return;
5451 5452
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id);
5453
}
unknown's avatar
unknown committed
5454
#endif /* MYSQL_CLIENT */
5455

unknown's avatar
unknown committed
5456
/*
5457
  Delete_file_log_event::pack_info()
unknown's avatar
unknown committed
5458
*/
5459

unknown's avatar
SCRUM  
unknown committed
5460
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5461
void Delete_file_log_event::pack_info(Protocol *protocol)
5462 5463 5464 5465
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
5466
  protocol->store(buf, (int32) length, &my_charset_bin);
5467
}
unknown's avatar
SCRUM  
unknown committed
5468
#endif
5469

unknown's avatar
unknown committed
5470
/*
5471
  Delete_file_log_event::do_apply_event()
unknown's avatar
unknown committed
5472
*/
5473

unknown's avatar
SCRUM  
unknown committed
5474
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5475
int Delete_file_log_event::do_apply_event(Relay_log_info const *rli)
5476 5477
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
5478
  char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
5479
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5480
  strmov(ext, ".info");
5481
  (void) my_delete(fname, MYF(MY_WME));
5482
  return 0;
5483
}
unknown's avatar
unknown committed
5484
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5485 5486


unknown's avatar
unknown committed
5487
/**************************************************************************
unknown's avatar
unknown committed
5488
	Execute_load_log_event methods
unknown's avatar
unknown committed
5489
**************************************************************************/
5490

unknown's avatar
unknown committed
5491
/*
5492
  Execute_load_log_event ctor
unknown's avatar
unknown committed
5493
*/
5494 5495

#ifndef MYSQL_CLIENT  
5496 5497
Execute_load_log_event::Execute_load_log_event(THD *thd_arg,
                                               const char* db_arg,
unknown's avatar
unknown committed
5498 5499
					       bool using_trans)
  :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
5500 5501
{
}
unknown's avatar
unknown committed
5502
#endif
5503 5504
  

unknown's avatar
unknown committed
5505
/*
5506
  Execute_load_log_event ctor
unknown's avatar
unknown committed
5507
*/
5508

5509 5510 5511
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)
5512
{
5513 5514 5515
  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))
5516
    return;
5517
  file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
5518 5519 5520
}


unknown's avatar
unknown committed
5521
/*
5522
  Execute_load_log_event::write()
unknown's avatar
unknown committed
5523
*/
5524

5525
#ifndef MYSQL_CLIENT
5526
bool Execute_load_log_event::write(IO_CACHE* file)
5527
{
5528
  uchar buf[EXEC_LOAD_HEADER_LEN];
5529
  int4store(buf + EL_FILE_ID_OFFSET, file_id);
5530 5531
  return (write_header(file, sizeof(buf)) || 
          my_b_safe_write(file, buf, sizeof(buf)));
5532
}
5533
#endif
5534 5535


unknown's avatar
unknown committed
5536
/*
5537
  Execute_load_log_event::print()
unknown's avatar
unknown committed
5538
*/
5539 5540

#ifdef MYSQL_CLIENT  
unknown's avatar
unknown committed
5541
void Execute_load_log_event::print(FILE* file,
unknown's avatar
unknown committed
5542
				   PRINT_EVENT_INFO* print_event_info)
5543
{
5544 5545
  Write_on_release_cache cache(&print_event_info->head_cache, file);

unknown's avatar
unknown committed
5546
  if (print_event_info->short_form)
5547
    return;
5548 5549 5550
  print_header(&cache, print_event_info, FALSE);
  my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
              file_id);
5551
}
unknown's avatar
unknown committed
5552
#endif
5553

unknown's avatar
unknown committed
5554
/*
5555
  Execute_load_log_event::pack_info()
unknown's avatar
unknown committed
5556
*/
5557

unknown's avatar
SCRUM  
unknown committed
5558
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
5559
void Execute_load_log_event::pack_info(Protocol *protocol)
5560 5561 5562 5563
{
  char buf[64];
  uint length;
  length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
5564
  protocol->store(buf, (int32) length, &my_charset_bin);
5565 5566 5567
}


unknown's avatar
unknown committed
5568
/*
5569
  Execute_load_log_event::do_apply_event()
unknown's avatar
unknown committed
5570
*/
unknown's avatar
SCRUM  
unknown committed
5571

5572
int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
5573 5574
{
  char fname[FN_REFLEN+10];
unknown's avatar
unknown committed
5575
  char *ext;
5576
  int fd;
unknown's avatar
unknown committed
5577
  int error= 1;
5578
  IO_CACHE file;
unknown's avatar
unknown committed
5579
  Load_log_event *lev= 0;
5580

unknown's avatar
unknown committed
5581
  ext= slave_load_file_stem(fname, file_id, server_id, ".info");
5582 5583
  if ((fd = my_open(fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
                    MYF(MY_WME))) < 0 ||
5584 5585 5586
      init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
		    MYF(MY_WME|MY_NABP)))
  {
5587 5588 5589
    rli->report(ERROR_LEVEL, my_errno,
                "Error in Exec_load event: could not open file '%s'",
                fname);
5590 5591
    goto err;
  }
5592
  if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
5593 5594
                                                         (pthread_mutex_t*)0,
                                                         rli->relay_log.description_event_for_exec)) ||
5595
      lev->get_type_code() != NEW_LOAD_EVENT)
5596
  {
5597
    rli->report(ERROR_LEVEL, 0, "Error in Exec_load event: "
5598
                    "file '%s' appears corrupted", fname);
5599 5600
    goto err;
  }
unknown's avatar
unknown committed
5601

5602
  lev->thd = thd;
5603
  /*
5604
    lev->do_apply_event should use rli only for errors i.e. should
5605 5606
    not advance rli's position.

5607
    lev->do_apply_event is the place where the table is loaded (it
5608
    calls mysql_load()).
5609
  */
unknown's avatar
unknown committed
5610

5611
  const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
5612
  if (lev->do_apply_event(0,rli,1)) 
5613
  {
5614 5615 5616 5617 5618 5619 5620 5621
    /*
      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.
    */
5622
    char *tmp= my_strdup(rli->last_error().message, MYF(MY_WME));
5623 5624
    if (tmp)
    {
5625
      rli->report(ERROR_LEVEL, rli->last_error().number,
5626
                  "%s. Failed executing load from '%s'", tmp, fname);
5627 5628
      my_free(tmp,MYF(0));
    }
5629 5630
    goto err;
  }
unknown's avatar
unknown committed
5631 5632 5633 5634 5635 5636 5637 5638 5639 5640
  /*
    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;
  }
5641
  (void) my_delete(fname, MYF(MY_WME));
unknown's avatar
unknown committed
5642
  memcpy(ext, ".data", 6);
5643
  (void) my_delete(fname, MYF(MY_WME));
5644
  error = 0;
5645

5646 5647 5648
err:
  delete lev;
  if (fd >= 0)
5649
  {
5650
    my_close(fd, MYF(0));
5651 5652
    end_io_cache(&file);
  }
5653
  return error;
5654
}
unknown's avatar
SCRUM  
unknown committed
5655

unknown's avatar
unknown committed
5656
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
5657 5658


unknown's avatar
unknown committed
5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683
/**************************************************************************
	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)
5684
int Begin_load_query_log_event::get_create_or_append() const
unknown's avatar
unknown committed
5685
{
5686
  return 1; /* create the file */
unknown's avatar
unknown committed
5687 5688 5689 5690
}
#endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */


5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
Log_event::enum_skip_reason
Begin_load_query_log_event::do_shall_skip(Relay_log_info *rli)
{
  /*
    If the slave skip counter is 1, then we should not start executing
    on the next event.
  */
  return continue_group(rli);
}
#endif


unknown's avatar
unknown committed
5704 5705 5706 5707 5708 5709 5710
/**************************************************************************
	Execute_load_query_log_event methods
**************************************************************************/


#ifndef MYSQL_CLIENT
Execute_load_query_log_event::
5711
Execute_load_query_log_event(THD *thd_arg, const char* query_arg,
5712 5713 5714 5715 5716
                             ulong query_length_arg, uint fn_pos_start_arg,
                             uint fn_pos_end_arg,
                             enum_load_dup_handling dup_handling_arg,
                             bool using_trans, bool suppress_use,
                             THD::killed_state killed_err_arg):
unknown's avatar
unknown committed
5717
  Query_log_event(thd_arg, query_arg, query_length_arg, using_trans,
5718
                  suppress_use, killed_err_arg),
unknown's avatar
unknown committed
5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 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
  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;
}


5755
#ifndef MYSQL_CLIENT
unknown's avatar
unknown committed
5756 5757 5758
bool
Execute_load_query_log_event::write_post_header_for_derived(IO_CACHE* file)
{
5759
  uchar buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
unknown's avatar
unknown committed
5760 5761 5762
  int4store(buf, file_id);
  int4store(buf + 4, fn_pos_start);
  int4store(buf + 4 + 4, fn_pos_end);
5763 5764
  *(buf + 4 + 4 + 4)= (uchar) dup_handling;
  return my_b_safe_write(file, buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
unknown's avatar
unknown committed
5765
}
5766
#endif
unknown's avatar
unknown committed
5767 5768 5769


#ifdef MYSQL_CLIENT
unknown's avatar
unknown committed
5770
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5771
                                         PRINT_EVENT_INFO* print_event_info)
unknown's avatar
unknown committed
5772
{
unknown's avatar
unknown committed
5773
  print(file, print_event_info, 0);
unknown's avatar
unknown committed
5774 5775 5776
}


unknown's avatar
unknown committed
5777
void Execute_load_query_log_event::print(FILE* file,
unknown's avatar
unknown committed
5778
                                         PRINT_EVENT_INFO* print_event_info,
unknown's avatar
unknown committed
5779 5780
                                         const char *local_fname)
{
5781 5782 5783
  Write_on_release_cache cache(&print_event_info->head_cache, file);

  print_query_header(&cache, print_event_info);
unknown's avatar
unknown committed
5784 5785 5786

  if (local_fname)
  {
5787
    my_b_write(&cache, (uchar*) query, fn_pos_start);
5788 5789 5790
    my_b_printf(&cache, " LOCAL INFILE \'");
    my_b_printf(&cache, local_fname);
    my_b_printf(&cache, "\'");
unknown's avatar
unknown committed
5791
    if (dup_handling == LOAD_DUP_REPLACE)
5792 5793
      my_b_printf(&cache, " REPLACE");
    my_b_printf(&cache, " INTO");
5794
    my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end);
5795
    my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5796 5797 5798
  }
  else
  {
5799
    my_b_write(&cache, (uchar*) query, q_len);
5800
    my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
unknown's avatar
unknown committed
5801 5802
  }

unknown's avatar
unknown committed
5803
  if (!print_event_info->short_form)
5804
    my_b_printf(&cache, "# file_id: %d \n", file_id);
unknown's avatar
unknown committed
5805 5806 5807 5808 5809 5810 5811 5812
}
#endif


#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
void Execute_load_query_log_event::pack_info(Protocol *protocol)
{
  char *buf, *pos;
5813
  if (!(buf= (char*) my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME))))
unknown's avatar
unknown committed
5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834
    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
5835
Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
unknown's avatar
unknown committed
5836 5837 5838 5839 5840 5841 5842
{
  char *p;
  char *buf;
  char *fname;
  char *fname_end;
  int error;

5843 5844
  buf= (char*) my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
                         (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME));
5845

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

unknown's avatar
unknown committed
5848
  /* Replace filename and LOCAL keyword in query before executing it */
5849
  if (buf == NULL)
unknown's avatar
unknown committed
5850
  {
5851 5852
    rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
                ER(ER_SLAVE_FATAL_ERROR), "Not enough memory");
unknown's avatar
unknown committed
5853 5854 5855 5856 5857 5858
    return 1;
  }

  p= buf;
  memcpy(p, query, fn_pos_start);
  p+= fn_pos_start;
5859
  fname= (p= strmake(p, STRING_WITH_LEN(" INFILE \'")));
unknown's avatar
unknown committed
5860 5861
  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
5862
  *(p++)='\'';
unknown's avatar
unknown committed
5863
  switch (dup_handling) {
unknown's avatar
unknown committed
5864
  case LOAD_DUP_IGNORE:
5865
    p= strmake(p, STRING_WITH_LEN(" IGNORE"));
unknown's avatar
unknown committed
5866 5867
    break;
  case LOAD_DUP_REPLACE:
5868
    p= strmake(p, STRING_WITH_LEN(" REPLACE"));
unknown's avatar
unknown committed
5869 5870 5871 5872 5873
    break;
  default:
    /* Ordinary load data */
    break;
  }
5874
  p= strmake(p, STRING_WITH_LEN(" INTO"));
unknown's avatar
unknown committed
5875 5876
  p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);

5877
  error= Query_log_event::do_apply_event(rli, buf, p-buf);
unknown's avatar
unknown committed
5878 5879 5880 5881

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

5882 5883 5884 5885 5886 5887
  /*
    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
5888 5889 5890 5891 5892 5893 5894

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


unknown's avatar
unknown committed
5895
/**************************************************************************
unknown's avatar
unknown committed
5896
	sql_ex_info methods
unknown's avatar
unknown committed
5897
**************************************************************************/
5898

unknown's avatar
unknown committed
5899
/*
5900
  sql_ex_info::write_data()
unknown's avatar
unknown committed
5901
*/
5902

5903
bool sql_ex_info::write_data(IO_CACHE* file)
5904 5905 5906
{
  if (new_format())
  {
5907 5908 5909 5910 5911
    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) ||
5912
	    my_b_safe_write(file,(uchar*) &opt_flags,1));
5913 5914 5915
  }
  else
  {
5916 5917 5918 5919
    /**
      @todo This is sensitive to field padding. We should write a
      char[7], not an old_sql_ex. /sven
    */
5920 5921 5922 5923 5924 5925 5926 5927
    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;
5928
    return my_b_safe_write(file, (uchar*) &old_ex, sizeof(old_ex)) != 0;
5929 5930 5931 5932
  }
}


unknown's avatar
unknown committed
5933
/*
5934
  sql_ex_info::init()
unknown's avatar
unknown committed
5935
*/
5936

5937
char *sql_ex_info::init(char *buf, char *buf_end, bool use_new_format)
5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949
{
  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.
    */
5950 5951 5952 5953 5954 5955
    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))
5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981
      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;
}
5982 5983 5984 5985 5986 5987 5988 5989 5990 5991


/**************************************************************************
	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),
5992
    m_row_count(0),
5993 5994
    m_table(tbl_arg),
    m_table_id(tid),
5995
    m_width(tbl_arg ? tbl_arg->s->fields : 1),
5996 5997
    m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0) 
#ifdef HAVE_REPLICATION
5998
    , m_curr_row(NULL), m_curr_row_end(NULL), m_key(NULL)
5999
#endif
6000
{
6001 6002
  /*
    We allow a special form of dummy event when the table, and cols
6003
    are null and the table id is ~0UL.  This is a temporary
6004
    solution, to be able to terminate a started statement in the
6005
    binary log: the extraneous events will be removed in the future.
6006
   */
6007 6008
  DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
              !tbl_arg && !cols && tid == ~0UL);
6009 6010 6011 6012 6013

  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);
6014
  /* if bitmap_init fails, caught in is_valid() */
6015 6016
  if (likely(!bitmap_init(&m_cols,
                          m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
6017
                          m_width,
6018
                          false)))
6019 6020 6021
  {
    /* Cols can be zero if this is a dummy binrows event */
    if (likely(cols != NULL))
6022
    {
6023
      memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
6024 6025
      create_last_word_mask(&m_cols);
    }
6026
  }
6027
  else
6028 6029 6030 6031
  {
    // Needed because bitmap_init() does not set it to null on failure
    m_cols.bitmap= 0;
  }
6032 6033 6034 6035 6036 6037 6038 6039
}
#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),
6040
    m_row_count(0),
6041
#ifndef MYSQL_CLIENT
6042
    m_table(NULL),
6043 6044 6045
#endif
    m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
6046
    , m_curr_row(NULL), m_curr_row_end(NULL), m_key(NULL)
6047
#endif
6048 6049 6050 6051 6052
{
  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
6053 6054
  DBUG_PRINT("enter",("event_len: %u  common_header_len: %d  "
		      "post_header_len: %d",
6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067
		      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
  {
6068
    m_table_id= (ulong) uint6korr(post_start);
6069 6070 6071 6072 6073
    post_start+= RW_FLAGS_OFFSET;
  }

  m_flags= uint2korr(post_start);

6074 6075 6076
  uchar const *const var_start=
    (const uchar *)buf + common_header_len + post_header_len;
  uchar const *const ptr_width= var_start;
6077
  uchar *ptr_after_width= (uchar*) ptr_width;
6078
  DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
6079
  m_width = net_field_length(&ptr_after_width);
6080 6081 6082 6083
  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,
6084
                          m_width,
6085 6086 6087 6088
                          false)))
  {
    DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
    memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8);
6089
    create_last_word_mask(&m_cols);
6090
    ptr_after_width+= (m_width + 7) / 8;
6091
    DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
6092 6093 6094 6095 6096 6097 6098
  }
  else
  {
    // Needed because bitmap_init() does not set it to null on failure
    m_cols.bitmap= NULL;
    DBUG_VOID_RETURN;
  }
6099

6100 6101 6102 6103 6104 6105
  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));

6106
    /* if bitmap_init fails, caught in is_valid() */
6107 6108
    if (likely(!bitmap_init(&m_cols_ai,
                            m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
6109
                            m_width,
6110 6111 6112 6113
                            false)))
    {
      DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
      memcpy(m_cols_ai.bitmap, ptr_after_width, (m_width + 7) / 8);
6114
      create_last_word_mask(&m_cols_ai);
6115
      ptr_after_width+= (m_width + 7) / 8;
6116 6117
      DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
                no_bytes_in_map(&m_cols_ai));
6118 6119 6120 6121 6122 6123 6124 6125 6126
    }
    else
    {
      // Needed because bitmap_init() does not set it to null on failure
      m_cols_ai.bitmap= 0;
      DBUG_VOID_RETURN;
    }
  }

6127
  const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
6128

6129
  size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf);
unknown's avatar
unknown committed
6130
  DBUG_PRINT("info",("m_table_id: %lu  m_flags: %d  m_width: %lu  data_size: %lu",
6131
                     m_table_id, m_flags, m_width, (ulong) data_size));
6132

6133
  m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
6134 6135
  if (likely((bool)m_rows_buf))
  {
6136
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
6137
    m_curr_row= m_rows_buf;
6138
#endif
6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153
    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().
6154
  my_free((uchar*)m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
6155 6156
}

6157 6158 6159 6160
int Rows_log_event::get_data_size()
{
  int const type_code= get_type_code();

6161 6162
  uchar buf[sizeof(m_width)+1];
  uchar *end= net_store_length(buf, (m_width + 7) / 8);
6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179

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


6180
#ifndef MYSQL_CLIENT
6181
int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
6182 6183 6184 6185 6186 6187
{
  /*
    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
  */
6188
  DBUG_ENTER("Rows_log_event::do_add_row_data");
unknown's avatar
unknown committed
6189 6190
  DBUG_PRINT("enter", ("row_data: 0x%lx  length: %lu", (ulong) row_data,
                       (ulong) length));
6191 6192 6193 6194 6195
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
6196
  DBUG_DUMP("row_data", row_data, min(length, 32));
6197
#endif
6198 6199

  DBUG_ASSERT(m_rows_buf <= m_rows_cur);
6200
  DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
6201 6202 6203
  DBUG_ASSERT(m_rows_cur <= m_rows_end);

  /* The cast will always work since m_rows_cur <= m_rows_end */
6204
  if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
6205
  {
6206
    size_t const block_size= 1024;
6207
    my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
6208
    my_ptrdiff_t const new_alloc= 
6209
        block_size * ((cur_size + length + block_size - 1) / block_size);
6210

6211
    uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc,
6212
                                           MYF(MY_ALLOW_ZERO_PTR|MY_WME));
6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229
    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;
  }

6230
  DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
6231 6232
  memcpy(m_rows_cur, row_data, length);
  m_rows_cur+= length;
6233
  m_row_count++;
6234 6235 6236 6237 6238
  DBUG_RETURN(0);
}
#endif

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
6239
int Rows_log_event::do_apply_event(Relay_log_info const *rli)
6240
{
6241
  DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
6242
  int error= 0;
6243
  /*
6244 6245 6246
    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
6247
    success.
6248
   */
6249
  if (m_table_id == ~0UL)
6250 6251 6252 6253 6254 6255 6256
  {
    /*
       This one is supposed to be set: just an extra check so that
       nothing strange has happened.
     */
    DBUG_ASSERT(get_flags(STMT_END_F));

6257
    const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6258
    close_thread_tables(thd);
6259 6260 6261
    thd->clear_error();
    DBUG_RETURN(0);
  }
6262 6263 6264

  /*
    'thd' has been set by exec_relay_log_event(), just before calling
6265
    do_apply_event(). We still check here to prevent future coding
6266
    errors.
6267 6268 6269 6270
  */
  DBUG_ASSERT(rli->sql_thd == thd);

  /*
6271 6272 6273 6274
    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.
6275
  */
6276
  if (!thd->lock)
6277
  {
6278 6279
    bool need_reopen= 1; /* To execute the first lap of the loop below */

6280
    /*
6281
      lock_tables() reads the contents of thd->lex, so they must be
6282
      initialized. Contrary to in
6283
      Table_map_log_event::do_apply_event() we don't call
6284
      mysql_init_query() as that may reset the binlog format.
6285
    */
6286
    lex_start(thd);
6287

6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305
    /*
      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));


6306 6307 6308
    while ((error= lock_tables(thd, rli->tables_to_lock,
                               rli->tables_to_lock_count, &need_reopen)))
    {
6309 6310
      if (!need_reopen)
      {
6311
        if (thd->is_slave_error || thd->is_fatal_error)
6312 6313 6314 6315 6316
        {
          /*
            Error reporting borrowed from Query_log_event with many excessive
            simplifications (we don't honour --slave-skip-errors)
          */
6317
          uint actual_error= thd->main_da.sql_errno();
6318 6319
          rli->report(ERROR_LEVEL, actual_error,
                      "Error '%s' in %s event: when locking tables",
6320
                      (actual_error ? thd->main_da.message():
6321 6322
                       "unexpected success or fatal error"),
                      get_type_str());
6323 6324 6325 6326
          thd->is_fatal_error= 1;
        }
        else
        {
6327 6328 6329
          rli->report(ERROR_LEVEL, error,
                      "Error in %s event: when locking tables",
                      get_type_str());
6330
        }
6331
        const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6332 6333
        DBUG_RETURN(error);
      }
6334

6335
      /*
6336
        So we need to reopen the tables.
6337

6338 6339
        We need to flush the pending RBR event, since it keeps a
        pointer to an open table.
6340

6341 6342 6343
        ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
        the pending RBR event and reset the table pointer after the
        tables has been reopened.
6344

6345 6346
        NOTE: For this new scheme there should be no pending event:
        need to add code to assert that is the case.
6347 6348
       */
      thd->binlog_flush_pending_rows_event(false);
6349 6350
      TABLE_LIST *tables= rli->tables_to_lock;
      close_tables_for_reopen(thd, &tables);
6351

6352 6353
      uint tables_count= rli->tables_to_lock_count;
      if ((error= open_tables(thd, &tables, &tables_count, 0)))
6354
      {
6355
        if (thd->is_slave_error || thd->is_fatal_error)
6356 6357 6358 6359 6360
        {
          /*
            Error reporting borrowed from Query_log_event with many excessive
            simplifications (we don't honour --slave-skip-errors)
          */
6361
          uint actual_error= thd->main_da.sql_errno();
6362 6363
          rli->report(ERROR_LEVEL, actual_error,
                      "Error '%s' on reopening tables",
6364
                      (actual_error ? thd->main_da.message() :
6365
                       "unexpected success or fatal error"));
6366
          thd->is_slave_error= 1;
6367
        }
6368
        const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6369
        DBUG_RETURN(error);
6370
      }
6371
    }
6372 6373 6374 6375 6376 6377 6378 6379 6380 6381

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

    {
6382
      RPL_TABLE_LIST *ptr= rli->tables_to_lock;
6383 6384 6385 6386 6387 6388
      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;
6389
          thd->is_slave_error= 1;
6390
          const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6391 6392 6393 6394 6395
          DBUG_RETURN(ERR_BAD_TABLE_DEF);
        }
      }
    }

6396
    /*
6397 6398
      ... and then we add all the tables to the table map and remove
      them from tables to lock.
6399 6400 6401

      We also invalidate the query cache for all the tables, since
      they will now be changed.
6402 6403 6404 6405 6406 6407 6408

      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.
6409
     */
6410
    for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
6411
    {
6412
      const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
6413
    }
6414 6415 6416
#ifdef HAVE_QUERY_CACHE
    query_cache.invalidate_locked_for_write(rli->tables_to_lock);
#endif
6417 6418
  }

6419 6420
  TABLE* 
    table= 
6421
    m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
6422 6423 6424 6425 6426

  if (table)
  {
    /*
      table == NULL means that this table should not be replicated
6427
      (this was set up by Table_map_log_event::do_apply_event()
6428
      which tested replicate-* rules).
6429
    */
6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440

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

6441 6442 6443 6444 6445 6446 6447 6448 6449
    /*
      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.
     */
6450
    const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
6451

6452 6453
     if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
      set_flags(COMPLETE_ROWS_F);
6454

6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470
    /* 
      Set tables write and read sets.
      
      Read_set contains all slave columns (in case we are going to fetch
      a complete record from slave)
      
      Write_set equals the m_cols bitmap sent from master but it can be 
      longer if slave has extra columns. 
     */ 

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

6472 6473
    this->slave_exec_mode= slave_exec_mode_options; // fix the mode

6474 6475
    // Do event specific preparations 
    error= do_before_row_operations(rli);
6476

6477
    // row processing loop
6478

6479 6480
    while (error == 0 && m_curr_row < m_rows_end)
    {
6481 6482 6483 6484
      /* 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;
6485 6486 6487

      error= do_exec_row(rli);

6488 6489 6490 6491 6492
      table->in_use = old_thd;
      switch (error)
      {
      case 0:
	break;
6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505
      /*
        The following list of "idempotent" errors
        means that an error from the list might happen
        because of idempotent (more than once) 
        applying of a binlog file.
        Notice, that binlog has a  ddl operation its
        second applying may cause

        case HA_ERR_TABLE_DEF_CHANGED:
        case HA_ERR_CANNOT_ADD_FOREIGN:
        
        which are not included into to the list.
      */
6506
      case HA_ERR_RECORD_CHANGED:
6507 6508 6509
      case HA_ERR_RECORD_DELETED:
      case HA_ERR_KEY_NOT_FOUND:
      case HA_ERR_END_OF_FILE:
6510 6511 6512 6513 6514 6515
      case HA_ERR_FOUND_DUPP_KEY:
      case HA_ERR_FOUND_DUPP_UNIQUE:
      case HA_ERR_FOREIGN_DUPLICATE_KEY:
      case HA_ERR_NO_REFERENCED_ROW:
      case HA_ERR_ROW_IS_REFERENCED:

6516
        DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
6517 6518 6519 6520 6521 6522 6523 6524
        if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1)
        {
          if (global_system_variables.log_warnings)
            slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
                                    get_type_str(),
                                    RPL_LOG_NAME, (ulong) log_pos);
          error= 0;
        }
6525
        break;
6526
        
6527
      default:
6528
	thd->is_slave_error= 1;
6529 6530 6531
	break;
      }

6532 6533 6534 6535 6536 6537 6538
      /*
       If m_curr_row_end  was not set during event execution (e.g., because
       of errors) we can't proceed to the next row. If the error is transient
       (i.e., error==0 at this point) we must call unpack_current_row() to set 
       m_curr_row_end.
      */ 
   
6539 6540 6541 6542
      DBUG_PRINT("info", ("error: %d", error));
      DBUG_PRINT("info", ("curr_row: 0x%lu; curr_row_end: 0x%lu; rows_end: 0x%lu",
                          (ulong) m_curr_row, (ulong) m_curr_row_end, (ulong) m_rows_end));

6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554
      if (!m_curr_row_end && !error)
        unpack_current_row(rli);
  
      // at this moment m_curr_row_end should be set
      DBUG_ASSERT(error || m_curr_row_end != NULL); 
      DBUG_ASSERT(error || m_curr_row < m_curr_row_end);
      DBUG_ASSERT(error || m_curr_row_end <= m_rows_end);
  
      m_curr_row= m_curr_row_end;
 
    } // row processing loop

6555
    DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
6556
                    const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
6557
    error= do_after_row_operations(rli, error);
6558
    if (!cache_stmt)
6559 6560 6561 6562
    {
      DBUG_PRINT("info", ("Marked that we need to keep log"));
      thd->options|= OPTION_KEEP_LOG;
    }
6563
  } // if (table)
6564

6565
  /*
6566 6567
    We need to delay this clear until here bacause unpack_current_row() uses
    master-side table definitions stored in rli.
6568 6569
  */
  if (rli->tables_to_lock && get_flags(STMT_END_F))
6570
    const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
6571
  
6572 6573
  if (error)
  {                     /* error has occured during the transaction */
6574 6575 6576 6577 6578
    slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table,
                            get_type_str(), RPL_LOG_NAME, (ulong) log_pos);
  }
  if (error)
  {
6579
    /*
6580 6581 6582 6583 6584 6585 6586 6587
      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
6588
      rollback at the caller along with sbr.
6589
    */
unknown's avatar
unknown committed
6590
    thd->reset_current_stmt_binlog_row_based();
6591
    const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
6592
    thd->is_slave_error= 1;
6593 6594 6595
    DBUG_RETURN(error);
  }

6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617
  /*
    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
6618
      Relay_log_info::last_event_start_time and all its occurrences.
6619
    */
6620
    const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
6621 6622 6623 6624 6625
  }

  DBUG_RETURN(0);
}

6626
Log_event::enum_skip_reason
6627
Rows_log_event::do_shall_skip(Relay_log_info *rli)
6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639
{
  /*
    If the slave skip counter is 1 and this event does not end a
    statement, then we should not start executing on the next event.
    Otherwise, we defer the decision to the normal skipping logic.
  */
  if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
    return Log_event::EVENT_SKIP_IGNORE;
  else
    return Log_event::do_shall_skip(rli);
}

6640
int
6641
Rows_log_event::do_update_pos(Relay_log_info *rli)
6642 6643 6644 6645 6646 6647 6648
{
  DBUG_ENTER("Rows_log_event::do_update_pos");
  int error= 0;

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

6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666
  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);
6667

6668 6669 6670 6671 6672 6673 6674 6675 6676 6677
    /*
      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);
6678

6679 6680 6681 6682 6683 6684 6685 6686 6687 6688
    /*
      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
6689
    thd->reset_current_stmt_binlog_row_based();
6690

6691
    rli->cleanup_context(thd, 0);
6692 6693
    if (error == 0)
    {
6694 6695 6696 6697 6698 6699 6700
      /*
        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);

6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711
      /*
        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
6712 6713 6714
      rli->report(ERROR_LEVEL, error,
                  "Error in %s event: commit of row events failed, "
                  "table `%s`.`%s`",
6715 6716
                  get_type_str(), m_table->s->db.str,
                  m_table->s->table_name.str);
6717
  }
6718
  else
6719
  {
6720
    rli->inc_event_relay_log_pos();
6721 6722
  }

6723
  DBUG_RETURN(error);
6724
}
6725

6726 6727 6728 6729 6730
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
{
6731
  uchar buf[ROWS_HEADER_LEN];	// No need to init the buffer
6732
  DBUG_ASSERT(m_table_id != ~0UL);
6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749
  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.
  */
6750
  uchar sbuf[sizeof(m_width)];
6751
  my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
6752
  bool res= false;
6753 6754
  uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
  DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
6755

6756 6757
  DBUG_DUMP("m_width", sbuf, (size_t) (sbuf_end - sbuf));
  res= res || my_b_safe_write(file, sbuf, (size_t) (sbuf_end - sbuf));
6758

6759 6760
  DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
  res= res || my_b_safe_write(file, (uchar*) m_cols.bitmap,
6761 6762 6763 6764 6765 6766
                              no_bytes_in_map(&m_cols));
  /*
    TODO[refactor write]: Remove the "down cast" here (and elsewhere).
   */
  if (get_type_code() == UPDATE_ROWS_EVENT)
  {
6767 6768 6769
    DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
              no_bytes_in_map(&m_cols_ai));
    res= res || my_b_safe_write(file, (uchar*) m_cols_ai.bitmap,
6770 6771
                                no_bytes_in_map(&m_cols_ai));
  }
6772 6773
  DBUG_DUMP("rows", m_rows_buf, data_size);
  res= res || my_b_safe_write(file, m_rows_buf, (size_t) data_size);
6774 6775 6776

  return res;

6777 6778 6779
}
#endif

6780
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
6781 6782
void Rows_log_event::pack_info(Protocol *protocol)
{
6783 6784 6785
  char buf[256];
  char const *const flagstr=
    get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
6786
  size_t bytes= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
6787
                               "table_id: %lu%s", m_table_id, flagstr);
6788
  protocol->store(buf, bytes, &my_charset_bin);
6789 6790 6791
}
#endif

6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802
#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);
6803 6804 6805
    my_b_printf(head, "\t%s: table id %lu%s\n",
                name, m_table_id,
                last_stmt_event ? " flags: STMT_END_F" : "");
6806 6807 6808 6809 6810
    print_base64(body, print_event_info, !last_stmt_event);
  }

  if (get_flags(STMT_END_F))
  {
6811 6812
    copy_event_cache_to_file_and_reinit(head, file);
    copy_event_cache_to_file_and_reinit(body, file);
6813 6814 6815 6816
  }
}
#endif

6817
/**************************************************************************
6818
	Table_map_log_event member functions and support functions
6819 6820
**************************************************************************/

6821 6822 6823 6824
/**
  @page How replication of field metadata works.
  
  When a table map is created, the master first calls 
6825
  Table_map_log_event::save_field_metadata() which calculates how many 
6826
  values will be in the field metadata. Only those fields that require the 
6827 6828 6829 6830 6831 6832 6833
  extra data are added. The method also loops through all of the fields in 
  the table calling the method Field::save_field_metadata() which returns the
  values for the field that will be saved in the metadata and replicated to
  the slave. Once all fields have been processed, the table map is written to
  the binlog adding the size of the field metadata and the field metadata to
  the end of the body of the table map.

6834 6835 6836 6837 6838 6839 6840
  When a table map is read on the slave, the field metadata is read from the 
  table map and passed to the table_def class constructor which saves the 
  field metadata from the table map into an array based on the type of the 
  field. Field metadata values not present (those fields that do not use extra 
  data) in the table map are initialized as zero (0). The array size is the 
  same as the columns for the table on the slave.

6841 6842 6843 6844 6845 6846 6847 6848 6849 6850
  Additionally, values saved for field metadata on the master are saved as a 
  string of bytes (uchar) in the binlog. A field may require 1 or more bytes
  to store the information. In cases where values require multiple bytes 
  (e.g. values > 255), the endian-safe methods are used to properly encode 
  the values on the master and decode them on the slave. When the field
  metadata values are captured on the slave, they are stored in an array of
  type uint16. This allows the least number of casts to prevent casting bugs
  when the field metadata is used in comparisons of field attributes. When
  the field metadata is used for calculating addresses in pointer math, the
  type used is uint32. 
6851 6852 6853 6854 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
*/

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

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

6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900
/*
  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),
6901 6902 6903 6904 6905 6906 6907 6908 6909
    m_colcnt(tbl->s->fields),
    m_memory(NULL),
    m_table_id(tid),
    m_flags(flags),
    m_data_size(0),
    m_field_metadata(0),
    m_field_metadata_size(0),
    m_null_bits(0),
    m_meta_memory(NULL)
6910
{
6911
  DBUG_ASSERT(m_table_id != ~0UL);
6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923
  /*
    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
6924
  DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;);
6925 6926 6927 6928
  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

6929
  /* If malloc fails, caught in is_valid() */
6930
  if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
6931
  {
unknown's avatar
unknown committed
6932
    m_coltype= reinterpret_cast<uchar*>(m_memory);
6933 6934 6935
    for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
      m_coltype[i]= m_table->field[i]->type();
  }
6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946

  /*
    Calculate a bitmap for the results of maybe_null() for all columns.
    The bitmap is used to determine when there is a column from the master
    that is not on the slave and is null and thus not in the row data during
    replication.
  */
  uint num_null_bytes= (m_table->s->fields + 7) / 8;
  m_data_size+= num_null_bytes;
  m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
                                 &m_null_bits, num_null_bytes,
6947
                                 &m_field_metadata, (m_colcnt * 2),
6948
                                 NULL);
6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966

  bzero(m_field_metadata, (m_colcnt * 2));

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

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

6967 6968 6969 6970 6971
  bzero(m_null_bits, num_null_bytes);
  for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
    if (m_table->field[i]->maybe_null())
      m_null_bits[(i / 8)]+= 1 << (i % 8);

6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984
}
#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
6985
    m_table(NULL),
6986
#endif
6987 6988 6989 6990 6991
    m_dbnam(NULL), m_dblen(0), m_tblnam(NULL), m_tbllen(0),
    m_colcnt(0), m_coltype(0),
    m_memory(NULL), m_table_id(ULONG_MAX), m_flags(0),
    m_data_size(0), m_field_metadata(0), m_field_metadata_size(0),
    m_null_bits(0), m_meta_memory(NULL)
6992
{
6993
  unsigned int bytes_read= 0;
6994 6995 6996 6997
  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
6998
  DBUG_PRINT("info",("event_len: %u  common_header_len: %d  post_header_len: %d",
6999 7000
                     event_len, common_header_len, post_header_len));

7001 7002 7003 7004 7005
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
7006
  DBUG_DUMP("event buffer", (uchar*) buf, event_len);
7007
#endif
7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021

  /* 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);
7022
    m_table_id= (ulong) uint6korr(post_start);
7023 7024 7025
    post_start+= TM_FLAGS_OFFSET;
  }

7026
  DBUG_ASSERT(m_table_id != ~0UL);
7027 7028 7029 7030 7031 7032 7033

  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 */
7034
  uchar const *const ptr_dblen= (uchar const*)vpart + 0;
unknown's avatar
unknown committed
7035
  m_dblen= *(uchar*) ptr_dblen;
7036 7037

  /* Length of database name + counter + terminating null */
7038
  uchar const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
unknown's avatar
unknown committed
7039
  m_tbllen= *(uchar*) ptr_tbllen;
7040 7041

  /* Length of table name + counter + terminating null */
7042
  uchar const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
7043 7044
  uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
  m_colcnt= net_field_length(&ptr_after_colcnt);
7045

unknown's avatar
unknown committed
7046
  DBUG_PRINT("info",("m_dblen: %lu  off: %ld  m_tbllen: %lu  off: %ld  m_colcnt: %lu  off: %ld",
7047 7048
                     (ulong) m_dblen, (long) (ptr_dblen-(const uchar*)vpart), 
                     (ulong) m_tbllen, (long) (ptr_tbllen-(const uchar*)vpart),
7049
                     m_colcnt, (long) (ptr_colcnt-(const uchar*)vpart)));
7050

7051
  /* Allocate mem for all fields in one go. If fails, caught in is_valid() */
7052 7053 7054 7055 7056
  m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
                                     &m_dbnam, (uint) m_dblen + 1,
                                     &m_tblnam, (uint) m_tbllen + 1,
                                     &m_coltype, (uint) m_colcnt,
                                     NullS);
7057 7058 7059 7060

  if (m_memory)
  {
    /* Copy the different parts into their memory */
7061 7062
    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);
7063
    memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
7064 7065 7066 7067 7068 7069 7070

    ptr_after_colcnt= ptr_after_colcnt + m_colcnt;
    bytes_read= ptr_after_colcnt - (uchar *)buf;
    DBUG_PRINT("info", ("Bytes read: %d.\n", bytes_read));
    if (bytes_read < event_len)
    {
      m_field_metadata_size= net_field_length(&ptr_after_colcnt);
7071
      DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
7072 7073 7074 7075 7076 7077 7078 7079 7080
      uint num_null_bytes= (m_colcnt + 7) / 8;
      m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
                                     &m_null_bits, num_null_bytes,
                                     &m_field_metadata, m_field_metadata_size,
                                     NULL);
      memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
      ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
      memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
    }
7081 7082 7083 7084 7085 7086 7087 7088
  }

  DBUG_VOID_RETURN;
}
#endif

Table_map_log_event::~Table_map_log_event()
{
7089
  my_free(m_meta_memory, MYF(MY_ALLOW_ZERO_PTR));
7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104
  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)
7105
int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
7106
{
7107 7108 7109 7110
  RPL_TABLE_LIST *table_list;
  char *db_mem, *tname_mem;
  size_t dummy_len;
  void *memory;
7111
  DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
7112 7113 7114 7115 7116 7117 7118
  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);

7119 7120 7121 7122 7123
  if (!(memory= my_multi_malloc(MYF(MY_WME),
                                &table_list, (uint) sizeof(RPL_TABLE_LIST),
                                &db_mem, (uint) NAME_LEN + 1,
                                &tname_mem, (uint) NAME_LEN + 1,
                                NullS)))
7124
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
7125 7126 7127 7128 7129 7130 7131 7132 7133 7134

  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);
7135 7136 7137

  int error= 0;

7138 7139 7140
  if (!rpl_filter->db_ok(table_list->db) ||
      (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list)))
  {
7141
    my_free(memory, MYF(MY_WME));
7142 7143
  }
  else
7144
  {
7145 7146 7147 7148 7149
    /*
      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.
    */
7150 7151
    lex_start(thd);
    mysql_reset_thd_for_next_command(thd);
7152
    /*
unknown's avatar
unknown committed
7153 7154 7155
      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.
7156
    */
unknown's avatar
unknown committed
7157 7158
    if (!thd->current_stmt_binlog_row_based &&
        mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
7159
    {
unknown's avatar
unknown committed
7160
      thd->set_current_stmt_binlog_row_based();
7161 7162 7163
    }

    /*
7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179
      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.
7180 7181
    */
    uint count;
7182 7183 7184
    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)))
7185
    {
7186
      if (thd->is_slave_error || thd->is_fatal_error)
7187
      {
7188 7189 7190 7191
        /*
          Error reporting borrowed from Query_log_event with many excessive
          simplifications (we don't honour --slave-skip-errors)
        */
7192
        uint actual_error= thd->main_da.sql_errno();
7193 7194
        rli->report(ERROR_LEVEL, actual_error,
                    "Error '%s' on opening table `%s`.`%s`",
7195
                    (actual_error ? thd->main_da.message() :
7196 7197
                     "unexpected success or fatal error"),
                    table_list->db, table_list->table_name);
7198
        thd->is_slave_error= 1;
7199
      }
7200
      goto err;
7201 7202
    }

7203
    m_table= table_list->table;
7204 7205 7206 7207 7208 7209 7210

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

7211 7212 7213
    /*
      Use placement new to construct the table_def instance in the
      memory allocated for it inside table_list.
7214 7215 7216

      The memory allocated by the table_def structure (i.e., not the
      memory allocated *for* the table_def structure) is released
7217
      inside Relay_log_info::clear_tables_to_lock() by calling the
7218
      table_def destructor explicitly.
7219
    */
7220 7221
    new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt, 
         m_field_metadata, m_field_metadata_size, m_null_bits);
7222
    table_list->m_tabledef_valid= TRUE;
7223 7224

    /*
7225
      We record in the slave's information that the table should be
unknown's avatar
unknown committed
7226
      locked by linking the table into the list of tables to lock.
7227
    */
7228
    table_list->next_global= table_list->next_local= rli->tables_to_lock;
7229 7230
    const_cast<Relay_log_info*>(rli)->tables_to_lock= table_list;
    const_cast<Relay_log_info*>(rli)->tables_to_lock_count++;
7231
    /* 'memory' is freed in clear_tables_to_lock */
7232 7233
  }

7234
  DBUG_RETURN(error);
7235

7236
err:
7237
  my_free(memory, MYF(MY_WME));
7238 7239
  DBUG_RETURN(error);
}
7240

7241
Log_event::enum_skip_reason
7242
Table_map_log_event::do_shall_skip(Relay_log_info *rli)
7243 7244 7245 7246 7247
{
  /*
    If the slave skip counter is 1, then we should not start executing
    on the next event.
  */
7248
  return continue_group(rli);
7249 7250
}

7251
int Table_map_log_event::do_update_pos(Relay_log_info *rli)
7252 7253 7254 7255 7256
{
  rli->inc_event_relay_log_pos();
  return 0;
}

7257 7258 7259 7260 7261
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifndef MYSQL_CLIENT
bool Table_map_log_event::write_data_header(IO_CACHE *file)
{
7262
  DBUG_ASSERT(m_table_id != ~0UL);
7263
  uchar buf[TABLE_MAP_HEADER_LEN];
7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282
  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);

7283 7284
  uchar const dbuf[]= { (uchar) m_dblen };
  uchar const tbuf[]= { (uchar) m_tbllen };
7285

7286 7287 7288
  uchar cbuf[sizeof(m_colcnt)];
  uchar *const cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
  DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
7289

7290 7291 7292 7293
  /*
    Store the size of the field metadata.
  */
  uchar mbuf[sizeof(m_field_metadata_size)];
7294
  uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
7295

7296
  return (my_b_safe_write(file, dbuf,      sizeof(dbuf)) ||
7297
          my_b_safe_write(file, (const uchar*)m_dbnam,   m_dblen+1) ||
7298
          my_b_safe_write(file, tbuf,      sizeof(tbuf)) ||
7299 7300
          my_b_safe_write(file, (const uchar*)m_tblnam,  m_tbllen+1) ||
          my_b_safe_write(file, cbuf, (size_t) (cbuf_end - cbuf)) ||
7301 7302 7303 7304
          my_b_safe_write(file, m_coltype, m_colcnt) ||
          my_b_safe_write(file, mbuf, (size_t) (mbuf_end - mbuf)) ||
          my_b_safe_write(file, m_field_metadata, m_field_metadata_size),
          my_b_safe_write(file, m_null_bits, (m_colcnt + 7) / 8));
7305 7306 7307 7308 7309 7310 7311 7312 7313 7314
 }
#endif

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

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

7315
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
7316 7317 7318
void Table_map_log_event::pack_info(Protocol *protocol)
{
    char buf[256];
7319
    size_t bytes= my_snprintf(buf, sizeof(buf),
unknown's avatar
unknown committed
7320
                                 "table_id: %lu (%s.%s)",
7321
                              m_table_id, m_dbnam, m_tblnam);
7322 7323
    protocol->store(buf, bytes, &my_charset_bin);
}
7324 7325
#endif

7326 7327 7328 7329 7330 7331 7332 7333 7334

#endif


#ifdef MYSQL_CLIENT
void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
{
  if (!print_event_info->short_form)
  {
7335 7336 7337 7338 7339
    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);
7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373
  }
}
#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)
7374 7375
int 
Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
7376 7377 7378
{
  int error= 0;

7379 7380 7381
  /**
     todo: to introduce a property for the event (handler?) which forces
     applying the event in the replace (idempotent) fashion.
7382
  */
7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421
  if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 ||
      m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
  {
    /*
      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;
    /* 
       Do not raise the error flag in case of hitting to an unique attribute
    */
    m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
    /* 
       NDB specific: update from ndb master wrapped as Write_rows
       so that the event should be applied to replace slave's row
    */
    m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
    /* 
       NDB specific: if update from ndb master wrapped as Write_rows
       does not find the row it's assumed idempotent binlog applying
       is taking place; don't raise the error.
    */
    m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
    /*
      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.
    */
  }
7422

7423
  m_table->file->ha_start_bulk_insert(0);
7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438
  /*
    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).
  */
7439
  m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
7440 7441 7442
  return error;
}

7443
int 
7444
Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
7445
                                              int error)
7446
{
7447
  int local_error= 0;
7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460
  if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 ||
      m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
  {
    m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
    m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
    /*
      resetting the extra with 
      table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY); 
      fires bug#27077
      explanation: file->reset() performs this duty
      ultimately. Still todo: fix
    */
  }
7461
  if ((local_error= m_table->file->ha_end_bulk_insert()))
7462
  {
7463
    m_table->file->print_error(local_error, MYF(0));
7464 7465
  }
  return error? error : local_error;
7466 7467
}

7468
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481

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

7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506
/**
   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;
}

7507 7508
/**
  Write the current row into event's table.
7509

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

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

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

7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542
  @param  rli   Relay log info (needed for row unpacking).
  @param  overwrite  
                Shall we overwrite if the row already exists or signal 
                error (currently ignored).

  @returns Error code on failure, 0 on success.

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

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

int
7543
Rows_log_event::write_row(const Relay_log_info *const rli,
7544
                          const bool overwrite)
7545
{
7546 7547
  DBUG_ENTER("write_row");
  DBUG_ASSERT(m_table != NULL && thd != NULL);
7548

7549
  TABLE *table= m_table;  // pointer to event's table
7550 7551 7552 7553
  int error;
  int keynum;
  auto_afree_ptr<char> key(NULL);

7554 7555 7556 7557 7558 7559 7560 7561 7562
  /* fill table->record[0] with default values */

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

7563
#ifndef DBUG_OFF
7564
  DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
7565 7566 7567 7568
  DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
  DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif

7569 7570 7571 7572 7573 7574 7575 7576
  /* 
    Try to write record. If a corresponding record already exists in the table,
    we try to change it using ha_update_row() if possible. Otherwise we delete
    it and repeat the whole process again. 

    TODO: Add safety measures against infinite looping. 
   */

7577 7578
  while ((error= table->file->ha_write_row(table->record[0])))
  {
7579 7580 7581 7582
    if (error == HA_ERR_LOCK_DEADLOCK ||
        error == HA_ERR_LOCK_WAIT_TIMEOUT ||
        (keynum= table->file->get_dup_key(error)) < 0 ||
        !overwrite)
7583
    {
7584
      DBUG_PRINT("info",("get_dup_key returns %d)", keynum));
7585
      /*
7586 7587 7588
        Deadlock, waiting for lock or just an error from the handler
        such as HA_ERR_FOUND_DUPP_KEY when overwrite is false.
        Retrieval of the duplicate key number may fail
7589 7590 7591
        - either because the error was not "duplicate key" error
        - or because the information which key is not available
      */
7592
      table->file->print_error(error, MYF(0));
7593
      DBUG_RETURN(error);
7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604
    }
    /*
       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.
     */
7605
    if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
7606
    {
7607
      DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
7608
      error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
7609
      if (error)
7610
      {
7611
        DBUG_PRINT("info",("rnd_pos() returns error %d",error));
7612
        table->file->print_error(error, MYF(0));
7613
        DBUG_RETURN(error);
7614
      }
7615 7616 7617
    }
    else
    {
7618 7619
      DBUG_PRINT("info",("Locating offending record using index_read_idx()"));

7620 7621
      if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
      {
7622
        DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
7623
        DBUG_RETURN(my_errno);
7624 7625 7626 7627 7628 7629
      }

      if (key.get() == NULL)
      {
        key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
        if (key.get() == NULL)
7630 7631
        {
          DBUG_PRINT("info",("Can't allocate key buffer"));
7632
          DBUG_RETURN(ENOMEM);
7633
        }
7634 7635
      }

7636 7637
      key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
               0);
7638 7639 7640 7641
      error= table->file->index_read_idx_map(table->record[1], keynum,
                                             (const uchar*)key.get(),
                                             HA_WHOLE_KEY,
                                             HA_READ_KEY_EXACT);
7642
      if (error)
7643
      {
7644
        DBUG_PRINT("info",("index_read_idx() returns error %d",error)); 
7645
        table->file->print_error(error, MYF(0));
7646
        DBUG_RETURN(error);
7647
      }
7648 7649 7650
    }

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

7656 7657 7658
    /*
      If row is incomplete we will use the record found to fill 
      missing columns.  
7659
    */
7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670
    if (!get_flags(COMPLETE_ROWS_F))
    {
      restore_record(table,record[1]);
      error= unpack_current_row(rli);
    }

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

7672
    /*
7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689
       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())
    {
7690
      DBUG_PRINT("info",("Updating row using ha_update_row()"));
7691 7692
      error=table->file->ha_update_row(table->record[1],
                                       table->record[0]);
7693 7694 7695 7696 7697
      switch (error) {
                
      case HA_ERR_RECORD_IS_THE_SAME:
        DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
                           " ha_update_row()"));
7698
        error= 0;
7699 7700 7701 7702 7703 7704 7705 7706 7707
      
      case 0:
        break;
        
      default:    
        DBUG_PRINT("info",("ha_update_row() returns error %d",error));
        table->file->print_error(error, MYF(0));
      }
      
7708
      DBUG_RETURN(error);
7709 7710 7711
    }
    else
    {
7712
      DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
7713
      if ((error= table->file->ha_delete_row(table->record[1])))
7714
      {
7715
        DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
7716
        table->file->print_error(error, MYF(0));
7717
        DBUG_RETURN(error);
7718
      }
7719 7720 7721
      /* Will retry ha_write_row() with the offending row removed. */
    }
  }
7722

7723
  DBUG_RETURN(error);
7724 7725
}

7726 7727 7728
#endif

int 
7729
Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
7730
{
7731
  DBUG_ASSERT(m_table != NULL);
7732 7733 7734 7735
  int error=
    write_row(rli,        /* if 1 then overwrite */
              bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1);
    
7736 7737 7738 7739 7740
  if (error && !thd->is_error())
  {
    DBUG_ASSERT(0);
    my_error(ER_UNKNOWN_ERROR, MYF(0));
  }
7741
  
7742
  return error; 
7743
}
7744

7745 7746 7747 7748 7749
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
{
7750
  Rows_log_event::print_helper(file, print_event_info, "Write_rows");
7751 7752 7753 7754 7755 7756 7757 7758
}
#endif

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

#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
7759 7760 7761 7762 7763 7764
/*
  Compares table->record[0] and table->record[1]

  Returns TRUE if different.
*/
static bool record_compare(TABLE *table)
7765
{
7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777
  /*
    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.
   */

7778 7779 7780
  DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
  DBUG_DUMP("record[1]", table->record[1], table->s->reclength);

7781
  bool result= FALSE;
7782
  uchar saved_x[2], saved_filler[2];
7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795

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

7796
  if (table->s->blob_fields + table->s->varchar_fields == 0)
7797 7798 7799 7800 7801
  {
    result= cmp_record(table,record[1]);
    goto record_compare_exit;
  }

7802 7803 7804 7805
  /* Compare null bits */
  if (memcmp(table->null_flags,
	     table->null_flags+table->s->rec_buff_length,
	     table->s->null_bytes))
7806 7807 7808 7809 7810
  {
    result= TRUE;				// Diff in NULL value
    goto record_compare_exit;
  }

7811 7812
  /* Compare updated fields */
  for (Field **ptr=table->field ; *ptr ; ptr++)
7813
  {
7814
    if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
7815 7816 7817 7818
    {
      result= TRUE;
      goto record_compare_exit;
    }
7819
  }
7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834

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];
    }
7835
  }
7836 7837

  return result;
7838 7839
}

7840 7841
/**
  Locate the current row in event's table.
7842

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

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

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

7858 7859 7860 7861
  @returns Error code on failure, 0 on success. 
  
  @post In case of success @c m_table->record[0] contains the record found. 
  Also, the internal "cursor" of the table is positioned at the record found.
7862

7863 7864
  @note If the engine allows random access of the records, a combination of
  @c position() and @c rnd_pos() will be used. 
7865
 */
7866

7867
int Rows_log_event::find_row(const Relay_log_info *rli)
7868
{
7869
  DBUG_ENTER("Rows_log_event::find_row");
7870 7871

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

7873 7874
  TABLE *table= m_table;
  int error;
7875

7876
  /* unpack row - missing fields get default values */
7877

7878 7879 7880 7881 7882 7883
  // TODO: shall we check and report errors here?
  prepare_record(NULL,table,m_width,FALSE /* don't check errors */); 
  error= unpack_current_row(rli); 

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

7887 7888
  if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
      table->s->primary_key < MAX_KEY)
7889 7890 7891 7892 7893 7894
  {
    /*
      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
7895
      the "cursor" (i.e., record[0] in this case) at the correct row.
7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906

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

7907
    */
7908
    DBUG_PRINT("info",("locating record using primary key (position)"));
7909
    int error= table->file->rnd_pos_by_record(table->record[0]);
7910 7911 7912 7913 7914
    if (error)
    {
      DBUG_PRINT("info",("rnd_pos returns error %d",error));
      table->file->print_error(error, MYF(0));
    }
7915
    DBUG_RETURN(error);
7916 7917
  }

7918
  // We can't use position() - try other methods.
7919 7920 7921 7922 7923
  
  /* 
    We need to retrieve all fields
    TODO: Move this out from this function to main loop 
   */
7924
  table->use_all_columns();
unknown's avatar
unknown committed
7925

7926 7927 7928 7929 7930 7931
  /*
    Save copy of the record in table->record[1]. It might be needed 
    later if linear search is used to find exact match.
   */ 
  store_record(table,record[1]);    

7932 7933
  if (table->s->keys > 0)
  {
7934 7935
    DBUG_PRINT("info",("locating record using primary key (index_read)"));

7936
    /* We have a key: search the table using the index */
7937
    if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
7938 7939 7940
    {
      DBUG_PRINT("info",("ha_index_init returns error %d",error));
      table->file->print_error(error, MYF(0));
7941
      DBUG_RETURN(error);
7942
    }
7943

7944 7945 7946 7947 7948 7949 7950 7951 7952
    /* Fill key data for the row */

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

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

7957 7958 7959 7960 7961 7962
    /*
      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.
    */
7963 7964
    my_ptrdiff_t const pos=
      table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
7965 7966 7967
    table->record[0][pos]= 0xFF;
    
    if ((error= table->file->index_read_map(table->record[0], m_key, 
7968
                                            HA_WHOLE_KEY,
7969
                                            HA_READ_KEY_EXACT)))
7970
    {
7971
      DBUG_PRINT("info",("no record matching the key found in the table"));
7972
      table->file->print_error(error, MYF(0));
7973
      table->file->ha_index_end();
7974 7975 7976
      DBUG_RETURN(error);
    }

7977 7978 7979 7980 7981
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
#ifndef HAVE_purify
7982 7983
    DBUG_PRINT("info",("found first matching record")); 
    DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
7984
#endif
7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997
    /*
      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.
7998
    */
7999
    if (table->key_info->flags & HA_NOSAME)
8000 8001
    {
      table->file->ha_index_end();
8002
      DBUG_RETURN(0);
8003
    }
8004

8005 8006
    /*
      In case key is not unique, we still have to iterate over records found
8007 8008
      and find the one which is identical to the row given. A copy of the 
      record we are looking for is stored in record[1].
8009 8010 8011
     */ 
    DBUG_PRINT("info",("non-unique index, scanning it to find matching record")); 

8012
    while (record_compare(table))
8013
    {
8014 8015 8016 8017 8018
      /*
        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.
8019 8020 8021

        TODO[record format ndb]: Remove this code once NDB returns the
        correct record format.
8022
      */
8023 8024
      if (table->s->null_bytes > 0)
      {
8025
        table->record[0][table->s->null_bytes - 1]|=
8026 8027 8028
          256U - (1U << table->s->last_null_bit_pos);
      }

8029
      if ((error= table->file->index_next(table->record[0])))
8030
      {
8031 8032
        DBUG_PRINT("info",("no record matching the given row found"));
        table->file->print_error(error, MYF(0));
8033
        table->file->ha_index_end();
8034
        DBUG_RETURN(error);
8035 8036
      }
    }
8037 8038 8039 8040 8041

    /*
      Have to restart the scan to be able to fetch the next row.
    */
    table->file->ha_index_end();
8042 8043 8044
  }
  else
  {
8045 8046
    DBUG_PRINT("info",("locating record using table scan (rnd_next)"));

8047
    int restart_count= 0; // Number of times scanning has restarted from top
8048 8049 8050

    /* We don't have a key: search the table using rnd_next() */
    if ((error= table->file->ha_rnd_init(1)))
8051 8052 8053 8054 8055 8056
    {
      DBUG_PRINT("info",("error initializing table scan"
                         " (ha_rnd_init returns %d)",error));
      table->file->print_error(error, MYF(0));
      DBUG_RETURN(error);
    }
8057 8058

    /* Continue until we find the right record or have made a full loop */
8059 8060
    do
    {
8061
      error= table->file->rnd_next(table->record[0]);
8062

8063
      switch (error) {
8064

8065 8066
      case 0:
      case HA_ERR_RECORD_DELETED:
8067
        break;
8068 8069

      case HA_ERR_END_OF_FILE:
8070 8071 8072
        if (++restart_count < 2)
          table->file->ha_rnd_init(1);
        break;
8073 8074

      default:
8075 8076 8077
        DBUG_PRINT("info", ("Failed to get next record"
                            " (rnd_next returns %d)",error));
        table->file->print_error(error, MYF(0));
8078
        table->file->ha_rnd_end();
8079
        DBUG_RETURN(error);
8080 8081
      }
    }
8082
    while (restart_count < 2 && record_compare(table));
8083 8084 8085 8086 8087
    
    /* 
      Note: above record_compare will take into accout all record fields 
      which might be incorrect in case a partial row was given in the event
     */
8088

8089 8090 8091
    /*
      Have to restart the scan to be able to fetch the next row.
    */
8092 8093
    if (restart_count == 2)
      DBUG_PRINT("info", ("Record not found"));
8094 8095
    else
      DBUG_DUMP("record found", table->record[0], table->s->reclength);
8096 8097
    table->file->ha_rnd_end();

8098
    DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == HA_ERR_RECORD_DELETED || error == 0);
8099 8100 8101 8102 8103
    DBUG_RETURN(error);
  }

  DBUG_RETURN(0);
}
8104

8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133
#endif

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

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

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

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

8134 8135 8136 8137 8138
int 
Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
  if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
      m_table->s->primary_key < MAX_KEY)
8139 8140
  {
    /*
8141
      We don't need to allocate any memory for m_key since it is not used.
8142 8143 8144 8145
    */
    return 0;
  }

8146
  if (m_table->s->keys > 0)
8147
  {
8148 8149 8150 8151
    // Allocate buffer for key searches
    m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
    if (!m_key)
      return HA_ERR_OUT_OF_MEM;
8152
  }
8153
  return 0;
8154 8155
}

8156 8157 8158
int 
Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, 
                                               int error)
8159 8160
{
  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
8161 8162
  m_table->file->ha_index_or_rnd_end();
  my_free(m_key, MYF(MY_ALLOW_ZERO_PTR));
8163
  m_key= NULL;
8164 8165 8166 8167

  return error;
}

8168
int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
8169
{
8170
  int error;
8171
  DBUG_ASSERT(m_table != NULL);
8172

8173
  if (!(error= find_row(rli))) 
8174 8175
  { 
    /*
8176
      Delete the record found, located in record[0]
8177
    */
8178
    error= m_table->file->ha_delete_row(m_table->record[0]);
8179
  }
8180 8181 8182 8183 8184 8185 8186 8187 8188
  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)
{
8189
  Rows_log_event::print_helper(file, print_event_info, "Delete_rows");
8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202
}
#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,
8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214
                                             ulong tid,
                                             MY_BITMAP const *cols_bi,
                                             MY_BITMAP const *cols_ai,
                                             bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional)
{
  init(cols_ai);
}

Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
                                             ulong tid,
                                             MY_BITMAP const *cols,
8215 8216 8217
                                             bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
{
8218 8219 8220 8221 8222
  init(cols);
}

void Update_rows_log_event::init(MY_BITMAP const *cols)
{
8223
  /* if bitmap_init fails, caught in is_valid() */
8224 8225
  if (likely(!bitmap_init(&m_cols_ai,
                          m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
8226
                          m_width,
8227 8228 8229 8230
                          false)))
  {
    /* Cols can be zero if this is a dummy binrows event */
    if (likely(cols != NULL))
8231
    {
8232
      memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols));
8233 8234
      create_last_word_mask(&m_cols_ai);
    }
8235
  }
8236 8237 8238
}
#endif /* !defined(MYSQL_CLIENT) */

8239 8240 8241 8242 8243 8244 8245 8246 8247

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


8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262
/*
  Constructor used by slave to read the event from the binary log.
 */
#ifdef HAVE_REPLICATION
Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
                                             const
                                             Format_description_log_event
                                             *description_event)
  : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
{
}
#endif

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

8263 8264 8265 8266
int 
Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
  if (m_table->s->keys > 0)
8267
  {
8268 8269 8270 8271
    // Allocate buffer for key searches
    m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
    if (!m_key)
      return HA_ERR_OUT_OF_MEM;
8272 8273
  }

8274
  m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
8275

8276
  return 0;
8277 8278
}

8279 8280 8281
int 
Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, 
                                               int error)
8282
{
8283 8284 8285
  /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
  m_table->file->ha_index_or_rnd_end();
  my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
8286
  m_key= NULL;
8287 8288 8289 8290

  return error;
}

8291
int 
8292
Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
8293
{
8294
  DBUG_ASSERT(m_table != NULL);
8295

8296 8297
  int error= find_row(rli); 
  if (error)
8298 8299 8300 8301 8302 8303 8304
  {
    /*
      We need to read the second image in the event of error to be
      able to skip to the next pair of updates
    */
    m_curr_row= m_curr_row_end;
    unpack_current_row(rli);
8305
    return error;
8306
  }
8307

8308
  /*
8309
    This is the situation after locating BI:
8310

8311 8312 8313
    ===|=== before image ====|=== after image ===|===
       ^                     ^
       m_curr_row            m_curr_row_end
8314

8315 8316 8317
    BI found in the table is stored in record[0]. We copy it to record[1]
    and unpack AI to record[0].
   */
8318

8319
  store_record(m_table,record[1]);
8320

8321 8322
  m_curr_row= m_curr_row_end;
  error= unpack_current_row(rli); // this also updates m_curr_row_end
8323

8324
  /*
8325
    Now we have the right row to update.  The old row (the one we're
8326
    looking for) is in record[1] and the new row is in record[0].
8327
  */
8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338
#ifndef HAVE_purify
  /*
    Don't print debug messages when running valgrind since they can
    trigger false warnings.
   */
  DBUG_PRINT("info",("Updating row in table"));
  DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
  DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
#endif

  error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
8339 8340
  if (error == HA_ERR_RECORD_IS_THE_SAME)
    error= 0;
8341 8342 8343

  return error;
}
8344

8345 8346 8347 8348 8349 8350
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */

#ifdef MYSQL_CLIENT
void Update_rows_log_event::print(FILE *file,
				  PRINT_EVENT_INFO* print_event_info)
{
8351
  Rows_log_event::print_helper(file, print_event_info, "Update_rows");
8352 8353 8354
}
#endif

8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371

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
8372 8373
  uint8 len= 0;                   // Assignment to keep compiler happy
  const char *str= NULL;          // Assignment to keep compiler happy
8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397
  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);
8398
  DBUG_ASSERT((size_t) m_incident <= sizeof(description)/sizeof(*description));
8399 8400 8401 8402 8403 8404 8405 8406 8407

  return description[m_incident];
}


#ifndef MYSQL_CLIENT
void Incident_log_event::pack_info(Protocol *protocol)
{
  char buf[256];
8408
  size_t bytes;
8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435
  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
8436
Incident_log_event::do_apply_event(Relay_log_info const *rli)
8437
{
8438
  DBUG_ENTER("Incident_log_event::do_apply_event");
8439 8440 8441 8442
  rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT,
              ER(ER_SLAVE_INCIDENT),
              description(),
              m_message.length > 0 ? m_message.str : "<none>");
8443 8444 8445 8446 8447 8448 8449 8450 8451
  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));
8452
  uchar buf[sizeof(int16)];
8453 8454 8455 8456 8457 8458 8459 8460 8461 8462
  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));
}