Commit 8bb8385f authored by Dmitry Shulga's avatar Dmitry Shulga

Fixed bug#12546938 (formerly known as 61005) - CREATE IF NOT EXIST EVENT

will create multiple running events.

A CREATE IF NOT EXIST on an event that existed and was enabled caused
multiple instances of the event to run. Disabling the event didn't  help.
If the event was  dropped, the event stopped running, but when created
again, multiple instances of the event were still running. The only way
to get out of this situation was  to restart the server.

The problem was that Event_db_repository::create_event() didn't return
enough information to discriminate between situation when event didn't
exist and was created and when event did exist and was not created
(but a warning was emitted). As result in the latter case event
was added to in-memory queue of events second time. And this led to
unwarranted multiple executions of the same event.

The solution is to add out-parameter to Event_db_repository::create_event()
method which will signal that event was not created because it already
exists and so it should not be added to the in-memory queue.
parent d076be2a
...@@ -535,6 +535,7 @@ DROP EVENT e3; ...@@ -535,6 +535,7 @@ DROP EVENT e3;
DROP EVENT e2; DROP EVENT e2;
DROP EVENT e1; DROP EVENT e1;
SET TIME_ZONE=@save_time_zone; SET TIME_ZONE=@save_time_zone;
SET TIMESTAMP=DEFAULT;
drop event if exists new_event; drop event if exists new_event;
CREATE EVENT new_event ON SCHEDULE EVERY 0 SECOND DO SELECT 1; CREATE EVENT new_event ON SCHEDULE EVERY 0 SECOND DO SELECT 1;
ERROR HY000: INTERVAL is either not positive or too big ERROR HY000: INTERVAL is either not positive or too big
...@@ -756,6 +757,45 @@ SHOW EVENTS; ...@@ -756,6 +757,45 @@ SHOW EVENTS;
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation
DROP DATABASE event_test1; DROP DATABASE event_test1;
DROP DATABASE event_test12; DROP DATABASE event_test12;
#
# Bug#12546938 (formerly known as bug#61005):
# CREATE IF NOT EXIST EVENT WILL CREATE MULTIPLE RUNNING EVENTS
#
USE events_test;
SET GLOBAL event_scheduler = ON;
DROP TABLE IF EXISTS table_bug12546938;
DROP EVENT IF EXISTS event_Bug12546938;
CREATE TABLE table_bug12546938 (i INT);
# Create an event which will be executed with a small delay
# and won't be automatically dropped after that.
CREATE EVENT event_Bug12546938
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND ON COMPLETION PRESERVE
ENABLE DO
BEGIN
INSERT INTO table_bug12546938 VALUES(1);
END
|
# Now try to create the same event using CREATE EVENT IF NOT EXISTS.
# A warning should be emitted. A new event should not be created nor
# the old event should be re-executed.
CREATE EVENT IF NOT EXISTS event_bug12546938
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND ON COMPLETION PRESERVE
ENABLE DO
BEGIN
INSERT INTO table_bug12546938 VALUES (1);
END
|
Warnings:
Note 1537 Event 'event_bug12546938' already exists
# Wait until at least one instance of event is executed.
# Check that only one instance of our event was executed.
SELECT COUNT(*) FROM table_bug12546938;
COUNT(*)
1
# Clean-up.
DROP EVENT IF EXISTS event_Bug12546938;
DROP TABLE table_bug12546938;
SET GLOBAL EVENT_SCHEDULER = OFF;
DROP DATABASE events_test; DROP DATABASE events_test;
SET GLOBAL event_scheduler= 'ON'; SET GLOBAL event_scheduler= 'ON';
SET @@global.concurrent_insert= @concurrent_insert; SET @@global.concurrent_insert= @concurrent_insert;
...@@ -857,6 +857,7 @@ DROP EVENT e2; ...@@ -857,6 +857,7 @@ DROP EVENT e2;
DROP EVENT e1; DROP EVENT e1;
SET TIME_ZONE=@save_time_zone; SET TIME_ZONE=@save_time_zone;
SET TIMESTAMP=DEFAULT;
# #
# START - BUG#28666 CREATE EVENT ... EVERY 0 SECOND let server crash # START - BUG#28666 CREATE EVENT ... EVERY 0 SECOND let server crash
...@@ -1235,6 +1236,55 @@ SHOW EVENTS; ...@@ -1235,6 +1236,55 @@ SHOW EVENTS;
DROP DATABASE event_test1; DROP DATABASE event_test1;
DROP DATABASE event_test12; DROP DATABASE event_test12;
--echo #
--echo # Bug#12546938 (formerly known as bug#61005):
--echo # CREATE IF NOT EXIST EVENT WILL CREATE MULTIPLE RUNNING EVENTS
--echo #
USE events_test;
SET GLOBAL event_scheduler = ON;
--disable_warnings
DROP TABLE IF EXISTS table_bug12546938;
DROP EVENT IF EXISTS event_Bug12546938;
--enable_warnings
CREATE TABLE table_bug12546938 (i INT);
delimiter |;
--echo # Create an event which will be executed with a small delay
--echo # and won't be automatically dropped after that.
CREATE EVENT event_Bug12546938
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND ON COMPLETION PRESERVE
ENABLE DO
BEGIN
INSERT INTO table_bug12546938 VALUES(1);
END
|
--echo # Now try to create the same event using CREATE EVENT IF NOT EXISTS.
--echo # A warning should be emitted. A new event should not be created nor
--echo # the old event should be re-executed.
CREATE EVENT IF NOT EXISTS event_bug12546938
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND ON COMPLETION PRESERVE
ENABLE DO
BEGIN
INSERT INTO table_bug12546938 VALUES (1);
END
|
delimiter ;|
--echo # Wait until at least one instance of event is executed.
let $wait_condition= SELECT COUNT(*) FROM table_bug12546938;
--source include/wait_condition.inc
--echo # Check that only one instance of our event was executed.
SELECT COUNT(*) FROM table_bug12546938;
--echo # Clean-up.
DROP EVENT IF EXISTS event_Bug12546938;
DROP TABLE table_bug12546938;
SET GLOBAL EVENT_SCHEDULER = OFF;
########################################################################### ###########################################################################
# #
......
...@@ -604,18 +604,21 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, ...@@ -604,18 +604,21 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
only creates a record on disk. only creates a record on disk.
@pre The thread handle has no open tables. @pre The thread handle has no open tables.
@param[in,out] thd THD @param[in,out] thd THD
@param[in] parse_data Parsed event definition @param[in] parse_data Parsed event definition
@param[in] create_if_not TRUE if IF NOT EXISTS clause was provided @param[in] create_if_not TRUE if IF NOT EXISTS clause was provided
to CREATE EVENT statement to CREATE EVENT statement
@param[out] event_already_exists When method is completed successfully
set to true if event already exists else
set to false
@retval FALSE success @retval FALSE success
@retval TRUE error @retval TRUE error
*/ */
bool bool
Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
my_bool create_if_not) bool create_if_not,
bool *event_already_exists)
{ {
int ret= 1; int ret= 1;
TABLE *table= NULL; TABLE *table= NULL;
...@@ -641,6 +644,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -641,6 +644,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
{ {
if (create_if_not) if (create_if_not)
{ {
*event_already_exists= true;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
parse_data->name.str); parse_data->name.str);
...@@ -648,8 +652,10 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -648,8 +652,10 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
} }
else else
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str); my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
goto end; goto end;
} } else
*event_already_exists= false;
DBUG_PRINT("info", ("non-existent, go forward")); DBUG_PRINT("info", ("non-existent, go forward"));
......
...@@ -73,7 +73,8 @@ public: ...@@ -73,7 +73,8 @@ public:
Event_db_repository(){} Event_db_repository(){}
bool bool
create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not); create_event(THD *thd, Event_parse_data *parse_data, bool create_if_not,
bool *event_already_exists);
bool bool
update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname, update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname,
......
...@@ -370,6 +370,7 @@ create_query_string(THD *thd, String *buf) ...@@ -370,6 +370,7 @@ create_query_string(THD *thd, String *buf)
return 0; return 0;
} }
/** /**
Create a new event. Create a new event.
...@@ -390,8 +391,8 @@ bool ...@@ -390,8 +391,8 @@ bool
Events::create_event(THD *thd, Event_parse_data *parse_data, Events::create_event(THD *thd, Event_parse_data *parse_data,
bool if_not_exists) bool if_not_exists)
{ {
int ret; bool ret;
bool save_binlog_row_based; bool save_binlog_row_based, event_already_exists;
DBUG_ENTER("Events::create_event"); DBUG_ENTER("Events::create_event");
/* /*
...@@ -440,28 +441,32 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -440,28 +441,32 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
pthread_mutex_lock(&LOCK_event_metadata); pthread_mutex_lock(&LOCK_event_metadata);
/* On error conditions my_error() is called so no need to handle here */ /* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists))) if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists,
&event_already_exists)))
{ {
Event_queue_element *new_element; Event_queue_element *new_element;
bool dropped= 0; bool dropped= 0;
if (!(new_element= new Event_queue_element())) if (!event_already_exists)
ret= TRUE; // OOM
else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
parse_data->name,
new_element)))
{
if (!db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
TRUE))
dropped= 1;
delete new_element;
}
else
{ {
/* TODO: do not ignore the out parameter and a possible OOM error! */ if (!(new_element= new Event_queue_element()))
bool created; ret= TRUE; // OOM
if (event_queue) else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
event_queue->create_event(thd, new_element, &created); parse_data->name,
new_element)))
{
if (!db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
TRUE))
dropped= 1;
delete new_element;
}
else
{
/* TODO: do not ignore the out parameter and a possible OOM error! */
bool created;
if (event_queue)
event_queue->create_event(thd, new_element, &created);
}
} }
/* /*
binlog the create event unless it's been successfully dropped binlog the create event unless it's been successfully dropped
...@@ -475,13 +480,14 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, ...@@ -475,13 +480,14 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
{ {
sql_print_error("Event Error: An error occurred while creating query string, " sql_print_error("Event Error: An error occurred while creating query string, "
"before writing it into binary log."); "before writing it into binary log.");
/* Restore the state of binlog format */ ret= true;
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(TRUE);
} }
/* If the definer is not set or set to CURRENT_USER, the value of CURRENT_USER else
will be written into the binary log as the definer for the SQL thread. */ /*
ret= write_bin_log(thd, TRUE, log_query.c_ptr(), log_query.length()); If the definer is not set or set to CURRENT_USER, the value of CURRENT_USER
will be written into the binary log as the definer for the SQL thread.
*/
ret= write_bin_log(thd, TRUE, log_query.c_ptr(), log_query.length());
} }
} }
pthread_mutex_unlock(&LOCK_event_metadata); pthread_mutex_unlock(&LOCK_event_metadata);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment