~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/events.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2004-2006 MySQL AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
#include "mysql_priv.h"
 
17
#include "events.h"
 
18
#include "event_data_objects.h"
 
19
#include "event_db_repository.h"
 
20
#include "event_queue.h"
 
21
#include "event_scheduler.h"
 
22
#include "sp_head.h" // for Stored_program_creation_ctx
 
23
 
 
24
/**
 
25
  @addtogroup Event_Scheduler
 
26
  @{
 
27
*/
 
28
 
 
29
/*
 
30
 TODO list :
 
31
 - CREATE EVENT should not go into binary log! Does it now? The SQL statements
 
32
   issued by the EVENT are replicated.
 
33
   I have an idea how to solve the problem at failover. So the status field
 
34
   will be ENUM('DISABLED', 'ENABLED', 'SLAVESIDE_DISABLED').
 
35
   In this case when CREATE EVENT is replicated it should go into the binary
 
36
   as SLAVESIDE_DISABLED if it is ENABLED, when it's created as DISABLEd it
 
37
   should be replicated as disabled. If an event is ALTERed as DISABLED the
 
38
   query should go untouched into the binary log, when ALTERed as enable then
 
39
   it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface.
 
40
   TT routines however modify mysql.event internally and this does not go the
 
41
   log so in this case queries has to be injected into the log...somehow... or
 
42
   maybe a solution is RBR for this case, because the event may go only from
 
43
   ENABLED to DISABLED status change and this is safe for replicating. As well
 
44
   an event may be deleted which is also safe for RBR.
 
45
 
 
46
 - Add logging to file
 
47
 
 
48
*/
 
49
 
 
50
 
 
51
/*
 
52
  If the user (un)intentionally removes an event directly from mysql.event
 
53
  the following sequence has to be used to be able to remove the in-memory
 
54
  counterpart.
 
55
  1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
 
56
  2. DROP EVENT the_name
 
57
 
 
58
  In other words, the first one will create a row in mysql.event . In the
 
59
  second step because there will be a line, disk based drop will pass and
 
60
  the scheduler will remove the memory counterpart. The reason is that
 
61
  in-memory queue does not check whether the event we try to drop from memory
 
62
  is disabled. Disabled events are not kept in-memory because they are not
 
63
  eligible for execution.
 
64
*/
 
65
 
 
66
/*
 
67
  Keep the order of the first to as in var_typelib
 
68
  sys_var_event_scheduler::value_ptr() references this array. Keep in
 
69
  mind!
 
70
*/
 
71
static const char *opt_event_scheduler_state_names[]=
 
72
    { "OFF", "ON", "0", "1", "DISABLED", NullS };
 
73
 
 
74
const TYPELIB Events::opt_typelib=
 
75
{
 
76
  array_elements(opt_event_scheduler_state_names)-1,
 
77
  "",
 
78
  opt_event_scheduler_state_names,
 
79
  NULL
 
80
};
 
81
 
 
82
 
 
83
/*
 
84
  The order should not be changed. We consider OFF to be equivalent of INT 0
 
85
  And ON of 1. If OFF & ON are interchanged the logic in
 
86
  sys_var_event_scheduler::update() will be broken!
 
87
*/
 
88
static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };
 
89
 
 
90
const TYPELIB Events::var_typelib=
 
91
{
 
92
  array_elements(var_event_scheduler_state_names)-1,
 
93
  "",
 
94
  var_event_scheduler_state_names,
 
95
  NULL
 
96
};
 
97
 
 
98
Event_queue *Events::event_queue;
 
99
Event_scheduler *Events::scheduler;
 
100
Event_db_repository *Events::db_repository;
 
101
enum Events::enum_opt_event_scheduler
 
102
Events::opt_event_scheduler= Events::EVENTS_OFF;
 
103
pthread_mutex_t Events::LOCK_event_metadata;
 
104
bool Events::check_system_tables_error= FALSE;
 
105
 
 
106
 
 
107
/*
 
108
  Compares 2 LEX strings regarding case.
 
109
 
 
110
  SYNOPSIS
 
111
    sortcmp_lex_string()
 
112
      s   First LEX_STRING
 
113
      t   Second LEX_STRING
 
114
      cs  Charset
 
115
 
 
116
  RETURN VALUE
 
117
   -1   s < t
 
118
    0   s == t
 
119
    1   s > t
 
120
*/
 
121
 
 
122
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
 
123
{
 
124
 return cs->coll->strnncollsp(cs, (uchar *) s.str,s.length,
 
125
                                  (uchar *) t.str,t.length, 0);
 
126
}
 
127
 
 
128
 
 
129
/**
 
130
  @brief Initialize the start up option of the Events scheduler.
 
131
 
 
132
  Do not initialize the scheduler subsystem yet - the initialization
 
133
  is split into steps as it has to fit into the common MySQL
 
134
  initialization framework.
 
135
  No locking as this is called only at start up.
 
136
 
 
137
  @param[in,out]  argument  The value of the argument. If this value
 
138
                            is found in the typelib, the argument is
 
139
                            updated.
 
140
 
 
141
  @retval TRUE  unknown option value
 
142
  @retval FALSE success
 
143
*/
 
144
 
 
145
bool
 
146
Events::set_opt_event_scheduler(char *argument)
 
