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