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