~ubuntu-branches/ubuntu/trusty/mariadb-5.5/trusty-proposed

« back to all changes in this revision

Viewing changes to sql/log.cc

  • Committer: Package Import Robot
  • Author(s): Otto Kekäläinen
  • Date: 2013-12-22 10:27:05 UTC
  • Revision ID: package-import@ubuntu.com-20131222102705-mndw7s12mz0szrcn
Tags: upstream-5.5.32
Import upstream version 5.5.32

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
 
2
   Copyright (c) 2009, 2013, Monty Program Ab
 
3
 
 
4
   This program is free software; you can redistribute it and/or modify
 
5
   it under the terms of the GNU General Public License as published by
 
6
   the Free Software Foundation; version 2 of the License.
 
7
 
 
8
   This program is distributed in the hope that it will be useful,
 
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
   GNU General Public License for more details.
 
12
 
 
13
   You should have received a copy of the GNU General Public License
 
14
   along with this program; if not, write to the Free Software
 
15
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
 
16
 
 
17
 
 
18
/**
 
19
  @file
 
20
 
 
21
  @brief
 
22
  logging of commands
 
23
 
 
24
  @todo
 
25
    Abort logging when we get an error in reading or writing log files
 
26
*/
 
27
 
 
28
#include "my_global.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
 
29
#include "sql_priv.h"
 
30
#include "log.h"
 
31
#include "sql_base.h"                           // open_log_table
 
32
#include "sql_repl.h"
 
33
#include "sql_delete.h"                         // mysql_truncate
 
34
#include "sql_parse.h"                          // command_name
 
35
#include "sql_time.h"           // calc_time_from_sec, my_time_compare
 
36
#include "tztime.h"             // my_tz_OFFSET0, struct Time_zone
 
37
#include "sql_acl.h"            // SUPER_ACL
 
38
#include "log_event.h"          // Query_log_event
 
39
#include "rpl_filter.h"
 
40
#include "rpl_rli.h"
 
41
#include "sql_audit.h"
 
42
#include "log_slow.h"
 
43
 
 
44
#include <my_dir.h>
 
45
#include <stdarg.h>
 
46
#include <m_ctype.h>                            // For test_if_number
 
47
 
 
48
#ifdef _WIN32
 
49
#include "message.h"
 
50
#endif
 
51
 
 
52
#include "sql_plugin.h"
 
53
#include "rpl_handler.h"
 
54
#include "debug_sync.h"
 
55
#include "sql_show.h"
 
56
 
 
57
/* max size of the log message */
 
58
#define MAX_LOG_BUFFER_SIZE 1024
 
59
#define MAX_TIME_SIZE 32
 
60
#define MY_OFF_T_UNDEF (~(my_off_t)0UL)
 
61
 
 
62
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
 
63
 
 
64
LOGGER logger;
 
65
 
 
66
MYSQL_BIN_LOG mysql_bin_log(&sync_binlog_period);
 
67
 
 
68
static bool test_if_number(const char *str,
 
69
                           ulong *res, bool allow_wildcards);
 
70
static int binlog_init(void *p);
 
71
static int binlog_close_connection(handlerton *hton, THD *thd);
 
72
static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
 
73
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
 
74
static int binlog_commit(handlerton *hton, THD *thd, bool all);
 
75
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
 
76
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
 
77
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
 
78
 
 
79
static LEX_STRING const write_error_msg=
 
80
    { C_STRING_WITH_LEN("error writing to the binary log") };
 
81
 
 
82
static my_bool opt_optimize_thread_scheduling= TRUE;
 
83
ulong binlog_checksum_options;
 
84
#ifndef DBUG_OFF
 
85
ulong opt_binlog_dbug_fsync_sleep= 0;
 
86
#endif
 
87
 
 
88
mysql_mutex_t LOCK_prepare_ordered;
 
89
mysql_mutex_t LOCK_commit_ordered;
 
90
 
 
91
static ulonglong binlog_status_var_num_commits;
 
92
static ulonglong binlog_status_var_num_group_commits;
 
93
static char binlog_snapshot_file[FN_REFLEN];
 
94
static ulonglong binlog_snapshot_position;
 
95
 
 
96
static SHOW_VAR binlog_status_vars_detail[]=
 
97
{
 
98
  {"commits",
 
99
    (char *)&binlog_status_var_num_commits, SHOW_LONGLONG},
 
100
  {"group_commits",
 
101
    (char *)&binlog_status_var_num_group_commits, SHOW_LONGLONG},
 
102
  {"snapshot_file",
 
103
    (char *)&binlog_snapshot_file, SHOW_CHAR},
 
104
  {"snapshot_position",
 
105
   (char *)&binlog_snapshot_position, SHOW_LONGLONG},
 
106
  {NullS, NullS, SHOW_LONG}
 
107
};
 
108
 
 
109
 
 
110
/**
 
111
   purge logs, master and slave sides both, related error code
 
112
   convertor.
 
113
   Called from @c purge_error_message(), @c MYSQL_BIN_LOG::reset_logs()
 
114
 
 
115
   @param  res  an internal to purging routines error code 
 
116
 
 
117
   @return the user level error code ER_*
 
118
*/
 
119
uint purge_log_get_error_code(int res)
 
120
{
 
121
  uint errcode= 0;
 
122
 
 
123
  switch (res)  {
 
124
  case 0: break;
 
125
  case LOG_INFO_EOF:    errcode= ER_UNKNOWN_TARGET_BINLOG; break;
 
126
  case LOG_INFO_IO:     errcode= ER_IO_ERR_LOG_INDEX_READ; break;
 
127
  case LOG_INFO_INVALID:errcode= ER_BINLOG_PURGE_PROHIBITED; break;
 
128
  case LOG_INFO_SEEK:   errcode= ER_FSEEK_FAIL; break;
 
129
  case LOG_INFO_MEM:    errcode= ER_OUT_OF_RESOURCES; break;
 
130
  case LOG_INFO_FATAL:  errcode= ER_BINLOG_PURGE_FATAL_ERR; break;
 
131
  case LOG_INFO_IN_USE: errcode= ER_LOG_IN_USE; break;
 
132
  case LOG_INFO_EMFILE: errcode= ER_BINLOG_PURGE_EMFILE; break;
 
133
  default:              errcode= ER_LOG_PURGE_UNKNOWN_ERR; break;
 
134
  }
 
135
 
 
136
  return errcode;
 
137
}
 
138
 
 
139
/**
 
140
  Silence all errors and warnings reported when performing a write
 
141
  to a log table.
 
142
  Errors and warnings are not reported to the client or SQL exception
 
143
  handlers, so that the presence of logging does not interfere and affect
 
144
  the logic of an application.
 
145
*/
 
146
class Silence_log_table_errors : public Internal_error_handler
 
147
{
 
148
  char m_message[MYSQL_ERRMSG_SIZE];
 
149
public:
 
150
  Silence_log_table_errors()
 
151
  {
 
152
    m_message[0]= '\0';
 
153
  }
 
154
 
 
155
  virtual ~Silence_log_table_errors() {}
 
156
 
 
157
  virtual bool handle_condition(THD *thd,
 
158
                                uint sql_errno,
 
159
                                const char* sql_state,
 
160
                                MYSQL_ERROR::enum_warning_level level,
 
161
                                const char* msg,
 
162
                                MYSQL_ERROR ** cond_hdl);
 
163
  const char *message() const { return m_message; }
 
164
};
 
165
 
 
166
bool
 
167
Silence_log_table_errors::handle_condition(THD *,
 
168
                                           uint,
 
169
                                           const char*,
 
170
                                           MYSQL_ERROR::enum_warning_level,
 
171
                                           const char* msg,
 
172
                                           MYSQL_ERROR ** cond_hdl)
 
173
{
 
174
  *cond_hdl= NULL;
 
175
  strmake_buf(m_message, msg);
 
176
  return TRUE;
 
177
}
 
178
 
 
179
sql_print_message_func sql_print_message_handlers[3] =
 
180
{
 
181
  sql_print_information,
 
182
  sql_print_warning,
 
183
  sql_print_error
 
184
};
 
185
 
 
186
 
 
187
/**
 
188
  Create the name of the log file
 
189
  
 
190
  @param[OUT] out    a pointer to a new allocated name will go there
 
191
  @param[IN] log_ext The extension for the file (e.g .log)
 
192
  @param[IN] once    whether to use malloc_once or a normal malloc.
 
193
*/
 
194
void make_default_log_name(char **out, const char* log_ext, bool once)
 
195
{
 
196
  char buff[FN_REFLEN+10];
 
197
  fn_format(buff, opt_log_basename, "", log_ext, MYF(MY_REPLACE_EXT));
 
198
  if (once)
 
199
    *out= my_once_strdup(buff, MYF(MY_WME));
 
200
  else
 
201
  {
 
202
    my_free(*out);
 
203
    *out= my_strdup(buff, MYF(MY_WME));
 
204
  }
 
205
}
 
206
 
 
207
 
 
208
/*
 
209
  Helper classes to store non-transactional and transactional data
 
210
  before copying it to the binary log.
 
211
*/
 
212
class binlog_cache_data
 
213
{
 
214
public:
 
215
  binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF),
 
216
  incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE),
 
217
  saved_max_binlog_cache_size(0), ptr_binlog_cache_use(0),
 
218
  ptr_binlog_cache_disk_use(0)
 
219
  { }
 
220
  
 
221
  ~binlog_cache_data()
 
222
  {
 
223
    DBUG_ASSERT(empty());
 
224
    close_cached_file(&cache_log);
 
225
  }
 
226
 
 
227
  bool empty() const
 
228
  {
 
229
    return pending() == NULL && my_b_tell(&cache_log) == 0;
 
230
  }
 
231
 
 
232
  Rows_log_event *pending() const
 
233
  {
 
234
    return m_pending;
 
235
  }
 
236
 
 
237
  void set_pending(Rows_log_event *const pending)
 
238
  {
 
239
    m_pending= pending;
 
240
  }
 
241
 
 
242
  void set_incident(void)
 
243
  {
 
244
    incident= TRUE;
 
245
  }
 
246
  
 
247
  bool has_incident(void)
 
248
  {
 
249
    return(incident);
 
250
  }
 
251
 
 
252
  void set_changes_to_non_trans_temp_table()
 
253
  {
 
254
    changes_to_non_trans_temp_table_flag= TRUE;    
 
255
  }
 
256
 
 
257
  bool changes_to_non_trans_temp_table()
 
258
  {
 
259
    return (changes_to_non_trans_temp_table_flag);    
 
260
  }
 
261
 
 
262
  void reset()
 
263
  {
 
264
    compute_statistics();
 
265
    truncate(0);
 
266
    changes_to_non_trans_temp_table_flag= FALSE;
 
267
    incident= FALSE;
 
268
    before_stmt_pos= MY_OFF_T_UNDEF;
 
269
    /*
 
270
      The truncate function calls reinit_io_cache that calls my_b_flush_io_cache
 
271
      which may increase disk_writes. This breaks the disk_writes use by the
 
272
      binary log which aims to compute the ratio between in-memory cache usage
 
273
      and disk cache usage. To avoid this undesirable behavior, we reset the
 
274
      variable after truncating the cache.
 
275
    */
 
276
    cache_log.disk_writes= 0;
 
277
    DBUG_ASSERT(empty());
 
278
  }
 
279
 
 
280
  my_off_t get_byte_position() const
 
281
  {
 
282
    return my_b_tell(&cache_log);
 
283
  }
 
284
 
 
285
  my_off_t get_prev_position()
 
286
  {
 
287
     return(before_stmt_pos);
 
288
  }
 
289
 
 
290
  void set_prev_position(my_off_t pos)
 
291
  {
 
292
     before_stmt_pos= pos;
 
293
  }
 
294
  
 
295
  void restore_prev_position()
 
296
  {
 
297
    truncate(before_stmt_pos);
 
298
  }
 
299
 
 
300
  void restore_savepoint(my_off_t pos)
 
301
  {
 
302
    truncate(pos);
 
303
    if (pos < before_stmt_pos)
 
304
      before_stmt_pos= MY_OFF_T_UNDEF;
 
305
  }
 
306
 
 
307
  void set_binlog_cache_info(my_off_t param_max_binlog_cache_size,
 
308
                             ulong *param_ptr_binlog_cache_use,
 
309
                             ulong *param_ptr_binlog_cache_disk_use)
 
310
  {
 
311
    /*
 
312
      The assertions guarantee that the set_binlog_cache_info is
 
313
      called just once and information passed as parameters are
 
314
      never zero.
 
315
 
 
316
      This is done while calling the constructor binlog_cache_mngr.
 
317
      We cannot set informaton in the constructor binlog_cache_data
 
318
      because the space for binlog_cache_mngr is allocated through
 
319
      a placement new.
 
320
 
 
321
      In the future, we can refactor this and change it to avoid
 
322
      the set_binlog_info. 
 
323
    */
 
324
    DBUG_ASSERT(saved_max_binlog_cache_size == 0 &&
 
325
                param_max_binlog_cache_size != 0 &&
 
326
                ptr_binlog_cache_use == 0 &&
 
327
                param_ptr_binlog_cache_use != 0 &&
 
328
                ptr_binlog_cache_disk_use == 0 &&
 
329
                param_ptr_binlog_cache_disk_use != 0);
 
330
 
 
331
    saved_max_binlog_cache_size= param_max_binlog_cache_size;
 
332
    ptr_binlog_cache_use= param_ptr_binlog_cache_use;
 
333
    ptr_binlog_cache_disk_use= param_ptr_binlog_cache_disk_use;
 
334
    cache_log.end_of_file= saved_max_binlog_cache_size;
 
335
  }
 
336
 
 
337
  /*
 
338
    Cache to store data before copying it to the binary log.
 
339
  */
 
340
  IO_CACHE cache_log;
 
341
 
 
342
private:
 
343
  /*
 
344
    Pending binrows event. This event is the event where the rows are currently
 
345
    written.
 
346
   */
 
347
  Rows_log_event *m_pending;
 
348
 
 
349
  /*
 
350
    Binlog position before the start of the current statement.
 
351
  */
 
352
  my_off_t before_stmt_pos;
 
353
 
 
354
  /*
 
355
    This indicates that some events did not get into the cache and most likely
 
356
    it is corrupted.
 
357
  */ 
 
358
  bool incident;
 
359
 
 
360
  /*
 
361
    This flag indicates if the cache has changes to temporary tables.
 
362
    @TODO This a temporary fix and should be removed after BUG#54562.
 
363
  */
 
364
  bool changes_to_non_trans_temp_table_flag;
 
365
 
 
366
  /**
 
367
    This function computes binlog cache and disk usage.
 
368
  */
 
369
  void compute_statistics()
 
370
  {
 
371
    if (!empty())
 
372
    {
 
373
      statistic_increment(*ptr_binlog_cache_use, &LOCK_status);
 
374
      if (cache_log.disk_writes != 0)
 
375
        statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status);
 
376
    }
 
377
  }
 
378
 
 
379
  /*
 
380
    Stores the values of maximum size of the cache allowed when this cache
 
381
    is configured. This corresponds to either
 
382
      . max_binlog_cache_size or max_binlog_stmt_cache_size.
 
383
  */
 
384
  my_off_t saved_max_binlog_cache_size;
 
385
 
 
386
  /*
 
387
    Stores a pointer to the status variable that keeps track of the in-memory 
 
388
    cache usage. This corresponds to either
 
389
      . binlog_cache_use or binlog_stmt_cache_use.
 
390
  */
 
391
  ulong *ptr_binlog_cache_use;
 
392
 
 
393
  /*
 
394
    Stores a pointer to the status variable that keeps track of the disk
 
395
    cache usage. This corresponds to either
 
396
      . binlog_cache_disk_use or binlog_stmt_cache_disk_use.
 
397
  */
 
398
  ulong *ptr_binlog_cache_disk_use;
 
399
 
 
400
  /*
 
401
    It truncates the cache to a certain position. This includes deleting the
 
402
    pending event.
 
403
   */
 
404
  void truncate(my_off_t pos)
 
405
  {
 
406
    DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos));
 
407
    if (pending())
 
408
    {
 
409
      delete pending();
 
410
      set_pending(0);
 
411
    }
 
412
    reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0);
 
413
    cache_log.end_of_file= saved_max_binlog_cache_size;
 
414
  }
 
415
 
 
416
  binlog_cache_data& operator=(const binlog_cache_data& info);
 
417
  binlog_cache_data(const binlog_cache_data& info);
 
418
};
 
419
 
 
420
class binlog_cache_mngr {
 
421
public:
 
422
  binlog_cache_mngr(my_off_t param_max_binlog_stmt_cache_size,
 
423
                    my_off_t param_max_binlog_cache_size,
 
424
                    ulong *param_ptr_binlog_stmt_cache_use,
 
425
                    ulong *param_ptr_binlog_stmt_cache_disk_use,
 
426
                    ulong *param_ptr_binlog_cache_use,
 
427
                    ulong *param_ptr_binlog_cache_disk_use)
 
428
    : last_commit_pos_offset(0), using_xa(FALSE), xa_xid(0)
 
429
  {
 
430
     stmt_cache.set_binlog_cache_info(param_max_binlog_stmt_cache_size,
 
431
                                      param_ptr_binlog_stmt_cache_use,
 
432
                                      param_ptr_binlog_stmt_cache_disk_use);
 
433
     trx_cache.set_binlog_cache_info(param_max_binlog_cache_size,
 
434
                                     param_ptr_binlog_cache_use,
 
435
                                     param_ptr_binlog_cache_disk_use);
 
436
     last_commit_pos_file[0]= 0;
 
437
  }
 
438
 
 
439
  void reset(bool do_stmt, bool do_trx)
 
440
  {
 
441
    if (do_stmt)
 
442
      stmt_cache.reset();
 
443
    if (do_trx)
 
444
    {
 
445
      trx_cache.reset();
 
446
      using_xa= FALSE;
 
447
      last_commit_pos_file[0]= 0;
 
448
      last_commit_pos_offset= 0;
 
449
    }
 
450
  }
 
451
 
 
452
  binlog_cache_data* get_binlog_cache_data(bool is_transactional)
 
453
  {
 
454
    return (is_transactional ? &trx_cache : &stmt_cache);
 
455
  }
 
456
 
 
457
  IO_CACHE* get_binlog_cache_log(bool is_transactional)
 
458
  {
 
459
    return (is_transactional ? &trx_cache.cache_log : &stmt_cache.cache_log);
 
460
  }
 
461
 
 
462
  binlog_cache_data stmt_cache;
 
463
 
 
464
  binlog_cache_data trx_cache;
 
465
 
 
466
  /*
 
467
    Binlog position for current transaction.
 
468
    For START TRANSACTION WITH CONSISTENT SNAPSHOT, this is the binlog
 
469
    position corresponding to the snapshot taken. During (and after) commit,
 
470
    this is set to the binlog position corresponding to just after the
 
471
    commit (so storage engines can store it in their transaction log).
 
472
  */
 
473
  char last_commit_pos_file[FN_REFLEN];
 
474
  my_off_t last_commit_pos_offset;
 
475
 
 
476
  /*
 
477
    Flag set true if this transaction is committed with log_xid() as part of
 
478
    XA, false if not.
 
479
  */
 
480
  bool using_xa;
 
481
  my_xid xa_xid;
 
482
 
 
483
private:
 
484
 
 
485
  binlog_cache_mngr& operator=(const binlog_cache_mngr& info);
 
486
  binlog_cache_mngr(const binlog_cache_mngr& info);
 
487
};
 
488
 
 
489
handlerton *binlog_hton;
 
490
 
 
491
bool LOGGER::is_log_table_enabled(uint log_table_type)
 
492
{
 
493
  switch (log_table_type) {
 
494
  case QUERY_LOG_SLOW:
 
495
    return (table_log_handler != NULL) && opt_slow_log;
 
496
  case QUERY_LOG_GENERAL:
 
497
    return (table_log_handler != NULL) && opt_log ;
 
498
  default:
 
499
    DBUG_ASSERT(0);
 
500
    return FALSE;                             /* make compiler happy */
 
501
  }
 
502
}
 
503
 
 
504
 
 
505
/* Check if a given table is opened log table */
 
506
int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
 
507
                       const char *table_name, bool check_if_opened)
 
508
{
 
509
  if (db_len == 5 &&
 
510
      !(lower_case_table_names ?
 
511
        my_strcasecmp(system_charset_info, db, "mysql") :
 
512
        strcmp(db, "mysql")))
 
513
  {
 
514
    if (table_name_len == 11 && !(lower_case_table_names ?
 
515
                                  my_strcasecmp(system_charset_info,
 
516
                                                table_name, "general_log") :
 
517
                                  strcmp(table_name, "general_log")))
 
518
    {
 
519
      if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL))
 
520
        return QUERY_LOG_GENERAL;
 
521
      return 0;
 
522
    }
 
523
 
 
524
    if (table_name_len == 8 && !(lower_case_table_names ?
 
525
      my_strcasecmp(system_charset_info, table_name, "slow_log") :
 
526
      strcmp(table_name, "slow_log")))
 
527
    {
 
528
      if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_SLOW))
 
529
        return QUERY_LOG_SLOW;
 
530
      return 0;
 
531
    }
 
532
  }
 
533
  return 0;
 
534
}
 
535
 
 
536
 
 
537
Log_to_csv_event_handler::Log_to_csv_event_handler()
 
538
{
 
539
}
 
540
 
 
541
 
 
542
Log_to_csv_event_handler::~Log_to_csv_event_handler()
 
543
{
 
544
}
 
545
 
 
546
 
 
547
void Log_to_csv_event_handler::cleanup()
 
548
{
 
549
  logger.is_log_tables_initialized= FALSE;
 
550
}
 
551
 
 
552
/* log event handlers */
 
553
 
 
554
/**
 
555
  Log command to the general log table
 
556
 
 
557
  Log given command to the general log table.
 
558
 
 
559
  @param  event_time        command start timestamp
 
560
  @param  user_host         the pointer to the string with user@host info
 
561
  @param  user_host_len     length of the user_host string. this is computed
 
562
                            once and passed to all general log event handlers
 
563
  @param  thread_id         Id of the thread, issued a query
 
564
  @param  command_type      the type of the command being logged
 
565
  @param  command_type_len  the length of the string above
 
566
  @param  sql_text          the very text of the query being executed
 
567
  @param  sql_text_len      the length of sql_text string
 
568
 
 
569
 
 
570
  @return This function attempts to never call my_error(). This is
 
571
  necessary, because general logging happens already after a statement
 
572
  status has been sent to the client, so the client can not see the
 
573
  error anyway. Besides, the error is not related to the statement
 
574
  being executed and is internal, and thus should be handled
 
575
  internally (@todo: how?).
 
576
  If a write to the table has failed, the function attempts to
 
577
  write to a short error message to the file. The failure is also
 
578
  indicated in the return value. 
 
579
 
 
580
  @retval  FALSE   OK
 
581
  @retval  TRUE    error occured
 
582
*/
 
583
 
 
584
bool Log_to_csv_event_handler::
 
585
  log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
 
586
              uint user_host_len, int thread_id,
 
587
              const char *command_type, uint command_type_len,
 
588
              const char *sql_text, uint sql_text_len,
 
589
              CHARSET_INFO *client_cs)
 
590
{
 
591
  TABLE_LIST table_list;
 
592
  TABLE *table;
 
593
  bool result= TRUE;
 
594
  bool need_close= FALSE;
 
595
  bool need_pop= FALSE;
 
596
  bool need_rnd_end= FALSE;
 
597
  uint field_index;
 
598
  Silence_log_table_errors error_handler;
 
599
  Open_tables_backup open_tables_backup;
 
600
  ulonglong save_thd_options;
 
601
  bool save_time_zone_used;
 
602
  DBUG_ENTER("log_general");
 
603
 
 
604
  /*
 
605
    CSV uses TIME_to_timestamp() internally if table needs to be repaired
 
606
    which will set thd->time_zone_used
 
607
  */
 
608
  save_time_zone_used= thd->time_zone_used;
 
609
 
 
610
  save_thd_options= thd->variables.option_bits;
 
611
  thd->variables.option_bits&= ~OPTION_BIN_LOG;
 
612
 
 
613
  table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
 
614
                            GENERAL_LOG_NAME.str, GENERAL_LOG_NAME.length,
 
615
                            GENERAL_LOG_NAME.str,
 
616
                            TL_WRITE_CONCURRENT_INSERT);
 
617
 
 
618
  /*
 
619
    1) open_log_table generates an error of the
 
620
    table can not be opened or is corrupted.
 
621
    2) "INSERT INTO general_log" can generate warning sometimes.
 
622
 
 
623
    Suppress these warnings and errors, they can't be dealt with
 
624
    properly anyway.
 
625
 
 
626
    QQ: this problem needs to be studied in more detail.
 
627
    Comment this 2 lines and run "cast.test" to see what's happening.
 
628
  */
 
629
  thd->push_internal_handler(& error_handler);
 
630
  need_pop= TRUE;
 
631
 
 
632
  if (!(table= open_log_table(thd, &table_list, &open_tables_backup)))
 
633
    goto err;
 
634
 
 
635
  need_close= TRUE;
 
636
 
 
637
  if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
 
638
      table->file->ha_rnd_init_with_error(0))
 
639
    goto err;
 
640
 
 
641
  need_rnd_end= TRUE;
 
642
 
 
643
  /* Honor next number columns if present */
 
644
  table->next_number_field= table->found_next_number_field;
 
645
 
 
646
  /*
 
647
    NOTE: we do not call restore_record() here, as all fields are
 
648
    filled by the Logger (=> no need to load default ones).
 
649
  */
 
650
 
 
651
  /*
 
652
    We do not set a value for table->field[0], as it will use
 
653
    default value (which is CURRENT_TIMESTAMP).
 
654
  */
 
655
 
 
656
  /* check that all columns exist */
 
657
  if (table->s->fields < 6)
 
658
    goto err;
 
659
 
 
660
  DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP);
 
661
 
 
662
  ((Field_timestamp*) table->field[0])->store_TIME(
 
663
                  hrtime_to_my_time(event_time), hrtime_sec_part(event_time));
 
664
 
 
665
  /* do a write */
 
666
  if (table->field[1]->store(user_host, user_host_len, client_cs) ||
 
667
      table->field[2]->store((longlong) thread_id, TRUE) ||
 
668
      table->field[3]->store((longlong) server_id, TRUE) ||
 
669
      table->field[4]->store(command_type, command_type_len, client_cs))
 
670
    goto err;
 
671
 
 
672
  /*
 
673
    A positive return value in store() means truncation.
 
674
    Still logging a message in the log in this case.
 
675
  */
 
676
  table->field[5]->flags|= FIELDFLAG_HEX_ESCAPE;
 
677
  if (table->field[5]->store(sql_text, sql_text_len, client_cs) < 0)
 
678
    goto err;
 
679
 
 
680
  /* mark all fields as not null */
 
681
  table->field[1]->set_notnull();
 
682
  table->field[2]->set_notnull();
 
683
  table->field[3]->set_notnull();
 
684
  table->field[4]->set_notnull();
 
685
  table->field[5]->set_notnull();
 
686
 
 
687
  /* Set any extra columns to their default values */
 
688
  for (field_index= 6 ; field_index < table->s->fields ; field_index++)
 
689
  {
 
690
    table->field[field_index]->set_default();
 
691
  }
 
692
 
 
693
  /* log table entries are not replicated */
 
694
  if (table->file->ha_write_row(table->record[0]))
 
695
    goto err;
 
696
 
 
697
  result= FALSE;
 
698
 
 
699
err:
 
700
  if (result && !thd->killed)
 
701
    sql_print_error("Failed to write to mysql.general_log: %s",
 
702
                    error_handler.message());
 
703
 
 
704
  if (need_rnd_end)
 
705
  {
 
706
    table->file->ha_rnd_end();
 
707
    table->file->ha_release_auto_increment();
 
708
  }
 
709
  if (need_pop)
 
710
    thd->pop_internal_handler();
 
711
  if (need_close)
 
712
    close_log_table(thd, &open_tables_backup);
 
713
 
 
714
  thd->variables.option_bits= save_thd_options;
 
715
  thd->time_zone_used= save_time_zone_used;
 
716
  DBUG_RETURN(result);
 
717
}
 
718
 
 
719
 
 
720
/*
 
721
  Log a query to the slow log table
 
722
 
 
723
  SYNOPSIS
 
724
    log_slow()
 
725
    thd               THD of the query
 
726
    current_time      current timestamp
 
727
    user_host         the pointer to the string with user@host info
 
728
    user_host_len     length of the user_host string. this is computed once
 
729
                      and passed to all general log event handlers
 
730
    query_time        Amount of time the query took to execute (in microseconds)
 
731
    lock_time         Amount of time the query was locked (in microseconds)
 
732
    is_command        The flag, which determines, whether the sql_text is a
 
733
                      query or an administrator command (these are treated
 
734
                      differently by the old logging routines)
 
735
    sql_text          the very text of the query or administrator command
 
736
                      processed
 
737
    sql_text_len      the length of sql_text string
 
738
 
 
739
  DESCRIPTION
 
740
 
 
741
   Log a query to the slow log table
 
742
 
 
743
  RETURN
 
744
    FALSE - OK
 
745
    TRUE - error occured
 
746
*/
 
747
 
 
748
bool Log_to_csv_event_handler::
 
749
  log_slow(THD *thd, my_hrtime_t current_time,
 
750
           const char *user_host, uint user_host_len,
 
751
           ulonglong query_utime, ulonglong lock_utime, bool is_command,
 
752
           const char *sql_text, uint sql_text_len)
 
753
{
 
754
  TABLE_LIST table_list;
 
755
  TABLE *table;
 
756
  bool result= TRUE;
 
757
  bool need_close= FALSE;
 
758
  bool need_rnd_end= FALSE;
 
759
  Silence_log_table_errors error_handler;
 
760
  Open_tables_backup open_tables_backup;
 
761
  CHARSET_INFO *client_cs= thd->variables.character_set_client;
 
762
  bool save_time_zone_used;
 
763
  long query_time= (long) min(query_utime/1000000, TIME_MAX_VALUE_SECONDS);
 
764
  long lock_time=  (long) min(lock_utime/1000000, TIME_MAX_VALUE_SECONDS);
 
765
  long query_time_micro= (long) (query_utime % 1000000);
 
766
  long lock_time_micro=  (long) (lock_utime % 1000000);
 
767
 
 
768
  DBUG_ENTER("Log_to_csv_event_handler::log_slow");
 
769
 
 
770
  thd->push_internal_handler(& error_handler);
 
771
  /*
 
772
    CSV uses TIME_to_timestamp() internally if table needs to be repaired
 
773
    which will set thd->time_zone_used
 
774
  */
 
775
  save_time_zone_used= thd->time_zone_used;
 
776
 
 
777
  table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
 
778
                            SLOW_LOG_NAME.str, SLOW_LOG_NAME.length,
 
779
                            SLOW_LOG_NAME.str,
 
780
                            TL_WRITE_CONCURRENT_INSERT);
 
781
 
 
782
  if (!(table= open_log_table(thd, &table_list, &open_tables_backup)))
 
783
    goto err;
 
784
 
 
785
  need_close= TRUE;
 
786
 
 
787
  if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
 
788
      table->file->ha_rnd_init_with_error(0))
 
789
    goto err;
 
790
 
 
791
  need_rnd_end= TRUE;
 
792
 
 
793
  /* Honor next number columns if present */
 
794
  table->next_number_field= table->found_next_number_field;
 
795
 
 
796
  restore_record(table, s->default_values);    // Get empty record
 
797
 
 
798
  /* check that all columns exist */
 
799
  if (table->s->fields < 11)
 
800
    goto err;
 
801
 
 
802
  /* store the time and user values */
 
803
  DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP);
 
804
  ((Field_timestamp*) table->field[0])->store_TIME(
 
805
             hrtime_to_my_time(current_time), hrtime_sec_part(current_time));
 
806
  if (table->field[1]->store(user_host, user_host_len, client_cs))
 
807
    goto err;
 
808
 
 
809
  /*
 
810
    A TIME field can not hold the full longlong range; query_time or
 
811
    lock_time may be truncated without warning here, if greater than
 
812
    839 hours (~35 days)
 
813
  */
 
814
  MYSQL_TIME t;
 
815
  t.neg= 0;
 
816
 
 
817
  /* fill in query_time field */
 
818
  calc_time_from_sec(&t, query_time, query_time_micro);
 
819
  if (table->field[2]->store_time(&t))
 
820
    goto err;
 
821
  /* lock_time */
 
822
  calc_time_from_sec(&t, lock_time, lock_time_micro);
 
823
  if (table->field[3]->store_time(&t))
 
824
    goto err;
 
825
  /* rows_sent */
 
826
  if (table->field[4]->store((longlong) thd->sent_row_count, TRUE))
 
827
    goto err;
 
828
  /* rows_examined */
 
829
  if (table->field[5]->store((longlong) thd->examined_row_count, TRUE))
 
830
    goto err;
 
831
 
 
832
  /* fill database field */
 
833
  if (thd->db)
 
834
  {
 
835
    if (table->field[6]->store(thd->db, thd->db_length, client_cs))
 
836
      goto err;
 
837
    table->field[6]->set_notnull();
 
838
  }
 
839
 
 
840
  if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
 
841
  {
 
842
    if (table->
 
843
        field[7]->store((longlong)
 
844
                        thd->first_successful_insert_id_in_prev_stmt_for_binlog,
 
845
                        TRUE))
 
846
      goto err;
 
847
    table->field[7]->set_notnull();
 
848
  }
 
849
 
 
850
  /*
 
851
    Set value if we do an insert on autoincrement column. Note that for
 
852
    some engines (those for which get_auto_increment() does not leave a
 
853
    table lock until the statement ends), this is just the first value and
 
854
    the next ones used may not be contiguous to it.
 
855
  */
 
856
  if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
 
857
  {
 
858
    if (table->
 
859
        field[8]->store((longlong)
 
860
          thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(), TRUE))
 
861
      goto err;
 
862
    table->field[8]->set_notnull();
 
863
  }
 
864
 
 
865
  if (table->field[9]->store((longlong) server_id, TRUE))
 
866
    goto err;
 
867
  table->field[9]->set_notnull();
 
868
 
 
869
  /*
 
870
    Column sql_text.
 
871
    A positive return value in store() means truncation.
 
872
    Still logging a message in the log in this case.
 
873
  */
 
874
  if (table->field[10]->store(sql_text, sql_text_len, client_cs) < 0)
 
875
    goto err;
 
876
 
 
877
  /* log table entries are not replicated */
 
878
  if (table->file->ha_write_row(table->record[0]))
 
879
    goto err;
 
880
 
 
881
  result= FALSE;
 
882
 
 
883
err:
 
884
  thd->pop_internal_handler();
 
885
 
 
886
  if (result && !thd->killed)
 
887
    sql_print_error("Failed to write to mysql.slow_log: %s",
 
888
                    error_handler.message());
 
889
 
 
890
  if (need_rnd_end)
 
891
  {
 
892
    table->file->ha_rnd_end();
 
893
    table->file->ha_release_auto_increment();
 
894
  }
 
895
  if (need_close)
 
896
    close_log_table(thd, &open_tables_backup);
 
897
  thd->time_zone_used= save_time_zone_used;
 
898
  DBUG_RETURN(result);
 
899
}
 
900
 
 
901
int Log_to_csv_event_handler::
 
902
  activate_log(THD *thd, uint log_table_type)
 
903
{
 
904
  TABLE_LIST table_list;
 
905
  TABLE *table;
 
906
  LEX_STRING *UNINIT_VAR(log_name);
 
907
  int result;
 
908
  Open_tables_backup open_tables_backup;
 
909
 
 
910
  DBUG_ENTER("Log_to_csv_event_handler::activate_log");
 
911
 
 
912
  if (log_table_type == QUERY_LOG_GENERAL)
 
913
  {
 
914
    log_name= &GENERAL_LOG_NAME;
 
915
  }
 
916
  else
 
917
  {
 
918
    DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW);
 
919
 
 
920
    log_name= &SLOW_LOG_NAME;
 
921
  }
 
922
  table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
 
923
                            log_name->str, log_name->length, log_name->str,
 
924
                            TL_WRITE_CONCURRENT_INSERT);
 
925
 
 
926
  table= open_log_table(thd, &table_list, &open_tables_backup);
 
927
  if (table)
 
928
  {
 
929
    result= 0;
 
930
    close_log_table(thd, &open_tables_backup);
 
931
  }
 
932
  else
 
933
    result= 1;
 
934
 
 
935
  DBUG_RETURN(result);
 
936
}
 
937
 
 
938
bool Log_to_csv_event_handler::
 
939
  log_error(enum loglevel level, const char *format, va_list args)
 
940
{
 
941
  /* No log table is implemented */
 
942
  DBUG_ASSERT(0);
 
943
  return FALSE;
 
944
}
 
945
 
 
946
bool Log_to_file_event_handler::
 
947
  log_error(enum loglevel level, const char *format,
 
948
            va_list args)
 
949
{
 
950
  return vprint_msg_to_log(level, format, args);
 
951
}
 
952
 
 
953
void Log_to_file_event_handler::init_pthread_objects()
 
954
{
 
955
  mysql_log.init_pthread_objects();
 
956
  mysql_slow_log.init_pthread_objects();
 
957
}
 
958
 
 
959
 
 
960
/** Wrapper around MYSQL_LOG::write() for slow log. */
 
961
 
 
962
bool Log_to_file_event_handler::
 
963
  log_slow(THD *thd, my_hrtime_t current_time,
 
964
           const char *user_host, uint user_host_len,
 
965
           ulonglong query_utime, ulonglong lock_utime, bool is_command,
 
966
           const char *sql_text, uint sql_text_len)
 
967
{
 
968
  Silence_log_table_errors error_handler;
 
969
  thd->push_internal_handler(&error_handler);
 
970
  bool retval= mysql_slow_log.write(thd, hrtime_to_my_time(current_time),
 
971
                                    user_host, user_host_len,
 
972
                                    query_utime, lock_utime, is_command,
 
973
                                    sql_text, sql_text_len);
 
974
  thd->pop_internal_handler();
 
975
  return retval;
 
976
}
 
977
 
 
978
 
 
979
/**
 
980
   Wrapper around MYSQL_LOG::write() for general log. We need it since we
 
981
   want all log event handlers to have the same signature.
 
982
*/
 
983
 
 
984
bool Log_to_file_event_handler::
 
985
  log_general(THD *thd, my_hrtime_t event_time, const char *user_host,
 
986
              uint user_host_len, int thread_id,
 
987
              const char *command_type, uint command_type_len,
 
988
              const char *sql_text, uint sql_text_len,
 
989
              CHARSET_INFO *client_cs)
 
990
{
 
991
  Silence_log_table_errors error_handler;
 
992
  thd->push_internal_handler(&error_handler);
 
993
  bool retval= mysql_log.write(hrtime_to_time(event_time), user_host,
 
994
                               user_host_len,
 
995
                               thread_id, command_type, command_type_len,
 
996
                               sql_text, sql_text_len);
 
997
  thd->pop_internal_handler();
 
998
  return retval;
 
999
}
 
