diff options
Diffstat (limited to 'sql/event_parse_data.cc')
-rw-r--r-- | sql/event_parse_data.cc | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc new file mode 100644 index 00000000..80d2f9c9 --- /dev/null +++ b/sql/event_parse_data.cc @@ -0,0 +1,587 @@ +/* + Copyright (c) 2008, 2011, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "mariadb.h" +#include "sql_priv.h" +#include "unireg.h" +#include "sp_head.h" +#include "event_parse_data.h" +#include "sql_time.h" // TIME_to_timestamp + +/* + Returns a new instance + + SYNOPSIS + Event_parse_data::new_instance() + + RETURN VALUE + Address or NULL in case of error + + NOTE + Created on THD's mem_root +*/ + +Event_parse_data * +Event_parse_data::new_instance(THD *thd) +{ + return new (thd->mem_root) Event_parse_data; +} + + +/* + Constructor + + SYNOPSIS + Event_parse_data::Event_parse_data() +*/ + +Event_parse_data::Event_parse_data() + :on_completion(Event_parse_data::ON_COMPLETION_DEFAULT), + status(Event_parse_data::ENABLED), status_changed(false), + do_not_create(FALSE), body_changed(FALSE), + item_starts(NULL), item_ends(NULL), item_execute_at(NULL), + starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE), + item_expression(NULL), expression(0) +{ + DBUG_ENTER("Event_parse_data::Event_parse_data"); + + /* Actually in the parser STARTS is always set */ + starts= ends= execute_at= 0; + + comment.str= NULL; + comment.length= 0; + + DBUG_VOID_RETURN; +} + + +/* + Set a name of the event + + SYNOPSIS + Event_parse_data::init_name() + thd THD + spn the name extracted in the parser +*/ + +void +Event_parse_data::init_name(THD *thd, sp_name *spn) +{ + DBUG_ENTER("Event_parse_data::init_name"); + + /* We have to copy strings to get them into the right memroot */ + dbname.length= spn->m_db.length; + dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length); + name.length= spn->m_name.length; + name.str= thd->strmake(spn->m_name.str, spn->m_name.length); + + DBUG_VOID_RETURN; +} + + +/* + This function is called on CREATE EVENT or ALTER EVENT. When either + ENDS or AT is in the past, we are trying to create an event that + will never be executed. If it has ON COMPLETION NOT PRESERVE + (default), then it would normally be dropped already, so on CREATE + EVENT we give a warning, and do not create anything. On ALTER EVENT + we give a error, and do not change the event. + + If the event has ON COMPLETION PRESERVE, then we see if the event is + created or altered to the ENABLED (default) state. If so, then we + give a warning, and change the state to DISABLED. + + Otherwise it is a valid event in ON COMPLETION PRESERVE DISABLE + state. +*/ + +void +Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) +{ + if (ltime_utc >= thd->query_start()) + return; + + /* + We'll come back later when we have the real on_completion value + */ + if (on_completion == Event_parse_data::ON_COMPLETION_DEFAULT) + return; + + if (on_completion == Event_parse_data::ON_COMPLETION_DROP) + { + switch (thd->lex->sql_command) { + case SQLCOM_CREATE_EVENT: + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_EVENT_CANNOT_CREATE_IN_THE_PAST, + ER_THD(thd, ER_EVENT_CANNOT_CREATE_IN_THE_PAST)); + break; + case SQLCOM_ALTER_EVENT: + my_error(ER_EVENT_CANNOT_ALTER_IN_THE_PAST, MYF(0)); + break; + default: + DBUG_ASSERT(0); + } + + do_not_create= TRUE; + } + else if (status == Event_parse_data::ENABLED) + { + status= Event_parse_data::DISABLED; + status_changed= true; + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_EVENT_EXEC_TIME_IN_THE_PAST, + ER_THD(thd, ER_EVENT_EXEC_TIME_IN_THE_PAST)); + } +} + + +/* + Check time/dates in ALTER EVENT + + We check whether ALTER EVENT was given dates that are in the past. + However to know how to react, we need the ON COMPLETION type. Hence, + the check is deferred until we have the previous ON COMPLETION type + from the event-db to fall back on if nothing was specified in the + ALTER EVENT-statement. + + SYNOPSIS + Event_parse_data::check_dates() + thd Thread + on_completion ON COMPLETION value currently in event-db. + Will be overridden by value in ALTER EVENT if given. + + RETURN VALUE + TRUE an error occurred, do not ALTER + FALSE OK +*/ + +bool +Event_parse_data::check_dates(THD *thd, int previous_on_completion) +{ + if (on_completion == Event_parse_data::ON_COMPLETION_DEFAULT) + { + on_completion= previous_on_completion; + if (!ends_null) + check_if_in_the_past(thd, ends); + if (!execute_at_null) + check_if_in_the_past(thd, execute_at); + } + return do_not_create; +} + + + +/* + Sets time for execution for one-time event. + + SYNOPSIS + Event_parse_data::init_execute_at() + thd Thread + + RETURN VALUE + 0 OK + ER_WRONG_VALUE Wrong value for execute at (reported) +*/ + +int +Event_parse_data::init_execute_at(THD *thd) +{ + uint not_used; + MYSQL_TIME ltime; + my_time_t ltime_utc; + + DBUG_ENTER("Event_parse_data::init_execute_at"); + + if (!item_execute_at) + DBUG_RETURN(0); + + if (item_execute_at->fix_fields(thd, &item_execute_at)) + goto wrong_value; + + /* no starts and/or ends in case of execute_at */ + DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d", + (starts_null && ends_null))); + DBUG_ASSERT(starts_null && ends_null); + + /* + The expected data type is DATETIME. No nanoseconds truncation should + normally be needed. Using the default rounding mode. + See more comments in event_data_object.cc. + */ + if (item_execute_at->get_date(thd, <ime, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) + goto wrong_value; + + ltime_utc= TIME_to_timestamp(thd,<ime,¬_used); + if (!ltime_utc) + { + DBUG_PRINT("error", ("Execute AT after year 2037")); + goto wrong_value; + } + + check_if_in_the_past(thd, ltime_utc); + + execute_at_null= FALSE; + execute_at= ltime_utc; + DBUG_RETURN(0); + +wrong_value: + report_bad_value("AT", item_execute_at); + DBUG_RETURN(ER_WRONG_VALUE); +} + + +/* + Sets time for execution of multi-time event.s + + SYNOPSIS + Event_parse_data::init_interval() + thd Thread + + RETURN VALUE + 0 OK + EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported) + ER_WRONG_VALUE Wrong value for interval (reported) +*/ + +int +Event_parse_data::init_interval(THD *thd) +{ + INTERVAL interval_tmp; + + DBUG_ENTER("Event_parse_data::init_interval"); + if (!item_expression) + DBUG_RETURN(0); + + switch (interval) { + case INTERVAL_MINUTE_MICROSECOND: + case INTERVAL_HOUR_MICROSECOND: + case INTERVAL_DAY_MICROSECOND: + case INTERVAL_SECOND_MICROSECOND: + case INTERVAL_MICROSECOND: + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND"); + DBUG_RETURN(EVEX_BAD_PARAMS); + default: + break; + } + + if (item_expression->fix_fields(thd, &item_expression)) + goto wrong_value; + + if (get_interval_value(thd, item_expression, interval, &interval_tmp)) + goto wrong_value; + + expression= 0; + + switch (interval) { + case INTERVAL_YEAR: + expression= interval_tmp.year; + break; + case INTERVAL_QUARTER: + case INTERVAL_MONTH: + expression= interval_tmp.month; + break; + case INTERVAL_WEEK: + case INTERVAL_DAY: + expression= interval_tmp.day; + break; + case INTERVAL_HOUR: + expression= interval_tmp.hour; + break; + case INTERVAL_MINUTE: + expression= interval_tmp.minute; + break; + case INTERVAL_SECOND: + expression= interval_tmp.second; + break; + case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM + expression= interval_tmp.year* 12 + interval_tmp.month; + break; + case INTERVAL_DAY_HOUR: + expression= interval_tmp.day* 24 + interval_tmp.hour; + break; + case INTERVAL_DAY_MINUTE: + expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 + + interval_tmp.minute; + break; + case INTERVAL_HOUR_SECOND: /* day is anyway 0 */ + case INTERVAL_DAY_SECOND: + /* DAY_SECOND having problems because of leap seconds? */ + expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 + + interval_tmp.minute)*60 + + interval_tmp.second; + break; + case INTERVAL_HOUR_MINUTE: + expression= interval_tmp.hour * 60 + interval_tmp.minute; + break; + case INTERVAL_MINUTE_SECOND: + expression= interval_tmp.minute * 60 + interval_tmp.second; + break; + case INTERVAL_LAST: + DBUG_ASSERT(0); + default: + ;/* these are the microsec stuff */ + } + if (interval_tmp.neg || expression == 0 || + expression > EVEX_MAX_INTERVAL_VALUE) + { + my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0)); + DBUG_RETURN(EVEX_BAD_PARAMS); + } + + DBUG_RETURN(0); + +wrong_value: + report_bad_value("INTERVAL", item_expression); + DBUG_RETURN(ER_WRONG_VALUE); +} + + +/* + Sets STARTS. + + SYNOPSIS + Event_parse_data::init_starts() + expr how much? + + NOTES + Note that activation time is not execution time. + EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that + the event will be executed every 5 minutes but this will + start at the date shown above. Expressions are possible : + DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tomorrow at + same time. + + RETURN VALUE + 0 OK + ER_WRONG_VALUE Starts before now +*/ + +int +Event_parse_data::init_starts(THD *thd) +{ + uint not_used; + MYSQL_TIME ltime; + my_time_t ltime_utc; + + DBUG_ENTER("Event_parse_data::init_starts"); + if (!item_starts) + DBUG_RETURN(0); + + if (item_starts->fix_fields(thd, &item_starts)) + goto wrong_value; + + if (item_starts->get_date(thd, <ime, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) + goto wrong_value; + + ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); + if (!ltime_utc) + goto wrong_value; + + DBUG_PRINT("info",("now: %ld starts: %ld", + (long) thd->query_start(), (long) ltime_utc)); + + starts_null= FALSE; + starts= ltime_utc; + DBUG_RETURN(0); + +wrong_value: + report_bad_value("STARTS", item_starts); + DBUG_RETURN(ER_WRONG_VALUE); +} + + +/* + Sets ENDS (deactivation time). + + SYNOPSIS + Event_parse_data::init_ends() + thd THD + + NOTES + Note that activation time is not execution time. + EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that + the event will be executed every 5 minutes but this will + end at the date shown above. Expressions are possible : + DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tomorrow at + same time. + + RETURN VALUE + 0 OK + EVEX_BAD_PARAMS Error (reported) +*/ + +int +Event_parse_data::init_ends(THD *thd) +{ + uint not_used; + MYSQL_TIME ltime; + my_time_t ltime_utc; + + DBUG_ENTER("Event_parse_data::init_ends"); + if (!item_ends) + DBUG_RETURN(0); + + if (item_ends->fix_fields(thd, &item_ends)) + goto error_bad_params; + + DBUG_PRINT("info", ("convert to TIME")); + if (item_ends->get_date(thd, <ime, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) + goto error_bad_params; + + ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); + if (!ltime_utc) + goto error_bad_params; + + /* Check whether ends is after starts */ + DBUG_PRINT("info", ("ENDS after STARTS?")); + if (!starts_null && starts >= ltime_utc) + goto error_bad_params; + + check_if_in_the_past(thd, ltime_utc); + + ends_null= FALSE; + ends= ltime_utc; + DBUG_RETURN(0); + +error_bad_params: + my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0)); + DBUG_RETURN(EVEX_BAD_PARAMS); +} + + +/* + Prints an error message about invalid value. Internally used + during input data verification + + SYNOPSIS + Event_parse_data::report_bad_value() + item_name The name of the parameter + bad_item The parameter +*/ + +void +Event_parse_data::report_bad_value(const char *item_name, Item *bad_item) +{ + char buff[120]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + String *str2= bad_item->fixed() ? bad_item->val_str(&str) : NULL; + my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL"); +} + + +/* + Checks for validity the data gathered during the parsing phase. + + SYNOPSIS + Event_parse_data::check_parse_data() + thd Thread + + RETURN VALUE + FALSE OK + TRUE Error (reported) +*/ + +bool +Event_parse_data::check_parse_data(THD *thd) +{ + bool ret; + DBUG_ENTER("Event_parse_data::check_parse_data"); + DBUG_PRINT("info", ("execute_at: %p expr=%p starts=%p ends=%p", + item_execute_at, item_expression, + item_starts, item_ends)); + + init_name(thd, identifier); + + init_definer(thd); + + ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) || + init_ends(thd); + check_originator_id(thd); + DBUG_RETURN(ret); +} + + +/* + Inits definer (definer_user and definer_host) during parsing. + + SYNOPSIS + Event_parse_data::init_definer() + thd Thread +*/ + +void +Event_parse_data::init_definer(THD *thd) +{ + DBUG_ENTER("Event_parse_data::init_definer"); + + DBUG_ASSERT(thd->lex->definer); + + const char *definer_user= thd->lex->definer->user.str; + const char *definer_host= thd->lex->definer->host.str; + size_t definer_user_len= thd->lex->definer->user.length; + size_t definer_host_len= thd->lex->definer->host.length; + char *tmp; + DBUG_PRINT("info",("init definer_user thd->mem_root: %p " + "definer_user: %p", thd->mem_root, + definer_user)); + + /* + 1 for @ */ + DBUG_PRINT("info",("init definer as whole")); + definer.length= definer_user_len + definer_host_len + 1; + definer.str= tmp= (char*) thd->alloc(definer.length + 1); + + DBUG_PRINT("info",("copy the user")); + strmake(tmp, definer_user, definer_user_len); + tmp[definer_user_len]= '@'; + + DBUG_PRINT("info",("copy the host")); + strmake(tmp + definer_user_len + 1, definer_host, definer_host_len); + DBUG_PRINT("info",("definer [%s] initted", definer.str)); + + DBUG_VOID_RETURN; +} + + +/** + Set the originator id of the event to the server_id if executing on + the master or set to the server_id of the master if executing on + the slave. If executing on slave, also set status to SLAVESIDE_DISABLED. + + SYNOPSIS + Event_parse_data::check_originator_id() +*/ +void Event_parse_data::check_originator_id(THD *thd) +{ + /* Disable replicated events on slave. */ + if ((WSREP(thd) && IF_WSREP(thd->wsrep_applier, 0)) || + (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) || + (thd->system_thread == SYSTEM_THREAD_SLAVE_IO)) + { + DBUG_PRINT("info", ("Invoked object status set to SLAVESIDE_DISABLED.")); + if ((status == Event_parse_data::ENABLED) || + (status == Event_parse_data::DISABLED)) + { + status= Event_parse_data::SLAVESIDE_DISABLED; + status_changed= true; + } + originator = thd->variables.server_id; + } + else + originator = global_system_variables.server_id; +} |