events.cc 22.1 KB
Newer Older
1
/* Copyright (C) 2004-2006 MySQL AB
unknown's avatar
unknown committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

17 18
#include "mysql_priv.h"
#include "events.h"
19
#include "event_data_objects.h"
unknown's avatar
unknown committed
20
#include "event_db_repository.h"
21
#include "event_queue.h"
22
#include "event_scheduler.h"
23
#include "sp_head.h"
24 25 26

/*
 TODO list :
unknown's avatar
unknown committed
27 28
 - CREATE EVENT should not go into binary log! Does it now? The SQL statements
   issued by the EVENT are replicated.
29 30 31 32 33 34 35
   I have an idea how to solve the problem at failover. So the status field
   will be ENUM('DISABLED', 'ENABLED', 'SLAVESIDE_DISABLED').
   In this case when CREATE EVENT is replicated it should go into the binary
   as SLAVESIDE_DISABLED if it is ENABLED, when it's created as DISABLEd it
   should be replicated as disabled. If an event is ALTERed as DISABLED the
   query should go untouched into the binary log, when ALTERed as enable then
   it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface.
36 37
   TT routines however modify mysql.event internally and this does not go the
   log so in this case queries has to be injected into the log...somehow... or
38 39 40
   maybe a solution is RBR for this case, because the event may go only from
   ENABLED to DISABLED status change and this is safe for replicating. As well
   an event may be deleted which is also safe for RBR.
unknown's avatar
unknown committed
41 42 43

 - Add logging to file

unknown's avatar
unknown committed
44
*/
unknown's avatar
unknown committed
45 46


47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
/*
  If the user (un)intentionally removes an event directly from mysql.event
  the following sequence has to be used to be able to remove the in-memory
  counterpart.
  1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
  2. DROP EVENT the_name
  
  In other words, the first one will create a row in mysql.event . In the
  second step because there will be a line, disk based drop will pass and
  the scheduler will remove the memory counterpart. The reason is that
  in-memory queue does not check whether the event we try to drop from memory
  is disabled. Disabled events are not kept in-memory because they are not
  eligible for execution.
*/

62 63 64 65 66 67 68
/*
  Keep the order of the first to as in var_typelib
  sys_var_event_scheduler::value_ptr() references this array. Keep in
  mind!
*/
static const char *opt_event_scheduler_state_names[]=
    { "OFF", "ON", "0", "1", "DISABLED", NullS };
69 70 71

TYPELIB Events::opt_typelib=
{
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
  array_elements(opt_event_scheduler_state_names)-1,
  "",
  opt_event_scheduler_state_names,
  NULL
};


/*
  The order should not be changed. We consider OFF to be equivalent of INT 0
  And ON of 1. If OFF & ON are interchanged the logic in
  sys_var_event_scheduler::update() will be broken!
*/
static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };

TYPELIB Events::var_typelib=
{
  array_elements(var_event_scheduler_state_names)-1,
89
  "",
90
  var_event_scheduler_state_names,
91 92 93
  NULL
};

94

95 96 97 98 99 100 101 102 103
static
Event_queue events_event_queue;

static
Event_scheduler events_event_scheduler;

static
Event_db_repository events_event_db_repository;

unknown's avatar
unknown committed
104
Events Events::singleton;
105

106 107
enum Events::enum_opt_event_scheduler Events::opt_event_scheduler=
     Events::EVENTS_OFF;
108

109 110 111

/*
  Compares 2 LEX strings regarding case.
unknown's avatar
unknown committed
112

113
  SYNOPSIS
114
    sortcmp_lex_string()
115 116 117
      s   First LEX_STRING
      t   Second LEX_STRING
      cs  Charset
118

119
  RETURN VALUE
120 121 122
   -1   s < t
    0   s == t
    1   s > t
123
*/
124

unknown's avatar
unknown committed
125 126
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
{
unknown's avatar
unknown committed
127 128
 return cs->coll->strnncollsp(cs, (uchar *) s.str,s.length,
                                  (uchar *) t.str,t.length, 0);
unknown's avatar
unknown committed
129 130
}

131

unknown's avatar
unknown committed
132 133 134 135 136 137 138
/*
  Accessor for the singleton instance.

  SYNOPSIS
    Events::get_instance()

  RETURN VALUE
unknown's avatar
unknown committed
139
    address
unknown's avatar
unknown committed
140
*/
unknown's avatar
unknown committed
141