1000
 
 
1001
 
 
1002
bool Log_to_file_event_handler::init()
 
1003
{
 
1004
  if (!is_initialized)
 
1005
  {
 
1006
    if (opt_slow_log)
 
1007
      mysql_slow_log.open_slow_log(opt_slow_logname);
 
1008
 
 
1009
    if (opt_log)
 
1010
      mysql_log.open_query_log(opt_logname);
 
1011
 
 
1012
    is_initialized= TRUE;
 
1013
  }
 
1014
 
 
1015
  return FALSE;
 
1016
}
 
1017
 
 
1018
 
 
1019
void Log_to_file_event_handler::cleanup()
 
1020
{
 
1021
  mysql_log.cleanup();
 
1022
  mysql_slow_log.cleanup();
 
1023
}
 
1024
 
 
1025
void Log_to_file_event_handler::flush()
 
1026
{
 
1027
  /* reopen log files */
 
1028
  if (opt_log)
 
1029
    mysql_log.reopen_file();
 
1030
  if (opt_slow_log)
 
1031
    mysql_slow_log.reopen_file();
 
1032
}
 
1033
 
 
1034
/*
 
1035
  Log error with all enabled log event handlers
 
1036
 
 
1037
  SYNOPSIS
 
1038
    error_log_print()
 
1039
 
 
1040
    level             The level of the error significance: NOTE,
 
1041
                      WARNING or ERROR.
 
1042
    format            format string for the error message
 
1043
    args              list of arguments for the format string
 
1044
 
 
1045
  RETURN
 
1046
    FALSE - OK
 
1047
    TRUE - error occured
 
1048
*/
 
1049
 
 
1050
bool LOGGER::error_log_print(enum loglevel level, const char *format,
 
1051
                             va_list args)
 
1052
{
 
1053
  bool error= FALSE;
 
1054
  Log_event_handler **current_handler;
 
1055
 
 
1056
  /* currently we don't need locking here as there is no error_log table */
 
1057
  for (current_handler= error_log_handler_list ; *current_handler ;)
 
1058
    error= (*current_handler++)->log_error(level, format, args) || error;
 
1059
 
 
1060
  return error;
 
1061
}
 
1062
 
 
1063
 
 
1064
void LOGGER::cleanup_base()
 
1065
{
 
1066
  DBUG_ASSERT(inited == 1);
 
1067
  mysql_rwlock_destroy(&LOCK_logger);
 
1068
  if (table_log_handler)
 
1069
  {
 
1070
    table_log_handler->cleanup();
 
1071
    delete table_log_handler;
 
1072
    table_log_handler= NULL;
 
1073
  }
 
1074
  if (file_log_handler)
 
1075
    file_log_handler->cleanup();
 
1076
}
 
1077
 
 
1078
 
 
1079
void LOGGER::cleanup_end()
 
1080
{
 
1081
  DBUG_ASSERT(inited == 1);
 
1082
  if (file_log_handler)
 
1083
  {
 
1084
    delete file_log_handler;
 
1085
    file_log_handler=NULL;
 
1086
  }
 
1087
  inited= 0;
 
1088
}
 
1089
 
 
1090
 
 
1091
/**
 
1092
  Perform basic log initialization: create file-based log handler and
 
1093
  init error log.
 
1094
*/
 
1095
void LOGGER::init_base()
 
1096
{
 
1097
  DBUG_ASSERT(inited == 0);
 
1098
  inited= 1;
 
1099
 
 
1100
  /*
 
1101
    Here we create file log handler. We don't do it for the table log handler
 
1102
    here as it cannot be created so early. The reason is THD initialization,
 
1103
    which depends on the system variables (parsed later).
 
1104
  */
 
1105
  if (!file_log_handler)
 
1106
    file_log_handler= new Log_to_file_event_handler;
 
1107
 
 
1108
  /* by default we use traditional error log */
 
1109
  init_error_log(LOG_FILE);
 
1110
 
 
1111
  file_log_handler->init_pthread_objects();
 
1112
  mysql_rwlock_init(key_rwlock_LOCK_logger, &LOCK_logger);
 
1113
}
 
1114
 
 
1115
 
 
1116
void LOGGER::init_log_tables()
 
1117
{
 
1118
  if (!table_log_handler)
 
1119
    table_log_handler= new Log_to_csv_event_handler;
 
1120
 
 
1121
  if (!is_log_tables_initialized &&
 
1122
      !table_log_handler->init() && !file_log_handler->init())
 
1123
    is_log_tables_initialized= TRUE;
 
1124
}
 
1125
 
 
1126
 
 
1127
bool LOGGER::flush_logs(THD *thd)
 
1128
{
 
1129
  /*
 
1130
    Now we lock logger, as nobody should be able to use logging routines while
 
1131
    log tables are closed
 
1132
  */
 
1133
  logger.lock_exclusive();
 
1134
 
 
1135
  /* reopen log files */
 
1136
  file_log_handler->flush();
 
1137
 
 
1138
  /* end of log flush */
 
1139
  logger.unlock();
 
1140
  return 0;
 
1141
}
 
1142
 
 
1143
 
 
1144
/**
 
1145
  Close and reopen the slow log (with locks).
 
1146
  
 
1147
  @returns FALSE.
 
1148
*/
 
1149
bool LOGGER::flush_slow_log()
 
1150
{
 
1151
  /*
 
1152
    Now we lock logger, as nobody should be able to use logging routines while
 
1153
    log tables are closed
 
1154
  */
 
1155
  logger.lock_exclusive();
 
1156
 
 
1157
  /* Reopen slow log file */
 
1158
  if (opt_slow_log)
 
1159
    file_log_handler->get_mysql_slow_log()->reopen_file();
 
1160
 
 
1161
  /* End of log flush */
 
1162
  logger.unlock();
 
1163
 
 
1164
  return 0;
 
1165
}
 
1166
 
 
1167
 
 
1168
/**
 
1169
  Close and reopen the general log (with locks).
 
1170
 
 
1171
  @returns FALSE.
 
1172
*/
 
1173
bool LOGGER::flush_general_log()
 
1174
{
 
1175
  /*
 
1176
    Now we lock logger, as nobody should be able to use logging routines while
 
1177
    log tables are closed
 
1178
  */
 
1179
  logger.lock_exclusive();
 
1180
 
 
1181
  /* Reopen general log file */
 
1182
  if (opt_log)
 
1183
    file_log_handler->get_mysql_log()->reopen_file();
 
1184
 
 
1185
  /* End of log flush */
 
1186
  logger.unlock();
 
1187
 
 
1188
  return 0;
 
1189
}
 
1190
 
 
1191
 
 
1192
/*
 
1193
  Log slow query with all enabled log event handlers
 
1194
 
 
1195
  SYNOPSIS
 
1196
    slow_log_print()
 
1197
 
 
1198
    thd                 THD of the query being logged
 
1199
    query               The query being logged
 
1200
    query_length        The length of the query string
 
1201
    current_utime       Current time in microseconds (from undefined start)
 
1202
 
 
1203
  RETURN
 
1204
    FALSE   OK
 
1205
    TRUE    error occured
 
1206
*/
 
1207
 
 
1208
bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
 
1209
                            ulonglong current_utime)
 
1210
 
 
1211
{
 
1212
  bool error= FALSE;
 
1213
  Log_event_handler **current_handler;
 
1214
  bool is_command= FALSE;
 
1215
  char user_host_buff[MAX_USER_HOST_SIZE + 1];
 
1216
  Security_context *sctx= thd->security_ctx;
 
1217
  uint user_host_len= 0;
 
1218
  ulonglong query_utime, lock_utime;
 
1219
 
 
1220
  DBUG_ASSERT(thd->enable_slow_log);
 
1221
  /*
 
1222
    Print the message to the buffer if we have slow log enabled
 
1223
  */
 
1224
 
 
1225
  if (*slow_log_handler_list)
 
1226
  {
 
1227
    /* do not log slow queries from replication threads */
 
1228
    if (thd->slave_thread && !opt_log_slow_slave_statements)
 
1229
      return 0;
 
1230
 
 
1231
    lock_shared();
 
1232
    if (!opt_slow_log)
 
1233
    {
 
1234
      unlock();
 
1235
      return 0;
 
1236
    }
 
1237
 
 
1238
    /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */
 
1239
    user_host_len= (strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
 
1240
                             sctx->priv_user ? sctx->priv_user : "", "[",
 
1241
                             sctx->user ? sctx->user : (thd->slave_thread ? "SQL_SLAVE" : ""), "] @ ",
 
1242
                             sctx->host ? sctx->host : "", " [",
 
1243
                             sctx->ip ? sctx->ip : "", "]", NullS) -
 
1244
                    user_host_buff);
 
1245
 
 
1246
    DBUG_ASSERT(thd->start_utime);
 
1247
    DBUG_ASSERT(thd->start_time);
 
1248
    query_utime= (current_utime - thd->start_utime);
 
1249
    lock_utime=  (thd->utime_after_lock - thd->start_utime);
 
1250
    my_hrtime_t current_time= { hrtime_from_time(thd->start_time) +
 
1251
                                thd->start_time_sec_part + query_utime };
 
1252
 
 
1253
    if (!query)
 
1254
    {
 
1255
      is_command= TRUE;
 
1256
      query= command_name[thd->command].str;
 
1257
      query_length= command_name[thd->command].length;
 
1258
    }
 
1259
 
 
1260
    for (current_handler= slow_log_handler_list; *current_handler ;)
 
1261
      error= (*current_handler++)->log_slow(thd, current_time,
 
1262
                                            user_host_buff, user_host_len,
 
1263
                                            query_utime, lock_utime, is_command,
 
1264
                                            query, query_length) || error;
 
1265
 
 
1266
    unlock();
 
1267
  }
 
1268
  return error;
 
1269
}
 
1270
 
 
1271
bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
 
1272
                               const char *query, uint query_length)
 
1273
{
 
1274
  bool error= FALSE;
 
1275
  Log_event_handler **current_handler= general_log_handler_list;
 
1276
  char user_host_buff[MAX_USER_HOST_SIZE + 1];
 
1277
  uint user_host_len= 0;
 
1278
  my_hrtime_t current_time;
 
1279
 
 
1280
  DBUG_ASSERT(thd);
 
1281
 
 
1282
  user_host_len= make_user_name(thd, user_host_buff);
 
1283
 
 
1284
  current_time= my_hrtime();
 
1285
 
 
1286
  mysql_audit_general_log(thd, hrtime_to_time(current_time),
 
1287
                          user_host_buff, user_host_len,
 
1288
                          command_name[(uint) command].str,
 
1289
                          command_name[(uint) command].length,
 
1290
                          query, query_length);
 
1291
                        
 
1292
  if (opt_log && log_command(thd, command))
 
1293
  {
 
1294
    lock_shared();
 
1295
    while (*current_handler)
 
1296
      error|= (*current_handler++)->
 
1297
        log_general(thd, current_time, user_host_buff,
 
1298
                    user_host_len, thd->thread_id,
 
1299
                    command_name[(uint) command].str,
 
1300
                    command_name[(uint) command].length,
 
1301
                    query, query_length,
 
1302
                    thd->variables.character_set_client) || error;
 
1303
    unlock();
 
1304
  }
 
1305
 
 
1306
  return error;
 
1307
}
 
1308
 
 
1309
bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
 
1310
                               const char *format, va_list args)
 
1311
{
 
1312
  uint message_buff_len= 0;
 
1313
  char message_buff[MAX_LOG_BUFFER_SIZE];
 
1314
 
 
1315
  /* prepare message */
 
1316
  if (format)
 
1317
    message_buff_len= my_vsnprintf(message_buff, sizeof(message_buff),
 
1318
                                   format, args);
 
1319
  else
 
1320
    message_buff[0]= '\0';
 
1321
 
 
1322
  return general_log_write(thd, command, message_buff, message_buff_len);
 
1323
}
 
1324
 
 
1325
void LOGGER::init_error_log(ulonglong error_log_printer)
 
1326
{
 
1327
  if (error_log_printer & LOG_NONE)
 
1328
  {
 
1329
    error_log_handler_list[0]= 0;
 
1330
    return;
 
1331
  }
 
1332
 
 
1333
  switch (error_log_printer) {
 
1334
  case LOG_FILE:
 
1335
    error_log_handler_list[0]= file_log_handler;
 
1336
    error_log_handler_list[1]= 0;
 
1337
    break;
 
1338
    /* these two are disabled for now */
 
1339
  case LOG_TABLE:
 
1340
    DBUG_ASSERT(0);
 
1341
    break;
 
1342
  case LOG_TABLE|LOG_FILE:
 
1343
    DBUG_ASSERT(0);
 
1344
    break;
 
1345
  }
 
1346
}
 
1347
 
 
1348
void LOGGER::init_slow_log(ulonglong slow_log_printer)
 
1349
{
 
1350
  if (slow_log_printer & LOG_NONE)
 
1351
  {
 
1352
    slow_log_handler_list[0]= 0;
 
1353
    return;
 
1354
  }
 
1355
 
 
1356
  switch (slow_log_printer) {
 
1357
  case LOG_FILE:
 
1358
    slow_log_handler_list[0]= file_log_handler;
 
1359
    slow_log_handler_list[1]= 0;
 
1360
    break;
 
1361
  case LOG_TABLE:
 
1362
    slow_log_handler_list[0]= table_log_handler;
 
1363
    slow_log_handler_list[1]= 0;
 
1364
    break;
 
1365
  case LOG_TABLE|LOG_FILE:
 
1366
    slow_log_handler_list[0]= file_log_handler;
 
1367
    slow_log_handler_list[1]= table_log_handler;
 
1368
    slow_log_handler_list[2]= 0;
 
1369
    break;
 
1370
  }
 
1371
}
 
1372
 
 
1373
void LOGGER::init_general_log(ulonglong general_log_printer)
 
1374
{
 
1375
  if (general_log_printer & LOG_NONE)
 
1376
  {
 
1377
    general_log_handler_list[0]= 0;
 
1378
    return;
 
1379
  }
 
1380
 
 
1381
  switch (general_log_printer) {
 
1382
  case LOG_FILE:
 
1383
    general_log_handler_list[0]= file_log_handler;
 
1384
    general_log_handler_list[1]= 0;
 
1385
    break;
 
1386
  case LOG_TABLE:
 
1387
    general_log_handler_list[0]= table_log_handler;
 
1388
    general_log_handler_list[1]= 0;
 
1389
    break;
 
1390
  case LOG_TABLE|LOG_FILE:
 
1391
    general_log_handler_list[0]= file_log_handler;
 
1392
    general_log_handler_list[1]= table_log_handler;
 
1393
    general_log_handler_list[2]= 0;
 
1394
    break;
 
1395
  }
 
1396
}
 
1397
 
 
1398
 
 
1399
bool LOGGER::activate_log_handler(THD* thd, uint log_type)
 
1400
{
 
1401
  MYSQL_QUERY_LOG *file_log;
 
1402
  bool res= FALSE;
 
1403
  lock_exclusive();
 
1404
  switch (log_type) {
 
1405
  case QUERY_LOG_SLOW:
 
1406
    if (!opt_slow_log)
 
1407
    {
 
1408
      file_log= file_log_handler->get_mysql_slow_log();
 
1409
 
 
1410
      file_log->open_slow_log(opt_slow_logname);
 
1411
      if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW))
 
1412
      {
 
1413
        /* Error printed by open table in activate_log() */
 
1414
        res= TRUE;
 
1415
        file_log->close(0);
 
1416
      }
 
1417
      else
 
1418
      {
 
1419
        init_slow_log(log_output_options);
 
1420
        opt_slow_log= TRUE;
 
1421
      }
 
1422
    }
 
1423
    break;
 
1424
  case QUERY_LOG_GENERAL:
 
1425
    if (!opt_log)
 
1426
    {
 
1427
      file_log= file_log_handler->get_mysql_log();
 
1428
 
 
1429
      file_log->open_query_log(opt_logname);
 
1430
      if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL))
 
1431
      {
 
1432
        /* Error printed by open table in activate_log() */
 
1433
        res= TRUE;
 
1434
        file_log->close(0);
 
1435
      }
 
1436
      else
 
1437
      {
 
1438
        init_general_log(log_output_options);
 
1439
        opt_log= TRUE;
 
1440
      }
 
1441
    }
 
1442
    break;
 
1443
  default:
 
1444
    DBUG_ASSERT(0);
 
1445
  }
 
1446
  unlock();
 
1447
  return res;
 
1448
}
 
1449
 
 
1450
 
 
1451
void LOGGER::deactivate_log_handler(THD *thd, uint log_type)
 
1452
{
 
1453
  my_bool *tmp_opt= 0;
 
1454
  MYSQL_LOG *file_log;
 
1455
  LINT_INIT(file_log);
 
1456
 
 
1457
  switch (log_type) {
 
1458
  case QUERY_LOG_SLOW:
 
1459
    tmp_opt= &opt_slow_log;
 
1460
    file_log= file_log_handler->get_mysql_slow_log();
 
1461
    break;
 
1462
  case QUERY_LOG_GENERAL:
 
1463
    tmp_opt= &opt_log;
 
1464
    file_log= file_log_handler->get_mysql_log();
 
1465
    break;
 
1466
  default:
 
1467
    MY_ASSERT_UNREACHABLE();
 
1468
  }
 
1469
 
 
1470
  if (!(*tmp_opt))
 
1471
    return;
 
1472
 
 
1473
  lock_exclusive();
 
1474
  file_log->close(0);
 
1475
  *tmp_opt= FALSE;
 
1476
  unlock();
 
1477
}
 
1478
 
 
1479
 
 
1480
/* the parameters are unused for the log tables */
 
1481
bool Log_to_csv_event_handler::init()
 
1482
{
 
1483
  return 0;
 
1484
}
 
1485
 
 
1486
int LOGGER::set_handlers(ulonglong error_log_printer,
 
1487
                         ulonglong slow_log_printer,
 
1488
                         ulonglong general_log_printer)
 
1489
{
 
1490
  /* error log table is not supported yet */
 
1491
  DBUG_ASSERT(error_log_printer < LOG_TABLE);
 
1492
 
 
1493
  lock_exclusive();
 
1494
 
 
1495
  if ((slow_log_printer & LOG_TABLE || general_log_printer & LOG_TABLE) &&
 
1496
      !is_log_tables_initialized)
 
1497
  {
 
1498
    slow_log_printer= (slow_log_printer & ~LOG_TABLE) | LOG_FILE;
 
1499
    general_log_printer= (general_log_printer & ~LOG_TABLE) | LOG_FILE;
 
1500
 
 
1501
    sql_print_error("Failed to initialize log tables. "
 
1502
                    "Falling back to the old-fashioned logs");
 
1503
  }
 
1504
 
 
1505
  init_error_log(error_log_printer);
 
1506
  init_slow_log(slow_log_printer);
 
1507
  init_general_log(general_log_printer);
 
1508
 
 
1509
  unlock();
 
1510
 
 
1511
  return 0;
 
1512
}
 
1513
 
 
1514
 /*
 
1515
  Save position of binary log transaction cache.
 
1516
 
 
1517
  SYNPOSIS
 
1518
    binlog_trans_log_savepos()
 
1519
 
 
1520
    thd      The thread to take the binlog data from
 
1521
    pos      Pointer to variable where the position will be stored
 
1522
 
 
1523
  DESCRIPTION
 
1524
 
 
1525
    Save the current position in the binary log transaction cache into
 
1526
    the variable pointed to by 'pos'
 
1527
 */
 
1528
 
 
1529
static void
 
1530
binlog_trans_log_savepos(THD *thd, my_off_t *pos)
 
1531
{
 
1532
  DBUG_ENTER("binlog_trans_log_savepos");
 
1533
  DBUG_ASSERT(pos != NULL);
 
1534
  binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
 
1535
  DBUG_ASSERT(mysql_bin_log.is_open());
 
1536
  *pos= cache_mngr->trx_cache.get_byte_position();
 
1537
  DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
 
1538
  DBUG_VOID_RETURN;
 
1539
}
 
1540
 
 
1541
 
 
1542
/*
 
1543
  Truncate the binary log transaction cache.
 
1544
 
 
1545
  SYNPOSIS
 
1546
    binlog_trans_log_truncate()
 
1547
 
 
1548
    thd      The thread to take the binlog data from
 
1549
    pos      Position to truncate to
 
1550
 
 
1551
  DESCRIPTION
 
1552
 
 
1553
    Truncate the binary log to the given position. Will not change
 
1554
    anything else.
 
1555
 
 
1556
 */
 
1557
static void
 
1558
binlog_trans_log_truncate(THD *thd, my_off_t pos)
 
1559
{
 
1560
  DBUG_ENTER("binlog_trans_log_truncate");
 
1561
  DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
 
1562
 
 
1563
  DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
 
1564
  /* Only true if binlog_trans_log_savepos() wasn't called before */
 
1565
  DBUG_ASSERT(pos != ~(my_off_t) 0);
 
1566
 
 
1567
  binlog_cache_mngr *const cache_mngr=
 
1568
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
1569
  cache_mngr->trx_cache.restore_savepoint(pos);
 
1570
  DBUG_VOID_RETURN;
 
1571
}
 
1572
 
 
1573
 
 
1574
/*
 
1575
  this function is mostly a placeholder.
 
1576
  conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open)
 
1577
  should be moved here.
 
1578
*/
 
1579
 
 
1580
int binlog_init(void *p)
 
1581
{
 
1582
  binlog_hton= (handlerton *)p;
 
1583
  binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
 
1584
  binlog_hton->db_type=DB_TYPE_BINLOG;
 
1585
  binlog_hton->savepoint_offset= sizeof(my_off_t);
 
1586
  binlog_hton->close_connection= binlog_close_connection;
 
1587
  binlog_hton->savepoint_set= binlog_savepoint_set;
 
1588
  binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
 
1589
  binlog_hton->commit= binlog_commit;
 
1590
  binlog_hton->rollback= binlog_rollback;
 
1591
  binlog_hton->prepare= binlog_prepare;
 
1592
  binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
 
1593
  binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
 
1594
  return 0;
 
1595
}
 
1596
 
 
1597
static int binlog_close_connection(handlerton *hton, THD *thd)
 
1598
{
 
1599
  binlog_cache_mngr *const cache_mngr=
 
1600
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
1601
  DBUG_ASSERT(cache_mngr->trx_cache.empty() && cache_mngr->stmt_cache.empty());
 
1602
  thd_set_ha_data(thd, binlog_hton, NULL);
 
1603
  cache_mngr->~binlog_cache_mngr();
 
1604
  my_free(cache_mngr);
 
1605
  return 0;
 
1606
}
 
1607
 
 
1608
/*
 
1609
  This function flushes a cache upon commit/rollback.
 
1610
 
 
1611
  SYNOPSIS
 
1612
    binlog_flush_cache()
 
1613
 
 
1614
    thd        The thread whose transaction should be ended
 
1615
    cache_mngr Pointer to the binlog_cache_mngr to use
 
1616
    all        True if the entire transaction should be ended, false if
 
1617
               only the statement transaction should be ended.
 
1618
    end_ev     The end event to use (COMMIT, ROLLBACK, or commit XID)
 
1619
    using_stmt True if the statement cache should be flushed
 
1620
    using_trx  True if the transaction cache should be flushed
 
1621
 
 
1622
  DESCRIPTION
 
1623
 
 
1624
    End the currently transaction or statement. The transaction can be either
 
1625
    a real transaction or a statement transaction.
 
1626
 
 
1627
    This can be to commit a transaction, with a COMMIT query event or an XA
 
1628
    commit XID event. But it can also be to rollback a transaction with a
 
1629
    ROLLBACK query event, used for rolling back transactions which also
 
1630
    contain updates to non-transactional tables. Or it can be a flush of
 
1631
    a statement cache.
 
1632
 */
 
1633
static int
 
1634
binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
 
1635
                   Log_event *end_ev, bool all, bool using_stmt,
 
1636
                   bool using_trx)
 
1637
{
 
1638
  int error= 0;
 
1639
  DBUG_ENTER("binlog_flush_cache");
 
1640
 
 
1641
  if ((using_stmt && !cache_mngr->stmt_cache.empty()) ||
 
1642
      (using_trx && !cache_mngr->trx_cache.empty()))
 
1643
  {
 
1644
    if (using_stmt && thd->binlog_flush_pending_rows_event(TRUE, FALSE))
 
1645
      DBUG_RETURN(1);
 
1646
    if (using_trx && thd->binlog_flush_pending_rows_event(TRUE, TRUE))
 
1647
      DBUG_RETURN(1);
 
1648
 
 
1649
    /*
 
1650
      Doing a commit or a rollback including non-transactional tables,
 
1651
      i.e., ending a transaction where we might write the transaction
 
1652
      cache to the binary log.
 
1653
 
 
1654
      We can always end the statement when ending a transaction since
 
1655
      transactions are not allowed inside stored functions.  If they
 
1656
      were, we would have to ensure that we're not ending a statement
 
1657
      inside a stored function.
 
1658
    */
 
1659
    error= mysql_bin_log.write_transaction_to_binlog(thd, cache_mngr,
 
1660
                                                     end_ev, all,
 
1661
                                                     using_stmt, using_trx);
 
1662
  }
 
1663
  cache_mngr->reset(using_stmt, using_trx);
 
1664
 
 
1665
  DBUG_ASSERT((!using_stmt || cache_mngr->stmt_cache.empty()) &&
 
1666
              (!using_trx || cache_mngr->trx_cache.empty()));
 
1667
  DBUG_RETURN(error);
 
1668
}
 
1669
 
 
1670
 
 
1671
/**
 
1672
  This function flushes the stmt-cache upon commit.
 
1673
 
 
1674
  @param thd                The thread whose transaction should be flushed
 
1675
  @param cache_mngr         Pointer to the cache manager
 
1676
 
 
1677
  @return
 
1678
    nonzero if an error pops up when flushing the cache.
 
1679
*/
 
1680
static inline int
 
1681
binlog_commit_flush_stmt_cache(THD *thd, bool all,
 
1682
                               binlog_cache_mngr *cache_mngr)
 
1683
{
 
1684
  Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
 
1685
                          FALSE, TRUE, TRUE, 0);
 
1686
  return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE));
 
1687
}
 
1688
 
 
1689
/**
 
1690
  This function flushes the trx-cache upon commit.
 
1691
 
 
1692
  @param thd                The thread whose transaction should be flushed
 
1693
  @param cache_mngr         Pointer to the cache manager
 
1694
 
 
1695
  @return
 
1696
    nonzero if an error pops up when flushing the cache.
 
1697
*/
 
1698
static inline int
 
1699
binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr)
 
1700
{
 
1701
  Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
 
1702
                          TRUE, TRUE, TRUE, 0);
 
1703
  return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
 
1704
}
 
1705
 
 
1706
/**
 
1707
  This function flushes the trx-cache upon rollback.
 
1708
 
 
1709
  @param thd                The thread whose transaction should be flushed
 
1710
  @param cache_mngr         Pointer to the cache manager
 
1711
 
 
1712
  @return
 
1713
    nonzero if an error pops up when flushing the cache.
 
1714
*/
 
1715
static inline int
 
1716
binlog_rollback_flush_trx_cache(THD *thd, bool all,
 
1717
                                binlog_cache_mngr *cache_mngr)
 
1718
{
 
1719
  Query_log_event end_evt(thd, STRING_WITH_LEN("ROLLBACK"),
 
1720
                          TRUE, TRUE, TRUE, 0);
 
1721
  return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
 
1722
}
 
1723
 
 
1724
/**
 
1725
  This function flushes the trx-cache upon commit.
 
1726
 
 
1727
  @param thd                The thread whose transaction should be flushed
 
1728
  @param cache_mngr         Pointer to the cache manager
 
1729
  @param xid                Transaction Id
 
1730
 
 
1731
  @return
 
1732
    nonzero if an error pops up when flushing the cache.
 
1733
*/
 
1734
static inline int
 
1735
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
 
1736
                               bool all, my_xid xid)
 
1737
{
 
1738
  if (xid)
 
1739
  {
 
1740
    Xid_log_event end_evt(thd, xid, TRUE);
 
1741
    return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
 
1742
  }
 
1743
  else
 
1744
  {
 
1745
    /*
 
1746
      Empty xid occurs in XA COMMIT ... ONE PHASE.
 
1747
      In this case, we do not have a MySQL xid for the transaction, and the
 
1748
      external XA transaction coordinator will have to handle recovery if
 
1749
      needed. So we end the transaction with a plain COMMIT query event.
 
1750
    */
 
1751
    Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
 
1752
                            TRUE, TRUE, TRUE, 0);
 
1753
    return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
 
1754
  }
 
1755
}
 
1756
 
 
1757
/**
 
1758
  This function truncates the transactional cache upon committing or rolling
 
1759
  back either a transaction or a statement.
 
1760
 
 
1761
  @param thd        The thread whose transaction should be flushed
 
1762
  @param cache_mngr Pointer to the cache data to be flushed
 
1763
  @param all        @c true means truncate the transaction, otherwise the
 
1764
                    statement must be truncated.
 
1765
 
 
1766
  @return
 
1767
    nonzero if an error pops up when truncating the transactional cache.
 
1768
*/
 
1769
static int
 
1770
binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
 
1771
{
 
1772
  DBUG_ENTER("binlog_truncate_trx_cache");
 
1773
  int error=0;
 
1774
  /*
 
1775
    This function handles transactional changes and as such this flag
 
1776
    equals to true.
 
1777
  */
 
1778
  bool const is_transactional= TRUE;
 
1779
 
 
1780
  DBUG_PRINT("info", ("thd->options={ %s %s}, transaction: %s",
 
1781
                      FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT),
 
1782
                      FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
 
1783
                      all ? "all" : "stmt"));
 
1784
 
 
1785
  thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
 
1786
  /*
 
1787
    If rolling back an entire transaction or a single statement not
 
1788
    inside a transaction, we reset the transaction cache.
 
1789
  */
 
1790
  if (ending_trans(thd, all))
 
1791
  {
 
1792
    if (cache_mngr->trx_cache.has_incident())
 
1793
      error= mysql_bin_log.write_incident(thd);
 
1794
 
 
1795
    thd->clear_binlog_table_maps();
 
1796
 
 
1797
    cache_mngr->reset(false, true);
 
1798
  }
 
1799
  /*
 
1800
    If rolling back a statement in a transaction, we truncate the
 
1801
    transaction cache to remove the statement.
 
1802
  */
 
1803
  else
 
1804
    cache_mngr->trx_cache.restore_prev_position();
 
1805
 
 
1806
  DBUG_ASSERT(thd->binlog_get_pending_rows_event(is_transactional) == NULL);
 
1807
  DBUG_RETURN(error);
 
1808
}
 
1809
 
 
1810
static int binlog_prepare(handlerton *hton, THD *thd, bool all)
 
1811
{
 
1812
  /*
 
1813
    do nothing.
 
1814
    just pretend we can do 2pc, so that MySQL won't
 
1815
    switch to 1pc.
 
1816
    real work will be done in MYSQL_BIN_LOG::log_and_order()
 
1817
  */
 
1818
  return 0;
 
1819
}
 
1820
 
 
1821
/**
 
1822
  This function is called once after each statement.
 
1823
 
 
1824
  It has the responsibility to flush the caches to the binary log on commits.
 
1825
 
 
1826
  @param hton  The binlog handlerton.
 
1827
  @param thd   The client thread that executes the transaction.
 
1828
  @param all   This is @c true if this is a real transaction commit, and
 
1829
               @false otherwise.
 
1830
 
 
1831
  @see handlerton::commit
 
1832
*/
 
1833
static int binlog_commit(handlerton *hton, THD *thd, bool all)
 
1834
{
 
1835
  int error= 0;
 
1836
  DBUG_ENTER("binlog_commit");
 
1837
  binlog_cache_mngr *const cache_mngr=
 
1838
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
1839
 
 
1840
  DBUG_PRINT("debug",
 
1841
             ("all: %d, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
 
1842
              all,
 
1843
              YESNO(thd->in_multi_stmt_transaction_mode()),
 
1844
              YESNO(thd->transaction.all.modified_non_trans_table),
 
1845
              YESNO(thd->transaction.stmt.modified_non_trans_table)));
 
1846
 
 
1847
  if (!cache_mngr->stmt_cache.empty())
 
1848
  {
 
1849
    error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
 
1850
  }
 
1851
 
 
1852
  if (cache_mngr->trx_cache.empty())
 
1853
  {
 
1854
    /*
 
1855
      we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
 
1856
    */
 
1857
    cache_mngr->reset(false, true);
 
1858
    DBUG_RETURN(error);
 
1859
  }
 
1860
 
 
1861
  /*
 
1862
    We commit the transaction if:
 
1863
     - We are not in a transaction and committing a statement, or
 
1864
     - We are in a transaction and a full transaction is committed.
 
1865
    Otherwise, we accumulate the changes.
 
1866
  */
 
1867
  if (!error && ending_trans(thd, all))
 
1868
    error= binlog_commit_flush_trx_cache(thd, all, cache_mngr);
 
1869
 
 
1870
  /*
 
1871
    This is part of the stmt rollback.
 
1872
  */
 
1873
  if (!all)
 
1874
    cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
 
1875
 
 
1876
  DBUG_RETURN(error);
 
1877
}
 
1878
 
 
1879
/**
 
1880
  This function is called when a transaction or a statement is rolled back.
 
1881
 
 
1882
  @param hton  The binlog handlerton.
 
1883
  @param thd   The client thread that executes the transaction.
 
1884
  @param all   This is @c true if this is a real transaction rollback, and
 
1885
               @false otherwise.
 
1886
 
 
1887
  @see handlerton::rollback
 
1888
*/
 
1889
static int binlog_rollback(handlerton *hton, THD *thd, bool all)
 
1890
{
 
1891
  DBUG_ENTER("binlog_rollback");
 
1892
  int error= 0;
 
1893
  binlog_cache_mngr *const cache_mngr=
 
1894
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
1895
 
 
1896
  DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
 
1897
                       YESNO(all),
 
1898
                       YESNO(thd->transaction.all.modified_non_trans_table),
 
1899
                       YESNO(thd->transaction.stmt.modified_non_trans_table)));
 
1900
 
 
1901
  /*
 
1902
    If an incident event is set we do not flush the content of the statement
 
1903
    cache because it may be corrupted.
 
1904
  */
 
1905
  if (cache_mngr->stmt_cache.has_incident())
 
1906
  {
 
1907
    error= mysql_bin_log.write_incident(thd);
 
1908
    cache_mngr->reset(true, false);
 
1909
  }
 
1910
  else if (!cache_mngr->stmt_cache.empty())
 
1911
  {
 
1912
    error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
 
1913
  }
 
1914
 
 
1915
  if (cache_mngr->trx_cache.empty())
 
1916
  {
 
1917
    /*
 
1918
      we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
 
1919
    */
 
1920
    cache_mngr->reset(false, true);
 
1921
    DBUG_RETURN(error);
 
1922
  }
 
1923
 
 
1924
  if (mysql_bin_log.check_write_error(thd))
 
1925
  {
 
1926
    /*
 
1927
      "all == true" means that a "rollback statement" triggered the error and
 
1928
      this function was called. However, this must not happen as a rollback
 
1929
      is written directly to the binary log. And in auto-commit mode, a single
 
1930
      statement that is rolled back has the flag all == false.
 
1931
    */
 
1932
    DBUG_ASSERT(!all);
 
1933
    /*
 
1934
      We reach this point if the effect of a statement did not properly get into
 
1935
      a cache and need to be rolled back.
 
1936
    */
 
1937
    error |= binlog_truncate_trx_cache(thd, cache_mngr, all);
 
1938
  }
 
1939
  else if (!error)
 
1940
  {  
 
1941
    /*
 
1942
      We flush the cache wrapped in a beging/rollback if:
 
1943
        . aborting a single or multi-statement transaction and;
 
1944
        . the OPTION_KEEP_LOG is active or;
 
1945
        . the format is STMT and a non-trans table was updated or;
 
1946
        . the format is MIXED and a temporary non-trans table was
 
1947
          updated or;
 
1948
        . the format is MIXED, non-trans table was updated and
 
1949
          aborting a single statement transaction;
 
1950
    */
 
1951
    if (ending_trans(thd, all) &&
 
1952
        ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
 
1953
         (trans_has_updated_non_trans_table(thd) &&
 
1954
          thd->variables.binlog_format == BINLOG_FORMAT_STMT) ||
 
1955
         (cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
 
1956
          thd->variables.binlog_format == BINLOG_FORMAT_MIXED) ||
 
1957
         (trans_has_updated_non_trans_table(thd) &&
 
1958
          ending_single_stmt_trans(thd,all) &&
 
1959
          thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
 
1960
      error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr);
 
1961
    /*
 
1962
      Truncate the cache if:
 
1963
        . aborting a single or multi-statement transaction or;
 
1964
        . the OPTION_KEEP_LOG is not active and;
 
1965
        . the format is not STMT or no non-trans table was
 
1966
          updated and;
 
1967
        . the format is not MIXED or no temporary non-trans table
 
1968
          was updated.
 
1969
    */
 
1970
    else if (ending_trans(thd, all) ||
 
1971
             (!(thd->variables.option_bits & OPTION_KEEP_LOG) &&
 
1972
              (!stmt_has_updated_non_trans_table(thd) ||
 
1973
               thd->variables.binlog_format != BINLOG_FORMAT_STMT) &&
 
1974
              (!cache_mngr->trx_cache.changes_to_non_trans_temp_table() ||
 
1975
               thd->variables.binlog_format != BINLOG_FORMAT_MIXED)))
 
1976
      error= binlog_truncate_trx_cache(thd, cache_mngr, all);
 
1977
  }
 
1978
 
 
1979
  /* 
 
1980
    This is part of the stmt rollback.
 
1981
  */
 
1982
  if (!all)
 
1983
    cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
 
1984
 
 
1985
  DBUG_RETURN(error);
 
1986
}
 
1987
 
 
1988
void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
 
1989
{
 
1990
  DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
 
1991
 
 
1992
  write_error= 1;
 
1993
 
 
1994
  if (check_write_error(thd))
 
1995
    DBUG_VOID_RETURN;
 
1996
 
 
1997
  if (my_errno == EFBIG)
 
1998
  {
 
1999
    if (is_transactional)
 
2000
    {
 
2001
      my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
 
2002
    }
 
2003
    else
 
2004
    {
 
2005
      my_message(ER_STMT_CACHE_FULL, ER(ER_STMT_CACHE_FULL), MYF(MY_WME));
 
2006
    }
 
2007
  }
 
2008
  else
 
2009
  {
 
2010
    my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
 
2011
  }
 
2012
 
 
2013
  DBUG_VOID_RETURN;
 
2014
}
 
2015
 
 
2016
bool MYSQL_BIN_LOG::check_write_error(THD *thd)
 
