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-1335 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
80 Event_queue *Events::event_queue;
81 Event_scheduler *Events::scheduler;
82 Event_db_repository *Events::db_repository;
83 ulong Events::opt_event_scheduler= Events::EVENTS_OFF;
84 ulong Events::startup_state= Events::EVENTS_OFF;
85 ulong 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
sortcmp_lex_string(const LEX_CSTRING * s,const LEX_CSTRING * t,const CHARSET_INFO * cs)103 int 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
check_if_system_tables_error()116 bool 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 smallest 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
148 int
reconstruct_interval_expression(String * buf,interval_type interval,longlong expression)149 Events::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;
170 common_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 */
276 static int
create_query_string(THD * thd,String * buf)277 create_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
315 bool
create_event(THD * thd,Event_parse_data * parse_data)316 Events::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 if (!ret && Events::opt_event_scheduler == Events::EVENTS_OFF)
422 {
423 push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
424 "Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.");
425 }
426
427 DBUG_RETURN(ret);
428
429 WSREP_ERROR_LABEL:
430 DBUG_RETURN(TRUE);
431
432 }
433
434
435 /**
436 Alter an event.
437
438 @param[in,out] thd THD
439 @param[in] parse_data Event's data from parsing stage
440 @param[in] new_dbname A new schema name for the event. Set in the case of
441 ALTER EVENT RENAME, otherwise is NULL.
442 @param[in] new_name A new name for the event. Set in the case of
443 ALTER EVENT RENAME
444
445 Parameter 'et' contains data about dbname and event name.
446 Parameter 'new_name' is the new name of the event, if not null
447 this means that RENAME TO was specified in the query
448 @sa Events::drop_event for the locking notes.
449
450 @retval FALSE OK
451 @retval TRUE error (reported)
452 */
453
454 bool
update_event(THD * thd,Event_parse_data * parse_data,LEX_CSTRING * new_dbname,LEX_CSTRING * new_name)455 Events::update_event(THD *thd, Event_parse_data *parse_data,
456 LEX_CSTRING *new_dbname, LEX_CSTRING *new_name)
457 {
458 int ret;
459 enum_binlog_format save_binlog_format;
460 Event_queue_element *new_element;
461
462 DBUG_ENTER("Events::update_event");
463
464 if (unlikely(check_if_system_tables_error()))
465 DBUG_RETURN(TRUE);
466
467 if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
468 DBUG_RETURN(TRUE);
469
470 if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0))
471 DBUG_RETURN(TRUE);
472
473 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
474
475 if (lock_object_name(thd, MDL_key::EVENT,
476 parse_data->dbname.str, parse_data->name.str))
477 DBUG_RETURN(TRUE);
478
479 if (check_db_dir_existence(parse_data->dbname.str))
480 {
481 my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
482 DBUG_RETURN(TRUE);
483 }
484
485
486 if (new_dbname) /* It's a rename */
487 {
488 /* Check that the new and the old names differ. */
489 if ( !sortcmp_lex_string(&parse_data->dbname, new_dbname,
490 system_charset_info) &&
491 !sortcmp_lex_string(&parse_data->name, new_name,
492 system_charset_info))
493 {
494 my_error(ER_EVENT_SAME_NAME, MYF(0));
495 DBUG_RETURN(TRUE);
496 }
497
498 /*
499 And the user has sufficient privileges to use the target database.
500 Do it before checking whether the database exists: we don't want
501 to tell the user that a database doesn't exist if they can not
502 access it.
503 */
504 if (check_access(thd, EVENT_ACL, new_dbname->str, NULL, NULL, 0, 0))
505 DBUG_RETURN(TRUE);
506
507 /*
508 Acquire mdl exclusive lock on target database name.
509 */
510 if (lock_object_name(thd, MDL_key::EVENT,
511 new_dbname->str, new_name->str))
512 DBUG_RETURN(TRUE);
513
514 /* Check that the target database exists */
515 if (check_db_dir_existence(new_dbname->str))
516 {
517 my_error(ER_BAD_DB_ERROR, MYF(0), new_dbname->str);
518 DBUG_RETURN(TRUE);
519 }
520 }
521
522 /*
523 Turn off row binlogging of this statement and use statement-based
524 so that all supporting tables are updated for UPDATE EVENT command.
525 */
526 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
527
528 /* On error conditions my_error() is called so no need to handle here */
529 if (!(ret= db_repository->update_event(thd, parse_data,
530 new_dbname, new_name)))
531 {
532 LEX_CSTRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
533 LEX_CSTRING name= new_name ? *new_name : parse_data->name;
534
535 if (!(new_element= new Event_queue_element()))
536 ret= TRUE; // OOM
537 else if ((ret= db_repository->load_named_event(thd, &dbname, &name,
538 new_element)))
539 delete new_element;
540 else
541 {
542 /*
543 TODO: check if an update actually has inserted an entry
544 into the queue.
545 If not, and the element is ON COMPLETION NOT PRESERVE, delete
546 it right away.
547 */
548 if (event_queue)
549 event_queue->update_event(thd, &parse_data->dbname, &parse_data->name,
550 new_element);
551 /* Binlog the alter event. */
552 DBUG_ASSERT(thd->query() && thd->query_length());
553 ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
554 }
555 }
556
557 thd->restore_stmt_binlog_format(save_binlog_format);
558 DBUG_RETURN(ret);
559
560 WSREP_ERROR_LABEL:
561 DBUG_RETURN(TRUE);
562 }
563
564
565 /**
566 Drops an event
567
568 @param[in,out] thd THD
569 @param[in] dbname Event's schema
570 @param[in] name Event's name
571 @param[in] if_exists When this is set and the event does not exist
572 a warning is pushed into the warning stack.
573 Otherwise the operation produces an error.
574
575 @note Similarly to DROP PROCEDURE, we do not allow DROP EVENT
576 under LOCK TABLES mode, unless table mysql.event is locked. To
577 ensure that, we do not reset & backup the open tables state in
578 this function - if in LOCK TABLES or pre-locking mode, this will
579 lead to an error 'Table mysql.event is not locked with LOCK
580 TABLES' unless it _is_ locked. In pre-locked mode there is
581 another barrier - DROP EVENT commits the current transaction,
582 and COMMIT/ROLLBACK is not allowed in stored functions and
583 triggers.
584
585 @retval FALSE OK
586 @retval TRUE Error (reported)
587 */
588
589 bool
drop_event(THD * thd,const LEX_CSTRING * dbname,const LEX_CSTRING * name,bool if_exists)590 Events::drop_event(THD *thd, const LEX_CSTRING *dbname,
591 const LEX_CSTRING *name, bool if_exists)
592 {
593 int ret;
594 enum_binlog_format save_binlog_format;
595 DBUG_ENTER("Events::drop_event");
596
597 if (unlikely(check_if_system_tables_error()))
598 DBUG_RETURN(TRUE);
599
600 if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0))
601 DBUG_RETURN(TRUE);
602
603 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
604
605 /*
606 Turn off row binlogging of this statement and use statement-based so
607 that all supporting tables are updated for DROP EVENT command.
608 */
609 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
610
611 if (lock_object_name(thd, MDL_key::EVENT,
612 dbname->str, name->str))
613 DBUG_RETURN(TRUE);
614 /* On error conditions my_error() is called so no need to handle here */
615 if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
616 {
617 if (event_queue)
618 event_queue->drop_event(thd, dbname, name);
619 /* Binlog the drop event. */
620 DBUG_ASSERT(thd->query() && thd->query_length());
621 ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
622 }
623
624 thd->restore_stmt_binlog_format(save_binlog_format);
625 DBUG_RETURN(ret);
626
627 WSREP_ERROR_LABEL:
628 DBUG_RETURN(TRUE);
629 }
630
631
632 /**
633 Drops all events from a schema
634
635 @note We allow to drop all events in a schema even if the
636 scheduler is disabled. This is to not produce any warnings
637 in case of DROP DATABASE and a disabled scheduler.
638
639 @param[in,out] thd Thread
640 @param[in] db ASCIIZ schema name
641 */
642
643 void
drop_schema_events(THD * thd,const char * db)644 Events::drop_schema_events(THD *thd, const char *db)
645 {
646 const LEX_CSTRING db_lex= { db, strlen(db) };
647
648 DBUG_ENTER("Events::drop_schema_events");
649 DBUG_PRINT("enter", ("dropping events from %s", db));
650
651 DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
652
653 /*
654 Sic: no check if the scheduler is disabled or system tables
655 are damaged, as intended.
656 */
657 if (event_queue)
658 event_queue->drop_schema_events(thd, &db_lex);
659 db_repository->drop_schema_events(thd, &db_lex);
660
661 DBUG_VOID_RETURN;
662 }
663
664
665 /**
666 A helper function to generate SHOW CREATE EVENT output from
667 a named event
668 */
669
670 static bool
send_show_create_event(THD * thd,Event_timed * et,Protocol * protocol)671 send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
672 {
673 char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
674 String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
675 List<Item> field_list;
676 LEX_CSTRING sql_mode;
677 const String *tz_name;
678 MEM_ROOT *mem_root= thd->mem_root;
679 DBUG_ENTER("send_show_create_event");
680
681 show_str.length(0);
682 if (et->get_create_event(thd, &show_str))
683 DBUG_RETURN(TRUE);
684
685 field_list.push_back(new (mem_root)
686 Item_empty_string(thd, "Event", NAME_CHAR_LEN),
687 mem_root);
688
689 if (sql_mode_string_representation(thd, et->sql_mode, &sql_mode))
690 DBUG_RETURN(TRUE);
691
692 field_list.push_back(new (mem_root)
693 Item_empty_string(thd, "sql_mode",
694 (uint) sql_mode.length), mem_root);
695
696 tz_name= et->time_zone->get_name();
697
698 field_list.push_back(new (mem_root)
699 Item_empty_string(thd, "time_zone", tz_name->length()),
700 mem_root);
701
702 field_list.push_back(new (mem_root)
703 Item_empty_string(thd, "Create Event",
704 show_str.length()), mem_root);
705
706 field_list.push_back(new (mem_root)
707 Item_empty_string(thd, "character_set_client",
708 MY_CS_NAME_SIZE), mem_root);
709
710 field_list.push_back(new (mem_root)
711 Item_empty_string(thd, "collation_connection",
712 MY_CS_NAME_SIZE), mem_root);
713
714 field_list.push_back(new (mem_root)
715 Item_empty_string(thd, "Database Collation",
716 MY_CS_NAME_SIZE), mem_root);
717
718 if (protocol->send_result_set_metadata(&field_list,
719 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
720 DBUG_RETURN(TRUE);
721
722 protocol->prepare_for_resend();
723
724 protocol->store(et->name.str, et->name.length, system_charset_info);
725 protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
726 protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info);
727 protocol->store(show_str.ptr(), show_str.length(),
728 et->creation_ctx->get_client_cs());
729 protocol->store(et->creation_ctx->get_client_cs()->csname,
730 strlen(et->creation_ctx->get_client_cs()->csname),
731 system_charset_info);
732 protocol->store(et->creation_ctx->get_connection_cl()->name,
733 strlen(et->creation_ctx->get_connection_cl()->name),
734 system_charset_info);
735 protocol->store(et->creation_ctx->get_db_cl()->name,
736 strlen(et->creation_ctx->get_db_cl()->name),
737 system_charset_info);
738
739 if (protocol->write())
740 DBUG_RETURN(TRUE);
741
742 my_eof(thd);
743
744 DBUG_RETURN(FALSE);
745 }
746
747
748 /**
749 Implement SHOW CREATE EVENT statement
750
751 thd Thread context
752 spn The name of the event (db, name)
753
754 @retval FALSE OK
755 @retval TRUE error (reported)
756 */
757
758 bool
show_create_event(THD * thd,const LEX_CSTRING * dbname,const LEX_CSTRING * name)759 Events::show_create_event(THD *thd, const LEX_CSTRING *dbname,
760 const LEX_CSTRING *name)
761 {
762 Event_timed et;
763 bool ret;
764
765 DBUG_ENTER("Events::show_create_event");
766 DBUG_PRINT("enter", ("name: %s@%s", dbname->str, name->str));
767
768 if (unlikely(check_if_system_tables_error()))
769 DBUG_RETURN(TRUE);
770
771 if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0))
772 DBUG_RETURN(TRUE);
773
774 /*
775 We would like to allow SHOW CREATE EVENT under LOCK TABLES and
776 in pre-locked mode. mysql.event table is marked as a system table.
777 This flag reduces the set of its participation scenarios in LOCK TABLES
778 operation, and therefore an out-of-bound open of this table
779 for reading like the one below (sic, only for reading) is
780 more or less deadlock-free. For additional information about when a
781 deadlock can occur please refer to the description of 'system table'
782 flag.
783 */
784 ret= db_repository->load_named_event(thd, dbname, name, &et);
785
786 if (!ret)
787 ret= send_show_create_event(thd, &et, thd->protocol);
788
789 DBUG_RETURN(ret);
790 }
791
792
793 /**
794 Check access rights and fill INFORMATION_SCHEMA.events table.
795
796 @param[in,out] thd Thread context
797 @param[in] tables The temporary table to fill.
798
799 In MySQL INFORMATION_SCHEMA tables are temporary tables that are
800 created and filled on demand. In this function, we fill
801 INFORMATION_SCHEMA.events. It is a callback for I_S module, invoked from
802 sql_show.cc
803
804 @return Has to be integer, as such is the requirement of the I_S API
805 @retval 0 success
806 @retval 1 an error, pushed into the error stack
807 */
808
809 int
fill_schema_events(THD * thd,TABLE_LIST * tables,COND *)810 Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
811 {
812 const char *db= NULL;
813 int ret;
814 char db_tmp[SAFE_NAME_LEN];
815 DBUG_ENTER("Events::fill_schema_events");
816
817 /*
818 If we didn't start events because of --skip-grant-tables, return an
819 empty set
820 */
821 if (opt_noacl)
822 DBUG_RETURN(0);
823
824 if (unlikely(check_if_system_tables_error()))
825 DBUG_RETURN(1);
826
827 /*
828 If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
829 be NULL. Let's do an assert anyway.
830 */
831 if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
832 {
833 DBUG_ASSERT(thd->lex->select_lex.db.str);
834 if (!is_infoschema_db(&thd->lex->select_lex.db) && // There is no events in I_S
835 check_access(thd, EVENT_ACL, thd->lex->select_lex.db.str,
836 NULL, NULL, 0, 0))
837 DBUG_RETURN(1);
838 db= normalize_db_name(thd->lex->select_lex.db.str, db_tmp, sizeof(db_tmp));
839 }
840 ret= db_repository->fill_schema_events(thd, tables, db);
841
842 DBUG_RETURN(ret);
843 }
844
845
846 /**
847 Initializes the scheduler's structures.
848
849 @param THD or null (if called by init)
850 @param opt_noacl_or_bootstrap
851 TRUE if there is --skip-grant-tables or --bootstrap
852 option. In that case we disable the event scheduler.
853
854 @note This function is not synchronized.
855
856 @retval FALSE Perhaps there was an error, and the event scheduler
857 is disabled. But the error is not fatal and the
858 server start up can continue.
859 @retval TRUE Fatal error. Startup must terminate (call unireg_abort()).
860 */
861
862 bool
init(THD * thd,bool opt_noacl_or_bootstrap)863 Events::init(THD *thd, bool opt_noacl_or_bootstrap)
864 {
865 int err_no;
866 bool res= FALSE;
867 bool had_thd= thd != 0;
868 DBUG_ENTER("Events::init");
869
870 DBUG_ASSERT(inited == 0);
871
872 /*
873 Was disabled explicitly from the command line
874 */
875 if (opt_event_scheduler == Events::EVENTS_DISABLED ||
876 opt_noacl_or_bootstrap)
877 DBUG_RETURN(FALSE);
878
879 /* We need a temporary THD during boot */
880 if (!thd)
881 {
882
883 if (!(thd= new THD(0)))
884 {
885 res= TRUE;
886 goto end;
887 }
888 /*
889 The thread stack does not start from this function but we cannot
890 guess the real value. So better some value that doesn't assert than
891 no value.
892 */
893 thd->thread_stack= (char*) &thd;
894 thd->store_globals();
895 /*
896 Set current time for the thread that handles events.
897 Current time is stored in data member start_time of THD class.
898 Subsequently, this value is used to check whether event was expired
899 when make loading events from storage. Check for event expiration time
900 is done at Event_queue_element::compute_next_execution_time() where
901 event's status set to Event_parse_data::DISABLED and dropped flag set
902 to true if event was expired.
903 */
904 thd->set_time();
905 }
906
907 /*
908 We will need Event_db_repository anyway, even if the scheduler is
909 disabled - to perform events DDL.
910 */
911 if (!(db_repository= new Event_db_repository))
912 {
913 res= TRUE; /* fatal error: request unireg_abort */
914 goto end;
915 }
916
917 /*
918 Since we allow event DDL even if the scheduler is disabled,
919 check the system tables, as we might need them.
920
921 If run with --skip-grant-tables or --bootstrap, don't try to do the
922 check of system tables and don't complain: in these modes the tables
923 are most likely not there and we're going to disable the event
924 scheduler anyway.
925 */
926 if (Event_db_repository::check_system_tables(thd))
927 {
928 delete db_repository;
929 db_repository= 0;
930 my_message(ER_STARTUP,
931 "Event Scheduler: An error occurred when initializing "
932 "system tables. Disabling the Event Scheduler.",
933 MYF(ME_NOREFRESH));
934 /* Disable the scheduler since the system tables are not up to date */
935 opt_event_scheduler= EVENTS_OFF;
936 goto end;
937 }
938
939
940 DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
941 opt_event_scheduler == Events::EVENTS_OFF);
942
943 if (!(event_queue= new Event_queue) ||
944 !(scheduler= new Event_scheduler(event_queue)))
945 {
946 res= TRUE; /* fatal error: request unireg_abort */
947 goto end;
948 }
949
950 if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
951 (opt_event_scheduler == EVENTS_ON && scheduler->start(&err_no)))
952 {
953 my_message_sql(ER_STARTUP,
954 "Event Scheduler: Error while loading from mysql.event table.",
955 MYF(ME_NOREFRESH));
956 res= TRUE; /* fatal error: request unireg_abort */
957 goto end;
958 }
959 Event_worker_thread::init(db_repository);
960 inited= 1;
961
962 end:
963 if (res)
964 deinit();
965 if (!had_thd)
966 delete thd;
967
968 DBUG_RETURN(res);
969 }
970
971 /*
972 Cleans up scheduler's resources. Called at server shutdown.
973
974 SYNOPSIS
975 Events::deinit()
976
977 NOTES
978 This function is not synchronized.
979 */
980
981 void
deinit()982 Events::deinit()
983 {
984 DBUG_ENTER("Events::deinit");
985
986 delete scheduler;
987 scheduler= NULL; /* For restart */
988 delete event_queue;
989 event_queue= NULL; /* For restart */
990 delete db_repository;
991 db_repository= NULL; /* For restart */
992
993 inited= 0;
994 DBUG_VOID_RETURN;
995 }
996
997 #ifdef HAVE_PSI_INTERFACE
998 PSI_mutex_key key_LOCK_event_queue,
999 key_event_scheduler_LOCK_scheduler_state;
1000
1001 static PSI_mutex_info all_events_mutexes[]=
1002 {
1003 { &key_LOCK_event_queue, "LOCK_event_queue", PSI_FLAG_GLOBAL},
1004 { &key_event_scheduler_LOCK_scheduler_state, "Event_scheduler::LOCK_scheduler_state", PSI_FLAG_GLOBAL}
1005 };
1006
1007 PSI_cond_key key_event_scheduler_COND_state, key_COND_queue_state;
1008
1009 static PSI_cond_info all_events_conds[]=
1010 {
1011 { &key_event_scheduler_COND_state, "Event_scheduler::COND_state", PSI_FLAG_GLOBAL},
1012 { &key_COND_queue_state, "COND_queue_state", PSI_FLAG_GLOBAL},
1013 };
1014
1015 PSI_thread_key key_thread_event_scheduler, key_thread_event_worker;
1016
1017 static PSI_thread_info all_events_threads[]=
1018 {
1019 { &key_thread_event_scheduler, "event_scheduler", PSI_FLAG_GLOBAL},
1020 { &key_thread_event_worker, "event_worker", 0}
1021 };
1022 #endif /* HAVE_PSI_INTERFACE */
1023
1024 PSI_stage_info stage_waiting_on_empty_queue= { 0, "Waiting on empty queue", 0};
1025 PSI_stage_info stage_waiting_for_next_activation= { 0, "Waiting for next activation", 0};
1026 PSI_stage_info stage_waiting_for_scheduler_to_stop= { 0, "Waiting for the scheduler to stop", 0};
1027
1028 #ifdef HAVE_PSI_INTERFACE
1029 PSI_stage_info *all_events_stages[]=
1030 {
1031 & stage_waiting_on_empty_queue,
1032 & stage_waiting_for_next_activation,
1033 & stage_waiting_for_scheduler_to_stop
1034 };
1035
init_events_psi_keys(void)1036 static void init_events_psi_keys(void)
1037 {
1038 const char* category= "sql";
1039 int count;
1040
1041 count= array_elements(all_events_mutexes);
1042 mysql_mutex_register(category, all_events_mutexes, count);
1043
1044 count= array_elements(all_events_conds);
1045 mysql_cond_register(category, all_events_conds, count);
1046
1047 count= array_elements(all_events_threads);
1048 mysql_thread_register(category, all_events_threads, count);
1049
1050 count= array_elements(all_events_stages);
1051 mysql_stage_register(category, all_events_stages, count);
1052
1053 }
1054 #endif /* HAVE_PSI_INTERFACE */
1055
1056 /**
1057 Inits Events mutexes
1058
1059 SYNOPSIS
1060 Events::init_mutexes()
1061 thd Thread
1062 */
1063
1064 void
init_mutexes()1065 Events::init_mutexes()
1066 {
1067 #ifdef HAVE_PSI_INTERFACE
1068 init_events_psi_keys();
1069 #endif
1070 }
1071
1072
1073 /*
1074 Dumps the internal status of the scheduler and the memory cache
1075 into a table with two columns - Name & Value. Different properties
1076 which could be useful for debugging for instance deadlocks are
1077 returned.
1078
1079 SYNOPSIS
1080 Events::dump_internal_status()
1081 */
1082
1083 void
dump_internal_status()1084 Events::dump_internal_status()
1085 {
1086 DBUG_ENTER("Events::dump_internal_status");
1087 puts("\n\n\nEvents status:");
1088 puts("LLA = Last Locked At LUA = Last Unlocked At");
1089 puts("WOC = Waiting On Condition DL = Data Locked");
1090
1091 /*
1092 opt_event_scheduler should only be accessed while
1093 holding LOCK_global_system_variables.
1094 */
1095 mysql_mutex_lock(&LOCK_global_system_variables);
1096 if (!inited)
1097 puts("The Event Scheduler is disabled");
1098 else
1099 {
1100 scheduler->dump_internal_status();
1101 event_queue->dump_internal_status();
1102 }
1103
1104 mysql_mutex_unlock(&LOCK_global_system_variables);
1105 DBUG_VOID_RETURN;
1106 }
1107
start(int * err_no)1108 bool Events::start(int *err_no)
1109 {
1110 DBUG_ASSERT(inited);
1111 return scheduler->start(err_no);
1112 }
1113
stop()1114 bool Events::stop()
1115 {
1116 DBUG_ASSERT(inited);
1117 return scheduler->stop();
1118 }
1119
1120 /**
1121 Loads all ENABLED events from mysql.event into a prioritized
1122 queue.
1123
1124 This function is called during the server start up. It reads
1125 every event, computes the next execution time, and if the event
1126 needs execution, adds it to a prioritized queue. Otherwise, if
1127 ON COMPLETION DROP is specified, the event is automatically
1128 removed from the table.
1129
1130 @param[in,out] thd Thread context. Used for memory allocation in some cases.
1131
1132 @retval FALSE success
1133 @retval TRUE error, the load is aborted
1134
1135 @note Reports the error to the console
1136 */
1137
1138 bool
load_events_from_db(THD * thd)1139 Events::load_events_from_db(THD *thd)
1140 {
1141 TABLE *table;
1142 READ_RECORD read_record_info;
1143 bool ret= TRUE;
1144 uint count= 0;
1145 ulong saved_master_access;
1146 DBUG_ENTER("Events::load_events_from_db");
1147 DBUG_PRINT("enter", ("thd: %p", thd));
1148
1149 /*
1150 NOTE: even if we run in read-only mode, we should be able to lock the
1151 mysql.event table for writing. In order to achieve this, we should call
1152 mysql_lock_tables() under the super user.
1153
1154 Same goes for transaction access mode.
1155 Temporarily reset it to read-write.
1156 */
1157
1158 saved_master_access= thd->security_ctx->master_access;
1159 thd->security_ctx->master_access |= SUPER_ACL;
1160 bool save_tx_read_only= thd->tx_read_only;
1161 thd->tx_read_only= false;
1162
1163 ret= db_repository->open_event_table(thd, TL_WRITE, &table);
1164
1165 thd->tx_read_only= save_tx_read_only;
1166 thd->security_ctx->master_access= saved_master_access;
1167
1168 if (ret)
1169 {
1170 my_message_sql(ER_STARTUP,
1171 "Event Scheduler: Failed to open table mysql.event",
1172 MYF(ME_NOREFRESH));
1173 DBUG_RETURN(TRUE);
1174 }
1175
1176 if (init_read_record(&read_record_info, thd, table, NULL, NULL, 0, 1, FALSE))
1177 {
1178 close_thread_tables(thd);
1179 DBUG_RETURN(TRUE);
1180 }
1181
1182 while (!(read_record_info.read_record()))
1183 {
1184 Event_queue_element *et;
1185 bool created, dropped;
1186
1187 if (!(et= new Event_queue_element))
1188 goto end;
1189
1190 DBUG_PRINT("info", ("Loading event from row."));
1191
1192 if (et->load_from_row(thd, table))
1193 {
1194 my_message(ER_STARTUP,
1195 "Event Scheduler: "
1196 "Error while loading events from mysql.event. "
1197 "The table probably contains bad data or is corrupted",
1198 MYF(ME_NOREFRESH));
1199 delete et;
1200 goto end;
1201 }
1202
1203 #ifdef WITH_WSREP
1204 /**
1205 If SST is done from a galera node that is also acting as MASTER
1206 newly synced node in galera eco-system will also copy-over the
1207 event state enabling duplicate event in galera eco-system.
1208 DISABLE such events if the current node is not event orginator.
1209 (Also, make sure you skip disabling it if is already disabled to avoid
1210 creation of redundant action)
1211 NOTE:
1212 This complete system relies on server-id. Ideally server-id should be
1213 same for all nodes of galera eco-system but they aren't same.
1214 Infact, based on galera use-case it seems like it recommends to have each
1215 node with different server-id.
1216 */
1217 if (WSREP(thd) && et->originator != thd->variables.server_id)
1218 {
1219 if (et->status == Event_parse_data::SLAVESIDE_DISABLED)
1220 continue;
1221
1222 store_record(table, record[1]);
1223 table->field[ET_FIELD_STATUS]->
1224 store((longlong) Event_parse_data::SLAVESIDE_DISABLED,
1225 TRUE);
1226
1227 /* All the dmls to mysql.events tables are stmt bin-logged. */
1228 bool save_binlog_row_based;
1229 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
1230 thd->set_current_stmt_binlog_format_stmt();
1231
1232 (void) table->file->ha_update_row(table->record[1], table->record[0]);
1233
1234 if (save_binlog_row_based)
1235 thd->set_current_stmt_binlog_format_row();
1236
1237 delete et;
1238 continue;
1239 }
1240 #endif /* WITH_WSREP */
1241
1242 /**
1243 Since the Event_queue_element object could be deleted inside
1244 Event_queue::create_event we should save the value of dropped flag
1245 into the temporary variable.
1246 */
1247 dropped= et->dropped;
1248 if (event_queue->create_event(thd, et, &created))
1249 {
1250 /* Out of memory */
1251 delete et;
1252 goto end;
1253 }
1254 if (created)
1255 count++;
1256 else if (dropped)
1257 {
1258 /*
1259 If not created, a stale event - drop if immediately if
1260 ON COMPLETION NOT PRESERVE.
1261 XXX: This won't be replicated, thus the drop won't appear in
1262 in the slave. When the slave is restarted it will drop events.
1263 However, as the slave will be "out of sync", it might happen that
1264 an event created on the master, after master restart, won't be
1265 replicated to the slave correctly, as the create will fail there.
1266 */
1267 int rc= table->file->ha_delete_row(table->record[0]);
1268 if (rc)
1269 {
1270 table->file->print_error(rc, MYF(0));
1271 goto end;
1272 }
1273 }
1274 }
1275 my_printf_error(ER_STARTUP,
1276 "Event Scheduler: Loaded %d event%s",
1277 MYF(ME_NOREFRESH |
1278 (global_system_variables.log_warnings) ?
1279 ME_JUST_INFO: 0),
1280 count, (count == 1) ? "" : "s");
1281 ret= FALSE;
1282
1283 end:
1284 end_read_record(&read_record_info);
1285
1286 close_mysql_tables(thd);
1287 DBUG_RETURN(ret);
1288 }
1289
1290 #ifdef WITH_WSREP
wsrep_create_event_query(THD * thd,uchar ** buf,size_t * buf_len)1291 int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len)
1292 {
1293 char buffer[1024];
1294 String log_query(buffer, sizeof(buffer), &my_charset_bin);
1295
1296 if (create_query_string(thd, &log_query))
1297 {
1298 WSREP_WARN("events create string failed: schema: %s, query: %s",
1299 thd->get_db(), thd->query());
1300 return 1;
1301 }
1302 return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
1303 }
1304 #endif /* WITH_WSREP */
1305 /**
1306 @} (End of group Event_Scheduler)
1307 */
1308