1
/* Copyright (C) 2004-2006 MySQL AB
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.
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.
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 */
16
#include "mysql_priv.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
25
@addtogroup Event_Scheduler
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.
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
55
1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
56
2. DROP EVENT the_name
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.
67
Keep the order of the first to as in var_typelib
68
sys_var_event_scheduler::value_ptr() references this array. Keep in
71
static const char *opt_event_scheduler_state_names[]=
72
{ "OFF", "ON", "0", "1", "DISABLED", NullS };
74
const TYPELIB Events::opt_typelib=
76
array_elements(opt_event_scheduler_state_names)-1,
78
opt_event_scheduler_state_names,
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!
88
static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };
90
const TYPELIB Events::var_typelib=
92
array_elements(var_event_scheduler_state_names)-1,
94
var_event_scheduler_state_names,
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;
108
Compares 2 LEX strings regarding case.
122
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
124
return cs->coll->strnncollsp(cs, (uchar *) s.str,s.length,
125
(uchar *) t.str,t.length, 0);
130
@brief Initialize the start up option of the Events scheduler.
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.
137
@param[in,out] argument The value of the argument. If this value
138
is found in the typelib, the argument is
141
@retval TRUE unknown option value
142
@retval FALSE success
146
Events::set_opt_event_scheduler(char *argument)
148
if (argument == NULL)
149
opt_event_scheduler= Events::EVENTS_ON;
155
(OFF | ON) - (0 | 1) (DISABLE )
157
const static enum enum_opt_event_scheduler type2state[]=
158
{ EVENTS_OFF, EVENTS_ON, EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
160
type= find_type(argument, &opt_typelib, 1);
162
DBUG_ASSERT(type >= 0 && type <= 5); /* guaranteed by find_type */
166
fprintf(stderr, "Unknown option to event-scheduler: %s\n", argument);
169
opt_event_scheduler= type2state[type-1];
176
Return a string representation of the current scheduler mode.
180
Events::get_opt_event_scheduler_str()
184
pthread_mutex_lock(&LOCK_event_metadata);
185
str= opt_typelib.type_names[(int) opt_event_scheduler];
186
pthread_mutex_unlock(&LOCK_event_metadata);
193
Push an error into the error stack if the system tables are
197
bool Events::check_if_system_tables_error()
199
DBUG_ENTER("Events::check_if_system_tables_error");
201
if (check_system_tables_error)
203
my_error(ER_EVENTS_DB_ERROR, MYF(0));
212
Reconstructs interval expression from interval type and expression
213
value that is in form of a value of the smalles entity:
215
YEAR_MONTH - expression is in months
216
DAY_MINUTE - expression is in minutes
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
230
Events::reconstruct_interval_expression(String *buf, interval_type interval,
233
ulonglong expr= expression;
234
char tmp_buff[128], *end;
235
bool close_quote= TRUE;
240
case INTERVAL_YEAR_MONTH:
243
goto common_1_lev_code;
244
case INTERVAL_DAY_HOUR:
247
goto common_1_lev_code;
248
case INTERVAL_HOUR_MINUTE:
249
case INTERVAL_MINUTE_SECOND:
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;
257
case INTERVAL_DAY_MINUTE:
259
ulonglong tmp_expr= expr;
263
end= longlong10_to_str(tmp_expr, tmp_buff, 10);
264
buf->append(tmp_buff, (uint) (end- tmp_buff));// days
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
271
expr= tmp_expr - (tmp_expr/60)*60;
272
/* the code after the switch will finish */
275
case INTERVAL_HOUR_SECOND:
277
ulonglong tmp_expr= expr;
280
end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
281
buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
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
288
expr= tmp_expr - (tmp_expr/60)*60;
289
/* the code after the switch will finish */
292
case INTERVAL_DAY_SECOND:
294
ulonglong tmp_expr= expr;
298
end= longlong10_to_str(tmp_expr, tmp_buff, 10);
299
buf->append(tmp_buff, (uint) (end- tmp_buff));// days
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
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
311
expr= tmp_expr - (tmp_expr/60)*60;
312
/* the code after the switch will finish */
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");
323
case INTERVAL_QUARTER:
334
buf->append(separator);
335
end= longlong10_to_str(expr, tmp_buff, 10);
336
buf->append(tmp_buff, (uint) (end- tmp_buff));
345
Create a new query string for removing executable comments
346
for avoiding leak and keeping consistency of the execution
349
@param[in] thd Thread handler
350
@param[in] buf Query string
357
create_query_string(THD *thd, String *buf)
359
/* Append the "CREATE" part of the query */
360
if (buf->append(STRING_WITH_LEN("CREATE ")))
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))
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
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
384
@retval TRUE Error (reported)
388
Events::create_event(THD *thd, Event_parse_data *parse_data,
392
bool save_binlog_row_based;
393
DBUG_ENTER("Events::create_event");
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.
401
if (end_active_trans(thd))
404
if (check_if_system_tables_error())
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.
412
if (parse_data->check_parse_data(thd))
415
/* At create, one of them must be set */
416
DBUG_ASSERT(parse_data->expression || parse_data->execute_at);
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)))
423
if (check_db_dir_existence(parse_data->dbname.str))
425
my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
429
if (parse_data->do_not_create)
432
Turn off row binlogging of this statement and use statement-based
433
so that all supporting tables are updated for CREATE EVENT command.
435
save_binlog_row_based= thd->current_stmt_binlog_row_based;
436
thd->clear_current_stmt_binlog_row_based();
438
pthread_mutex_lock(&LOCK_event_metadata);
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)))
443
Event_queue_element *new_element;
446
if (!(new_element= new Event_queue_element()))
448
else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
452
if (!db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
459
/* TODO: do not ignore the out parameter and a possible OOM error! */
462
event_queue->create_event(thd, new_element, &created);
465
binlog the create event unless it's been successfully dropped
469
/* Binlog the create event. */
470
DBUG_ASSERT(thd->query() && thd->query_length());
472
if (create_query_string(thd, &log_query))
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;
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());
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;
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
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.
509
@retval TRUE error (reported)
513
Events::update_event(THD *thd, Event_parse_data *parse_data,
514
LEX_STRING *new_dbname, LEX_STRING *new_name)
517
bool save_binlog_row_based;
518
Event_queue_element *new_element;
520
DBUG_ENTER("Events::update_event");
523
For consistency, implicit COMMIT should be the first thing in the
526
if (end_active_trans(thd))
529
if (check_if_system_tables_error())
532
if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
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)))
540
if (new_dbname) /* It's a rename */
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))
548
my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
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
558
if (check_access(thd, EVENT_ACL, new_dbname->str, 0, 0, 0,
559
is_schema_db(new_dbname->str, new_dbname->length)))
562
/* Check that the target database exists */
563
if (check_db_dir_existence(new_dbname->str))
565
my_error(ER_BAD_DB_ERROR, MYF(0), new_dbname->str);
571
Turn off row binlogging of this statement and use statement-based
572
so that all supporting tables are updated for UPDATE EVENT command.
574
save_binlog_row_based= thd->current_stmt_binlog_row_based;
575
thd->clear_current_stmt_binlog_row_based();
577
pthread_mutex_lock(&LOCK_event_metadata);
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)))
583
LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
584
LEX_STRING name= new_name ? *new_name : parse_data->name;
586
if (!(new_element= new Event_queue_element()))
588
else if ((ret= db_repository->load_named_event(thd, dbname, name,
591
DBUG_ASSERT(ret == OP_LOAD_ERROR);
597
TODO: check if an update actually has inserted an entry
599
If not, and the element is ON COMPLETION NOT PRESERVE, delete
603
event_queue->update_event(thd, parse_data->dbname, parse_data->name,
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());
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;
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.
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
639
@retval TRUE Error (reported)
643
Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
646
bool save_binlog_row_based;
647
DBUG_ENTER("Events::drop_event");
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
660
if (end_active_trans(thd))
663
if (check_if_system_tables_error())
666
if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
667
is_schema_db(dbname.str, dbname.length)))
671
Turn off row binlogging of this statement and use statement-based so
672
that all supporting tables are updated for DROP EVENT command.
674
save_binlog_row_based= thd->current_stmt_binlog_row_based;
675
thd->clear_current_stmt_binlog_row_based();
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)))
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());
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;
695
Drops all events from a schema
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.
701
@param[in,out] thd Thread
702
@param[in] db ASCIIZ schema name
706
Events::drop_schema_events(THD *thd, char *db)
708
LEX_STRING const db_lex= { db, strlen(db) };
710
DBUG_ENTER("Events::drop_schema_events");
711
DBUG_PRINT("enter", ("dropping events from %s", db));
714
sic: no check if the scheduler is disabled or system tables
715
are damaged, as intended.
718
pthread_mutex_lock(&LOCK_event_metadata);
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);
729
A helper function to generate SHOW CREATE EVENT output from
734
send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
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;
740
const String *tz_name;
742
DBUG_ENTER("send_show_create_event");
745
if (et->get_create_event(thd, &show_str))
748
field_list.push_back(new Item_empty_string("Event", NAME_CHAR_LEN));
750
if (sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
754
field_list.push_back(new Item_empty_string("sql_mode", (uint) sql_mode.length));
756
tz_name= et->time_zone->get_name();
758
field_list.push_back(new Item_empty_string("time_zone",
761
field_list.push_back(new Item_empty_string("Create Event",
764
field_list.push_back(
765
new Item_empty_string("character_set_client", MY_CS_NAME_SIZE));
767
field_list.push_back(
768
new Item_empty_string("collation_connection", MY_CS_NAME_SIZE));
770
field_list.push_back(
771
new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
773
if (protocol->send_fields(&field_list,
774
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
777
protocol->prepare_for_resend();
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);
794
if (protocol->write())
804
Implement SHOW CREATE EVENT statement
807
spn The name of the event (db, name)
810
@retval TRUE error (reported)
814
Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
816
Open_tables_state open_tables_backup;
820
DBUG_ENTER("Events::show_create_event");
821
DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
823
if (check_if_system_tables_error())
826
if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
827
is_schema_db(dbname.str, dbname.length)))
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'
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);
845
ret= send_show_create_event(thd, &et, thd->protocol);
852
Check access rights and fill INFORMATION_SCHEMA.events table.
854
@param[in,out] thd Thread context
855
@param[in] tables The temporary table to fill.
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
862
@return Has to be integer, as such is the requirement of the I_S API
864
@retval 1 an error, pushed into the error stack
868
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
872
Open_tables_state open_tables_backup;
873
DBUG_ENTER("Events::fill_schema_events");
875
if (check_if_system_tables_error())
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.
882
if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
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))
888
db= thd->lex->select_lex.db;
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.
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);
905
Initializes the scheduler's structures.
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.
911
@note This function is not synchronized.
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()).
920
Events::init(my_bool opt_noacl_or_bootstrap)
926
DBUG_ENTER("Events::init");
928
/* We need a temporary THD during boot */
929
if (!(thd= new THD()))
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
939
thd->thread_stack= (char*) &thd;
940
thd->store_globals();
944
We will need Event_db_repository anyway, even if the scheduler is
945
disabled - to perform events DDL.
947
if (!(db_repository= new Event_db_repository))
949
res= TRUE; /* fatal error: request unireg_abort */
954
Since we allow event DDL even if the scheduler is disabled,
955
check the system tables, as we might need them.
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
962
if (opt_noacl_or_bootstrap || Event_db_repository::check_system_tables(thd))
964
if (! opt_noacl_or_bootstrap)
966
sql_print_error("Event Scheduler: An error occurred when initializing "
967
"system tables. Disabling the Event Scheduler.");
968
check_system_tables_error= TRUE;
971
/* Disable the scheduler since the system tables are not up to date */
972
opt_event_scheduler= EVENTS_DISABLED;
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
981
if (opt_event_scheduler == Events::EVENTS_DISABLED)
985
DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
986
opt_event_scheduler == Events::EVENTS_OFF);
988
if (!(event_queue= new Event_queue) ||
989
!(scheduler= new Event_scheduler(event_queue)))
991
res= TRUE; /* fatal error: request unireg_abort */
995
if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
996
(opt_event_scheduler == EVENTS_ON && scheduler->start()))
998
sql_print_error("Event Scheduler: Error while loading from disk.");
999
res= TRUE; /* fatal error: request unireg_abort */
1002
Event_worker_thread::init(db_repository);
1007
delete db_repository;
1012
/* Remember that we don't have a THD */
1013
my_pthread_setspecific_ptr(THR_THD, NULL);
1019
Cleans up scheduler's resources. Called at server shutdown.
1025
This function is not synchronized.
1031
DBUG_ENTER("Events::deinit");
1033
if (opt_event_scheduler != EVENTS_DISABLED)
1036
scheduler= NULL; /* safety */
1038
event_queue= NULL; /* safety */
1041
delete db_repository;
1042
db_repository= NULL; /* safety */
1049
Inits Events mutexes
1052
Events::init_mutexes()
1057
Events::init_mutexes()
1059
pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
1064
Destroys Events mutexes
1067
Events::destroy_mutexes()
1071
Events::destroy_mutexes()
1073
pthread_mutex_destroy(&LOCK_event_metadata);
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
1084
Events::dump_internal_status()
1088
Events::dump_internal_status()
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");
1095
pthread_mutex_lock(&LOCK_event_metadata);
1096
if (opt_event_scheduler == EVENTS_DISABLED)
1097
puts("The Event Scheduler is disabled");
1100
scheduler->dump_internal_status();
1101
event_queue->dump_internal_status();
1104
pthread_mutex_unlock(&LOCK_event_metadata);
1110
Starts or stops the event scheduler thread.
1112
@retval FALSE success
1117
Events::switch_event_scheduler_state(enum_opt_event_scheduler new_state)
1121
DBUG_ENTER("Events::switch_event_scheduler_state");
1123
DBUG_ASSERT(new_state == Events::EVENTS_ON ||
1124
new_state == Events::EVENTS_OFF);
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
1131
if (check_if_system_tables_error())
1134
pthread_mutex_lock(&LOCK_event_metadata);
1136
if (opt_event_scheduler == EVENTS_DISABLED)
1138
my_error(ER_OPTION_PREVENTS_STATEMENT,
1139
MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables");
1144
if (new_state == EVENTS_ON)
1145
ret= scheduler->start();
1147
ret= scheduler->stop();
1151
my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
1155
opt_event_scheduler= new_state;
1158
pthread_mutex_unlock(&LOCK_event_metadata);
1164
Loads all ENABLED events from mysql.event into a prioritized
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.
1173
@param[in,out] thd Thread context. Used for memory allocation in some cases.
1175
@retval FALSE success
1176
@retval TRUE error, the load is aborted
1178
@note Reports the error to the console
1182
Events::load_events_from_db(THD *thd)
1185
READ_RECORD read_record_info;
1188
ulong saved_master_access;
1190
DBUG_ENTER("Events::load_events_from_db");
1191
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
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.
1199
saved_master_access= thd->security_ctx->master_access;
1200
thd->security_ctx->master_access |= SUPER_ACL;
1202
ret= db_repository->open_event_table(thd, TL_WRITE, &table);
1204
thd->security_ctx->master_access= saved_master_access;
1208
sql_print_error("Event Scheduler: Failed to open table mysql.event");
1212
init_read_record(&read_record_info, thd, table, NULL, 0, 1, FALSE);
1213
while (!(read_record_info.read_record(&read_record_info)))
1215
Event_queue_element *et;
1217
bool drop_on_completion;
1219
if (!(et= new Event_queue_element))
1222
DBUG_PRINT("info", ("Loading event from row."));
1224
if (et->load_from_row(thd, table))
1226
sql_print_error("Event Scheduler: "
1227
"Error while loading events from mysql.event. "
1228
"The table probably contains bad data or is corrupted");
1232
drop_on_completion= (et->on_completion ==
1233
Event_parse_data::ON_COMPLETION_DROP);
1236
if (event_queue->create_event(thd, et, &created))
1244
else if (drop_on_completion)
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.
1255
int rc= table->file->ha_delete_row(table->record[0]);
1258
table->file->print_error(rc, MYF(0));
1263
sql_print_information("Event Scheduler: Loaded %d event%s",
1264
count, (count == 1) ? "" : "s");
1268
end_read_record(&read_record_info);
1270
close_thread_tables(thd);
1276
@} (End of group Event_Scheduler)