2017
{
 
2018
  DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
 
2019
 
 
2020
  bool checked= FALSE;
 
2021
 
 
2022
  if (!thd->is_error())
 
2023
    DBUG_RETURN(checked);
 
2024
 
 
2025
  switch (thd->stmt_da->sql_errno())
 
2026
  {
 
2027
    case ER_TRANS_CACHE_FULL:
 
2028
    case ER_STMT_CACHE_FULL:
 
2029
    case ER_ERROR_ON_WRITE:
 
2030
    case ER_BINLOG_LOGGING_IMPOSSIBLE:
 
2031
      checked= TRUE;
 
2032
    break;
 
2033
  }
 
2034
 
 
2035
  DBUG_RETURN(checked);
 
2036
}
 
2037
 
 
2038
 
 
2039
/**
 
2040
  @note
 
2041
  How do we handle this (unlikely but legal) case:
 
2042
  @verbatim
 
2043
    [transaction] + [update to non-trans table] + [rollback to savepoint] ?
 
2044
  @endverbatim
 
2045
  The problem occurs when a savepoint is before the update to the
 
2046
  non-transactional table. Then when there's a rollback to the savepoint, if we
 
2047
  simply truncate the binlog cache, we lose the part of the binlog cache where
 
2048
  the update is. If we want to not lose it, we need to write the SAVEPOINT
 
2049
  command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
 
2050
  is easy: it's just write at the end of the binlog cache, but the former
 
2051
  should be *inserted* to the place where the user called SAVEPOINT. The
 
2052
  solution is that when the user calls SAVEPOINT, we write it to the binlog
 
2053
  cache (so no need to later insert it). As transactions are never intermixed
 
2054
  in the binary log (i.e. they are serialized), we won't have conflicts with
 
2055
  savepoint names when using mysqlbinlog or in the slave SQL thread.
 
2056
  Then when ROLLBACK TO SAVEPOINT is called, if we updated some
 
2057
  non-transactional table, we don't truncate the binlog cache but instead write
 
2058
  ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
 
2059
  will chop the SAVEPOINT command from the binlog cache, which is good as in
 
2060
  that case there is no need to have it in the binlog).
 
2061
*/
 
2062
 
 
2063
static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
 
2064
{
 
2065
  DBUG_ENTER("binlog_savepoint_set");
 
2066
 
 
2067
  binlog_trans_log_savepos(thd, (my_off_t*) sv);
 
2068
  /* Write it to the binary log */
 
2069
 
 
2070
  char buf[1024];
 
2071
  String log_query(buf, sizeof(buf), &my_charset_bin);
 
2072
  if (log_query.copy(STRING_WITH_LEN("SAVEPOINT "), &my_charset_bin) ||
 
2073
      append_identifier(thd, &log_query,
 
2074
                        thd->lex->ident.str, thd->lex->ident.length))
 
2075
    DBUG_RETURN(1);
 
2076
  int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
 
2077
  Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
 
2078
                        TRUE, FALSE, TRUE, errcode);
 
2079
  int ret= mysql_bin_log.write(&qinfo);
 
2080
  DBUG_RETURN(ret);
 
2081
}
 
2082
 
 
2083
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
 
2084
{
 
2085
  DBUG_ENTER("binlog_savepoint_rollback");
 
2086
 
 
2087
  /*
 
2088
    Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
 
2089
    non-transactional table. Otherwise, truncate the binlog cache starting
 
2090
    from the SAVEPOINT command.
 
2091
  */
 
2092
  if (unlikely(trans_has_updated_non_trans_table(thd) ||
 
2093
               (thd->variables.option_bits & OPTION_KEEP_LOG)))
 
2094
  {
 
2095
    char buf[1024];
 
2096
    String log_query(buf, sizeof(buf), &my_charset_bin);
 
2097
    if (log_query.copy(STRING_WITH_LEN("ROLLBACK TO "), &my_charset_bin) ||
 
2098
        append_identifier(thd, &log_query,
 
2099
                          thd->lex->ident.str, thd->lex->ident.length))
 
2100
      DBUG_RETURN(1);
 
2101
    int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
 
2102
    Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
 
2103
                          TRUE, FALSE, TRUE, errcode);
 
2104
    DBUG_RETURN(mysql_bin_log.write(&qinfo));
 
2105
  }
 
2106
  binlog_trans_log_truncate(thd, *(my_off_t*)sv);
 
2107
  DBUG_RETURN(0);
 
2108
}
 
2109
 
 
2110
 
 
2111
int check_binlog_magic(IO_CACHE* log, const char** errmsg)
 
2112
{
 
2113
  uchar magic[4];
 
2114
  DBUG_ASSERT(my_b_tell(log) == 0);
 
2115
 
 
2116
  if (my_b_read(log, magic, sizeof(magic)))
 
2117
  {
 
2118
    *errmsg = "I/O error reading the header from the binary log";
 
2119
    sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno,
 
2120
                    log->error);
 
2121
    return 1;
 
2122
  }
 
2123
  if (bcmp(magic, BINLOG_MAGIC, sizeof(magic)))
 
2124
  {
 
2125
    *errmsg = "Binlog has bad magic number;  It's not a binary log file that can be used by this version of MySQL";
 
2126
    return 1;
 
2127
  }
 
2128
  return 0;
 
2129
}
 
2130
 
 
2131
 
 
2132
File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
 
2133
{
 
2134
  File file;
 
2135
  DBUG_ENTER("open_binlog");
 
2136
 
 
2137
  if ((file= mysql_file_open(key_file_binlog,
 
2138
                             log_file_name, O_RDONLY | O_BINARY | O_SHARE,
 
2139
                             MYF(MY_WME))) < 0)
 
2140
  {
 
2141
    sql_print_error("Failed to open log (file '%s', errno %d)",
 
2142
                    log_file_name, my_errno);
 
2143
    *errmsg = "Could not open log file";
 
2144
    goto err;
 
2145
  }
 
2146
  if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0,
 
2147
                    MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
 
2148
  {
 
2149
    sql_print_error("Failed to create a cache on log (file '%s')",
 
2150
                    log_file_name);
 
2151
    *errmsg = "Could not open log file";
 
2152
    goto err;
 
2153
  }
 
2154
  if (check_binlog_magic(log,errmsg))
 
2155
    goto err;
 
2156
  DBUG_RETURN(file);
 
2157
 
 
2158
err:
 
2159
  if (file >= 0)
 
2160
  {
 
2161
    mysql_file_close(file, MYF(0));
 
2162
    end_io_cache(log);
 
2163
  }
 
2164
  DBUG_RETURN(-1);
 
2165
}
 
2166
 
 
2167
#ifdef _WIN32
 
2168
static int eventSource = 0;
 
2169
 
 
2170
static void setup_windows_event_source()
 
2171
{
 
2172
  HKEY    hRegKey= NULL;
 
2173
  DWORD   dwError= 0;
 
2174
  TCHAR   szPath[MAX_PATH];
 
2175
  DWORD dwTypes;
 
2176
 
 
2177
  if (eventSource)               // Ensure that we are only called once
 
2178
    return;
 
2179
  eventSource= 1;
 
2180
 
 
2181
  // Create the event source registry key
 
2182
  dwError= RegCreateKey(HKEY_LOCAL_MACHINE,
 
2183
                          "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MySQL", 
 
2184
                          &hRegKey);
 
2185
 
 
2186
  /* Name of the PE module that contains the message resource */
 
2187
  GetModuleFileName(NULL, szPath, MAX_PATH);
 
2188
 
 
2189
  /* Register EventMessageFile */
 
2190
  dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
 
2191
                          (PBYTE) szPath, (DWORD) (strlen(szPath) + 1));
 
2192
 
 
2193
  /* Register supported event types */
 
2194
  dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
 
2195
            EVENTLOG_INFORMATION_TYPE);
 
2196
  dwError= RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
 
2197
                         (LPBYTE) &dwTypes, sizeof dwTypes);
 
2198
 
 
2199
  RegCloseKey(hRegKey);
 
2200
}
 
2201
 
 
2202
#endif /* _WIN32 */
 
2203
 
 
2204
 
 
2205
/**
 
2206
  Find a unique filename for 'filename.#'.
 
2207
 
 
2208
  Set '#' to the number next to the maximum found in the most
 
2209
  recent log file extension.
 
2210
 
 
2211
  This function will return nonzero if: (i) the generated name
 
2212
  exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
 
2213
  or (iii) some other error happened while examining the filesystem.
 
2214
 
 
2215
  @return
 
2216
    nonzero if not possible to get unique filename.
 
2217
*/
 
2218
 
 
2219
static int find_uniq_filename(char *name)
 
2220
{
 
2221
  uint                  i;
 
2222
  char                  buff[FN_REFLEN], ext_buf[FN_REFLEN];
 
2223
  struct st_my_dir     *dir_info;
 
2224
  reg1 struct fileinfo *file_info;
 
2225
  ulong                 max_found= 0, next= 0, number= 0;
 
2226
  size_t                buf_length, length;
 
2227
  char                  *start, *end;
 
2228
  int                   error= 0;
 
2229
  DBUG_ENTER("find_uniq_filename");
 
2230
 
 
2231
  length= dirname_part(buff, name, &buf_length);
 
2232
  start=  name + length;
 
2233
  end=    strend(start);
 
2234
 
 
2235
  *end='.';
 
2236
  length= (size_t) (end - start + 1);
 
2237
 
 
2238
  if ((DBUG_EVALUATE_IF("error_unique_log_filename", 1, 
 
2239
      !(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))))
 
2240
  {                                             // This shouldn't happen
 
2241
    strmov(end,".1");                           // use name+1
 
2242
    DBUG_RETURN(1);
 
2243
  }
 
2244
  file_info= dir_info->dir_entry;
 
2245
  for (i= dir_info->number_off_files ; i-- ; file_info++)
 
2246
  {
 
2247
    if (memcmp(file_info->name, start, length) == 0 &&
 
2248
        test_if_number(file_info->name+length, &number,0))
 
2249
    {
 
2250
      set_if_bigger(max_found,(ulong) number);
 
2251
    }
 
2252
  }
 
2253
  my_dirend(dir_info);
 
2254
 
 
2255
  /* check if reached the maximum possible extension number */
 
2256
  if (max_found == MAX_LOG_UNIQUE_FN_EXT)
 
2257
  {
 
2258
    sql_print_error("Log filename extension number exhausted: %06lu. \
 
2259
Please fix this by archiving old logs and \
 
2260
updating the index files.", max_found);
 
2261
    error= 1;
 
2262
    goto end;
 
2263
  }
 
2264
 
 
2265
  next= max_found + 1;
 
2266
  if (sprintf(ext_buf, "%06lu", next)<0)
 
2267
  {
 
2268
    error= 1;
 
2269
    goto end;
 
2270
  }
 
2271
  *end++='.';
 
2272
 
 
2273
  /* 
 
2274
    Check if the generated extension size + the file name exceeds the
 
2275
    buffer size used. If one did not check this, then the filename might be
 
2276
    truncated, resulting in error.
 
2277
   */
 
2278
  if (((strlen(ext_buf) + (end - name)) >= FN_REFLEN))
 
2279
  {
 
2280
    sql_print_error("Log filename too large: %s%s (%zu). \
 
2281
Please fix this by archiving old logs and updating the \
 
2282
index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
 
2283
    error= 1;
 
2284
    goto end;
 
2285
  }
 
2286
 
 
2287
  if (sprintf(end, "%06lu", next)<0)
 
2288
  {
 
2289
    error= 1;
 
2290
    goto end;
 
2291
  }
 
2292
 
 
2293
  /* print warning if reaching the end of available extensions. */
 
2294
  if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
 
2295
    sql_print_warning("Next log extension: %lu. \
 
2296
Remaining log filename extensions: %lu. \
 
2297
Please consider archiving some logs.", next, (MAX_LOG_UNIQUE_FN_EXT - next));
 
2298
 
 
2299
end:
 
2300
  DBUG_RETURN(error);
 
2301
}
 
2302
 
 
2303
 
 
2304
void MYSQL_LOG::init(enum_log_type log_type_arg,
 
2305
                     enum cache_type io_cache_type_arg)
 
2306
{
 
2307
  DBUG_ENTER("MYSQL_LOG::init");
 
2308
  log_type= log_type_arg;
 
2309
  io_cache_type= io_cache_type_arg;
 
2310
  DBUG_PRINT("info",("log_type: %d", log_type));
 
2311
  DBUG_VOID_RETURN;
 
2312
}
 
2313
 
 
2314
 
 
2315
bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
 
2316
                                           const char *new_name,
 
2317
                                           enum_log_type log_type_arg,
 
2318
                                           enum cache_type io_cache_type_arg)
 
2319
{
 
2320
  init(log_type_arg, io_cache_type_arg);
 
2321
 
 
2322
  if (new_name && !strmov(log_file_name, new_name))
 
2323
    return TRUE;
 
2324
  else if (!new_name && generate_new_name(log_file_name, log_name))
 
2325
    return TRUE;
 
2326
 
 
2327
  return FALSE;
 
2328
}
 
2329
 
 
2330
 
 
2331
/*
 
2332
  Open a (new) log file.
 
2333
 
 
2334
  SYNOPSIS
 
2335
    open()
 
2336
 
 
2337
    log_name            The name of the log to open
 
2338
    log_type_arg        The type of the log. E.g. LOG_NORMAL
 
2339
    new_name            The new name for the logfile. This is only needed
 
2340
                        when the method is used to open the binlog file.
 
2341
    io_cache_type_arg   The type of the IO_CACHE to use for this log file
 
2342
 
 
2343
  DESCRIPTION
 
2344
    Open the logfile, init IO_CACHE and write startup messages
 
2345
    (in case of general and slow query logs).
 
2346
 
 
2347
  RETURN VALUES
 
2348
    0   ok
 
2349
    1   error
 
2350
*/
 
2351
 
 
2352
bool MYSQL_LOG::open(
 
2353
#ifdef HAVE_PSI_INTERFACE
 
2354
                     PSI_file_key log_file_key,
 
2355
#endif
 
2356
                     const char *log_name, enum_log_type log_type_arg,
 
2357
                     const char *new_name, enum cache_type io_cache_type_arg)
 
2358
{
 
2359
  char buff[FN_REFLEN];
 
2360
  File file= -1;
 
2361
  int open_flags= O_CREAT | O_BINARY;
 
2362
  DBUG_ENTER("MYSQL_LOG::open");
 
2363
  DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
 
2364
 
 
2365
  write_error= 0;
 
2366
 
 
2367
  if (!(name= my_strdup(log_name, MYF(MY_WME))))
 
2368
  {
 
2369
    name= (char *)log_name; // for the error message
 
2370
    goto err;
 
2371
  }
 
2372
 
 
2373
  if (init_and_set_log_file_name(name, new_name,
 
2374
                                 log_type_arg, io_cache_type_arg))
 
2375
    goto err;
 
2376
 
 
2377
  if (io_cache_type == SEQ_READ_APPEND)
 
2378
    open_flags |= O_RDWR | O_APPEND;
 
2379
  else
 
2380
    open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
 
2381
 
 
2382
  db[0]= 0;
 
2383
 
 
2384
#ifdef HAVE_PSI_INTERFACE
 
2385
  /* Keep the key for reopen */
 
2386
  m_log_file_key= log_file_key;
 
2387
#endif
 
2388
 
 
2389
  if ((file= mysql_file_open(log_file_key,
 
2390
                             log_file_name, open_flags,
 
2391
                             MYF(MY_WME | ME_WAITTANG))) < 0 ||
 
2392
      init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
 
2393
                    mysql_file_tell(file, MYF(MY_WME)), 0,
 
2394
                    MYF(MY_WME | MY_NABP |
 
2395
                        ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
 
2396
    goto err;
 
2397
 
 
2398
  if (log_type == LOG_NORMAL)
 
2399
  {
 
2400
    char *end;
 
2401
    int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
 
2402
#ifdef EMBEDDED_LIBRARY
 
2403
                        "embedded library\n",
 
2404
                        my_progname, server_version, MYSQL_COMPILATION_COMMENT
 
2405
#elif _WIN32
 
2406
                        "started with:\nTCP Port: %d, Named Pipe: %s\n",
 
2407
                        my_progname, server_version, MYSQL_COMPILATION_COMMENT,
 
2408
                        mysqld_port, mysqld_unix_port
 
2409
#else
 
2410
                        "started with:\nTcp port: %d  Unix socket: %s\n",
 
2411
                        my_progname, server_version, MYSQL_COMPILATION_COMMENT,
 
2412
                        mysqld_port, mysqld_unix_port
 
2413
#endif
 
2414
                       );
 
2415
    end= strnmov(buff + len, "Time                 Id Command    Argument\n",
 
2416
                 sizeof(buff) - len);
 
2417
    if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
 
2418
        flush_io_cache(&log_file))
 
2419
      goto err;
 
2420
  }
 
2421
 
 
2422
  log_state= LOG_OPENED;
 
2423
  DBUG_RETURN(0);
 
2424
 
 
2425
err:
 
2426
  sql_print_error("Could not use %s for logging (error %d). \
 
2427
Turning logging off for the whole duration of the MySQL server process. \
 
2428
To turn it on again: fix the cause, \
 
2429
shutdown the MySQL server and restart it.", name, errno);
 
2430
  if (file >= 0)
 
2431
    mysql_file_close(file, MYF(0));
 
2432
  end_io_cache(&log_file);
 
2433
  my_free(name);
 
2434
  name= NULL;
 
2435
  log_state= LOG_CLOSED;
 
2436
  DBUG_RETURN(1);
 
2437
}
 
2438
 
 
2439
MYSQL_LOG::MYSQL_LOG()
 
2440
  : name(0), write_error(FALSE), inited(FALSE), log_type(LOG_UNKNOWN),
 
2441
    log_state(LOG_CLOSED)
 
2442
{
 
2443
  /*
 
2444
    We don't want to initialize LOCK_Log here as such initialization depends on
 
2445
    safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
 
2446
    called only in main(). Doing initialization here would make it happen
 
2447
    before main().
 
2448
  */
 
2449
  bzero((char*) &log_file, sizeof(log_file));
 
2450
}
 
2451
 
 
2452
void MYSQL_LOG::init_pthread_objects()
 
2453
{
 
2454
  DBUG_ASSERT(inited == 0);
 
2455
  inited= 1;
 
2456
  mysql_mutex_init(key_LOG_LOCK_log, &LOCK_log, MY_MUTEX_INIT_SLOW);
 
2457
}
 
2458
 
 
2459
/*
 
2460
  Close the log file
 
2461
 
 
2462
  SYNOPSIS
 
2463
    close()
 
2464
    exiting     Bitmask. LOG_CLOSE_TO_BE_OPENED is used if we intend to call
 
2465
                open at once after close. LOG_CLOSE_DELAYED_CLOSE is used for
 
2466
                binlog rotation, to delay actual close of the old file until
 
2467
                we have successfully created the new file.
 
2468
 
 
2469
  NOTES
 
2470
    One can do an open on the object at once after doing a close.
 
2471
    The internal structures are not freed until cleanup() is called
 
2472
*/
 
2473
 
 
2474
void MYSQL_LOG::close(uint exiting)
 
2475
{                                       // One can't set log_type here!
 
2476
  DBUG_ENTER("MYSQL_LOG::close");
 
2477
  DBUG_PRINT("enter",("exiting: %d", (int) exiting));
 
2478
  if (log_state == LOG_OPENED)
 
2479
  {
 
2480
    end_io_cache(&log_file);
 
2481
 
 
2482
    if (mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error)
 
2483
    {
 
2484
      write_error= 1;
 
2485
      sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
 
2486
    }
 
2487
 
 
2488
    if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) &&
 
2489
        mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error)
 
2490
    {
 
2491
      write_error= 1;
 
2492
      sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
 
2493
    }
 
2494
  }
 
2495
 
 
2496
  log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
 
2497
  my_free(name);
 
2498
  name= NULL;
 
2499
  DBUG_VOID_RETURN;
 
2500
}
 
2501
 
 
2502
/** This is called only once. */
 
2503
 
 
2504
void MYSQL_LOG::cleanup()
 
2505
{
 
2506
  DBUG_ENTER("cleanup");
 
2507
  if (inited)
 
2508
  {
 
2509
    inited= 0;
 
2510
    mysql_mutex_destroy(&LOCK_log);
 
2511
    close(0);
 
2512
  }
 
2513
  DBUG_VOID_RETURN;
 
2514
}
 
2515
 
 
2516
 
 
2517
int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
 
2518
{
 
2519
  fn_format(new_name, log_name, mysql_data_home, "", 4);
 
2520
  if (log_type == LOG_BIN)
 
2521
  {
 
2522
    if (!fn_ext(log_name)[0])
 
2523
    {
 
2524
      if (find_uniq_filename(new_name))
 
2525
      {
 
2526
        my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
 
2527
                        MYF(ME_FATALERROR), log_name);
 
2528
        sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
 
2529
        return 1;
 
2530
      }
 
2531
    }
 
2532
  }
 
2533
  return 0;
 
2534
}
 
2535
 
 
2536
 
 
2537
/*
 
2538
  Reopen the log file
 
2539
 
 
2540
  SYNOPSIS
 
2541
    reopen_file()
 
2542
 
 
2543
  DESCRIPTION
 
2544
    Reopen the log file. The method is used during FLUSH LOGS
 
2545
    and locks LOCK_log mutex
 
2546
*/
 
2547
 
 
2548
 
 
2549
void MYSQL_QUERY_LOG::reopen_file()
 
2550
{
 
2551
  char *save_name;
 
2552
 
 
2553
  DBUG_ENTER("MYSQL_LOG::reopen_file");
 
2554
  if (!is_open())
 
2555
  {
 
2556
    DBUG_PRINT("info",("log is closed"));
 
2557
    DBUG_VOID_RETURN;
 
2558
  }
 
2559
 
 
2560
  mysql_mutex_lock(&LOCK_log);
 
2561
 
 
2562
  save_name= name;
 
2563
  name= 0;                              // Don't free name
 
2564
  close(LOG_CLOSE_TO_BE_OPENED);
 
2565
 
 
2566
  /*
 
2567
     Note that at this point, log_state != LOG_CLOSED (important for is_open()).
 
2568
  */
 
2569
 
 
2570
  open(
 
2571
#ifdef HAVE_PSI_INTERFACE
 
2572
       m_log_file_key,
 
2573
#endif
 
2574
       save_name, log_type, 0, io_cache_type);
 
2575
  my_free(save_name);
 
2576
 
 
2577
  mysql_mutex_unlock(&LOCK_log);
 
2578
 
 
2579
  DBUG_VOID_RETURN;
 
2580
}
 
2581
 
 
2582
 
 
2583
/*
 
2584
  Write a command to traditional general log file
 
2585
 
 
2586
  SYNOPSIS
 
2587
    write()
 
2588
 
 
2589
    event_time        command start timestamp
 
2590
    user_host         the pointer to the string with user@host info
 
2591
    user_host_len     length of the user_host string. this is computed once
 
2592
                      and passed to all general log  event handlers
 
2593
    thread_id         Id of the thread, issued a query
 
2594
    command_type      the type of the command being logged
 
2595
    command_type_len  the length of the string above
 
2596
    sql_text          the very text of the query being executed
 
2597
    sql_text_len      the length of sql_text string
 
2598
 
 
2599
  DESCRIPTION
 
2600
 
 
2601
   Log given command to to normal (not rotable) log file
 
2602
 
 
2603
  RETURN
 
2604
    FASE - OK
 
2605
    TRUE - error occured
 
2606
*/
 
2607
 
 
2608
bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
 
2609
                            uint user_host_len, int thread_id,
 
2610
                            const char *command_type, uint command_type_len,
 
2611
                            const char *sql_text, uint sql_text_len)
 
2612
{
 
2613
  char buff[32];
 
2614
  uint length= 0;
 
2615
  char local_time_buff[MAX_TIME_SIZE];
 
2616
  struct tm start;
 
2617
  uint time_buff_len= 0;
 
2618
 
 
2619
  mysql_mutex_lock(&LOCK_log);
 
2620
 
 
2621
  /* Test if someone closed between the is_open test and lock */
 
2622
  if (is_open())
 
2623
  {
 
2624
    /* for testing output of timestamp and thread id */
 
2625
    DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
 
2626
 
 
2627
    /* Note that my_b_write() assumes it knows the length for this */
 
2628
      if (event_time != last_time)
 
2629
      {
 
2630
        last_time= event_time;
 
2631
 
 
2632
        localtime_r(&event_time, &start);
 
2633
 
 
2634
        time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
 
2635
                                   "%02d%02d%02d %2d:%02d:%02d\t",
 
2636
                                   start.tm_year % 100, start.tm_mon + 1,
 
2637
                                   start.tm_mday, start.tm_hour,
 
2638
                                   start.tm_min, start.tm_sec);
 
2639
 
 
2640
        if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
 
2641
          goto err;
 
2642
      }
 
2643
      else
 
2644
        if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
 
2645
          goto err;
 
2646
 
 
2647
      /* command_type, thread_id */
 
2648
      length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
 
2649
 
 
2650
    if (my_b_write(&log_file, (uchar*) buff, length))
 
2651
      goto err;
 
2652
 
 
2653
    if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
 
2654
      goto err;
 
2655
 
 
2656
    if (my_b_write(&log_file, (uchar*) "\t", 1))
 
2657
      goto err;
 
2658
 
 
2659
    /* sql_text */
 
2660
    if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
 
2661
      goto err;
 
2662
 
 
2663
    if (my_b_write(&log_file, (uchar*) "\n", 1) ||
 
2664
        flush_io_cache(&log_file))
 
2665
      goto err;
 
2666
  }
 
2667
 
 
2668
  mysql_mutex_unlock(&LOCK_log);
 
2669
  return FALSE;
 
2670
err:
 
2671
 
 
2672
  if (!write_error)
 
2673
  {
 
2674
    write_error= 1;
 
2675
    sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
 
2676
  }
 
2677
  mysql_mutex_unlock(&LOCK_log);
 
2678
  return TRUE;
 
2679
}
 
2680
 
 
2681
 
 
2682
/*
 
2683
  Log a query to the traditional slow log file
 
2684
 
 
2685
  SYNOPSIS
 
2686
    write()
 
2687
 
 
2688
    thd               THD of the query
 
2689
    current_time      current timestamp
 
2690
    user_host         the pointer to the string with user@host info
 
2691
    user_host_len     length of the user_host string. this is computed once
 
2692
                      and passed to all general log event handlers
 
2693
    query_utime       Amount of time the query took to execute (in microseconds)
 
2694
    lock_utime        Amount of time the query was locked (in microseconds)
 
2695
    is_command        The flag, which determines, whether the sql_text is a
 
2696
                      query or an administrator command.
 
2697
    sql_text          the very text of the query or administrator command
 
2698
                      processed
 
2699
    sql_text_len      the length of sql_text string
 
2700
 
 
2701
  DESCRIPTION
 
2702
 
 
2703
   Log a query to the slow log file.
 
2704
 
 
2705
  RETURN
 
2706
    FALSE - OK
 
2707
    TRUE - error occured
 
2708
*/
 
2709
 
 
2710
bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
 
2711
                            const char *user_host,
 
2712
                            uint user_host_len, ulonglong query_utime,
 
2713
                            ulonglong lock_utime, bool is_command,
 
2714
                            const char *sql_text, uint sql_text_len)
 
