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-1335  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
SimplifyRedundantTransposeSimplifyRedundantTranspose32 
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),
getCanonicalizationPatterns(RewritePatternSet & results,MLIRContext * context)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 anything.  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(&ltime, TIME_NO_ZERO_DATE))
220     goto wrong_value;
221 
222   ltime_utc= TIME_to_timestamp(thd,&ltime,&not_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 tomorrow 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(&ltime, TIME_NO_ZERO_DATE))
382     goto wrong_value;
383 
384   ltime_utc= TIME_to_timestamp(thd, &ltime, &not_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 tomorrow 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(&ltime, TIME_NO_ZERO_DATE))
437     goto error_bad_params;
438 
439   ltime_utc= TIME_to_timestamp(thd, &ltime, &not_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