147
{
 
148
  if (argument == NULL)
 
149
    opt_event_scheduler= Events::EVENTS_ON;
 
150
  else
 
151
  {
 
152
    int type;
 
153
    /*
 
154
      type=   1   2      3   4      5
 
155
           (OFF | ON) - (0 | 1) (DISABLE )
 
156
    */
 
157
    const static enum enum_opt_event_scheduler type2state[]=
 
158
    { EVENTS_OFF, EVENTS_ON, EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
 
159
 
 
160
    type= find_type(argument, &opt_typelib, 1);
 
161
 
 
162
    DBUG_ASSERT(type >= 0 && type <= 5); /* guaranteed by find_type */
 
163
 
 
164
    if (type == 0)
 
165
    {
 
166
      fprintf(stderr, "Unknown option to event-scheduler: %s\n", argument);
 
167
      return TRUE;
 
168
    }
 
169
    opt_event_scheduler= type2state[type-1];
 
170
  }
 
171
  return FALSE;
 
172
}
 
173
 
 
174
 
 
175
/**
 
176
  Return a string representation of the current scheduler mode.
 
177
*/
 
178
 
 
179
const char *
 
180
Events::get_opt_event_scheduler_str()
 
181
{
 
182
  const char *str;
 
183
 
 
184
  pthread_mutex_lock(&LOCK_event_metadata);
 
185
  str= opt_typelib.type_names[(int) opt_event_scheduler];
 
186
  pthread_mutex_unlock(&LOCK_event_metadata);
 
187
 
 
188
  return str;
 
189
}
 
190
 
 
191
 
 
192
/**
 
193
  Push an error into the error stack if the system tables are
 
194
  not up to date.
 
195
*/
 
196
 
 
197
bool Events::check_if_system_tables_error()
 
198
{
 
199
  DBUG_ENTER("Events::check_if_system_tables_error");
 
200
 
 
201
  if (check_system_tables_error)
 
202
  {
 
203
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
 
204
    DBUG_RETURN(TRUE);
 
205
  }
 
206
 
 
207
  DBUG_RETURN(FALSE);
 
208
}
 
209
 
 
210
 
 
211
/**
 
212
  Reconstructs interval expression from interval type and expression
 
213
  value that is in form of a value of the smalles entity:
 
214
  For
 
215
    YEAR_MONTH - expression is in months
 
216
    DAY_MINUTE - expression is in minutes
 
217
 
 
218
  SYNOPSIS
 
219
    Events::reconstruct_interval_expression()
 
220
      buf         Preallocated String buffer to add the value to
 
221
      interval    The interval type (for instance YEAR_MONTH)
 
222
      expression  The value in the lowest entity
 
223
 
 
224
  RETURN VALUE
 
225
    0  OK
 
226
    1  Error
 
227
*/
 
228
 
 
229
int
 
230
Events::reconstruct_interval_expression(String *buf, interval_type interval,
 
231
                                        longlong expression)
 
232
{
 
233
  ulonglong expr= expression;
 
234
  char tmp_buff[128], *end;
 
235
  bool close_quote= TRUE;
 
236
  int multipl= 0;
 
237
  char separator=':';
 
238
 
 
239
  switch (interval) {
 
240
  case INTERVAL_YEAR_MONTH:
 
241
    multipl= 12;
 
242
    separator= '-';
 
243
    goto common_1_lev_code;
 
244
  case INTERVAL_DAY_HOUR:
 
245
    multipl= 24;
 
246
    separator= ' ';
 
247
    goto common_1_lev_code;
 
248
  case INTERVAL_HOUR_MINUTE:
 
249
  case INTERVAL_MINUTE_SECOND:
 
250
    multipl= 60;
 
251
common_1_lev_code:
 
252
    buf->append('\'');
 
253
    end= longlong10_to_str(expression/multipl, tmp_buff, 10);
 
254
    buf->append(tmp_buff, (uint) (end- tmp_buff));
 
255
    expr= expr - (expr/multipl)*multipl;
 
256
    break;
 
257
  case INTERVAL_DAY_MINUTE:
 
258
  {
 
259
    ulonglong tmp_expr= expr;
 
260
 
 
261
    tmp_expr/=(24*60);
 
262
    buf->append('\'');
 
263
    end= longlong10_to_str(tmp_expr, tmp_buff, 10);
 
264
    buf->append(tmp_buff, (uint) (end- tmp_buff));// days
 
265
    buf->append(' ');
 
266
 
 
267
    tmp_expr= expr - tmp_expr*(24*60);//minutes left
 
268
    end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
 
269
    buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
 
270
 
 
271
    expr= tmp_expr - (tmp_expr/60)*60;
 
272
    /* the code after the switch will finish */
 
273
  }
 
274
    break;
 
275
  case INTERVAL_HOUR_SECOND:
 
276
  {
 
277
    ulonglong tmp_expr= expr;
 
278
 
 
279
    buf->append('\'');
 
280
    end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
 
281
    buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
 
282
    buf->append(':');
 
283
 
 
284
    tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
 
285
    end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
 
286
    buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
 
287
 
 
288
    expr= tmp_expr - (tmp_expr/60)*60;
 
289
    /* the code after the switch will finish */
 
290
  }
 
291
    break;
 
292
  case INTERVAL_DAY_SECOND:
 
293
  {
 
294
    ulonglong tmp_expr= expr;
 
295
 
 
296
    tmp_expr/=(24*3600);
 
297
    buf->append('\'');
 
298
    end= longlong10_to_str(tmp_expr, tmp_buff, 10);
 
299
    buf->append(tmp_buff, (uint) (end- tmp_buff));// days
 
300
    buf->append(' ');
 
301
 
 
302
    tmp_expr= expr - tmp_expr*(24*3600);//seconds left
 
303
    end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
 
304
    buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
 
305
    buf->append(':');
 
306
 
 
307
    tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
 
308
    end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
 
309
    buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
 
310
 
 
311
    expr= tmp_expr - (tmp_expr/60)*60;
 
312
    /* the code after the switch will finish */
 
313
  }
 
314
    break;
 
315
  case INTERVAL_DAY_MICROSECOND:
 
316
  case INTERVAL_HOUR_MICROSECOND:
 
317
  case INTERVAL_MINUTE_MICROSECOND:
 
318
  case INTERVAL_SECOND_MICROSECOND:
 
319
  case INTERVAL_MICROSECOND:
 
320
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
 
321
    return 1;
 
322
    break;
 
323
  case INTERVAL_QUARTER:
 
324
    expr/= 3;
 
325
    close_quote= FALSE;
 
326
    break;
 
327
  case INTERVAL_WEEK:
 
328
    expr/= 7;
 
329
  default:
 
330
    close_quote= FALSE;
 
331
    break;
 
332
  }
 
333
  if (close_quote)
 
334
    buf->append(separator);
 
335
  end= longlong10_to_str(expr, tmp_buff, 10);
 
336
  buf->append(tmp_buff, (uint) (end- tmp_buff));
 
337
  if (close_quote)
 
338
    buf->append('\'');
 
339
 
 
340
  return 0;
 
341
}
 
342
 
 
343
 
 
344
/**
 
345
  Create a new query string for removing executable comments 
 
346
  for avoiding leak and keeping consistency of the execution 
 
347
  on master and slave.
 
348
  
 
349
  @param[in] thd                 Thread handler
 
350
  @param[in] buf                 Query string
 
351
 
 
352
  @return
 
353
             0           ok
 
354
             1           error
 
355
*/
 
356
static int
 
357
create_query_string(THD *thd, String *buf)
 
358
{
 
359
  /* Append the "CREATE" part of the query */
 
360
  if (buf->append(STRING_WITH_LEN("CREATE ")))
 
361
    return 1;
 
362
  /* Append definer */
 
363
  append_definer(thd, buf, &(thd->lex->definer->user), &(thd->lex->definer->host));
 
364
  /* Append the left part of thd->query after "DEFINER" part */
 
365
  if (buf->append(thd->lex->stmt_definition_begin))
 
366
    return 1;
 
367
 
 
368
  return 0;
 
369
}
 
370
 
 
371
/**
 
372
  Create a new event.
 
373
 
 
374
  @param[in,out]  thd            THD
 
375
  @param[in]      parse_data     Event's data from parsing stage
 
376
  @param[in]      if_not_exists  Whether IF NOT EXISTS was
 
377
                                 specified
 
378
  In case there is an event with the same name (db) and
 
379
  IF NOT EXISTS is specified, an warning is put into the stack.
 
380
  @sa Events::drop_event for the notes about locking, pre-locking
 
381
  and Events DDL.
 
382
 
 
383
  @retval  FALSE  OK
 
384
  @retval  TRUE   Error (reported)
 
385
*/
 
386
 
 
387
bool
 
388
Events::create_event(THD *thd, Event_parse_data *parse_data,
 
389
                     bool if_not_exists)
 
390
{
 
391
  int ret;
 
392
  bool save_binlog_row_based;
 
393
  DBUG_ENTER("Events::create_event");
 
394
 
 
395
  /*
 
396
    Let's commit the transaction first - MySQL manual specifies
 
397
    that a DDL issues an implicit commit, and it doesn't say "successful
 
398
    DDL", so that an implicit commit is a property of any successfully
 
399
    parsed DDL statement.
 
400
  */
 
401
  if (end_active_trans(thd))
 
402
    DBUG_RETURN(TRUE);
 
403
 
 
404
  if (check_if_system_tables_error())
 
405
    DBUG_RETURN(TRUE);
 
406
 
 
407
  /*
 
408
    Perform semantic checks outside of Event_db_repository:
 
409
    once CREATE EVENT is supported in prepared statements, the
 
410
    checks will be moved to PREPARE phase.
 
411
  */
 
412
  if (parse_data->check_parse_data(thd))
 
413
    DBUG_RETURN(TRUE);
 
414
 
 
415
  /* At create, one of them must be set */
 
416
  DBUG_ASSERT(parse_data->expression || parse_data->execute_at);
 
417
 
 
418
  if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
 
419
                   is_schema_db(parse_data->dbname.str,
 
420
                                parse_data->dbname.length)))
 