2715
{
 
2716
  bool error= 0;
 
2717
  DBUG_ENTER("MYSQL_QUERY_LOG::write");
 
2718
 
 
2719
  mysql_mutex_lock(&LOCK_log);
 
2720
 
 
2721
  if (!is_open())
 
2722
  {
 
2723
    mysql_mutex_unlock(&LOCK_log);
 
2724
    DBUG_RETURN(0);
 
2725
  }
 
2726
 
 
2727
  if (is_open())
 
2728
  {                                             // Safety agains reopen
 
2729
    int tmp_errno= 0;
 
2730
    char buff[80], *end;
 
2731
    char query_time_buff[22+7], lock_time_buff[22+7];
 
2732
    uint buff_len;
 
2733
    end= buff;
 
2734
 
 
2735
    if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
 
2736
    {
 
2737
      if (current_time != last_time)
 
2738
      {
 
2739
        last_time= current_time;
 
2740
        struct tm start;
 
2741
        localtime_r(&current_time, &start);
 
2742
 
 
2743
        buff_len= my_snprintf(buff, sizeof buff,
 
2744
                              "# Time: %02d%02d%02d %2d:%02d:%02d\n",
 
2745
                              start.tm_year % 100, start.tm_mon + 1,
 
2746
                              start.tm_mday, start.tm_hour,
 
2747
                              start.tm_min, start.tm_sec);
 
2748
 
 
2749
        /* Note that my_b_write() assumes it knows the length for this */
 
2750
        if (my_b_write(&log_file, (uchar*) buff, buff_len))
 
2751
          tmp_errno= errno;
 
2752
      }
 
2753
      const uchar uh[]= "# User@Host: ";
 
2754
      if (my_b_write(&log_file, uh, sizeof(uh) - 1))
 
2755
        tmp_errno= errno;
 
2756
      if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
 
2757
        tmp_errno= errno;
 
2758
      if (my_b_write(&log_file, (uchar*) "\n", 1))
 
2759
        tmp_errno= errno;
 
2760
    }
 
2761
    
 
2762
    /* For slow query log */
 
2763
    sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
 
2764
    sprintf(lock_time_buff,  "%.6f", ulonglong2double(lock_utime)/1000000.0);
 
2765
    if (my_b_printf(&log_file,
 
2766
                    "# Thread_id: %lu  Schema: %s  QC_hit: %s\n" \
 
2767
                    "# Query_time: %s  Lock_time: %s  Rows_sent: %lu  Rows_examined: %lu\n",
 
2768
                    (ulong) thd->thread_id, (thd->db ? thd->db : ""),
 
2769
                    ((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"),
 
2770
                    query_time_buff, lock_time_buff,
 
2771
                    (ulong) thd->sent_row_count,
 
2772
                    (ulong) thd->examined_row_count) == (size_t) -1)
 
2773
      tmp_errno= errno;
 
2774
     if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) &&
 
2775
         (thd->query_plan_flags &
 
2776
          (QPLAN_FULL_SCAN | QPLAN_FULL_JOIN | QPLAN_TMP_TABLE |
 
2777
           QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK)) &&
 
2778
         my_b_printf(&log_file,
 
2779
                     "# Full_scan: %s  Full_join: %s  "
 
2780
                     "Tmp_table: %s  Tmp_table_on_disk: %s\n"
 
2781
                     "# Filesort: %s  Filesort_on_disk: %s  Merge_passes: %lu\n",
 
2782
                     ((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"),
 
2783
                     ((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"),
 
2784
                     ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"),
 
2785
                     ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"),
 
2786
                     ((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"),
 
2787
                     ((thd->query_plan_flags & QPLAN_FILESORT_DISK) ?
 
2788
                      "Yes" : "No"),
 
2789
                     thd->query_plan_fsort_passes) == (size_t) -1)
 
2790
       tmp_errno= errno;
 
2791
    if (thd->db && strcmp(thd->db, db))
 
2792
    {                                           // Database changed
 
2793
      if (my_b_printf(&log_file,"use %s;\n",thd->db) == (size_t) -1)
 
2794
        tmp_errno= errno;
 
2795
      strmov(db,thd->db);
 
2796
    }
 
2797
    if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
 
2798
    {
 
2799
      end=strmov(end, ",last_insert_id=");
 
2800
      end=longlong10_to_str((longlong)
 
2801
                            thd->first_successful_insert_id_in_prev_stmt_for_binlog,
 
2802
                            end, -10);
 
2803
    }
 
2804
    // Save value if we do an insert.
 
2805
    if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
 
2806
    {
 
2807
      if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
 
2808
      {
 
2809
        end=strmov(end,",insert_id=");
 
2810
        end=longlong10_to_str((longlong)
 
2811
                              thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(),
 
2812
                              end, -10);
 
2813
      }
 
2814
    }
 
2815
 
 
2816
    /*
 
2817
      This info used to show up randomly, depending on whether the query
 
2818
      checked the query start time or not. now we always write current
 
2819
      timestamp to the slow log
 
2820
    */
 
2821
    end= strmov(end, ",timestamp=");
 
2822
    end= int10_to_str((long) current_time, end, 10);
 
2823
 
 
2824
    if (end != buff)
 
2825
    {
 
2826
      *end++=';';
 
2827
      *end='\n';
 
2828
      if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
 
2829
          my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
 
2830
        tmp_errno= errno;
 
2831
    }
 
2832
    if (is_command)
 
2833
    {
 
2834
      end= strxmov(buff, "# administrator command: ", NullS);
 
2835
      buff_len= (ulong) (end - buff);
 
2836
      DBUG_EXECUTE_IF("simulate_slow_log_write_error",
 
2837
                      {DBUG_SET("+d,simulate_file_write_error");});
 
2838
      if(my_b_write(&log_file, (uchar*) buff, buff_len))
 
2839
        tmp_errno= errno;
 
2840
    }
 
2841
    if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
 
2842
        my_b_write(&log_file, (uchar*) ";\n",2) ||
 
2843
        flush_io_cache(&log_file))
 
2844
      tmp_errno= errno;
 
2845
    if (tmp_errno)
 
2846
    {
 
2847
      error= 1;
 
2848
      if (! write_error)
 
2849
      {
 
2850
        write_error= 1;
 
2851
        sql_print_error(ER(ER_ERROR_ON_WRITE), name, error);
 
2852
      }
 
2853
    }
 
2854
  }
 
2855
  mysql_mutex_unlock(&LOCK_log);
 
2856
  DBUG_RETURN(error);
 
2857
}
 
2858
 
 
2859
 
 
2860
/**
 
2861
  @todo
 
2862
  The following should be using fn_format();  We just need to
 
2863
  first change fn_format() to cut the file name if it's too long.
 
2864
*/
 
2865
const char *MYSQL_LOG::generate_name(const char *log_name,
 
2866
                                      const char *suffix,
 
2867
                                      bool strip_ext, char *buff)
 
2868
{
 
2869
  if (!log_name || !log_name[0])
 
2870
  {
 
2871
    strmake(buff, pidfile_name, FN_REFLEN - strlen(suffix) - 1);
 
2872
    return (const char *)
 
2873
      fn_format(buff, buff, "", suffix, MYF(MY_REPLACE_EXT|MY_REPLACE_DIR));
 
2874
  }
 
2875
  // get rid of extension if the log is binary to avoid problems
 
2876
  if (strip_ext)
 
2877
  {
 
2878
    char *p= fn_ext(log_name);
 
2879
    uint length= (uint) (p - log_name);
 
2880
    strmake(buff, log_name, min(length, FN_REFLEN-1));
 
2881
    return (const char*)buff;
 
2882
  }
 
2883
  return log_name;
 
2884
}
 
2885
 
 
2886
 
 
2887
 
 
2888
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
 
2889
  :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
 
2890
   need_start_event(TRUE),
 
2891
   group_commit_queue(0), group_commit_queue_busy(FALSE),
 
2892
   num_commits(0), num_group_commits(0),
 
2893
   sync_period_ptr(sync_period), sync_counter(0),
 
2894
   is_relay_log(0), signal_cnt(0),
 
2895
   checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
 
2896
   relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
 
2897
   description_event_for_exec(0), description_event_for_queue(0)
 
2898
{
 
2899
  /*
 
2900
    We don't want to initialize locks here as such initialization depends on
 
2901
    safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
 
2902
    called only in main(). Doing initialization here would make it happen
 
2903
    before main().
 
2904
  */
 
2905
  index_file_name[0] = 0;
 
2906
  bzero((char*) &index_file, sizeof(index_file));
 
2907
  bzero((char*) &purge_index_file, sizeof(purge_index_file));
 
2908
}
 
2909
 
 
2910
/* this is called only once */
 
2911
 
 
2912
void MYSQL_BIN_LOG::cleanup()
 
2913
{
 
2914
  DBUG_ENTER("cleanup");
 
2915
  if (inited)
 
2916
  {
 
2917
    inited= 0;
 
2918
    close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
 
2919
    delete description_event_for_queue;
 
2920
    delete description_event_for_exec;
 
2921
    mysql_mutex_destroy(&LOCK_log);
 
2922
    mysql_mutex_destroy(&LOCK_index);
 
2923
    mysql_cond_destroy(&update_cond);
 
2924
  }
 
2925
  DBUG_VOID_RETURN;
 
2926
}
 
2927
 
 
2928
 
 
2929
/* Init binlog-specific vars */
 
2930
void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
 
2931
{
 
2932
  DBUG_ENTER("MYSQL_BIN_LOG::init");
 
2933
  no_auto_events= no_auto_events_arg;
 
2934
  max_size= max_size_arg;
 
2935
  DBUG_PRINT("info",("max_size: %lu", max_size));
 
2936
  DBUG_VOID_RETURN;
 
2937
}
 
2938
 
 
2939
 
 
2940
void MYSQL_BIN_LOG::init_pthread_objects()
 
2941
{
 
2942
  MYSQL_LOG::init_pthread_objects();
 
2943
  mysql_mutex_init(m_key_LOCK_index, &LOCK_index, MY_MUTEX_INIT_SLOW);
 
2944
  mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION);
 
2945
  mysql_cond_init(m_key_update_cond, &update_cond, 0);
 
2946
  mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0);
 
2947
}
 
2948
 
 
2949
 
 
2950
bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
 
2951
                                    const char *log_name, bool need_mutex)
 
2952
{
 
2953
  File index_file_nr= -1;
 
2954
  DBUG_ASSERT(!my_b_inited(&index_file));
 
2955
 
 
2956
  /*
 
2957
    First open of this class instance
 
2958
    Create an index file that will hold all file names uses for logging.
 
2959
    Add new entries to the end of it.
 
2960
  */
 
2961
  myf opt= MY_UNPACK_FILENAME;
 
2962
  if (!index_file_name_arg)
 
2963
  {
 
2964
    index_file_name_arg= log_name;    // Use same basename for index file
 
2965
    opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT;
 
2966
  }
 
2967
  fn_format(index_file_name, index_file_name_arg, mysql_data_home,
 
2968
            ".index", opt);
 
2969
  if ((index_file_nr= mysql_file_open(m_key_file_log_index,
 
2970
                                      index_file_name,
 
2971
                                      O_RDWR | O_CREAT | O_BINARY,
 
2972
                                      MYF(MY_WME))) < 0 ||
 
2973
       mysql_file_sync(index_file_nr, MYF(MY_WME)) ||
 
2974
       init_io_cache(&index_file, index_file_nr,
 
2975
                     IO_SIZE, WRITE_CACHE,
 
2976
                     mysql_file_seek(index_file_nr, 0L, MY_SEEK_END, MYF(0)),
 
2977
                                     0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
 
2978
      DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
 
2979
  {
 
2980
    /*
 
2981
      TODO: all operations creating/deleting the index file or a log, should
 
2982
      call my_sync_dir() or my_sync_dir_by_file() to be durable.
 
2983
      TODO: file creation should be done with mysql_file_create()
 
2984
      not mysql_file_open().
 
2985
    */
 
2986
    if (index_file_nr >= 0)
 
2987
      mysql_file_close(index_file_nr, MYF(0));
 
2988
    return TRUE;
 
2989
  }
 
2990
 
 
2991
#ifdef HAVE_REPLICATION
 
2992
  /*
 
2993
    Sync the index by purging any binary log file that is not registered.
 
2994
    In other words, either purge binary log files that were removed from
 
2995
    the index but not purged from the file system due to a crash or purge
 
2996
    any binary log file that was created but not register in the index
 
2997
    due to a crash.
 
2998
  */
 
2999
 
 
3000
  if (set_purge_index_file_name(index_file_name_arg) ||
 
3001
      open_purge_index_file(FALSE) ||
 
3002
      purge_index_entry(NULL, NULL, need_mutex) ||
 
3003
      close_purge_index_file() ||
 
3004
      DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0))
 
3005
  {
 
3006
    sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
 
3007
                    "file.");
 
3008
    return TRUE;
 
3009
  }
 
3010
#endif
 
3011
 
 
3012
  return FALSE;
 
3013
}
 
3014
 
 
3015
 
 
3016
/**
 
3017
  Open a (new) binlog file.
 
3018
 
 
3019
  - Open the log file and the index file. Register the new
 
3020
  file name in it
 
3021
  - When calling this when the file is in use, you must have a locks
 
3022
  on LOCK_log and LOCK_index.
 
3023
 
 
3024
  @retval
 
3025
    0   ok
 
3026
  @retval
 
3027
    1   error
 
3028
*/
 
3029
 
 
3030
bool MYSQL_BIN_LOG::open(const char *log_name,
 
3031
                         enum_log_type log_type_arg,
 
3032
                         const char *new_name,
 
3033
                         enum cache_type io_cache_type_arg,
 
3034
                         bool no_auto_events_arg,
 
3035
                         ulong max_size_arg,
 
3036
                         bool null_created_arg,
 
3037
                         bool need_mutex)
 
3038
{
 
3039
  File file= -1;
 
3040
 
 
3041
  DBUG_ENTER("MYSQL_BIN_LOG::open");
 
3042
  DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
 
3043
 
 
3044
  if (init_and_set_log_file_name(log_name, new_name, log_type_arg,
 
3045
                                 io_cache_type_arg))
 
3046
  {
 
3047
    sql_print_error("MSYQL_BIN_LOG::open failed to generate new file name.");
 
3048
    DBUG_RETURN(1);
 
3049
  }
 
3050
 
 
3051
#ifdef HAVE_REPLICATION
 
3052
  if (open_purge_index_file(TRUE) ||
 
3053
      register_create_index_entry(log_file_name) ||
 
3054
      sync_purge_index_file() ||
 
3055
      DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0))
 
3056
  {
 
3057
    /**
 
3058
        TODO: although this was introduced to appease valgrind
 
3059
              when injecting emulated faults using fault_injection_registering_index
 
3060
              it may be good to consider what actually happens when
 
3061
              open_purge_index_file succeeds but register or sync fails.
 
3062
 
 
3063
              Perhaps we might need the code below in MYSQL_LOG_BIN::cleanup
 
3064
              for "real life" purposes as well? 
 
3065
     */
 
3066
    DBUG_EXECUTE_IF("fault_injection_registering_index", {
 
3067
      if (my_b_inited(&purge_index_file))
 
3068
      {
 
3069
        end_io_cache(&purge_index_file);
 
3070
        my_close(purge_index_file.file, MYF(0));
 
3071
      }
 
3072
    });
 
3073
 
 
3074
    sql_print_error("MSYQL_BIN_LOG::open failed to sync the index file.");
 
3075
    DBUG_RETURN(1);
 
3076
  }
 
3077
  DBUG_EXECUTE_IF("crash_create_non_critical_before_update_index", DBUG_SUICIDE(););
 
3078
#endif
 
3079
 
 
3080
  write_error= 0;
 
3081
 
 
3082
  /* open the main log file */
 
3083
  if (MYSQL_LOG::open(
 
3084
#ifdef HAVE_PSI_INTERFACE
 
3085
                      m_key_file_log,
 
3086
#endif
 
3087
                      log_name, log_type_arg, new_name, io_cache_type_arg))
 
3088
  {
 
3089
#ifdef HAVE_REPLICATION
 
3090
    close_purge_index_file();
 
3091
#endif
 
3092
    DBUG_RETURN(1);                            /* all warnings issued */
 
3093
  }
 
3094
 
 
3095
  init(no_auto_events_arg, max_size_arg);
 
3096
 
 
3097
  open_count++;
 
3098
 
 
3099
  DBUG_ASSERT(log_type == LOG_BIN);
 
3100
 
 
3101
  {
 
3102
    bool write_file_name_to_index_file=0;
 
3103
 
 
3104
    if (!my_b_filelength(&log_file))
 
3105
    {
 
3106
      /*
 
3107
        The binary log file was empty (probably newly created)
 
3108
        This is the normal case and happens when the user doesn't specify
 
3109
        an extension for the binary log files.
 
3110
        In this case we write a standard header to it.
 
3111
      */
 
3112
      if (my_b_safe_write(&log_file, BINLOG_MAGIC,
 
3113
                          BIN_LOG_HEADER_SIZE))
 
3114
        goto err;
 
3115
      bytes_written+= BIN_LOG_HEADER_SIZE;
 
3116
      write_file_name_to_index_file= 1;
 
3117
    }
 
3118
 
 
3119
    if (need_start_event && !no_auto_events)
 
3120
    {
 
3121
      /*
 
3122
        In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
 
3123
        even if this is not the very first binlog.
 
3124
      */
 
3125
      Format_description_log_event s(BINLOG_VERSION);
 
3126
      /*
 
3127
        don't set LOG_EVENT_BINLOG_IN_USE_F for SEQ_READ_APPEND io_cache
 
3128
        as we won't be able to reset it later
 
3129
      */
 
3130
      if (io_cache_type == WRITE_CACHE)
 
3131
        s.flags |= LOG_EVENT_BINLOG_IN_USE_F;
 
3132
      s.checksum_alg= is_relay_log ?
 
3133
        /* relay-log */
 
3134
        /* inherit master's A descriptor if one has been received */
 
3135
        (relay_log_checksum_alg= 
 
3136
         (relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF) ?
 
3137
         relay_log_checksum_alg :
 
3138
         /* otherwise use slave's local preference of RL events verification */
 
3139
         (opt_slave_sql_verify_checksum == 0) ?
 
3140
         (uint8) BINLOG_CHECKSUM_ALG_OFF : (uint8) binlog_checksum_options):
 
3141
        /* binlog */
 
3142
        (uint8) binlog_checksum_options;
 
3143
      DBUG_ASSERT(s.checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
 
3144
      if (!s.is_valid())
 
3145
        goto err;
 
3146
      s.dont_set_created= null_created_arg;
 
3147
      if (s.write(&log_file))
 
3148
        goto err;
 
3149
      bytes_written+= s.data_written;
 
3150
    }
 
3151
    if (description_event_for_queue &&
 
3152
        description_event_for_queue->binlog_version>=4)
 
3153
    {
 
3154
      /*
 
3155
        This is a relay log written to by the I/O slave thread.
 
3156
        Write the event so that others can later know the format of this relay
 
3157
        log.
 
3158
        Note that this event is very close to the original event from the
 
3159
        master (it has binlog version of the master, event types of the
 
3160
        master), so this is suitable to parse the next relay log's event. It
 
3161
        has been produced by
 
3162
        Format_description_log_event::Format_description_log_event(char* buf,).
 
3163
        Why don't we want to write the description_event_for_queue if this
 
3164
        event is for format<4 (3.23 or 4.x): this is because in that case, the
 
3165
        description_event_for_queue describes the data received from the
 
3166
        master, but not the data written to the relay log (*conversion*),
 
3167
        which is in format 4 (slave's).
 
3168
      */
 
3169
      /*
 
3170
        Set 'created' to 0, so that in next relay logs this event does not
 
3171
        trigger cleaning actions on the slave in
 
3172
        Format_description_log_event::apply_event_impl().
 
3173
      */
 
3174
      description_event_for_queue->created= 0;
 
3175
      /* Don't set log_pos in event header */
 
3176
      description_event_for_queue->set_artificial_event();
 
3177
 
 
3178
      if (description_event_for_queue->write(&log_file))
 
3179
        goto err;
 
3180
      bytes_written+= description_event_for_queue->data_written;
 
3181
    }
 
3182
    if (flush_io_cache(&log_file) ||
 
3183
        mysql_file_sync(log_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
 
3184
      goto err;
 
3185
    mysql_mutex_lock(&LOCK_commit_ordered);
 
3186
    strmake_buf(last_commit_pos_file, log_file_name);
 
3187
    last_commit_pos_offset= my_b_tell(&log_file);
 
3188
    mysql_mutex_unlock(&LOCK_commit_ordered);
 
3189
 
 
3190
    if (write_file_name_to_index_file)
 
3191
    {
 
3192
#ifdef HAVE_REPLICATION
 
3193
#ifdef ENABLED_DEBUG_SYNC
 
3194
      if (current_thd)
 
3195
        DEBUG_SYNC(current_thd, "binlog_open_before_update_index");
 
3196
#endif
 
3197
      DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_SUICIDE(););
 
3198
#endif
 
3199
 
 
3200
      DBUG_ASSERT(my_b_inited(&index_file) != 0);
 
3201
      reinit_io_cache(&index_file, WRITE_CACHE,
 
3202
                      my_b_filelength(&index_file), 0, 0);
 
3203
      /*
 
3204
        As this is a new log file, we write the file name to the index
 
3205
        file. As every time we write to the index file, we sync it.
 
3206
      */
 
3207
      if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) ||
 
3208
          my_b_write(&index_file, (uchar*) log_file_name,
 
3209
                     strlen(log_file_name)) ||
 
3210
          my_b_write(&index_file, (uchar*) "\n", 1) ||
 
3211
          flush_io_cache(&index_file) ||
 
3212
          mysql_file_sync(index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
 
3213
        goto err;
 
3214
 
 
3215
#ifdef HAVE_REPLICATION
 
3216
      DBUG_EXECUTE_IF("crash_create_after_update_index", DBUG_SUICIDE(););
 
3217
#endif
 
3218
    }
 
3219
  }
 
3220
  log_state= LOG_OPENED;
 
3221
 
 
3222
#ifdef HAVE_REPLICATION
 
3223
  close_purge_index_file();
 
3224
#endif
 
3225
 
 
3226
  DBUG_RETURN(0);
 
3227
 
 
3228
err:
 
3229
#ifdef HAVE_REPLICATION
 
3230
  if (is_inited_purge_index_file())
 
3231
    purge_index_entry(NULL, NULL, need_mutex);
 
3232
  close_purge_index_file();
 
3233
#endif
 
3234
  sql_print_error("Could not use %s for logging (error %d). \
 
3235
Turning logging off for the whole duration of the MySQL server process. \
 
3236
To turn it on again: fix the cause, \
 
3237
shutdown the MySQL server and restart it.", name, errno);
 
3238
  if (file >= 0)
 
3239
    mysql_file_close(file, MYF(0));
 
3240
  close(LOG_CLOSE_INDEX);
 
3241
  DBUG_RETURN(1);
 
3242
}
 
3243
 
 
3244
 
 
3245
int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
 
3246
{
 
3247
  mysql_mutex_lock(&LOCK_log);
 
3248
  int ret = raw_get_current_log(linfo);
 
3249
  mysql_mutex_unlock(&LOCK_log);
 
3250
  return ret;
 
3251
}
 
3252
 
 
3253
int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo)
 
3254
{
 
3255
  strmake_buf(linfo->log_file_name, log_file_name);
 
3256
  linfo->pos = my_b_tell(&log_file);
 
3257
  return 0;
 
3258
}
 
3259
 
 
3260
/**
 
3261
  Move all data up in a file in an filename index file.
 
3262
 
 
3263
    We do the copy outside of the IO_CACHE as the cache buffers would just
 
3264
    make things slower and more complicated.
 
3265
    In most cases the copy loop should only do one read.
 
3266
 
 
3267
  @param index_file                     File to move
 
3268
  @param offset                 Move everything from here to beginning
 
3269
 
 
3270
  @note
 
3271
    File will be truncated to be 'offset' shorter or filled up with newlines
 
3272
 
 
3273
  @retval
 
3274
    0   ok
 
3275
*/
 
3276
 
 
3277
#ifdef HAVE_REPLICATION
 
3278
 
 
3279
static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
 
3280
{
 
3281
  int bytes_read;
 
3282
  my_off_t init_offset= offset;
 
3283
  File file= index_file->file;
 
3284
  uchar io_buf[IO_SIZE*2];
 
3285
  DBUG_ENTER("copy_up_file_and_fill");
 
3286
 
 
3287
  for (;; offset+= bytes_read)
 
3288
  {
 
3289
    mysql_file_seek(file, offset, MY_SEEK_SET, MYF(0));
 
3290
    if ((bytes_read= (int) mysql_file_read(file, io_buf, sizeof(io_buf),
 
3291
                                           MYF(MY_WME)))
 
3292
        < 0)
 
3293
      goto err;
 
3294
    if (!bytes_read)
 
3295
      break;                                    // end of file
 
3296
    mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
 
3297
    if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
 
3298
      goto err;
 
3299
  }
 
3300
  /* The following will either truncate the file or fill the end with \n' */
 
3301
  if (mysql_file_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
 
3302
      mysql_file_sync(file, MYF(MY_WME|MY_SYNC_FILESIZE)))
 
3303
    goto err;
 
3304
 
 
3305
  /* Reset data in old index cache */
 
3306
  reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1);
 
3307
  DBUG_RETURN(0);
 
3308
 
 
3309
err:
 
3310
  DBUG_RETURN(1);
 
3311
}
 
3312
 
 
3313
#endif /* HAVE_REPLICATION */
 
3314
 
 
3315
/**
 
3316
  Find the position in the log-index-file for the given log name.
 
3317
 
 
3318
  @param linfo          Store here the found log file name and position to
 
3319
                       the NEXT log file name in the index file.
 
3320
  @param log_name       Filename to find in the index file.
 
3321
                       Is a null pointer if we want to read the first entry
 
3322
  @param need_lock      Set this to 1 if the parent doesn't already have a
 
3323
                       lock on LOCK_index
 
3324
 
 
3325
  @note
 
3326
    On systems without the truncate function the file will end with one or
 
3327
    more empty lines.  These will be ignored when reading the file.
 
3328
 
 
3329
  @retval
 
3330
    0                   ok
 
3331
  @retval
 
3332
    LOG_INFO_EOF                End of log-index-file found
 
3333
  @retval
 
3334
    LOG_INFO_IO         Got IO error while reading file
 
3335
*/
 
3336
 
 
3337
int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
 
3338
                            bool need_lock)
 
3339
{
 
3340
  int error= 0;
 
3341
  char *full_fname= linfo->log_file_name;
 
3342
  char full_log_name[FN_REFLEN], fname[FN_REFLEN];
 
3343
  uint log_name_len= 0, fname_len= 0;
 
3344
  DBUG_ENTER("find_log_pos");
 
3345
  full_log_name[0]= full_fname[0]= 0;
 
3346
 
 
3347
  /*
 
3348
    Mutex needed because we need to make sure the file pointer does not
 
3349
    move from under our feet
 
3350
  */
 
3351
  if (need_lock)
 
3352
    mysql_mutex_lock(&LOCK_index);
 
3353
  mysql_mutex_assert_owner(&LOCK_index);
 
3354
 
 
3355
  // extend relative paths for log_name to be searched
 
3356
  if (log_name)
 
3357
  {
 
3358
    if(normalize_binlog_name(full_log_name, log_name, is_relay_log))
 
3359
    {
 
3360
      error= LOG_INFO_EOF;
 
3361
      goto end;
 
3362
    }
 
3363
  }
 
3364
 
 
3365
  log_name_len= log_name ? (uint) strlen(full_log_name) : 0;
 
3366
  DBUG_PRINT("enter", ("log_name: %s, full_log_name: %s", 
 
3367
                       log_name ? log_name : "NULL", full_log_name));
 
3368
 
 
3369
  /* As the file is flushed, we can't get an error here */
 
3370
  (void) reinit_io_cache(&index_file, READ_CACHE, (my_off_t) 0, 0, 0);
 
3371
 
 
3372
  for (;;)
 
3373
  {
 
3374
    uint length;
 
3375
    my_off_t offset= my_b_tell(&index_file);
 
3376
 
 
3377
    DBUG_EXECUTE_IF("simulate_find_log_pos_error",
 
3378
                    error=  LOG_INFO_EOF; break;);
 
3379
    /* If we get 0 or 1 characters, this is the end of the file */
 
3380
    if ((length= my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
 
3381
    {
 
3382
      /* Did not find the given entry; Return not found or error */
 
3383
      error= !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
 
3384
      break;
 
3385
    }
 
3386
 
 
3387
    // extend relative paths and match against full path
 
3388
    if (normalize_binlog_name(full_fname, fname, is_relay_log))
 
3389
    {
 
3390
      error= LOG_INFO_EOF;
 
3391
      break;
 
3392
    }
 
3393
    fname_len= (uint) strlen(full_fname);
 
3394
 
 
3395
    // if the log entry matches, null string matching anything
 
3396
    if (!log_name ||
 
3397
        (log_name_len == fname_len-1 && full_fname[log_name_len] == '\n' &&
 
3398
         !memcmp(full_fname, full_log_name, log_name_len)))
 
3399
    {
 
3400
      DBUG_PRINT("info", ("Found log file entry"));
 
3401
      full_fname[fname_len-1]= 0;                       // remove last \n
 
3402
      linfo->index_file_start_offset= offset;
 
3403
      linfo->index_file_offset = my_b_tell(&index_file);
 
3404
      break;
 
3405
    }
 
3406
  }
 
3407
 
 
3408
end:
 
3409
  if (need_lock)
 
3410
    mysql_mutex_unlock(&LOCK_index);
 
3411
  DBUG_RETURN(error);
 
3412
}
 
3413
 
 
3414
 
 
3415
/**
 
3416
  Find the position in the log-index-file for the given log name.
 
3417
 
 
3418
  @param
 
3419
    linfo               Store here the next log file name and position to
 
3420
                        the file name after that.
 
3421
  @param
 
3422
    need_lock           Set this to 1 if the parent doesn't already have a
 
3423
                        lock on LOCK_index
 
3424
 
 
3425
  @note
 
3426
    - Before calling this function, one has to call find_log_pos()
 
3427
    to set up 'linfo'
 
3428
    - Mutex needed because we need to make sure the file pointer does not move
 
3429
    from under our feet
 
3430
 
 
3431
  @retval
 
3432
    0                   ok
 
3433
  @retval
 
3434
    LOG_INFO_EOF                End of log-index-file found
 
3435
  @retval
 
3436
    LOG_INFO_IO         Got IO error while reading file
 
3437
*/
 
3438
 
 
3439
int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
 
3440
{
 
3441
  int error= 0;
 
3442
  uint length;
 
3443
  char fname[FN_REFLEN];
 
3444
  char *full_fname= linfo->log_file_name;
 
3445
 
 
3446
  if (need_lock)
 
3447
    mysql_mutex_lock(&LOCK_index);
 
3448
  mysql_mutex_assert_owner(&LOCK_index);
 
3449
 
 
3450
  /* As the file is flushed, we can't get an error here */
 
3451
  (void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset, 0,
 
3452
                         0);
 
3453
 
 
3454
  linfo->index_file_start_offset= linfo->index_file_offset;
 
3455
  if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
 
3456
  {
 
3457
    error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
 
3458
    goto err;
 
3459
  }
 
3460
 
 
3461
  if (fname[0] != 0)
 
3462
  {
 
3463
    if(normalize_binlog_name(full_fname, fname, is_relay_log))
 
3464
    {
 
3465
      error= LOG_INFO_EOF;
 
3466
      goto err;
 
3467
    }
 
3468
    length= strlen(full_fname);
 
3469
  }
 
3470
 
 
3471
  full_fname[length-1]= 0;                      // kill \n
 
3472
  linfo->index_file_offset= my_b_tell(&index_file);
 
3473
 
 
3474
err:
 
3475
  if (need_lock)
 
3476
    mysql_mutex_unlock(&LOCK_index);
 
3477
  return error;
 
3478
}
 
3479
 
 
3480
 
 
3481
/**
 
3482
  Delete all logs refered to in the index file.
 
3483
  Start writing to a new log file.
 
3484
 
 
3485
  The new index file will only contain this file.
 
3486
 
 
3487
  @param thd            Thread
 
3488
 
 
3489
  @note
 
3490
    If not called from slave thread, write start event to new log
 
3491
 
 
3492
  @retval
 
3493
    0   ok
 
3494
  @retval
 
3495
    1   error
 
3496
*/
 
3497
 
 
3498
bool MYSQL_BIN_LOG::reset_logs(THD* thd)
 
3499
{
 
3500
  LOG_INFO linfo;
 
3501
  bool error=0;
 
3502
  int err;
 
3503
  const char* save_name;
 
3504
  DBUG_ENTER("reset_logs");
 
3505
 
 
3506
  ha_reset_logs(thd);
 
3507
  /*
 
3508
    We need to get both locks to be sure that no one is trying to
 
3509
    write to the index log file.
 
3510
  */
 
3511
  mysql_mutex_lock(&LOCK_log);
 
3512
  mysql_mutex_lock(&LOCK_index);
 
3513
 
 
3514
  /*
 
3515
    The following mutex is needed to ensure that no threads call
 
3516
    'delete thd' as we would then risk missing a 'rollback' from this
 
3517
    thread. If the transaction involved MyISAM tables, it should go
 
3518
    into binlog even on rollback.
 
3519
  */
 
3520
  mysql_mutex_lock(&LOCK_thread_count);
 
3521
 
 
3522
  /* Save variables so that we can reopen the log */
 
3523
  save_name=name;
 
3524
  name=0;                                       // Protect against free
 
3525
  close(LOG_CLOSE_TO_BE_OPENED);
 
3526
 
 
3527
  /*
 
3528
    First delete all old log files and then update the index file.
 
3529
    As we first delete the log files and do not use sort of logging,
 
3530
    a crash may lead to an inconsistent state where the index has
 
3531
    references to non-existent files.
 
3532
 
 
3533
    We need to invert the steps and use the purge_index_file methods
 
3534
    in order to make the operation safe.
 
3535
  */
 
3536
 
 
3537
  if ((err= find_log_pos(&linfo, NullS, 0)) != 0)
 
3538
  {
 
3539
    uint errcode= purge_log_get_error_code(err);
 
3540
    sql_print_error("Failed to locate old binlog or relay log files");
 
3541
    my_message(errcode, ER(errcode), MYF(0));
 
3542
    error= 1;
 
3543
    goto err;
 
3544
  }
 
3545
 
 
3546
  for (;;)
 
3547
  {
 
3548
    if ((error= my_delete(linfo.log_file_name, MYF(0))) != 0)
 
3549
    {
 
3550
      if (my_errno == ENOENT) 
 
3551
      {
 
3552
        push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
3553
                            ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
 
3554
                            linfo.log_file_name);
 
3555
        sql_print_information("Failed to delete file '%s'",
 
3556
                              linfo.log_file_name);
 
3557
        my_errno= 0;
 
3558
        error= 0;
 
3559
      }
 
3560
      else
 
3561
      {
 
3562
        push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
3563
                            ER_BINLOG_PURGE_FATAL_ERR,
 
3564
                            "a problem with deleting %s; "
 
3565
                            "consider examining correspondence "
 
3566
                            "of your binlog index file "
 
3567
                            "to the actual binlog files",
 
3568
                            linfo.log_file_name);
 
3569
        error= 1;
 
3570
        goto err;
 
3571
      }
 
3572
    }
 
3573
    if (find_next_log(&linfo, 0))
 
3574
      break;
 
3575
  }
 
3576
 
 
3577
  /* Start logging with a new file */
 
3578
  close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED);
 
3579
  if ((error= my_delete(index_file_name, MYF(0))))      // Reset (open will update)
 
3580
  {
 
3581
    if (my_errno == ENOENT) 
 
3582
    {
 
3583
      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
3584
                          ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
 
3585
                          index_file_name);
 
3586
      sql_print_information("Failed to delete file '%s'",
 
3587
                            index_file_name);
 
3588
      my_errno= 0;
 
3589
      error= 0;
 
3590
    }
 
3591
    else
 
3592
    {
 
3593
      push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
3594
                          ER_BINLOG_PURGE_FATAL_ERR,
 
3595
                          "a problem with deleting %s; "
 
3596
                          "consider examining correspondence "
 
3597
                          "of your binlog index file "
 
3598
                          "to the actual binlog files",
 
3599
                          index_file_name);
 
3600
      error= 1;
 
3601
      goto err;
 
3602
    }
 
3603
  }
 
3604
  if (!thd->slave_thread)
 
3605
    need_start_event=1;
 
3606
  if (!open_index_file(index_file_name, 0, FALSE))
 
3607
    if ((error= open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE)))
 
3608
      goto err;
 
3609
  my_free((void *) save_name);
 
3610
 
 
3611
err:
 
3612
  if (error == 1)
 
3613
    name= const_cast<char*>(save_name);
 
3614
  mysql_mutex_unlock(&LOCK_thread_count);
 
3615
  mysql_mutex_unlock(&LOCK_index);
 
3616
  mysql_mutex_unlock(&LOCK_log);
 
3617
  DBUG_RETURN(error);
 
3618
}
 
3619
 
 
3620
 
 
3621
/**
 
3622
  Delete relay log files prior to rli->group_relay_log_name
 
3623
  (i.e. all logs which are not involved in a non-finished group
 
3624
  (transaction)), remove them from the index file and start on next
 
3625
  relay log.
 
3626
 
 
3627
  IMPLEMENTATION
 
3628
  - Protects index file with LOCK_index
 
3629
  - Delete relevant relay log files
 
3630
  - Copy all file names after these ones to the front of the index file
 
3631
  - If the OS has truncate, truncate the file, else fill it with \n'
 
3632
  - Read the next file name from the index file and store in rli->linfo
 
3633
 
 
3634
  @param rli           Relay log information
 
3635
  @param included     If false, all relay logs that are strictly before
 
3636
                      rli->group_relay_log_name are deleted ; if true, the
 
3637
                      latter is deleted too (i.e. all relay logs
 
3638
                      read by the SQL slave thread are deleted).
 
3639
 
 
3640
  @note
 
3641
    - This is only called from the slave-execute thread when it has read
 
3642
    all commands from a relay log and want to switch to a new relay log.
 
3643
    - When this happens, we can be in an active transaction as
 
3644
    a transaction can span over two relay logs
 
3645
    (although it is always written as a single block to the master's binary
 
3646
    log, hence cannot span over two master's binary logs).
 
3647
 
 
3648
  @retval
 
3649
    0                   ok
 
3650
  @retval
 
3651
    LOG_INFO_EOF                End of log-index-file found
 
3652
  @retval
 
3653
    LOG_INFO_SEEK       Could not allocate IO cache
 
3654
  @retval
 
3655
    LOG_INFO_IO         Got IO error while reading file
 
3656
*/
 
3657
 
 
3658
#ifdef HAVE_REPLICATION
 
3659
 
 
3660
int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
 
3661
{
 
3662
  int error;
 
3663
  char *to_purge_if_included= NULL;
 
3664
  DBUG_ENTER("purge_first_log");
 
3665
 
 
3666
  DBUG_ASSERT(is_open());
 
3667
  DBUG_ASSERT(rli->slave_running == 1);
 
3668
  DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
 
3669
 
 
3670
  mysql_mutex_lock(&LOCK_index);
 
3671
  to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(0));
 
3672
 
 
3673
  /*
 
3674
    Read the next log file name from the index file and pass it back to
 
3675
    the caller.
 
3676
  */
 
3677
  if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) || 
 
3678
     (error=find_next_log(&rli->linfo, 0)))
 
3679
  {
 
3680
    char buff[22];
 
3681
    sql_print_error("next log error: %d  offset: %s  log: %s included: %d",
 
3682
                    error,
 
3683
                    llstr(rli->linfo.index_file_offset,buff),
 
3684
                    rli->event_relay_log_name,
 
3685
                    included);
 
3686
    goto err;
 
3687
  }
 
3688
 
 
3689
  /*
 
3690
    Reset rli's coordinates to the current log.
 
3691
  */
 
3692
  rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
 
3693
  strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
 
3694
 
 
3695
  /*
 
3696
    If we removed the rli->group_relay_log_name file,
 
3697
    we must update the rli->group* coordinates, otherwise do not touch it as the
 
3698
    group's execution is not finished (e.g. COMMIT not executed)
 
3699
  */
 
3700
  if (included)
 
3701
  {
 
3702
    rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE;
 
3703
    strmake_buf(rli->group_relay_log_name,rli->linfo.log_file_name);
 
3704
    rli->notify_group_relay_log_name_update();
 
3705
  }
 
3706
 
 
3707
  /* Store where we are in the new file for the execution thread */
 
3708
  flush_relay_log_info(rli);
 
3709
 
 
3710
  DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_SUICIDE(););
 
3711
 
 
3712
  mysql_mutex_lock(&rli->log_space_lock);
 
3713
  rli->relay_log.purge_logs(to_purge_if_included, included,
 
3714
                            0, 0, &rli->log_space_total);
 
3715
  mysql_mutex_unlock(&rli->log_space_lock);
 
3716
 
 
3717
  /*
 
3718
    Ok to broadcast after the critical region as there is no risk of
 
3719
    the mutex being destroyed by this thread later - this helps save
 
3720
    context switches
 
3721
  */
 
3722
  mysql_cond_broadcast(&rli->log_space_cond);
 
3723
 
 
3724
  /*
 
3725
   * Need to update the log pos because purge logs has been called 
 
3726
   * after fetching initially the log pos at the begining of the method.
 
3727
   */
 
3728
  if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)))
 
3729
  {
 
3730
    char buff[22];
 
3731
    sql_print_error("next log error: %d  offset: %s  log: %s included: %d",
 
3732
                    error,
 
3733
                    llstr(rli->linfo.index_file_offset,buff),
 
3734
                    rli->group_relay_log_name,
 
3735
                    included);
 
3736
    goto err;
 
3737
  }
 
3738
 
 
3739
  /* If included was passed, rli->linfo should be the first entry. */
 
3740
  DBUG_ASSERT(!included || rli->linfo.index_file_start_offset == 0);
 
3741
 
 
3742
err:
 
3743
  my_free(to_purge_if_included);
 
3744
  mysql_mutex_unlock(&LOCK_index);
 
3745
  DBUG_RETURN(error);
 
3746
}
 
3747
 
 
3748
/**
 
3749
  Update log index_file.
 
3750
*/
 
3751
 
 
3752
int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
 
3753
{
 
3754
  if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset))
 
3755
    return LOG_INFO_IO;
 
3756
 
 
3757
  // now update offsets in index file for running threads
 
3758
  if (need_update_threads)
 
3759
    adjust_linfo_offsets(log_info->index_file_start_offset);
 
3760
  return 0;
 
3761
}
 
3762
 
 
3763
/**
 
3764
  Remove all logs before the given log from disk and from the index file.
 
3765
 
 
3766
  @param to_log       Delete all log file name before this file.
 
3767
  @param included            If true, to_log is deleted too.
 
3768
  @param need_mutex
 
3769
  @param need_update_threads If we want to update the log coordinates of
 
3770
                             all threads. False for relay logs, true otherwise.
 
3771
  @param freed_log_space     If not null, decrement this variable of
 
3772
                             the amount of log space freed
 
3773
 
 
3774
  @note
 
3775
    If any of the logs before the deleted one is in use,
 
3776
    only purge logs up to this one.
 
3777
 
 
3778
  @retval
 
3779
    0                   ok
 
3780
  @retval
 
3781
    LOG_INFO_EOF                to_log not found
 
3782
    LOG_INFO_EMFILE             too many files opened
 
3783
    LOG_INFO_FATAL              if any other than ENOENT error from
 
3784
                                mysql_file_stat() or mysql_file_delete()
 
3785
*/
 
3786
 
 
3787
int MYSQL_BIN_LOG::purge_logs(const char *to_log, 
 
3788
                          bool included,
 
3789
                          bool need_mutex, 
 
3790
                          bool need_update_threads, 
 
3791
                          ulonglong *decrease_log_space)
 
3792
{
 
3793
  int error= 0;
 
3794
  bool exit_loop= 0;
 
3795
  LOG_INFO log_info;
 
3796
  THD *thd= current_thd;
 
3797
  DBUG_ENTER("purge_logs");
 
3798
  DBUG_PRINT("info",("to_log= %s",to_log));
 
3799
 
 
3800
  if (need_mutex)
 
3801
    mysql_mutex_lock(&LOCK_index);
 
3802
  if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) 
 
3803
  {
 
3804
    sql_print_error("MYSQL_BIN_LOG::purge_logs was called with file %s not "
 
3805
                    "listed in the index.", to_log);
 
3806
    goto err;
 
3807
  }
 
3808
 
 
3809
  if ((error= open_purge_index_file(TRUE)))
 
3810
  {
 
3811
    sql_print_error("MYSQL_BIN_LOG::purge_logs failed to sync the index file.");
 
3812
    goto err;
 
3813
  }
 
3814
 
 
3815
  /*
 
3816
    File name exists in index file; delete until we find this file
 
3817
    or a file that is used.
 
3818
  */
 
3819
  if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
 
3820
    goto err;
 
3821
  while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
 
3822
         !is_active(log_info.log_file_name) &&
 
3823
         !log_in_use(log_info.log_file_name))
 
3824
  {
 
3825
    if ((error= register_purge_index_entry(log_info.log_file_name)))
 
3826
    {
 
3827
      sql_print_error("MYSQL_BIN_LOG::purge_logs failed to copy %s to register file.",
 
3828
                      log_info.log_file_name);
 
3829
      goto err;
 
3830
    }
 
3831
 
 
3832
    if (find_next_log(&log_info, 0) || exit_loop)
 
3833
      break;
 
3834
  }
 
3835
 
 
3836
  DBUG_EXECUTE_IF("crash_purge_before_update_index", DBUG_SUICIDE(););
 
3837
 
 
3838
  if ((error= sync_purge_index_file()))
 
3839
  {
 
3840
    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to flush register file.");
 
3841
    goto err;
 
3842
  }
 
3843
 
 
3844
  /* We know how many files to delete. Update index file. */
 
3845
  if ((error=update_log_index(&log_info, need_update_threads)))
 
3846
  {
 
3847
    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to update the index file");
 
3848
    goto err;
 
3849
  }
 
3850
 
 
3851
  DBUG_EXECUTE_IF("crash_purge_critical_after_update_index", DBUG_SUICIDE(););
 
3852
 
 
3853
err:
 
3854
  /* Read each entry from purge_index_file and delete the file. */
 
3855
  if (is_inited_purge_index_file() &&
 
3856
      (error= purge_index_entry(thd, decrease_log_space, FALSE)))
 
3857
    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to process registered files"
 
3858
                    " that would be purged.");
 
3859
  close_purge_index_file();
 
3860
 
 
3861
  DBUG_EXECUTE_IF("crash_purge_non_critical_after_update_index", DBUG_SUICIDE(););
 
3862
 
 
3863
  if (need_mutex)
 
3864
    mysql_mutex_unlock(&LOCK_index);
 
3865
  DBUG_RETURN(error);
 
3866
}
 
3867
 
 
3868
int MYSQL_BIN_LOG::set_purge_index_file_name(const char *base_file_name)
 
3869
{
 
3870
  int error= 0;
 
3871
  DBUG_ENTER("MYSQL_BIN_LOG::set_purge_index_file_name");
 
3872
  if (fn_format(purge_index_file_name, base_file_name, mysql_data_home,
 
3873
                ".~rec~", MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH |
 
3874
                              MY_REPLACE_EXT)) == NULL)
 
3875
  {
 
3876
    error= 1;
 
3877
    sql_print_error("MYSQL_BIN_LOG::set_purge_index_file_name failed to set "
 
3878
                      "file name.");
 
3879
  }
 
3880
  DBUG_RETURN(error);
 
3881
}
 
3882
 
 
3883
int MYSQL_BIN_LOG::open_purge_index_file(bool destroy)
 
3884
{
 
3885
  int error= 0;
 
3886
  File file= -1;
 
3887
 
 
3888
  DBUG_ENTER("MYSQL_BIN_LOG::open_purge_index_file");
 
3889
 
 
3890
  if (destroy)
 
3891
    close_purge_index_file();
 
3892
 
 
3893
  if (!my_b_inited(&purge_index_file))
 
3894
  {
 
3895
    if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY,
 
3896
                       MYF(MY_WME | ME_WAITTANG))) < 0  ||
 
3897
        init_io_cache(&purge_index_file, file, IO_SIZE,
 
3898
                      (destroy ? WRITE_CACHE : READ_CACHE),
 
3899
                      0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
 
3900
    {
 
3901
      error= 1;
 
3902
      sql_print_error("MYSQL_BIN_LOG::open_purge_index_file failed to open register "
 
3903
                      " file.");
 
3904
    }
 
3905
  }
 
3906
  DBUG_RETURN(error);
 
3907
}
 
3908
 
 
3909
int MYSQL_BIN_LOG::close_purge_index_file()
 
3910
{
 
3911
  int error= 0;
 
3912
 
 
3913
  DBUG_ENTER("MYSQL_BIN_LOG::close_purge_index_file");
 
3914
 
 
3915
  if (my_b_inited(&purge_index_file))
 
3916
  {
 
3917
    end_io_cache(&purge_index_file);
 
3918
    error= my_close(purge_index_file.file, MYF(0));
 
3919
  }
 
3920
  my_delete(purge_index_file_name, MYF(0));
 
3921
  bzero((char*) &purge_index_file, sizeof(purge_index_file));
 
3922
 
 
3923
  DBUG_RETURN(error);
 
3924
}
 
3925
 
 
3926
bool MYSQL_BIN_LOG::is_inited_purge_index_file()
 
3927
{
 
3928
  return my_b_inited(&purge_index_file);
 
3929
}
 
3930
 
 
3931
int MYSQL_BIN_LOG::sync_purge_index_file()
 
3932
{
 
3933
  int error= 0;
 
3934
  DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
 
3935
 
 
3936
  if ((error= flush_io_cache(&purge_index_file)) ||
 
3937
      (error= my_sync(purge_index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE))))
 
3938
    DBUG_RETURN(error);
 
3939
 
 
3940
  DBUG_RETURN(error);
 
3941
}
 
3942
 
 
3943
int MYSQL_BIN_LOG::register_purge_index_entry(const char *entry)
 
3944
{
 
3945
  int error= 0;
 
3946
  DBUG_ENTER("MYSQL_BIN_LOG::register_purge_index_entry");
 
3947
 
 
3948
  if ((error=my_b_write(&purge_index_file, (const uchar*)entry, strlen(entry))) ||
 
3949
      (error=my_b_write(&purge_index_file, (const uchar*)"\n", 1)))
 
3950
    DBUG_RETURN (error);
 
3951
 
 
3952
  DBUG_RETURN(error);
 
3953
}
 
3954
 
 
3955
int MYSQL_BIN_LOG::register_create_index_entry(const char *entry)
 
3956
{
 
3957
  DBUG_ENTER("MYSQL_BIN_LOG::register_create_index_entry");
 
3958
  DBUG_RETURN(register_purge_index_entry(entry));
 
3959
}
 
3960
 
 
3961
int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
 
3962
                                     bool need_mutex)
 
3963
{
 
3964
  DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry");
 
3965
  MY_STAT s;
 
3966
  int error= 0;
 
3967
  LOG_INFO log_info;
 
3968
  LOG_INFO check_log_info;
 
3969
 
 
3970
  DBUG_ASSERT(my_b_inited(&purge_index_file));
 
3971
 
 
3972
  if ((error=reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
 
3973
  {
 
3974
    sql_print_error("MSYQL_BIN_LOG::purge_index_entry failed to reinit register file "
 
3975
                    "for read");
 
3976
    goto err;
 
3977
  }
 
3978
 
 
3979
  for (;;)
 
3980
  {
 
3981
    uint length;
 
3982
 
 
3983
    if ((length=my_b_gets(&purge_index_file, log_info.log_file_name,
 
3984
                          FN_REFLEN)) <= 1)
 
3985
    {
 
3986
      if (purge_index_file.error)
 
3987
      {
 
3988
        error= purge_index_file.error;
 
3989
        sql_print_error("MSYQL_BIN_LOG::purge_index_entry error %d reading from "
 
3990
                        "register file.", error);
 
3991
        goto err;
 
3992
      }
 
3993
 
 
3994
      /* Reached EOF */
 
3995
      break;
 
3996
    }
 
3997
 
 
3998
    /* Get rid of the trailing '\n' */
 
3999
    log_info.log_file_name[length-1]= 0;
 
4000
 
 
4001
    if (!mysql_file_stat(m_key_file_log, log_info.log_file_name, &s, MYF(0)))
 
4002
    {
 
4003
      if (my_errno == ENOENT) 
 
4004
      {
 
4005
        /*
 
4006
          It's not fatal if we can't stat a log file that does not exist;
 
4007
          If we could not stat, we won't delete.
 
4008
        */
 
4009
        if (thd)
 
4010
        {
 
4011
          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
4012
                              ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
 
4013
                              log_info.log_file_name);
 
4014
        }
 
4015
        sql_print_information("Failed to execute mysql_file_stat on file '%s'",
 
4016
                              log_info.log_file_name);
 
4017
        my_errno= 0;
 
4018
      }
 
4019
      else
 
4020
      {
 
4021
        /*
 
4022
          Other than ENOENT are fatal
 
4023
        */
 
4024
        if (thd)
 
4025
        {
 
4026
          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
4027
                              ER_BINLOG_PURGE_FATAL_ERR,
 
4028
                              "a problem with getting info on being purged %s; "
 
4029
                              "consider examining correspondence "
 
4030
                              "of your binlog index file "
 
4031
                              "to the actual binlog files",
 
4032
                              log_info.log_file_name);
 
4033
        }
 
4034
        else
 
4035
        {
 
4036
          sql_print_information("Failed to delete log file '%s'; "
 
4037
                                "consider examining correspondence "
 
4038
                                "of your binlog index file "
 
4039
                                "to the actual binlog files",
 
4040
                                log_info.log_file_name);
 
4041
        }
 
4042
        error= LOG_INFO_FATAL;
 
4043
        goto err;
 
4044
      }
 
4045
    }
 
4046
    else
 
4047
    {
 
4048
      if ((error= find_log_pos(&check_log_info, log_info.log_file_name, need_mutex)))
 
4049
      {
 
4050
        if (error != LOG_INFO_EOF)
 
4051
        {
 
4052
          if (thd)
 
4053
          {
 
4054
            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
4055
                                ER_BINLOG_PURGE_FATAL_ERR,
 
4056
                                "a problem with deleting %s and "
 
4057
                                "reading the binlog index file",
 
4058
                                log_info.log_file_name);
 
4059
          }
 
4060
          else
 
4061
          {
 
4062
            sql_print_information("Failed to delete file '%s' and "
 
4063
                                  "read the binlog index file",
 
4064
                                  log_info.log_file_name);
 
4065
          }
 
4066
          goto err;
 
4067
        }
 
4068
           
 
4069
        error= 0;
 
4070
        if (!need_mutex)
 
4071
        {
 
4072
          /*
 
4073
            This is to avoid triggering an error in NDB.
 
4074
          */
 
4075
          ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
 
4076
        }
 
4077
 
 
4078
        DBUG_PRINT("info",("purging %s",log_info.log_file_name));
 
4079
        if (!my_delete(log_info.log_file_name, MYF(0)))
 
4080
        {
 
4081
          if (decrease_log_space)
 
4082
            *decrease_log_space-= s.st_size;
 
4083
        }
 
4084
        else
 
4085
        {
 
4086
          if (my_errno == ENOENT)
 
4087
          {
 
4088
            if (thd)
 
4089
            {
 
4090
              push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
4091
                                  ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
 
4092
                                  log_info.log_file_name);
 
4093
            }
 
4094
            sql_print_information("Failed to delete file '%s'",
 
4095
                                  log_info.log_file_name);
 
4096
            my_errno= 0;
 
4097
          }
 
4098
          else
 
4099
          {
 
4100
            if (thd)
 
4101
            {
 
4102
              push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
4103
                                  ER_BINLOG_PURGE_FATAL_ERR,
 
4104
                                  "a problem with deleting %s; "
 
4105
                                  "consider examining correspondence "
 
4106
                                  "of your binlog index file "
 
4107
                                  "to the actual binlog files",
 
4108
                                  log_info.log_file_name);
 
4109
            }
 
4110
            else
 
4111
            {
 
4112
              sql_print_information("Failed to delete file '%s'; "
 
4113
                                    "consider examining correspondence "
 
4114
                                    "of your binlog index file "
 
4115
                                    "to the actual binlog files",
 
4116
                                    log_info.log_file_name);
 
4117
            }
 
4118
            if (my_errno == EMFILE)
 
4119
            {
 
4120
              DBUG_PRINT("info",
 
4121
                         ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
 
4122
              error= LOG_INFO_EMFILE;
 
4123
              goto err;
 
4124
            }
 
4125
            error= LOG_INFO_FATAL;
 
4126
            goto err;
 
4127
          }
 
4128
        }
 
4129
      }
 
4130
    }
 
4131
  }
 
4132
 
 
4133
err:
 
4134
  DBUG_RETURN(error);
 
4135
}
 
4136
 
 
4137
/**
 
4138
  Remove all logs before the given file date from disk and from the
 
4139
  index file.
 
4140
 
 
4141
  @param thd            Thread pointer
 
4142
  @param purge_time     Delete all log files before given date.
 
4143
 
 
4144
  @note
 
4145
    If any of the logs before the deleted one is in use,
 
4146
    only purge logs up to this one.
 
4147
 
 
4148
  @retval
 
4149
    0                           ok
 
4150
  @retval
 
4151
    LOG_INFO_PURGE_NO_ROTATE    Binary file that can't be rotated
 
4152
    LOG_INFO_FATAL              if any other than ENOENT error from
 
4153
                                mysql_file_stat() or mysql_file_delete()
 
4154
*/
 
4155
 
 
4156
int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
 
4157
{
 
4158
  int error;
 
4159
  char to_log[FN_REFLEN];
 
4160
  LOG_INFO log_info;
 
4161
  MY_STAT stat_area;
 
4162
  THD *thd= current_thd;
 
4163
  
 
4164
  DBUG_ENTER("purge_logs_before_date");
 
4165
 
 
4166
  mysql_mutex_lock(&LOCK_index);
 
4167
  to_log[0]= 0;
 
4168
 
 
4169
  if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
 
4170
    goto err;
 
4171
 
 
4172
  while (strcmp(log_file_name, log_info.log_file_name) &&
 
4173
         !is_active(log_info.log_file_name) &&
 
4174
         !log_in_use(log_info.log_file_name))
 
4175
  {
 
4176
    if (!mysql_file_stat(m_key_file_log,
 
4177
                         log_info.log_file_name, &stat_area, MYF(0)))
 
4178
    {
 
4179
      if (my_errno == ENOENT) 
 
4180
      {
 
4181
        /*
 
4182
          It's not fatal if we can't stat a log file that does not exist.
 
4183
        */
 
4184
        my_errno= 0;
 
4185
      }
 
4186
      else
 
4187
      {
 
4188
        /*
 
4189
          Other than ENOENT are fatal
 
4190
        */
 
4191
        if (thd)
 
4192
        {
 
4193
          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
4194
                              ER_BINLOG_PURGE_FATAL_ERR,
 
4195
                              "a problem with getting info on being purged %s; "
 
4196
                              "consider examining correspondence "
 
4197
                              "of your binlog index file "
 
4198
                              "to the actual binlog files",
 
4199
                              log_info.log_file_name);
 
4200
        }
 
4201
        else
 
4202
        {
 
4203
          sql_print_information("Failed to delete log file '%s'",
 
4204
                                log_info.log_file_name);
 
4205
        }
 
4206
        error= LOG_INFO_FATAL;
 
4207
        goto err;
 
4208
      }
 
4209
    }
 
4210
    else
 
4211
    {
 
4212
      if (stat_area.st_mtime < purge_time) 
 
4213
        strmake_buf(to_log, log_info.log_file_name);
 
4214
      else
 
4215
        break;
 
4216
    }
 
4217
    if (find_next_log(&log_info, 0))
 
4218
      break;
 
4219
  }
 
4220
 
 
4221
  error= (to_log[0] ? purge_logs(to_log, 1, 0, 1, (ulonglong *) 0) : 0);
 
4222
 
 
4223
err:
 
4224
  mysql_mutex_unlock(&LOCK_index);
 
4225
  DBUG_RETURN(error);
 
4226
}
 
4227
#endif /* HAVE_REPLICATION */
 
4228
 
 
4229
 
 
4230
/**
 
4231
  Create a new log file name.
 
4232
 
 
4233
  @param buf            buf of at least FN_REFLEN where new name is stored
 
4234
 
 
4235
  @note
 
4236
    If file name will be longer then FN_REFLEN it will be truncated
 
4237
*/
 
4238
 
 
4239
void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident)
 
4240
{
 
4241
  uint dir_len = dirname_length(log_file_name); 
 
4242
  if (dir_len >= FN_REFLEN)
 
4243
    dir_len=FN_REFLEN-1;
 
4244
  strnmov(buf, log_file_name, dir_len);
 
4245
  strmake(buf+dir_len, log_ident, FN_REFLEN - dir_len -1);
 
4246
}
 
4247
 
 
4248
 
 
4249
/**
 
4250
  Check if we are writing/reading to the given log file.
 
4251
*/
 
4252
 
 
4253
bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg)
 
4254
{
 
4255
  return !strcmp(log_file_name, log_file_name_arg);
 
4256
}
 
4257
 
 
4258
 
 
4259
/*
 
4260
  Wrappers around new_file_impl to avoid using argument
 
4261
  to control locking. The argument 1) less readable 2) breaks
 
4262
  incapsulation 3) allows external access to the class without
 
4263
  a lock (which is not possible with private new_file_without_locking
 
4264
  method).
 
4265
 
 
4266
  @retval
 
4267
    nonzero - error
 
4268
*/
 
4269
 
 
4270
int MYSQL_BIN_LOG::new_file()
 
4271
{
 
4272
  return new_file_impl(1);
 
4273
}
 
4274
 
 
4275
/*
 
4276
  @retval
 
4277
    nonzero - error
 
4278
 */
 
4279
int MYSQL_BIN_LOG::new_file_without_locking()
 
4280
{
 
4281
  return new_file_impl(0);
 
4282
}
 
4283
 
 
4284
 
 
4285
/**
 
4286
  Start writing to a new log file or reopen the old file.
 
4287
 
 
4288
  @param need_lock              Set to 1 if caller has not locked LOCK_log
 
4289
 
 
4290
  @retval
 
4291
    nonzero - error
 
4292
 
 
4293
  @note
 
4294
    The new file name is stored last in the index file
 
4295
*/
 
4296
 
 
4297
int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
 
4298
{
 
4299
  int error= 0, close_on_error= FALSE;
 
4300
  char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open;
 
4301
  uint close_flag;
 
4302
  bool delay_close= false;
 
4303
  File old_file;
 
4304
  LINT_INIT(old_file);
 
4305
 
 
4306
  DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl");
 
4307
  if (!is_open())
 
4308
  {
 
4309
    DBUG_PRINT("info",("log is closed"));
 
4310
    DBUG_RETURN(error);
 
4311
  }
 
4312
 
 
4313
  if (need_lock)
 
4314
    mysql_mutex_lock(&LOCK_log);
 
4315
  mysql_mutex_lock(&LOCK_index);
 
4316
 
 
4317
  mysql_mutex_assert_owner(&LOCK_log);
 
4318
  mysql_mutex_assert_owner(&LOCK_index);
 
4319
 
 
4320
  /*
 
4321
    if binlog is used as tc log, be sure all xids are "unlogged",
 
4322
    so that on recover we only need to scan one - latest - binlog file
 
4323
    for prepared xids. As this is expected to be a rare event,
 
4324
    simple wait strategy is enough. We're locking LOCK_log to be sure no
 
4325
    new Xid_log_event's are added to the log (and prepared_xids is not
 
4326
    increased), and waiting on COND_prep_xids for late threads to
 
4327
    catch up.
 
4328
  */
 
4329
  if (prepared_xids)
 
4330
  {
 
4331
    tc_log_page_waits++;
 
4332
    mysql_mutex_lock(&LOCK_prep_xids);
 
4333
    while (prepared_xids) {
 
4334
      DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
 
4335
      mysql_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
 
4336
    }
 
4337
    mysql_mutex_unlock(&LOCK_prep_xids);
 
4338
  }
 
4339
 
 
4340
  /* Reuse old name if not binlog and not update log */
 
4341
  new_name_ptr= name;
 
4342
 
 
4343
  /*
 
4344
    If user hasn't specified an extension, generate a new log name
 
4345
    We have to do this here and not in open as we want to store the
 
4346
    new file name in the current binary log file.
 
4347
  */
 
4348
  if ((error= generate_new_name(new_name, name)))
 
4349
    goto end;
 
4350
  new_name_ptr=new_name;
 
4351
 
 
4352
  if (log_type == LOG_BIN)
 
4353
  {
 
4354
    if (!no_auto_events)
 
4355
    {
 
4356
      /*
 
4357
        We log the whole file name for log file as the user may decide
 
4358
        to change base names at some point.
 
4359
      */
 
4360
      Rotate_log_event r(new_name+dirname_length(new_name), 0, LOG_EVENT_OFFSET,
 
4361
                         is_relay_log ? Rotate_log_event::RELAY_LOG : 0);
 
4362
      /* 
 
4363
         The current relay-log's closing Rotate event must have checksum
 
4364
         value computed with an algorithm of the last relay-logged FD event.
 
4365
      */
 
4366
      if (is_relay_log)
 
4367
        r.checksum_alg= relay_log_checksum_alg;
 
4368
      DBUG_ASSERT(!is_relay_log || relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
 
4369
      if(DBUG_EVALUATE_IF("fault_injection_new_file_rotate_event", (error=close_on_error=TRUE), FALSE) ||
 
4370
         (error= r.write(&log_file)))
 
4371
      {
 
4372
        DBUG_EXECUTE_IF("fault_injection_new_file_rotate_event", errno=2;);
 
4373
        close_on_error= TRUE;
 
4374
        my_printf_error(ER_ERROR_ON_WRITE, ER(ER_CANT_OPEN_FILE), MYF(ME_FATALERROR), name, errno);
 
4375
        goto end;
 
4376
      }
 
4377
      bytes_written += r.data_written;
 
4378
    }
 
4379
    /*
 
4380
      Update needs to be signalled even if there is no rotate event
 
4381
      log rotation should give the waiting thread a signal to
 
4382
      discover EOF and move on to the next log.
 
4383
    */
 
4384
    signal_update();
 
4385
  }
 
4386
  old_name=name;
 
4387
  name=0;                               // Don't free name
 
4388
  close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX;
 
4389
  if (!is_relay_log)
 
4390
  {
 
4391
    /*
 
4392
      We need to keep the old binlog file open (and marked as in-use) until
 
4393
      the new one is fully created and synced to disk and index. Otherwise we
 
4394
      leave a window where if we crash, there is no binlog file marked as
 
4395
      crashed for server restart to detect the need for recovery.
 
4396
    */
 
4397
    old_file= log_file.file;
 
4398
    close_flag|= LOG_CLOSE_DELAYED_CLOSE;
 
4399
    delay_close= true;
 
4400
  }
 
4401
  close(close_flag);
 
4402
  if (log_type == LOG_BIN && checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF)
 
4403
  {
 
4404
    DBUG_ASSERT(!is_relay_log);
 
4405
    DBUG_ASSERT(binlog_checksum_options != checksum_alg_reset);
 
4406
    binlog_checksum_options= checksum_alg_reset;
 
4407
  }
 
4408
  /*
 
4409
     Note that at this point, log_state != LOG_CLOSED (important for is_open()).
 
4410
  */
 
4411
 
 
4412
  /*
 
4413
     new_file() is only used for rotation (in FLUSH LOGS or because size >
 
4414
     max_binlog_size or max_relay_log_size).
 
4415
     If this is a binary log, the Format_description_log_event at the beginning of
 
4416
     the new file should have created=0 (to distinguish with the
 
4417
     Format_description_log_event written at server startup, which should
 
4418
     trigger temp tables deletion on slaves.
 
4419
  */
 
4420
 
 
4421
  /* reopen index binlog file, BUG#34582 */
 
4422
  file_to_open= index_file_name;
 
4423
  error= open_index_file(index_file_name, 0, FALSE);
 
4424
  if (!error)
 
4425
  {
 
4426
    /* reopen the binary log file. */
 
4427
    file_to_open= new_name_ptr;
 
4428
    error= open(old_name, log_type, new_name_ptr, io_cache_type,
 
4429
                no_auto_events, max_size, 1, FALSE);
 
4430
  }
 
4431
 
 
4432
  /* handle reopening errors */
 
4433
  if (error)
 
4434
  {
 
4435
    my_printf_error(ER_CANT_OPEN_FILE, ER(ER_CANT_OPEN_FILE), 
 
4436
                    MYF(ME_FATALERROR), file_to_open, error);
 
4437
    close_on_error= TRUE;
 
4438
  }
 
4439
 
 
4440
  my_free(old_name);
 
4441
 
 
4442
end:
 
4443
 
 
4444
  if (delay_close)
 
4445
  {
 
4446
    clear_inuse_flag_when_closing(old_file);
 
4447
    mysql_file_close(old_file, MYF(MY_WME));
 
4448
  }
 
4449
 
 
4450
  if (error && close_on_error /* rotate or reopen failed */)
 
4451
  {
 
4452
    /* 
 
4453
      Close whatever was left opened.
 
4454
 
 
4455
      We are keeping the behavior as it exists today, ie,
 
4456
      we disable logging and move on (see: BUG#51014).
 
4457
 
 
4458
      TODO: as part of WL#1790 consider other approaches:
 
4459
       - kill mysql (safety);
 
4460
       - try multiple locations for opening a log file;
 
4461
       - switch server to protected/readonly mode
 
4462
       - ...
 
4463
    */
 
4464
    close(LOG_CLOSE_INDEX);
 
4465
    sql_print_error("Could not open %s for logging (error %d). "
 
4466
                     "Turning logging off for the whole duration "
 
4467
                     "of the MySQL server process. To turn it on "
 
4468
                     "again: fix the cause, shutdown the MySQL "
 
4469
                     "server and restart it.", 
 
4470
                     new_name_ptr, errno);
 
4471
  }
 
4472
 
 
4473
  if (need_lock)
 
4474
    mysql_mutex_unlock(&LOCK_log);
 
4475
  mysql_mutex_unlock(&LOCK_index);
 
4476
 
 
4477
  DBUG_RETURN(error);
 
4478
}
 
4479
 
 
4480
 
 
4481
bool MYSQL_BIN_LOG::append(Log_event* ev)
 
4482
{
 
4483
  bool error = 0;
 
4484
  mysql_mutex_lock(&LOCK_log);
 
4485
  DBUG_ENTER("MYSQL_BIN_LOG::append");
 
4486
 
 
4487
  DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
 
4488
  /*
 
4489
    Log_event::write() is smart enough to use my_b_write() or
 
4490
    my_b_append() depending on the kind of cache we have.
 
4491
  */
 
4492
  if (ev->write(&log_file))
 
4493
  {
 
4494
    error=1;
 
4495
    goto err;
 
4496
  }
 
4497
  bytes_written+= ev->data_written;
 
4498
  DBUG_PRINT("info",("max_size: %lu",max_size));
 
4499
  if (flush_and_sync(0))
 
4500
    goto err;
 
4501
  if ((uint) my_b_append_tell(&log_file) > max_size)
 
4502
    error= new_file_without_locking();
 
4503
err:
 
4504
  mysql_mutex_unlock(&LOCK_log);
 
4505
  signal_update();                              // Safe as we don't call close
 
4506
  DBUG_RETURN(error);
 
4507
}
 
4508
 
 
4509
 
 
4510
bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
 
4511
{
 
4512
  bool error= 0;
 
4513
  DBUG_ENTER("MYSQL_BIN_LOG::appendv");
 
4514
  va_list(args);
 
4515
  va_start(args,len);
 
4516
 
 
4517
  DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
 
4518
 
 
4519
  mysql_mutex_assert_owner(&LOCK_log);
 
4520
  do
 
4521
  {
 
4522
    if (my_b_append(&log_file,(uchar*) buf,len))
 
4523
    {
 
4524
      error= 1;
 
4525
      goto err;
 
4526
    }
 
4527
    bytes_written += len;
 
4528
  } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));
 
4529
  DBUG_PRINT("info",("max_size: %lu",max_size));
 
4530
  if (flush_and_sync(0))
 
4531
    goto err;
 
4532
  if ((uint) my_b_append_tell(&log_file) > max_size)
 
4533
    error= new_file_without_locking();
 
4534
err:
 
4535
  if (!error)
 
4536
    signal_update();
 
4537
  DBUG_RETURN(error);
 
4538
}
 
4539
 
 
4540
bool MYSQL_BIN_LOG::flush_and_sync(bool *synced)
 
4541
{
 
4542
  int err=0, fd=log_file.file;
 
4543
  if (synced)
 
4544
    *synced= 0;
 
4545
  mysql_mutex_assert_owner(&LOCK_log);
 
4546
  if (flush_io_cache(&log_file))
 
4547
    return 1;
 
4548
  uint sync_period= get_sync_period();
 
4549
  if (sync_period && ++sync_counter >= sync_period)
 
4550
  {
 
4551
    sync_counter= 0;
 
4552
    err= mysql_file_sync(fd, MYF(MY_WME|MY_SYNC_FILESIZE));
 
4553
    if (synced)
 
4554
      *synced= 1;
 
4555
#ifndef DBUG_OFF
 
4556
    if (opt_binlog_dbug_fsync_sleep > 0)
 
4557
      my_sleep(opt_binlog_dbug_fsync_sleep);
 
4558
#endif
 
4559
  }
 
4560
  return err;
 
4561
}
 
4562
 
 
4563
void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param)
 
4564
{
 
4565
  DBUG_ASSERT(!thd->binlog_evt_union.do_union);
 
4566
  thd->binlog_evt_union.do_union= TRUE;
 
4567
  thd->binlog_evt_union.unioned_events= FALSE;
 
4568
  thd->binlog_evt_union.unioned_events_trans= FALSE;
 
4569
  thd->binlog_evt_union.first_query_id= query_id_param;
 
4570
}
 
4571
 
 
4572
void MYSQL_BIN_LOG::stop_union_events(THD *thd)
 
4573
{
 
4574
  DBUG_ASSERT(thd->binlog_evt_union.do_union);
 
4575
  thd->binlog_evt_union.do_union= FALSE;
 
4576
}
 
4577
 
 
4578
bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
 
4579
{
 
4580
  return (thd->binlog_evt_union.do_union && 
 
4581
          query_id_param >= thd->binlog_evt_union.first_query_id);
 
4582
}
 
4583
 
 
4584
/** 
 
4585
  This function checks if a transactional table was updated by the
 
4586
  current transaction.
 
4587
 
 
4588
  @param thd The client thread that executed the current statement.
 
4589
  @return
 
4590
    @c true if a transactional table was updated, @c false otherwise.
 
4591
*/
 
4592
bool
 
4593
trans_has_updated_trans_table(const THD* thd)
 
4594
{
 
4595
  binlog_cache_mngr *const cache_mngr=
 
4596
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
4597
 
 
4598
  return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
 
4599
}
 
4600
 
 
4601
/** 
 
4602
  This function checks if a transactional table was updated by the
 
4603
  current statement.
 
4604
 
 
4605
  @param thd The client thread that executed the current statement.
 
4606
  @return
 
4607
    @c true if a transactional table was updated, @c false otherwise.
 
4608
*/
 
4609
bool
 
4610
stmt_has_updated_trans_table(const THD *thd)
 
4611
{
 
4612
  Ha_trx_info *ha_info;
 
4613
 
 
4614
  for (ha_info= thd->transaction.stmt.ha_list; ha_info;
 
4615
       ha_info= ha_info->next())
 
4616
  {
 
4617
    if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
 
4618
      return (TRUE);
 
4619
  }
 
4620
  return (FALSE);
 
4621
}
 
4622
 
 
4623
/** 
 
4624
  This function checks if either a trx-cache or a non-trx-cache should
 
4625
  be used. If @c bin_log_direct_non_trans_update is active or the format
 
4626
  is either MIXED or ROW, the cache to be used depends on the flag @c
 
4627
  is_transactional. 
 
4628
 
 
4629
  On the other hand, if binlog_format is STMT or direct option is
 
4630
  OFF, the trx-cache should be used if and only if the statement is
 
4631
  transactional or the trx-cache is not empty. Otherwise, the
 
4632
  non-trx-cache should be used.
 
4633
 
 
4634
  @param thd              The client thread.
 
4635
  @param is_transactional The changes are related to a trx-table.
 
4636
  @return
 
4637
    @c true if a trx-cache should be used, @c false otherwise.
 
4638
*/
 
4639
bool use_trans_cache(const THD* thd, bool is_transactional)
 
4640
{
 
4641
  binlog_cache_mngr *const cache_mngr=
 
4642
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
4643
 
 
4644
  return
 
4645
    ((thd->is_current_stmt_binlog_format_row() ||
 
4646
     thd->variables.binlog_direct_non_trans_update) ? is_transactional :
 
4647
     (is_transactional || !cache_mngr->trx_cache.empty()));
 
4648
}
 
4649
 
 
4650
/**
 
4651
  This function checks if a transaction, either a multi-statement
 
4652
  or a single statement transaction is about to commit or not.
 
4653
 
 
4654
  @param thd The client thread that executed the current statement.
 
4655
  @param all Committing a transaction (i.e. TRUE) or a statement
 
4656
             (i.e. FALSE).
 
4657
  @return
 
4658
    @c true if committing a transaction, otherwise @c false.
 
4659
*/
 
4660
bool ending_trans(THD* thd, const bool all)
 
4661
{
 
4662
  return (all || ending_single_stmt_trans(thd, all));
 
4663
}
 
4664
 
 
4665
/**
 
4666
  This function checks if a single statement transaction is about
 
4667
  to commit or not.
 
4668
 
 
4669
  @param thd The client thread that executed the current statement.
 
4670
  @param all Committing a transaction (i.e. TRUE) or a statement
 
4671
             (i.e. FALSE).
 
4672
  @return
 
4673
    @c true if committing a single statement transaction, otherwise
 
4674
    @c false.
 
4675
*/
 
4676
bool ending_single_stmt_trans(THD* thd, const bool all)
 
4677
{
 
4678
  return (!all && !thd->in_multi_stmt_transaction_mode());
 
4679
}
 
4680
 
 
4681
/**
 
4682
  This function checks if a non-transactional table was updated by
 
4683
  the current transaction.
 
4684
 
 
4685
  @param thd The client thread that executed the current statement.
 
4686
  @return
 
4687
    @c true if a non-transactional table was updated, @c false
 
4688
    otherwise.
 
4689
*/
 
4690
bool trans_has_updated_non_trans_table(const THD* thd)
 
4691
{
 
4692
  return (thd->transaction.all.modified_non_trans_table ||
 
4693
          thd->transaction.stmt.modified_non_trans_table);
 
4694
}
 
4695
 
 
4696
/**
 
4697
  This function checks if a non-transactional table was updated by the
 
4698
  current statement.
 
4699
 
 
4700
  @param thd The client thread that executed the current statement.
 
4701
  @return
 
4702
    @c true if a non-transactional table was updated, @c false otherwise.
 
4703
*/
 
4704
bool stmt_has_updated_non_trans_table(const THD* thd)
 
4705
{
 
4706
  return (thd->transaction.stmt.modified_non_trans_table);
 
4707
}
 
4708
 
 
4709
/*
 
4710
  These functions are placed in this file since they need access to
 
4711
  binlog_hton, which has internal linkage.
 
4712
*/
 
4713
 
 
4714
binlog_cache_mngr *THD::binlog_setup_trx_data()
 
4715
{
 
4716
  DBUG_ENTER("THD::binlog_setup_trx_data");
 
4717
  binlog_cache_mngr *cache_mngr=
 
4718
    (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
 
4719
 
 
4720
  if (cache_mngr)
 
4721
    DBUG_RETURN(cache_mngr);                             // Already set up
 
4722
 
 
4723
  cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr), MYF(MY_ZEROFILL));
 
4724
  if (!cache_mngr ||
 
4725
      open_cached_file(&cache_mngr->stmt_cache.cache_log, mysql_tmpdir,
 
4726
                       LOG_PREFIX, binlog_stmt_cache_size, MYF(MY_WME)) ||
 
4727
      open_cached_file(&cache_mngr->trx_cache.cache_log, mysql_tmpdir,
 
4728
                       LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
 
4729
  {
 
4730
    my_free(cache_mngr);
 
4731
    DBUG_RETURN(0);                      // Didn't manage to set it up
 
4732
  }
 
4733
  thd_set_ha_data(this, binlog_hton, cache_mngr);
 
4734
 
 
4735
  cache_mngr= new (cache_mngr)
 
4736
              binlog_cache_mngr(max_binlog_stmt_cache_size,
 
4737
                                max_binlog_cache_size,
 
4738
                                &binlog_stmt_cache_use,
 
4739
                                &binlog_stmt_cache_disk_use,
 
4740
                                &binlog_cache_use,
 
4741
                                &binlog_cache_disk_use);
 
4742
  DBUG_RETURN(cache_mngr);
 
4743
}
 
4744
 
 
4745
/*
 
4746
  Function to start a statement and optionally a transaction for the
 
4747
  binary log.
 
4748
 
 
4749
  SYNOPSIS
 
4750
    binlog_start_trans_and_stmt()
 
4751
 
 
4752
  DESCRIPTION
 
4753
 
 
4754
    This function does three things:
 
4755
    - Start a transaction if not in autocommit mode or if a BEGIN
 
4756
      statement has been seen.
 
4757
 
 
4758
    - Start a statement transaction to allow us to truncate the cache.
 
4759
 
 
4760
    - Save the currrent binlog position so that we can roll back the
 
4761
      statement by truncating the cache.
 
4762
 
 
4763
      We only update the saved position if the old one was undefined,
 
4764
      the reason is that there are some cases (e.g., for CREATE-SELECT)
 
4765
      where the position is saved twice (e.g., both in
 
4766
      select_create::prepare() and THD::binlog_write_table_map()) , but
 
4767
      we should use the first. This means that calls to this function
 
4768
      can be used to start the statement before the first table map
 
4769
      event, to include some extra events.
 
4770
 */
 
4771
 
 
4772
void
 
4773
THD::binlog_start_trans_and_stmt()
 
4774
{
 
4775
  binlog_cache_mngr *cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
 
4776
  DBUG_ENTER("binlog_start_trans_and_stmt");
 
4777
  DBUG_PRINT("enter", ("cache_mngr: %p  cache_mngr->trx_cache.get_prev_position(): %lu",
 
4778
                       cache_mngr,
 
4779
                       (cache_mngr ? (ulong) cache_mngr->trx_cache.get_prev_position() :
 
4780
                        (ulong) 0)));
 
4781
 
 
4782
  if (cache_mngr == NULL ||
 
4783
      cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
 
4784
  {
 
4785
    this->binlog_set_stmt_begin();
 
4786
    if (in_multi_stmt_transaction_mode())
 
4787
      trans_register_ha(this, TRUE, binlog_hton);
 
4788
    trans_register_ha(this, FALSE, binlog_hton);
 
4789
    /*
 
4790
      Mark statement transaction as read/write. We never start
 
4791
      a binary log transaction and keep it read-only,
 
4792
      therefore it's best to mark the transaction read/write just
 
4793
      at the same time we start it.
 
4794
      Not necessary to mark the normal transaction read/write
 
4795
      since the statement-level flag will be propagated automatically
 
4796
      inside ha_commit_trans.
 
4797
    */
 
4798
    ha_data[binlog_hton->slot].ha_info[0].set_trx_read_write();
 
4799
  }
 
4800
  DBUG_VOID_RETURN;
 
4801
}
 
4802
 
 
4803
void THD::binlog_set_stmt_begin() {
 
4804
  binlog_cache_mngr *cache_mngr=
 
4805
    (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
 
4806
 
 
4807
  /*
 
4808
    The call to binlog_trans_log_savepos() might create the cache_mngr
 
4809
    structure, if it didn't exist before, so we save the position
 
4810
    into an auto variable and then write it into the transaction
 
4811
    data for the binary log (i.e., cache_mngr).
 
4812
  */
 
4813
  my_off_t pos= 0;
 
4814
  binlog_trans_log_savepos(this, &pos);
 
4815
  cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
 
4816
  cache_mngr->trx_cache.set_prev_position(pos);
 
4817
}
 
4818
 
 
4819
static int
 
4820
binlog_start_consistent_snapshot(handlerton *hton, THD *thd)
 
4821
{
 
4822
  int err= 0;
 
4823
  DBUG_ENTER("binlog_start_consistent_snapshot");
 
4824
 
 
4825
  binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
 
4826
 
 
4827
  /* Server layer calls us with LOCK_commit_ordered locked, so this is safe. */
 
4828
  strmake_buf(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file);
 
4829
  cache_mngr->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset;
 
4830
 
 
4831
  trans_register_ha(thd, TRUE, hton);
 
4832
 
 
4833
  DBUG_RETURN(err);
 
4834
}
 
4835
 
 
4836
/**
 
4837
  This function writes a table map to the binary log. 
 
4838
  Note that in order to keep the signature uniform with related methods,
 
4839
  we use a redundant parameter to indicate whether a transactional table
 
4840
  was changed or not.
 
4841
 
 
4842
  If with_annotate != NULL and
 
4843
  *with_annotate = TRUE write also Annotate_rows before the table map.
 
4844
 
 
4845
  @param table             a pointer to the table.
 
4846
  @param is_transactional  @c true indicates a transactional table,
 
4847
                           otherwise @c false a non-transactional.
 
4848
  @return
 
4849
    nonzero if an error pops up when writing the table map event.
 
4850
*/
 
4851
int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
 
4852
                                my_bool *with_annotate)
 
4853
{
 
4854
  int error;
 
4855
  DBUG_ENTER("THD::binlog_write_table_map");
 
4856
  DBUG_PRINT("enter", ("table: 0x%lx  (%s: #%lu)",
 
4857
                       (long) table, table->s->table_name.str,
 
4858
                       table->s->table_map_id));
 
4859
 
 
4860
  /* Pre-conditions */
 
4861
  DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
 
4862
  DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
 
4863
 
 
4864
  Table_map_log_event
 
4865
    the_event(this, table, table->s->table_map_id, is_transactional);
 
4866
 
 
4867
  if (binlog_table_maps == 0)
 
4868
    binlog_start_trans_and_stmt();
 
4869
 
 
4870
  binlog_cache_mngr *const cache_mngr=
 
4871
    (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
 
4872
 
 
4873
  IO_CACHE *file=
 
4874
    cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
 
4875
  if (with_annotate && *with_annotate)
 
4876
  {
 
4877
    Annotate_rows_log_event anno(current_thd, is_transactional, false);
 
4878
    /* Annotate event should be written not more than once */
 
4879
    *with_annotate= 0;
 
4880
    if ((error= anno.write(file)))
 
4881
      DBUG_RETURN(error);
 
4882
  }
 
4883
  if ((error= the_event.write(file)))
 
4884
    DBUG_RETURN(error);
 
4885
 
 
4886
  binlog_table_maps++;
 
4887
  DBUG_RETURN(0);
 
4888
}
 
4889
 
 
4890
/**
 
4891
  This function retrieves a pending row event from a cache which is
 
4892
  specified through the parameter @c is_transactional. Respectively, when it
 
4893
  is @c true, the pending event is returned from the transactional cache.
 
4894
  Otherwise from the non-transactional cache.
 
4895
 
 
4896
  @param is_transactional  @c true indicates a transactional cache,
 
4897
                           otherwise @c false a non-transactional.
 
4898
  @return
 
4899
    The row event if any. 
 
4900
*/
 
4901
Rows_log_event*
 
4902
THD::binlog_get_pending_rows_event(bool is_transactional) const
 
4903
{
 
4904
  Rows_log_event* rows= NULL;
 
4905
  binlog_cache_mngr *const cache_mngr=
 
4906
    (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
 
4907
 
 
4908
  /*
 
4909
    This is less than ideal, but here's the story: If there is no cache_mngr,
 
4910
    prepare_pending_rows_event() has never been called (since the cache_mngr
 
4911
    is set up there). In that case, we just return NULL.
 
4912
   */
 
4913
  if (cache_mngr)
 
4914
  {
 
4915
    binlog_cache_data *cache_data=
 
4916
      cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
 
4917
 
 
4918
    rows= cache_data->pending();
 
4919
  }
 
4920
  return (rows);
 
4921
}
 
4922
 
 
4923
/**
 
4924
  This function stores a pending row event into a cache which is specified
 
4925
  through the parameter @c is_transactional. Respectively, when it is @c
 
4926
  true, the pending event is stored into the transactional cache. Otherwise
 
4927
  into the non-transactional cache.
 
4928
 
 
4929
  @param evt               a pointer to the row event.
 
4930
  @param is_transactional  @c true indicates a transactional cache,
 
4931
                           otherwise @c false a non-transactional.
 
4932
*/
 
4933
void
 
4934
THD::binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional)
 
4935
{
 
4936
  binlog_cache_mngr *const cache_mngr= binlog_setup_trx_data();
 
4937
 
 
4938
  DBUG_ASSERT(cache_mngr);
 
4939
 
 
4940
  binlog_cache_data *cache_data=
 
4941
    cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
 
4942
 
 
4943
  cache_data->set_pending(ev);
 
4944
}
 
4945
 
 
4946
 
 
4947
/**
 
4948
  This function removes the pending rows event, discarding any outstanding
 
4949
  rows. If there is no pending rows event available, this is effectively a
 
4950
  no-op.
 
4951
 
 
4952
  @param thd               a pointer to the user thread.
 
4953
  @param is_transactional  @c true indicates a transactional cache,
 
4954
                           otherwise @c false a non-transactional.
 
4955
*/
 
4956
int
 
4957
MYSQL_BIN_LOG::remove_pending_rows_event(THD *thd, bool is_transactional)
 
4958
{
 
4959
  DBUG_ENTER("MYSQL_BIN_LOG::remove_pending_rows_event");
 
4960
 
 
4961
  binlog_cache_mngr *const cache_mngr=
 
4962
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
4963
 
 
4964
  DBUG_ASSERT(cache_mngr);
 
4965
 
 
4966
  binlog_cache_data *cache_data=
 
4967
    cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
 
4968
 
 
4969
  if (Rows_log_event* pending= cache_data->pending())
 
4970
  {
 
4971
    delete pending;
 
4972
    cache_data->set_pending(NULL);
 
4973
  }
 
4974
 
 
4975
  DBUG_RETURN(0);
 
4976
}
 
4977
 
 
4978
/*
 
4979
  Moves the last bunch of rows from the pending Rows event to a cache (either
 
4980
  transactional cache if is_transaction is @c true, or the non-transactional
 
4981
  cache otherwise. Sets a new pending event.
 
4982
 
 
4983
  @param thd               a pointer to the user thread.
 
4984
  @param evt               a pointer to the row event.
 
4985
  @param is_transactional  @c true indicates a transactional cache,
 
4986
                           otherwise @c false a non-transactional.
 
4987
*/
 
4988
int
 
4989
MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
 
4990
                                                Rows_log_event* event,
 
4991
                                                bool is_transactional)
 
4992
{
 
4993
  DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)");
 
4994
  DBUG_ASSERT(mysql_bin_log.is_open());
 
4995
  DBUG_PRINT("enter", ("event: 0x%lx", (long) event));
 
4996
 
 
4997
  int error= 0;
 
4998
  binlog_cache_mngr *const cache_mngr=
 
4999
    (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
5000
 
 
5001
  DBUG_ASSERT(cache_mngr);
 
5002
 
 
5003
  binlog_cache_data *cache_data=
 
5004
    cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
 
5005
 
 
5006
  DBUG_PRINT("info", ("cache_mngr->pending(): 0x%lx", (long) cache_data->pending()));
 
5007
 
 
5008
  if (Rows_log_event* pending= cache_data->pending())
 
5009
  {
 
5010
    IO_CACHE *file= &cache_data->cache_log;
 
5011
 
 
5012
    /*
 
5013
      Write pending event to the cache.
 
5014
    */
 
5015
    DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
 
5016
                    {DBUG_SET("+d,simulate_file_write_error");});
 
5017
    if (pending->write(file))
 
5018
    {
 
5019
      set_write_error(thd, is_transactional);
 
5020
      if (check_write_error(thd) && cache_data &&
 
5021
          stmt_has_updated_non_trans_table(thd))
 
5022
        cache_data->set_incident();
 
5023
      delete pending;
 
5024
      cache_data->set_pending(NULL);
 
5025
      DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
 
5026
                      {DBUG_SET("-d,simulate_file_write_error");});
 
5027
      DBUG_RETURN(1);
 
5028
    }
 
5029
 
 
5030
    delete pending;
 
5031
  }
 
5032
 
 
5033
  thd->binlog_set_pending_rows_event(event, is_transactional);
 
5034
 
 
5035
  DBUG_RETURN(error);
 
5036
}
 
5037
 
 
5038
/**
 
5039
  Write an event to the binary log. If with_annotate != NULL and
 
5040
  *with_annotate = TRUE write also Annotate_rows before the event
 
5041
  (this should happen only if the event is a Table_map).
 
5042
*/
 
5043
 
 
5044
bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
 
5045
{
 
5046
  THD *thd= event_info->thd;
 
5047
  bool error= 1;
 
5048
  DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
 
5049
  binlog_cache_data *cache_data= 0;
 
5050
  bool is_trans_cache= FALSE;
 
5051
  bool using_trans= event_info->use_trans_cache();
 
5052
  bool direct= event_info->use_direct_logging();
 
5053
 
 
5054
  if (thd->binlog_evt_union.do_union)
 
5055
  {
 
5056
    /*
 
5057
      In Stored function; Remember that function call caused an update.
 
5058
      We will log the function call to the binary log on function exit
 
5059
    */
 
5060
    thd->binlog_evt_union.unioned_events= TRUE;
 
5061
    thd->binlog_evt_union.unioned_events_trans |= using_trans;
 
5062
    DBUG_RETURN(0);
 
5063
  }
 
5064
 
 
5065
  /*
 
5066
    We only end the statement if we are in a top-level statement.  If
 
5067
    we are inside a stored function, we do not end the statement since
 
5068
    this will close all tables on the slave.
 
5069
  */
 
5070
  bool const end_stmt=
 
5071
    thd->locked_tables_mode && thd->lex->requires_prelocking();
 
5072
  if (thd->binlog_flush_pending_rows_event(end_stmt, using_trans))
 
5073
    DBUG_RETURN(error);
 
5074
 
 
5075
  /*
 
5076
     In most cases this is only called if 'is_open()' is true; in fact this is
 
5077
     mostly called if is_open() *was* true a few instructions before, but it
 
5078
     could have changed since.
 
5079
  */
 
5080
  if (likely(is_open()))
 
5081
  {
 
5082
    my_off_t UNINIT_VAR(my_org_b_tell);
 
5083
#ifdef HAVE_REPLICATION
 
5084
    /*
 
5085
      In the future we need to add to the following if tests like
 
5086
      "do the involved tables match (to be implemented)
 
5087
      binlog_[wild_]{do|ignore}_table?" (WL#1049)"
 
5088
    */
 
5089
    const char *local_db= event_info->get_db();
 
5090
    if ((!(thd->variables.option_bits & OPTION_BIN_LOG)) ||
 
5091
        (thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT &&
 
5092
         thd->lex->sql_command != SQLCOM_SAVEPOINT &&
 
5093
         !binlog_filter->db_ok(local_db)))
 
5094
      DBUG_RETURN(0);
 
5095
#endif /* HAVE_REPLICATION */
 
5096
 
 
5097
    IO_CACHE *file= NULL;
 
5098
 
 
5099
    if (direct)
 
5100
    {
 
5101
      file= &log_file;
 
5102
      my_org_b_tell= my_b_tell(file);
 
5103
      mysql_mutex_lock(&LOCK_log);
 
5104
    }
 
5105
    else
 
5106
    {
 
5107
      binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
 
5108
      if (!cache_mngr)
 
5109
        goto err;
 
5110
 
 
5111
      is_trans_cache= use_trans_cache(thd, using_trans);
 
5112
      file= cache_mngr->get_binlog_cache_log(is_trans_cache);
 
5113
      cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache);
 
5114
 
 
5115
      if (thd->lex->stmt_accessed_non_trans_temp_table())
 
5116
        cache_data->set_changes_to_non_trans_temp_table();
 
5117
 
 
5118
      thd->binlog_start_trans_and_stmt();
 
5119
    }
 
5120
    DBUG_PRINT("info",("event type: %d",event_info->get_type_code()));
 
5121
 
 
5122
    /*
 
5123
       No check for auto events flag here - this write method should
 
5124
       never be called if auto-events are enabled.
 
5125
 
 
5126
       Write first log events which describe the 'run environment'
 
5127
       of the SQL command. If row-based binlogging, Insert_id, Rand
 
5128
       and other kind of "setting context" events are not needed.
 
5129
    */
 
5130
 
 
5131
    if (with_annotate && *with_annotate)
 
5132
    {
 
5133
      DBUG_ASSERT(event_info->get_type_code() == TABLE_MAP_EVENT);
 
5134
      Annotate_rows_log_event anno(thd, using_trans, direct);
 
5135
      /* Annotate event should be written not more than once */
 
5136
      *with_annotate= 0;
 
5137
      if (anno.write(file))
 
5138
        goto err;
 
5139
    }
 
5140
 
 
5141
    if (thd)
 
5142
    {
 
5143
      if (!thd->is_current_stmt_binlog_format_row())
 
5144
      {
 
5145
 
 
5146
        if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
 
5147
        {
 
5148
          Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
 
5149
                             thd->first_successful_insert_id_in_prev_stmt_for_binlog,
 
5150
                             using_trans, direct);
 
5151
          if (e.write(file))
 
5152
            goto err;
 
5153
        }
 
5154
        if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
 
5155
        {
 
5156
          DBUG_PRINT("info",("number of auto_inc intervals: %u",
 
5157
                             thd->auto_inc_intervals_in_cur_stmt_for_binlog.
 
5158
                             nb_elements()));
 
5159
          Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT,
 
5160
                             thd->auto_inc_intervals_in_cur_stmt_for_binlog.
 
5161
                             minimum(), using_trans, direct);
 
5162
          if (e.write(file))
 
5163
            goto err;
 
5164
        }
 
5165
        if (thd->rand_used)
 
5166
        {
 
5167
          Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2,
 
5168
                           using_trans, direct);
 
5169
          if (e.write(file))
 
5170
            goto err;
 
5171
        }
 
5172
        if (thd->user_var_events.elements)
 
5173
        {
 
5174
          for (uint i= 0; i < thd->user_var_events.elements; i++)
 
5175
          {
 
5176
            BINLOG_USER_VAR_EVENT *user_var_event;
 
5177
            get_dynamic(&thd->user_var_events,(uchar*) &user_var_event, i);
 
5178
 
 
5179
            /* setting flags for user var log event */
 
5180
            uchar flags= User_var_log_event::UNDEF_F;
 
5181
            if (user_var_event->unsigned_flag)
 
5182
              flags|= User_var_log_event::UNSIGNED_F;
 
5183
 
 
5184
            User_var_log_event e(thd, user_var_event->user_var_event->name.str,
 
5185
                                 user_var_event->user_var_event->name.length,
 
5186
                                 user_var_event->value,
 
5187
                                 user_var_event->length,
 
5188
                                 user_var_event->type,
 
5189
                                 user_var_event->charset_number,
 
5190
                                 flags,
 
5191
                                 using_trans,
 
5192
                                 direct);
 
5193
            if (e.write(file))
 
5194
              goto err;
 
5195
          }
 
5196
        }
 
5197
      }
 
5198
    }
 
5199
 
 
5200
    /*
 
5201
      Write the event.
 
5202
    */
 
5203
    if (event_info->write(file) ||
 
5204
        DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
 
5205
      goto err;
 
5206
 
 
5207
    error= 0;
 
5208
err:
 
5209
    if (direct)
 
5210
    {
 
5211
      my_off_t offset= my_b_tell(file);
 
5212
      bool check_purge= false;
 
5213
 
 
5214
      if (!error)
 
5215
      {
 
5216
        bool synced;
 
5217
 
 
5218
        if ((error= flush_and_sync(&synced)))
 
5219
        {
 
5220
        }
 
5221
        else if ((error= RUN_HOOK(binlog_storage, after_flush,
 
5222
                 (thd, log_file_name, file->pos_in_file, synced))))
 
5223
        {
 
5224
          sql_print_error("Failed to run 'after_flush' hooks");
 
5225
        } 
 
5226
        else
 
5227
        {
 
5228
          signal_update();
 
5229
          if ((error= rotate(false, &check_purge)))
 
5230
            check_purge= false;
 
5231
        }
 
5232
      }
 
5233
 
 
5234
      status_var_add(thd->status_var.binlog_bytes_written,
 
5235
                     offset - my_org_b_tell);
 
5236
 
 
5237
      /*
 
5238
        Take mutex to protect against a reader seeing partial writes of 64-bit
 
5239
        offset on 32-bit CPUs.
 
5240
      */
 
5241
      mysql_mutex_lock(&LOCK_commit_ordered);
 
5242
      last_commit_pos_offset= offset;
 
5243
      mysql_mutex_unlock(&LOCK_commit_ordered);
 
5244
      mysql_mutex_unlock(&LOCK_log);
 
5245
 
 
5246
      if (check_purge)
 
5247
        purge();
 
5248
    }
 
5249
 
 
5250
    if (error)
 
5251
    {
 
5252
      set_write_error(thd, is_trans_cache);
 
5253
      if (check_write_error(thd) && cache_data &&
 
5254
          stmt_has_updated_non_trans_table(thd))
 
5255
        cache_data->set_incident();
 
5256
    }
 
5257
  }
 
5258
 
 
5259
  DBUG_RETURN(error);
 
5260
}
 
5261
 
 
5262
 
 
5263
int error_log_print(enum loglevel level, const char *format,
 
5264
                    va_list args)
 
5265
{
 
5266
  return logger.error_log_print(level, format, args);
 
5267
}
 
5268
 
 
5269
 
 
5270
bool slow_log_print(THD *thd, const char *query, uint query_length,
 
5271
                    ulonglong current_utime)
 
5272
{
 
5273
  return logger.slow_log_print(thd, query, query_length, current_utime);
 
5274
}
 
5275
 
 
5276
 
 
5277
bool LOGGER::log_command(THD *thd, enum enum_server_command command)
 
5278
{
 
5279
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 
5280
  Security_context *sctx= thd->security_ctx;
 
5281
#endif
 
5282
  /*
 
5283
    Log command if we have at least one log event handler enabled and want
 
5284
    to log this king of commands
 
5285
  */
 
5286
  if (*general_log_handler_list && (what_to_log & (1L << (uint) command)))
 
5287
  {
 
5288
    if ((thd->variables.option_bits & OPTION_LOG_OFF)
 
5289
#ifndef NO_EMBEDDED_ACCESS_CHECKS
 
5290
         && (sctx->master_access & SUPER_ACL)
 
5291
#endif
 
5292
       )
 
5293
    {
 
5294
      /* No logging */
 
5295
      return FALSE;
 
5296
    }
 
5297
 
 
5298
    return TRUE;
 
5299
  }
 
5300
 
 
5301
  return FALSE;
 
5302
}
 
5303
 
 
5304
 
 
5305
bool general_log_print(THD *thd, enum enum_server_command command,
 
5306
                       const char *format, ...)
 
5307
{
 
5308
  va_list args;
 
5309
  uint error= 0;
 
5310
 
 
5311
  /* Print the message to the buffer if we want to log this king of commands */
 
5312
  if (! logger.log_command(thd, command))
 
5313
    return FALSE;
 
5314
 
 
5315
  va_start(args, format);
 
5316
  error= logger.general_log_print(thd, command, format, args);
 
5317
  va_end(args);
 
5318
 
 
5319
  return error;
 
5320
}
 
5321
 
 
5322
bool general_log_write(THD *thd, enum enum_server_command command,
 
5323
                       const char *query, uint query_length)
 
5324
{
 
5325
  /* Write the message to the log if we want to log this king of commands */
 
5326
  if (logger.log_command(thd, command) || mysql_audit_general_enabled())
 
5327
    return logger.general_log_write(thd, command, query, query_length);
 
5328
 
 
5329
  return FALSE;
 
5330
}
 
5331
 
 
5332
/**
 
5333
  The method executes rotation when LOCK_log is already acquired
 
5334
  by the caller.
 
5335
 
 
5336
  @param force_rotate  caller can request the log rotation
 
5337
  @param check_purge   is set to true if rotation took place
 
5338
 
 
5339
  @note
 
5340
    If rotation fails, for instance the server was unable 
 
5341
    to create a new log file, we still try to write an 
 
5342
    incident event to the current log.
 
5343
 
 
5344
  @retval
 
5345
    nonzero - error in rotating routine.
 
5346
*/
 
5347
int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
 
5348
{
 
5349
  int error= 0;
 
5350
  DBUG_ENTER("MYSQL_BIN_LOG::rotate");
 
5351
 
 
5352
  //todo: fix the macro def and restore safe_mutex_assert_owner(&LOCK_log);
 
5353
  *check_purge= false;
 
5354
 
 
5355
  if (force_rotate || (my_b_tell(&log_file) >= (my_off_t) max_size))
 
5356
  {
 
5357
    if ((error= new_file_without_locking()))
 
5358
      /** 
 
5359
         Be conservative... There are possible lost events (eg, 
 
5360
         failing to log the Execute_load_query_log_event
 
5361
         on a LOAD DATA while using a non-transactional
 
5362
         table)!
 
5363
 
 
5364
         We give it a shot and try to write an incident event anyway
 
5365
         to the current log. 
 
5366
      */
 
5367
      if (!write_incident_already_locked(current_thd))
 
5368
        flush_and_sync(0);
 
5369
 
 
5370
    *check_purge= true;
 
5371
  }
 
5372
  DBUG_RETURN(error);
 
5373
}
 
5374
 
 
5375
/**
 
5376
  The method executes logs purging routine.
 
5377
 
 
5378
  @retval
 
5379
    nonzero - error in rotating routine.
 
5380
*/
 
5381
void MYSQL_BIN_LOG::purge()
 
5382
{
 
5383
  mysql_mutex_assert_not_owner(&LOCK_log);
 
5384
#ifdef HAVE_REPLICATION
 
5385
  if (expire_logs_days)
 
5386
  {
 
5387
    DEBUG_SYNC(current_thd, "at_purge_logs_before_date");
 
5388
    time_t purge_time= my_time(0) - expire_logs_days*24*60*60;
 
5389
    if (purge_time >= 0)
 
5390
    {
 
5391
      purge_logs_before_date(purge_time);
 
5392
    }
 
5393
    DEBUG_SYNC(current_thd, "after_purge_logs_before_date");
 
5394
  }
 
5395
#endif
 
5396
}
 
5397
 
 
5398
/**
 
5399
  The method is a shortcut of @c rotate() and @c purge().
 
5400
  LOCK_log is acquired prior to rotate and is released after it.
 
5401
 
 
5402
  @param force_rotate  caller can request the log rotation
 
5403
 
 
5404
  @retval
 
5405
    nonzero - error in rotating routine.
 
5406
*/
 
5407
int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
 
5408
{
 
5409
  int error= 0;
 
5410
  DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
 
5411
  bool check_purge= false;
 
5412
 
 
5413
  //todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
 
5414
  mysql_mutex_lock(&LOCK_log);
 
5415
  if ((error= rotate(force_rotate, &check_purge)))
 
5416
    check_purge= false;
 
5417
  /*
 
5418
    NOTE: Run purge_logs wo/ holding LOCK_log because it does not need
 
5419
          the mutex. Otherwise causes various deadlocks.
 
5420
  */
 
5421
  mysql_mutex_unlock(&LOCK_log);
 
5422
 
 
5423
  if (check_purge)
 
5424
    purge();
 
5425
 
 
5426
  DBUG_RETURN(error);
 
5427
}
 
5428
 
 
5429
uint MYSQL_BIN_LOG::next_file_id()
 
5430
{
 
5431
  uint res;
 
5432
  mysql_mutex_lock(&LOCK_log);
 
5433
  res = file_id++;
 
5434
  mysql_mutex_unlock(&LOCK_log);
 
5435
  return res;
 
5436
}
 
5437
 
 
5438
 
 
5439
/**
 
5440
  Calculate checksum of possibly a part of an event containing at least
 
5441
  the whole common header.
 
5442
 
 
5443
  @param    buf       the pointer to trans cache's buffer
 
5444
  @param    off       the offset of the beginning of the event in the buffer
 
5445
  @param    event_len no-checksum length of the event
 
5446
  @param    length    the current size of the buffer
 
5447
 
 
5448
  @param    crc       [in-out] the checksum
 
5449
 
 
5450
  Event size in incremented by @c BINLOG_CHECKSUM_LEN.
 
5451
 
 
5452
  @return 0 or number of unprocessed yet bytes of the event excluding 
 
5453
            the checksum part.
 
5454
*/
 
5455
  static ulong fix_log_event_crc(uchar *buf, uint off, uint event_len,
 
5456
                                 uint length, ha_checksum *crc)
 
5457
{
 
5458
  ulong ret;
 
5459
  uchar *event_begin= buf + off;
 
5460
 
 
5461
  ret= length >= off + event_len ? 0 : off + event_len - length;
 
5462
  *crc= my_checksum(*crc, event_begin, event_len - ret); 
 
5463
  return ret;
 
5464
}
 
5465
 
 
5466
/*
 
5467
  Write the contents of a cache to the binary log.
 
5468
 
 
5469
  SYNOPSIS
 
5470
    write_cache()
 
5471
    thd      Current_thread
 
5472
    cache    Cache to write to the binary log
 
5473
 
 
5474
  DESCRIPTION
 
5475
    Write the contents of the cache to the binary log. The cache will
 
5476
    be reset as a READ_CACHE to be able to read the contents from it.
 
5477
 
 
5478
    Reading from the trans cache with possible (per @c binlog_checksum_options) 
 
5479
    adding checksum value  and then fixing the length and the end_log_pos of 
 
5480
    events prior to fill in the binlog cache.
 
5481
*/
 
5482
 
 
5483
int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
 
5484
{
 
5485
  mysql_mutex_assert_owner(&LOCK_log);
 
5486
  if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
 
5487
    return ER_ERROR_ON_WRITE;
 
5488
  uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
 
5489
  ulong remains= 0; // part of unprocessed yet netto length of the event
 
5490
  long val;
 
5491
  ulong end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t
 
5492
  uchar header[LOG_EVENT_HEADER_LEN];
 
5493
  ha_checksum crc= 0, crc_0= 0; // assignments to keep compiler happy
 
5494
  my_bool do_checksum= (binlog_checksum_options != BINLOG_CHECKSUM_ALG_OFF);
 
5495
  uchar buf[BINLOG_CHECKSUM_LEN];
 
5496
 
 
5497
  // while there is just one alg the following must hold:
 
5498
  DBUG_ASSERT(!do_checksum ||
 
5499
              binlog_checksum_options == BINLOG_CHECKSUM_ALG_CRC32);
 
5500
 
 
5501
  /*
 
5502
    The events in the buffer have incorrect end_log_pos data
 
5503
    (relative to beginning of group rather than absolute),
 
5504
    so we'll recalculate them in situ so the binlog is always
 
5505
    correct, even in the middle of a group. This is possible
 
5506
    because we now know the start position of the group (the
 
5507
    offset of this cache in the log, if you will); all we need
 
5508
    to do is to find all event-headers, and add the position of
 
5509
    the group to the end_log_pos of each event.  This is pretty
 
5510
    straight forward, except that we read the cache in segments,
 
5511
    so an event-header might end up on the cache-border and get
 
5512
    split.
 
5513
  */
 
5514
 
 
5515
  group= (uint)my_b_tell(&log_file);
 
5516
  hdr_offs= carry= 0;
 
5517
  if (do_checksum)
 
5518
    crc= crc_0= my_checksum(0L, NULL, 0);
 
5519
 
 
5520
  do
 
5521
  {
 
5522
    /*
 
5523
      if we only got a partial header in the last iteration,
 
5524
      get the other half now and process a full header.
 
5525
    */
 
5526
    if (unlikely(carry > 0))
 
5527
    {
 
5528
      DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
 
5529
 
 
5530
      /* assemble both halves */
 
5531
      memcpy(&header[carry], (char *)cache->read_pos,
 
5532
             LOG_EVENT_HEADER_LEN - carry);
 
5533
 
 
5534
      /* fix end_log_pos */
 
5535
      val= uint4korr(&header[LOG_POS_OFFSET]) + group +
 
5536
        (end_log_pos_inc+= (do_checksum ? BINLOG_CHECKSUM_LEN : 0));
 
5537
      int4store(&header[LOG_POS_OFFSET], val);
 
5538
 
 
5539
      if (do_checksum)
 
5540
      {
 
5541
        ulong len= uint4korr(&header[EVENT_LEN_OFFSET]);
 
5542
        /* fix len */
 
5543
        int4store(&header[EVENT_LEN_OFFSET], len + BINLOG_CHECKSUM_LEN);
 
5544
      }
 
5545
 
 
5546
      /* write the first half of the split header */
 
5547
      if (my_b_write(&log_file, header, carry))
 
5548
        return ER_ERROR_ON_WRITE;
 
5549
      status_var_add(thd->status_var.binlog_bytes_written, carry);
 
5550
 
 
5551
      /*
 
5552
        copy fixed second half of header to cache so the correct
 
5553
        version will be written later.
 
5554
      */
 
5555
      memcpy((char *)cache->read_pos, &header[carry],
 
5556
             LOG_EVENT_HEADER_LEN - carry);
 
5557
 
 
5558
      /* next event header at ... */
 
5559
      hdr_offs= uint4korr(&header[EVENT_LEN_OFFSET]) - carry -
 
5560
        (do_checksum ? BINLOG_CHECKSUM_LEN : 0);
 
5561
 
 
5562
      if (do_checksum)
 
5563
      {
 
5564
        DBUG_ASSERT(crc == crc_0 && remains == 0);
 
5565
        crc= my_checksum(crc, header, carry);
 
5566
        remains= uint4korr(header + EVENT_LEN_OFFSET) - carry -
 
5567
          BINLOG_CHECKSUM_LEN;
 
5568
      }
 
5569
      carry= 0;
 
5570
    }
 
5571
 
 
5572
    /* if there is anything to write, process it. */
 
5573
 
 
5574
    if (likely(length > 0))
 
5575
    {
 
5576
      /*
 
5577
        process all event-headers in this (partial) cache.
 
5578
        if next header is beyond current read-buffer,
 
5579
        we'll get it later (though not necessarily in the
 
5580
        very next iteration, just "eventually").
 
5581
      */
 
5582
 
 
5583
      /* crc-calc the whole buffer */
 
5584
      if (do_checksum && hdr_offs >= length)
 
5585
      {
 
5586
 
 
5587
        DBUG_ASSERT(remains != 0 && crc != crc_0);
 
5588
 
 
5589
        crc= my_checksum(crc, cache->read_pos, length); 
 
5590
        remains -= length;
 
5591
        if (my_b_write(&log_file, cache->read_pos, length))
 
5592
          return ER_ERROR_ON_WRITE;
 
5593
        if (remains == 0)
 
5594
        {
 
5595
          int4store(buf, crc);
 
5596
          if (my_b_write(&log_file, buf, BINLOG_CHECKSUM_LEN))
 
5597
            return ER_ERROR_ON_WRITE;
 
5598
          crc= crc_0;
 
5599
        }
 
5600
      }
 
5601
 
 
5602
      while (hdr_offs < length)
 
5603
      {
 
5604
        /*
 
5605
          partial header only? save what we can get, process once
 
5606
          we get the rest.
 
5607
        */
 
5608
 
 
5609
        if (do_checksum)
 
5610
        {
 
5611
          if (remains != 0)
 
5612
          {
 
5613
            /*
 
5614
              finish off with remains of the last event that crawls
 
5615
              from previous into the current buffer
 
5616
            */
 
5617
            DBUG_ASSERT(crc != crc_0);
 
5618
            crc= my_checksum(crc, cache->read_pos, hdr_offs);
 
5619
            int4store(buf, crc);
 
5620
            remains -= hdr_offs;
 
5621
            DBUG_ASSERT(remains == 0);
 
5622
            if (my_b_write(&log_file, cache->read_pos, hdr_offs) ||
 
5623
                my_b_write(&log_file, buf, BINLOG_CHECKSUM_LEN))
 
5624
              return ER_ERROR_ON_WRITE;
 
5625
            crc= crc_0;
 
5626
          }
 
5627
        }
 
5628
 
 
5629
        if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
 
5630
        {
 
5631
          carry= length - hdr_offs;
 
5632
          memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
 
5633
          length= hdr_offs;
 
5634
        }
 
5635
        else
 
5636
        {
 
5637
          /* we've got a full event-header, and it came in one piece */
 
5638
          uchar *ev= (uchar *)cache->read_pos + hdr_offs;
 
5639
          uint event_len= uint4korr(ev + EVENT_LEN_OFFSET); // netto len
 
5640
          uchar *log_pos= ev + LOG_POS_OFFSET;
 
5641
 
 
5642
          /* fix end_log_pos */
 
5643
          val= uint4korr(log_pos) + group +
 
5644
            (end_log_pos_inc += (do_checksum ? BINLOG_CHECKSUM_LEN : 0));
 
5645
          int4store(log_pos, val);
 
5646
 
 
5647
          /* fix CRC */
 
5648
          if (do_checksum)
 
5649
          {
 
5650
            /* fix length */
 
5651
            int4store(ev + EVENT_LEN_OFFSET, event_len + BINLOG_CHECKSUM_LEN);
 
5652
            remains= fix_log_event_crc(cache->read_pos, hdr_offs, event_len,
 
5653
                                       length, &crc);
 
5654
            if (my_b_write(&log_file, ev, 
 
5655
                           remains == 0 ? event_len : length - hdr_offs))
 
5656
              return ER_ERROR_ON_WRITE;
 
5657
            if (remains == 0)
 
5658
            {
 
5659
              int4store(buf, crc);
 
5660
              if (my_b_write(&log_file, buf, BINLOG_CHECKSUM_LEN))
 
5661
                return ER_ERROR_ON_WRITE;
 
5662
              crc= crc_0; // crc is complete
 
5663
            }
 
5664
          }
 
5665
 
 
5666
          /* next event header at ... */
 
5667
          hdr_offs += event_len; // incr by the netto len
 
5668
 
 
5669
          DBUG_ASSERT(!do_checksum || remains == 0 || hdr_offs >= length);
 
5670
        }
 
5671
      }
 
5672
 
 
5673
      /*
 
5674
        Adjust hdr_offs. Note that it may still point beyond the segment
 
5675
        read in the next iteration; if the current event is very long,
 
5676
        it may take a couple of read-iterations (and subsequent adjustments
 
5677
        of hdr_offs) for it to point into the then-current segment.
 
5678
        If we have a split header (!carry), hdr_offs will be set at the
 
5679
        beginning of the next iteration, overwriting the value we set here:
 
5680
      */
 
5681
      hdr_offs -= length;
 
5682
    }
 
5683
 
 
5684
    /* Write data to the binary log file */
 
5685
    DBUG_EXECUTE_IF("fail_binlog_write_1",
 
5686
                    errno= 28; return ER_ERROR_ON_WRITE;);
 
5687
    if (!do_checksum)
 
5688
      if (my_b_write(&log_file, cache->read_pos, length))
 
5689
        return ER_ERROR_ON_WRITE;
 
5690
    status_var_add(thd->status_var.binlog_bytes_written, length);
 
5691
 
 
5692
    cache->read_pos=cache->read_end;            // Mark buffer used up
 
5693
  } while ((length= my_b_fill(cache)));
 
5694
 
 
5695
  DBUG_ASSERT(carry == 0);
 
5696
  DBUG_ASSERT(!do_checksum || remains == 0);
 
5697
  DBUG_ASSERT(!do_checksum || crc == crc_0);
 
5698
 
 
5699
  return 0;                                     // All OK
 
5700
}
 
5701
 
 
5702
/*
 
5703
  Helper function to get the error code of the query to be binlogged.
 
5704
 */
 
5705
int query_error_code(THD *thd, bool not_killed)
 
5706
{
 
5707
  int error;
 
5708
  
 
5709
  if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA))
 
5710
  {
 
5711
    error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
 
5712
 
 
5713
    /* thd->stmt_da->sql_errno() might be ER_SERVER_SHUTDOWN or
 
5714
       ER_QUERY_INTERRUPTED, So here we need to make sure that error
 
5715
       is not set to these errors when specified not_killed by the
 
5716
       caller.
 
5717
    */
 
5718
    if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED ||
 
5719
        error == ER_NEW_ABORTING_CONNECTION || error == ER_CONNECTION_KILLED)
 
5720
      error= 0;
 
5721
  }
 
5722
  else
 
5723
  {
 
5724
    /* killed status for DELAYED INSERT thread should never be used */
 
5725
    DBUG_ASSERT(!(thd->system_thread & SYSTEM_THREAD_DELAYED_INSERT));
 
5726
    error= thd->killed_errno();
 
5727
  }
 
5728
 
 
5729
  return error;
 
5730
}
 
5731
 
 
5732
 
 
5733
bool MYSQL_BIN_LOG::write_incident_already_locked(THD *thd)
 
5734
{
 
5735
  uint error= 0;
 
5736
  DBUG_ENTER("MYSQL_BIN_LOG::write_incident_already_locked");
 
5737
  Incident incident= INCIDENT_LOST_EVENTS;
 
5738
  Incident_log_event ev(thd, incident, write_error_msg);
 
5739
 
 
5740
  if (likely(is_open()))
 
5741
  {
 
5742
    error= ev.write(&log_file);
 
5743
    status_var_add(thd->status_var.binlog_bytes_written, ev.data_written);
 
5744
  }
 
5745
 
 
5746
  DBUG_RETURN(error);
 
5747
}
 
5748
 
 
5749
 
 
5750
bool MYSQL_BIN_LOG::write_incident(THD *thd)
 
5751
{
 
5752
  uint error= 0;
 
5753
  my_off_t offset;
 
5754
  bool check_purge= false;
 
5755
  DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
 
5756
 
 
5757
  mysql_mutex_lock(&LOCK_log);
 
5758
  if (likely(is_open()))
 
5759
  {
 
5760
    if (!(error= write_incident_already_locked(thd)) &&
 
5761
        !(error= flush_and_sync(0)))
 
5762
    {
 
5763
      signal_update();
 
5764
      if ((error= rotate(false, &check_purge)))
 
5765
        check_purge= false;
 
5766
    }
 
5767
 
 
5768
    offset= my_b_tell(&log_file);
 
5769
    /*
 
5770
      Take mutex to protect against a reader seeing partial writes of 64-bit
 
5771
      offset on 32-bit CPUs.
 
5772
    */
 
5773
    mysql_mutex_lock(&LOCK_commit_ordered);
 
5774
    last_commit_pos_offset= offset;
 
5775
    mysql_mutex_unlock(&LOCK_commit_ordered);
 
5776
    mysql_mutex_unlock(&LOCK_log);
 
5777
 
 
5778
    if (check_purge)
 
5779
      purge();
 
5780
  }
 
5781
 
 
5782
  DBUG_RETURN(error);
 
5783
}
 
5784
 
 
5785
/**
 
5786
  Write a cached log entry to the binary log.
 
5787
  - To support transaction over replication, we wrap the transaction
 
5788
  with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
 
5789
  We want to write a BEGIN/ROLLBACK block when a non-transactional table
 
5790
  was updated in a transaction which was rolled back. This is to ensure
 
5791
  that the same updates are run on the slave.
 
5792
 
 
5793
  @param thd
 
5794
  @param cache          The cache to copy to the binlog
 
5795
  @param commit_event   The commit event to print after writing the
 
5796
                        contents of the cache.
 
5797
  @param incident       Defines if an incident event should be created to
 
5798
                        notify that some non-transactional changes did
 
5799
                        not get into the binlog.
 
5800
 
 
5801
  @note
 
5802
    We only come here if there is something in the cache.
 
5803
  @note
 
5804
    The thing in the cache is always a complete transaction.
 
5805
  @note
 
5806
    'cache' needs to be reinitialized after this functions returns.
 
5807
*/
 
5808
 
 
5809
bool
 
5810
MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
 
5811
                                           binlog_cache_mngr *cache_mngr,
 
5812
                                           Log_event *end_ev, bool all,
 
5813
                                           bool using_stmt_cache,
 
5814
                                           bool using_trx_cache)
 
5815
{
 
5816
  group_commit_entry entry;
 
5817
  DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_to_binlog");
 
5818
 
 
5819
  entry.thd= thd;
 
5820
  entry.cache_mngr= cache_mngr;
 
5821
  entry.error= 0;
 
5822
  entry.all= all;
 
5823
  entry.using_stmt_cache= using_stmt_cache;
 
5824
  entry.using_trx_cache= using_trx_cache;
 
5825
 
 
5826
  /*
 
5827
    Log "BEGIN" at the beginning of every transaction.  Here, a transaction is
 
5828
    either a BEGIN..COMMIT block or a single statement in autocommit mode.
 
5829
 
 
5830
    Create the necessary events here, where we have the correct THD (and
 
5831
    thread context).
 
5832
 
 
5833
    Due to group commit the actual writing to binlog may happen in a different
 
5834
    thread.
 
5835
  */
 
5836
  Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), using_trx_cache, TRUE,
 
5837
                        TRUE, 0);
 
5838
  entry.begin_event= &qinfo;
 
5839
  entry.end_event= end_ev;
 
5840
  if (cache_mngr->stmt_cache.has_incident() ||
 
5841
      cache_mngr->trx_cache.has_incident())
 
5842
  {
 
5843
    Incident_log_event inc_ev(thd, INCIDENT_LOST_EVENTS, write_error_msg);
 
5844
    entry.incident_event= &inc_ev;
 
5845
    DBUG_RETURN(write_transaction_to_binlog_events(&entry));
 
5846
  }
 
5847
  else
 
5848
  {
 
5849
    entry.incident_event= NULL;
 
5850
    DBUG_RETURN(write_transaction_to_binlog_events(&entry));
 
5851
  }
 
5852
}
 
5853
 
 
5854
bool
 
5855
MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
 
5856
{
 
5857
  /*
 
5858
    To facilitate group commit for the binlog, we first queue up ourselves in
 
5859
    the group commit queue. Then the first thread to enter the queue waits for
 
5860
    the LOCK_log mutex, and commits for everyone in the queue once it gets the
 
5861
    lock. Any other threads in the queue just wait for the first one to finish
 
5862
    the commit and wake them up.
 
5863
  */
 
5864
 
 
5865
  entry->thd->clear_wakeup_ready();
 
5866
  mysql_mutex_lock(&LOCK_prepare_ordered);
 
5867
  group_commit_entry *orig_queue= group_commit_queue;
 
5868
  entry->next= orig_queue;
 
5869
  group_commit_queue= entry;
 
5870
 
 
5871
  if (entry->cache_mngr->using_xa)
 
5872
  {
 
5873
    DEBUG_SYNC(entry->thd, "commit_before_prepare_ordered");
 
5874
    run_prepare_ordered(entry->thd, entry->all);
 
5875
    DEBUG_SYNC(entry->thd, "commit_after_prepare_ordered");
 
5876
  }
 
5877
  mysql_mutex_unlock(&LOCK_prepare_ordered);
 
5878
  DEBUG_SYNC(entry->thd, "commit_after_release_LOCK_prepare_ordered");
 
5879
 
 
5880
  /*
 
5881
    The first in the queue handle group commit for all; the others just wait
 
5882
    to be signalled when group commit is done.
 
5883
  */
 
5884
  if (orig_queue != NULL)
 
5885
    entry->thd->wait_for_wakeup_ready();
 
5886
  else
 
5887
    trx_group_commit_leader(entry);
 
5888
 
 
5889
  if (!opt_optimize_thread_scheduling)
 
5890
  {
 
5891
    /* For the leader, trx_group_commit_leader() already took the lock. */
 
5892
    if (orig_queue != NULL)
 
5893
      mysql_mutex_lock(&LOCK_commit_ordered);
 
5894
 
 
5895
    DEBUG_SYNC(entry->thd, "commit_loop_entry_commit_ordered");
 
5896
    ++num_commits;
 
5897
    if (entry->cache_mngr->using_xa && !entry->error)
 
5898
      run_commit_ordered(entry->thd, entry->all);
 
5899
 
 
5900
    group_commit_entry *next= entry->next;
 
5901
    if (!next)
 
5902
    {
 
5903
      group_commit_queue_busy= FALSE;
 
5904
      mysql_cond_signal(&COND_queue_busy);
 
5905
      DEBUG_SYNC(entry->thd, "commit_after_group_run_commit_ordered");
 
5906
    }
 
5907
    mysql_mutex_unlock(&LOCK_commit_ordered);
 
5908
 
 
5909
    if (next)
 
5910
    {
 
5911
      next->thd->signal_wakeup_ready();
 
5912
    }
 
5913
  }
 
5914
 
 
5915
  if (likely(!entry->error))
 
5916
    return 0;
 
5917
 
 
5918
  switch (entry->error)
 
5919
  {
 
5920
  case ER_ERROR_ON_WRITE:
 
5921
    my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, entry->commit_errno);
 
5922
    break;
 
5923
  case ER_ERROR_ON_READ:
 
5924
    my_error(ER_ERROR_ON_READ, MYF(ME_NOREFRESH),
 
5925
             entry->error_cache->file_name, entry->commit_errno);
 
5926
    break;
 
5927
  default:
 
5928
    /*
 
5929
      There are not (and should not be) any errors thrown not covered above.
 
5930
      But just in case one is added later without updating the above switch
 
5931
      statement, include a catch-all.
 
5932
    */
 
5933
    my_printf_error(entry->error,
 
5934
                    "Error writing transaction to binary log: %d",
 
5935
                    MYF(ME_NOREFRESH), entry->error);
 
5936
  }
 
