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