421
    DBUG_RETURN(TRUE);
 
422
 
 
423
  if (check_db_dir_existence(parse_data->dbname.str))
 
424
  {
 
425
    my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
 
426
    DBUG_RETURN(TRUE);
 
427
  }
 
428
 
 
429
  if (parse_data->do_not_create)
 
430
    DBUG_RETURN(FALSE);
 
431
  /* 
 
432
    Turn off row binlogging of this statement and use statement-based 
 
433
    so that all supporting tables are updated for CREATE EVENT command.
 
434
  */
 
435
  save_binlog_row_based= thd->current_stmt_binlog_row_based;
 
436
  thd->clear_current_stmt_binlog_row_based();
 
437
 
 
438
  pthread_mutex_lock(&LOCK_event_metadata);
 
439
 
 
440
  /* On error conditions my_error() is called so no need to handle here */
 
441
  if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
 
442
  {
 
443
    Event_queue_element *new_element;
 
444
    bool dropped= 0;
 
445
 
 
446
    if (!(new_element= new Event_queue_element()))
 
447
      ret= TRUE;                                // OOM
 
448
    else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
 
449
                                                   parse_data->name,
 
450
                                                   new_element)))
 
451
    {
 
452
      if (!db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
 
453
                                     TRUE))
 
454
        dropped= 1;
 
455
      delete new_element;
 
456
    }
 
457
    else
 
458
    {
 
459
      /* TODO: do not ignore the out parameter and a possible OOM error! */
 
460
      bool created;
 
461
      if (event_queue)
 
462
        event_queue->create_event(thd, new_element, &created);
 
463
    }
 
464
    /*
 
465
      binlog the create event unless it's been successfully dropped
 
466
    */
 
467
    if (!dropped)
 
468
    {
 
469
      /* Binlog the create event. */
 
470
      DBUG_ASSERT(thd->query() && thd->query_length());
 
471
      String log_query;
 
472
      if (create_query_string(thd, &log_query))
 
473
      {
 
474
        sql_print_error("Event Error: An error occurred while creating query string, "
 
475
                        "before writing it into binary log.");
 
476
        /* Restore the state of binlog format */
 
477
        thd->current_stmt_binlog_row_based= save_binlog_row_based;
 
478
        DBUG_RETURN(TRUE);
 
479
      }
 
480
      /* If the definer is not set or set to CURRENT_USER, the value of CURRENT_USER 
 
481
         will be written into the binary log as the definer for the SQL thread. */
 
482
      ret= write_bin_log(thd, TRUE, log_query.c_ptr(), log_query.length());
 
483
    }
 
484
  }
 
485
  pthread_mutex_unlock(&LOCK_event_metadata);
 
486
  /* Restore the state of binlog format */
 
487
  thd->current_stmt_binlog_row_based= save_binlog_row_based;
 
488
 
 
489
  DBUG_RETURN(ret);
 
490
}
 
491
 
 
492
 
 
493
/**
 
494
  Alter an event.
 
495
 
 
496
  @param[in,out] thd         THD
 
497
  @param[in]     parse_data  Event's data from parsing stage
 
498
  @param[in]     new_dbname  A new schema name for the event. Set in the case of
 
499
                             ALTER EVENT RENAME, otherwise is NULL.
 
500
  @param[in]     new_name    A new name for the event. Set in the case of
 
501
                             ALTER EVENT RENAME
 
502
 
 
503
  Parameter 'et' contains data about dbname and event name.
 
504
  Parameter 'new_name' is the new name of the event, if not null
 
505
  this means that RENAME TO was specified in the query
 
506
  @sa Events::drop_event for the locking notes.
 
507
 
 
508
  @retval  FALSE  OK
 
509
  @retval  TRUE   error (reported)
 
510
*/
 
511
 
 
512
bool
 
513
Events::update_event(THD *thd, Event_parse_data *parse_data,
 
514
                     LEX_STRING *new_dbname, LEX_STRING *new_name)
 
515
{
 
516
  int ret;
 
517
  bool save_binlog_row_based;
 
518
  Event_queue_element *new_element;
 
519
 
 
520
  DBUG_ENTER("Events::update_event");
 
521
 
 
522
  /*
 
523
    For consistency, implicit COMMIT should be the first thing in the
 
524
    execution chain.
 
525
  */
 
526
  if (end_active_trans(thd))
 
527
    DBUG_RETURN(TRUE);
 
528
 
 
529
  if (check_if_system_tables_error())
 
530
    DBUG_RETURN(TRUE);
 
531
 
 
532
  if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
 
533
    DBUG_RETURN(TRUE);
 
534
 
 
535
  if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
 
536
                   is_schema_db(parse_data->dbname.str,
 
537
                                parse_data->dbname.length)))
 
538
    DBUG_RETURN(TRUE);
 
539
 
 
540
  if (new_dbname)                               /* It's a rename */
 
541
  {
 
542
    /* Check that the new and the old names differ. */
 
543
    if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname,
 
544
                             system_charset_info) &&
 
545
         !sortcmp_lex_string(parse_data->name, *new_name,
 
546
                             system_charset_info))
 
547
    {
 
548
      my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
 
549
      DBUG_RETURN(TRUE);
 
550
    }
 
551
 
 
552
    /*
 
553
      And the user has sufficient privileges to use the target database.
 
554
      Do it before checking whether the database exists: we don't want
 
555
      to tell the user that a database doesn't exist if they can not
 
556
      access it.
 
557
    */
 
558
    if (check_access(thd, EVENT_ACL, new_dbname->str, 0, 0, 0,
 
559
                     is_schema_db(new_dbname->str, new_dbname->length)))
 
560
      DBUG_RETURN(TRUE);
 
561
 
 
562
    /* Check that the target database exists */
 
563
    if (check_db_dir_existence(new_dbname->str))
 
564
    {
 
565
      my_error(ER_BAD_DB_ERROR, MYF(0), new_dbname->str);
 
566
      DBUG_RETURN(TRUE);
 
567
    }
 
568
  }
 
569
 
 
570
  /* 
 
571
    Turn off row binlogging of this statement and use statement-based 
 
572
    so that all supporting tables are updated for UPDATE EVENT command.
 
573
  */
 
574
  save_binlog_row_based= thd->current_stmt_binlog_row_based;
 
575
  thd->clear_current_stmt_binlog_row_based();
 
576
 
 
577
  pthread_mutex_lock(&LOCK_event_metadata);
 
578
 
 
579
  /* On error conditions my_error() is called so no need to handle here */
 
580
  if (!(ret= db_repository->update_event(thd, parse_data,
 
581
                                         new_dbname, new_name)))
 
582
  {
 
583
    LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
 
584
    LEX_STRING name= new_name ? *new_name : parse_data->name;
 
585
 
 
586
    if (!(new_element= new Event_queue_element()))
 
587
      ret= TRUE;                                // OOM
 
588
    else if ((ret= db_repository->load_named_event(thd, dbname, name,
 
589
                                                   new_element)))
 
590
    {
 
591
      DBUG_ASSERT(ret == OP_LOAD_ERROR);
 
592
      delete new_element;
 
593
    }
 
594
    else
 
595
    {
 
596
      /*
 
597
        TODO: check if an update actually has inserted an entry
 
598
        into the queue.
 
599
        If not, and the element is ON COMPLETION NOT PRESERVE, delete
 
600
        it right away.
 
601
      */
 
602
      if (event_queue)
 
603
        event_queue->update_event(thd, parse_data->dbname, parse_data->name,
 
604
                                  new_element);
 
605
      /* Binlog the alter event. */
 
606
      DBUG_ASSERT(thd->query() && thd->query_length());
 
607
      ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
 
608
    }
 
609
  }
 
610
  pthread_mutex_unlock(&LOCK_event_metadata);
 
611
  /* Restore the state of binlog format */
 
612
  thd->current_stmt_binlog_row_based= save_binlog_row_based;
 
613
 
 
614
  DBUG_RETURN(ret);
 
615
}
 
616
 
 
617
 
 
618
/**
 
619
  Drops an event
 
620
 
 
621
  @param[in,out]  thd        THD
 
622
  @param[in]      dbname     Event's schema
 
623
  @param[in]      name       Event's name
 
624
  @param[in]      if_exists  When this is set and the event does not exist
 
625
                             a warning is pushed into the warning stack.
 
626
                             Otherwise the operation produces an error.
 
627
 
 
628
  @note Similarly to DROP PROCEDURE, we do not allow DROP EVENT
 
629
  under LOCK TABLES mode, unless table mysql.event is locked.  To
 
630
  ensure that, we do not reset & backup the open tables state in
 
631
  this function - if in LOCK TABLES or pre-locking mode, this will
 
632
  lead to an error 'Table mysql.event is not locked with LOCK
 
633
  TABLES' unless it _is_ locked. In pre-locked mode there is
 
634
  another barrier - DROP EVENT commits the current transaction,
 
635
  and COMMIT/ROLLBACK is not allowed in stored functions and
 
636
  triggers.
 
637
 
 
638
  @retval  FALSE  OK
 
639
  @retval  TRUE   Error (reported)
 
640
*/
 
