1 /* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "event_data_objects.h"
24
25 #include "sql_parse.h" // parse_sql
26 #include "strfunc.h" // find_string_in_array
27 #include "sql_db.h" // get_default_db_collation
28 #include "sql_time.h" // interval_type_to_name,
29 // date_add_interval,
30 // calc_time_diff
31 #include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
32 #include "auth_common.h" // EVENT_ACL, SUPER_ACL
33 #include "sp.h" // load_charset, load_collation
34 #include "events.h"
35 #include "event_db_repository.h"
36 #include "event_parse_data.h"
37 #include "sp_head.h"
38 #include "sql_show.h" // append_definer, append_identifier
39 #include "log.h"
40
41 #include "mysql/psi/mysql_sp.h"
42
43 /**
44 @addtogroup Event_Scheduler
45 @{
46 */
47
48 #ifdef HAVE_PSI_INTERFACE
init_scheduler_psi_keys()49 void init_scheduler_psi_keys()
50 {
51 const char *category= "scheduler";
52
53 PSI_server->register_statement(category, & Event_queue_element_for_exec::psi_info, 1);
54 }
55
56 PSI_statement_info Event_queue_element_for_exec::psi_info=
57 { 0, "event", 0};
58 #endif
59
60 /*************************************************************************/
61
62 /**
63 Event_creation_ctx -- creation context of events.
64 */
65
66 class Event_creation_ctx :public Stored_program_creation_ctx,
67 public Sql_alloc
68 {
69 public:
70 static bool load_from_db(THD *thd,
71 MEM_ROOT *event_mem_root,
72 const char *db_name,
73 const char *event_name,
74 TABLE *event_tbl,
75 Stored_program_creation_ctx **ctx);
76
77 public:
clone(MEM_ROOT * mem_root)78 virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
79 {
80 return new (mem_root)
81 Event_creation_ctx(m_client_cs, m_connection_cl, m_db_cl);
82 }
83
84 protected:
create_backup_ctx(THD * thd) const85 virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
86 {
87 /*
88 We can avoid usual backup/restore employed in stored programs since we
89 know that this is a top level statement and the worker thread is
90 allocated exclusively to execute this event.
91 */
92
93 return NULL;
94 }
95
96 private:
Event_creation_ctx(const CHARSET_INFO * client_cs,const CHARSET_INFO * connection_cl,const CHARSET_INFO * db_cl)97 Event_creation_ctx(const CHARSET_INFO *client_cs,
98 const CHARSET_INFO *connection_cl,
99 const CHARSET_INFO *db_cl)
100 : Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
101 { }
102 };
103
104 /**************************************************************************
105 Event_creation_ctx implementation.
106 **************************************************************************/
107
108 bool
load_from_db(THD * thd,MEM_ROOT * event_mem_root,const char * db_name,const char * event_name,TABLE * event_tbl,Stored_program_creation_ctx ** ctx)109 Event_creation_ctx::load_from_db(THD *thd,
110 MEM_ROOT *event_mem_root,
111 const char *db_name,
112 const char *event_name,
113 TABLE *event_tbl,
114 Stored_program_creation_ctx **ctx)
115 {
116 /* Load character set/collation attributes. */
117
118 const CHARSET_INFO *client_cs;
119 const CHARSET_INFO *connection_cl;
120 const CHARSET_INFO *db_cl;
121
122 bool invalid_creation_ctx= FALSE;
123
124 if (load_charset(event_mem_root,
125 event_tbl->field[ET_FIELD_CHARACTER_SET_CLIENT],
126 thd->variables.character_set_client,
127 &client_cs))
128 {
129 sql_print_warning("Event '%s'.'%s': invalid value "
130 "in column mysql.event.character_set_client.",
131 db_name,
132 event_name);
133
134 invalid_creation_ctx= TRUE;
135 }
136
137 if (load_collation(event_mem_root,
138 event_tbl->field[ET_FIELD_COLLATION_CONNECTION],
139 thd->variables.collation_connection,
140 &connection_cl))
141 {
142 sql_print_warning("Event '%s'.'%s': invalid value "
143 "in column mysql.event.collation_connection.",
144 db_name,
145 event_name);
146
147 invalid_creation_ctx= TRUE;
148 }
149
150 if (load_collation(event_mem_root,
151 event_tbl->field[ET_FIELD_DB_COLLATION],
152 NULL,
153 &db_cl))
154 {
155 sql_print_warning("Event '%s'.'%s': invalid value "
156 "in column mysql.event.db_collation.",
157 db_name,
158 event_name);
159
160 invalid_creation_ctx= TRUE;
161 }
162
163 /*
164 If we failed to resolve the database collation, load the default one
165 from the disk.
166 */
167
168 if (!db_cl)
169 db_cl= get_default_db_collation(thd, db_name);
170
171 /* Create the context. */
172
173 *ctx= new Event_creation_ctx(client_cs, connection_cl, db_cl);
174
175 return invalid_creation_ctx;
176 }
177
178 /*************************************************************************/
179
180 /*
181 Initiliazes dbname and name of an Event_queue_element_for_exec
182 object
183
184 SYNOPSIS
185 Event_queue_element_for_exec::init()
186
187 RETURN VALUE
188 FALSE OK
189 TRUE Error (OOM)
190 */
191
192 bool
init(LEX_STRING db,LEX_STRING n)193 Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
194 {
195 if (!(dbname.str= my_strndup(key_memory_Event_queue_element_for_exec_names,
196 db.str, dbname.length= db.length, MYF(MY_WME))))
197 return TRUE;
198 if (!(name.str= my_strndup(key_memory_Event_queue_element_for_exec_names,
199 n.str, name.length= n.length, MYF(MY_WME))))
200 {
201 my_free(dbname.str);
202 return TRUE;
203 }
204 return FALSE;
205 }
206
claim_memory_ownership()207 void Event_queue_element_for_exec::claim_memory_ownership()
208 {
209 my_claim(dbname.str);
210 my_claim(name.str);
211 }
212
213 /*
214 Destructor
215
216 SYNOPSIS
217 Event_queue_element_for_exec::~Event_queue_element_for_exec()
218 */
219
~Event_queue_element_for_exec()220 Event_queue_element_for_exec::~Event_queue_element_for_exec()
221 {
222 my_free(dbname.str);
223 my_free(name.str);
224 }
225
226
227 /*
228 Constructor
229
230 SYNOPSIS
231 Event_basic::Event_basic()
232 */
233
Event_basic()234 Event_basic::Event_basic()
235 {
236 DBUG_ENTER("Event_basic::Event_basic");
237 /* init memory root */
238 init_sql_alloc(key_memory_event_basic_root, &mem_root, 256, 512);
239 dbname.str= name.str= NULL;
240 dbname.length= name.length= 0;
241 time_zone= NULL;
242 DBUG_VOID_RETURN;
243 }
244
245
246 /*
247 Destructor
248
249 SYNOPSIS
250 Event_basic::Event_basic()
251 */
252
~Event_basic()253 Event_basic::~Event_basic()
254 {
255 DBUG_ENTER("Event_basic::~Event_basic");
256 free_root(&mem_root, MYF(0));
257 DBUG_VOID_RETURN;
258 }
259
260
261 /*
262 Short function to load a char column into a LEX_STRING
263
264 SYNOPSIS
265 Event_basic::load_string_field()
266 field_name The field( enum_events_table_field is not actually used
267 because it's unknown in event_data_objects.h)
268 fields The Field array
269 field_value The value
270 */
271
272 bool
load_string_fields(Field ** fields,...)273 Event_basic::load_string_fields(Field **fields, ...)
274 {
275 bool ret= FALSE;
276 va_list args;
277 enum enum_events_table_field field_name;
278 LEX_STRING *field_value;
279
280 DBUG_ENTER("Event_basic::load_string_fields");
281
282 va_start(args, fields);
283 field_name= (enum enum_events_table_field) va_arg(args, int);
284 while (field_name < ET_FIELD_COUNT)
285 {
286 field_value= va_arg(args, LEX_STRING *);
287 if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
288 {
289 ret= TRUE;
290 break;
291 }
292 field_value->length= strlen(field_value->str);
293
294 field_name= (enum enum_events_table_field) va_arg(args, int);
295 }
296 va_end(args);
297
298 DBUG_RETURN(ret);
299 }
300
301
302 bool
load_time_zone(THD * thd,const LEX_STRING tz_name)303 Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
304 {
305 String str(tz_name.str, &my_charset_latin1);
306 time_zone= my_tz_find(thd, &str);
307
308 return (time_zone == NULL);
309 }
310
311
312 /*
313 Constructor
314
315 SYNOPSIS
316 Event_queue_element::Event_queue_element()
317 */
318
Event_queue_element()319 Event_queue_element::Event_queue_element():
320 on_completion(Event_parse_data::ON_COMPLETION_DROP),
321 status(Event_parse_data::ENABLED), expression(0), dropped(FALSE),
322 execution_count(0)
323 {
324 DBUG_ENTER("Event_queue_element::Event_queue_element");
325
326 starts= ends= execute_at= last_executed= 0;
327 starts_null= ends_null= execute_at_null= TRUE;
328
329 DBUG_VOID_RETURN;
330 }
331
332
333 /*
334 Destructor
335
336 SYNOPSIS
337 Event_queue_element::Event_queue_element()
338 */
~Event_queue_element()339 Event_queue_element::~Event_queue_element()
340 {
341 }
342
343
344 /*
345 Constructor
346
347 SYNOPSIS
348 Event_timed::Event_timed()
349 */
350
Event_timed()351 Event_timed::Event_timed():
352 created(0), modified(0), sql_mode(0)
353 {
354 DBUG_ENTER("Event_timed::Event_timed");
355 init();
356 DBUG_VOID_RETURN;
357 }
358
359
360 /*
361 Destructor
362
363 SYNOPSIS
364 Event_timed::~Event_timed()
365 */
366
~Event_timed()367 Event_timed::~Event_timed()
368 {
369 }
370
371
372 /*
373 Constructor
374
375 SYNOPSIS
376 Event_job_data::Event_job_data()
377 */
378
Event_job_data()379 Event_job_data::Event_job_data()
380 :sql_mode(0)
381 {
382 }
383
384 /*
385 Init all member variables
386
387 SYNOPSIS
388 Event_timed::init()
389 */
390
391 void
init()392 Event_timed::init()
393 {
394 DBUG_ENTER("Event_timed::init");
395
396 definer_user= NULL_CSTR;
397 definer_host= NULL_CSTR;
398 body= NULL_STR;
399 comment= NULL_STR;
400
401 sql_mode= 0;
402
403 DBUG_VOID_RETURN;
404 }
405
406
407 /**
408 Load an event's body from a row from mysql.event.
409
410 @details This method is silent on errors and should behave like that.
411 Callers should handle throwing of error messages. The reason is that the
412 class should not know about how to deal with communication.
413
414 @return Operation status
415 @retval FALSE OK
416 @retval TRUE Error
417 */
418
419 bool
load_from_row(THD * thd,TABLE * table)420 Event_job_data::load_from_row(THD *thd, TABLE *table)
421 {
422 char *ptr;
423 size_t len;
424 LEX_STRING tz_name;
425
426 DBUG_ENTER("Event_job_data::load_from_row");
427
428 if (!table)
429 DBUG_RETURN(TRUE);
430
431 if (table->s->fields < ET_FIELD_COUNT)
432 DBUG_RETURN(TRUE);
433
434 if (load_string_fields(table->field,
435 ET_FIELD_DB, &dbname,
436 ET_FIELD_NAME, &name,
437 ET_FIELD_BODY, &body,
438 ET_FIELD_DEFINER, &definer,
439 ET_FIELD_TIME_ZONE, &tz_name,
440 ET_FIELD_COUNT))
441 DBUG_RETURN(TRUE);
442
443 if (load_time_zone(thd, tz_name))
444 DBUG_RETURN(TRUE);
445
446 Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, table,
447 &creation_ctx);
448
449 ptr= strchr(definer.str, '@');
450
451 if (! ptr)
452 ptr= definer.str;
453
454 len= ptr - definer.str;
455 definer_user.str= strmake_root(&mem_root, definer.str, len);
456 definer_user.length= len;
457 len= definer.length - len - 1;
458 /* 1:because of @ */
459 definer_host.str= strmake_root(&mem_root, ptr + 1, len);
460 definer_host.length= len;
461
462 sql_mode= (sql_mode_t) table->field[ET_FIELD_SQL_MODE]->val_int();
463
464 DBUG_RETURN(FALSE);
465 }
466
467
468 /**
469 Load an event's body from a row from mysql.event.
470
471 @details This method is silent on errors and should behave like that.
472 Callers should handle throwing of error messages. The reason is that the
473 class should not know about how to deal with communication.
474
475 @return Operation status
476 @retval FALSE OK
477 @retval TRUE Error
478 */
479
480 bool
load_from_row(THD * thd,TABLE * table)481 Event_queue_element::load_from_row(THD *thd, TABLE *table)
482 {
483 char *ptr;
484 MYSQL_TIME time;
485 LEX_STRING tz_name;
486
487 DBUG_ENTER("Event_queue_element::load_from_row");
488
489 if (!table)
490 DBUG_RETURN(TRUE);
491
492 if (table->s->fields < ET_FIELD_COUNT)
493 DBUG_RETURN(TRUE);
494
495 if (load_string_fields(table->field,
496 ET_FIELD_DB, &dbname,
497 ET_FIELD_NAME, &name,
498 ET_FIELD_DEFINER, &definer,
499 ET_FIELD_TIME_ZONE, &tz_name,
500 ET_FIELD_COUNT))
501 DBUG_RETURN(TRUE);
502
503 if (load_time_zone(thd, tz_name))
504 DBUG_RETURN(TRUE);
505
506 starts_null= table->field[ET_FIELD_STARTS]->is_null();
507 my_bool not_used= FALSE;
508 if (!starts_null)
509 {
510 table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE);
511 starts= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used);
512 }
513
514 ends_null= table->field[ET_FIELD_ENDS]->is_null();
515 if (!ends_null)
516 {
517 table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE);
518 ends= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used);
519 }
520
521 if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
522 expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
523 else
524 expression= 0;
525 /*
526 If neigher STARTS and ENDS is set, then both fields are empty.
527 Hence, if ET_FIELD_EXECUTE_AT is empty there is an error.
528 */
529 execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
530 DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
531 if (!expression && !execute_at_null)
532 {
533 if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
534 TIME_NO_ZERO_DATE))
535 DBUG_RETURN(TRUE);
536 execute_at= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used);
537 }
538
539 /*
540 We load the interval type from disk as string and then map it to
541 an integer. This decouples the values of enum interval_type
542 and values actually stored on disk. Therefore the type can be
543 reordered without risking incompatibilities of data between versions.
544 */
545 if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
546 {
547 int i;
548 char buff[MAX_FIELD_WIDTH];
549 String str(buff, sizeof(buff), &my_charset_bin);
550 LEX_STRING tmp;
551
552 table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
553 if (!(tmp.length= str.length()))
554 DBUG_RETURN(TRUE);
555
556 tmp.str= str.c_ptr_safe();
557
558 i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info);
559 if (i < 0)
560 DBUG_RETURN(TRUE);
561 interval= (interval_type) i;
562 }
563
564 if (!table->field[ET_FIELD_LAST_EXECUTED]->is_null())
565 {
566 table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time,
567 TIME_NO_ZERO_DATE);
568 last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used);
569 }
570
571 if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
572 DBUG_RETURN(TRUE);
573
574 DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
575
576 /* Set event status (ENABLED | SLAVESIDE_DISABLED | DISABLED) */
577 switch (ptr[0])
578 {
579 case 'E' :
580 status = Event_parse_data::ENABLED;
581 break;
582 case 'S' :
583 status = Event_parse_data::SLAVESIDE_DISABLED;
584 break;
585 case 'D' :
586 default:
587 status = Event_parse_data::DISABLED;
588 break;
589 }
590 if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS)
591 DBUG_RETURN(TRUE);
592 originator = table->field[ET_FIELD_ORIGINATOR]->val_int();
593
594 /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
595 if ((ptr= get_field(&mem_root,
596 table->field[ET_FIELD_ON_COMPLETION])) == NullS)
597 DBUG_RETURN(TRUE);
598
599 on_completion= (ptr[0]=='D'? Event_parse_data::ON_COMPLETION_DROP:
600 Event_parse_data::ON_COMPLETION_PRESERVE);
601
602 DBUG_RETURN(FALSE);
603 }
604
605
606 /**
607 Load an event's body from a row from mysql.event.
608
609 @details This method is silent on errors and should behave like that.
610 Callers should handle throwing of error messages. The reason is that the
611 class should not know about how to deal with communication.
612
613 @return Operation status
614 @retval FALSE OK
615 @retval TRUE Error
616 */
617
618 bool
load_from_row(THD * thd,TABLE * table)619 Event_timed::load_from_row(THD *thd, TABLE *table)
620 {
621 char *ptr;
622 size_t len;
623
624 DBUG_ENTER("Event_timed::load_from_row");
625
626 if (Event_queue_element::load_from_row(thd, table))
627 DBUG_RETURN(TRUE);
628
629 if (load_string_fields(table->field,
630 ET_FIELD_BODY, &body,
631 ET_FIELD_BODY_UTF8, &body_utf8,
632 ET_FIELD_COUNT))
633 DBUG_RETURN(TRUE);
634
635 if (Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str,
636 table, &creation_ctx))
637 {
638 push_warning_printf(thd,
639 Sql_condition::SL_WARNING,
640 ER_EVENT_INVALID_CREATION_CTX,
641 ER(ER_EVENT_INVALID_CREATION_CTX),
642 (const char *) dbname.str,
643 (const char *) name.str);
644 }
645
646 ptr= strchr(definer.str, '@');
647
648 if (! ptr)
649 ptr= definer.str;
650
651 len= ptr - definer.str;
652 definer_user.str= strmake_root(&mem_root, definer.str, len);
653 definer_user.length= len;
654 len= definer.length - len - 1;
655 /* 1:because of @ */
656 definer_host.str= strmake_root(&mem_root, ptr + 1, len);
657 definer_host.length= len;
658
659 created= table->field[ET_FIELD_CREATED]->val_int();
660 modified= table->field[ET_FIELD_MODIFIED]->val_int();
661
662 comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
663 if (comment.str != NullS)
664 comment.length= strlen(comment.str);
665 else
666 comment.length= 0;
667
668 sql_mode= (sql_mode_t) table->field[ET_FIELD_SQL_MODE]->val_int();
669
670 DBUG_RETURN(FALSE);
671 }
672
673
674 /*
675 add_interval() adds a specified interval to time 'ltime' in time
676 zone 'time_zone', and returns the result converted to the number of
677 seconds since epoch (aka Unix time; in UTC time zone). Zero result
678 means an error.
679 */
680 static
681 my_time_t
add_interval(MYSQL_TIME * ltime,const Time_zone * time_zone,interval_type scale,Interval interval)682 add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone,
683 interval_type scale, Interval interval)
684 {
685 if (date_add_interval(ltime, scale, interval))
686 return 0;
687
688 my_bool not_used;
689 return time_zone->TIME_to_gmt_sec(ltime, ¬_used);
690 }
691
692
693 /*
694 Computes the sum of a timestamp plus interval.
695
696 SYNOPSIS
697 get_next_time()
698 time_zone event time zone
699 next the sum
700 start add interval_value to this time
701 time_now current time
702 i_value quantity of time type interval to add
703 i_type type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
704
705 RETURN VALUE
706 0 OK
707 1 Error
708
709 NOTES
710 1) If the interval is conversible to SECOND, like MINUTE, HOUR, DAY, WEEK.
711 Then we use TIMEDIFF()'s implementation as underlying and number of
712 seconds as resolution for computation.
713 2) In all other cases - MONTH, QUARTER, YEAR we use MONTH as resolution
714 and PERIOD_DIFF()'s implementation
715 */
716
717 static
get_next_time(const Time_zone * time_zone,my_time_t * next,my_time_t start,my_time_t time_now,int i_value,interval_type i_type)718 bool get_next_time(const Time_zone *time_zone, my_time_t *next,
719 my_time_t start, my_time_t time_now,
720 int i_value, interval_type i_type)
721 {
722 DBUG_ENTER("get_next_time");
723 DBUG_PRINT("enter", ("start: %lu now: %lu", (long) start, (long) time_now));
724
725 DBUG_ASSERT(start <= time_now);
726
727 longlong months=0, seconds=0;
728
729 switch (i_type) {
730 case INTERVAL_YEAR:
731 months= i_value*12;
732 break;
733 case INTERVAL_QUARTER:
734 /* Has already been converted to months */
735 case INTERVAL_YEAR_MONTH:
736 case INTERVAL_MONTH:
737 months= i_value;
738 break;
739 case INTERVAL_WEEK:
740 /* WEEK has already been converted to days */
741 case INTERVAL_DAY:
742 seconds= i_value*24*3600;
743 break;
744 case INTERVAL_DAY_HOUR:
745 case INTERVAL_HOUR:
746 seconds= i_value*3600;
747 break;
748 case INTERVAL_DAY_MINUTE:
749 case INTERVAL_HOUR_MINUTE:
750 case INTERVAL_MINUTE:
751 seconds= i_value*60;
752 break;
753 case INTERVAL_DAY_SECOND:
754 case INTERVAL_HOUR_SECOND:
755 case INTERVAL_MINUTE_SECOND:
756 case INTERVAL_SECOND:
757 seconds= i_value;
758 break;
759 case INTERVAL_DAY_MICROSECOND:
760 case INTERVAL_HOUR_MICROSECOND:
761 case INTERVAL_MINUTE_MICROSECOND:
762 case INTERVAL_SECOND_MICROSECOND:
763 case INTERVAL_MICROSECOND:
764 /*
765 We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
766 would give an error then.
767 */
768 DBUG_RETURN(1);
769 break;
770 case INTERVAL_LAST:
771 DBUG_ASSERT(0);
772 }
773 DBUG_PRINT("info", ("seconds: %ld months: %ld", (long) seconds, (long) months));
774
775 MYSQL_TIME local_start;
776 MYSQL_TIME local_now;
777
778 /* Convert times from UTC to local. */
779 {
780 time_zone->gmt_sec_to_TIME(&local_start, start);
781 time_zone->gmt_sec_to_TIME(&local_now, time_now);
782 }
783
784 Interval interval;
785 memset(&interval, 0, sizeof(interval));
786 my_time_t next_time= 0;
787
788 if (seconds)
789 {
790 longlong seconds_diff;
791 long microsec_diff;
792 bool negative= calc_time_diff(&local_now, &local_start, 1,
793 &seconds_diff, µsec_diff);
794 if (!negative)
795 {
796 /*
797 The formula below returns the interval that, when added to
798 local_start, will always give the time in the future.
799 */
800 interval.second= seconds_diff - seconds_diff % seconds + seconds;
801 next_time= add_interval(&local_start, time_zone,
802 INTERVAL_SECOND, interval);
803 if (next_time == 0)
804 goto done;
805 }
806
807 if (next_time <= time_now)
808 {
809 /*
810 If 'negative' is true above, then 'next_time == 0', and
811 'next_time <= time_now' is also true. If negative is false,
812 then next_time was set, but perhaps to the value that is less
813 then time_now. See below for elaboration.
814 */
815 DBUG_ASSERT(negative || next_time > 0);
816
817 /*
818 If local_now < local_start, i.e. STARTS time is in the future
819 according to the local time (it always in the past according
820 to UTC---this is a prerequisite of this function), then
821 STARTS is almost always in the past according to the local
822 time too. However, in the time zone that has backward
823 Daylight Saving Time shift, the following may happen: suppose
824 we have a backward DST shift at certain date after 2:59:59,
825 i.e. local time goes 1:59:59, 2:00:00, ... , 2:59:59, (shift
826 here) 2:00:00 (again), ... , 2:59:59 (again), 3:00:00, ... .
827 Now suppose the time has passed the first 2:59:59, has been
828 shifted backward, and now is (the second) 2:20:00. The user
829 does CREATE EVENT with STARTS 'current-date 2:40:00'. Local
830 time 2:40:00 from create statement is treated by time
831 functions as the first such time, so according to UTC it comes
832 before the second 2:20:00. But according to local time it is
833 obviously in the future, so we end up in this branch.
834
835 Since we are in the second pass through 2:00:00--2:59:59, and
836 any local time form this interval is treated by system
837 functions as the time from the first pass, we have to find the
838 time for the next execution that is past the DST-affected
839 interval (past the second 2:59:59 for our example,
840 i.e. starting from 3:00:00). We do this in the loop until the
841 local time is mapped onto future UTC time. 'start' time is in
842 the past, so we may use 'do { } while' here, and add the first
843 interval right away.
844
845 Alternatively, it could be that local_now >= local_start. Now
846 for the example above imagine we do CREATE EVENT with STARTS
847 'current-date 2:10:00'. Local start 2:10 is in the past (now
848 is local 2:20), so we add an interval, and get next execution
849 time, say, 2:40. It is in the future according to local time,
850 but, again, since we are in the second pass through
851 2:00:00--2:59:59, 2:40 will be converted into UTC time in the
852 past. So we will end up in this branch again, and may add
853 intervals in a 'do { } while' loop.
854
855 Note that for any given event we may end up here only if event
856 next execution time will map to the time interval that is
857 passed twice, and only if the server was started during the
858 second pass, or the event is being created during the second
859 pass. After that, we never will get here (unless we again
860 start the server during the second pass). In other words,
861 such a condition is extremely rare.
862 */
863 interval.second= seconds;
864 do
865 {
866 next_time= add_interval(&local_start, time_zone,
867 INTERVAL_SECOND, interval);
868 if (next_time == 0)
869 goto done;
870 }
871 while (next_time <= time_now);
872 }
873 }
874 else
875 {
876 long diff_months= ((long) local_now.year - (long) local_start.year)*12 +
877 ((long) local_now.month - (long) local_start.month);
878
879 /*
880 Unlike for seconds above, the formula below returns the interval
881 that, when added to the local_start, will give the time in the
882 past, or somewhere in the current month. We are interested in
883 the latter case, to see if this time has already passed, or is
884 yet to come this month.
885
886 Note that the time is guaranteed to be in the past unless
887 (diff_months % months == 0), but no good optimization is
888 possible here, because (diff_months % months == 0) is what will
889 happen most of the time, as get_next_time() will be called right
890 after the execution of the event. We could pass last_executed
891 time to this function, and see if the execution has already
892 happened this month, but for that we will have to convert
893 last_executed from seconds since epoch to local broken-down
894 time, and this will greatly reduce the effect of the
895 optimization. So instead we keep the code simple and clean.
896 */
897 interval.month= (ulong) (diff_months - diff_months % months);
898 next_time= add_interval(&local_start, time_zone,
899 INTERVAL_MONTH, interval);
900 if (next_time == 0)
901 goto done;
902
903 if (next_time <= time_now)
904 {
905 interval.month= (ulong) months;
906 next_time= add_interval(&local_start, time_zone,
907 INTERVAL_MONTH, interval);
908 if (next_time == 0)
909 goto done;
910 }
911 }
912
913 DBUG_ASSERT(time_now < next_time);
914
915 *next= next_time;
916
917 done:
918 DBUG_PRINT("info", ("next_time: %ld", (long) next_time));
919 DBUG_RETURN(next_time == 0);
920 }
921
922
923 /*
924 Computes next execution time.
925
926 SYNOPSIS
927 Event_queue_element::compute_next_execution_time()
928
929 RETURN VALUE
930 FALSE OK
931 TRUE Error
932
933 NOTES
934 The time is set in execute_at, if no more executions the latter is
935 set to 0.
936 */
937
938 bool
compute_next_execution_time()939 Event_queue_element::compute_next_execution_time()
940 {
941 my_time_t time_now;
942 DBUG_ENTER("Event_queue_element::compute_next_execution_time");
943 DBUG_PRINT("enter", ("starts: %lu ends: %lu last_executed: %lu this: 0x%lx",
944 (long) starts, (long) ends, (long) last_executed,
945 (long) this));
946
947 if (status != Event_parse_data::ENABLED)
948 {
949 DBUG_PRINT("compute_next_execution_time",
950 ("Event %s is DISABLED", name.str));
951 goto ret;
952 }
953 /* If one-time, no need to do computation */
954 if (!expression)
955 {
956 /* Let's check whether it was executed */
957 if (last_executed)
958 {
959 DBUG_PRINT("info",("One-time event %s.%s of was already executed",
960 dbname.str, name.str));
961 dropped= (on_completion == Event_parse_data::ON_COMPLETION_DROP);
962 DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
963
964 status= Event_parse_data::DISABLED;
965 }
966 goto ret;
967 }
968
969 time_now= (my_time_t) current_thd->query_start();
970
971 DBUG_PRINT("info",("NOW: [%lu]", (ulong) time_now));
972
973 /* if time_now is after ends don't execute anymore */
974 if (!ends_null && ends < time_now)
975 {
976 DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
977 /* time_now is after ends. don't execute anymore */
978 execute_at= 0;
979 execute_at_null= TRUE;
980 if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
981 dropped= TRUE;
982 DBUG_PRINT("info", ("Dropped: %d", dropped));
983 status= Event_parse_data::DISABLED;
984
985 goto ret;
986 }
987
988 /*
989 Here time_now is before or equals ends if the latter is set.
990 Let's check whether time_now is before starts.
991 If so schedule for starts.
992 */
993 if (!starts_null && time_now <= starts)
994 {
995 if (time_now == starts && starts == last_executed)
996 {
997 /*
998 do nothing or we will schedule for second time execution at starts.
999 */
1000 }
1001 else
1002 {
1003 DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
1004 /*
1005 starts is in the future
1006 time_now before starts. Scheduling for starts
1007 */
1008 execute_at= starts;
1009 execute_at_null= FALSE;
1010 goto ret;
1011 }
1012 }
1013
1014 if (!starts_null && !ends_null)
1015 {
1016 /*
1017 Both starts and m_ends are set and time_now is between them (incl.)
1018 If last_executed is set then increase with m_expression. The new MYSQL_TIME is
1019 after m_ends set execute_at to 0. And check for on_completion
1020 If not set then schedule for now.
1021 */
1022 DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
1023 if (!last_executed)
1024 {
1025 DBUG_PRINT("info", ("Not executed so far."));
1026 }
1027
1028 {
1029 my_time_t next_exec;
1030
1031 if (get_next_time(time_zone, &next_exec, starts, time_now,
1032 (int) expression, interval))
1033 goto err;
1034
1035 /* There was previous execution */
1036 if (ends < next_exec)
1037 {
1038 DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
1039 name.str));
1040 /* Next execution after ends. No more executions */
1041 execute_at= 0;
1042 execute_at_null= TRUE;
1043 if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
1044 dropped= TRUE;
1045 status= Event_parse_data::DISABLED;
1046 }
1047 else
1048 {
1049 DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
1050 execute_at= next_exec;
1051 execute_at_null= FALSE;
1052 }
1053 }
1054 goto ret;
1055 }
1056 else if (starts_null && ends_null)
1057 {
1058 /* starts is always set, so this is a dead branch !! */
1059 DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
1060 /*
1061 Both starts and m_ends are not set, so we schedule for the next
1062 based on last_executed.
1063 */
1064 if (last_executed)
1065 {
1066 my_time_t next_exec;
1067 if (get_next_time(time_zone, &next_exec, starts, time_now,
1068 (int) expression, interval))
1069 goto err;
1070 execute_at= next_exec;
1071 DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
1072 }
1073 else
1074 {
1075 /* last_executed not set. Schedule the event for now */
1076 DBUG_PRINT("info", ("Execute NOW"));
1077 execute_at= time_now;
1078 }
1079 execute_at_null= FALSE;
1080 }
1081 else
1082 {
1083 /* either starts or m_ends is set */
1084 if (!starts_null)
1085 {
1086 DBUG_PRINT("info", ("STARTS is set"));
1087 /*
1088 - starts is set.
1089 - starts is not in the future according to check made before
1090 Hence schedule for starts + m_expression in case last_executed
1091 is not set, otherwise to last_executed + m_expression
1092 */
1093 if (!last_executed)
1094 {
1095 DBUG_PRINT("info", ("Not executed so far."));
1096 }
1097
1098 {
1099 my_time_t next_exec;
1100 if (get_next_time(time_zone, &next_exec, starts, time_now,
1101 (int) expression, interval))
1102 goto err;
1103 execute_at= next_exec;
1104 DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
1105 }
1106 execute_at_null= FALSE;
1107 }
1108 else
1109 {
1110 /* this is a dead branch, because starts is always set !!! */
1111 DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
1112 /*
1113 - m_ends is set
1114 - m_ends is after time_now or is equal
1115 Hence check for m_last_execute and increment with m_expression.
1116 If last_executed is not set then schedule for now
1117 */
1118
1119 if (!last_executed)
1120 execute_at= time_now;
1121 else
1122 {
1123 my_time_t next_exec;
1124
1125 if (get_next_time(time_zone, &next_exec, starts, time_now,
1126 (int) expression, interval))
1127 goto err;
1128
1129 if (ends < next_exec)
1130 {
1131 DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
1132 execute_at= 0;
1133 execute_at_null= TRUE;
1134 status= Event_parse_data::DISABLED;
1135 if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
1136 dropped= TRUE;
1137 }
1138 else
1139 {
1140 DBUG_PRINT("info", ("Next[%lu]", (ulong) next_exec));
1141 execute_at= next_exec;
1142 execute_at_null= FALSE;
1143 }
1144 }
1145 }
1146 goto ret;
1147 }
1148 ret:
1149 DBUG_PRINT("info", ("ret: 0 execute_at: %lu", (long) execute_at));
1150 DBUG_RETURN(FALSE);
1151 err:
1152 DBUG_PRINT("info", ("ret=1"));
1153 DBUG_RETURN(TRUE);
1154 }
1155
1156
1157 /*
1158 Set the internal last_executed MYSQL_TIME struct to now. NOW is the
1159 time according to thd->query_start(), so the THD's clock.
1160
1161 SYNOPSIS
1162 Event_queue_element::mark_last_executed()
1163 thd thread context
1164 */
1165
1166 void
mark_last_executed(THD * thd)1167 Event_queue_element::mark_last_executed(THD *thd)
1168 {
1169 last_executed= (my_time_t) thd->query_start();
1170
1171 execution_count++;
1172 }
1173
1174
1175 static
1176 void
append_datetime(String * buf,Time_zone * time_zone,my_time_t secs,const char * name,uint len)1177 append_datetime(String *buf, Time_zone *time_zone, my_time_t secs,
1178 const char *name, uint len)
1179 {
1180 char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
1181 buf->append(STRING_WITH_LEN(" "));
1182 buf->append(name, len);
1183 buf->append(STRING_WITH_LEN(" '"));
1184 /*
1185 Pass the buffer and the second param tells fills the buffer and
1186 returns the number of chars to copy.
1187 */
1188 MYSQL_TIME time;
1189 time_zone->gmt_sec_to_TIME(&time, secs);
1190 buf->append(dtime_buff, my_datetime_to_str(&time, dtime_buff, 0));
1191 buf->append(STRING_WITH_LEN("'"));
1192 }
1193
1194
1195 /*
1196 Get SHOW CREATE EVENT as string
1197
1198 SYNOPSIS
1199 Event_timed::get_create_event(THD *thd, String *buf)
1200 thd Thread
1201 buf String*, should be already allocated. CREATE EVENT goes inside.
1202
1203 RETURN VALUE
1204 0 OK
1205 EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been
1206 tampered and MICROSECONDS interval or
1207 derivative has been put there.
1208 */
1209
1210 int
get_create_event(THD * thd,String * buf)1211 Event_timed::get_create_event(THD *thd, String *buf)
1212 {
1213 char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
1214 String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
1215 expr_buf.length(0);
1216
1217 DBUG_ENTER("get_create_event");
1218 DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]",
1219 (int) body.length, body.str));
1220
1221 if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
1222 expression))
1223 DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
1224
1225 buf->append(STRING_WITH_LEN("CREATE "));
1226 append_definer(thd, buf, definer_user, definer_host);
1227 buf->append(STRING_WITH_LEN("EVENT "));
1228 append_identifier(thd, buf, name.str, name.length);
1229
1230 if (expression)
1231 {
1232 buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
1233 buf->append(expr_buf);
1234 buf->append(' ');
1235 LEX_STRING *ival= &interval_type_to_name[interval];
1236 buf->append(ival->str, ival->length);
1237
1238 if (!starts_null)
1239 append_datetime(buf, time_zone, starts, STRING_WITH_LEN("STARTS"));
1240
1241 if (!ends_null)
1242 append_datetime(buf, time_zone, ends, STRING_WITH_LEN("ENDS"));
1243 }
1244 else
1245 {
1246 append_datetime(buf, time_zone, execute_at,
1247 STRING_WITH_LEN("ON SCHEDULE AT"));
1248 }
1249
1250 if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
1251 buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
1252 else
1253 buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));
1254
1255 if (status == Event_parse_data::ENABLED)
1256 buf->append(STRING_WITH_LEN("ENABLE"));
1257 else if (status == Event_parse_data::SLAVESIDE_DISABLED)
1258 buf->append(STRING_WITH_LEN("DISABLE ON SLAVE"));
1259 else
1260 buf->append(STRING_WITH_LEN("DISABLE"));
1261
1262 if (comment.length)
1263 {
1264 buf->append(STRING_WITH_LEN(" COMMENT "));
1265 append_unescaped(buf, comment.str, comment.length);
1266 }
1267 buf->append(STRING_WITH_LEN(" DO "));
1268 buf->append(body.str, body.length);
1269
1270 DBUG_RETURN(0);
1271 }
1272
1273
1274 /**
1275 Get an artificial stored procedure to parse as an event definition.
1276 */
1277
1278 bool
construct_sp_sql(THD * thd,String * sp_sql)1279 Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
1280 {
1281 LEX_STRING buffer;
1282 const uint STATIC_SQL_LENGTH= 44;
1283
1284 DBUG_ENTER("Event_job_data::construct_sp_sql");
1285
1286 /*
1287 Allocate a large enough buffer on the thread execution memory
1288 root to avoid multiple [re]allocations on system heap
1289 */
1290 buffer.length= STATIC_SQL_LENGTH + name.length + body.length;
1291 if (! (buffer.str= (char*) thd->alloc(buffer.length)))
1292 DBUG_RETURN(TRUE);
1293
1294 sp_sql->set(buffer.str, buffer.length, system_charset_info);
1295 sp_sql->length(0);
1296
1297
1298 sp_sql->append(C_STRING_WITH_LEN("CREATE "));
1299 sp_sql->append(C_STRING_WITH_LEN("PROCEDURE "));
1300 /*
1301 Let's use the same name as the event name to perhaps produce a
1302 better error message in case it is a part of some parse error.
1303 We're using append_identifier here to successfully parse
1304 events with reserved names.
1305 */
1306 append_identifier(thd, sp_sql, name.str, name.length);
1307
1308 /*
1309 The default SQL security of a stored procedure is DEFINER. We
1310 have already activated the security context of the event, so
1311 let's execute the procedure with the invoker rights to save on
1312 resets of security contexts.
1313 */
1314 sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER "));
1315
1316 sp_sql->append(body.str, body.length);
1317
1318 DBUG_RETURN(thd->is_fatal_error);
1319 }
1320
1321
1322 /**
1323 Compiles and executes the event (the underlying sp_head object)
1324
1325 @retval TRUE error (reported to the error log)
1326 @retval FALSE success
1327 */
1328
1329 bool
execute(THD * thd,bool drop)1330 Event_job_data::execute(THD *thd, bool drop)
1331 {
1332 String sp_sql;
1333 #ifndef NO_EMBEDDED_ACCESS_CHECKS
1334 Security_context event_sctx, *save_sctx= NULL;
1335 #endif
1336 List<Item> empty_item_list;
1337 bool ret= TRUE;
1338 sql_digest_state *parent_digest= thd->m_digest;
1339 PSI_statement_locker *parent_locker= thd->m_statement_psi;
1340
1341 DBUG_ENTER("Event_job_data::execute");
1342
1343 mysql_reset_thd_for_next_command(thd);
1344
1345 /*
1346 MySQL parser currently assumes that current database is either
1347 present in THD or all names in all statements are fully specified.
1348 And yet not fully specified names inside stored programs must be
1349 be supported, even if the current database is not set:
1350 CREATE PROCEDURE db1.p1() BEGIN CREATE TABLE t1; END//
1351 -- in this example t1 should be always created in db1 and the statement
1352 must parse even if there is no current database.
1353
1354 To support this feature and still address the parser limitation,
1355 we need to set the current database here.
1356 We don't have to call mysql_change_db, since the checks performed
1357 in it are unnecessary for the purpose of parsing, and
1358 mysql_change_db will be invoked anyway later, to activate the
1359 procedure database before it's executed.
1360 */
1361 thd->set_db(to_lex_cstring(dbname));
1362
1363 lex_start(thd);
1364
1365 #ifndef NO_EMBEDDED_ACCESS_CHECKS
1366 if (event_sctx.change_security_context(thd,
1367 definer_user, definer_host,
1368 &dbname, &save_sctx))
1369 {
1370 sql_print_error("Event Scheduler: "
1371 "[%s].[%s.%s] execution failed, "
1372 "failed to authenticate the user.",
1373 definer.str, dbname.str, name.str);
1374 goto end;
1375 }
1376 #endif
1377
1378 if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0))
1379 {
1380 /*
1381 This aspect of behavior is defined in the worklog,
1382 and this is how triggers work too: if TRIGGER
1383 privilege is revoked from trigger definer,
1384 triggers are not executed.
1385 */
1386 sql_print_error("Event Scheduler: "
1387 "[%s].[%s.%s] execution failed, "
1388 "user no longer has EVENT privilege.",
1389 definer.str, dbname.str, name.str);
1390 goto end;
1391 }
1392
1393 if (construct_sp_sql(thd, &sp_sql))
1394 goto end;
1395
1396 /*
1397 Set up global thread attributes to reflect the properties of
1398 this Event. We can simply reset these instead of usual
1399 backup/restore employed in stored programs since we know that
1400 this is a top level statement and the worker thread is
1401 allocated exclusively to execute this event.
1402 */
1403
1404 thd->variables.sql_mode= sql_mode;
1405 thd->variables.time_zone= time_zone;
1406
1407 thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
1408
1409 {
1410 Parser_state parser_state;
1411
1412 if (parser_state.init(thd, thd->query().str, thd->query().length))
1413 goto end;
1414
1415 thd->m_digest= NULL;
1416 thd->m_statement_psi= NULL;
1417 if (parse_sql(thd, & parser_state, creation_ctx))
1418 {
1419 sql_print_error("Event Scheduler: "
1420 "%serror during compilation of %s.%s",
1421 thd->is_fatal_error ? "fatal " : "",
1422 (const char *) dbname.str, (const char *) name.str);
1423 thd->m_digest= parent_digest;
1424 thd->m_statement_psi= parent_locker;
1425 goto end;
1426 }
1427 thd->m_digest= parent_digest;
1428 thd->m_statement_psi= parent_locker;
1429 }
1430
1431 {
1432 sp_head *sphead= thd->lex->sphead;
1433
1434 DBUG_ASSERT(sphead);
1435
1436 if (thd->enable_slow_log)
1437 sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
1438 sphead->m_flags|= sp_head::LOG_GENERAL_LOG;
1439
1440 sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode);
1441 sphead->set_creation_ctx(creation_ctx);
1442 sphead->optimize();
1443
1444 sphead->m_type= SP_TYPE_EVENT;
1445 #ifdef HAVE_PSI_SP_INTERFACE
1446 sphead->m_sp_share= MYSQL_GET_SP_SHARE(SP_TYPE_EVENT,
1447 dbname.str, dbname.length,
1448 name.str, name.length);
1449 #endif
1450
1451 ret= sphead->execute_procedure(thd, &empty_item_list);
1452 /*
1453 There is no pre-locking and therefore there should be no
1454 tables open and locked left after execute_procedure.
1455 */
1456 }
1457
1458 end:
1459 if (drop && !thd->is_fatal_error)
1460 {
1461 /*
1462 We must do it here since here we're under the right authentication
1463 ID of the event definer.
1464 */
1465 sql_print_information("Event Scheduler: Dropping %s.%s",
1466 (const char *) dbname.str, (const char *) name.str);
1467 /*
1468 Construct a query for the binary log, to ensure the event is dropped
1469 on the slave
1470 */
1471 if (construct_drop_event_sql(thd, &sp_sql, dbname, name))
1472 ret= 1;
1473 else
1474 {
1475 ulong saved_master_access;
1476
1477 thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
1478
1479 /*
1480 NOTE: even if we run in read-only mode, we should be able to lock
1481 the mysql.event table for writing. In order to achieve this, we
1482 should call mysql_lock_tables() under the super-user.
1483
1484 Same goes for transaction access mode.
1485 Temporarily reset it to read-write.
1486 */
1487
1488 saved_master_access= thd->security_context()->master_access();
1489 thd->security_context()->set_master_access(saved_master_access |
1490 SUPER_ACL);
1491 bool save_tx_read_only= thd->tx_read_only;
1492 thd->tx_read_only= false;
1493
1494 ret= Events::drop_event(thd, dbname, name, FALSE);
1495
1496 thd->tx_read_only= save_tx_read_only;
1497 thd->security_context()->set_master_access(saved_master_access);
1498 }
1499 }
1500 #ifndef NO_EMBEDDED_ACCESS_CHECKS
1501 if (save_sctx)
1502 event_sctx.restore_security_context(thd, save_sctx);
1503 #endif
1504 thd->lex->unit->cleanup(true);
1505 thd->end_statement();
1506 thd->cleanup_after_query();
1507 /* Avoid races with SHOW PROCESSLIST */
1508 thd->reset_query();
1509
1510 DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
1511
1512 DBUG_RETURN(ret);
1513 }
1514
1515 /**
1516 Get DROP EVENT statement to binlog the drop of ON COMPLETION NOT
1517 PRESERVE event.
1518 */
construct_drop_event_sql(THD * thd,String * sp_sql,const LEX_STRING & schema_name,const LEX_STRING & event_name)1519 bool construct_drop_event_sql(THD *thd, String *sp_sql,
1520 const LEX_STRING &schema_name,
1521 const LEX_STRING &event_name)
1522 {
1523 LEX_STRING buffer;
1524 const uint STATIC_SQL_LENGTH= 14;
1525 int ret= 0;
1526
1527 DBUG_ENTER("construct_drop_event_sql");
1528
1529 buffer.length= STATIC_SQL_LENGTH + event_name.length*2 +
1530 schema_name.length * 2;
1531 if (! (buffer.str= (char*) thd->alloc(buffer.length)))
1532 DBUG_RETURN(true);
1533
1534 sp_sql->set(buffer.str, buffer.length, system_charset_info);
1535 sp_sql->length(0);
1536
1537 ret|= sp_sql->append(C_STRING_WITH_LEN("DROP EVENT IF EXISTS"));
1538 append_identifier(thd, sp_sql, schema_name.str, schema_name.length);
1539 ret|= sp_sql->append('.');
1540 append_identifier(thd, sp_sql, event_name.str,
1541 event_name.length);
1542
1543 DBUG_RETURN(ret);
1544 }
1545
1546
1547 /*
1548 Checks whether two events are in the same schema
1549
1550 SYNOPSIS
1551 event_basic_db_equal()
1552 db Schema
1553 et Compare et->dbname to `db`
1554
1555 RETURN VALUE
1556 TRUE Equal
1557 FALSE Not equal
1558 */
1559
1560 bool
event_basic_db_equal(LEX_STRING db,Event_basic * et)1561 event_basic_db_equal(LEX_STRING db, Event_basic *et)
1562 {
1563 return !sortcmp_lex_string(et->dbname, db, system_charset_info);
1564 }
1565
1566
1567 /*
1568 Checks whether an event has equal `db` and `name`
1569
1570 SYNOPSIS
1571 event_basic_identifier_equal()
1572 db Schema
1573 name Name
1574 et The event object
1575
1576 RETURN VALUE
1577 TRUE Equal
1578 FALSE Not equal
1579 */
1580
1581 bool
event_basic_identifier_equal(LEX_STRING db,LEX_STRING name,Event_basic * b)1582 event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
1583 {
1584 return !sortcmp_lex_string(name, b->name, system_charset_info) &&
1585 !sortcmp_lex_string(db, b->dbname, system_charset_info);
1586 }
1587
1588 /**
1589 @} (End of group Event_Scheduler)
1590 */
1591