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-1301 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
40static
41const 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
168static LEX_CSTRING MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") };
169
170static const TABLE_FIELD_DEF
171event_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. */
174static 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
192static bool
193mysql_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
380err_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
401bool
402Event_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
465end:
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
486bool
487Event_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
532bool
533Event_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
599bool
600Event_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
646bool
647Event_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 existance 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
745end:
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
772bool
773Event_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
860end:
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
883bool
884Event_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
922end:
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
945bool
946Event_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
996void
997Event_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
1036end:
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
1058bool
1059Event_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
1112bool
1113Event_db_repository::
1114update_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
1160end:
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
1184bool
1185Event_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