641
 
 
642
bool
 
643
Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
 
644
{
 
645
  int ret;
 
646
  bool save_binlog_row_based;
 
647
  DBUG_ENTER("Events::drop_event");
 
648
 
 
649
  /*
 
650
    In MySQL, DDL must always commit: since mysql.* tables are
 
651
    non-transactional, we must modify them outside a transaction
 
652
    to not break atomicity.
 
653
    But the second and more important reason to commit here
 
654
    regardless whether we're actually changing mysql.event table
 
655
    or not is replication: end_active_trans syncs the binary log,
 
656
    and unless we run DDL in it's own transaction it may simply
 
657
    never appear on the slave in case the outside transaction
 
658
    rolls back.
 
659
  */
 
660
  if (end_active_trans(thd))
 
661
    DBUG_RETURN(TRUE);
 
662
 
 
663
  if (check_if_system_tables_error())
 
664
    DBUG_RETURN(TRUE);
 
665
 
 
666
  if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
 
667
                   is_schema_db(dbname.str, dbname.length)))
 
668
    DBUG_RETURN(TRUE);
 
669
 
 
670
  /*
 
671
    Turn off row binlogging of this statement and use statement-based so
 
672
    that all supporting tables are updated for DROP EVENT command.
 
673
  */
 
674
  save_binlog_row_based= thd->current_stmt_binlog_row_based;
 
675
  thd->clear_current_stmt_binlog_row_based();
 
676
 
 
677
  pthread_mutex_lock(&LOCK_event_metadata);
 
678
  /* On error conditions my_error() is called so no need to handle here */
 
679
  if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
 
680
  {
 
681
    if (event_queue)
 
682
      event_queue->drop_event(thd, dbname, name);
 
683
    /* Binlog the drop event. */
 
684
    DBUG_ASSERT(thd->query() && thd->query_length());
 
685
    ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
 
686
  }
 
687
  pthread_mutex_unlock(&LOCK_event_metadata);
 
688
  /* Restore the state of binlog format */
 
689
  thd->current_stmt_binlog_row_based= save_binlog_row_based;
 
690
  DBUG_RETURN(ret);
 
691
}
 
692
 
 
693
 
 
694
/**
 
695
  Drops all events from a schema
 
696
 
 
697
  @note We allow to drop all events in a schema even if the
 
698
  scheduler is disabled. This is to not produce any warnings
 
699
  in case of DROP DATABASE and a disabled scheduler.
 
700
 
 
701
  @param[in,out]  thd  Thread
 
702
  @param[in]      db   ASCIIZ schema name
 
703
*/
 
704
 
 
705
void
 
706
Events::drop_schema_events(THD *thd, char *db)
 
707
{
 
708
  LEX_STRING const db_lex= { db, strlen(db) };
 
709
 
 
710
  DBUG_ENTER("Events::drop_schema_events");
 
711
  DBUG_PRINT("enter", ("dropping events from %s", db));
 
712
 
 
713
  /*
 
714
    sic: no check if the scheduler is disabled or system tables
 
715
    are damaged, as intended.
 
716
  */
 
717
 
 
718
  pthread_mutex_lock(&LOCK_event_metadata);
 
719
  if (event_queue)
 
720
    event_queue->drop_schema_events(thd, db_lex);
 
721
  db_repository->drop_schema_events(thd, db_lex);
 
722
  pthread_mutex_unlock(&LOCK_event_metadata);
 
723
 
 
724
  DBUG_VOID_RETURN;
 
725
}
 
726
 
 
727
 
 
728
/**
 
729
  A helper function to generate SHOW CREATE EVENT output from
 
730
  a named event
 
731
*/
 
732
 
 
733
static bool
 
734
send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
 
735
{
 
736
  char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
 
737
  String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
 
738
  List<Item> field_list;
 
739
  LEX_STRING sql_mode;
 
740
  const String *tz_name;
 
741
 
 
742
  DBUG_ENTER("send_show_create_event");
 
743
 
 
744
  show_str.length(0);
 
745
  if (et->get_create_event(thd, &show_str))
 
746
    DBUG_RETURN(TRUE);
 
747
 
 
748
  field_list.push_back(new Item_empty_string("Event", NAME_CHAR_LEN));
 
749
 
 
750
  if (sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
 
751
                                                         &sql_mode))
 
752
    DBUG_RETURN(TRUE);
 
753
 
 
754
  field_list.push_back(new Item_empty_string("sql_mode", (uint) sql_mode.length));
 
755
 
 
756
  tz_name= et->time_zone->get_name();
 
757
 
 
758
  field_list.push_back(new Item_empty_string("time_zone",
 
759
                                             tz_name->length()));
 
760
 
 
761
  field_list.push_back(new Item_empty_string("Create Event",
 
762
                                             show_str.length()));
 
763
 
 
764
  field_list.push_back(
 
765
    new Item_empty_string("character_set_client", MY_CS_NAME_SIZE));
 
766
 
 
767
  field_list.push_back(
 
768
    new Item_empty_string("collation_connection", MY_CS_NAME_SIZE));
 