5937
 
 
5938
  /*
 
5939
    Since we return error, this transaction XID will not be committed, so
 
5940
    we need to mark it as not needed for recovery (unlog() is not called
 
5941
    for a transaction if log_xid() fails).
 
5942
  */
 
5943
  if (entry->cache_mngr->using_xa && entry->cache_mngr->xa_xid)
 
5944
    mark_xid_done();
 
5945
 
 
5946
  return 1;
 
5947
}
 
5948
 
 
5949
/*
 
5950
  Do binlog group commit as the lead thread.
 
5951
 
 
5952
  This must be called when this statement/transaction is queued at the start of
 
5953
  the group_commit_queue. It will wait to obtain the LOCK_log mutex, then group
 
5954
  commit all the transactions in the queue (more may have entered while waiting
 
5955
  for LOCK_log). After commit is done, all other threads in the queue will be
 
5956
  signalled.
 
5957
 
 
5958
 */
 
5959
void
 
5960
MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
 
5961
{
 
5962
  uint xid_count= 0;
 
5963
  my_off_t UNINIT_VAR(commit_offset);
 
5964
  group_commit_entry *current;
 
5965
  group_commit_entry *last_in_queue;
 
5966
  group_commit_entry *queue= NULL;
 
5967
  bool check_purge= false;
 
5968
  DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader");
 
5969
 
 
5970
  DBUG_ASSERT(is_open());
 
5971
  if (likely(is_open()))                       // Should always be true
 
5972
  {
 
5973
    /*
 
5974
      Lock the LOCK_log(), and once we get it, collect any additional writes
 
5975
      that queued up while we were waiting.
 
5976
    */
 
5977
    mysql_mutex_lock(&LOCK_log);
 
5978
    DEBUG_SYNC(leader->thd, "commit_after_get_LOCK_log");
 
5979
 
 
5980
    mysql_mutex_lock(&LOCK_prepare_ordered);
 
5981
    current= group_commit_queue;
 
5982
    group_commit_queue= NULL;
 
5983
    mysql_mutex_unlock(&LOCK_prepare_ordered);
 
5984
 
 
5985
    /* As the queue is in reverse order of entering, reverse it. */
 
5986
    last_in_queue= current;
 
5987
    while (current)
 
5988
    {
 
5989
      group_commit_entry *next= current->next;
 
5990
      current->next= queue;
 
5991
      queue= current;
 
5992
      current= next;
 
5993
    }
 
5994
    DBUG_ASSERT(leader == queue /* the leader should be first in queue */);
 
5995
 
 
5996
    /* Now we have in queue the list of transactions to be committed in order. */
 
5997
    
 
5998
    /*
 
5999
      Commit every transaction in the queue.
 
6000
 
 
6001
      Note that we are doing this in a different thread than the one running
 
6002
      the transaction! So we are limited in the operations we can do. In
 
6003
      particular, we cannot call my_error() on behalf of a transaction, as
 
6004
      that obtains the THD from thread local storage. Instead, we must set
 
6005
      current->error and let the thread do the error reporting itself once
 
6006
      we wake it up.
 
6007
    */
 
6008
    for (current= queue; current != NULL; current= current->next)
 
6009
    {
 
6010
      binlog_cache_mngr *cache_mngr= current->cache_mngr;
 
6011
 
 
6012
      /*
 
6013
        We already checked before that at least one cache is non-empty; if both
 
6014
        are empty we would have skipped calling into here.
 
6015
      */
 
6016
      DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
 
6017
 
 
6018
      current->error= write_transaction_or_stmt(current);
 
6019
 
 
6020
      strmake_buf(cache_mngr->last_commit_pos_file, log_file_name);
 
6021
      commit_offset= my_b_write_tell(&log_file);
 
6022
      cache_mngr->last_commit_pos_offset= commit_offset;
 
6023
      if (cache_mngr->using_xa && cache_mngr->xa_xid)
 
6024
        xid_count++;
 
6025
    }
 
6026
 
 
6027
    bool synced= 0;
 
6028
    if (flush_and_sync(&synced))
 
6029
    {
 
6030
      for (current= queue; current != NULL; current= current->next)
 
6031
      {
 
6032
        if (!current->error)
 
6033
        {
 
6034
          current->error= ER_ERROR_ON_WRITE;
 
6035
          current->commit_errno= errno;
 
6036
          current->error_cache= NULL;
 
6037
        }
 
6038
      }
 
6039
    }
 
6040
    else
 
6041
    {
 
6042
      bool any_error= false;
 
6043
      bool all_error= true;
 
6044
      for (current= queue; current != NULL; current= current->next)
 
6045
      {
 
6046
        if (!current->error &&
 
6047
            RUN_HOOK(binlog_storage, after_flush,
 
6048
                (current->thd, log_file_name,
 
6049
                 current->cache_mngr->last_commit_pos_offset, synced)))
 
6050
        {
 
6051
          current->error= ER_ERROR_ON_WRITE;
 
6052
          current->commit_errno= -1;
 
6053
          current->error_cache= NULL;
 
6054
          any_error= true;
 
6055
        }
 
6056
        else
 
6057
          all_error= false;
 
6058
      }
 
6059
 
 
6060
      if (any_error)
 
6061
        sql_print_error("Failed to run 'after_flush' hooks");
 
6062
      if (!all_error)
 
6063
        signal_update();
 
6064
    }
 
6065
 
 
6066
    /*
 
6067
      if any commit_events are Xid_log_event, increase the number of
 
6068
      prepared_xids (it's decreased in ::unlog()). Binlog cannot be rotated
 
6069
      if there're prepared xids in it - see the comment in new_file() for
 
6070
      an explanation.
 
6071
      If no Xid_log_events (then it's all Query_log_event) rotate binlog,
 
6072
      if necessary.
 
6073
    */
 
6074
    if (xid_count > 0)
 
6075
    {
 
6076
      mark_xids_active(xid_count);
 
6077
    }
 
6078
    else
 
6079
    {
 
6080
      if (rotate(false, &check_purge))
 
6081
      {
 
6082
        /*
 
6083
          If we fail to rotate, which thread should get the error?
 
6084
          We give the error to the *last* transaction thread; that seems to
 
6085
          make the most sense, as it was the last to write to the log.
 
6086
        */
 
6087
        last_in_queue->error= ER_ERROR_ON_WRITE;
 
6088
        last_in_queue->commit_errno= errno;
 
6089
        check_purge= false;
 
6090
      }
 
6091
    }
 
6092
  }
 
6093
 
 
6094
  DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered");
 
6095
  mysql_mutex_lock(&LOCK_commit_ordered);
 
6096
  last_commit_pos_offset= commit_offset;
 
6097
  /*
 
6098
    We cannot unlock LOCK_log until we have locked LOCK_commit_ordered;
 
6099
    otherwise scheduling could allow the next group commit to run ahead of us,
 
6100
    messing up the order of commit_ordered() calls. But as soon as
 
6101
    LOCK_commit_ordered is obtained, we can let the next group commit start.
 
6102
  */
 
6103
  mysql_mutex_unlock(&LOCK_log);
 
6104
 
 
6105
  if (check_purge) 
 
6106
    purge();
 
6107
 
 
6108
  DEBUG_SYNC(leader->thd, "commit_after_release_LOCK_log");
 
6109
  ++num_group_commits;
 
6110
 
 
6111
  if (!opt_optimize_thread_scheduling)
 
6112
  {
 
6113
    /*
 
6114
      If we want to run commit_ordered() each in the transaction's own thread
 
6115
      context, then we need to mark the queue reserved; we need to finish all
 
6116
      threads in one group commit before the next group commit can be allowed
 
6117
      to proceed, and we cannot unlock a simple pthreads mutex in a different
 
6118
      thread from the one that locked it.
 
6119
    */
 
6120
 
 
6121
    while (group_commit_queue_busy)
 
6122
      mysql_cond_wait(&COND_queue_busy, &LOCK_commit_ordered);
 
6123
    group_commit_queue_busy= TRUE;
 
6124
 
 
6125
    /* Note that we return with LOCK_commit_ordered locked! */
 
6126
    DBUG_VOID_RETURN;
 
6127
  }
 
6128
 
 
6129
  /*
 
6130
    Wakeup each participant waiting for our group commit, first calling the
 
6131
    commit_ordered() methods for any transactions doing 2-phase commit.
 
6132
  */
 
6133
  current= queue;
 
6134
  while (current != NULL)
 
6135
  {
 
6136
    group_commit_entry *next;
 
6137
 
 
6138
    DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered");
 
6139
    ++num_commits;
 
6140
    if (current->cache_mngr->using_xa && !current->error)
 
6141
      run_commit_ordered(current->thd, current->all);
 
6142
 
 
6143
    /*
 
6144
      Careful not to access current->next after waking up the other thread! As
 
6145
      it may change immediately after wakeup.
 
6146
    */
 
6147
    next= current->next;
 
6148
    if (current != leader)                      // Don't wake up ourself
 
6149
      current->thd->signal_wakeup_ready();
 
6150
    current= next;
 
6151
  }
 
6152
  DEBUG_SYNC(leader->thd, "commit_after_group_run_commit_ordered");
 
6153
  mysql_mutex_unlock(&LOCK_commit_ordered);
 
6154
 
 
6155
  DBUG_VOID_RETURN;
 
6156
}
 
6157
 
 
6158
 
 
6159
int
 
6160
MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
 
6161
{
 
6162
  binlog_cache_mngr *mngr= entry->cache_mngr;
 
6163
 
 
6164
  if (entry->begin_event->write(&log_file))
 
6165
    return ER_ERROR_ON_WRITE;
 
6166
  status_var_add(entry->thd->status_var.binlog_bytes_written,
 
6167
                 entry->begin_event->data_written);
 
6168
 
 
6169
  if (entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
 
6170
      write_cache(entry->thd, mngr->get_binlog_cache_log(FALSE)))
 
6171
  {
 
6172
    entry->error_cache= &mngr->stmt_cache.cache_log;
 
6173
    entry->commit_errno= errno;
 
6174
    return ER_ERROR_ON_WRITE;
 
6175
  }
 
6176
 
 
6177
  if (entry->using_trx_cache && !mngr->trx_cache.empty())
 
6178
  {
 
6179
    DBUG_EXECUTE_IF("crash_before_writing_xid",
 
6180
                    {
 
6181
                      if ((write_cache(entry->thd,
 
6182
                                       mngr->get_binlog_cache_log(TRUE))))
 
6183
                        DBUG_PRINT("info", ("error writing binlog cache"));
 
6184
                      else
 
6185
                        flush_and_sync(0);
 
6186
 
 
6187
                      DBUG_PRINT("info", ("crashing before writing xid"));
 
6188
                      DBUG_SUICIDE();
 
6189
                    });
 
6190
 
 
6191
    if (write_cache(entry->thd, mngr->get_binlog_cache_log(TRUE)))
 
6192
    {
 
6193
      entry->error_cache= &mngr->trx_cache.cache_log;
 
6194
      entry->commit_errno= errno;
 
6195
      return ER_ERROR_ON_WRITE;
 
6196
    }
 
6197
  }
 
6198
 
 
6199
  if (entry->end_event->write(&log_file))
 
6200
  {
 
6201
    entry->error_cache= NULL;
 
6202
    entry->commit_errno= errno;
 
6203
    return ER_ERROR_ON_WRITE;
 
6204
  }
 
6205
  status_var_add(entry->thd->status_var.binlog_bytes_written,
 
6206
                 entry->end_event->data_written);
 
6207
 
 
6208
  if (entry->incident_event)
 
6209
  {
 
6210
    if (entry->incident_event->write(&log_file))
 
6211
    {
 
6212
      entry->error_cache= NULL;
 
6213
      entry->commit_errno= errno;
 
6214
      return ER_ERROR_ON_WRITE;
 
6215
    }
 
6216
  }
 
6217
 
 
6218
  if (mngr->get_binlog_cache_log(FALSE)->error) // Error on read
 
6219
  {
 
6220
    entry->error_cache= &mngr->stmt_cache.cache_log;
 
6221
    entry->commit_errno= errno;
 
6222
    return ER_ERROR_ON_READ;
 
6223
  }
 
6224
  if (mngr->get_binlog_cache_log(TRUE)->error)  // Error on read
 
6225
  {
 
6226
    entry->error_cache= &mngr->trx_cache.cache_log;
 
6227
    entry->commit_errno= errno;
 
6228
    return ER_ERROR_ON_READ;
 
6229
  }
 
6230
 
 
6231
  return 0;
 
6232
}
 
6233
 
 
6234
/**
 
6235
  Wait until we get a signal that the relay log has been updated.
 
6236
 
 
6237
  @param thd            Thread variable
 
6238
 
 
6239
  @note
 
6240
    One must have a lock on LOCK_log before calling this function.
 
6241
    This lock will be released before return! That's required by
 
6242
    THD::enter_cond() (see NOTES in sql_class.h).
 
6243
*/
 
6244
 
 
6245
void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
 
6246
{
 
6247
  const char *old_msg;
 
6248
  DBUG_ENTER("wait_for_update_relay_log");
 
6249
 
 
6250
  old_msg= thd->enter_cond(&update_cond, &LOCK_log,
 
6251
                           "Slave has read all relay log; "
 
6252
                           "waiting for the slave I/O "
 
6253
                           "thread to update it" );
 
6254
  mysql_cond_wait(&update_cond, &LOCK_log);
 
6255
  thd->exit_cond(old_msg);
 
6256
  DBUG_VOID_RETURN;
 
6257
}
 
6258
 
 
6259
/**
 
6260
  Wait until we get a signal that the binary log has been updated.
 
6261
  Applies to master only.
 
6262
     
 
6263
  NOTES
 
6264
  @param[in] thd        a THD struct
 
6265
  @param[in] timeout    a pointer to a timespec;
 
6266
                        NULL means to wait w/o timeout.
 
6267
  @retval    0          if got signalled on update
 
6268
  @retval    non-0      if wait timeout elapsed
 
6269
  @note
 
6270
    LOCK_log must be taken before calling this function.
 
6271
    LOCK_log is being released while the thread is waiting.
 
6272
    LOCK_log is released by the caller.
 
6273
*/
 
6274
 
 
6275
int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
 
6276
                                           const struct timespec *timeout)
 
6277
{
 
6278
  int ret= 0;
 
6279
  DBUG_ENTER("wait_for_update_bin_log");
 
6280
 
 
6281
  if (!timeout)
 
6282
    mysql_cond_wait(&update_cond, &LOCK_log);
 
6283
  else
 
6284
    ret= mysql_cond_timedwait(&update_cond, &LOCK_log,
 
6285
                              const_cast<struct timespec *>(timeout));
 
6286
  DBUG_RETURN(ret);
 
6287
}
 
6288
 
 
6289
 
 
6290
/**
 
6291
  Close the log file.
 
6292
 
 
6293
  @param exiting     Bitmask for one or more of the following bits:
 
6294
          - LOG_CLOSE_INDEX : if we should close the index file
 
6295
          - LOG_CLOSE_TO_BE_OPENED : if we intend to call open
 
6296
                                     at once after close.
 
6297
          - LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log
 
6298
          - LOG_CLOSE_DELAYED_CLOSE : do not yet close the file and clear the
 
6299
                                      LOG_EVENT_BINLOG_IN_USE_F flag
 
6300
 
 
6301
  @note
 
6302
    One can do an open on the object at once after doing a close.
 
6303
    The internal structures are not freed until cleanup() is called
 
6304
*/
 
6305
 
 
6306
void MYSQL_BIN_LOG::close(uint exiting)
 
6307
{                                       // One can't set log_type here!
 
6308
  DBUG_ENTER("MYSQL_BIN_LOG::close");
 
6309
  DBUG_PRINT("enter",("exiting: %d", (int) exiting));
 
6310
  if (log_state == LOG_OPENED)
 
6311
  {
 
6312
#ifdef HAVE_REPLICATION
 
6313
    if (log_type == LOG_BIN && !no_auto_events &&
 
6314
        (exiting & LOG_CLOSE_STOP_EVENT))
 
6315
    {
 
6316
      Stop_log_event s;
 
6317
      // the checksumming rule for relay-log case is similar to Rotate
 
6318
        s.checksum_alg= is_relay_log ?
 
6319
          (uint8) relay_log_checksum_alg : (uint8) binlog_checksum_options;
 
6320
      DBUG_ASSERT(!is_relay_log ||
 
6321
                  relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
 
6322
      s.write(&log_file);
 
6323
      bytes_written+= s.data_written;
 
6324
      signal_update();
 
6325
    }
 
6326
#endif /* HAVE_REPLICATION */
 
6327
 
 
6328
    /* don't pwrite in a file opened with O_APPEND - it doesn't work */
 
6329
    if (log_file.type == WRITE_CACHE && log_type == LOG_BIN
 
6330
        && !(exiting & LOG_CLOSE_DELAYED_CLOSE))
 
6331
    {
 
6332
      my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
 
6333
      clear_inuse_flag_when_closing(log_file.file);
 
6334
      /*
 
6335
        Restore position so that anything we have in the IO_cache is written
 
6336
        to the correct position.
 
6337
        We need the seek here, as mysql_file_pwrite() is not guaranteed to keep the
 
6338
        original position on system that doesn't support pwrite().
 
6339
      */
 
6340
      mysql_file_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));
 
6341
    }
 
6342
 
 
6343
    /* this will cleanup IO_CACHE, sync and close the file */
 
6344
    MYSQL_LOG::close(exiting);
 
6345
  }
 
6346
 
 
6347
  /*
 
6348
    The following test is needed even if is_open() is not set, as we may have
 
6349
    called a not complete close earlier and the index file is still open.
 
6350
  */
 
6351
 
 
6352
  if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
 
6353
  {
 
6354
    end_io_cache(&index_file);
 
6355
    if (mysql_file_close(index_file.file, MYF(0)) < 0 && ! write_error)
 
6356
    {
 
6357
      write_error= 1;
 
6358
      sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno);
 
6359
    }
 
6360
  }
 
6361
  log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
 
6362
  my_free(name);
 
6363
  name= NULL;
 
6364
  DBUG_VOID_RETURN;
 
6365
}
 
6366
 
 
6367
 
 
6368
/*
 
6369
  Clear the LOG_EVENT_BINLOG_IN_USE_F; this marks the binlog file as cleanly
 
6370
  closed and not needing crash recovery.
 
6371
*/
 
6372
void MYSQL_BIN_LOG::clear_inuse_flag_when_closing(File file)
 
6373
{
 
6374
  my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
 
6375
  uchar flags= 0;            // clearing LOG_EVENT_BINLOG_IN_USE_F
 
6376
  mysql_file_pwrite(file, &flags, 1, offset, MYF(0));
 
6377
}
 
6378
 
 
6379
 
 
6380
void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
 
6381
{
 
6382
  /*
 
6383
    We need to take locks, otherwise this may happen:
 
6384
    new_file() is called, calls open(old_max_size), then before open() starts,
 
6385
    set_max_size() sets max_size to max_size_arg, then open() starts and
 
6386
    uses the old_max_size argument, so max_size_arg has been overwritten and
 
6387
    it's like if the SET command was never run.
 
6388
  */
 
6389
  DBUG_ENTER("MYSQL_BIN_LOG::set_max_size");
 
6390
  mysql_mutex_lock(&LOCK_log);
 
6391
  if (is_open())
 
6392
    max_size= max_size_arg;
 
6393
  mysql_mutex_unlock(&LOCK_log);
 
6394
  DBUG_VOID_RETURN;
 
6395
}
 
6396
 
 
6397
 
 
6398
/**
 
6399
  Check if a string is a valid number.
 
6400
 
 
6401
  @param str                    String to test
 
6402
  @param res                    Store value here
 
6403
  @param allow_wildcards        Set to 1 if we should ignore '%' and '_'
 
6404
 
 
6405
  @note
 
6406
    For the moment the allow_wildcards argument is not used
 
6407
    Should be move to some other file.
 
6408
 
 
6409
  @retval
 
6410
    1   String is a number
 
6411
  @retval
 
6412
    0   String is not a number
 
6413
*/
 
6414
 
 
6415
static bool test_if_number(register const char *str,
 
6416
                           ulong *res, bool allow_wildcards)
 
6417
{
 
6418
  reg2 int flag;
 
6419
  const char *start;
 
6420
  DBUG_ENTER("test_if_number");
 
6421
 
 
6422
  flag=0; start=str;
 
6423
  while (*str++ == ' ') ;
 
6424
  if (*--str == '-' || *str == '+')
 
6425
    str++;
 
6426
  while (my_isdigit(files_charset_info,*str) ||
 
6427
         (allow_wildcards && (*str == wild_many || *str == wild_one)))
 
6428
  {
 
6429
    flag=1;
 
6430
    str++;
 
6431
  }
 
6432
  if (*str == '.')
 
6433
  {
 
6434
    for (str++ ;
 
6435
         my_isdigit(files_charset_info,*str) ||
 
6436
           (allow_wildcards && (*str == wild_many || *str == wild_one)) ;
 
6437
         str++, flag=1) ;
 
6438
  }
 
6439
  if (*str != 0 || flag == 0)
 
6440
    DBUG_RETURN(0);
 
6441
  if (res)
 
6442
    *res=atol(start);
 
6443
  DBUG_RETURN(1);                       /* Number ok */
 
6444
} /* test_if_number */
 
6445
 
 
6446
 
 
6447
void sql_perror(const char *message)
 
6448
{
 
6449
#if defined(_WIN32)
 
6450
  char* buf;
 
6451
  DWORD dw= GetLastError();
 
6452
  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |  FORMAT_MESSAGE_FROM_SYSTEM |
 
6453
        FORMAT_MESSAGE_IGNORE_INSERTS,  NULL, dw,
 
6454
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL ) > 0)
 
6455
  {
 
6456
    sql_print_error("%s: %s",message, buf);
 
6457
    LocalFree((HLOCAL)buf);
 
6458
  }
 
6459
  else
 
6460
  {
 
6461
    sql_print_error("%s", message);
 
6462
  }
 
6463
#elif defined(HAVE_STRERROR)
 
6464
  sql_print_error("%s: %s",message, strerror(errno));
 
6465
#else 
 
6466
  perror(message);
 
6467
#endif
 
6468
}
 
6469
 
 
6470
 
 
6471
/*
 
6472
  Change the file associated with two output streams. Used to
 
6473
  redirect stdout and stderr to a file. The streams are reopened
 
6474
  only for appending (writing at end of file).
 
6475
*/
 
6476
extern "C" my_bool reopen_fstreams(const char *filename,
 
6477
                                   FILE *outstream, FILE *errstream)
 
6478
{
 
6479
  if (outstream && !my_freopen(filename, "a", outstream))
 
6480
    return TRUE;
 
6481
 
 
6482
  if (errstream && !my_freopen(filename, "a", errstream))
 
6483
    return TRUE;
 
6484
 
 
6485
  /* The error stream must be unbuffered. */
 
6486
  if (errstream)
 
6487
    setbuf(errstream, NULL);
 
6488
 
 
6489
  return FALSE;
 
6490
}
 
6491
 
 
6492
 
 
6493
/*
 
6494
  Unfortunately, there seems to be no good way
 
6495
  to restore the original streams upon failure.
 
6496
*/
 
6497
static bool redirect_std_streams(const char *file)
 
6498
{
 
6499
  if (reopen_fstreams(file, stdout, stderr))
 
6500
    return TRUE;
 
6501
 
 
6502
  setbuf(stderr, NULL);
 
6503
  return FALSE;
 
6504
}
 
6505
 
 
6506
 
 
6507
bool flush_error_log()
 
6508
{
 
6509
  bool result= 0;
 
6510
  if (opt_error_log)
 
6511
  {
 
6512
    mysql_mutex_lock(&LOCK_error_log);
 
6513
    if (redirect_std_streams(log_error_file))
 
6514
      result= 1;
 
6515
    mysql_mutex_unlock(&LOCK_error_log);
 
6516
  }
 
6517
  return result;
 
6518
}
 
6519
 
 
6520
void MYSQL_BIN_LOG::signal_update()
 
6521
{
 
6522
  DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
 
6523
  signal_cnt++;
 
6524
  mysql_cond_broadcast(&update_cond);
 
6525
  DBUG_VOID_RETURN;
 
6526
}
 
6527
 
 
6528
#ifdef _WIN32
 
6529
static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
 
6530
                                        size_t length, size_t buffLen)
 
6531
{
 
6532
  HANDLE event;
 
6533
  char   *buffptr= buff;
 
6534
  DBUG_ENTER("print_buffer_to_nt_eventlog");
 
6535
 
 
6536
  /* Add ending CR/LF's to string, overwrite last chars if necessary */
 
6537
  strmov(buffptr+min(length, buffLen-5), "\r\n\r\n");
 
6538
 
 
6539
  setup_windows_event_source();
 
6540
  if ((event= RegisterEventSource(NULL,"MySQL")))
 
6541
  {
 
6542
    switch (level) {
 
6543
      case ERROR_LEVEL:
 
6544
        ReportEvent(event, EVENTLOG_ERROR_TYPE, 0, MSG_DEFAULT, NULL, 1, 0,
 
6545
                    (LPCSTR*)&buffptr, NULL);
 
6546
        break;
 
6547
      case WARNING_LEVEL:
 
6548
        ReportEvent(event, EVENTLOG_WARNING_TYPE, 0, MSG_DEFAULT, NULL, 1, 0,
 
6549
                    (LPCSTR*) &buffptr, NULL);
 
6550
        break;
 
6551
      case INFORMATION_LEVEL:
 
6552
        ReportEvent(event, EVENTLOG_INFORMATION_TYPE, 0, MSG_DEFAULT, NULL, 1,
 
6553
                    0, (LPCSTR*) &buffptr, NULL);
 
6554
        break;
 
6555
    }
 
6556
    DeregisterEventSource(event);
 
6557
  }
 
6558
 
 
6559
  DBUG_VOID_RETURN;
 
6560
}
 
6561
#endif /* _WIN32 */
 
6562
 
 
6563
 
 
6564
#ifndef EMBEDDED_LIBRARY
 
6565
static void print_buffer_to_file(enum loglevel level, const char *buffer,
 
6566
                                 size_t length)
 
6567
{
 
6568
  time_t skr;
 
6569
  struct tm tm_tmp;
 
6570
  struct tm *start;
 
6571
  DBUG_ENTER("print_buffer_to_file");
 
6572
  DBUG_PRINT("enter",("buffer: %s", buffer));
 
6573
 
 
6574
  mysql_mutex_lock(&LOCK_error_log);
 
6575
 
 
6576
  skr= my_time(0);
 
6577
  localtime_r(&skr, &tm_tmp);
 
6578
  start=&tm_tmp;
 
6579
 
 
6580
  fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %.*s\n",
 
6581
          start->tm_year % 100,
 
6582
          start->tm_mon+1,
 
6583
          start->tm_mday,
 
6584
          start->tm_hour,
 
6585
          start->tm_min,
 
6586
          start->tm_sec,
 
6587
          (level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ?
 
6588
           "Warning" : "Note"),
 
6589
          (int) length, buffer);
 
6590
 
 
6591
  fflush(stderr);
 
6592
 
 
6593
  mysql_mutex_unlock(&LOCK_error_log);
 
6594
  DBUG_VOID_RETURN;
 
6595
}
 
6596
 
 
6597
/**
 
6598
  Prints a printf style message to the error log and, under NT, to the
 
6599
  Windows event log.
 
6600
 
 
6601
  This function prints the message into a buffer and then sends that buffer
 
6602
  to other functions to write that message to other logging sources.
 
6603
 
 
6604
  @param level          The level of the msg significance
 
6605
  @param format         Printf style format of message
 
6606
  @param args           va_list list of arguments for the message
 
6607
 
 
6608
  @returns
 
6609
    The function always returns 0. The return value is present in the
 
6610
    signature to be compatible with other logging routines, which could
 
6611
    return an error (e.g. logging to the log tables)
 
6612
*/
 
6613
int vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
 
6614
{
 
6615
  char   buff[1024];
 
6616
  size_t length;
 
6617
  DBUG_ENTER("vprint_msg_to_log");
 
6618
 
 
6619
  length= my_vsnprintf(buff, sizeof(buff), format, args);
 
6620
  print_buffer_to_file(level, buff, length);
 
6621
 
 
6622
#ifdef _WIN32
 
6623
  print_buffer_to_nt_eventlog(level, buff, length, sizeof(buff));
 
6624
#endif
 
6625
 
 
6626
  DBUG_RETURN(0);
 
6627
}
 
6628
#endif /* EMBEDDED_LIBRARY */
 
6629
 
 
6630
 
 
6631
void sql_print_error(const char *format, ...) 
 
6632
{
 
6633
  va_list args;
 
6634
  DBUG_ENTER("sql_print_error");
 
6635
 
 
6636
  va_start(args, format);
 
6637
  error_log_print(ERROR_LEVEL, format, args);
 
6638
  va_end(args);
 
6639
 
 
6640
  DBUG_VOID_RETURN;
 
6641
}
 
6642
 
 
6643
 
 
6644
void sql_print_warning(const char *format, ...) 
 
6645
{
 
6646
  va_list args;
 
6647
  DBUG_ENTER("sql_print_warning");
 
6648
 
 
6649
  va_start(args, format);
 
6650
  error_log_print(WARNING_LEVEL, format, args);
 
6651
  va_end(args);
 
6652
 
 
6653
  DBUG_VOID_RETURN;
 
6654
}
 
6655
 
 
6656
 
 
6657
void sql_print_information(const char *format, ...) 
 
6658
{
 
6659
  va_list args;
 
6660
  DBUG_ENTER("sql_print_information");
 
6661
 
 
6662
  va_start(args, format);
 
6663
  error_log_print(INFORMATION_LEVEL, format, args);
 
6664
  va_end(args);
 
6665
 
 
6666
  DBUG_VOID_RETURN;
 
6667
}
 
6668
 
 
6669
 
 
6670
void
 
6671
TC_LOG::run_prepare_ordered(THD *thd, bool all)
 
6672
{
 
6673
  Ha_trx_info *ha_info=
 
6674
    all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
 
6675
 
 
6676
  mysql_mutex_assert_owner(&LOCK_prepare_ordered);
 
6677
  for (; ha_info; ha_info= ha_info->next())
 
6678
  {
 
6679
    handlerton *ht= ha_info->ht();
 
6680
    if (!ht->prepare_ordered)
 
6681
      continue;
 
6682
    ht->prepare_ordered(ht, thd, all);
 
6683
  }
 
6684
}
 
6685
 
 
6686
 
 
6687
void
 
6688
TC_LOG::run_commit_ordered(THD *thd, bool all)
 
6689
{
 
6690
  Ha_trx_info *ha_info=
 
6691
    all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
 
6692
 
 
6693
  mysql_mutex_assert_owner(&LOCK_commit_ordered);
 
6694
  for (; ha_info; ha_info= ha_info->next())
 
6695
  {
 
6696
    handlerton *ht= ha_info->ht();
 
6697
    if (!ht->commit_ordered)
 
6698
      continue;
 
6699
    ht->commit_ordered(ht, thd, all);
 
6700
    DEBUG_SYNC(thd, "commit_after_run_commit_ordered");
 
6701
  }
 
6702
}
 
6703
 
 
6704
 
 
6705
int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
 
6706
                               bool need_prepare_ordered,
 
6707
                               bool need_commit_ordered)
 
6708
{
 
6709
  int cookie;
 
6710
  struct commit_entry entry;
 
6711
  bool is_group_commit_leader;
 
6712
  LINT_INIT(is_group_commit_leader);
 
6713
 
 
6714
  if (need_prepare_ordered)
 
6715
  {
 
6716
    mysql_mutex_lock(&LOCK_prepare_ordered);
 
6717
    run_prepare_ordered(thd, all);
 
6718
    if (need_commit_ordered)
 
6719
    {
 
6720
      /*
 
6721
        Must put us in queue so we can run_commit_ordered() in same sequence
 
6722
        as we did run_prepare_ordered().
 
6723
      */
 
6724
      thd->clear_wakeup_ready();
 
6725
      entry.thd= thd;
 
6726
      commit_entry *previous_queue= commit_ordered_queue;
 
6727
      entry.next= previous_queue;
 
6728
      commit_ordered_queue= &entry;
 
6729
      is_group_commit_leader= (previous_queue == NULL);
 
6730
    }
 
6731
    mysql_mutex_unlock(&LOCK_prepare_ordered);
 
6732
  }
 
6733
 
 
6734
  cookie= 0;
 
6735
  if (xid)
 
6736
    cookie= log_one_transaction(xid);
 
6737
 
 
6738
  if (need_commit_ordered)
 
6739
  {
 
6740
    if (need_prepare_ordered)
 
6741
    {
 
6742
      /*
 
6743
        We did the run_prepare_ordered() serialised, then ran the log_xid() in
 
6744
        parallel. Now we have to do run_commit_ordered() serialised in the
 
6745
        same sequence as run_prepare_ordered().
 
6746
 
 
6747
        We do this starting from the head of the queue, each thread doing
 
6748
        run_commit_ordered() and signalling the next in queue.
 
6749
      */
 
6750
      if (is_group_commit_leader)
 
6751
      {
 
6752
        /* The first in queue starts the ball rolling. */
 
6753
        mysql_mutex_lock(&LOCK_prepare_ordered);
 
6754
        while (commit_ordered_queue_busy)
 
6755
          mysql_cond_wait(&COND_queue_busy, &LOCK_prepare_ordered);
 
6756
        commit_entry *queue= commit_ordered_queue;
 
6757
        commit_ordered_queue= NULL;
 
6758
        /*
 
6759
          Mark the queue busy while we bounce it from one thread to the
 
6760
          next.
 
6761
        */
 
6762
        commit_ordered_queue_busy= true;
 
6763
        mysql_mutex_unlock(&LOCK_prepare_ordered);
 
6764
 
 
6765
        /* Reverse the queue list so we get correct order. */
 
6766
        commit_entry *prev= NULL;
 
6767
        while (queue)
 
6768
        {
 
6769
          commit_entry *next= queue->next;
 
6770
          queue->next= prev;
 
6771
          prev= queue;
 
6772
          queue= next;
 
6773
        }
 
6774
        DBUG_ASSERT(prev == &entry && prev->thd == thd);
 
6775
      }
 
6776
      else
 
6777
      {
 
6778
        /* Not first in queue; just wait until previous thread wakes us up. */
 
6779
        thd->wait_for_wakeup_ready();
 
6780
      }
 
6781
    }
 
6782
 
 
6783
    /* Only run commit_ordered() if log_xid was successful. */
 
6784
    if (cookie)
 
6785
    {
 
6786
      mysql_mutex_lock(&LOCK_commit_ordered);
 
6787
      run_commit_ordered(thd, all);
 
6788
      mysql_mutex_unlock(&LOCK_commit_ordered);
 
6789
    }
 
6790
 
 
6791
    if (need_prepare_ordered)
 
6792
    {
 
6793
      commit_entry *next= entry.next;
 
6794
      if (next)
 
6795
      {
 
6796
        next->thd->signal_wakeup_ready();
 
6797
      }
 
6798
      else
 
6799
      {
 
6800
        mysql_mutex_lock(&LOCK_prepare_ordered);
 
6801
        commit_ordered_queue_busy= false;
 
6802
        mysql_cond_signal(&COND_queue_busy);
 
6803
        mysql_mutex_unlock(&LOCK_prepare_ordered);
 
6804
      }
 
6805
    }
 
6806
  }
 
6807
 
 
6808
  return cookie;
 
6809
}
 
6810
 
 
6811
 
 
6812
/********* transaction coordinator log for 2pc - mmap() based solution *******/
 
6813
 
 
6814
/*
 
6815
  the log consists of a file, mapped to memory.
 
6816
  file is divided into pages of tc_log_page_size size.
 
6817
  (usable size of the first page is smaller because of the log header)
 
6818
  there is a PAGE control structure for each page
 
6819
  each page (or rather its PAGE control structure) can be in one of
 
6820
  the three states - active, syncing, pool.
 
6821
  there could be only one page in the active or syncing state,
 
6822
  but many in pool - pool is a fifo queue.
 
6823
  the usual lifecycle of a page is pool->active->syncing->pool.
 
6824
  the "active" page is a page where new xid's are logged.
 
6825
  the page stays active as long as the syncing slot is taken.
 
6826
  the "syncing" page is being synced to disk. no new xid can be added to it.
 
6827
  when the syncing is done the page is moved to a pool and an active page
 
6828
  becomes "syncing".
 
6829
 
 
6830
  the result of such an architecture is a natural "commit grouping" -
 
6831
  If commits are coming faster than the system can sync, they do not
 
6832
  stall. Instead, all commits that came since the last sync are
 
6833
  logged to the same "active" page, and they all are synced with the next -
 
6834
  one - sync. Thus, thought individual commits are delayed, throughput
 
6835
  is not decreasing.
 
6836
 
 
6837
  when an xid is added to an active page, the thread of this xid waits
 
6838
  for a page's condition until the page is synced. when syncing slot
 
6839
  becomes vacant one of these waiters is awaken to take care of syncing.
 
6840
  it syncs the page and signals all waiters that the page is synced.
 
6841
  PAGE::waiters is used to count these waiters, and a page may never
 
6842
  become active again until waiters==0 (that is all waiters from the
 
6843
  previous sync have noticed that the sync was completed)
 
6844
 
 
6845
  note, that the page becomes "dirty" and has to be synced only when a
 
6846
  new xid is added into it. Removing a xid from a page does not make it
 
6847
  dirty - we don't sync xid removals to disk.
 
6848
*/
 
6849
 
 
6850
ulong tc_log_page_waits= 0;
 
6851
 
 
6852
#ifdef HAVE_MMAP
 