unknown's avatar
unknown committed
142 143 144 145 146 147 148
Events *
Events::get_instance()
{
  DBUG_ENTER("Events::get_instance");
  DBUG_RETURN(&singleton);
}

unknown's avatar
unknown committed
149

150 151 152 153 154 155
/*
  Reconstructs interval expression from interval type and expression
  value that is in form of a value of the smalles entity:
  For
    YEAR_MONTH - expression is in months
    DAY_MINUTE - expression is in minutes
unknown's avatar
unknown committed
156

157 158
  SYNOPSIS
    Events::reconstruct_interval_expression()
159 160 161
      buf         Preallocated String buffer to add the value to
      interval    The interval type (for instance YEAR_MONTH)
      expression  The value in the lowest entity
unknown's avatar
unknown committed
162

unknown's avatar
unknown committed
163
  RETURN VALUE
164 165
    0  OK
    1  Error
166 167 168
*/

int
unknown's avatar
unknown committed
169 170
Events::reconstruct_interval_expression(String *buf, interval_type interval,
                                        longlong expression)
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
{
  ulonglong expr= expression;
  char tmp_buff[128], *end;
  bool close_quote= TRUE;
  int multipl= 0;
  char separator=':';

  switch (interval) {
  case INTERVAL_YEAR_MONTH:
    multipl= 12;
    separator= '-';
    goto common_1_lev_code;
  case INTERVAL_DAY_HOUR:
    multipl= 24;
    separator= ' ';
    goto common_1_lev_code;
  case INTERVAL_HOUR_MINUTE:
  case INTERVAL_MINUTE_SECOND:
unknown's avatar
unknown committed
189
    multipl= 60;
190 191 192 193 194 195 196 197
common_1_lev_code:
    buf->append('\'');
    end= longlong10_to_str(expression/multipl, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));
    expr= expr - (expr/multipl)*multipl;
    break;
  case INTERVAL_DAY_MINUTE:
  {
198
    ulonglong tmp_expr= expr;
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

    tmp_expr/=(24*60);
    buf->append('\'');
    end= longlong10_to_str(tmp_expr, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// days
    buf->append(' ');

    tmp_expr= expr - tmp_expr*(24*60);//minutes left
    end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// hours

    expr= tmp_expr - (tmp_expr/60)*60;
    /* the code after the switch will finish */
  }
    break;
  case INTERVAL_HOUR_SECOND:
  {
216
    ulonglong tmp_expr= expr;
217 218 219 220 221 222 223 224 225 226 227 228 229

    buf->append('\'');
    end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
    buf->append(':');

    tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
    end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes

    expr= tmp_expr - (tmp_expr/60)*60;
    /* the code after the switch will finish */
  }
unknown's avatar
unknown committed
230
    break;
231 232
  case INTERVAL_DAY_SECOND:
  {
233
    ulonglong tmp_expr= expr;
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

    tmp_expr/=(24*3600);
    buf->append('\'');
    end= longlong10_to_str(tmp_expr, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// days
    buf->append(' ');

    tmp_expr= expr - tmp_expr*(24*3600);//seconds left
    end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
    buf->append(':');

    tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
    end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
    buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes

    expr= tmp_expr - (tmp_expr/60)*60;
    /* the code after the switch will finish */
  }
253
    break;
254 255 256 257
  case INTERVAL_DAY_MICROSECOND:
  case INTERVAL_HOUR_MICROSECOND:
  case INTERVAL_MINUTE_MICROSECOND:
  case INTERVAL_SECOND_MICROSECOND:
258
  case INTERVAL_MICROSECOND:
unknown's avatar
unknown committed
259
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    return 1;
    break;
  case INTERVAL_QUARTER:
    expr/= 3;
    close_quote= FALSE;
    break;
  case INTERVAL_WEEK:
    expr/= 7;
  default:
    close_quote= FALSE;
    break;
  }
  if (close_quote)
    buf->append(separator);
  end= longlong10_to_str(expr, tmp_buff, 10);
  buf->append(tmp_buff, (uint) (end- tmp_buff));
  if (close_quote)
    buf->append('\'');
unknown's avatar
unknown committed
278

279 280 281
  return 0;
}

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
/*
  Constructor of Events class. It's called when events.o
  is loaded. Assigning addressed of static variables in this
  object file.

  SYNOPSIS
    Events::Events()
*/

Events::Events()
{
  scheduler=     &events_event_scheduler;
  event_queue=   &events_event_queue;
  db_repository= &events_event_db_repository;
}

298

unknown's avatar
unknown committed
299
/*
300
  Opens mysql.event table with specified lock
unknown's avatar
unknown committed
301 302

  SYNOPSIS
303
    Events::open_event_table()
304 305 306
    thd         Thread context
    lock_type   How to lock the table
    table       We will store the open table here
307

308
  RETURN VALUE
unknown's avatar
unknown committed
309
    1   Cannot lock table
unknown's avatar
unknown committed
310
    2   The table is corrupted - different number of fields
unknown's avatar
unknown committed
311
    0   OK
unknown's avatar
unknown committed
312 313
*/

unknown's avatar
unknown committed
314
int
315
Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
316
                         TABLE **table)
unknown's avatar
unknown committed
317
{
unknown's avatar
unknown committed
318
  return db_repository->open_event_table(thd, lock_type, table);
319 320 321
}


unknown's avatar
unknown committed
322
/*
323
  The function exported to the world for creating of events.
324

325 326
  SYNOPSIS
    Events::create_event()
327
      thd            [in]  THD
328
      parse_data     [in]  Event's data from parsing stage
329
      if_not_exists  [in]  Whether IF NOT EXISTS was specified in the DDL
330

331
  RETURN VALUE
332 333
    FALSE  OK
    TRUE   Error (Reported)
334

335
  NOTES
336 337
    In case there is an event with the same name (db) and 
    IF NOT EXISTS is specified, an warning is put into the stack.
338 339
*/

340
bool
341
Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
342
{
343 344
  int ret;
  DBUG_ENTER("Events::create_event");
345 346 347 348 349
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }
350 351 352

  pthread_mutex_lock(&LOCK_event_metadata);
  /* On error conditions my_error() is called so no need to handle here */
353
  if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
354
  {
355 356 357 358 359 360
    if ((ret= event_queue->create_event(thd, parse_data->dbname,
                                        parse_data->name)))
    {
      DBUG_ASSERT(ret == OP_LOAD_ERROR);
      my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
    }
361
  }
362
  pthread_mutex_unlock(&LOCK_event_metadata);
unknown's avatar
unknown committed
363

364 365 366 367 368
  DBUG_RETURN(ret);
}


/*
369 370 371 372
  The function exported to the world for alteration of events.

  SYNOPSIS
    Events::update_event()
373
      thd           [in]  THD
374
      parse_data    [in]  Event's data from parsing stage
375
      rename_to     [in]  Set in case of RENAME TO.
376 377

  RETURN VALUE
378 379
    FALSE  OK
    TRUE   Error
380 381 382

  NOTES
    et contains data about dbname and event name. 
383 384
    new_name is the new name of the event, if not null this means
    that RENAME TO was specified in the query
385 386
*/

387
bool
388
Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
389
{
390
  int ret;
391
  DBUG_ENTER("Events::update_event");
392 393 394 395 396 397 398
  LEX_STRING *new_dbname= rename_to ? &rename_to->m_db : NULL;
  LEX_STRING *new_name= rename_to ? &rename_to->m_name : NULL;
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }
399 400 401

  pthread_mutex_lock(&LOCK_event_metadata);
  /* On error conditions my_error() is called so no need to handle here */
402
  if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name)))
403
  {
404 405
    if ((ret= event_queue->update_event(thd, parse_data->dbname,
                                        parse_data->name, new_dbname, new_name)))
406 407 408 409
    {
      DBUG_ASSERT(ret == OP_LOAD_ERROR);
      my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
    }
410
  }
411 412
  pthread_mutex_unlock(&LOCK_event_metadata);

413 414 415 416
  DBUG_RETURN(ret);
}


417
/*
418 419 420 421
  Drops an event

  SYNOPSIS
    Events::drop_event()
422 423 424 425 426 427 428 429 430
      thd             [in]  THD
      dbname          [in]  Event's schema
      name            [in]  Event's name
      if_exists       [in]  When set and the event does not exist =>
                            warning onto the stack
      only_from_disk  [in]  Whether to remove the event from the queue too.
                            In case of Event_job_data::drop() it's needed to
                            do only disk drop because Event_queue will handle
                            removal from memory queue.
431 432

  RETURN VALUE
433 434
    FALSE  OK
    TRUE   Error (reported)
435 436
*/

