BUG#16420: Events: timestamps become UTC

BUG#26429: SHOW CREATE EVENT is incorrect for an event that
           STARTS NOW()
BUG#26431: Impossible to re-create an event from backup if its
           STARTS clause is in the past
WL#3698: Events: execution in local time zone

The problem was that local times specified by the user in AT, STARTS
and ENDS of CREATE EVENT/ALTER EVENT statement were converted to UTC,
and the original time zone was forgotten.  This way, event scheduler
couldn't honor Daylight Saving Time shifts, and times shown to the
user were also in UTC.  Additionally, CREATE EVENT didn't allow times
in the past, thus preventing straightforward event restoration from
old backups.

This patch reworks event scheduler time computations, performing them
in the time zone associated with the event.  Also it allows times to
be in the past.

The patch adds time_zone column to mysql.event table.

NOTE: The patch is almost final, but the bug#9953 should be pushed
first.
parent 245668ef
......@@ -1431,6 +1431,8 @@ static uint dump_events_for_db(char *db)
strcpy(delimiter, ";");
if (mysql_num_rows(event_list_res) > 0)
{
fprintf(sql_file, "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;\n");
while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL)
{
event_name= quote_name(event_list_row[1], name_buff, 0);
......@@ -1447,13 +1449,13 @@ static uint dump_events_for_db(char *db)
if the user has EXECUTE privilege he can see event names, but not the
event body!
*/
if (strlen(row[2]) != 0)
if (strlen(row[3]) != 0)
{
if (opt_drop)
fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n",
event_name, delimiter);
delimit_test= create_delimiter(row[2], delimiter, sizeof(delimiter));
delimit_test= create_delimiter(row[3], delimiter, sizeof(delimiter));
if (delimit_test == NULL) {
fprintf(stderr, "%s: Warning: Can't dump event '%s'\n",
event_name, my_progname);
......@@ -1461,11 +1463,15 @@ static uint dump_events_for_db(char *db)
}
fprintf(sql_file, "DELIMITER %s\n", delimiter);
fprintf(sql_file, "/*!50106 %s */ %s\n", row[2], delimiter);
fprintf(sql_file, "/*!50106 SET TIME_ZONE= '%s' */ %s\n",
row[2], delimiter);
fprintf(sql_file, "/*!50106 %s */ %s\n", row[3], delimiter);
}
} /* end of event printing */
} /* end of list of events */
fprintf(sql_file, "DELIMITER ;\n");
fprintf(sql_file, "/*!50106 SET TIME_ZONE= @save_time_zone */ ;\n");
mysql_free_result(event_res);
}
mysql_free_result(event_list_res);
......
......@@ -11,13 +11,28 @@
# SELECT c = 3 FROM t;
# --source include/wait_condition.inc
#
# OR
#
# let $wait_timeout= 60; # Override default 30 seconds with 60.
# let $wait_condition=
# SELECT c = 3 FROM t;
# --source include/wait_condition.inc
#
# EXAMPLE
# events_bugs.test
# events_bugs.test, events_time_zone.test
#
--disable_query_log
let $wait_counter= 300;
if ($wait_timeout)
{
let $wait_counter= `SELECT $wait_timeout * 10`;
}
# Reset $wait_timeout so that its value won't be used on subsequent
# calls, and default will be used instead.
let $wait_timeout= 0;
while ($wait_counter)
{
let $success= `$wait_condition`;
......
......@@ -645,6 +645,7 @@ CREATE TABLE event (
'HIGH_NOT_PRECEDENCE'
) DEFAULT '' NOT NULL,
comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
time_zone char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',
PRIMARY KEY (db, name)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';
......
This diff is collapsed.
......@@ -25,9 +25,12 @@ ERROR HY000: Incorrect STARTS value: '99990101000000'
create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t;
ERROR HY000: ENDS is either invalid or before STARTS
create event e_55 on schedule at 10000101000000 do drop table t;
ERROR HY000: Activation (AT) time is in the past
ERROR HY000: Incorrect AT value: '10000101000000'
create event e_55 on schedule at 20000101000000 do drop table t;
ERROR HY000: Activation (AT) time is in the past
Warnings:
Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
show events;
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
create event e_55 on schedule at 20200101000000 starts 10000101000000 do drop table t;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'starts 10000101000000 do drop table t' at line 1
create event e_55 on schedule at 20200101000000 ends 10000101000000 do drop table t;
......
......@@ -2,8 +2,8 @@ CREATE DATABASE IF NOT EXISTS events_test;
use events_test;
CREATE EVENT one_event ON SCHEDULE EVERY 10 SECOND DO SELECT 123;
SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
events_test one_event root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED
SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_DEFINITION, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_SCHEMA, EVENT_NAME;
EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_DEFINITION EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT
NULL events_test one_event root@localhost SQL SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE
......@@ -29,8 +29,8 @@ ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_te
USE events_test;
"We should see one event";
SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
events_test one_event root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED
SELECT CONCAT("Let's create some new events from the name of ", USER());
CONCAT("Let's create some new events from the name of ", USER())
Let's create some new events from the name of ev_test@localhost
......@@ -40,18 +40,18 @@ CREATE EVENT two_event ON SCHEDULE EVERY 20 SECOND ON COMPLETION NOT PRESERVE CO
CREATE EVENT three_event ON SCHEDULE EVERY 20 SECOND ON COMPLETION PRESERVE COMMENT "three event" DO SELECT 123;
"Now we should see 3 events:";
SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED
events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED
events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
events_test one_event root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED
events_test three_event ev_test@localhost SYSTEM RECURRING NULL 20 # # NULL ENABLED
events_test two_event ev_test@localhost SYSTEM RECURRING NULL 20 # # NULL ENABLED
"This should show us only 2 events:";
SHOW EVENTS LIKE 't%event';
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED
events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
events_test three_event ev_test@localhost SYSTEM RECURRING NULL 20 # # NULL ENABLED
events_test two_event ev_test@localhost SYSTEM RECURRING NULL 20 # # NULL ENABLED
"This should show us no events:";
SHOW EVENTS FROM test LIKE '%';
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
GRANT EVENT ON events_test2.* TO ev_test@localhost;
USE events_test2;
CREATE EVENT four_event ON SCHEDULE EVERY 20 SECOND DO SELECT 42;
......
......@@ -7,6 +7,6 @@ create event abc2 on schedule every 1 second do insert into execution_log value(
create event abc3 on schedule every 1 second do insert into execution_log value('abc3');
select name from execution_log;
name
insert into mysql.event values ('db1','bad','select 42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment1');
insert into mysql.event values ('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment2');
insert into mysql.event values ('db1','bad','select 42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment1','SYSTEM');
insert into mysql.event values ('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment2','SYSTEM');
"Now we restart the server"
......@@ -83,3 +83,4 @@ DROP TABLE table_2;
DROP TABLE table_3;
DROP TABLE table_4;
DROP DATABASE events_test;
SET GLOBAL event_scheduler=OFF;
This diff is collapsed.
......@@ -3440,35 +3440,35 @@ use first;
set time_zone = 'UTC';
create event ee1 on schedule at '2035-12-31 20:01:23' do set @a=5;
show events;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
first ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
first ee1 root@localhost UTC ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
show create event ee1;
Event sql_mode Create Event
ee1 CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5
Event sql_mode time_zone Create Event
ee1 UTC CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5
drop database first;
create database second;
use second;
show events;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
second ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
second ee1 root@localhost UTC ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
show create event ee1;
Event sql_mode Create Event
ee1 NO_AUTO_VALUE_ON_ZERO CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5
Event sql_mode time_zone Create Event
ee1 NO_AUTO_VALUE_ON_ZERO UTC CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5
create event ee2 on schedule at '2018-12-31 21:01:23' do set @a=5;
create event ee3 on schedule at '2030-12-31 22:01:23' do set @a=5;
show events;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
second ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
second ee2 root@localhost ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED
second ee3 root@localhost ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
second ee1 root@localhost UTC ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
second ee2 root@localhost UTC ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED
second ee3 root@localhost UTC ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED
drop database second;
create database third;
use third;
show events;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
third ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
third ee2 root@localhost ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED
third ee3 root@localhost ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
third ee1 root@localhost UTC ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED
third ee2 root@localhost UTC ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED
third ee3 root@localhost UTC ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED
drop database third;
set time_zone = 'SYSTEM';
use test;
......
......@@ -1968,11 +1968,11 @@ prepare abc from "show master logs";
deallocate prepare abc;
create procedure proc_1() show events;
call proc_1();
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
call proc_1();
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
call proc_1();
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
drop procedure proc_1;
create function func_1() returns int begin show events; return 1; end|
ERROR 0A000: Not allowed to return a result set from a function
......@@ -1982,11 +1982,11 @@ drop function func_1;
ERROR 42000: FUNCTION test.func_1 does not exist
prepare abc from "show events";
execute abc;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
execute abc;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
execute abc;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
deallocate prepare abc;
drop procedure if exists a;
create procedure a() select 42;
......
......@@ -224,6 +224,7 @@ event CREATE TABLE `event` (
`on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP',
`sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE') NOT NULL DEFAULT '',
`comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
`time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',
PRIMARY KEY (`db`,`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events'
show create table general_log;
......
......@@ -122,43 +122,62 @@ set names utf8;
# SHOW CREATE EVENT test begin
#
CREATE EVENT root6 ON SCHEDULE EVERY '10:20' MINUTE_SECOND ON COMPLETION PRESERVE ENABLE COMMENT 'some comment' DO select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root6;
create event root7 on schedule every 2 year do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root7;
create event root8 on schedule every '2:5' year_month do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root8;
create event root8_1 on schedule every '2:15' year_month do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root8_1;
create event root9 on schedule every 2 week ON COMPLETION PRESERVE DISABLE COMMENT 'коментар на кирилица' do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root9;
create event root10 on schedule every '20:5' day_hour do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root10;
create event root11 on schedule every '20:25' day_hour do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root11;
create event root12 on schedule every '20:25' hour_minute do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root12;
create event root13 on schedule every '25:25' hour_minute do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root13;
create event root13_1 on schedule every '11:65' hour_minute do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root13_1;
create event root14 on schedule every '35:35' minute_second do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root14;
create event root15 on schedule every '35:66' minute_second do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root15;
create event root16 on schedule every '35:56' day_minute do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root16;
create event root17 on schedule every '35:12:45' day_minute do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root17;
create event root17_1 on schedule every '35:25:65' day_minute do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root17_1;
create event root18 on schedule every '35:12:45' hour_second do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root18;
create event root19 on schedule every '15:59:85' hour_second do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root19;
create event root20 on schedule every '50:20:12:45' day_second do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT root20;
set names cp1251;
create event 21 on schedule every '50:23:59:95' day_second COMMENT ' 1251 ' do select 1;
--replace_regex /STARTS '[^']+'/STARTS '#'/
SHOW CREATE EVENT 21;
insert into mysql.event (db, name, body, definer, interval_value, interval_field) values (database(), "root22", "select 1", user(), 100, "SECOND_MICROSECOND");
--error ER_NOT_SUPPORTED_YET
......
......@@ -45,10 +45,17 @@ create event e_55 on schedule at 99990101000000 do drop table t;
create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t;
--error ER_EVENT_ENDS_BEFORE_STARTS
create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t;
--error ER_EVENT_EXEC_TIME_IN_THE_PAST
--error ER_WRONG_VALUE
create event e_55 on schedule at 10000101000000 do drop table t;
--error ER_EVENT_EXEC_TIME_IN_THE_PAST
# For the purpose of backup we allow times in the past. Here, no
# error will be given, but the event won't be created. One may think
# of that as if the event was created, then it turned out it's in the
# past, so it was dropped because of implicit ON COMPLETION NOT
# PRESERVE.
create event e_55 on schedule at 20000101000000 do drop table t;
show events;
--error ER_PARSE_ERROR
create event e_55 on schedule at 20200101000000 starts 10000101000000 do drop table t;
--error ER_PARSE_ERROR
......
......@@ -14,6 +14,6 @@ create event abc3 on schedule every 1 second do insert into execution_log value(
--sleep 1.5
select name from execution_log;
insert into mysql.event values ('db1','bad','select 42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment1');
insert into mysql.event values ('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment2');
insert into mysql.event values ('db1','bad','select 42','root@localhost',NULL,1000,'MICROSECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment1','SYSTEM');
insert into mysql.event values ('db1','bad2','sect','root@localhost',NULL,1000,'SECOND','2006-05-05 17:39:11','2006-05-05 17:39:20','2016-05-05 15:39:24','2016-05-05 15:39:11',NULL,'ENABLED','DROP','','comment2','SYSTEM');
--echo "Now we restart the server"
......@@ -63,3 +63,4 @@ DROP TABLE table_2;
DROP TABLE table_3;
DROP TABLE table_4;
DROP DATABASE events_test;
SET GLOBAL event_scheduler=OFF;
This diff is collapsed.
......@@ -867,6 +867,7 @@ then
c_ev="$c_ev 'HIGH_NOT_PRECEDENCE'"
c_ev="$c_ev ) DEFAULT '' NOT NULL,"
c_ev="$c_ev comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',"
c_ev="$c_ev time_zone char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',"
c_ev="$c_ev PRIMARY KEY (db, name)"
c_ev="$c_ev ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';"
fi
......
......@@ -683,6 +683,7 @@ CREATE TABLE event (
'HIGH_NOT_PRECEDENCE'
) DEFAULT '' NOT NULL,
comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
time_zone char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',
PRIMARY KEY (db,name)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';
......@@ -734,10 +735,12 @@ ALTER TABLE event ADD sql_mode
'NO_AUTO_CREATE_USER',
'HIGH_NOT_PRECEDENCE'
) DEFAULT '' NOT NULL AFTER on_completion;
UPDATE user SET Event_priv=Super_priv WHERE @hadEventPriv = 0;
ALTER TABLE event MODIFY name char(64) CHARACTER SET utf8 NOT NULL default '';
ALTER TABLE event ADD COLUMN time_zone char(64) CHARACTER SET latin1
NOT NULL DEFAULT 'SYSTEM' AFTER comment;
#
# TRIGGER privilege
#
......
This diff is collapsed.
......@@ -58,15 +58,20 @@ class Event_basic
LEX_STRING name;
LEX_STRING definer;// combination of user and host
Time_zone *time_zone;
Event_basic();
virtual ~Event_basic();
virtual int
load_from_row(TABLE *table) = 0;
load_from_row(THD *thd, TABLE *table) = 0;
protected:
bool
load_string_fields(Field **fields, ...);
bool
load_time_zone(THD *thd, const LEX_STRING tz_name);
};
......@@ -92,11 +97,11 @@ class Event_queue_element : public Event_basic
enum enum_on_completion on_completion;
enum enum_status status;
TIME last_executed;
TIME execute_at;
TIME starts;
TIME ends;
my_time_t last_executed;
my_time_t execute_at;
my_time_t starts;
my_time_t ends;
my_bool starts_null;
my_bool ends_null;
my_bool execute_at_null;
......@@ -112,7 +117,7 @@ class Event_queue_element : public Event_basic
virtual ~Event_queue_element();
virtual int
load_from_row(TABLE *table);
load_from_row(THD *thd, TABLE *table);
bool
compute_next_execution_time();
......@@ -168,7 +173,7 @@ class Event_timed : public Event_queue_element
init();
virtual int
load_from_row(TABLE *table);
load_from_row(THD *thd, TABLE *table);
int
get_create_event(THD *thd, String *buf);
......@@ -192,7 +197,7 @@ class Event_job_data : public Event_basic
virtual ~Event_job_data();
virtual int
load_from_row(TABLE *table);
load_from_row(THD *thd, TABLE *table);
int
execute(THD *thd);
......@@ -224,6 +229,11 @@ class Event_parse_data : public Sql_alloc
};
enum enum_on_completion on_completion;
enum enum_status status;
/*
do_not_create will be set if STARTS time is in the past and
on_completion == ON_COMPLETION_DROP.
*/
bool do_not_create;
const uchar *body_begin;
......@@ -237,9 +247,9 @@ class Event_parse_data : public Sql_alloc
Item* item_ends;
Item* item_execute_at;
TIME starts;
TIME ends;
TIME execute_at;
my_time_t starts;
my_time_t ends;
my_time_t execute_at;
my_bool starts_null;
my_bool ends_null;
my_bool execute_at_null;
......@@ -284,6 +294,9 @@ class Event_parse_data : public Sql_alloc
void
report_bad_value(const char *item_name, Item *bad_item);
void
check_if_in_the_past(THD *thd, my_time_t ltime_utc);
Event_parse_data(const Event_parse_data &); /* Prevent use of these */
void operator=(Event_parse_data &);
};
......
......@@ -118,6 +118,11 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
{ C_STRING_WITH_LEN("comment") },
{ C_STRING_WITH_LEN("char(64)") },
{ C_STRING_WITH_LEN("utf8") }
},
{
{ C_STRING_WITH_LEN("time_zone") },
{ C_STRING_WITH_LEN("char(64)") },
{ C_STRING_WITH_LEN("latin1") }
}
};
......@@ -183,6 +188,14 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
if (et->expression)
{
const String *tz_name= thd->variables.time_zone->get_name();
if (!is_update || !et->starts_null)
{
fields[ET_FIELD_TIME_ZONE]->set_notnull();
fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
tz_name->charset());
}
fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
......@@ -197,26 +210,40 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
if (!et->starts_null)
{
TIME time;
my_tz_UTC->gmt_sec_to_TIME(&time, et->starts);
fields[ET_FIELD_STARTS]->set_notnull();
fields[ET_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
fields[ET_FIELD_STARTS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
}
if (!et->ends_null)
{
TIME time;
my_tz_UTC->gmt_sec_to_TIME(&time, et->ends);
fields[ET_FIELD_ENDS]->set_notnull();
fields[ET_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
fields[ET_FIELD_ENDS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
}
}
else if (et->execute_at.year)
else if (et->execute_at)
{
const String *tz_name= thd->variables.time_zone->get_name();
fields[ET_FIELD_TIME_ZONE]->set_notnull();
fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
tz_name->charset());
fields[ET_FIELD_INTERVAL_EXPR]->set_null();
fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
fields[ET_FIELD_STARTS]->set_null();
fields[ET_FIELD_ENDS]->set_null();
TIME time;
my_tz_UTC->gmt_sec_to_TIME(&time, et->execute_at);
fields[ET_FIELD_EXECUTE_AT]->set_notnull();
fields[ET_FIELD_EXECUTE_AT]->
store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
store_time(&time, MYSQL_TIMESTAMP_DATETIME);
}
else
{
......@@ -527,6 +554,8 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
if (check_parse_params(thd, parse_data))
goto err;
if (parse_data->do_not_create)
goto ok;
DBUG_PRINT("info", ("open mysql.event for update"));
if (open_event_table(thd, TL_WRITE, &table))
......@@ -587,7 +616,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
goto err;
}
if (!(parse_data->expression) && !(parse_data->execute_at.year))
if (!(parse_data->expression) && !(parse_data->execute_at))
{
DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
......@@ -664,7 +693,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
goto err;
}
if (check_parse_params(thd, parse_data))
if (check_parse_params(thd, parse_data) || parse_data->do_not_create)
goto err;
DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
......@@ -964,7 +993,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
else if ((ret= find_named_event(thd, dbname, name, table)))
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
else if ((ret= etn->load_from_row(table)))
else if ((ret= etn->load_from_row(thd, table)))
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
if (table)
......
......@@ -35,6 +35,7 @@ enum enum_events_table_field
ET_FIELD_ON_COMPLETION,
ET_FIELD_SQL_MODE,
ET_FIELD_COMMENT,
ET_FIELD_TIME_ZONE,
ET_FIELD_COUNT /* a cool trick to count the number of fields :) */
};
......
......@@ -65,8 +65,13 @@ struct event_queue_param
static int
event_queue_element_compare_q(void *vptr, byte* a, byte *b)
{
return my_time_compare(&((Event_queue_element *)a)->execute_at,
&((Event_queue_element *)b)->execute_at);
/*
Note that no overflow is possible here because both values are
non-negative, and subtraction is done in the signed my_time_t
type.
*/
return (((Event_queue_element *)a)->execute_at
- ((Event_queue_element *)b)->execute_at);
}
......@@ -84,7 +89,7 @@ Event_queue::Event_queue()
{
mutex_last_unlocked_in_func= mutex_last_locked_in_func=
mutex_last_attempted_lock_in_func= "";
set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME);
next_activation_at= 0;
}
......@@ -504,15 +509,11 @@ Event_queue::dbug_dump_queue(time_t now)
DBUG_PRINT("info", ("exec_at: %lu starts: %lu ends: %lu execs_so_far: %u "
"expr: %ld et.exec_at: %ld now: %ld "
"(et.exec_at - now): %d if: %d",
(long) TIME_to_ulonglong_datetime(&et->execute_at),
(long) TIME_to_ulonglong_datetime(&et->starts),
(long) TIME_to_ulonglong_datetime(&et->ends),
et->execution_count,
(long) et->expression,
(long) (sec_since_epoch_TIME(&et->execute_at)),
(long) now,
(int) (sec_since_epoch_TIME(&et->execute_at) - now),
sec_since_epoch_TIME(&et->execute_at) <= now));
(long) et->execute_at, (long) et->starts,
(long) et->ends, et->execution_count,
(long) et->expression, (long) et->execute_at,
(long) now, (int) (et->execute_at - now),
et->execute_at <= now));
}
DBUG_VOID_RETURN;
#endif
......@@ -541,7 +542,6 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
Event_queue_element_for_exec **event_name)
{
bool ret= FALSE;
struct timespec top_time;
*event_name= NULL;
DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
......@@ -560,7 +560,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
if (!queue.elements)
{
/* There are no events in the queue */
set_zero_time(&next_activation_at, MYSQL_TIMESTAMP_DATETIME);
next_activation_at= 0;
/* Wait on condition until signaled. Release LOCK_queue while waiting. */
cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__);
......@@ -572,16 +572,16 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
thd->end_time(); /* Get current time */
time_t seconds_to_next_event=
sec_since_epoch_TIME(&top->execute_at) - thd->query_start();
next_activation_at= top->execute_at;
if (seconds_to_next_event > 0)
if (next_activation_at > thd->query_start())
{
/*
Not yet time for top event, wait on condition with
time or until signaled. Release LOCK_queue while waiting.
*/
set_timespec(top_time, seconds_to_next_event);
struct timespec top_time;
top_time.tv_sec= next_activation_at;
top_time.tv_nsec= 0;
cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__);
continue;
......@@ -759,10 +759,11 @@ Event_queue::dump_internal_status()
printf("Last lock attempt at: %s:%u\n", mutex_last_attempted_lock_in_func,
mutex_last_attempted_lock_at_line);
printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
TIME time;
my_tz_UTC->gmt_sec_to_TIME(&time, next_activation_at);
printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n",
next_activation_at.year, next_activation_at.month,
next_activation_at.day, next_activation_at.hour,
next_activation_at.minute, next_activation_at.second);
time.year, time.month, time.day, time.hour, time.minute, time.second);
DBUG_VOID_RETURN;
}
......@@ -86,7 +86,7 @@ class Event_queue
/* The sorted queue with the Event_queue_element objects */
QUEUE queue;
TIME next_activation_at;
my_time_t next_activation_at;
uint mutex_last_locked_at_line;
uint mutex_last_unlocked_at_line;
......
......@@ -574,6 +574,14 @@ Event_scheduler::execute_top(THD *thd, Event_queue_element_for_exec *event_name)
DBUG_PRINT("info", ("Event %s@%s ready for start",
event_name->dbname.str, event_name->name.str));
/*
TODO: should use thread pool here, preferably with an upper limit
on number of threads: if too many events are scheduled for the
same time, starting all of them at once won't help them run truly
in parallel (because of the great amount of synchronization), so
we may as well execute them in sequence, keeping concurrency at a
reasonable level.
*/
/* Major failure */
if ((res= pthread_create(&th, &connection_attrib, event_worker_thread,
event_name)))
......
......@@ -326,7 +326,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
pthread_mutex_lock(&LOCK_event_metadata);
/* 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)) &&
!parse_data->do_not_create)
{
Event_queue_element *new_element;
......@@ -527,6 +528,10 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
const String *tz_name= et->time_zone->get_name();
field_list.push_back(new Item_empty_string("time_zone",
tz_name->length()));
field_list.push_back(new Item_empty_string("Create Event",
show_str.length()));
......@@ -539,6 +544,8 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
protocol->store((char*) sql_mode_str, sql_mode_len, scs);
protocol->store((char*) tz_name->ptr(), tz_name->length(), scs);
protocol->store(show_str.c_ptr(), show_str.length(), scs);
ret= protocol->write();
send_eof(thd);
......@@ -942,7 +949,7 @@ Events::load_events_from_db(THD *thd)
}
DBUG_PRINT("info", ("Loading event from row."));
if ((ret= et->load_from_row(table)))
if ((ret= et->load_from_row(thd, table)))
{
sql_print_error("SCHEDULER: Error while loading from mysql.event. "
"Table probably corrupted");
......@@ -967,7 +974,7 @@ Events::load_events_from_db(THD *thd)
Event_job_data temp_job_data;
DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
temp_job_data.load_from_row(table);
temp_job_data.load_from_row(thd, table);
/*
We load only on scheduler root just to check whether the body
......
......@@ -5873,8 +5873,7 @@ ER_EVENT_ENDS_BEFORE_STARTS
eng "ENDS is either invalid or before STARTS"
ger "ENDS ist entweder ungltig oder liegt vor STARTS"
ER_EVENT_EXEC_TIME_IN_THE_PAST
eng "Activation (AT) time is in the past"
ger "Aktivierungszeit (AT) liegt in der Vergangenheit"
eng "Event execution time is in the past. Event has been disabled"
ER_EVENT_OPEN_TABLE_FAILED
eng "Failed to open mysql.event"
ger "ffnen von mysql.event fehlgeschlagen"
......@@ -6052,3 +6051,7 @@ ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009
ER_BINLOG_PURGE_EMFILE
eng "Too many files opened, please execute the command again"
ger "Zu viele offene Dateien, bitte fhren Sie den Befehl noch einmal aus"
ER_EVENT_CANNOT_CREATE_IN_THE_PAST
eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created"
ER_EVENT_CANNOT_ALTER_IN_THE_PAST
eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been altered"
......@@ -38,6 +38,7 @@ enum enum_i_s_events_fields
ISE_EVENT_SCHEMA,
ISE_EVENT_NAME,
ISE_DEFINER,
ISE_TIME_ZONE,
ISE_EVENT_BODY,
ISE_EVENT_DEFINITION,
ISE_EVENT_TYPE,
......@@ -4310,7 +4311,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
restore_record(sch_table, s->default_values);
if (et.load_from_row(event_table))
if (et.load_from_row(thd, event_table))
{
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
DBUG_RETURN(1);
......@@ -4337,6 +4338,9 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
store(et.name.str, et.name.length, scs);
sch_table->field[ISE_DEFINER]->
store(et.definer.str, et.definer.length, scs);
const String *tz_name= et.time_zone->get_name();
sch_table->field[ISE_TIME_ZONE]->
store(tz_name->ptr(), tz_name->length(), scs);
sch_table->field[ISE_EVENT_BODY]->
store(STRING_WITH_LEN("SQL"), scs);
sch_table->field[ISE_EVENT_DEFINITION]->
......@@ -4353,6 +4357,8 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
store((const char*)sql_mode_str, sql_mode_len, scs);
}
int not_used=0;
if (et.expression)
{
String show_str;
......@@ -4372,15 +4378,17 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[ISE_INTERVAL_FIELD]->store(ival->str, ival->length, scs);
/* starts & ends . STARTS is always set - see sql_yacc.yy */
et.time_zone->gmt_sec_to_TIME(&time, et.starts);
sch_table->field[ISE_STARTS]->set_notnull();
sch_table->field[ISE_STARTS]->
store_time(&et.starts, MYSQL_TIMESTAMP_DATETIME);
store_time(&time, MYSQL_TIMESTAMP_DATETIME);
if (!et.ends_null)
{
et.time_zone->gmt_sec_to_TIME(&time, et.ends);
sch_table->field[ISE_ENDS]->set_notnull();
sch_table->field[ISE_ENDS]->
store_time(&et.ends, MYSQL_TIMESTAMP_DATETIME);
store_time(&time, MYSQL_TIMESTAMP_DATETIME);
}
}
else
......@@ -4388,9 +4396,10 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
/* type */
sch_table->field[ISE_EVENT_TYPE]->store(STRING_WITH_LEN("ONE TIME"), scs);
et.time_zone->gmt_sec_to_TIME(&time, et.execute_at);
sch_table->field[ISE_EXECUTE_AT]->set_notnull();
sch_table->field[ISE_EXECUTE_AT]->
store_time(&et.execute_at, MYSQL_TIMESTAMP_DATETIME);
store_time(&time, MYSQL_TIMESTAMP_DATETIME);
}
/* status */
......@@ -4407,7 +4416,6 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[ISE_ON_COMPLETION]->
store(STRING_WITH_LEN("PRESERVE"), scs);
int not_used=0;
number_to_datetime(et.created, &time, 0, &not_used);
DBUG_ASSERT(not_used==0);
sch_table->field[ISE_CREATED]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
......@@ -4417,11 +4425,12 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[ISE_LAST_ALTERED]->
store_time(&time, MYSQL_TIMESTAMP_DATETIME);
if (et.last_executed.year)
if (et.last_executed)
{
et.time_zone->gmt_sec_to_TIME(&time, et.last_executed);
sch_table->field[ISE_LAST_EXECUTED]->set_notnull();
sch_table->field[ISE_LAST_EXECUTED]->
store_time(&et.last_executed, MYSQL_TIMESTAMP_DATETIME);
store_time(&time, MYSQL_TIMESTAMP_DATETIME);
}
sch_table->field[ISE_EVENT_COMMENT]->
......@@ -5427,6 +5436,7 @@ ST_FIELD_INFO events_fields_info[]=
{"EVENT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Db"},
{"EVENT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"},
{"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"},
{"TIME_ZONE", 64, MYSQL_TYPE_STRING, 0, 0, "Time zone"},
{"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0},
{"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
{"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"},
......
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