6853
 
 
6854
#define TC_LOG_HEADER_SIZE (sizeof(tc_log_magic)+1)
 
6855
 
 
6856
static const uchar tc_log_magic[]={(uchar) 254, 0x23, 0x05, 0x74};
 
6857
 
 
6858
ulong opt_tc_log_size= TC_LOG_MIN_SIZE;
 
6859
ulong tc_log_max_pages_used=0, tc_log_page_size=0, tc_log_cur_pages_used=0;
 
6860
 
 
6861
int TC_LOG_MMAP::open(const char *opt_name)
 
6862
{
 
6863
  uint i;
 
6864
  bool crashed=FALSE;
 
6865
  PAGE *pg;
 
6866
 
 
6867
  DBUG_ASSERT(total_ha_2pc > 1);
 
6868
  DBUG_ASSERT(opt_name && opt_name[0]);
 
6869
 
 
6870
  tc_log_page_size= my_getpagesize();
 
6871
  DBUG_ASSERT(TC_LOG_PAGE_SIZE % tc_log_page_size == 0);
 
6872
 
 
6873
  fn_format(logname,opt_name,mysql_data_home,"",MY_UNPACK_FILENAME);
 
6874
  if ((fd= mysql_file_open(key_file_tclog, logname, O_RDWR, MYF(0))) < 0)
 
6875
  {
 
6876
    if (my_errno != ENOENT)
 
6877
      goto err;
 
6878
    if (using_heuristic_recover())
 
6879
      return 1;
 
6880
    if ((fd= mysql_file_create(key_file_tclog, logname, CREATE_MODE,
 
6881
                               O_RDWR, MYF(MY_WME))) < 0)
 
6882
      goto err;
 
6883
    inited=1;
 
6884
    file_length= opt_tc_log_size;
 
6885
    if (mysql_file_chsize(fd, file_length, 0, MYF(MY_WME)))
 
6886
      goto err;
 
6887
  }
 
6888
  else
 
6889
  {
 
6890
    inited= 1;
 
6891
    crashed= TRUE;
 
6892
    sql_print_information("Recovering after a crash using %s", opt_name);
 
6893
    if (tc_heuristic_recover)
 
6894
    {
 
6895
      sql_print_error("Cannot perform automatic crash recovery when "
 
6896
                      "--tc-heuristic-recover is used");
 
6897
      goto err;
 
6898
    }
 
6899
    file_length= mysql_file_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE));
 
6900
    if (file_length == MY_FILEPOS_ERROR || file_length % tc_log_page_size)
 
6901
      goto err;
 
6902
  }
 
6903
 
 
6904
  data= (uchar *)my_mmap(0, (size_t)file_length, PROT_READ|PROT_WRITE,
 
6905
                        MAP_NOSYNC|MAP_SHARED, fd, 0);
 
6906
  if (data == MAP_FAILED)
 
6907
  {
 
6908
    my_errno=errno;
 
6909
    goto err;
 
6910
  }
 
6911
  inited=2;
 
6912
 
 
6913
  npages=(uint)file_length/tc_log_page_size;
 
6914
  if (npages < 3)             // to guarantee non-empty pool
 
6915
    goto err;
 
6916
  if (!(pages=(PAGE *)my_malloc(npages*sizeof(PAGE), MYF(MY_WME|MY_ZEROFILL))))
 
6917
    goto err;
 
6918
  inited=3;
 
6919
  for (pg=pages, i=0; i < npages; i++, pg++)
 
6920
  {
 
6921
    pg->next=pg+1;
 
6922
    pg->waiters=0;
 
6923
    pg->state=PS_POOL;
 
6924
    mysql_mutex_init(key_PAGE_lock, &pg->lock, MY_MUTEX_INIT_FAST);
 
6925
    mysql_cond_init(key_PAGE_cond, &pg->cond, 0);
 
6926
    pg->ptr= pg->start=(my_xid *)(data + i*tc_log_page_size);
 
6927
    pg->size=pg->free=tc_log_page_size/sizeof(my_xid);
 
6928
    pg->end=pg->start + pg->size;
 
6929
  }
 
6930
  pages[0].size=pages[0].free=
 
6931
                (tc_log_page_size-TC_LOG_HEADER_SIZE)/sizeof(my_xid);
 
6932
  pages[0].start=pages[0].end-pages[0].size;
 
6933
  pages[npages-1].next=0;
 
6934
  inited=4;
 
6935
 
 
6936
  if (crashed && recover())
 
6937
      goto err;
 
6938
 
 
6939
  memcpy(data, tc_log_magic, sizeof(tc_log_magic));
 
6940
  data[sizeof(tc_log_magic)]= (uchar)total_ha_2pc;
 
6941
  my_msync(fd, data, tc_log_page_size, MS_SYNC);
 
6942
  inited=5;
 
6943
 
 
6944
  mysql_mutex_init(key_LOCK_sync, &LOCK_sync, MY_MUTEX_INIT_FAST);
 
6945
  mysql_mutex_init(key_LOCK_active, &LOCK_active, MY_MUTEX_INIT_FAST);
 
6946
  mysql_mutex_init(key_LOCK_pool, &LOCK_pool, MY_MUTEX_INIT_FAST);
 
6947
  mysql_cond_init(key_COND_active, &COND_active, 0);
 
6948
  mysql_cond_init(key_COND_pool, &COND_pool, 0);
 
6949
  mysql_cond_init(key_TC_LOG_MMAP_COND_queue_busy, &COND_queue_busy, 0);
 
6950
 
 
6951
  inited=6;
 
6952
 
 
6953
  syncing= 0;
 
6954
  active=pages;
 
6955
  DBUG_ASSERT(npages >= 2);
 
6956
  pool=pages+1;
 
6957
  pool_last_ptr= &((pages+npages-1)->next);
 
6958
  commit_ordered_queue= NULL;
 
6959
  commit_ordered_queue_busy= false;
 
6960
 
 
6961
  return 0;
 
6962
 
 
6963
err:
 
6964
  close();
 
6965
  return 1;
 
6966
}
 
6967
 
 
6968
/**
 
6969
  there is no active page, let's got one from the pool.
 
6970
 
 
6971
  Two strategies here:
 
6972
    -# take the first from the pool
 
6973
    -# if there're waiters - take the one with the most free space.
 
6974
 
 
6975
  @todo
 
6976
    page merging. try to allocate adjacent page first,
 
6977
    so that they can be flushed both in one sync
 
6978
*/
 
6979
 
 
6980
void TC_LOG_MMAP::get_active_from_pool()
 
6981
{
 
6982
  PAGE **p, **best_p=0;
 
6983
  int best_free;
 
6984
 
 
6985
  mysql_mutex_lock(&LOCK_pool);
 
6986
 
 
6987
  do
 
6988
  {
 
6989
    best_p= p= &pool;
 
6990
    if ((*p)->waiters == 0 && (*p)->free > 0) // can the first page be used ?
 
6991
      break;                                  // yes - take it.
 
6992
 
 
6993
    best_free=0;            // no - trying second strategy
 
6994
    for (p=&(*p)->next; *p; p=&(*p)->next)
 
6995
    {
 
6996
      if ((*p)->waiters == 0 && (*p)->free > best_free)
 
6997
      {
 
6998
        best_free=(*p)->free;
 
6999
        best_p=p;
 
7000
      }
 
7001
    }
 
7002
  }
 
7003
  while ((*best_p == 0 || best_free == 0) && overflow());
 
7004
 
 
7005
  mysql_mutex_assert_owner(&LOCK_active);
 
7006
  active=*best_p;
 
7007
 
 
7008
  /* Unlink the page from the pool. */
 
7009
  if (!(*best_p)->next)
 
7010
    pool_last_ptr= best_p;
 
7011
  *best_p=(*best_p)->next;
 
7012
  mysql_mutex_unlock(&LOCK_pool);
 
7013
 
 
7014
  mysql_mutex_lock(&active->lock);
 
7015
  if (active->free == active->size) // we've chosen an empty page
 
7016
  {
 
7017
    tc_log_cur_pages_used++;
 
7018
    set_if_bigger(tc_log_max_pages_used, tc_log_cur_pages_used);
 
7019
  }
 
7020
}
 
7021
 
 
7022
/**
 
7023
  @todo
 
7024
  perhaps, increase log size ?
 
7025
*/
 
7026
int TC_LOG_MMAP::overflow()
 
7027
{
 
7028
  /*
 
7029
    simple overflow handling - just wait
 
7030
    TODO perhaps, increase log size ?
 
7031
    let's check the behaviour of tc_log_page_waits first
 
7032
  */
 
7033
  tc_log_page_waits++;
 
7034
  mysql_cond_wait(&COND_pool, &LOCK_pool);
 
7035
  return 1; // always return 1
 
7036
}
 
7037
 
 
7038
/**
 
7039
  Record that transaction XID is committed on the persistent storage.
 
7040
 
 
7041
    This function is called in the middle of two-phase commit:
 
7042
    First all resources prepare the transaction, then tc_log->log() is called,
 
7043
    then all resources commit the transaction, then tc_log->unlog() is called.
 
7044
 
 
7045
    All access to active page is serialized but it's not a problem, as
 
7046
    we're assuming that fsync() will be a main bottleneck.
 
7047
    That is, parallelizing writes to log pages we'll decrease number of
 
7048
    threads waiting for a page, but then all these threads will be waiting
 
7049
    for a fsync() anyway
 
7050
 
 
7051
   If tc_log == MYSQL_LOG then tc_log writes transaction to binlog and
 
7052
   records XID in a special Xid_log_event.
 
7053
   If tc_log = TC_LOG_MMAP then xid is written in a special memory-mapped
 
7054
   log.
 
7055
 
 
7056
  @retval
 
7057
    0  - error
 
7058
  @retval
 
7059
    \# - otherwise, "cookie", a number that will be passed as an argument
 
7060
    to unlog() call. tc_log can define it any way it wants,
 
7061
    and use for whatever purposes. TC_LOG_MMAP sets it
 
7062
    to the position in memory where xid was logged to.
 
7063
*/
 
7064
 
 
7065
int TC_LOG_MMAP::log_one_transaction(my_xid xid)
 
7066
{
 
7067
  int err;
 
7068
  PAGE *p;
 
7069
  ulong cookie;
 
7070
 
 
7071
  mysql_mutex_lock(&LOCK_active);
 
7072
 
 
7073
  /*
 
7074
    if the active page is full - just wait...
 
7075
    frankly speaking, active->free here accessed outside of mutex
 
7076
    protection, but it's safe, because it only means we may miss an
 
7077
    unlog() for the active page, and we're not waiting for it here -
 
7078
    unlog() does not signal COND_active.
 
7079
  */
 
7080
  while (unlikely(active && active->free == 0))
 
7081
    mysql_cond_wait(&COND_active, &LOCK_active);
 
7082
 
 
7083
  /* no active page ? take one from the pool */
 
7084
  if (active == 0)
 
7085
    get_active_from_pool();
 
7086
  else
 
7087
    mysql_mutex_lock(&active->lock);
 
7088
 
 
7089
  p=active;
 
7090
 
 
7091
  /*
 
7092
    p->free is always > 0 here because to decrease it one needs
 
7093
    to take p->lock and before it one needs to take LOCK_active.
 
7094
    But checked that active->free > 0 under LOCK_active and
 
7095
    haven't release it ever since
 
7096
  */
 
7097
 
 
7098
  /* searching for an empty slot */
 
7099
  while (*p->ptr)
 
7100
  {
 
7101
    p->ptr++;
 
7102
    DBUG_ASSERT(p->ptr < p->end);               // because p->free > 0
 
7103
  }
 
7104
 
 
7105
  /* found! store xid there and mark the page dirty */
 
7106
  cookie= (ulong)((uchar *)p->ptr - data);      // can never be zero
 
7107
  *p->ptr++= xid;
 
7108
  p->free--;
 
7109
  p->state= PS_DIRTY;
 
7110
  mysql_mutex_unlock(&p->lock);
 
7111
 
 
7112
  mysql_mutex_lock(&LOCK_sync);
 
7113
  if (syncing)
 
7114
  {                                          // somebody's syncing. let's wait
 
7115
    mysql_mutex_unlock(&LOCK_active);
 
7116
    mysql_mutex_lock(&p->lock);
 
7117
    p->waiters++;
 
7118
    while (p->state == PS_DIRTY && syncing)
 
7119
    {
 
7120
      mysql_mutex_unlock(&p->lock);
 
7121
      mysql_cond_wait(&p->cond, &LOCK_sync);
 
7122
      mysql_mutex_lock(&p->lock);
 
7123
    }
 
7124
    p->waiters--;
 
7125
    err= p->state == PS_ERROR;
 
7126
    if (p->state != PS_DIRTY)                   // page was synced
 
7127
    {
 
7128
      mysql_mutex_unlock(&LOCK_sync);
 
7129
      if (p->waiters == 0)
 
7130
        mysql_cond_signal(&COND_pool);     // in case somebody's waiting
 
7131
      mysql_mutex_unlock(&p->lock);
 
7132
      goto done;                             // we're done
 
7133
    }
 
7134
    DBUG_ASSERT(!syncing);
 
7135
    mysql_mutex_unlock(&p->lock);
 
7136
    syncing = p;
 
7137
    mysql_mutex_unlock(&LOCK_sync);
 
7138
 
 
7139
    mysql_mutex_lock(&LOCK_active);
 
7140
    active=0;                                  // page is not active anymore
 
7141
    mysql_cond_broadcast(&COND_active);
 
7142
    mysql_mutex_unlock(&LOCK_active);
 
7143
  }
 
7144
  else
 
7145
  {
 
7146
    syncing = p;                               // place is vacant - take it
 
7147
    mysql_mutex_unlock(&LOCK_sync);
 
7148
    active = 0;                                // page is not active anymore
 
7149
    mysql_cond_broadcast(&COND_active);
 
7150
    mysql_mutex_unlock(&LOCK_active);
 
7151
  }
 
7152
  err= sync();
 
7153
 
 
7154
done:
 
7155
  return err ? 0 : cookie;
 
7156
}
 
7157
 
 
7158
int TC_LOG_MMAP::sync()
 
7159
{
 
7160
  int err;
 
7161
 
 
7162
  DBUG_ASSERT(syncing != active);
 
7163
 
 
7164
  /*
 
7165
    sit down and relax - this can take a while...
 
7166
    note - no locks are held at this point
 
7167
  */
 
7168
  err= my_msync(fd, syncing->start, syncing->size * sizeof(my_xid), MS_SYNC);
 
7169
 
 
7170
  /* page is synced. let's move it to the pool */
 
7171
  mysql_mutex_lock(&LOCK_pool);
 
7172
  (*pool_last_ptr)=syncing;
 
7173
  pool_last_ptr=&(syncing->next);
 
7174
  syncing->next=0;
 
7175
  syncing->state= err ? PS_ERROR : PS_POOL;
 
7176
  mysql_cond_signal(&COND_pool);           // in case somebody's waiting
 
7177
  mysql_mutex_unlock(&LOCK_pool);
 
7178
 
 
7179
  /* marking 'syncing' slot free */
 
7180
  mysql_mutex_lock(&LOCK_sync);
 
7181
  mysql_cond_broadcast(&syncing->cond);    // signal "sync done"
 
7182
  syncing=0;
 
7183
  /*
 
7184
    we check the "active" pointer without LOCK_active. Still, it's safe -
 
7185
    "active" can change from NULL to not NULL any time, but it
 
7186
    will take LOCK_sync before waiting on active->cond. That is, it can never
 
7187
    miss a signal.
 
7188
    And "active" can change to NULL only by the syncing thread
 
7189
    (the thread that will send a signal below)
 
7190
  */
 
7191
  if (active)
 
7192
    mysql_cond_signal(&active->cond);      // wake up a new syncer
 
7193
  mysql_mutex_unlock(&LOCK_sync);
 
7194
  return err;
 
7195
}
 
7196
 
 
7197
/**
 
7198
  erase xid from the page, update page free space counters/pointers.
 
7199
  cookie points directly to the memory where xid was logged.
 
7200
*/
 
7201
 
 
7202
int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
 
7203
{
 
7204
  PAGE *p=pages+(cookie/tc_log_page_size);
 
7205
  my_xid *x=(my_xid *)(data+cookie);
 
7206
 
 
7207
  DBUG_ASSERT(*x == xid);
 
7208
  DBUG_ASSERT(x >= p->start && x < p->end);
 
7209
 
 
7210
  mysql_mutex_lock(&p->lock);
 
7211
  *x=0;
 
7212
  p->free++;
 
7213
  DBUG_ASSERT(p->free <= p->size);
 
7214
  set_if_smaller(p->ptr, x);
 
7215
  if (p->free == p->size)              // the page is completely empty
 
7216
    statistic_decrement(tc_log_cur_pages_used, &LOCK_status);
 
7217
  if (p->waiters == 0)                 // the page is in pool and ready to rock
 
7218
    mysql_cond_signal(&COND_pool);     // ping ... for overflow()
 
7219
  mysql_mutex_unlock(&p->lock);
 
7220
  return 0;
 
7221
}
 
7222
 
 
7223
void TC_LOG_MMAP::close()
 
7224
{
 
7225
  uint i;
 
7226
  switch (inited) {
 
7227
  case 6:
 
7228
    mysql_mutex_destroy(&LOCK_sync);
 
7229
    mysql_mutex_destroy(&LOCK_active);
 
7230
    mysql_mutex_destroy(&LOCK_pool);
 
7231
    mysql_cond_destroy(&COND_pool);
 
7232
    mysql_cond_destroy(&COND_active);
 
7233
    mysql_cond_destroy(&COND_queue_busy);
 
7234
  case 5:
 
7235
    data[0]='A'; // garble the first (signature) byte, in case mysql_file_delete fails
 
7236
  case 4:
 
7237
    for (i=0; i < npages; i++)
 
7238
    {
 
7239
      if (pages[i].ptr == 0)
 
7240
        break;
 
7241
      mysql_mutex_destroy(&pages[i].lock);
 
7242
      mysql_cond_destroy(&pages[i].cond);
 
7243
    }
 
7244
  case 3:
 
7245
    my_free(pages);
 
7246
  case 2:
 
7247
    my_munmap((char*)data, (size_t)file_length);
 
7248
  case 1:
 
7249
    mysql_file_close(fd, MYF(0));
 
7250
  }
 
7251
  if (inited>=5) // cannot do in the switch because of Windows
 
7252
    mysql_file_delete(key_file_tclog, logname, MYF(MY_WME));
 
7253
  inited=0;
 
7254
}
 
7255
 
 
7256
int TC_LOG_MMAP::recover()
 
7257
{
 
7258
  HASH xids;
 
7259
  PAGE *p=pages, *end_p=pages+npages;
 
7260
 
 
7261
  if (bcmp(data, tc_log_magic, sizeof(tc_log_magic)))
 
7262
  {
 
7263
    sql_print_error("Bad magic header in tc log");
 
7264
    goto err1;
 
7265
  }
 
7266
 
 
7267
  /*
 
7268
    the first byte after magic signature is set to current
 
7269
    number of storage engines on startup
 
7270
  */
 
7271
  if (data[sizeof(tc_log_magic)] != total_ha_2pc)
 
7272
  {
 
7273
    sql_print_error("Recovery failed! You must enable "
 
7274
                    "exactly %d storage engines that support "
 
7275
                    "two-phase commit protocol",
 
7276
                    data[sizeof(tc_log_magic)]);
 
7277
    goto err1;
 
7278
  }
 
7279
 
 
7280
  if (my_hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0,
 
7281
                   sizeof(my_xid), 0, 0, MYF(0)))
 
7282
    goto err1;
 
7283
 
 
7284
  for ( ; p < end_p ; p++)
 
7285
  {
 
7286
    for (my_xid *x=p->start; x < p->end; x++)
 
7287
      if (*x && my_hash_insert(&xids, (uchar *)x))
 
7288
        goto err2; // OOM
 
7289
  }
 
7290
 
 
7291
  if (ha_recover(&xids))
 
7292
    goto err2;
 
7293
 
 
7294
  my_hash_free(&xids);
 
7295
  bzero(data, (size_t)file_length);
 
7296
  return 0;
 
7297
 
 
7298
err2:
 
7299
  my_hash_free(&xids);
 
7300
err1:
 
7301
  sql_print_error("Crash recovery failed. Either correct the problem "
 
7302
                  "(if it's, for example, out of memory error) and restart, "
 
7303
                  "or delete tc log and start mysqld with "
 
7304
                  "--tc-heuristic-recover={commit|rollback}");
 
7305
  return 1;
 
7306
}
 
7307
#endif
 
7308
 
 
7309
TC_LOG *tc_log;
 
7310
TC_LOG_DUMMY tc_log_dummy;
 
7311
TC_LOG_MMAP  tc_log_mmap;
 
7312
 
 
7313
/**
 
7314
  Perform heuristic recovery, if --tc-heuristic-recover was used.
 
7315
 
 
7316
  @note
 
7317
    no matter whether heuristic recovery was successful or not
 
7318
    mysqld must exit. So, return value is the same in both cases.
 
7319
 
 
7320
  @retval
 
7321
    0   no heuristic recovery was requested
 
7322
  @retval
 
7323
    1   heuristic recovery was performed
 
7324
*/
 
7325
 
 
7326
int TC_LOG::using_heuristic_recover()
 
7327
{
 
7328
  if (!tc_heuristic_recover)
 
7329
    return 0;
 
7330
 
 
7331
  sql_print_information("Heuristic crash recovery mode");
 
7332
  if (ha_recover(0))
 
7333
    sql_print_error("Heuristic crash recovery failed");
 
7334
  sql_print_information("Please restart mysqld without --tc-heuristic-recover");
 
7335
  return 1;
 
7336
}
 
7337
 
 
7338
/****** transaction coordinator log for 2pc - binlog() based solution ******/
 
7339
#define TC_LOG_BINLOG MYSQL_BIN_LOG
 
7340
 
 
7341
/**
 
7342
  @todo
 
7343
  keep in-memory list of prepared transactions
 
7344
  (add to list in log(), remove on unlog())
 
7345
  and copy it to the new binlog if rotated
 
7346
  but let's check the behaviour of tc_log_page_waits first!
 
7347
*/
 
7348
 
 
7349
int TC_LOG_BINLOG::open(const char *opt_name)
 
7350
{
 
7351
  LOG_INFO log_info;
 
7352
  int      error= 1;
 
7353
 
 
7354
  DBUG_ASSERT(total_ha_2pc > 1);
 
7355
  DBUG_ASSERT(opt_name && opt_name[0]);
 
7356
 
 
7357
  mysql_mutex_init(key_BINLOG_LOCK_prep_xids,
 
7358
                   &LOCK_prep_xids, MY_MUTEX_INIT_FAST);
 
7359
  mysql_cond_init(key_BINLOG_COND_prep_xids, &COND_prep_xids, 0);
 
7360
 
 
7361
  if (!my_b_inited(&index_file))
 
7362
  {
 
7363
    /* There was a failure to open the index file, can't open the binlog */
 
7364
    cleanup();
 
7365
    return 1;
 
7366
  }
 
7367
 
 
7368
  if (using_heuristic_recover())
 
7369
  {
 
7370
    /* generate a new binlog to mask a corrupted one */
 
7371
    open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
 
7372
    cleanup();
 
7373
    return 1;
 
7374
  }
 
7375
 
 
7376
  if ((error= find_log_pos(&log_info, NullS, 1)))
 
7377
  {
 
7378
    if (error != LOG_INFO_EOF)
 
7379
      sql_print_error("find_log_pos() failed (error: %d)", error);
 
7380
    else
 
7381
      error= 0;
 
7382
    goto err;
 
7383
  }
 
7384
 
 
7385
  {
 
7386
    const char *errmsg;
 
7387
    IO_CACHE    log;
 
7388
    File        file;
 
7389
    Log_event  *ev=0;
 
7390
    Format_description_log_event fdle(BINLOG_VERSION);
 
7391
    char        log_name[FN_REFLEN];
 
7392
 
 
7393
    if (! fdle.is_valid())
 
7394
      goto err;
 
7395
 
 
7396
    do
 
7397
    {
 
7398
      strmake_buf(log_name, log_info.log_file_name);
 
7399
    } while (!(error= find_next_log(&log_info, 1)));
 
7400
 
 
7401
    if (error !=  LOG_INFO_EOF)
 
7402
    {
 
7403
      sql_print_error("find_log_pos() failed (error: %d)", error);
 
7404
      goto err;
 
7405
    }
 
7406
 
 
7407
    if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
 
7408
    {
 
7409
      sql_print_error("%s", errmsg);
 
7410
      goto err;
 
7411
    }
 
7412
 
 
7413
    if ((ev= Log_event::read_log_event(&log, 0, &fdle,
 
7414
                                       opt_master_verify_checksum)) &&
 
7415
        ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
 
7416
        ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
 
7417
    {
 
7418
      sql_print_information("Recovering after a crash using %s", opt_name);
 
7419
      error= recover(&log, (Format_description_log_event *)ev);
 
7420
    }
 
7421
    else
 
7422
      error=0;
 
7423
 
 
7424
    delete ev;
 
7425
    end_io_cache(&log);
 
7426
    mysql_file_close(file, MYF(MY_WME));
 
7427
 
 
7428
    if (error)
 
7429
      goto err;
 
7430
  }
 
7431
 
 
7432
err:
 
7433
  return error;
 
7434
}
 
7435
 
 
7436
/** This is called on shutdown, after ha_panic. */
 
7437
void TC_LOG_BINLOG::close()
 
7438
{
 
7439
  DBUG_ASSERT(prepared_xids==0);
 
7440
  mysql_mutex_destroy(&LOCK_prep_xids);
 
7441
  mysql_cond_destroy(&COND_prep_xids);
 
7442
}
 
7443
 
 
7444
/*
 
7445
  Do a binlog log_xid() for a group of transactions, linked through
 
7446
  thd->next_commit_ordered.
 
7447
*/
 
7448
int
 
7449
TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
 
7450
                             bool need_prepare_ordered __attribute__((unused)),
 
7451
                             bool need_commit_ordered __attribute__((unused)))
 
7452
{
 
7453
  int err;
 
7454
  DBUG_ENTER("TC_LOG_BINLOG::log_and_order");
 
7455
 
 
7456
  binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
 
7457
  if (!cache_mngr)
 
7458
    DBUG_RETURN(0);
 
7459
 
 
7460
  cache_mngr->using_xa= TRUE;
 
7461
  cache_mngr->xa_xid= xid;
 
7462
  err= binlog_commit_flush_xid_caches(thd, cache_mngr, all, xid);
 
7463
 
 
7464
  DEBUG_SYNC(thd, "binlog_after_log_and_order");
 
7465
 
 
7466
  DBUG_RETURN(!err);
 
7467
}
 
7468
 
 
7469
/*
 
7470
  After an XID is logged, we need to hold on to the current binlog file until
 
7471
  it is fully committed in the storage engine. The reason is that crash
 
7472
  recovery only looks at the latest binlog, so we must make sure there are no
 
7473
  outstanding prepared (but not committed) transactions before rotating the
 
7474
  binlog.
 
7475
 
 
7476
  To handle this, we keep a count of outstanding XIDs. This function is used
 
7477
  to increase this count when committing one or more transactions to the
 
7478
  binary log.
 
7479
*/
 
7480
void
 
7481
TC_LOG_BINLOG::mark_xids_active(uint xid_count)
 
7482
{
 
7483
  DBUG_ENTER("TC_LOG_BINLOG::mark_xids_active");
 
7484
  DBUG_PRINT("info", ("xid_count=%u", xid_count));
 
7485
  mysql_mutex_lock(&LOCK_prep_xids);
 
7486
  prepared_xids+= xid_count;
 
7487
  mysql_mutex_unlock(&LOCK_prep_xids);
 
7488
  DBUG_VOID_RETURN;
 
7489
}
 
7490
 
 
7491
/*
 
7492
  Once an XID is committed, it is safe to rotate the binary log, as it can no
 
7493
  longer be needed during crash recovery.
 
7494
 
 
7495
  This function is called to mark an XID this way. It needs to decrease the
 
7496
  count of pending XIDs, and signal the log rotator thread when it reaches zero.
 
7497
*/
 
7498
void
 
7499
TC_LOG_BINLOG::mark_xid_done()
 
7500
{
 
7501
  my_bool send_signal;
 
7502
 
 
7503
  DBUG_ENTER("TC_LOG_BINLOG::mark_xid_done");
 
7504
  mysql_mutex_lock(&LOCK_prep_xids);
 
7505
  // prepared_xids can be 0 if the transaction had ignorable errors.
 
7506
  DBUG_ASSERT(prepared_xids >= 0);
 
7507
  if (prepared_xids > 0)
 
7508
    prepared_xids--;
 
7509
  send_signal= (prepared_xids == 0);
 
7510
  mysql_mutex_unlock(&LOCK_prep_xids);
 
7511
  if (send_signal) {
 
7512
    DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
 
7513
    mysql_cond_signal(&COND_prep_xids);
 
7514
  }
 
7515
  DBUG_VOID_RETURN;
 
7516
}
 
7517
 
 
7518
int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
 
7519
{
 
7520
  DBUG_ENTER("TC_LOG_BINLOG::unlog");
 
7521
  if (xid)
 
7522
    mark_xid_done();
 
7523
  /* As ::write_transaction_to_binlog() did not rotate, do it here. */
 
7524
  DBUG_RETURN(rotate_and_purge(0));
 
7525
}
 
7526
 
 
7527
int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
 
7528
{
 
7529
  Log_event  *ev;
 
7530
  HASH xids;
 
7531
  MEM_ROOT mem_root;
 
7532
 
 
7533
  if (! fdle->is_valid() ||
 
7534
      my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
 
7535
                   sizeof(my_xid), 0, 0, MYF(0)))
 
7536
    goto err1;
 
7537
 
 
7538
  init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
 
7539
 
 
7540
  fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
 
7541
 
 
7542
  while ((ev= Log_event::read_log_event(log, 0, fdle,
 
7543
                                        opt_master_verify_checksum))
 
7544
         && ev->is_valid())
 
7545
  {
 
7546
    if (ev->get_type_code() == XID_EVENT)
 
7547
    {
 
7548
      Xid_log_event *xev=(Xid_log_event *)ev;
 
7549
      uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
 
7550
                                      sizeof(xev->xid));
 
7551
      if (!x || my_hash_insert(&xids, x))
 
7552
        goto err2;
 
7553
    }
 
7554
    delete ev;
 
7555
  }
 
7556
 
 
7557
  if (ha_recover(&xids))
 
7558
    goto err2;
 
7559
 
 
7560
  free_root(&mem_root, MYF(0));
 
7561
  my_hash_free(&xids);
 
7562
  return 0;
 
7563
 
 
7564
err2:
 
7565
  free_root(&mem_root, MYF(0));
 
7566
  my_hash_free(&xids);
 
7567
err1:
 
7568
  sql_print_error("Crash recovery failed. Either correct the problem "
 
7569
                  "(if it's, for example, out of memory error) and restart, "
 
7570
                  "or delete (or rename) binary log and start mysqld with "
 
7571
                  "--tc-heuristic-recover={commit|rollback}");
 
7572
  return 1;
 
7573
}
 
7574
 
 
7575
 
 
7576
#ifdef INNODB_COMPATIBILITY_HOOKS
 
7577
/**
 
7578
  Get the file name of the MySQL binlog.
 
7579
  @return the name of the binlog file
 
7580
*/
 
7581
extern "C"
 
7582
const char* mysql_bin_log_file_name(void)
 
7583
{
 
7584
  return mysql_bin_log.get_log_fname();
 
7585
}
 
7586
/**
 
7587
  Get the current position of the MySQL binlog.
 
7588
  @return byte offset from the beginning of the binlog
 
7589
*/
 
7590
extern "C"
 
7591
ulonglong mysql_bin_log_file_pos(void)
 
7592
{
 
7593
  return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file;
 
7594
}
 
7595
/*
 
7596
  Get the current position of the MySQL binlog for transaction currently being
 
7597
  committed.
 
7598
 
 
7599
  This is valid to call from within storage engine commit_ordered() and
 
7600
  commit() methods only.
 
7601
 
 
7602
  Since it stores the position inside THD, it is safe to call without any
 
7603
  locking.
 
7604
*/
 
7605
void
 
7606
mysql_bin_log_commit_pos(THD *thd, ulonglong *out_pos, const char **out_file)
 
7607
{
 
7608
  binlog_cache_mngr *cache_mngr;
 
7609
  if (opt_bin_log &&
 
7610
      (cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton)))
 
7611
  {
 
7612
    *out_file= cache_mngr->last_commit_pos_file;
 
7613
    *out_pos= (ulonglong)(cache_mngr->last_commit_pos_offset);
 
7614
  }
 
7615
  else
 
7616
  {
 
7617
    *out_file= NULL;
 
7618
    *out_pos= 0;
 
7619
  }
 
7620
}
 
7621
#endif /* INNODB_COMPATIBILITY_HOOKS */
 
7622
 
 
7623
 
 
7624
static void
 
7625
binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
 
7626
                       void *var_ptr, const void *save)
 
7627
{
 
7628
  ulong value=  *((ulong *)save);
 
7629
  bool check_purge= false;
 
7630
 
 
7631
  mysql_mutex_lock(mysql_bin_log.get_log_lock());
 
7632
  if(mysql_bin_log.is_open())
 
7633
  {
 
7634
    if (binlog_checksum_options != value)
 
7635
      mysql_bin_log.checksum_alg_reset= (uint8) value;
 
7636
    if (mysql_bin_log.rotate(true, &check_purge))
 
7637
      check_purge= false;
 
7638
  }
 
7639
  else
 
7640
  {
 
7641
    binlog_checksum_options= value;
 
7642
  }
 
7643
  DBUG_ASSERT(binlog_checksum_options == value);
 
7644
  mysql_bin_log.checksum_alg_reset= BINLOG_CHECKSUM_ALG_UNDEF;
 
7645
  mysql_mutex_unlock(mysql_bin_log.get_log_lock());
 
7646
  if (check_purge)
 
7647
    mysql_bin_log.purge();
 
7648
}
 
7649
 
 
7650
 
 
7651
static int show_binlog_vars(THD *thd, SHOW_VAR *var, char *buff)
 
7652
{
 
7653
  mysql_bin_log.set_status_variables(thd);
 
7654
  var->type= SHOW_ARRAY;
 
7655
  var->value= (char *)&binlog_status_vars_detail;
 
7656
  return 0;
 
7657
}
 
7658
 
 
7659
static SHOW_VAR binlog_status_vars_top[]= {
 
7660
  {"Binlog", (char *) &show_binlog_vars, SHOW_FUNC},
 
7661
  {NullS, NullS, SHOW_LONG}
 
7662
};
 
7663
 
 
7664
static MYSQL_SYSVAR_BOOL(
 
7665
  optimize_thread_scheduling,
 
7666
  opt_optimize_thread_scheduling,
 
7667
  PLUGIN_VAR_READONLY,
 
7668
  "Run fast part of group commit in a single thread, to optimize kernel "
 
7669
  "thread scheduling. On by default. Disable to run each transaction in group "
 
7670
  "commit in its own thread, which can be slower at very high concurrency. "
 
7671
  "This option is mostly for testing one algorithm versus the other, and it "
 
7672
  "should not normally be necessary to change it.",
 
7673
  NULL,
 
7674
  NULL,
 
7675
  1);
 
7676
 
 
7677
static MYSQL_SYSVAR_ENUM(
 
7678
  checksum,
 
7679
  binlog_checksum_options,
 
7680
  PLUGIN_VAR_RQCMDARG,
 
7681
  "Type of BINLOG_CHECKSUM_ALG. Include checksum for "
 
7682
  "log events in the binary log. Possible values are NONE and CRC32; "
 
7683
  "default is NONE.",
 
7684
  NULL,
 
7685
  binlog_checksum_update,
 
7686
  BINLOG_CHECKSUM_ALG_OFF,
 
7687
  &binlog_checksum_typelib);
 
7688
 
 
7689
static struct st_mysql_sys_var *binlog_sys_vars[]=
 
7690
{
 
7691
  MYSQL_SYSVAR(optimize_thread_scheduling),
 
7692
  MYSQL_SYSVAR(checksum),
 
7693
  NULL
 
7694
};
 
7695
 
 
7696
 
 
7697
/*
 
7698
  Copy out the non-directory part of binlog position filename for the
 
7699
  `binlog_snapshot_file' status variable, same way as it is done for
 
7700
  SHOW MASTER STATUS.
 
7701
*/
 
7702
static void
 
7703
set_binlog_snapshot_file(const char *src)
 
7704
{
 
7705
  int dir_len = dirname_length(src);
 
7706
  strmake_buf(binlog_snapshot_file, src + dir_len);
 
7707
}
 
7708
 
 
7709
/*
 
7710
  Copy out current values of status variables, for SHOW STATUS or
 
7711
  information_schema.global_status.
 
7712
 
 
7713
  This is called only under LOCK_status, so we can fill in a static array.
 
7714
*/
 
7715
void
 
7716
TC_LOG_BINLOG::set_status_variables(THD *thd)
 
7717
{
 
7718
  binlog_cache_mngr *cache_mngr;
 
7719
 
 
7720
  if (thd && opt_bin_log)
 
7721
    cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
 
7722
  else
 
7723
    cache_mngr= 0;
 
7724
 
 
7725
  bool have_snapshot= (cache_mngr && cache_mngr->last_commit_pos_file[0] != 0);
 
7726
  mysql_mutex_lock(&LOCK_commit_ordered);
 
7727
  binlog_status_var_num_commits= this->num_commits;
 
7728
  binlog_status_var_num_group_commits= this->num_group_commits;
 
7729
  if (!have_snapshot)
 
7730
  {
 
7731
    set_binlog_snapshot_file(last_commit_pos_file);
 
7732
    binlog_snapshot_position= last_commit_pos_offset;
 
7733
  }
 
7734
  mysql_mutex_unlock(&LOCK_commit_ordered);
 
7735
 
 
7736
  if (have_snapshot)
 
7737
  {
 
7738
    set_binlog_snapshot_file(cache_mngr->last_commit_pos_file);
 
7739
    binlog_snapshot_position= cache_mngr->last_commit_pos_offset;
 
7740
  }
 
7741
}
 
7742
 
 
7743
struct st_mysql_storage_engine binlog_storage_engine=
 
7744
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
 
7745
 
 
7746
maria_declare_plugin(binlog)
 
7747
{
 
7748
  MYSQL_STORAGE_ENGINE_PLUGIN,
 
7749
  &binlog_storage_engine,
 
7750
  "binlog",
 
7751
  "MySQL AB",
 
7752
  "This is a pseudo storage engine to represent the binlog in a transaction",
 
7753
  PLUGIN_LICENSE_GPL,
 
7754
  binlog_init, /* Plugin Init */
 
7755
  NULL, /* Plugin Deinit */
 
7756
  0x0100 /* 1.0 */,
 
7757
  binlog_status_vars_top,     /* status variables                */
 
7758
  binlog_sys_vars,            /* system variables                */
 
7759
  "1.0",                      /* string version */
 
7760
  MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
 
7761
}
 
7762
maria_declare_plugin_end;