1 /*
2    Copyright (c) 2006, 2011, 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-1335  USA */
16 
17 #include "mariadb.h"
18 #include "sql_priv.h"
19 #include "unireg.h"
20 #include "sql_base.h"                           // close_thread_tables
21 #include "sql_parse.h"
22 #include "event_db_repository.h"
23 #include "key.h"                                // key_copy
24 #include "sql_db.h"                        // get_default_db_collation
25 #include "sql_time.h"                      // interval_type_to_name
26 #include "tztime.h"                             // struct Time_zone
27 #include "sql_acl.h" // SUPER_ACL, MYSQL_DB_FIELD_COUNT, mysql_db_table_fields
28 #include "records.h"          // init_read_record, end_read_record
29 #include "sp_head.h"
30 #include "event_data_objects.h"
31 #include "events.h"
32 #include "sql_show.h"
33 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
34 
35 /**
36   @addtogroup Event_Scheduler
37   @{
38 */
39 
40 static
41 const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
42 {
43   {
44     { STRING_WITH_LEN("db") },
45     { STRING_WITH_LEN("char(64)") },
46     { STRING_WITH_LEN("utf8") }
47   },
48   {
49     { STRING_WITH_LEN("name") },
50     { STRING_WITH_LEN("char(64)") },
51     { STRING_WITH_LEN("utf8") }
52   },
53   {
54     { STRING_WITH_LEN("body") },
55     { STRING_WITH_LEN("longblob") },
56     {NULL, 0}
57   },
58   {
59     { STRING_WITH_LEN("definer") },
60     { STRING_WITH_LEN("char(") },
61     { STRING_WITH_LEN("utf8") }
62   },
63   {
64     { STRING_WITH_LEN("execute_at") },
65     { STRING_WITH_LEN("datetime") },
66     {NULL, 0}
67   },
68   {
69     { STRING_WITH_LEN("interval_value") },
70     { STRING_WITH_LEN("int(11)") },
71     {NULL, 0}
72   },
73   {
74     { STRING_WITH_LEN("interval_field") },
75     { STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
76     "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
77     "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
78     "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
79     "'SECOND_MICROSECOND')") },
80     {NULL, 0}
81   },
82   {
83     { STRING_WITH_LEN("created") },
84     { STRING_WITH_LEN("timestamp") },
85     {NULL, 0}
86   },
87   {
88     { STRING_WITH_LEN("modified") },
89     { STRING_WITH_LEN("timestamp") },
90     {NULL, 0}
91   },
92   {
93     { STRING_WITH_LEN("last_executed") },
94     { STRING_WITH_LEN("datetime") },
95     {NULL, 0}
96   },
97   {
98     { STRING_WITH_LEN("starts") },
99     { STRING_WITH_LEN("datetime") },
100     {NULL, 0}
101   },
102   {
103     { STRING_WITH_LEN("ends") },
104     { STRING_WITH_LEN("datetime") },
105     {NULL, 0}
106   },
107   {
108     { STRING_WITH_LEN("status") },
109     { STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
110     {NULL, 0}
111   },
112   {
113     { STRING_WITH_LEN("on_completion") },
114     { STRING_WITH_LEN("enum('DROP','PRESERVE')") },
115     {NULL, 0}
116   },
117   {
118     { STRING_WITH_LEN("sql_mode") },
119     { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
120     "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
121     "'NO_UNSIGNED_SUBTRACTION',"
122     "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
123     "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
124     "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
125     "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
126     "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
127     "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
128     "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
129     {NULL, 0}
130   },
131   {
132     { STRING_WITH_LEN("comment") },
133     { STRING_WITH_LEN("char(64)") },
134     { STRING_WITH_LEN("utf8") }
135   },
136   {
137     { STRING_WITH_LEN("originator") },
138     { STRING_WITH_LEN("int(10)") },
139     {NULL, 0}
140   },
141   {
142     { STRING_WITH_LEN("time_zone") },
143     { STRING_WITH_LEN("char(64)") },
144     { STRING_WITH_LEN("latin1") }
145   },
146   {
147     { STRING_WITH_LEN("character_set_client") },
148     { STRING_WITH_LEN("char(32)") },
149     { STRING_WITH_LEN("utf8") }
150   },
151   {
152     { STRING_WITH_LEN("collation_connection") },
153     { STRING_WITH_LEN("char(32)") },
154     { STRING_WITH_LEN("utf8") }
155   },
156   {
157     { STRING_WITH_LEN("db_collation") },
158     { STRING_WITH_LEN("char(32)") },
159     { STRING_WITH_LEN("utf8") }
160   },
161   {
162     { STRING_WITH_LEN("body_utf8") },
163     { STRING_WITH_LEN("longblob") },
164     { NULL, 0 }
165   }
166 };
167 
168 static LEX_CSTRING MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") };
169 
170 static const TABLE_FIELD_DEF
171 event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
172 
173 /** In case of an error, a message is printed to the error log. */
174 static Table_check_intact_log_error table_intact;
175 
176 
177 /**
178   Puts some data common to CREATE and ALTER EVENT into a row.
179 
180   Used both when an event is created and when it is altered.
181 
182   @param   thd        THD
183   @param   table      The row to fill out
184   @param   et         Event's data
185   @param   sp         Event stored routine
186   @param   is_update  CREATE EVENT or ALTER EVENT
187 
188   @retval  FALSE success
189   @retval  TRUE error
190 */
191 
192 static bool
mysql_event_fill_row(THD * thd,TABLE * table,Event_parse_data * et,sp_head * sp,sql_mode_t sql_mode,my_bool is_update)193 mysql_event_fill_row(THD *thd,
194                      TABLE *table,
195                      Event_parse_data *et,
196                      sp_head *sp,
197                      sql_mode_t sql_mode,
198                      my_bool is_update)
199 {
200   CHARSET_INFO *scs= system_charset_info;
201   enum enum_events_table_field f_num;
202   Field **fields= table->field;
203   int rs= FALSE;
204 
205   DBUG_ENTER("mysql_event_fill_row");
206 
207   DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
208   DBUG_PRINT("info", ("name  =[%s]", et->name.str));
209 
210   DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT);
211 
212   if (table->s->fields < ET_FIELD_COUNT)
213   {
214     /*
215       Safety: this can only happen if someone started the server
216       and then altered mysql.event.
217     */
218     my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, MYF(0),
219              table->s->db.str, table->alias.c_ptr(),
220              (int) ET_FIELD_COUNT, table->s->fields);
221     DBUG_RETURN(TRUE);
222   }
223 
224   if (fields[f_num= ET_FIELD_DEFINER]->
225                               store(et->definer.str, et->definer.length, scs))
226     goto err_truncate;
227 
228   if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
229     goto err_truncate;
230 
231   if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
232     goto err_truncate;
233 
234   /* ON_COMPLETION field is NOT NULL thus not calling set_notnull()*/
235   rs|= fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
236 
237   /*
238     Set STATUS value unconditionally in case of CREATE EVENT.
239     For ALTER EVENT set it only if value of this field was changed.
240     Since STATUS field is NOT NULL call to set_notnull() is not needed.
241   */
242   if (!is_update || et->status_changed)
243     rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
244   rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
245 
246   if (!is_update)
247     rs|= fields[ET_FIELD_CREATED]->set_time();
248 
249   /*
250     Change the SQL_MODE only if body was present in an ALTER EVENT and of course
251     always during CREATE EVENT.
252   */
253   if (et->body_changed)
254   {
255     DBUG_ASSERT(sp->m_body.str);
256 
257     rs|= fields[ET_FIELD_SQL_MODE]->store((longlong)sql_mode, TRUE);
258 
259     if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
260                                             sp->m_body.length,
261                                             scs))
262     {
263       goto err_truncate;
264     }
265   }
266 
267   if (et->expression)
268   {
269     const String *tz_name= thd->variables.time_zone->get_name();
270     if (!is_update || !et->starts_null)
271     {
272       fields[ET_FIELD_TIME_ZONE]->set_notnull();
273       rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
274                                              tz_name->charset());
275     }
276 
277     fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
278     rs|= fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
279 
280     fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
281 
282     rs|= fields[ET_FIELD_TRANSIENT_INTERVAL]->
283                             store(interval_type_to_name[et->interval].str,
284                                   interval_type_to_name[et->interval].length,
285                                   scs);
286 
287     fields[ET_FIELD_EXECUTE_AT]->set_null();
288 
289     if (!et->starts_null)
290     {
291       MYSQL_TIME time;
292       my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->starts);
293 
294       fields[ET_FIELD_STARTS]->set_notnull();
295       fields[ET_FIELD_STARTS]->store_time(&time);
296     }
297 
298     if (!et->ends_null)
299     {
300       MYSQL_TIME time;
301       my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->ends);
302 
303       fields[ET_FIELD_ENDS]->set_notnull();
304       fields[ET_FIELD_ENDS]->store_time(&time);
305     }
306   }
307   else if (et->execute_at)
308   {
309     const String *tz_name= thd->variables.time_zone->get_name();
310     fields[ET_FIELD_TIME_ZONE]->set_notnull();
311     rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
312                                            tz_name->charset());
313 
314     fields[ET_FIELD_INTERVAL_EXPR]->set_null();
315     fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
316     fields[ET_FIELD_STARTS]->set_null();
317     fields[ET_FIELD_ENDS]->set_null();
318 
319     MYSQL_TIME time;
320     my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->execute_at);
321 
322     fields[ET_FIELD_EXECUTE_AT]->set_notnull();
323     fields[ET_FIELD_EXECUTE_AT]->store_time(&time);
324   }
325   else
326   {
327     DBUG_ASSERT(is_update);
328     /*
329       it is normal to be here when the action is update
330       this is an error if the action is create. something is borked
331     */
332   }
333 
334   rs|= fields[ET_FIELD_MODIFIED]->set_time();
335 
336   if (et->comment.str)
337   {
338     if (fields[f_num= ET_FIELD_COMMENT]->
339                           store(et->comment.str, et->comment.length, scs))
340       goto err_truncate;
341   }
342 
343   fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
344   rs|= fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
345     thd->variables.character_set_client->csname,
346     strlen(thd->variables.character_set_client->csname),
347     system_charset_info);
348 
349   fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull();
350   rs|= fields[ET_FIELD_COLLATION_CONNECTION]->store(
351     thd->variables.collation_connection->name,
352     strlen(thd->variables.collation_connection->name),
353     system_charset_info);
354 
355   {
356     CHARSET_INFO *db_cl= get_default_db_collation(thd, et->dbname.str);
357 
358     fields[ET_FIELD_DB_COLLATION]->set_notnull();
359     rs|= fields[ET_FIELD_DB_COLLATION]->store(db_cl->name,
360                                               strlen(db_cl->name),
361                                               system_charset_info);
362   }
363 
364   if (et->body_changed)
365   {
366     fields[ET_FIELD_BODY_UTF8]->set_notnull();
367     rs|= fields[ET_FIELD_BODY_UTF8]->store(sp->m_body_utf8.str,
368                                            sp->m_body_utf8.length,
369                                            system_charset_info);
370   }
371 
372   if (rs)
373   {
374     my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name.str, rs);
375     DBUG_RETURN(TRUE);
376   }
377 
378   DBUG_RETURN(FALSE);
379 
380 err_truncate:
381   my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name.str);
382   DBUG_RETURN(TRUE);
383 }
384 
385 
386 /*
387   Performs an index scan of event_table (mysql.event) and fills schema_table.
388 
389   SYNOPSIS
390     Event_db_repository::index_read_for_db_for_i_s()
391       thd          Thread
392       schema_table The I_S.EVENTS table
393       event_table  The event table to use for loading (mysql.event)
394       db           For which schema to do an index scan.
395 
396   RETURN VALUE
397     0  OK
398     1  Error
399 */
400 
401 bool
index_read_for_db_for_i_s(THD * thd,TABLE * schema_table,TABLE * event_table,const char * db)402 Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
403                                                TABLE *event_table,
404                                                const char *db)
405 {
406   CHARSET_INFO *scs= system_charset_info;
407   KEY *key_info;
408   uint key_len;
409   uchar *key_buf;
410   DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");
411 
412   DBUG_PRINT("info", ("Using prefix scanning on PK"));
413 
414   int ret= event_table->file->ha_index_init(0, 1);
415   if (ret)
416   {
417     event_table->file->print_error(ret, MYF(0));
418     DBUG_RETURN(true);
419   }
420 
421   key_info= event_table->key_info;
422 
423   if (key_info->user_defined_key_parts == 0 ||
424       key_info->key_part[0].field != event_table->field[ET_FIELD_DB])
425   {
426     /* Corrupted table: no index or index on a wrong column */
427     my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
428     ret= 1;
429     goto end;
430   }
431 
432   event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs);
433   key_len= key_info->key_part[0].store_length;
434 
435   if (!(key_buf= (uchar *)alloc_root(thd->mem_root, key_len)))
436   {
437     /* Don't send error, it would be done by sql_alloc_error_handler() */
438     ret= 1;
439     goto end;
440   }
441 
442   key_copy(key_buf, event_table->record[0], key_info, key_len);
443   if (!(ret= event_table->file->ha_index_read_map(event_table->record[0],
444                                                   key_buf,
445                                                   (key_part_map)1,
446                                                   HA_READ_KEY_EXACT)))
447   {
448     DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
449     do
450     {
451       ret= copy_event_to_schema_table(thd, schema_table, event_table);
452       if (ret == 0)
453         ret= event_table->file->ha_index_next_same(event_table->record[0],
454                                                    key_buf, key_len);
455     } while (ret == 0);
456   }
457   DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
458 
459   /*  ret is guaranteed to be != 0 */
460   if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
461     ret= 0;
462   else
463     event_table->file->print_error(ret, MYF(0));
464 
465 end:
466   event_table->file->ha_index_end();
467 
468   DBUG_RETURN(MY_TEST(ret));
469 }
470 
471 
472 /*
473   Performs a table scan of event_table (mysql.event) and fills schema_table.
474 
475   SYNOPSIS
476     Events_db_repository::table_scan_all_for_i_s()
477       thd          Thread
478       schema_table The I_S.EVENTS in memory table
479       event_table  The event table to use for loading.
480 
481   RETURN VALUE
482     FALSE  OK
483     TRUE   Error
484 */
485 
486 bool
table_scan_all_for_i_s(THD * thd,TABLE * schema_table,TABLE * event_table)487 Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
488                                             TABLE *event_table)
489 {
490   int ret;
491   READ_RECORD read_record_info;
492   DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
493 
494   if (init_read_record(&read_record_info, thd, event_table, NULL, NULL, 1, 0,
495                        FALSE))
496     DBUG_RETURN(TRUE);
497 
498   /*
499     rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
500     but rr_handle_error returns -1 for that reason. Thus, read_record()
501     returns -1 eventually.
502   */
503   do
504   {
505     ret= read_record_info.read_record();
506     if (ret == 0)
507       ret= copy_event_to_schema_table(thd, schema_table, event_table);
508   } while (ret == 0);
509 
510   DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
511   end_read_record(&read_record_info);
512 
513   /*  ret is guaranteed to be != 0 */
514   DBUG_RETURN(ret == -1? FALSE:TRUE);
515 }
516 
517 
518 /**
519   Fills I_S.EVENTS with data loaded from mysql.event. Also used by
520   SHOW EVENTS
521 
522   The reason we reset and backup open tables here is that this
523   function may be called from any query that accesses
524   INFORMATION_SCHEMA - including a query that is issued from
525   a pre-locked statement, one that already has open and locked
526   tables.
527 
528   @retval FALSE  success
529   @retval TRUE   error
530 */
531 
532 bool
fill_schema_events(THD * thd,TABLE_LIST * i_s_table,const char * db)533 Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
534                                         const char *db)
535 {
536   TABLE *schema_table= i_s_table->table;
537   Open_tables_backup open_tables_backup;
538   TABLE_LIST event_table;
539   int ret= 0;
540 
541   DBUG_ENTER("Event_db_repository::fill_schema_events");
542   DBUG_PRINT("info",("db=%s", db? db:"(null)"));
543 
544   event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
545 
546   if (open_system_tables_for_read(thd, &event_table, &open_tables_backup))
547     DBUG_RETURN(TRUE);
548 
549   if (table_intact.check(event_table.table, &event_table_def))
550   {
551     close_system_tables(thd, &open_tables_backup);
552     my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
553     DBUG_RETURN(TRUE);
554   }
555 
556   /*
557     1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
558                      thus we won't order it. OTOH, SHOW EVENTS will be
559                      ordered.
560     2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
561        Reasoning: Events are per schema, therefore a scan over an index
562                   will save use from doing a table scan and comparing
563                   every single row's `db` with the schema which we show.
564   */
565   if (db)
566     ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
567   else
568     ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
569 
570   close_system_tables(thd, &open_tables_backup);
571 
572   DBUG_PRINT("info", ("Return code=%d", ret));
573   DBUG_RETURN(ret);
574 }
575 
576 
577 /**
578   Open mysql.event table for read.
579 
580   It's assumed that the caller knows what they are doing:
581   - whether it was necessary to reset-and-backup the open tables state
582   - whether the requested lock does not lead to a deadlock
583   - whether this open mode would work under LOCK TABLES, or inside a
584   stored function or trigger.
585 
586   Note that if the table can't be locked successfully this operation will
587   close it. Therefore it provides guarantee that it either opens and locks
588   table or fails without leaving any tables open.
589 
590   @param[in]  thd  Thread context
591   @param[in]  lock_type  How to lock the table
592   @param[out] table  We will store the open table here
593 
594   @retval TRUE open and lock failed - an error message is pushed into the
595                stack
596   @retval FALSE success
597 */
598 
599 bool
open_event_table(THD * thd,enum thr_lock_type lock_type,TABLE ** table)600 Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
601                                       TABLE **table)
602 {
603   TABLE_LIST tables;
604   DBUG_ENTER("Event_db_repository::open_event_table");
605 
606   tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type);
607 
608   if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
609     DBUG_RETURN(TRUE);
610 
611   *table= tables.table;
612   tables.table->use_all_columns();
613 
614   if (table_intact.check(*table, &event_table_def))
615   {
616     close_thread_tables(thd);
617     my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
618     DBUG_RETURN(TRUE);
619   }
620 
621   DBUG_RETURN(FALSE);
622 }
623 
624 
625 /**
626   Creates an event record in mysql.event table.
627 
628   Creates an event. Relies on mysql_event_fill_row which is shared with
629   ::update_event.
630 
631   @pre All semantic checks must be performed outside. This function
632   only creates a record on disk.
633   @pre The thread handle has no open tables.
634 
635   @param[in,out] thd                   THD
636   @param[in]     parse_data            Parsed event definition
637   @param[in]     create_if_not         TRUE if IF NOT EXISTS clause was provided
638                                        to CREATE EVENT statement
639   @param[out]    event_already_exists  When method is completed successfully
640                                        set to true if event already exists else
641                                        set to false
642   @retval FALSE  success
643   @retval TRUE   error
644 */
645 
646 bool
create_event(THD * thd,Event_parse_data * parse_data,bool * event_already_exists)647 Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
648                                   bool *event_already_exists)
649 {
650   int ret= 1;
651   TABLE *table= NULL;
652   sp_head *sp= thd->lex->sphead;
653   sql_mode_t saved_mode= thd->variables.sql_mode;
654   /*
655     Take a savepoint to release only the lock on mysql.event
656     table at the end but keep the global read lock and
657     possible other locks taken by the caller.
658   */
659   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
660 
661   DBUG_ENTER("Event_db_repository::create_event");
662 
663   DBUG_PRINT("info", ("open mysql.event for update"));
664   DBUG_ASSERT(sp);
665 
666   /* Reset sql_mode during data dictionary operations. */
667   thd->variables.sql_mode= 0;
668 
669   if (open_event_table(thd, TL_WRITE, &table))
670     goto end;
671 
672   DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
673              parse_data->name.str));
674 
675   DBUG_PRINT("info", ("check existence of an event with the same name"));
676   if (!find_named_event(&parse_data->dbname, &parse_data->name, table))
677   {
678     if (thd->lex->create_info.or_replace())
679     {
680       *event_already_exists= false; // Force the caller to update event_queue
681       if ((ret= table->file->ha_delete_row(table->record[0])))
682       {
683         table->file->print_error(ret, MYF(0));
684         goto end;
685       }
686     }
687     else if (thd->lex->create_info.if_not_exists())
688     {
689       *event_already_exists= true;
690       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
691                           ER_EVENT_ALREADY_EXISTS,
692                           ER_THD(thd, ER_EVENT_ALREADY_EXISTS),
693                           parse_data->name.str);
694       ret= 0;
695       goto end;
696     }
697     else
698     {
699       my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
700       goto end;
701     }
702   } else
703     *event_already_exists= false;
704 
705   DBUG_PRINT("info", ("non-existent, go forward"));
706 
707   restore_record(table, s->default_values);     // Get default values for fields
708 
709   if (check_string_char_length(&parse_data->dbname, 0,
710                                table->field[ET_FIELD_DB]->char_length(),
711                                system_charset_info, 1))
712   {
713     my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
714     goto end;
715   }
716 
717   if (check_string_char_length(&parse_data->name, 0,
718                                table->field[ET_FIELD_NAME]->char_length(),
719                                system_charset_info, 1))
720   {
721     my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
722     goto end;
723   }
724 
725   if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
726   {
727     my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
728     goto end;
729   }
730 
731   /*
732     mysql_event_fill_row() calls my_error() in case of error so no need to
733     handle it here
734   */
735   if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
736     goto end;
737 
738   if ((ret= table->file->ha_write_row(table->record[0])))
739   {
740     table->file->print_error(ret, MYF(0));
741     goto end;
742   }
743   ret= 0;
744 
745 end:
746   close_thread_tables(thd);
747   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
748 
749   thd->variables.sql_mode= saved_mode;
750   DBUG_RETURN(MY_TEST(ret));
751 }
752 
753 
754 /**
755   Used to execute ALTER EVENT. Pendant to Events::update_event().
756 
757   @param[in,out]  thd         thread handle
758   @param[in]      parse_data  parsed event definition
759   @param[in]      new_dbname  not NULL if ALTER EVENT RENAME
760                               points at a new database name
761   @param[in]      new_name    not NULL if ALTER EVENT RENAME
762                               points at a new event name
763 
764   @pre All semantic checks are performed outside this function,
765   it only updates the event definition on disk.
766   @pre We don't have any tables open in the given thread.
767 
768   @retval FALSE success
769   @retval TRUE error (reported)
770 */
771 
772 bool
update_event(THD * thd,Event_parse_data * parse_data,LEX_CSTRING * new_dbname,LEX_CSTRING * new_name)773 Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
774                                   LEX_CSTRING *new_dbname,
775                                   LEX_CSTRING *new_name)
776 {
777   CHARSET_INFO *scs= system_charset_info;
778   TABLE *table= NULL;
779   sp_head *sp= thd->lex->sphead;
780   sql_mode_t saved_mode= thd->variables.sql_mode;
781   /*
782     Take a savepoint to release only the lock on mysql.event
783     table at the end but keep the global read lock and
784     possible other locks taken by the caller.
785   */
786   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
787   int ret= 1;
788 
789   DBUG_ENTER("Event_db_repository::update_event");
790 
791   /* None or both must be set */
792   DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
793 
794   /* Reset sql_mode during data dictionary operations. */
795   thd->variables.sql_mode= 0;
796 
797   if (open_event_table(thd, TL_WRITE, &table))
798     goto end;
799 
800   DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
801   DBUG_PRINT("info", ("name: %s", parse_data->name.str));
802   DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
803 
804   /* first look whether we overwrite */
805   if (new_name)
806   {
807     DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
808     if (!find_named_event(new_dbname, new_name, table))
809     {
810       my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
811       goto end;
812     }
813   }
814   /*
815     ...and then if there is such an event. Don't exchange the blocks
816     because you will get error 120 from table handler because new_name will
817     overwrite the key and SE will tell us that it cannot find the already found
818     row (copied into record[1] later
819   */
820   if (find_named_event(&parse_data->dbname, &parse_data->name, table))
821   {
822     my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
823     goto end;
824   }
825 
826   store_record(table,record[1]);
827 
828   /*
829     We check whether ALTER EVENT was given dates that are in the past.
830     However to know how to react, we need the ON COMPLETION type. The
831     check is deferred to this point because by now we have the previous
832     setting (from the event-table) to fall back on if nothing was specified
833     in the ALTER EVENT-statement.
834   */
835 
836   if (parse_data->check_dates(thd,
837                               (int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
838     goto end;
839 
840   /*
841     mysql_event_fill_row() calls my_error() in case of error so no need to
842     handle it here
843   */
844   if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
845     goto end;
846 
847   if (new_dbname)
848   {
849     table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs);
850     table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
851   }
852 
853   if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
854   {
855     table->file->print_error(ret, MYF(0));
856     goto end;
857   }
858   ret= 0;
859 
860 end:
861   close_thread_tables(thd);
862   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
863 
864   thd->variables.sql_mode= saved_mode;
865   DBUG_RETURN(MY_TEST(ret));
866 }
867 
868 
869 /**
870   Delete event record from mysql.event table.
871 
872   @param[in,out] thd            thread handle
873   @param[in]     db             Database name
874   @param[in]     name           Event name
875   @param[in]     drop_if_exists DROP IF EXISTS clause was specified.
876                                 If set, and the event does not exist,
877                                 the error is downgraded to a warning.
878 
879   @retval FALSE success
880   @retval TRUE error (reported)
881 */
882 
883 bool
drop_event(THD * thd,const LEX_CSTRING * db,const LEX_CSTRING * name,bool drop_if_exists)884 Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db,
885                                 const LEX_CSTRING *name,
886                                 bool drop_if_exists)
887 {
888   TABLE *table= NULL;
889   /*
890     Take a savepoint to release only the lock on mysql.event
891     table at the end but keep the global read lock and
892     possible other locks taken by the caller.
893   */
894   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
895   int ret= 1;
896 
897   DBUG_ENTER("Event_db_repository::drop_event");
898   DBUG_PRINT("enter", ("%s@%s", db->str, name->str));
899 
900   if (open_event_table(thd, TL_WRITE, &table))
901     goto end;
902 
903   if (!find_named_event(db, name, table))
904   {
905     if ((ret= table->file->ha_delete_row(table->record[0])))
906       table->file->print_error(ret, MYF(0));
907     goto end;
908   }
909 
910   /* Event not found */
911   if (!drop_if_exists)
912   {
913     my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
914     goto end;
915   }
916 
917   push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
918                       ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
919                       "Event", name->str);
920   ret= 0;
921 
922 end:
923   close_thread_tables(thd);
924   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
925 
926   DBUG_RETURN(MY_TEST(ret));
927 }
928 
929 
930 /**
931   Positions the internal pointer of `table` to the place where (db, name)
932   is stored.
933 
934   In case search succeeded, the table cursor points at the found row.
935 
936   @param[in]      db     database name
937   @param[in]      name   event name
938   @param[in,out]  table  mysql.event table
939 
940 
941   @retval FALSE  an event with such db/name key exists
942   @retval  TRUE   no record found or an error occurred.
943 */
944 
945 bool
find_named_event(const LEX_CSTRING * db,const LEX_CSTRING * name,TABLE * table)946 Event_db_repository::find_named_event(const LEX_CSTRING *db,
947                                       const LEX_CSTRING *name,
948                                       TABLE *table)
949 {
950   uchar key[MAX_KEY_LENGTH];
951   DBUG_ENTER("Event_db_repository::find_named_event");
952   DBUG_PRINT("enter", ("name: %.*s", (int) name->length, name->str));
953 
954   /*
955     Create key to find row. We have to use field->store() to be able to
956     handle VARCHAR and CHAR fields.
957     Assumption here is that the two first fields in the table are
958     'db' and 'name' and the first key is the primary key over the
959     same fields.
960   */
961   if (db->length > table->field[ET_FIELD_DB]->field_length ||
962       name->length > table->field[ET_FIELD_NAME]->field_length ||
963       table->s->keys == 0 ||
964       table->key_info[0].user_defined_key_parts != 2 ||
965       table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 ||
966       table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1)
967     DBUG_RETURN(TRUE);
968 
969   table->field[ET_FIELD_DB]->store(db->str, db->length, &my_charset_bin);
970   table->field[ET_FIELD_NAME]->store(name->str, name->length, &my_charset_bin);
971 
972   key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
973 
974   if (table->file->ha_index_read_idx_map(table->record[0], 0, key,
975                                          HA_WHOLE_KEY,
976                                          HA_READ_KEY_EXACT))
977   {
978     DBUG_PRINT("info", ("Row not found"));
979     DBUG_RETURN(TRUE);
980   }
981 
982   DBUG_PRINT("info", ("Row found!"));
983   DBUG_RETURN(FALSE);
984 }
985 
986 
987 /*
988   Drops all events in the selected database, from mysql.event.
989 
990   SYNOPSIS
991     Event_db_repository::drop_schema_events()
992       thd     Thread
993       schema  The database to clean from events
994 */
995 
996 void
drop_schema_events(THD * thd,const LEX_CSTRING * schema)997 Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
998 {
999   int ret= 0;
1000   TABLE *table= NULL;
1001   READ_RECORD read_record_info;
1002   enum enum_events_table_field field= ET_FIELD_DB;
1003   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
1004   DBUG_ENTER("Event_db_repository::drop_schema_events");
1005   DBUG_PRINT("enter", ("field: %d  schema: %s", field, schema->str));
1006 
1007   if (open_event_table(thd, TL_WRITE, &table))
1008     DBUG_VOID_RETURN;
1009 
1010   /* only enabled events are in memory, so we go now and delete the rest */
1011   if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE))
1012     goto end;
1013 
1014   while (!ret && !(read_record_info.read_record()))
1015   {
1016     char *et_field= get_field(thd->mem_root, table->field[field]);
1017 
1018     /* et_field may be NULL if the table is corrupted or out of memory */
1019     if (et_field)
1020     {
1021       LEX_CSTRING et_field_lex= { et_field, strlen(et_field) };
1022       DBUG_PRINT("info", ("Current event %s name=%s", et_field,
1023                           get_field(thd->mem_root,
1024                                     table->field[ET_FIELD_NAME])));
1025 
1026       if (!sortcmp_lex_string(&et_field_lex, schema, system_charset_info))
1027       {
1028         DBUG_PRINT("info", ("Dropping"));
1029         if ((ret= table->file->ha_delete_row(table->record[0])))
1030           table->file->print_error(ret, MYF(0));
1031       }
1032     }
1033   }
1034   end_read_record(&read_record_info);
1035 
1036 end:
1037   close_thread_tables(thd);
1038   /*
1039     Make sure to only release the MDL lock on mysql.event, not other
1040     metadata locks DROP DATABASE might have acquired.
1041   */
1042   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
1043 
1044   DBUG_VOID_RETURN;
1045 }
1046 
1047 
1048 /**
1049   Looks for a named event in mysql.event and then loads it from
1050   the table.
1051 
1052   @pre The given thread does not have open tables.
1053 
1054   @retval FALSE  success
1055   @retval TRUE   error
1056 */
1057 
1058 bool
load_named_event(THD * thd,const LEX_CSTRING * dbname,const LEX_CSTRING * name,Event_basic * etn)1059 Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
1060                                       const LEX_CSTRING *name,
1061                                       Event_basic *etn)
1062 {
1063   bool ret;
1064   ulonglong saved_mode= thd->variables.sql_mode;
1065   Open_tables_backup open_tables_backup;
1066   TABLE_LIST event_table;
1067 
1068   DBUG_ENTER("Event_db_repository::load_named_event");
1069   DBUG_PRINT("enter",("thd: %p  name: %*s", thd,
1070                       (int) name->length, name->str));
1071 
1072   event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
1073 
1074   /* Reset sql_mode during data dictionary operations. */
1075   thd->variables.sql_mode= 0;
1076 
1077   /*
1078     We don't use open_event_table() here to make sure that SHOW
1079     CREATE EVENT works properly in transactional context, and
1080     does not release transactional metadata locks when the
1081     event table is closed.
1082   */
1083   if (!(ret= open_system_tables_for_read(thd, &event_table, &open_tables_backup)))
1084   {
1085     if (table_intact.check(event_table.table, &event_table_def))
1086     {
1087       close_system_tables(thd, &open_tables_backup);
1088       my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
1089       DBUG_RETURN(TRUE);
1090     }
1091 
1092     if ((ret= find_named_event(dbname, name, event_table.table)))
1093       my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
1094     else if ((ret= etn->load_from_row(thd, event_table.table)))
1095       my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
1096 
1097     close_system_tables(thd, &open_tables_backup);
1098   }
1099 
1100   thd->variables.sql_mode= saved_mode;
1101   DBUG_RETURN(ret);
1102 }
1103 
1104 
1105 /**
1106   Update the event record in mysql.event table with a changed status
1107   and/or last execution time.
1108 
1109   @pre The thread handle does not have open tables.
1110 */
1111 
1112 bool
1113 Event_db_repository::
update_timing_fields_for_event(THD * thd,const LEX_CSTRING * event_db_name,const LEX_CSTRING * event_name,my_time_t last_executed,ulonglong status)1114 update_timing_fields_for_event(THD *thd,
1115                                const LEX_CSTRING *event_db_name,
1116                                const LEX_CSTRING *event_name,
1117                                my_time_t last_executed,
1118                                ulonglong status)
1119 {
1120   TABLE *table= NULL;
1121   Field **fields;
1122   int ret= 1;
1123   enum_binlog_format save_binlog_format;
1124   MYSQL_TIME time;
1125   DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
1126 
1127   /*
1128     Turn off row binlogging of event timing updates. These are not used
1129     for RBR of events replicated to the slave.
1130   */
1131   save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
1132 
1133   DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
1134 
1135   if (open_event_table(thd, TL_WRITE, &table))
1136     goto end;
1137 
1138   fields= table->field;
1139 
1140   if (find_named_event(event_db_name, event_name, table))
1141     goto end;
1142 
1143   store_record(table, record[1]);
1144 
1145   my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
1146   fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
1147   fields[ET_FIELD_LAST_EXECUTED]->store_time(&time);
1148 
1149   fields[ET_FIELD_STATUS]->set_notnull();
1150   fields[ET_FIELD_STATUS]->store(status, TRUE);
1151 
1152   if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
1153   {
1154     table->file->print_error(ret, MYF(0));
1155     goto end;
1156   }
1157 
1158   ret= 0;
1159 
1160 end:
1161   if (table)
1162     close_mysql_tables(thd);
1163 
1164   thd->restore_stmt_binlog_format(save_binlog_format);
1165 
1166   DBUG_RETURN(MY_TEST(ret));
1167 }
1168 
1169 
1170 /**
1171   Open mysql.db, mysql.user and mysql.event and check whether:
1172     - mysql.db exists and is up to date (or from a newer version of MySQL),
1173     - mysql.user has column Event_priv at an expected position,
1174     - mysql.event exists and is up to date (or from a newer version of
1175       MySQL)
1176 
1177   This function is called only when the server is started.
1178   @pre The passed in thread handle has no open tables.
1179 
1180   @retval FALSE  OK
1181   @retval TRUE   Error, an error message is output to the error log.
1182 */
1183 
1184 bool
check_system_tables(THD * thd)1185 Event_db_repository::check_system_tables(THD *thd)
1186 {
1187   TABLE_LIST tables;
1188   int ret= FALSE;
1189   DBUG_ENTER("Event_db_repository::check_system_tables");
1190   DBUG_PRINT("enter", ("thd: %p", thd));
1191 
1192   /* Check mysql.event */
1193   tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
1194 
1195   if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1196   {
1197     ret= 1;
1198     sql_print_error("Cannot open mysql.event");
1199   }
1200   else
1201   {
1202     if (table_intact.check(tables.table, &event_table_def))
1203       ret= 1;
1204     close_mysql_tables(thd);
1205   }
1206 
1207   DBUG_RETURN(MY_TEST(ret));
1208 }
1209 
1210 /**
1211   @} (End of group Event_Scheduler)
1212 */
1213