437
bool
438
Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
439
                   bool only_from_disk)
440
{
441 442
  int ret;
  DBUG_ENTER("Events::drop_event");
443 444 445 446 447
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
448

449 450
  pthread_mutex_lock(&LOCK_event_metadata);
  /* On error conditions my_error() is called so no need to handle here */
451
  if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
452
  {
453 454
    if (!only_from_disk)
      event_queue->drop_event(thd, dbname, name);
455
  }
456 457 458 459 460 461 462 463 464 465 466 467 468 469
  pthread_mutex_unlock(&LOCK_event_metadata);
  DBUG_RETURN(ret);
}


/*
  Drops all events from a schema

  SYNOPSIS
    Events::drop_schema_events()
      thd  Thread
      db   ASCIIZ schema name
*/

470
void
471 472
Events::drop_schema_events(THD *thd, char *db)
{
473
  LEX_STRING const db_lex= { db, strlen(db) };
474
  
475
  DBUG_ENTER("Events::drop_schema_events");  
476
  DBUG_PRINT("enter", ("dropping events from %s", db));
477 478 479 480 481
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_VOID_RETURN;
  }
482 483 484

  pthread_mutex_lock(&LOCK_event_metadata);
  event_queue->drop_schema_events(thd, db_lex);
485
  db_repository->drop_schema_events(thd, db_lex);
486 487
  pthread_mutex_unlock(&LOCK_event_metadata);

488
  DBUG_VOID_RETURN;
489 490
}

491 492

/*
493
  SHOW CREATE EVENT
494

495 496
  SYNOPSIS
    Events::show_create_event()
497 498
      thd   Thread context
      spn   The name of the event (db, name)
unknown's avatar
unknown committed
499

500
  RETURN VALUE
501 502
    FALSE  OK
    TRUE   Error during writing to the wire
503 504
*/

505
bool
506
Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
507
{
508
  CHARSET_INFO *scs= system_charset_info;
509
  int ret;
510
  Event_timed *et= new Event_timed();
511

512
  DBUG_ENTER("Events::show_create_event");
513
  DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
514 515 516 517 518
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }
519

520
  ret= db_repository->load_named_event(thd, dbname, name, et);
521

522
  if (!ret)
unknown's avatar
unknown committed
523
  {
524
    Protocol *protocol= thd->protocol;
525 526
    char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
    String show_str(show_str_buf, sizeof(show_str_buf), scs);
527
    List<Item> field_list;
528
    byte *sql_mode_str;
529
    ulong sql_mode_len=0;
unknown's avatar
unknown committed
530

531
    show_str.length(0);
532
    show_str.set_charset(system_charset_info);
533 534

    if (et->get_create_event(thd, &show_str))
535
      goto err;
536 537 538 539 540 541 542 543 544

    field_list.push_back(new Item_empty_string("Event", NAME_LEN));

    sql_mode_str=
      sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
                                                         &sql_mode_len);

    field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));

545 546
    field_list.push_back(new Item_empty_string("Create Event",
                                               show_str.length()));
547

548 549
    if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
                                           Protocol::SEND_EOF))
550 551
      goto err;

552
    protocol->prepare_for_resend();
553
    protocol->store(et->name.str, et->name.length, scs);
554

555
    protocol->store((char*) sql_mode_str, sql_mode_len, scs);
556

557
    protocol->store(show_str.c_ptr(), show_str.length(), scs);
558 559 560
    ret= protocol->write();
    send_eof(thd);
  }
561
  delete et;
562
  DBUG_RETURN(ret);
563 564
err:
  delete et;
565
  DBUG_RETURN(TRUE);
566
}
567

568 569

/*
570 571
  Proxy for Event_db_repository::fill_schema_events.
  Callback for I_S from sql_show.cc
unknown's avatar
unknown committed
572

573
  SYNOPSIS
574
    Events::fill_schema_events()
575
      thd     Thread context
576 577
      tables  The schema table
      cond    Unused
578 579

  RETURN VALUE
580 581
    0  OK
    !0 Error
582 583 584
*/

