diff --git a/mysql-test/r/events_2.result b/mysql-test/r/events_2.result index 64d643505c5..db503f7aa6d 100644 --- a/mysql-test/r/events_2.result +++ b/mysql-test/r/events_2.result @@ -328,4 +328,81 @@ create event очень_очень_очень_очень_очень_очень_очень_очень_длинная_строка_66 on schedule every 2 year do select 1; ERROR 42000: Identifier name 'очень_очень_очень_очень_очень_очень_очень_очень_длинна' is too long +create event event_35981 on schedule every 6 month on completion preserve +disable +do +select 1; +The following SELECTs should all give 1 +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and +on_completion = 'PRESERVE'; +count(*) +1 +alter event event_35981 enable; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and +on_completion = 'PRESERVE'; +count(*) +1 +alter event event_35981 on completion not preserve; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and +on_completion = 'NOT PRESERVE'; +count(*) +1 +alter event event_35981 disable; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and +on_completion = 'NOT PRESERVE'; +count(*) +1 +alter event event_35981 on completion preserve; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and +on_completion = 'PRESERVE'; +count(*) +1 +drop event event_35981; +create event event_35981 on schedule every 6 month disable +do +select 1; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and +on_completion = 'NOT PRESERVE'; +count(*) +1 +drop event event_35981; +create event event_35981 on schedule every 1 hour starts current_timestamp +on completion not preserve +do +select 1; +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00'; +ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation. +drop event event_35981; +create event event_35981 on schedule every 1 hour starts current_timestamp +on completion not preserve +do +select 1; +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00' on completion preserve; +Warnings: +Note 1544 Event execution time is in the past. Event has been disabled +drop event event_35981; +create event event_35981 on schedule every 1 hour starts current_timestamp +on completion preserve +do +select 1; +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00'; +Warnings: +Note 1544 Event execution time is in the past. Event has been disabled +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00' on completion not preserve; +ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation. +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00' on completion preserve; +Warnings: +Note 1544 Event execution time is in the past. Event has been disabled +drop event event_35981; drop database events_test; diff --git a/mysql-test/t/events_2.test b/mysql-test/t/events_2.test index 58a6fffe6f5..a50255e9f8b 100644 --- a/mysql-test/t/events_2.test +++ b/mysql-test/t/events_2.test @@ -411,6 +411,108 @@ create event очень_очень_очень_очень_очень_очень_очень_очень_длинная_строка_66 on schedule every 2 year do select 1; +# +# Bug#35981: ALTER EVENT causes the server to change the PRESERVE option. +# + +create event event_35981 on schedule every 6 month on completion preserve +disable +do + select 1; + +echo The following SELECTs should all give 1; + +# show current ON_COMPLETION +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and + on_completion = 'PRESERVE'; + +# show ON_COMPLETION remains "PRESERVE" when not given in ALTER EVENT +alter event event_35981 enable; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and + on_completion = 'PRESERVE'; + +# show we can change ON_COMPLETION +alter event event_35981 on completion not preserve; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and + on_completion = 'NOT PRESERVE'; + +# show ON_COMPLETION remains "NOT PRESERVE" when not given in ALTER EVENT +alter event event_35981 disable; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and + on_completion = 'NOT PRESERVE'; + +# show we can change ON_COMPLETION +alter event event_35981 on completion preserve; +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and + on_completion = 'PRESERVE'; + + +drop event event_35981; + +create event event_35981 on schedule every 6 month disable +do + select 1; + +# show that the defaults for CREATE EVENT are still correct (NOT PRESERVE) +select count(*) from information_schema.events +where event_schema = database() and event_name = 'event_35981' and + on_completion = 'NOT PRESERVE'; + +drop event event_35981; + + +# show that backdating doesn't break + +create event event_35981 on schedule every 1 hour starts current_timestamp + on completion not preserve +do + select 1; + +# should fail thanks to above's NOT PRESERVE +--error ER_EVENT_CANNOT_ALTER_IN_THE_PAST +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00'; + +drop event event_35981; + +create event event_35981 on schedule every 1 hour starts current_timestamp + on completion not preserve +do + select 1; + +# succeed with warning +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00' on completion preserve; + +drop event event_35981; + + + +create event event_35981 on schedule every 1 hour starts current_timestamp + on completion preserve +do + select 1; + +# this should succeed thanks to above PRESERVE! give a warning though. +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00'; + +# this should fail, as the event would have passed already +--error ER_EVENT_CANNOT_ALTER_IN_THE_PAST +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00' on completion not preserve; + +# should succeed giving a warning +alter event event_35981 on schedule every 1 hour starts '1999-01-01 00:00:00' + ends '1999-01-02 00:00:00' on completion preserve; + +drop event event_35981; + # # End of tests # diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 401f76f5d26..e33cae18cee 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -185,6 +185,8 @@ mysql_event_fill_row(THD *thd, DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str)); DBUG_PRINT("info", ("name =[%s]", et->name.str)); + DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT); + if (table->s->fields < ET_FIELD_COUNT) { /* @@ -745,6 +747,18 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, store_record(table,record[1]); + /* + 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. The + check is deferred to this point because by now we have the previous + setting (from the event-table) to fall back on if nothing was specified + in the ALTER EVENT-statement. + */ + + if (parse_data->check_dates(thd, + table->field[ET_FIELD_ON_COMPLETION]->val_int())) + goto end; + /* Don't update create on row update. */ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index e87e4593f8f..df419e92d0d 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -45,7 +45,7 @@ Event_parse_data::new_instance(THD *thd) */ Event_parse_data::Event_parse_data() - :on_completion(Event_parse_data::ON_COMPLETION_DROP), + :on_completion(Event_parse_data::ON_COMPLETION_DEFAULT), status(Event_parse_data::ENABLED), do_not_create(FALSE), body_changed(FALSE), @@ -114,6 +114,12 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) if (ltime_utc >= (my_time_t) 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) { @@ -141,6 +147,42 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) } +/* + 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. diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h index 221bf92664f..87a800c2078 100644 --- a/sql/event_parse_data.h +++ b/sql/event_parse_data.h @@ -38,7 +38,12 @@ public: enum enum_on_completion { - ON_COMPLETION_DROP = 1, + /* + On CREATE EVENT, DROP is the DEFAULT as per the docs. + On ALTER EVENT, "no change" is the DEFAULT. + */ + ON_COMPLETION_DEFAULT = 0, + ON_COMPLETION_DROP, ON_COMPLETION_PRESERVE }; @@ -80,6 +85,9 @@ public: bool check_parse_data(THD *thd); + bool + check_dates(THD *thd, int previous_on_completion); + private: void diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 54399c3791c..90dad3994c6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1786,6 +1786,8 @@ event_tail: if (!(lex->event_parse_data= Event_parse_data::new_instance(thd))) MYSQL_YYABORT; lex->event_parse_data->identifier= $3; + lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_DROP; /* We have to turn of CLIENT_MULTI_QUERIES while parsing a