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