int
585
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
586
{
587 588
  char *db= NULL;
  DBUG_ENTER("Events::fill_schema_events");
589 590 591 592 593 594 595
  Events *myself= get_instance();
  if (unlikely(myself->check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }

596 597 598 599 600 601 602 603 604 605 606 607
  /*
    If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
    be NULL. Let's do an assert anyway.
  */
  if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
  {
    DBUG_ASSERT(thd->lex->select_lex.db);
    if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
                     is_schema_db(thd->lex->select_lex.db)))
      DBUG_RETURN(1);
    db= thd->lex->select_lex.db;
  }
608
  DBUG_RETURN(myself->db_repository->fill_schema_events(thd, tables, db));
609
}
610 611


612 613 614 615 616 617 618 619 620 621
/*
  Inits the scheduler's structures.

  SYNOPSIS
    Events::init()

  NOTES
    This function is not synchronized.

  RETURN VALUE
622 623
    FALSE  OK
    TRUE   Error in case the scheduler can't start
624 625
*/

unknown's avatar
unknown committed
626
bool
627 628
Events::init()
{
629 630
  THD *thd;
  bool res= FALSE;
631
  DBUG_ENTER("Events::init");
632

633 634 635
  if (opt_event_scheduler == Events::EVENTS_DISABLED)
    DBUG_RETURN(FALSE);

636 637
  /* We need a temporary THD during boot */
  if (!(thd= new THD()))
unknown's avatar
unknown committed
638
  {
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    res= TRUE;
    goto end;
  }
  /*
    The thread stack does not start from this function but we cannot
    guess the real value. So better some value that doesn't assert than
    no value.
  */
  thd->thread_stack= (char*) &thd;
  thd->store_globals();

  if (check_system_tables(thd))
  {
    check_system_tables_error= TRUE;
    sql_print_error("SCHEDULER: The system tables are damaged. "
                    "The scheduler subsystem will be unusable during this run.");
    goto end;
  }
  check_system_tables_error= FALSE;

659
  if (event_queue->init_queue(thd, db_repository))
660 661 662
  {
    sql_print_error("SCHEDULER: Error while loading from disk.");
    goto end;
unknown's avatar
unknown committed
663
  }
664
  scheduler->init_scheduler(event_queue);
665

666 667 668 669
  DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
              opt_event_scheduler == Events::EVENTS_OFF);
  if (opt_event_scheduler == Events::EVENTS_ON)
    res= scheduler->start();
670

671 672 673 674 675 676
end:
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  NULL);

  DBUG_RETURN(res);
677 678 679 680 681 682 683
}


/*
  Cleans up scheduler's resources. Called at server shutdown.

  SYNOPSIS
unknown's avatar
unknown committed
684
    Events::deinit()
685 686 687 688 689 690

  NOTES
    This function is not synchronized.
*/

void
unknown's avatar
unknown committed
691
Events::deinit()
692
{
unknown's avatar
unknown committed
693
  DBUG_ENTER("Events::deinit");
unknown's avatar
unknown committed
694
  if (likely(!check_system_tables_error))
695 696 697
  {
    scheduler->stop();
    scheduler->deinit_scheduler();
698

699 700
    event_queue->deinit_queue();
  }
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716

  DBUG_VOID_RETURN;
}


/*
  Inits Events mutexes

  SYNOPSIS
    Events::init_mutexes()
      thd  Thread
*/

void
Events::init_mutexes()
{
717
  pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
718
  event_queue->init_mutexes();
719
  scheduler->init_mutexes();
720 721 722 723 724 725 726 727 728 729 730 731 732
}


/*
  Destroys Events mutexes

  SYNOPSIS
    Events::destroy_mutexes()
*/

void
Events::destroy_mutexes()
{
733
  event_queue->deinit_mutexes();
734
  scheduler->deinit_mutexes();
735
  pthread_mutex_destroy(&LOCK_event_metadata);
unknown's avatar
unknown committed
736 737 738
}


739
/*
740 741 742 743
  Dumps the internal status of the scheduler and the memory cache
  into a table with two columns - Name & Value. Different properties
  which could be useful for debugging for instance deadlocks are
  returned.
744 745 746 747

  SYNOPSIS
    Events::dump_internal_status()
      thd  Thread
748

749
  RETURN VALUE
750 751
    FALSE  OK
    TRUE   Error
752 753
*/

754
bool
755 756
Events::dump_internal_status(THD *thd)
{
757 758 759 760 761 762 763 764 765 766
  DBUG_ENTER("Events::dump_internal_status");
  Protocol *protocol= thd->protocol;
  List<Item> field_list;

  field_list.push_back(new Item_empty_string("Name", 30));
  field_list.push_back(new Item_empty_string("Value",20));
  if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
                                         Protocol::SEND_EOF))
    DBUG_RETURN(TRUE);