769
 
 
770
  field_list.push_back(
 
771
    new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
 
772
 
 
773
  if (protocol->send_fields(&field_list,
 
774
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
 
775
    DBUG_RETURN(TRUE);
 
776
 
 
777
  protocol->prepare_for_resend();
 
778
 
 
779
  protocol->store(et->name.str, et->name.length, system_charset_info);
 
780
  protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
 
781
  protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info);
 
782
  protocol->store(show_str.c_ptr(), show_str.length(),
 
783
                  et->creation_ctx->get_client_cs());
 
784
  protocol->store(et->creation_ctx->get_client_cs()->csname,
 
785
                  strlen(et->creation_ctx->get_client_cs()->csname),
 
786
                  system_charset_info);
 
787
  protocol->store(et->creation_ctx->get_connection_cl()->name,
 
788
                  strlen(et->creation_ctx->get_connection_cl()->name),
 
789
                  system_charset_info);
 
790
  protocol->store(et->creation_ctx->get_db_cl()->name,
 
791
                  strlen(et->creation_ctx->get_db_cl()->name),
 
792
                  system_charset_info);
 
793
 
 
794
  if (protocol->write())
 
795
    DBUG_RETURN(TRUE);
 
796
 
 
797
  my_eof(thd);
 
798
 
 
799
  DBUG_RETURN(FALSE);
 
800
}
 
801
 
 
802
 
 
803
/**
 
804
  Implement SHOW CREATE EVENT statement
 
805
 
 
806
      thd   Thread context
 
807
      spn   The name of the event (db, name)
 
808
 
 
809
  @retval  FALSE  OK
 
810
  @retval  TRUE   error (reported)
 
811
*/
 
812
 
 
813
bool
 
814
Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
 
815
{
 
816
  Open_tables_state open_tables_backup;
 
817
  Event_timed et;
 
818
  bool ret;
 
819
 
 
820
  DBUG_ENTER("Events::show_create_event");
 
821
  DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
 
822
 
 
823
  if (check_if_system_tables_error())
 
824
    DBUG_RETURN(TRUE);
 
825
 
 
826
  if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
 
827
                   is_schema_db(dbname.str, dbname.length)))
 
828
    DBUG_RETURN(TRUE);
 
829
 
 
830
  /*
 
831
    We would like to allow SHOW CREATE EVENT under LOCK TABLES and
 
832
    in pre-locked mode. mysql.event table is marked as a system table.
 
833
    This flag reduces the set of its participation scenarios in LOCK TABLES
 
834
    operation, and therefore an out-of-bound open of this table
 
835
    for reading like the one below (sic, only for reading) is
 
836
    more or less deadlock-free. For additional information about when a
 
837
    deadlock can occur please refer to the description of 'system table'
 
838
    flag.
 
839
  */
 
840
  thd->reset_n_backup_open_tables_state(&open_tables_backup);
 
841
  ret= db_repository->load_named_event(thd, dbname, name, &et);
 
842
  thd->restore_backup_open_tables_state(&open_tables_backup);
 
843
 
 
844
  if (!ret)
 
845
    ret= send_show_create_event(thd, &et, thd->protocol);
 
846
 
 
847
  DBUG_RETURN(ret);
 
848
}
 
849
 
 
850
 
 
851
/**
 
852
  Check access rights and fill INFORMATION_SCHEMA.events table.
 
853
 
 
854
  @param[in,out]  thd     Thread context
 
855
  @param[in]      tables  The temporary table to fill.
 
856
 
 
857
  In MySQL INFORMATION_SCHEMA tables are temporary tables that are
 
858
  created and filled on demand. In this function, we fill
 
859
  INFORMATION_SCHEMA.events. It is a callback for I_S module, invoked from
 
860
  sql_show.cc
 
861
 
 
862
  @return Has to be integer, as such is the requirement of the I_S API
 
863
  @retval  0  success
 
864
  @retval  1  an error, pushed into the error stack
 
865
*/
 
866
 
 
867
int
 
868
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
 
869
{
 
870
  char *db= NULL;
 
871
  int ret;
 
872
  Open_tables_state open_tables_backup;
 
873
  DBUG_ENTER("Events::fill_schema_events");
 
874
 
 
875
  if (check_if_system_tables_error())
 
876
    DBUG_RETURN(1);
 
877
 
 
878
  /*
 
879
    If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
 
880
    be NULL. Let's do an assert anyway.
 
881
  */
 
882
  if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
 
883
  {
 
884
    DBUG_ASSERT(thd->lex->select_lex.db);
 
885
    if (!is_schema_db(thd->lex->select_lex.db) &&    // There is no events in I_S
 
886
        check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, 0))
 
887
      DBUG_RETURN(1);
 
888
    db= thd->lex->select_lex.db;
 
889
  }
 
890
  /*
 
891
    Reset and backup of the currently open tables in this thread
 
892
    is a way to allow SELECTs from INFORMATION_SCHEMA.events under
 
893
    LOCK TABLES and in pre-locked mode. See also
 
894
    Events::show_create_event for additional comments.
 
895
  */
 
896
  thd->reset_n_backup_open_tables_state(&open_tables_backup);
 
897
  ret= db_repository->fill_schema_events(thd, tables, db);
 
898
  thd->restore_backup_open_tables_state(&open_tables_backup);
 
899
 
 
900
  DBUG_RETURN(ret);
 
901
}
 
902
 
 
903
 
 
904
/**
 
905
  Initializes the scheduler's structures.
 
906
 
 
907
  @param  opt_noacl_or_bootstrap
 
908
                     TRUE if there is --skip-grant-tables or --bootstrap
 
909
                     option. In that case we disable the event scheduler.
 
910
 
 
911
  @note   This function is not synchronized.
 
912
 
 
913
  @retval  FALSE   Perhaps there was an error, and the event scheduler
 
914
                   is disabled. But the error is not fatal and the 
 
915
                   server start up can continue.
 
916
  @retval  TRUE    Fatal error. Startup must terminate (call unireg_abort()).
 
917
*/
 
918
 
 
919
bool
 
920
Events::init(my_bool opt_noacl_or_bootstrap)
 
