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 "records.h"          // init_read_record, end_read_record
28 #include "sp_head.h"
29 #include "event_data_objects.h"
30 #include "events.h"
31 #include "sql_show.h"
32 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
33 #include "transaction.h"
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   TABLE_LIST event_table;
538   int ret= 0;
539   DBUG_ENTER("Event_db_repository::fill_schema_events");
540   DBUG_PRINT("info",("db=%s", db? db:"(null)"));
541 
542   start_new_trans new_trans(thd);
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))
547   {
548     new_trans.restore_old_transaction();
549     DBUG_RETURN(TRUE);
550   }
551 
552   if (table_intact.check(event_table.table, &event_table_def))
553   {
554     my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
555     ret= 1;
556     goto err;
557   }
558 
559   /*
560     1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
561                      thus we won't order it. OTOH, SHOW EVENTS will be
562                      ordered.
563     2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
564        Reasoning: Events are per schema, therefore a scan over an index
565                   will save use from doing a table scan and comparing
566                   every single row's `db` with the schema which we show.
567   */
568   if (db)
569     ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
570   else
571     ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
572 
573 err:
574   thd->commit_whole_transaction_and_close_tables();
575   new_trans.restore_old_transaction();
576 
577   DBUG_PRINT("info", ("Return code=%d", ret));
578   DBUG_RETURN(ret);
579 }
580 
581 
582 /**
583   Open mysql.event table for read.
584 
585   It's assumed that the caller knows what they are doing:
586   - whether it was necessary to reset-and-backup the open tables state
587   - whether the requested lock does not lead to a deadlock
588   - whether this open mode would work under LOCK TABLES, or inside a
589   stored function or trigger.
590 
591   Note that if the table can't be locked successfully this operation will
592   close it. Therefore it provides guarantee that it either opens and locks
593   table or fails without leaving any tables open.
594 
595   @param[in]  thd  Thread context
596   @param[in]  lock_type  How to lock the table
597   @param[out] table  We will store the open table here
598 
599   @retval TRUE open and lock failed - an error message is pushed into the
600                stack
601   @retval FALSE success
602 */
603 
604 bool
open_event_table(THD * thd,enum thr_lock_type lock_type,TABLE ** table)605 Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
606                                       TABLE **table)
607 {
608   TABLE_LIST tables;
609   DBUG_ENTER("Event_db_repository::open_event_table");
610 
611   tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type);
612 
613   if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
614     DBUG_RETURN(TRUE);
615 
616   *table= tables.table;
617   tables.table->use_all_columns();
618 
619   if (table_intact.check(*table, &event_table_def))
620   {
621     thd->commit_whole_transaction_and_close_tables();
622     *table= 0;                                  // Table is now closed
623     my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
624     DBUG_RETURN(TRUE);
625   }
626 
627   DBUG_RETURN(FALSE);
628 }
629 
630 
631 /**
632   Creates an event record in mysql.event table.
633 
634   Creates an event. Relies on mysql_event_fill_row which is shared with
635   ::update_event.
636 
637   @pre All semantic checks must be performed outside. This function
638   only creates a record on disk.
639   @pre The thread handle has no open tables.
640 
641   @param[in,out] thd                   THD
642   @param[in]     parse_data            Parsed event definition
643   @param[in]     create_if_not         TRUE if IF NOT EXISTS clause was provided
644                                        to CREATE EVENT statement
645   @param[out]    event_already_exists  When method is completed successfully
646                                        set to true if event already exists else
647                                        set to false
648   @retval FALSE  success
649   @retval TRUE   error
650 */
651 
652 bool
create_event(THD * thd,Event_parse_data * parse_data,bool * event_already_exists)653 Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
654                                   bool *event_already_exists)
655 {
656   int ret= 1;
657   TABLE *table= NULL;
658   sp_head *sp= thd->lex->sphead;
659   sql_mode_t saved_mode= thd->variables.sql_mode;
660   /*
661     Take a savepoint to release only the lock on mysql.event
662     table at the end but keep the global read lock and
663     possible other locks taken by the caller.
664   */
665   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
666 
667   DBUG_ENTER("Event_db_repository::create_event");
668 
669   DBUG_PRINT("info", ("open mysql.event for update"));
670   DBUG_ASSERT(sp);
671 
672   /* Reset sql_mode during data dictionary operations. */
673   thd->variables.sql_mode= 0;
674 
675   if (open_event_table(thd, TL_WRITE, &table))
676     goto end;
677 
678   DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
679              parse_data->name.str));
680 
681   DBUG_PRINT("info", ("check existence of an event with the same name"));
682   if (!find_named_event(&parse_data->dbname, &parse_data->name, table))
683   {
684     if (thd->lex->create_info.or_replace())
685     {
686       *event_already_exists= false; // Force the caller to update event_queue
687       if ((ret= table->file->ha_delete_row(table->record[0])))
688       {
689         table->file->print_error(ret, MYF(0));
690         goto end;
691       }
692     }
693     else if (thd->lex->create_info.if_not_exists())
694     {
695       *event_already_exists= true;
696       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
697                           ER_EVENT_ALREADY_EXISTS,
698                           ER_THD(thd, ER_EVENT_ALREADY_EXISTS),
699                           parse_data->name.str);
700       ret= 0;
701       goto end;
702     }
703     else
704     {
705       my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
706       goto end;
707     }
708   } else
709     *event_already_exists= false;
710 
711   DBUG_PRINT("info", ("non-existent, go forward"));
712 
713   restore_record(table, s->default_values);     // Get default values for fields
714 
715   if (check_string_char_length(&parse_data->dbname, 0,
716                                table->field[ET_FIELD_DB]->char_length(),
717                                system_charset_info, 1))
718   {
719     my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
720     goto end;
721   }
722 
723   if (check_string_char_length(&parse_data->name, 0,
724                                table->field[ET_FIELD_NAME]->char_length(),
725                                system_charset_info, 1))
726   {
727     my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
728     goto end;
729   }
730 
731   if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
732   {
733     my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
734     goto end;
735   }
736 
737   /*
738     mysql_event_fill_row() calls my_error() in case of error so no need to
739     handle it here
740   */
741   if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
742     goto end;
743 
744   if ((ret= table->file->ha_write_row(table->record[0])))
745   {
746     table->file->print_error(ret, MYF(0));
747     goto end;
748   }
749   ret= 0;
750 
751 end:
752   if (table)
753     thd->commit_whole_transaction_and_close_tables();
754   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
755 
756   thd->variables.sql_mode= saved_mode;
757   DBUG_RETURN(MY_TEST(ret));
758 }
759 
760 
761 /**
762   Used to execute ALTER EVENT. Pendant to Events::update_event().
763 
764   @param[in,out]  thd         thread handle
765   @param[in]      parse_data  parsed event definition
766   @param[in]      new_dbname  not NULL if ALTER EVENT RENAME
767                               points at a new database name
768   @param[in]      new_name    not NULL if ALTER EVENT RENAME
769                               points at a new event name
770 
771   @pre All semantic checks are performed outside this function,
772   it only updates the event definition on disk.
773   @pre We don't have any tables open in the given thread.
774 
775   @retval FALSE success
776   @retval TRUE error (reported)
777 */
778 
779 bool
update_event(THD * thd,Event_parse_data * parse_data,LEX_CSTRING * new_dbname,LEX_CSTRING * new_name)780 Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
781                                   LEX_CSTRING *new_dbname,
782                                   LEX_CSTRING *new_name)
783 {
784   CHARSET_INFO *scs= system_charset_info;
785   TABLE *table= NULL;
786   sp_head *sp= thd->lex->sphead;
787   sql_mode_t saved_mode= thd->variables.sql_mode;
788   /*
789     Take a savepoint to release only the lock on mysql.event
790     table at the end but keep the global read lock and
791     possible other locks taken by the caller.
792   */
793   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
794   int ret= 1;
795   DBUG_ENTER("Event_db_repository::update_event");
796 
797   /* None or both must be set */
798   DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
799 
800   /* Reset sql_mode during data dictionary operations. */
801   thd->variables.sql_mode= 0;
802 
803   if (open_event_table(thd, TL_WRITE, &table))
804     goto end;
805 
806   DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
807   DBUG_PRINT("info", ("name: %s", parse_data->name.str));
808   DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
809 
810   /* first look whether we overwrite */
811   if (new_name)
812   {
813     DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
814     if (!find_named_event(new_dbname, new_name, table))
815     {
816       my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
817       goto end;
818     }
819   }
820   /*
821     ...and then if there is such an event. Don't exchange the blocks
822     because you will get error 120 from table handler because new_name will
823     overwrite the key and SE will tell us that it cannot find the already found
824     row (copied into record[1] later
825   */
826   if (find_named_event(&parse_data->dbname, &parse_data->name, table))
827   {
828     my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
829     goto end;
830   }
831 
832   store_record(table,record[1]);
833 
834   /*
835     We check whether ALTER EVENT was given dates that are in the past.
836     However to know how to react, we need the ON COMPLETION type. The
837     check is deferred to this point because by now we have the previous
838     setting (from the event-table) to fall back on if nothing was specified
839     in the ALTER EVENT-statement.
840   */
841 
842   if (parse_data->check_dates(thd,
843                               (int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
844     goto end;
845 
846   /*
847     mysql_event_fill_row() calls my_error() in case of error so no need to
848     handle it here
849   */
850   if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
851     goto end;
852 
853   if (new_dbname)
854   {
855     table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs);
856     table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
857   }
858 
859   if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
860   {
861     table->file->print_error(ret, MYF(0));
862     goto end;
863   }
864   ret= 0;
865 
866 end:
867   if (table)
868     thd->commit_whole_transaction_and_close_tables();
869   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
870 
871   thd->variables.sql_mode= saved_mode;
872   DBUG_RETURN(MY_TEST(ret));
873 }
874 
875 
876 /**
877   Delete event record from mysql.event table.
878 
879   @param[in,out] thd            thread handle
880   @param[in]     db             Database name
881   @param[in]     name           Event name
882   @param[in]     drop_if_exists DROP IF EXISTS clause was specified.
883                                 If set, and the event does not exist,
884                                 the error is downgraded to a warning.
885 
886   @retval FALSE success
887   @retval TRUE error (reported)
888 */
889 
890 bool
drop_event(THD * thd,const LEX_CSTRING * db,const LEX_CSTRING * name,bool drop_if_exists)891 Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db,
892                                 const LEX_CSTRING *name,
893                                 bool drop_if_exists)
894 {
895   TABLE *table= NULL;
896   /*
897     Take a savepoint to release only the lock on mysql.event
898     table at the end but keep the global read lock and
899     possible other locks taken by the caller.
900   */
901   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
902   int ret= 1;
903 
904   DBUG_ENTER("Event_db_repository::drop_event");
905   DBUG_PRINT("enter", ("%s@%s", db->str, name->str));
906 
907   if (open_event_table(thd, TL_WRITE, &table))
908     goto end;
909 
910   if (!find_named_event(db, name, table))
911   {
912     if ((ret= table->file->ha_delete_row(table->record[0])))
913       table->file->print_error(ret, MYF(0));
914     goto end;
915   }
916 
917   /* Event not found */
918   if (!drop_if_exists)
919   {
920     my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
921     goto end;
922   }
923 
924   push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
925                       ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
926                       "Event", name->str);
927   ret= 0;
928 
929 end:
930   if (table)
931     thd->commit_whole_transaction_and_close_tables();
932   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
933 
934   DBUG_RETURN(MY_TEST(ret));
935 }
936 
937 
938 /**
939   Positions the internal pointer of `table` to the place where (db, name)
940   is stored.
941 
942   In case search succeeded, the table cursor points at the found row.
943 
944   @param[in]      db     database name
945   @param[in]      name   event name
946   @param[in,out]  table  mysql.event table
947 
948 
949   @retval FALSE  an event with such db/name key exists
950   @retval  TRUE   no record found or an error occurred.
951 */
952 
953 bool
find_named_event(const LEX_CSTRING * db,const LEX_CSTRING * name,TABLE * table)954 Event_db_repository::find_named_event(const LEX_CSTRING *db,
955                                       const LEX_CSTRING *name,
956                                       TABLE *table)
957 {
958   uchar key[MAX_KEY_LENGTH];
959   DBUG_ENTER("Event_db_repository::find_named_event");
960   DBUG_PRINT("enter", ("name: %.*s", (int) name->length, name->str));
961 
962   /*
963     Create key to find row. We have to use field->store() to be able to
964     handle VARCHAR and CHAR fields.
965     Assumption here is that the two first fields in the table are
966     'db' and 'name' and the first key is the primary key over the
967     same fields.
968   */
969   if (db->length > table->field[ET_FIELD_DB]->field_length ||
970       name->length > table->field[ET_FIELD_NAME]->field_length ||
971       table->s->keys == 0 ||
972       table->key_info[0].user_defined_key_parts != 2 ||
973       table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 ||
974       table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1)
975     DBUG_RETURN(TRUE);
976 
977   table->field[ET_FIELD_DB]->store(db->str, db->length, &my_charset_bin);
978   table->field[ET_FIELD_NAME]->store(name->str, name->length, &my_charset_bin);
979 
980   key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
981 
982   if (table->file->ha_index_read_idx_map(table->record[0], 0, key,
983                                          HA_WHOLE_KEY,
984                                          HA_READ_KEY_EXACT))
985   {
986     DBUG_PRINT("info", ("Row not found"));
987     DBUG_RETURN(TRUE);
988   }
989 
990   DBUG_PRINT("info", ("Row found!"));
991   DBUG_RETURN(FALSE);
992 }
993 
994 
995 /*
996   Drops all events in the selected database, from mysql.event.
997 
998   SYNOPSIS
999     Event_db_repository::drop_schema_events()
1000       thd     Thread
1001       schema  The database to clean from events
1002 */
1003 
1004 void
drop_schema_events(THD * thd,const LEX_CSTRING * schema)1005 Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
1006 {
1007   int ret= 0;
1008   TABLE *table= NULL;
1009   READ_RECORD read_record_info;
1010   enum enum_events_table_field field= ET_FIELD_DB;
1011   DBUG_ENTER("Event_db_repository::drop_schema_events");
1012   DBUG_PRINT("enter", ("field: %d  schema: %s", field, schema->str));
1013 
1014   start_new_trans new_trans(thd);
1015 
1016   if (open_event_table(thd, TL_WRITE, &table))
1017   {
1018     new_trans.restore_old_transaction();
1019     DBUG_VOID_RETURN;
1020   }
1021 
1022   /* only enabled events are in memory, so we go now and delete the rest */
1023   if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE))
1024     goto end;
1025 
1026   while (!ret && !(read_record_info.read_record()))
1027   {
1028     char *et_field= get_field(thd->mem_root, table->field[field]);
1029 
1030     /* et_field may be NULL if the table is corrupted or out of memory */
1031     if (et_field)
1032     {
1033       LEX_CSTRING et_field_lex= { et_field, strlen(et_field) };
1034       DBUG_PRINT("info", ("Current event %s name=%s", et_field,
1035                           get_field(thd->mem_root,
1036                                     table->field[ET_FIELD_NAME])));
1037 
1038       if (!sortcmp_lex_string(&et_field_lex, schema, system_charset_info))
1039       {
1040         DBUG_PRINT("info", ("Dropping"));
1041         if ((ret= table->file->ha_delete_row(table->record[0])))
1042           table->file->print_error(ret, MYF(0));
1043       }
1044     }
1045   }
1046   end_read_record(&read_record_info);
1047 
1048 end:
1049   thd->commit_whole_transaction_and_close_tables();
1050   new_trans.restore_old_transaction();
1051   DBUG_VOID_RETURN;
1052 }
1053 
1054 
1055 /**
1056   Looks for a named event in mysql.event and then loads it from
1057   the table.
1058 
1059   @pre The given thread does not have open tables.
1060 
1061   @retval FALSE  success
1062   @retval TRUE   error
1063 */
1064 
1065 bool
load_named_event(THD * thd,const LEX_CSTRING * dbname,const LEX_CSTRING * name,Event_basic * etn)1066 Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
1067                                       const LEX_CSTRING *name,
1068                                       Event_basic *etn)
1069 {
1070   bool ret;
1071   TABLE_LIST event_table;
1072   DBUG_ENTER("Event_db_repository::load_named_event");
1073   DBUG_PRINT("enter",("thd: %p  name: %*s", thd,
1074                       (int) name->length, name->str));
1075 
1076   start_new_trans new_trans(thd);
1077   /* Reset sql_mode during data dictionary operations. */
1078   Sql_mode_instant_set sms(thd, 0);
1079 
1080   event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
1081 
1082   /*
1083     We don't use open_event_table() here to make sure that SHOW
1084     CREATE EVENT works properly in transactional context, and
1085     does not release transactional metadata locks when the
1086     event table is closed.
1087   */
1088   if (!(ret= open_system_tables_for_read(thd, &event_table)))
1089   {
1090     if (table_intact.check(event_table.table, &event_table_def))
1091     {
1092       thd->commit_whole_transaction_and_close_tables();
1093       new_trans.restore_old_transaction();
1094       my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
1095       DBUG_RETURN(TRUE);
1096     }
1097 
1098     if ((ret= find_named_event(dbname, name, event_table.table)))
1099       my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
1100     else if ((ret= etn->load_from_row(thd, event_table.table)))
1101       my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
1102     thd->commit_whole_transaction_and_close_tables();
1103   }
1104   new_trans.restore_old_transaction();
1105 
1106   DBUG_RETURN(ret);
1107 }
1108 
1109 
1110 /**
1111   Update the event record in mysql.event table with a changed status
1112   and/or last execution time.
1113 
1114   @pre The thread handle does not have open tables.
1115 */
1116 
1117 bool
1118 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)1119 update_timing_fields_for_event(THD *thd,
1120                                const LEX_CSTRING *event_db_name,
1121                                const LEX_CSTRING *event_name,
1122                                my_time_t last_executed,
1123                                ulonglong status)
1124 {
1125   TABLE *table= NULL;
1126   Field **fields;
1127   int ret= 1;
1128   MYSQL_TIME time;
1129   DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
1130 
1131   DBUG_ASSERT(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY);
1132 
1133   /*
1134     Take a savepoint to release only the lock on mysql.event
1135     table at the end but keep the global read lock and
1136     possible other locks taken by the caller.
1137   */
1138   MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
1139   if (open_event_table(thd, TL_WRITE, &table))
1140     DBUG_RETURN(1);
1141 
1142   fields= table->field;
1143   /*
1144     Turn off row binlogging of event timing updates. These are not used
1145     for RBR of events replicated to the slave.
1146   */
1147   table->file->row_logging= 0;
1148 
1149   if (find_named_event(event_db_name, event_name, table))
1150     goto end;
1151 
1152   store_record(table, record[1]);
1153 
1154   my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
1155   fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
1156   fields[ET_FIELD_LAST_EXECUTED]->store_time(&time);
1157 
1158   fields[ET_FIELD_STATUS]->set_notnull();
1159   fields[ET_FIELD_STATUS]->store(status, TRUE);
1160 
1161   if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
1162   {
1163     table->file->print_error(ret, MYF(0));
1164     goto end;
1165   }
1166 
1167   ret= 0;
1168 end:
1169   if (thd->commit_whole_transaction_and_close_tables())
1170     ret= 1;
1171   thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
1172 
1173   DBUG_RETURN(MY_TEST(ret));
1174 }
1175 
1176 
1177 /**
1178   Open mysql.db, mysql.user and mysql.event and check whether:
1179     - mysql.db exists and is up to date (or from a newer version of MySQL),
1180     - mysql.user has column Event_priv at an expected position,
1181     - mysql.event exists and is up to date (or from a newer version of
1182       MySQL)
1183 
1184   This function is called only when the server is started.
1185   @pre The passed in thread handle has no open tables.
1186 
1187   @retval FALSE  OK
1188   @retval TRUE   Error, an error message is output to the error log.
1189 */
1190 
1191 bool
check_system_tables(THD * thd)1192 Event_db_repository::check_system_tables(THD *thd)
1193 {
1194   TABLE_LIST tables;
1195   int ret= FALSE;
1196   DBUG_ENTER("Event_db_repository::check_system_tables");
1197   DBUG_PRINT("enter", ("thd: %p", thd));
1198 
1199   /* Check mysql.event */
1200   tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
1201 
1202   if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1203   {
1204     ret= 1;
1205     sql_print_error("Cannot open mysql.event");
1206   }
1207   else
1208   {
1209     if (table_intact.check(tables.table, &event_table_def))
1210       ret= 1;
1211     close_mysql_tables(thd);
1212   }
1213 
1214   DBUG_RETURN(MY_TEST(ret));
1215 }
1216 
1217 /**
1218   @} (End of group Event_Scheduler)
1219 */
1220