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