921
{
 
922
 
 
923
  THD *thd;
 
924
  bool res= FALSE;
 
925
 
 
926
  DBUG_ENTER("Events::init");
 
927
 
 
928
  /* We need a temporary THD during boot */
 
929
  if (!(thd= new THD()))
 
930
  {
 
931
    res= TRUE;
 
932
    goto end;
 
933
  }
 
934
  /*
 
935
    The thread stack does not start from this function but we cannot
 
936
    guess the real value. So better some value that doesn't assert than
 
937
    no value.
 
938
  */
 
939
  thd->thread_stack= (char*) &thd;
 
940
  thd->store_globals();
 
941
  lex_start(thd);
 
942
 
 
943
  /*
 
944
    We will need Event_db_repository anyway, even if the scheduler is
 
945
    disabled - to perform events DDL.
 
946
  */
 
947
  if (!(db_repository= new Event_db_repository))
 
948
  {
 
949
    res= TRUE; /* fatal error: request unireg_abort */
 
950
    goto end;
 
951
  }
 
952
 
 
953
  /*
 
954
    Since we allow event DDL even if the scheduler is disabled,
 
955
    check the system tables, as we might need them.
 
956
 
 
957
    If run with --skip-grant-tables or --bootstrap, don't try to do the
 
958
    check of system tables and don't complain: in these modes the tables
 
959
    are most likely not there and we're going to disable the event
 
960
    scheduler anyway.
 
961
  */
 
962
  if (opt_noacl_or_bootstrap || Event_db_repository::check_system_tables(thd))
 
963
  {
 
964
    if (! opt_noacl_or_bootstrap)
 
965
    {
 
966
      sql_print_error("Event Scheduler: An error occurred when initializing "
 
967
                      "system tables. Disabling the Event Scheduler.");
 
968
      check_system_tables_error= TRUE;
 
969
    }
 
970
 
 
971
    /* Disable the scheduler since the system tables are not up to date */
 
972
    opt_event_scheduler= EVENTS_DISABLED;
 
973
    goto end;
 
974
  }
 
975
 
 
976
  /*
 
977
    Was disabled explicitly from the command line, or because we're running
 
978
    with --skip-grant-tables, or --bootstrap, or because we have no system
 
979
    tables.
 
980
  */
 
981
  if (opt_event_scheduler == Events::EVENTS_DISABLED)
 
982
    goto end;
 
983
 
 
984
 
 
985
  DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
 
986
              opt_event_scheduler == Events::EVENTS_OFF);
 
987
 
 
988
  if (!(event_queue= new Event_queue) ||
 
989
      !(scheduler= new Event_scheduler(event_queue)))
 
990
  {
 
991
    res= TRUE; /* fatal error: request unireg_abort */
 
992
    goto end;
 
993
  }
 
994
 
 
995
  if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
 
996
      (opt_event_scheduler == EVENTS_ON && scheduler->start()))
 
997
  {
 
998
    sql_print_error("Event Scheduler: Error while loading from disk.");
 
999
    res= TRUE; /* fatal error: request unireg_abort */
 
1000
    goto end;
 
1001
  }
 
1002
  Event_worker_thread::init(db_repository);
 
1003
 
 
1004
end:
 
1005
  if (res)
 
1006
  {
 
1007
    delete db_repository;
 
1008
    delete event_queue;
 
1009
    delete scheduler;
 
1010
  }
 
1011
  delete thd;
 
1012
  /* Remember that we don't have a THD */
 
1013
  my_pthread_setspecific_ptr(THR_THD,  NULL);
 
1014
 
 
1015
  DBUG_RETURN(res);
 
1016
}
 
1017
 
 
1018
/*
 
1019
  Cleans up scheduler's resources. Called at server shutdown.
 
1020
 
 
1021
  SYNOPSIS
 
1022
    Events::deinit()
 
1023
 
 
1024
  NOTES
 
1025
    This function is not synchronized.
 
1026
*/
 
1027
 
 
1028
void
 
1029
Events::deinit()
 
1030
{
 
1031
  DBUG_ENTER("Events::deinit");
 
1032
 
 
1033
  if (opt_event_scheduler != EVENTS_DISABLED)
 
1034
  {
 
1035
    delete scheduler;
 
1036
    scheduler= NULL;                            /* safety */
 
1037
    delete event_queue;
 
1038
    event_queue= NULL;                          /* safety */
 
1039
  }
 
1040
 
 
1041
  delete db_repository;
 
1042
  db_repository= NULL;                          /* safety */
 
1043
 
 
1044
  DBUG_VOID_RETURN;
 
1045
}
 
1046
 
 
1047
 
 
1048
/**
 
1049
  Inits Events mutexes
 
1050
 
 
1051
  SYNOPSIS
 
1052
    Events::init_mutexes()
 
1053
      thd  Thread
 
1054
*/
 
1055
 
 
1056
void
 
1057
Events::init_mutexes()
 
1058
{
 
1059
  pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
 
1060
}
 
1061
 
 
1062
 
 
1063
/*
 
1064
  Destroys Events mutexes
 
1065
 
 
1066
  SYNOPSIS
 
1067
    Events::destroy_mutexes()
 
1068
*/
 
1069
 
 
1070
void
 
1071
Events::destroy_mutexes()
 
1072
{
 
1073
  pthread_mutex_destroy(&LOCK_event_metadata);
 
1074
}
 
1075
 
 
1076
 
 
1077
/*
 
1078
  Dumps the internal status of the scheduler and the memory cache
 
1079
  into a table with two columns - Name & Value. Different properties
 
1080
  which could be useful for debugging for instance deadlocks are
 
1081
  returned.
 
1082
 
 
1083
  SYNOPSIS
 
1084
    Events::dump_internal_status()
 
1085
*/
 
1086
 
 
1087
void
 
1088
Events::dump_internal_status()
 
1089
{
 
1090
  DBUG_ENTER("Events::dump_internal_status");
 
1091
  puts("\n\n\nEvents status:");
 
1092
  puts("LLA = Last Locked At  LUA = Last Unlocked At");
 
1093
  puts("WOC = Waiting On Condition  DL = Data Locked");
 
1094
 
 
1095
  pthread_mutex_lock(&LOCK_event_metadata);
 
1096
  if (opt_event_scheduler == EVENTS_DISABLED)
 
1097
    puts("The Event Scheduler is disabled");
 
1098
  else
 
1099
  {
 
1100
    scheduler->dump_internal_status();
 
1101
    event_queue->dump_internal_status();
 
1102
  }
 
1103
 
 
1104
  pthread_mutex_unlock(&LOCK_event_metadata);
 
1105
  DBUG_VOID_RETURN;
 
1106
}
 
1107
 
 
1108
 
 
1109
/**
 
1110
  Starts or stops the event scheduler thread.
 
1111
 
 
1112
  @retval FALSE success
 
1113
  @retval TRUE  error
 
1114
*/
 
1115
 
 
1116
bool
 
1117
Events::switch_event_scheduler_state(enum_opt_event_scheduler new_state)
 