767
  if (scheduler->dump_internal_status(thd) ||
768 769 770 771 772
      event_queue->dump_internal_status(thd))
    DBUG_RETURN(TRUE);

  send_eof(thd);
  DBUG_RETURN(FALSE);
773 774 775
}


unknown's avatar
unknown committed
776
/*
777
  Starts execution of events by the scheduler
unknown's avatar
unknown committed
778 779

  SYNOPSIS
780
    Events::start_execution_of_events()
unknown's avatar
unknown committed
781 782

  RETURN VALUE
783 784
    FALSE  OK
    TRUE   Error
unknown's avatar
unknown committed
785 786
*/

787 788 789 790
bool
Events::start_execution_of_events()
{
  DBUG_ENTER("Events::start_execution_of_events");
791 792 793 794 795
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }
796
  DBUG_RETURN(scheduler->start());
797 798 799
}


800 801 802 803 804 805 806 807 808 809 810 811 812
/*
  Stops execution of events by the scheduler.
  Already running events will not be stopped. If the user needs
  them stopped manual intervention is needed.

  SYNOPSIS
    Events::stop_execution_of_events()

  RETURN VALUE
    FALSE  OK
    TRUE   Error
*/

813 814 815 816
bool
Events::stop_execution_of_events()
{
  DBUG_ENTER("Events::stop_execution_of_events");
817 818 819 820 821
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(TRUE);
  }
822
  DBUG_RETURN(scheduler->stop());
823 824
}

825 826 827 828 829 830 831 832

/*
  Checks whether the scheduler is running or not.

  SYNOPSIS
    Events::is_started()

  RETURN VALUE
833 834
    TRUE   Yes
    FALSE  No
835 836
*/

837
bool
838
Events::is_execution_of_events_started()
839
{
840 841 842 843 844 845
  DBUG_ENTER("Events::is_execution_of_events_started");
  if (unlikely(check_system_tables_error))
  {
    my_error(ER_EVENTS_DB_ERROR, MYF(0));
    DBUG_RETURN(FALSE);
  }
846
  DBUG_RETURN(scheduler->is_running());
847
}
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920



/*
  Opens mysql.db and mysql.user and checks whether:
    1. mysql.db has column Event_priv at column 20 (0 based);
    2. mysql.user has column Event_priv at column 29 (0 based);

  SYNOPSIS
    Events::check_system_tables()
      thd  Thread

  RETURN VALUE
    FALSE  OK
    TRUE   Error
*/

bool
Events::check_system_tables(THD *thd)
{
  TABLE_LIST tables;
  bool not_used;
  Open_tables_state backup;
  bool ret= FALSE;

  DBUG_ENTER("Events::check_system_tables");
  DBUG_PRINT("enter", ("thd=0x%lx", thd));

  thd->reset_n_backup_open_tables_state(&backup);

  bzero((char*) &tables, sizeof(tables));
  tables.db= (char*) "mysql";
  tables.table_name= tables.alias= (char*) "db";
  tables.lock_type= TL_READ;

  if ((ret= simple_open_n_lock_tables(thd, &tables)))
  {
    sql_print_error("SCHEDULER: Cannot open mysql.db");
    ret= TRUE;
  }
  ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
                          mysql_db_table_fields, &mysql_db_table_last_check,
                          ER_CANNOT_LOAD_FROM_TABLE);
  close_thread_tables(thd);

  bzero((char*) &tables, sizeof(tables));
  tables.db= (char*) "mysql";
  tables.table_name= tables.alias= (char*) "user";
  tables.lock_type= TL_READ;

  if (simple_open_n_lock_tables(thd, &tables))
  {
    sql_print_error("SCHEDULER: Cannot open mysql.user");
    ret= TRUE;
  }
  else
  {
    if (tables.table->s->fields < 29 ||
        strncmp(tables.table->field[29]->field_name,
                STRING_WITH_LEN("Event_priv")))
    {
      sql_print_error("mysql.user has no `Event_priv` column at position %d",
                      29);
      ret= TRUE;
    }
    close_thread_tables(thd);
  }

end:
  thd->restore_backup_open_tables_state(&backup);

  DBUG_RETURN(ret);
}