1 | /* |
2 | Copyright (c) 2008, 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 "sp_head.h" |
21 | #include "event_parse_data.h" |
22 | #include "sql_time.h" // TIME_to_timestamp |
23 | |
24 | /* |
25 | Returns a new instance |
26 | |
27 | SYNOPSIS |
28 | Event_parse_data::new_instance() |
29 | |
30 | RETURN VALUE |
31 | Address or NULL in case of error |
32 | |
33 | NOTE |
34 | Created on THD's mem_root |
35 | */ |
36 | |
37 | Event_parse_data * |
38 | Event_parse_data::new_instance(THD *thd) |
39 | { |
40 | return new (thd->mem_root) Event_parse_data; |
41 | } |
42 | |
43 | |
44 | /* |
45 | Constructor |
46 | |
47 | SYNOPSIS |
48 | Event_parse_data::Event_parse_data() |
49 | */ |
50 | |
51 | Event_parse_data::Event_parse_data() |
52 | :on_completion(Event_parse_data::ON_COMPLETION_DEFAULT), |
53 | status(Event_parse_data::ENABLED), status_changed(false), |
54 | do_not_create(FALSE), body_changed(FALSE), |
55 | item_starts(NULL), item_ends(NULL), item_execute_at(NULL), |
56 | starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE), |
57 | item_expression(NULL), expression(0) |
58 | { |
59 | DBUG_ENTER("Event_parse_data::Event_parse_data" ); |
60 | |
61 | /* Actually in the parser STARTS is always set */ |
62 | starts= ends= execute_at= 0; |
63 | |
64 | comment.str= NULL; |
65 | comment.length= 0; |
66 | |
67 | DBUG_VOID_RETURN; |
68 | } |
69 | |
70 | |
71 | /* |
72 | Set a name of the event |
73 | |
74 | SYNOPSIS |
75 | Event_parse_data::init_name() |
76 | thd THD |
77 | spn the name extracted in the parser |
78 | */ |
79 | |
80 | void |
81 | Event_parse_data::init_name(THD *thd, sp_name *spn) |
82 | { |
83 | DBUG_ENTER("Event_parse_data::init_name" ); |
84 | |
85 | /* We have to copy strings to get them into the right memroot */ |
86 | dbname.length= spn->m_db.length; |
87 | dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length); |
88 | name.length= spn->m_name.length; |
89 | name.str= thd->strmake(spn->m_name.str, spn->m_name.length); |
90 | |
91 | DBUG_VOID_RETURN; |
92 | } |
93 | |
94 | |
95 | /* |
96 | This function is called on CREATE EVENT or ALTER EVENT. When either |
97 | ENDS or AT is in the past, we are trying to create an event that |
98 | will never be executed. If it has ON COMPLETION NOT PRESERVE |
99 | (default), then it would normally be dropped already, so on CREATE |
100 | EVENT we give a warning, and do not create anyting. On ALTER EVENT |
101 | we give a error, and do not change the event. |
102 | |
103 | If the event has ON COMPLETION PRESERVE, then we see if the event is |
104 | created or altered to the ENABLED (default) state. If so, then we |
105 | give a warning, and change the state to DISABLED. |
106 | |
107 | Otherwise it is a valid event in ON COMPLETION PRESERVE DISABLE |
108 | state. |
109 | */ |
110 | |
111 | void |
112 | Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) |
113 | { |
114 | if (ltime_utc >= thd->query_start()) |
115 | return; |
116 | |
117 | /* |
118 | We'll come back later when we have the real on_completion value |
119 | */ |
120 | if (on_completion == Event_parse_data::ON_COMPLETION_DEFAULT) |
121 | return; |
122 | |
123 | if (on_completion == Event_parse_data::ON_COMPLETION_DROP) |
124 | { |
125 | switch (thd->lex->sql_command) { |
126 | case SQLCOM_CREATE_EVENT: |
127 | push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, |
128 | ER_EVENT_CANNOT_CREATE_IN_THE_PAST, |
129 | ER_THD(thd, ER_EVENT_CANNOT_CREATE_IN_THE_PAST)); |
130 | break; |
131 | case SQLCOM_ALTER_EVENT: |
132 | my_error(ER_EVENT_CANNOT_ALTER_IN_THE_PAST, MYF(0)); |
133 | break; |
134 | default: |
135 | DBUG_ASSERT(0); |
136 | } |
137 | |
138 | do_not_create= TRUE; |
139 | } |
140 | else if (status == Event_parse_data::ENABLED) |
141 | { |
142 | status= Event_parse_data::DISABLED; |
143 | status_changed= true; |
144 | push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, |
145 | ER_EVENT_EXEC_TIME_IN_THE_PAST, |
146 | ER_THD(thd, ER_EVENT_EXEC_TIME_IN_THE_PAST)); |
147 | } |
148 | } |
149 | |
150 | |
151 | /* |
152 | Check time/dates in ALTER EVENT |
153 | |
154 | We check whether ALTER EVENT was given dates that are in the past. |
155 | However to know how to react, we need the ON COMPLETION type. Hence, |
156 | the check is deferred until we have the previous ON COMPLETION type |
157 | from the event-db to fall back on if nothing was specified in the |
158 | ALTER EVENT-statement. |
159 | |
160 | SYNOPSIS |
161 | Event_parse_data::check_dates() |
162 | thd Thread |
163 | on_completion ON COMPLETION value currently in event-db. |
164 | Will be overridden by value in ALTER EVENT if given. |
165 | |
166 | RETURN VALUE |
167 | TRUE an error occurred, do not ALTER |
168 | FALSE OK |
169 | */ |
170 | |
171 | bool |
172 | Event_parse_data::check_dates(THD *thd, int previous_on_completion) |
173 | { |
174 | if (on_completion == Event_parse_data::ON_COMPLETION_DEFAULT) |
175 | { |
176 | on_completion= previous_on_completion; |
177 | if (!ends_null) |
178 | check_if_in_the_past(thd, ends); |
179 | if (!execute_at_null) |
180 | check_if_in_the_past(thd, execute_at); |
181 | } |
182 | return do_not_create; |
183 | } |
184 | |
185 | |
186 | |
187 | /* |
188 | Sets time for execution for one-time event. |
189 | |
190 | SYNOPSIS |
191 | Event_parse_data::init_execute_at() |
192 | thd Thread |
193 | |
194 | RETURN VALUE |
195 | 0 OK |
196 | ER_WRONG_VALUE Wrong value for execute at (reported) |
197 | */ |
198 | |
199 | int |
200 | Event_parse_data::init_execute_at(THD *thd) |
201 | { |
202 | uint not_used; |
203 | MYSQL_TIME ltime; |
204 | my_time_t ltime_utc; |
205 | |
206 | DBUG_ENTER("Event_parse_data::init_execute_at" ); |
207 | |
208 | if (!item_execute_at) |
209 | DBUG_RETURN(0); |
210 | |
211 | if (item_execute_at->fix_fields(thd, &item_execute_at)) |
212 | goto wrong_value; |
213 | |
214 | /* no starts and/or ends in case of execute_at */ |
215 | DBUG_PRINT("info" , ("starts_null && ends_null should be 1 is %d" , |
216 | (starts_null && ends_null))); |
217 | DBUG_ASSERT(starts_null && ends_null); |
218 | |
219 | if (item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE)) |
220 | goto wrong_value; |
221 | |
222 | ltime_utc= TIME_to_timestamp(thd,<ime,¬_used); |
223 | if (!ltime_utc) |
224 | { |
225 | DBUG_PRINT("error" , ("Execute AT after year 2037" )); |
226 | goto wrong_value; |
227 | } |
228 | |
229 | check_if_in_the_past(thd, ltime_utc); |
230 | |
231 | execute_at_null= FALSE; |
232 | execute_at= ltime_utc; |
233 | DBUG_RETURN(0); |
234 | |
235 | wrong_value: |
236 | report_bad_value("AT" , item_execute_at); |
237 | DBUG_RETURN(ER_WRONG_VALUE); |
238 | } |
239 | |
240 | |
241 | /* |
242 | Sets time for execution of multi-time event.s |
243 | |
244 | SYNOPSIS |
245 | Event_parse_data::init_interval() |
246 | thd Thread |
247 | |
248 | RETURN VALUE |
249 | 0 OK |
250 | EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported) |
251 | ER_WRONG_VALUE Wrong value for interval (reported) |
252 | */ |
253 | |
254 | int |
255 | Event_parse_data::init_interval(THD *thd) |
256 | { |
257 | INTERVAL interval_tmp; |
258 | |
259 | DBUG_ENTER("Event_parse_data::init_interval" ); |
260 | if (!item_expression) |
261 | DBUG_RETURN(0); |
262 | |
263 | switch (interval) { |
264 | case INTERVAL_MINUTE_MICROSECOND: |
265 | case INTERVAL_HOUR_MICROSECOND: |
266 | case INTERVAL_DAY_MICROSECOND: |
267 | case INTERVAL_SECOND_MICROSECOND: |
268 | case INTERVAL_MICROSECOND: |
269 | my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND" ); |
270 | DBUG_RETURN(EVEX_BAD_PARAMS); |
271 | default: |
272 | break; |
273 | } |
274 | |
275 | if (item_expression->fix_fields(thd, &item_expression)) |
276 | goto wrong_value; |
277 | |
278 | if (get_interval_value(item_expression, interval, &interval_tmp)) |
279 | goto wrong_value; |
280 | |
281 | expression= 0; |
282 | |
283 | switch (interval) { |
284 | case INTERVAL_YEAR: |
285 | expression= interval_tmp.year; |
286 | break; |
287 | case INTERVAL_QUARTER: |
288 | case INTERVAL_MONTH: |
289 | expression= interval_tmp.month; |
290 | break; |
291 | case INTERVAL_WEEK: |
292 | case INTERVAL_DAY: |
293 | expression= interval_tmp.day; |
294 | break; |
295 | case INTERVAL_HOUR: |
296 | expression= interval_tmp.hour; |
297 | break; |
298 | case INTERVAL_MINUTE: |
299 | expression= interval_tmp.minute; |
300 | break; |
301 | case INTERVAL_SECOND: |
302 | expression= interval_tmp.second; |
303 | break; |
304 | case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM |
305 | expression= interval_tmp.year* 12 + interval_tmp.month; |
306 | break; |
307 | case INTERVAL_DAY_HOUR: |
308 | expression= interval_tmp.day* 24 + interval_tmp.hour; |
309 | break; |
310 | case INTERVAL_DAY_MINUTE: |
311 | expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 + |
312 | interval_tmp.minute; |
313 | break; |
314 | case INTERVAL_HOUR_SECOND: /* day is anyway 0 */ |
315 | case INTERVAL_DAY_SECOND: |
316 | /* DAY_SECOND having problems because of leap seconds? */ |
317 | expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 + |
318 | interval_tmp.minute)*60 |
319 | + interval_tmp.second; |
320 | break; |
321 | case INTERVAL_HOUR_MINUTE: |
322 | expression= interval_tmp.hour * 60 + interval_tmp.minute; |
323 | break; |
324 | case INTERVAL_MINUTE_SECOND: |
325 | expression= interval_tmp.minute * 60 + interval_tmp.second; |
326 | break; |
327 | case INTERVAL_LAST: |
328 | DBUG_ASSERT(0); |
329 | default: |
330 | ;/* these are the microsec stuff */ |
331 | } |
332 | if (interval_tmp.neg || expression == 0 || |
333 | expression > EVEX_MAX_INTERVAL_VALUE) |
334 | { |
335 | my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0)); |
336 | DBUG_RETURN(EVEX_BAD_PARAMS); |
337 | } |
338 | |
339 | DBUG_RETURN(0); |
340 | |
341 | wrong_value: |
342 | report_bad_value("INTERVAL" , item_expression); |
343 | DBUG_RETURN(ER_WRONG_VALUE); |
344 | } |
345 | |
346 | |
347 | /* |
348 | Sets STARTS. |
349 | |
350 | SYNOPSIS |
351 | Event_parse_data::init_starts() |
352 | expr how much? |
353 | |
354 | NOTES |
355 | Note that activation time is not execution time. |
356 | EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that |
357 | the event will be executed every 5 minutes but this will |
358 | start at the date shown above. Expressions are possible : |
359 | DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at |
360 | same time. |
361 | |
362 | RETURN VALUE |
363 | 0 OK |
364 | ER_WRONG_VALUE Starts before now |
365 | */ |
366 | |
367 | int |
368 | Event_parse_data::init_starts(THD *thd) |
369 | { |
370 | uint not_used; |
371 | MYSQL_TIME ltime; |
372 | my_time_t ltime_utc; |
373 | |
374 | DBUG_ENTER("Event_parse_data::init_starts" ); |
375 | if (!item_starts) |
376 | DBUG_RETURN(0); |
377 | |
378 | if (item_starts->fix_fields(thd, &item_starts)) |
379 | goto wrong_value; |
380 | |
381 | if (item_starts->get_date(<ime, TIME_NO_ZERO_DATE)) |
382 | goto wrong_value; |
383 | |
384 | ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); |
385 | if (!ltime_utc) |
386 | goto wrong_value; |
387 | |
388 | DBUG_PRINT("info" ,("now: %ld starts: %ld" , |
389 | (long) thd->query_start(), (long) ltime_utc)); |
390 | |
391 | starts_null= FALSE; |
392 | starts= ltime_utc; |
393 | DBUG_RETURN(0); |
394 | |
395 | wrong_value: |
396 | report_bad_value("STARTS" , item_starts); |
397 | DBUG_RETURN(ER_WRONG_VALUE); |
398 | } |
399 | |
400 | |
401 | /* |
402 | Sets ENDS (deactivation time). |
403 | |
404 | SYNOPSIS |
405 | Event_parse_data::init_ends() |
406 | thd THD |
407 | |
408 | NOTES |
409 | Note that activation time is not execution time. |
410 | EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that |
411 | the event will be executed every 5 minutes but this will |
412 | end at the date shown above. Expressions are possible : |
413 | DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at |
414 | same time. |
415 | |
416 | RETURN VALUE |
417 | 0 OK |
418 | EVEX_BAD_PARAMS Error (reported) |
419 | */ |
420 | |
421 | int |
422 | Event_parse_data::init_ends(THD *thd) |
423 | { |
424 | uint not_used; |
425 | MYSQL_TIME ltime; |
426 | my_time_t ltime_utc; |
427 | |
428 | DBUG_ENTER("Event_parse_data::init_ends" ); |
429 | if (!item_ends) |
430 | DBUG_RETURN(0); |
431 | |
432 | if (item_ends->fix_fields(thd, &item_ends)) |
433 | goto error_bad_params; |
434 | |
435 | DBUG_PRINT("info" , ("convert to TIME" )); |
436 | if (item_ends->get_date(<ime, TIME_NO_ZERO_DATE)) |
437 | goto error_bad_params; |
438 | |
439 | ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); |
440 | if (!ltime_utc) |
441 | goto error_bad_params; |
442 | |
443 | /* Check whether ends is after starts */ |
444 | DBUG_PRINT("info" , ("ENDS after STARTS?" )); |
445 | if (!starts_null && starts >= ltime_utc) |
446 | goto error_bad_params; |
447 | |
448 | check_if_in_the_past(thd, ltime_utc); |
449 | |
450 | ends_null= FALSE; |
451 | ends= ltime_utc; |
452 | DBUG_RETURN(0); |
453 | |
454 | error_bad_params: |
455 | my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0)); |
456 | DBUG_RETURN(EVEX_BAD_PARAMS); |
457 | } |
458 | |
459 | |
460 | /* |
461 | Prints an error message about invalid value. Internally used |
462 | during input data verification |
463 | |
464 | SYNOPSIS |
465 | Event_parse_data::report_bad_value() |
466 | item_name The name of the parameter |
467 | bad_item The parameter |
468 | */ |
469 | |
470 | void |
471 | Event_parse_data::report_bad_value(const char *item_name, Item *bad_item) |
472 | { |
473 | char buff[120]; |
474 | String str(buff,(uint32) sizeof(buff), system_charset_info); |
475 | String *str2= bad_item->fixed? bad_item->val_str(&str):NULL; |
476 | my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL" ); |
477 | } |
478 | |
479 | |
480 | /* |
481 | Checks for validity the data gathered during the parsing phase. |
482 | |
483 | SYNOPSIS |
484 | Event_parse_data::check_parse_data() |
485 | thd Thread |
486 | |
487 | RETURN VALUE |
488 | FALSE OK |
489 | TRUE Error (reported) |
490 | */ |
491 | |
492 | bool |
493 | Event_parse_data::check_parse_data(THD *thd) |
494 | { |
495 | bool ret; |
496 | DBUG_ENTER("Event_parse_data::check_parse_data" ); |
497 | DBUG_PRINT("info" , ("execute_at: %p expr=%p starts=%p ends=%p" , |
498 | item_execute_at, item_expression, |
499 | item_starts, item_ends)); |
500 | |
501 | init_name(thd, identifier); |
502 | |
503 | init_definer(thd); |
504 | |
505 | ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) || |
506 | init_ends(thd); |
507 | check_originator_id(thd); |
508 | DBUG_RETURN(ret); |
509 | } |
510 | |
511 | |
512 | /* |
513 | Inits definer (definer_user and definer_host) during parsing. |
514 | |
515 | SYNOPSIS |
516 | Event_parse_data::init_definer() |
517 | thd Thread |
518 | */ |
519 | |
520 | void |
521 | Event_parse_data::init_definer(THD *thd) |
522 | { |
523 | DBUG_ENTER("Event_parse_data::init_definer" ); |
524 | |
525 | DBUG_ASSERT(thd->lex->definer); |
526 | |
527 | const char *definer_user= thd->lex->definer->user.str; |
528 | const char *definer_host= thd->lex->definer->host.str; |
529 | size_t definer_user_len= thd->lex->definer->user.length; |
530 | size_t definer_host_len= thd->lex->definer->host.length; |
531 | char *tmp; |
532 | DBUG_PRINT("info" ,("init definer_user thd->mem_root: %p " |
533 | "definer_user: %p" , thd->mem_root, |
534 | definer_user)); |
535 | |
536 | /* + 1 for @ */ |
537 | DBUG_PRINT("info" ,("init definer as whole" )); |
538 | definer.length= definer_user_len + definer_host_len + 1; |
539 | definer.str= tmp= (char*) thd->alloc(definer.length + 1); |
540 | |
541 | DBUG_PRINT("info" ,("copy the user" )); |
542 | strmake(tmp, definer_user, definer_user_len); |
543 | tmp[definer_user_len]= '@'; |
544 | |
545 | DBUG_PRINT("info" ,("copy the host" )); |
546 | strmake(tmp + definer_user_len + 1, definer_host, definer_host_len); |
547 | DBUG_PRINT("info" ,("definer [%s] initted" , definer.str)); |
548 | |
549 | DBUG_VOID_RETURN; |
550 | } |
551 | |
552 | |
553 | /** |
554 | Set the originator id of the event to the server_id if executing on |
555 | the master or set to the server_id of the master if executing on |
556 | the slave. If executing on slave, also set status to SLAVESIDE_DISABLED. |
557 | |
558 | SYNOPSIS |
559 | Event_parse_data::check_originator_id() |
560 | */ |
561 | void Event_parse_data::check_originator_id(THD *thd) |
562 | { |
563 | /* Disable replicated events on slave. */ |
564 | if ((WSREP(thd) && IF_WSREP(thd->wsrep_applier, 0)) || |
565 | (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) || |
566 | (thd->system_thread == SYSTEM_THREAD_SLAVE_IO)) |
567 | { |
568 | DBUG_PRINT("info" , ("Invoked object status set to SLAVESIDE_DISABLED." )); |
569 | if ((status == Event_parse_data::ENABLED) || |
570 | (status == Event_parse_data::DISABLED)) |
571 | { |
572 | status= Event_parse_data::SLAVESIDE_DISABLED; |
573 | status_changed= true; |
574 | } |
575 | originator = thd->variables.server_id; |
576 | } |
577 | else |
578 | originator = global_system_variables.server_id; |
579 | } |
580 | |