1118
{
 
1119
  bool ret= FALSE;
 
1120
 
 
1121
  DBUG_ENTER("Events::switch_event_scheduler_state");
 
1122
 
 
1123
  DBUG_ASSERT(new_state == Events::EVENTS_ON ||
 
1124
              new_state == Events::EVENTS_OFF);
 
1125
 
 
1126
  /*
 
1127
    If the scheduler was disabled because there are no/bad
 
1128
    system tables, produce a more meaningful error message
 
1129
    than ER_OPTION_PREVENTS_STATEMENT
 
1130
  */
 
1131
  if (check_if_system_tables_error())
 
1132
    DBUG_RETURN(TRUE);
 
1133
 
 
1134
  pthread_mutex_lock(&LOCK_event_metadata);
 
1135
 
 
1136
  if (opt_event_scheduler == EVENTS_DISABLED)
 
1137
  {
 
1138
    my_error(ER_OPTION_PREVENTS_STATEMENT,
 
1139
             MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables");
 
1140
    ret= TRUE;
 
1141
    goto end;
 
1142
  }
 
1143
 
 
1144
  if (new_state == EVENTS_ON)
 
1145
    ret= scheduler->start();
 
1146
  else
 
1147
    ret= scheduler->stop();
 
1148
 
 
1149
  if (ret)
 
1150
  {
 
1151
    my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
 
1152
    goto end;
 
1153
  }
 
1154
 
 
1155
  opt_event_scheduler= new_state;
 
1156
 
 
1157
end:
 
1158
  pthread_mutex_unlock(&LOCK_event_metadata);
 
1159
  DBUG_RETURN(ret);
 
1160
}
 
1161
 
 
1162
 
 
1163
/**
 
1164
  Loads all ENABLED events from mysql.event into a prioritized
 
1165
  queue.
 
1166
 
 
1167
  This function is called during the server start up. It reads
 
1168
  every event, computes the next execution time, and if the event
 
1169
  needs execution, adds it to a prioritized queue. Otherwise, if
 
1170
  ON COMPLETION DROP is specified, the event is automatically
 
1171
  removed from the table.
 
1172
 
 
1173
  @param[in,out] thd Thread context. Used for memory allocation in some cases.
 
1174
 
 
1175
  @retval  FALSE  success
 
1176
  @retval  TRUE   error, the load is aborted
 
1177
 
 
1178
  @note Reports the error to the console
 
1179
*/
 
1180
 
 
1181
bool
 
1182
Events::load_events_from_db(THD *thd)
 
1183
{
 
1184
  TABLE *table;
 
1185
  READ_RECORD read_record_info;
 
1186
  bool ret= TRUE;
 
1187
  uint count= 0;
 
1188
  ulong saved_master_access;
 
1189
 
 
1190
  DBUG_ENTER("Events::load_events_from_db");
 
1191
  DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
 
1192
 
 
1193
  /*
 
1194
    NOTE: even if we run in read-only mode, we should be able to lock the
 
1195
    mysql.event table for writing. In order to achieve this, we should call
 
1196
    mysql_lock_tables() under the super user.
 
1197
  */
 
1198
 
 
1199
  saved_master_access= thd->security_ctx->master_access;
 
1200
  thd->security_ctx->master_access |= SUPER_ACL;
 
1201
 
 
1202
  ret= db_repository->open_event_table(thd, TL_WRITE, &table);
 
1203
 
 
1204
  thd->security_ctx->master_access= saved_master_access;
 
1205
 
 
1206
  if (ret)
 
1207
  {
 
1208
    sql_print_error("Event Scheduler: Failed to open table mysql.event");
 
1209
    DBUG_RETURN(TRUE);
 
1210
  }
 
1211
 
 
1212
  init_read_record(&read_record_info, thd, table, NULL, 0, 1, FALSE);
 
1213
  while (!(read_record_info.read_record(&read_record_info)))
 
1214
  {
 
1215
    Event_queue_element *et;
 
1216
    bool created;
 
1217
    bool drop_on_completion;
 
1218
 
 
1219
    if (!(et= new Event_queue_element))
 
1220
      goto end;
 
1221
 
 
1222
    DBUG_PRINT("info", ("Loading event from row."));
 
1223
 
 
1224
    if (et->load_from_row(thd, table))
 
1225
    {
 
1226
      sql_print_error("Event Scheduler: "
 
1227
                      "Error while loading events from mysql.event. "
 
1228
                      "The table probably contains bad data or is corrupted");
 
1229
      delete et;
 
1230
      goto end;
 
1231
    }
 
1232
    drop_on_completion= (et->on_completion ==
 
1233
                         Event_parse_data::ON_COMPLETION_DROP);
 
1234
 
 
1235
 
 
1236
    if (event_queue->create_event(thd, et, &created))
 
1237
    {
 
1238
      /* Out of memory */
 
1239
      delete et;
 
1240
      goto end;
 
1241
    }
 
1242
    if (created)
 
1243
      count++;
 
1244
    else if (drop_on_completion)
 
1245
    {
 
1246
      /*
 
1247
        If not created, a stale event - drop if immediately if
 
1248
        ON COMPLETION NOT PRESERVE.
 
1249
        XXX: This won't be replicated, thus the drop won't appear in
 
1250
             in the slave. When the slave is restarted it will drop events.
 
1251
             However, as the slave will be "out of sync", it might happen that
 
1252
             an event created on the master, after master restart, won't be
 
1253
             replicated to the slave correctly, as the create will fail there.
 
1254
      */
 
1255
      int rc= table->file->ha_delete_row(table->record[0]);
 
1256
      if (rc)
 
1257
      {
 
1258
        table->file->print_error(rc, MYF(0));
 
1259
        goto end;
 
1260
      }
 
1261
    }
 
1262
  }
 
1263
  sql_print_information("Event Scheduler: Loaded %d event%s",
 
1264
                        count, (count == 1) ? "" : "s");
 
1265
  ret= FALSE;
 
1266
 
 
1267
end:
 
1268
  end_read_record(&read_record_info);
 
1269
 
 
1270
  close_thread_tables(thd);
 
1271
 
 
1272
  DBUG_RETURN(ret);
 
1273
}
 
1274
 
 
1275
/**
 
1276
  @} (End of group Event_Scheduler)
 
1277
*/