summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <kostja@vajra.(none)>2007-04-05 15:49:46 +0400
committerunknown <kostja@vajra.(none)>2007-04-05 15:49:46 +0400
commita36054f4201568fac1717b451040ffa7925900a2 (patch)
treea91b4f984fd6317085b8bd7e94cd91134184aab6
parenta0c4e184f80de8db3b9d1340715502454ee09ef6 (diff)
parentfa1d637e896d71932adcd1451c1564f724590189 (diff)
downloadmariadb-git-a36054f4201568fac1717b451040ffa7925900a2.tar.gz
Merge bk-internal.mysql.com:/home/bk/mysql-5.1-runtime
into vajra.(none):/opt/local/work/mysql-5.1-c1 mysql-test/r/events_bugs.result: Auto merged mysql-test/r/events_scheduling.result: Auto merged mysql-test/t/events.test: Auto merged mysql-test/t/events_scheduling.test: Auto merged sql/event_data_objects.h: Auto merged sql/event_db_repository.h: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql/set_var.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_show.cc: Auto merged sql/table.h: Auto merged sql/share/errmsg.txt: Auto merged mysql-test/r/events.result: e Use local. mysql-test/r/events_restart_phase1.result: Use local mysql-test/r/events_time_zone.result: SCCS merged mysql-test/t/events_restart_phase1.test: Use local sql/event_data_objects.cc: Use local sql/event_db_repository.cc: Manual merge. sql/event_queue.cc: Manual merge. sql/events.cc: Manual merge.
-rw-r--r--mysql-test/r/events.result419
-rw-r--r--mysql-test/r/events_bugs.result150
-rw-r--r--mysql-test/r/events_restart_phase1.result22
-rw-r--r--mysql-test/r/events_restart_phase2.result48
-rw-r--r--mysql-test/r/events_restart_phase3.result10
-rw-r--r--mysql-test/r/events_scheduling.result4
-rw-r--r--mysql-test/r/events_time_zone.result142
-rw-r--r--mysql-test/r/events_trans.result159
-rw-r--r--mysql-test/t/events.test374
-rw-r--r--mysql-test/t/events_bugs.test185
-rw-r--r--mysql-test/t/events_restart_phase1.test46
-rw-r--r--mysql-test/t/events_restart_phase2.test44
-rw-r--r--mysql-test/t/events_restart_phase3.test22
-rw-r--r--mysql-test/t/events_scheduling.test4
-rw-r--r--mysql-test/t/events_time_zone.test171
-rw-r--r--mysql-test/t/events_trans.test159
-rw-r--r--sql/event_data_objects.cc160
-rw-r--r--sql/event_data_objects.h3
-rw-r--r--sql/event_db_repository.cc654
-rw-r--r--sql/event_db_repository.h28
-rw-r--r--sql/event_queue.cc79
-rw-r--r--sql/event_queue.h41
-rw-r--r--sql/event_scheduler.cc99
-rw-r--r--sql/event_scheduler.h25
-rw-r--r--sql/events.cc956
-rw-r--r--sql/events.h106
-rw-r--r--sql/mysqld.cc46
-rw-r--r--sql/set_var.cc65
-rw-r--r--sql/set_var.h6
-rw-r--r--sql/share/errmsg.txt4
-rw-r--r--sql/sp_head.cc28
-rw-r--r--sql/sql_db.cc2
-rw-r--r--sql/sql_parse.cc53
-rw-r--r--sql/sql_show.cc25
-rw-r--r--sql/sql_test.cc2
-rw-r--r--sql/table.cc245
-rw-r--r--sql/table.h18
37 files changed, 2810 insertions, 1794 deletions
diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result
index 990f1aee6fa..7125b13cb88 100644
--- a/mysql-test/r/events.result
+++ b/mysql-test/r/events.result
@@ -1,4 +1,8 @@
-create database if not exists events_test;
+drop database if exists events_test;
+drop database if exists db_x;
+drop database if exists mysqltest_db2;
+drop database if exists mysqltest_no_such_database;
+create database events_test;
use events_test;
CREATE USER pauline@localhost;
CREATE DATABASE db_x;
@@ -193,7 +197,7 @@ create event ðóóò21 on schedule every '50:23:59:95' day_second COMMENT 'òîâà å 1
SHOW CREATE EVENT ðóóò21;
Event sql_mode time_zone Create Event
ðóóò21 SYSTEM CREATE EVENT `ðóóò21` ON SCHEDULE EVERY '51 0:0:35' DAY_SECOND STARTS '#' ON COMPLETION NOT PRESERVE ENABLE COMMENT 'òîâà å 1251 êîìåíòàð' DO select 1
-insert into mysql.event (db, name, body, definer, interval_value, interval_field, originator) values (database(), "root22", "select 1", user(), 100, "SECOND_MICROSECOND", 1);
+insert into mysql.event (db, name, body, definer, interval_value, interval_field) values (database(), "root22", "select 1", user(), 100, "SECOND_MICROSECOND");
show create event root22;
ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
SHOW EVENTS;
@@ -223,73 +227,180 @@ drop event root19;
drop event root20;
drop event ðóóò21;
set names latin1;
+Create a test event. Only event metadata is relevant,
+the actual schedule and body are not.
CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-events_test intact_check root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED 1
-ALTER TABLE mysql.event ADD dummy INT FIRST;
-SHOW EVENTS;
-ERROR HY000: Column count of mysql.event is wrong. Expected 18, found 19. The table is probably corrupted
-ALTER TABLE mysql.event DROP dummy, ADD dummy2 VARCHAR(64) FIRST;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test intact_check root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED
+Try to alter mysql.event: the server should fail to load
+event information after mysql.event was tampered with.
+
+First, let's add a column to the end and make sure everything
+works as before
+
+ALTER TABLE mysql.event ADD dummy INT;
SHOW EVENTS;
-ERROR HY000: Column count of mysql.event is wrong. Expected 18, found 19. The table is probably corrupted
-ALTER TABLE mysql.event DROP dummy2;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test intact_check root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+event_name
+intact_check
+SHOW CREATE EVENT intact_check;
+Event sql_mode time_zone Create Event
+intact_check SYSTEM CREATE EVENT `intact_check` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT PRESERVE ENABLE DO SELECT "nothing"
+DROP EVENT no_such_event;
+ERROR HY000: Unknown event 'no_such_event'
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+DROP EVENT intact_check_1;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_2;
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note 1008 Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name Value
+event_scheduler OFF
+SET GLOBAL event_scheduler=OFF;
+ALTER TABLE mysql.event DROP dummy;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+
+Now let's add a column to the first position: the server
+expects to see event schema name there
+
+ALTER TABLE mysql.event ADD dummy INT FIRST;
SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-events_test intact_check root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED 1
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+SHOW CREATE EVENT intact_check;
+ERROR HY000: Unknown event 'intact_check'
+DROP EVENT no_such_event;
+ERROR HY000: Unknown event 'no_such_event'
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ERROR HY000: Unknown event 'intact_check_1'
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_1;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_2;
+ERROR HY000: Unknown event 'intact_check_2'
+DROP EVENT intact_check;
+ERROR HY000: Unknown event 'intact_check'
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note 1008 Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name Value
+event_scheduler OFF
+SET GLOBAL event_scheduler=OFF;
+Clean up
+ALTER TABLE mysql.event DROP dummy;
+DELETE FROM mysql.event;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+Back up the table, further changes are not reversible
CREATE TABLE event_like LIKE mysql.event;
INSERT INTO event_like SELECT * FROM mysql.event;
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see the error log for details
-ALTER TABLE mysql.event MODIFY db char(20) character set utf8 collate utf8_bin default '';
-SHOW CREATE TABLE mysql.event;
-Table Create Table
-event CREATE TABLE `event` (
- `db` char(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `name` char(64) NOT NULL DEFAULT '',
- `body` longblob NOT NULL,
- `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
- `execute_at` datetime DEFAULT NULL,
- `interval_value` int(11) DEFAULT NULL,
- `interval_field` enum('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND') DEFAULT NULL,
- `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `last_executed` datetime DEFAULT NULL,
- `starts` datetime DEFAULT NULL,
- `ends` datetime DEFAULT NULL,
- `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED',
- `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 '',
- `originator` int(10) NOT NULL,
- `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',
- PRIMARY KEY (`db`,`name`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events'
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see the error log for details
-ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default '';
-"This should work"
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-events_test intact_check root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED 1
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see the error log for details
-ALTER TABLE mysql.event MODIFY db varchar(64) character set utf8 collate utf8_bin default '';
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Cannot load from mysql.event. The table is probably corrupted. Please see the error log for details
+
+Drop some columns and try more checks.
+
+
ALTER TABLE mysql.event DROP comment, DROP starts;
+SHOW EVENTS;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ERROR HY000: Column count of mysql.event is wrong. Expected 18, found 16. The table is probably corrupted
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+SHOW CREATE EVENT intact_check;
+ERROR HY000: Cannot load from mysql.event. The table is probably corrupted
+DROP EVENT no_such_event;
+ERROR HY000: Unknown event 'no_such_event'
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ERROR HY000: Column count of mysql.event is wrong. Expected 17, found 15. The table is probably corrupted
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ERROR HY000: Unknown event 'intact_check_1'
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_1;
+ERROR HY000: Unknown event 'intact_check_1'
+DROP EVENT intact_check_2;
+ERROR HY000: Unknown event 'intact_check_2'
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note 1008 Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name Value
+event_scheduler OFF
+SET GLOBAL event_scheduler=OFF;
+
+Now drop the table, and test again
+
+
DROP TABLE mysql.event;
+SHOW EVENTS;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+SHOW CREATE EVENT intact_check;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT no_such_event;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT intact_check_1;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT intact_check_2;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP EVENT intact_check;
+ERROR 42S02: Table 'mysql.event' doesn't exist
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+Warnings:
+Note 1008 Can't drop database 'mysqltest_no_such_database'; database doesn't exist
+Error 1146 Table 'mysql.event' doesn't exist
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+OK, there is an unnecessary warning about the non-existent table
+but it's not easy to fix and no one complained about it.
+A similar warning is printed if mysql.proc is missing.
+SHOW WARNINGS;
+Level Code Message
+Error 1146 Table 'mysql.event' doesn't exist
+SELECT @@event_scheduler;
+@@event_scheduler
+OFF
+SHOW VARIABLES LIKE 'event_scheduler';
+Variable_name Value
+event_scheduler OFF
+SET GLOBAL event_scheduler=OFF;
+Restore the original table.
CREATE TABLE mysql.event like event_like;
-INSERT INTO mysql.event SELECT * FROM event_like;
DROP TABLE event_like;
SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-events_test intact_check root@localhost SYSTEM RECURRING NULL 10 # # NULL ENABLED 1
-DROP EVENT intact_check;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5;
select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event;
db name body definer convert_tz(execute_at, 'UTC', 'SYSTEM') on_completion
@@ -401,5 +512,197 @@ ERROR 42000: Incorrect database name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
SHOW EVENTS FROM ``;
ERROR 42000: Incorrect database name ''
SHOW EVENTS FROM `events\\test`;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+
+LOCK TABLES mode.
+
+create table t1 (a int);
+create event e1 on schedule every 10 hour do select 1;
+lock table t1 read;
+show create event e1;
+Event sql_mode time_zone Create Event
+e1 SYSTEM CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 disable;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e2;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+unlock tables;
+lock table t1 write;
+show create event e1;
+Event sql_mode time_zone Create Event
+e1 SYSTEM CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 disable;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e2;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+drop event e1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+unlock tables;
+lock table t1 read, mysql.event read;
+show create event e1;
+Event sql_mode time_zone Create Event
+e1 SYSTEM CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 disable;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e2;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+unlock tables;
+lock table t1 write, mysql.event read;
+show create event e1;
+Event sql_mode time_zone Create Event
+e1 SYSTEM CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 disable;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+alter event e2 rename to e3;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e2;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+drop event e1;
+ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+unlock tables;
+lock table t1 read, mysql.event write;
+ERROR HY000: You can't combine write-locking of system tables with other tables or lock types
+lock table t1 write, mysql.event write;
+ERROR HY000: You can't combine write-locking of system tables with other tables or lock types
+lock table mysql.event write;
+show create event e1;
+Event sql_mode time_zone Create Event
+e1 SYSTEM CREATE EVENT `e1` ON SCHEDULE EVERY 10 HOUR STARTS '#' ON COMPLETION NOT PRESERVE ENABLE DO select 1
+select event_name from information_schema.events;
+event_name
+e1
+create event e2 on schedule every 10 hour do select 1;
+alter event e2 disable;
+alter event e2 rename to e3;
+drop event e3;
+drop event e1;
+unlock tables;
+Make sure we have left no events
+select event_name from information_schema.events;
+event_name
+
+Events in sub-statements, events and prelocking
+
+
+create event e1 on schedule every 10 hour do select 1;
+create function f1() returns int
+begin
+show create event e1;
+return 1;
+end|
+ERROR 0A000: Not allowed to return a result set from a function
+create trigger trg before insert on t1 for each row
+begin
+show create event e1;
+end|
+ERROR 0A000: Not allowed to return a result set from a trigger
+create function f1() returns int
+begin
+select event_name from information_schema.events;
+return 1;
+end|
+ERROR 0A000: Not allowed to return a result set from a function
+create trigger trg before insert on t1 for each row
+begin
+select event_name from information_schema.events;
+end|
+ERROR 0A000: Not allowed to return a result set from a trigger
+create function f1() returns int
+begin
+create event e2 on schedule every 10 hour do select 1;
+return 1;
+end|
+ERROR HY000: Recursion of EVENT DDL statements is forbidden when body is present
+create function f1() returns int
+begin
+alter event e1 rename to e2;
+return 1;
+end|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+create function f1() returns int
+begin
+drop event e2;
+return 1;
+end|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+----------------------------------------------------------------------
+create trigger trg before insert on t1 for each row
+begin
+set new.a= f1();
+end|
+create function f1() returns int
+begin
+call p1();
+return 0;
+end|
+create procedure p1()
+begin
+select event_name from information_schema.events;
+end|
+insert into t1 (a) values (1)|
+ERROR 0A000: Not allowed to return a result set from a trigger
+drop procedure p1|
+create procedure p1()
+begin
+show create event e1;
+end|
+insert into t1 (a) values (1)|
+ERROR 0A000: Not allowed to return a result set from a trigger
+drop procedure p1|
+create procedure p1()
+begin
+create temporary table tmp select event_name from information_schema.events;
+end|
+expected to work, since we redirect the output into a tmp table
+insert into t1 (a) values (1)|
+select * from tmp|
+event_name
+e1
+drop temporary table tmp|
+drop procedure p1|
+create procedure p1()
+begin
+alter event e1 rename to e2;
+end|
+insert into t1 (a) values (1)|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+drop procedure p1|
+create procedure p1()
+begin
+drop event e1;
+end|
+insert into t1 (a) values (1)|
+ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+drop table t1|
+drop event e1|
drop database events_test;
diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result
index c2278296bee..ba82a7d7b1b 100644
--- a/mysql-test/r/events_bugs.result
+++ b/mysql-test/r/events_bugs.result
@@ -1,4 +1,7 @@
-create database if not exists events_test;
+drop database if exists events_test;
+drop database if exists mysqltest_db1;
+drop database if exists mysqltest_db2;
+create database events_test;
use events_test;
CREATE EVENT lower_case ON SCHEDULE EVERY 1 MINUTE DO SELECT 1;
CREATE EVENT Lower_case ON SCHEDULE EVERY 2 MINUTE DO SELECT 2;
@@ -381,4 +384,149 @@ ERROR 42000: Access denied; you need the SUPER privilege for this operation
DROP EVENT e1;
ERROR HY000: Unknown event 'e1'
DROP USER mysqltest_u1@localhost;
+SET GLOBAL EVENT_SCHEDULER= OFF;
+SET @save_time_zone= @@TIME_ZONE;
+SET TIME_ZONE= '+00:00';
+SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost +00:00 RECURRING NULL 1 DAY 2005-12-31 23:58:59 NULL ENABLED
+SET TIME_ZONE= '-01:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost -01:00 RECURRING NULL 1 DAY 2000-01-01 00:00:00 NULL ENABLED
+SET TIME_ZONE= '+02:00';
+ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
+ ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost +02:00 ONE TIME 2000-01-02 00:00:00 NULL NULL NULL NULL DISABLED
+SET TIME_ZONE= '-03:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
+ ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost -03:00 RECURRING NULL 1 DAY 2005-12-31 20:58:59 2030-01-03 00:00:00 DISABLED
+SET TIME_ZONE= '+04:00';
+ALTER EVENT e1 DO SELECT 2;
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost -03:00 RECURRING NULL 1 DAY 2005-12-31 20:58:59 2030-01-03 00:00:00 ENABLED
+DROP EVENT e1;
+SET TIME_ZONE='+05:00';
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+SELECT 1;
+SET TIMESTAMP= @@TIMESTAMP + 1;
+SET TIME_ZONE='-05:00';
+CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+SELECT 1;
+SET TIMESTAMP= @@TIMESTAMP + 1;
+SET TIME_ZONE='+00:00';
+CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+SELECT 1;
+SELECT * FROM INFORMATION_SCHEMA.EVENTS ORDER BY event_name;
+EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER TIME_ZONE EVENT_BODY EVENT_DEFINITION EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD SQL_MODE STARTS ENDS STATUS ON_COMPLETION CREATED LAST_ALTERED LAST_EXECUTED EVENT_COMMENT
+NULL events_test e1 root@localhost +05:00 SQL SELECT 1 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED NOT PRESERVE 2005-12-31 23:58:59 2005-12-31 23:58:59 NULL
+NULL events_test e2 root@localhost -05:00 SQL SELECT 1 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED NOT PRESERVE 2005-12-31 23:59:00 2005-12-31 23:59:00 NULL
+NULL events_test e3 root@localhost +00:00 SQL SELECT 1 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED NOT PRESERVE 2005-12-31 23:59:01 2005-12-31 23:59:01 NULL
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost +05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED
+events_test e2 root@localhost -05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED
+events_test e3 root@localhost +00:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED
+SHOW CREATE EVENT e1;
+Event sql_mode time_zone Create Event
+e1 +05:00 CREATE EVENT `e1` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO SELECT 1
+SHOW CREATE EVENT e2;
+Event sql_mode time_zone Create Event
+e2 -05:00 CREATE EVENT `e2` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO SELECT 1
+SHOW CREATE EVENT e3;
+Event sql_mode time_zone Create Event
+e3 +00:00 CREATE EVENT `e3` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO SELECT 1
+The following should fail, and nothing should be altered.
+ALTER EVENT e1 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. Event has not been altered
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' DISABLE;
+ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been altered
+The following should give warnings, and nothing should be created.
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00'
+DO
+SELECT 1;
+Warnings:
+Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' DISABLE
+DO
+SELECT 1;
+Warnings:
+Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
+SELECT 1;
+Warnings:
+Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
+DO
+SELECT 1;
+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
+events_test e1 root@localhost +05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED
+events_test e2 root@localhost -05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED
+events_test e3 root@localhost +00:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED
+The following should succeed giving a warning.
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
+Warnings:
+Note 1533 Event execution time is in the past. Event has been disabled
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
+DO
+SELECT 1;
+Warnings:
+Note 1533 Event execution time is in the past. Event has been disabled
+CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
+ ON COMPLETION PRESERVE
+DO
+SELECT 1;
+Warnings:
+Note 1533 Event execution time is in the past. Event has been disabled
+The following should succeed without warnings.
+ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
+ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
+CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
+SELECT 1;
+CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
+DO
+SELECT 1;
+CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
+ ON COMPLETION PRESERVE DISABLE
+DO
+SELECT 1;
+SHOW EVENTS;
+Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status
+events_test e1 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED
+events_test e2 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 NULL ENABLED
+events_test e3 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED
+events_test e4 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED
+events_test e5 root@localhost +00:00 ONE TIME 1999-01-01 00:00:00 NULL NULL NULL NULL DISABLED
+events_test e6 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 NULL ENABLED
+events_test e7 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED
+events_test e8 root@localhost +00:00 ONE TIME 1999-01-01 00:00:00 NULL NULL NULL NULL DISABLED
+DROP EVENT e8;
+DROP EVENT e7;
+DROP EVENT e6;
+DROP EVENT e5;
+DROP EVENT e4;
+DROP EVENT e3;
+DROP EVENT e2;
+DROP EVENT e1;
+SET TIME_ZONE=@save_time_zone;
drop database events_test;
diff --git a/mysql-test/r/events_restart_phase1.result b/mysql-test/r/events_restart_phase1.result
index 75e3ef5176f..7b1de62f2ef 100644
--- a/mysql-test/r/events_restart_phase1.result
+++ b/mysql-test/r/events_restart_phase1.result
@@ -1,12 +1,16 @@
-create database if not exists mysqltest_events_test;
-use mysqltest_events_test;
set global event_scheduler=off;
+drop database if exists events_test;
+create database events_test;
+use events_test;
create table execution_log(name char(10));
-create event abc1 on schedule every 1 second do insert into execution_log value('abc1');
-create event abc2 on schedule every 1 second do insert into execution_log value('abc2');
-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',1,'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',1,'SYSTEM');
+create event abc1 on schedule every 1 second do
+insert into execution_log value('abc1');
+create event abc2 on schedule every 1 second do
+insert into execution_log value('abc2');
+create event abc3 on schedule every 1 second do
+insert into execution_log value('abc3');
+create table event_like like mysql.event;
+insert into event_like select * from mysql.event;
+alter table mysql.event
+change column body body longtext character set utf8 collate utf8_bin;
"Now we restart the server"
diff --git a/mysql-test/r/events_restart_phase2.result b/mysql-test/r/events_restart_phase2.result
index 703cb92324f..60ddf06bf23 100644
--- a/mysql-test/r/events_restart_phase2.result
+++ b/mysql-test/r/events_restart_phase2.result
@@ -1,6 +1,42 @@
-use mysqltest_events_test;
-"Should get 0 rows because the queue aborted run
-select distinct name from execution_log order by name;
-name
-delete from mysql.event where name like 'bad%';
-"Now restart the server again"
+use events_test;
+select @@event_scheduler;
+@@event_scheduler
+DISABLED
+show events;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+select event_name from information_schema.events;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+show create event intact_check;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+drop event no_such_event;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+create event intact_check_1 on schedule every 5 hour do select 5;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+alter event intact_check_1 on schedule every 8 hour do select 8;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+alter event intact_check_1 rename to intact_check_2;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+drop event intact_check_1;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+drop event intact_check_2;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+drop event intact_check;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+set global event_scheduler=on;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+set global event_scheduler=off;
+ERROR HY000: Cannot proceed because system tables used by Event Scheduler were found damaged at server start
+show variables like 'event_scheduler';
+Variable_name Value
+event_scheduler DISABLED
+Make sure that we still can create and drop databases,
+and no warnings are produced.
+drop database if exists mysqltest_database_not_exists;
+Warnings:
+Note 1008 Can't drop database 'mysqltest_database_not_exists'; database doesn't exist
+create database mysqltest_db1;
+drop database mysqltest_db1;
+Restore the original mysql.event table
+drop table mysql.event;
+rename table event_like to mysql.event;
+Now let's restart the server again
diff --git a/mysql-test/r/events_restart_phase3.result b/mysql-test/r/events_restart_phase3.result
index e653d6a7c23..8ac00fdc70d 100644
--- a/mysql-test/r/events_restart_phase3.result
+++ b/mysql-test/r/events_restart_phase3.result
@@ -1,12 +1,12 @@
-use mysqltest_events_test;
+use events_test;
+select @@event_scheduler;
+@@event_scheduler
+ON
"Should get 3 rows : abc1, abc2, abc3
select distinct name from execution_log order by name;
name
abc1
abc2
abc3
-drop event abc1;
-drop event abc2;
-drop event abc3;
drop table execution_log;
-drop database mysqltest_events_test;
+drop database events_test;
diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result
index 12f270748a6..33c0781d4e7 100644
--- a/mysql-test/r/events_scheduling.result
+++ b/mysql-test/r/events_scheduling.result
@@ -1,6 +1,8 @@
CREATE DATABASE IF NOT EXISTS events_test;
USE events_test;
SET GLOBAL event_scheduler=OFF;
+Try agian to make sure it's allowed
+SET GLOBAL event_scheduler=OFF;
SHOW VARIABLES LIKE 'event_scheduler';
Variable_name Value
event_scheduler OFF
@@ -13,6 +15,8 @@ SHOW VARIABLES LIKE 'event_scheduler';
Variable_name Value
event_scheduler OFF
SET GLOBAL event_scheduler=ON;
+Try again to make sure it's allowed
+SET GLOBAL event_scheduler=ON;
SHOW VARIABLES LIKE 'event_scheduler';
Variable_name Value
event_scheduler ON
diff --git a/mysql-test/r/events_time_zone.result b/mysql-test/r/events_time_zone.result
index ec5cae88f4f..b20aa445183 100644
--- a/mysql-test/r/events_time_zone.result
+++ b/mysql-test/r/events_time_zone.result
@@ -3,148 +3,6 @@ CREATE DATABASE mysqltest_db1;
USE mysqltest_db1;
SET GLOBAL EVENT_SCHEDULER= OFF;
SET @save_time_zone= @@TIME_ZONE;
-SET TIME_ZONE= '+00:00';
-SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost +00:00 RECURRING NULL 1 DAY 2005-12-31 23:58:59 NULL ENABLED 1
-SET TIME_ZONE= '-01:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost -01:00 RECURRING NULL 1 DAY 2000-01-01 00:00:00 NULL ENABLED 1
-SET TIME_ZONE= '+02:00';
-ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
- ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost +02:00 ONE TIME 2000-01-02 00:00:00 NULL NULL NULL NULL DISABLED 1
-SET TIME_ZONE= '-03:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
- ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost -03:00 RECURRING NULL 1 DAY 2005-12-31 20:58:59 2030-01-03 00:00:00 DISABLED 1
-SET TIME_ZONE= '+04:00';
-ALTER EVENT e1 DO SELECT 2;
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost -03:00 RECURRING NULL 1 DAY 2005-12-31 20:58:59 2030-01-03 00:00:00 ENABLED 1
-DROP EVENT e1;
-SET TIME_ZONE='+05:00';
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-SELECT 1;
-SET TIMESTAMP= @@TIMESTAMP + 1;
-SET TIME_ZONE='-05:00';
-CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-SELECT 1;
-SET TIMESTAMP= @@TIMESTAMP + 1;
-SET TIME_ZONE='+00:00';
-CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
-SELECT 1;
-SELECT * FROM INFORMATION_SCHEMA.EVENTS;
-EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER TIME_ZONE EVENT_BODY EVENT_DEFINITION EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD SQL_MODE STARTS ENDS STATUS ON_COMPLETION CREATED LAST_ALTERED LAST_EXECUTED EVENT_COMMENT ORIGINATOR
-NULL mysqltest_db1 e1 root@localhost +05:00 SQL SELECT 1 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED NOT PRESERVE 2005-12-31 23:58:59 2005-12-31 23:58:59 NULL 1
-NULL mysqltest_db1 e2 root@localhost -05:00 SQL SELECT 1 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED NOT PRESERVE 2005-12-31 23:59:00 2005-12-31 23:59:00 NULL 1
-NULL mysqltest_db1 e3 root@localhost +00:00 SQL SELECT 1 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED NOT PRESERVE 2005-12-31 23:59:01 2005-12-31 23:59:01 NULL 1
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost +05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED 1
-mysqltest_db1 e2 root@localhost -05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED 1
-mysqltest_db1 e3 root@localhost +00:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED 1
-SHOW CREATE EVENT e1;
-Event sql_mode time_zone Create Event
-e1 +05:00 CREATE EVENT `e1` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO SELECT 1
-SHOW CREATE EVENT e2;
-Event sql_mode time_zone Create Event
-e2 -05:00 CREATE EVENT `e2` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO SELECT 1
-SHOW CREATE EVENT e3;
-Event sql_mode time_zone Create Event
-e3 +00:00 CREATE EVENT `e3` ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO SELECT 1
-The following should fail, and nothing should be altered.
-ALTER EVENT e1 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. Event has not been altered
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' DISABLE;
-ERROR HY000: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been altered
-The following should give warnings, and nothing should be created.
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00'
-DO
-SELECT 1;
-Warnings:
-Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' DISABLE
-DO
-SELECT 1;
-Warnings:
-Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
-SELECT 1;
-Warnings:
-Note 1584 Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. Event has not been created
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
-DO
-SELECT 1;
-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 Originator
-mysqltest_db1 e1 root@localhost +05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED 1
-mysqltest_db1 e2 root@localhost -05:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED 1
-mysqltest_db1 e3 root@localhost +00:00 RECURRING NULL 1 DAY 2006-01-01 00:00:00 NULL ENABLED 1
-The following should succeed giving a warning.
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
-Warnings:
-Note 1533 Event execution time is in the past. Event has been disabled
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
-DO
-SELECT 1;
-Warnings:
-Note 1533 Event execution time is in the past. Event has been disabled
-CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
- ON COMPLETION PRESERVE
-DO
-SELECT 1;
-Warnings:
-Note 1533 Event execution time is in the past. Event has been disabled
-The following should succeed without warnings.
-ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
-ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
-CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
-SELECT 1;
-CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
-DO
-SELECT 1;
-CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
- ON COMPLETION PRESERVE DISABLE
-DO
-SELECT 1;
-SHOW EVENTS;
-Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator
-mysqltest_db1 e1 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED 1
-mysqltest_db1 e2 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 NULL ENABLED 1
-mysqltest_db1 e3 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED 1
-mysqltest_db1 e4 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED 1
-mysqltest_db1 e5 root@localhost +00:00 ONE TIME 1999-01-01 00:00:00 NULL NULL NULL NULL DISABLED 1
-mysqltest_db1 e6 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 NULL ENABLED 1
-mysqltest_db1 e7 root@localhost +00:00 RECURRING NULL 1 HOUR 1999-01-01 00:00:00 1999-01-02 00:00:00 DISABLED 1
-mysqltest_db1 e8 root@localhost +00:00 ONE TIME 1999-01-01 00:00:00 NULL NULL NULL NULL DISABLED 1
-DROP EVENT e8;
-DROP EVENT e7;
-DROP EVENT e6;
-DROP EVENT e5;
-DROP EVENT e4;
-DROP EVENT e3;
-DROP EVENT e2;
-DROP EVENT e1;
CREATE TABLE t_step (step INT);
INSERT INTO t_step VALUES (@step);
CREATE FUNCTION round_to_step(i INT, n INT) RETURNS INT
diff --git a/mysql-test/r/events_trans.result b/mysql-test/r/events_trans.result
new file mode 100644
index 00000000000..145fb8be084
--- /dev/null
+++ b/mysql-test/r/events_trans.result
@@ -0,0 +1,159 @@
+drop database if exists events_test;
+drop database if exists mysqltest_db2;
+drop database if exists mysqltest_no_such_database;
+create database events_test;
+use events_test;
+
+Test that Events DDL issue an implicit COMMIT
+
+
+set autocommit=off;
+select @@autocommit;
+@@autocommit
+0
+create table t1 (a varchar(255)) engine=innodb;
+begin work;
+insert into t1 (a) values ("OK: create event");
+create event e1 on schedule every 1 day do select 1;
+rollback work;
+select * from t1;
+a
+OK: create event
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: alter event");
+alter event e1 on schedule every 2 day do select 2;
+rollback work;
+select * from t1;
+a
+OK: alter event
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: alter event rename");
+alter event e1 rename to e2;
+rollback work;
+select * from t1;
+a
+OK: alter event rename
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: drop event");
+drop event e2;
+rollback work;
+select * from t1;
+a
+OK: drop event
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: drop event if exists");
+drop event if exists e2;
+Warnings:
+Note 1305 Event e2 does not exist
+rollback work;
+select * from t1;
+a
+OK: drop event if exists
+delete from t1;
+commit work;
+create event e1 on schedule every 1 day do select 1;
+begin work;
+insert into t1 (a) values ("OK: create event if not exists");
+create event if not exists e1 on schedule every 2 day do select 2;
+Warnings:
+Note 1526 Event 'e1' already exists
+rollback work;
+select * from t1;
+a
+OK: create event if not exists
+delete from t1;
+commit work;
+
+Now check various error conditions: make sure we issue an
+implicit commit anyway
+
+begin work;
+insert into t1 (a) values ("OK: create event: event already exists");
+create event e1 on schedule every 2 day do select 2;
+ERROR HY000: Event 'e1' already exists
+rollback work;
+select * from t1;
+a
+OK: create event: event already exists
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: alter event rename: rename to same name");
+alter event e1 rename to e1;
+ERROR HY000: Same old and new event name
+rollback work;
+select * from t1;
+a
+OK: alter event rename: rename to same name
+delete from t1;
+commit work;
+create event e2 on schedule every 3 day do select 3;
+begin work;
+insert into t1 (a) values ("OK: alter event rename: destination exists");
+alter event e2 rename to e1;
+ERROR HY000: Event 'e1' already exists
+rollback work;
+select * from t1;
+a
+OK: alter event rename: destination exists
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: create event: database does not exist");
+create event mysqltest_no_such_database.e1 on schedule every 1 day do select 1;
+ERROR 42000: Unknown database 'mysqltest_no_such_database'
+rollback work;
+select * from t1;
+a
+OK: create event: database does not exist
+delete from t1;
+commit work;
+grant create, insert, select, delete on mysqltest_db2.*
+to mysqltest_user1@localhost;
+create database mysqltest_db2;
+set autocommit=off;
+select @@autocommit;
+@@autocommit
+0
+create table t1 (a varchar(255)) engine=innodb;
+begin work;
+insert into t1 (a) values ("OK: create event: insufficient privileges");
+create event e1 on schedule every 1 day do select 1;
+ERROR 42000: Access denied for user 'mysqltest_user1'@'localhost' to database 'mysqltest_db2'
+rollback work;
+select * from t1;
+a
+OK: create event: insufficient privileges
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: alter event: insufficient privileges");
+alter event e1 on schedule every 1 day do select 1;
+ERROR 42000: Access denied for user 'mysqltest_user1'@'localhost' to database 'mysqltest_db2'
+rollback work;
+select * from t1;
+a
+OK: alter event: insufficient privileges
+delete from t1;
+commit work;
+begin work;
+insert into t1 (a) values ("OK: drop event: insufficient privileges");
+drop event e1;
+ERROR 42000: Access denied for user 'mysqltest_user1'@'localhost' to database 'mysqltest_db2'
+rollback work;
+select * from t1;
+a
+OK: drop event: insufficient privileges
+delete from t1;
+commit work;
+drop user mysqltest_user1@localhost;
+drop database mysqltest_db2;
+drop database events_test;
diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test
index 67baafd8b76..575f9984a79 100644
--- a/mysql-test/t/events.test
+++ b/mysql-test/t/events.test
@@ -1,7 +1,13 @@
# Can't test with embedded server that doesn't support grants
-- source include/not_embedded.inc
-create database if not exists events_test;
+--disable_warnings
+drop database if exists events_test;
+drop database if exists db_x;
+drop database if exists mysqltest_db2;
+drop database if exists mysqltest_no_such_database;
+--enable_warnings
+create database events_test;
use events_test;
#
@@ -215,62 +221,161 @@ set names latin1;
#
#
-# mysql.event intact checking start
+# mysql.event intact checking
+# Check that the server does not crash if
+# one has destroyed or tampered with the event table.
+# Please see see for events_restart_phase* tests to
+# see the server behavior at start up with bad mysql.event
+# table.
#
-# There should be at least 1 second between the ALTERs or we can't catch the change of create_time!!
+#
+--echo Create a test event. Only event metadata is relevant,
+--echo the actual schedule and body are not.
#
CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
--replace_column 8 # 9 #
SHOW EVENTS;
-ALTER TABLE mysql.event ADD dummy INT FIRST;
---error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
-SHOW EVENTS;
-ALTER TABLE mysql.event DROP dummy, ADD dummy2 VARCHAR(64) FIRST;
---error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
-SHOW EVENTS;
-ALTER TABLE mysql.event DROP dummy2;
+#
+--echo Try to alter mysql.event: the server should fail to load
+--echo event information after mysql.event was tampered with.
+--echo
+--echo First, let's add a column to the end and make sure everything
+--echo works as before
+--echo
+ALTER TABLE mysql.event ADD dummy INT;
--replace_column 8 # 9 #
SHOW EVENTS;
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+SHOW CREATE EVENT intact_check;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT no_such_event;
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_1;
+DROP EVENT intact_check_2;
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+# Clean up
+ALTER TABLE mysql.event DROP dummy;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+--echo
+--echo Now let's add a column to the first position: the server
+--echo expects to see event schema name there
+--echo
+ALTER TABLE mysql.event ADD dummy INT FIRST;
+--error ER_CANNOT_LOAD_FROM_TABLE
+SHOW EVENTS;
+--error ER_CANNOT_LOAD_FROM_TABLE
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+--error ER_EVENT_DOES_NOT_EXIST
+SHOW CREATE EVENT intact_check;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT no_such_event;
+--error ER_CANNOT_LOAD_FROM_TABLE
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_1;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check;
+# Should work OK
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+--echo Clean up
+ALTER TABLE mysql.event DROP dummy;
+DELETE FROM mysql.event;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+--echo Back up the table, further changes are not reversible
CREATE TABLE event_like LIKE mysql.event;
INSERT INTO event_like SELECT * FROM mysql.event;
-#sleep a bit or we won't catch the change of time
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
---error ER_CANNOT_LOAD_FROM_TABLE
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
-ALTER TABLE mysql.event MODIFY db char(20) character set utf8 collate utf8_bin default '';
-#wait a bit or we won't see the difference because of seconds resolution
---sleep 1.1
-SHOW CREATE TABLE mysql.event;
+--echo
+--echo Drop some columns and try more checks.
+--echo
+--echo
+ALTER TABLE mysql.event DROP comment, DROP starts;
--error ER_CANNOT_LOAD_FROM_TABLE
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default '';
---sleep 1.1
---echo "This should work"
---replace_column 8 # 9 #
SHOW EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
--error ER_CANNOT_LOAD_FROM_TABLE
SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event MODIFY db varchar(64) character set utf8 collate utf8_bin default '';
---sleep 1.1
--error ER_CANNOT_LOAD_FROM_TABLE
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
---sleep 1.1
-ALTER TABLE mysql.event DROP comment, DROP starts;
---sleep 1.1
+SHOW CREATE EVENT intact_check;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT no_such_event;
--error ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
-SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+--error ER_EVENT_DOES_NOT_EXIST
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_1;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT intact_check_2;
+# Should succeed
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+--echo
+--echo Now drop the table, and test again
+--echo
+--echo
DROP TABLE mysql.event;
+--error ER_NO_SUCH_TABLE
+SHOW EVENTS;
+--error ER_NO_SUCH_TABLE
+SELECT event_name FROM INFORMATION_SCHEMA.events;
+--error ER_NO_SUCH_TABLE
+SHOW CREATE EVENT intact_check;
+--error ER_NO_SUCH_TABLE
+DROP EVENT no_such_event;
+--error ER_NO_SUCH_TABLE
+CREATE EVENT intact_check_1 ON SCHEDULE EVERY 5 HOUR DO SELECT 5;
+--error ER_NO_SUCH_TABLE
+ALTER EVENT intact_check_1 ON SCHEDULE EVERY 8 HOUR DO SELECT 8;
+--error ER_NO_SUCH_TABLE
+ALTER EVENT intact_check_1 RENAME TO intact_check_2;
+--error ER_NO_SUCH_TABLE
+DROP EVENT intact_check_1;
+--error ER_NO_SUCH_TABLE
+DROP EVENT intact_check_2;
+--error ER_NO_SUCH_TABLE
+DROP EVENT intact_check;
+DROP DATABASE IF EXISTS mysqltest_no_such_database;
+CREATE DATABASE mysqltest_db2;
+DROP DATABASE mysqltest_db2;
+--echo OK, there is an unnecessary warning about the non-existent table
+--echo but it's not easy to fix and no one complained about it.
+--echo A similar warning is printed if mysql.proc is missing.
+SHOW WARNINGS;
+SELECT @@event_scheduler;
+SHOW VARIABLES LIKE 'event_scheduler';
+SET GLOBAL event_scheduler=OFF;
+--echo Restore the original table.
CREATE TABLE mysql.event like event_like;
-INSERT INTO mysql.event SELECT * FROM event_like;
DROP TABLE event_like;
--replace_column 8 # 9 #
SHOW EVENTS;
-DROP EVENT intact_check;
#
# mysql.event intact checking end
#
@@ -424,5 +529,200 @@ SHOW EVENTS FROM aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
SHOW EVENTS FROM ``;
SHOW EVENTS FROM `events\\test`;
+#
+# A check for events SQL under LOCK TABLES and in pre-locked mode.
+#
+--echo
+--echo LOCK TABLES mode.
+--echo
+#
+# SHOW CREATE EVENT and INFORMATION_SCHEMA.events are available and
+# cause an implicit lock/unlock of mysql.event table, regardless of the
+# currently locked tables.
+#
+create table t1 (a int);
+create event e1 on schedule every 10 hour do select 1;
+#
+lock table t1 read;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED
+drop event e2;
+--error ER_TABLE_NOT_LOCKED
+drop event e1;
+unlock tables;
+#
+lock table t1 write;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED
+drop event e2;
+--error ER_TABLE_NOT_LOCKED
+drop event e1;
+unlock tables;
+#
+lock table t1 read, mysql.event read;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e2;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e1;
+unlock tables;
+#
+lock table t1 write, mysql.event read;
+#
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create event e2 on schedule every 10 hour do select 1;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 disable;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+alter event e2 rename to e3;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e2;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop event e1;
+unlock tables;
+#
+--error ER_WRONG_LOCK_OF_SYSTEM_TABLE
+lock table t1 read, mysql.event write;
+#
+--error ER_WRONG_LOCK_OF_SYSTEM_TABLE
+lock table t1 write, mysql.event write;
+#
+lock table mysql.event write;
+--replace_regex /STARTS '[^']+'/STARTS '#'/
+show create event e1;
+select event_name from information_schema.events;
+create event e2 on schedule every 10 hour do select 1;
+alter event e2 disable;
+alter event e2 rename to e3;
+drop event e3;
+drop event e1;
+unlock tables;
+--echo Make sure we have left no events
+select event_name from information_schema.events;
+--echo
+--echo Events in sub-statements, events and prelocking
+--echo
+--echo
+create event e1 on schedule every 10 hour do select 1;
+delimiter |;
+--error ER_SP_NO_RETSET
+create function f1() returns int
+begin
+ show create event e1;
+ return 1;
+end|
+--error ER_SP_NO_RETSET
+create trigger trg before insert on t1 for each row
+begin
+ show create event e1;
+end|
+--error ER_SP_NO_RETSET
+create function f1() returns int
+begin
+ select event_name from information_schema.events;
+ return 1;
+end|
+--error ER_SP_NO_RETSET
+create trigger trg before insert on t1 for each row
+begin
+ select event_name from information_schema.events;
+end|
+--error ER_EVENT_RECURSION_FORBIDDEN
+create function f1() returns int
+begin
+ create event e2 on schedule every 10 hour do select 1;
+ return 1;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+create function f1() returns int
+begin
+ alter event e1 rename to e2;
+ return 1;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+create function f1() returns int
+begin
+ drop event e2;
+ return 1;
+end|
+--echo ----------------------------------------------------------------------
+create trigger trg before insert on t1 for each row
+begin
+ set new.a= f1();
+end|
+create function f1() returns int
+begin
+ call p1();
+ return 0;
+end|
+create procedure p1()
+begin
+ select event_name from information_schema.events;
+end|
+--error ER_SP_NO_RETSET
+insert into t1 (a) values (1)|
+drop procedure p1|
+create procedure p1()
+begin
+ show create event e1;
+end|
+--error ER_SP_NO_RETSET
+insert into t1 (a) values (1)|
+drop procedure p1|
+create procedure p1()
+begin
+ create temporary table tmp select event_name from information_schema.events;
+end|
+--echo expected to work, since we redirect the output into a tmp table
+insert into t1 (a) values (1)|
+select * from tmp|
+drop temporary table tmp|
+drop procedure p1|
+create procedure p1()
+begin
+ alter event e1 rename to e2;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 (a) values (1)|
+drop procedure p1|
+create procedure p1()
+begin
+ drop event e1;
+end|
+--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+insert into t1 (a) values (1)|
+drop table t1|
+drop event e1|
+delimiter ;|
drop database events_test;
diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test
index 56c44dc3a9f..8343c6b4bc6 100644
--- a/mysql-test/t/events_bugs.test
+++ b/mysql-test/t/events_bugs.test
@@ -1,7 +1,12 @@
# Can't test with embedded server that doesn't support grants
-- source include/not_embedded.inc
-create database if not exists events_test;
+--disable_warnings
+drop database if exists events_test;
+drop database if exists mysqltest_db1;
+drop database if exists mysqltest_db2;
+--enable_warnings
+create database events_test;
use events_test;
#
@@ -429,6 +434,184 @@ DROP USER mysqltest_u1@localhost;
#
+# 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
+#
+# Here we only check non-concurrent aspects of the patch.
+# For the actual tests of time zones please see events_time_zone.test
+#
+SET GLOBAL EVENT_SCHEDULER= OFF;
+SET @save_time_zone= @@TIME_ZONE;
+
+#----------------------------------------------------------------------
+
+# We will use a separate connection because SET TIMESTAMP will stop
+# the clock in that connection.
+
+SET TIME_ZONE= '+00:00';
+SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
+
+
+# Test when event time zone is updated on ALTER EVENT.
+#
+
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SHOW EVENTS;
+
+# Test storing and updating of the event time zone.
+#
+SET TIME_ZONE= '-01:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
+SHOW EVENTS;
+
+# This will update event time zone.
+SET TIME_ZONE= '+02:00';
+ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
+ ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+
+# This will update event time zone.
+SET TIME_ZONE= '-03:00';
+ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
+ ON COMPLETION PRESERVE DISABLE;
+SHOW EVENTS;
+
+# This will not update event time zone, as no time is being adjusted.
+SET TIME_ZONE= '+04:00';
+ALTER EVENT e1 DO SELECT 2;
+SHOW EVENTS;
+
+DROP EVENT e1;
+
+#----------------------------------------------------------------------
+
+# Create some events.
+SET TIME_ZONE='+05:00';
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+ SELECT 1;
+
+SET TIMESTAMP= @@TIMESTAMP + 1;
+
+SET TIME_ZONE='-05:00';
+CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+ SELECT 1;
+
+SET TIMESTAMP= @@TIMESTAMP + 1;
+
+SET TIME_ZONE='+00:00';
+CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
+ SELECT 1;
+
+
+# Test INFORMATION_SCHEMA.EVENTS.
+#
+
+SELECT * FROM INFORMATION_SCHEMA.EVENTS ORDER BY event_name;
+
+
+# Test SHOW EVENTS.
+#
+
+SHOW EVENTS;
+
+
+# Test SHOW CREATE EVENT.
+#
+
+SHOW CREATE EVENT e1;
+SHOW CREATE EVENT e2;
+SHOW CREATE EVENT e3;
+
+#----------------------------------------------------------------------
+
+# Test times in the past.
+#
+
+--echo The following should fail, and nothing should be altered.
+
+--error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00';
+
+--error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' DISABLE;
+
+--echo The following should give warnings, and nothing should be created.
+
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00'
+DO
+ SELECT 1;
+
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' DISABLE
+DO
+ SELECT 1;
+
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
+ SELECT 1;
+
+CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
+DO
+ SELECT 1;
+
+SHOW EVENTS;
+
+--echo The following should succeed giving a warning.
+
+ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
+
+CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
+DO
+ SELECT 1;
+
+CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
+ ON COMPLETION PRESERVE
+DO
+ SELECT 1;
+
+--echo The following should succeed without warnings.
+
+ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
+
+ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
+
+CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
+ SELECT 1;
+
+CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
+ ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
+DO
+ SELECT 1;
+
+CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
+ ON COMPLETION PRESERVE DISABLE
+DO
+ SELECT 1;
+
+SHOW EVENTS;
+
+
+DROP EVENT e8;
+DROP EVENT e7;
+DROP EVENT e6;
+DROP EVENT e5;
+DROP EVENT e4;
+DROP EVENT e3;
+DROP EVENT e2;
+DROP EVENT e1;
+
+SET TIME_ZONE=@save_time_zone;
+
+#
# End of tests
#
drop database events_test;
diff --git a/mysql-test/t/events_restart_phase1.test b/mysql-test/t/events_restart_phase1.test
index b2c052d303c..6a94ef12222 100644
--- a/mysql-test/t/events_restart_phase1.test
+++ b/mysql-test/t/events_restart_phase1.test
@@ -1,19 +1,43 @@
# Can't test with embedded server that doesn't support grants
-- source include/not_embedded.inc
+#
+# Test that when the server is restarted, it checks mysql.event table,
+# and disables the scheduler if it's not up to date.
+#
+
+# Switch off the scheduler for now.
+set global event_scheduler=off;
--disable_warnings
-create database if not exists mysqltest_events_test;
+drop database if exists events_test;
--enable_warnings
-
-use mysqltest_events_test;
-set global event_scheduler=off;
+create database events_test;
+use events_test;
create table execution_log(name char(10));
-create event abc1 on schedule every 1 second do insert into execution_log value('abc1');
-create event abc2 on schedule every 1 second do insert into execution_log value('abc2');
-create event abc3 on schedule every 1 second do insert into execution_log value('abc3');
---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',1,'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',1,'SYSTEM');
+create event abc1 on schedule every 1 second do
+ insert into execution_log value('abc1');
+create event abc2 on schedule every 1 second do
+ insert into execution_log value('abc2');
+create event abc3 on schedule every 1 second do
+ insert into execution_log value('abc3');
+#
+# There are various conditions when a server would regard mysql.event
+# table as damaged:
+# - the table has more column than specified in the compiled in value, but
+# the version of the server which created the table is the same
+# - the column count in the table is less than the compiled in value
+# - some column has an incompatible type specification (for what is an
+# incompatible type specification please refer to the comments in the source
+#
+# Unfortunately, in order to test a condition, we need to restart the
+# server. Therefore, here we test only one simple case: changing the data
+# type of the 'body' field to blob.
+#
+# First, let's do a backup to not depend on actual definition of mysql.event
+create table event_like like mysql.event;
+insert into event_like select * from mysql.event;
+# Now let's alter the table and restart the server
+alter table mysql.event
+ change column body body longtext character set utf8 collate utf8_bin;
--echo "Now we restart the server"
diff --git a/mysql-test/t/events_restart_phase2.test b/mysql-test/t/events_restart_phase2.test
index 845472377ba..c3f799634b3 100644
--- a/mysql-test/t/events_restart_phase2.test
+++ b/mysql-test/t/events_restart_phase2.test
@@ -1,9 +1,41 @@
# Can't test with embedded server that doesn't support grants
-- source include/not_embedded.inc
-use mysqltest_events_test;
---sleep 1.5
---echo "Should get 0 rows because the queue aborted run
-select distinct name from execution_log order by name;
-delete from mysql.event where name like 'bad%';
---echo "Now restart the server again"
+use events_test;
+# Event scheduler should be disabled: the system tables are damaged
+select @@event_scheduler;
+# Try various Event Scheduler operation and check the output.
+--error ER_EVENTS_DB_ERROR
+show events;
+--error ER_EVENTS_DB_ERROR
+select event_name from information_schema.events;
+--error ER_EVENTS_DB_ERROR
+show create event intact_check;
+--error ER_EVENTS_DB_ERROR
+drop event no_such_event;
+--error ER_EVENTS_DB_ERROR
+create event intact_check_1 on schedule every 5 hour do select 5;
+--error ER_EVENTS_DB_ERROR
+alter event intact_check_1 on schedule every 8 hour do select 8;
+--error ER_EVENTS_DB_ERROR
+alter event intact_check_1 rename to intact_check_2;
+--error ER_EVENTS_DB_ERROR
+drop event intact_check_1;
+--error ER_EVENTS_DB_ERROR
+drop event intact_check_2;
+--error ER_EVENTS_DB_ERROR
+drop event intact_check;
+--error ER_EVENTS_DB_ERROR
+set global event_scheduler=on;
+--error ER_EVENTS_DB_ERROR
+set global event_scheduler=off;
+show variables like 'event_scheduler';
+--echo Make sure that we still can create and drop databases,
+--echo and no warnings are produced.
+drop database if exists mysqltest_database_not_exists;
+create database mysqltest_db1;
+drop database mysqltest_db1;
+--echo Restore the original mysql.event table
+drop table mysql.event;
+rename table event_like to mysql.event;
+--echo Now let's restart the server again
diff --git a/mysql-test/t/events_restart_phase3.test b/mysql-test/t/events_restart_phase3.test
index 76cd9d22752..1be40c72717 100644
--- a/mysql-test/t/events_restart_phase3.test
+++ b/mysql-test/t/events_restart_phase3.test
@@ -1,14 +1,18 @@
# Can't test with embedded server that doesn't support grants
-- source include/not_embedded.inc
-
-use mysqltest_events_test;
---sleep 2
+#
+# We need this file primarily to make sure that the scheduler is restarted
+# and enabled after we have restored mysql.event table.
+# This is the final step of the "cleanup".
+#
+# Make sure also that events are executed OK after restart, just in case.
+use events_test;
+# Make sure the scheduler was started successfully
+select @@event_scheduler;
+let $wait_condition=select count(distinct name)=3 from execution_log;
+--source include/wait_condition.inc
--echo "Should get 3 rows : abc1, abc2, abc3
select distinct name from execution_log order by name;
-
-drop event abc1;
-drop event abc2;
-drop event abc3;
drop table execution_log;
-
-drop database mysqltest_events_test;
+# Will drop all events
+drop database events_test;
diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test
index f7d4c3c14d6..b53a548b6ed 100644
--- a/mysql-test/t/events_scheduling.test
+++ b/mysql-test/t/events_scheduling.test
@@ -6,12 +6,16 @@ CREATE DATABASE IF NOT EXISTS events_test;
USE events_test;
SET GLOBAL event_scheduler=OFF;
+--echo Try agian to make sure it's allowed
+SET GLOBAL event_scheduler=OFF;
SHOW VARIABLES LIKE 'event_scheduler';
SET GLOBAL event_scheduler=1;
SHOW VARIABLES LIKE 'event_scheduler';
SET GLOBAL event_scheduler=0;
SHOW VARIABLES LIKE 'event_scheduler';
SET GLOBAL event_scheduler=ON;
+--echo Try again to make sure it's allowed
+SET GLOBAL event_scheduler=ON;
SHOW VARIABLES LIKE 'event_scheduler';
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL event_scheduler=DISABLED;
diff --git a/mysql-test/t/events_time_zone.test b/mysql-test/t/events_time_zone.test
index fff84c7a995..5f929e0b07a 100644
--- a/mysql-test/t/events_time_zone.test
+++ b/mysql-test/t/events_time_zone.test
@@ -20,6 +20,7 @@ let $old_db= `SELECT DATABASE()`;
USE mysqltest_db1;
SET GLOBAL EVENT_SCHEDULER= OFF;
+SET @save_time_zone= @@TIME_ZONE;
#
@@ -31,176 +32,6 @@ SET GLOBAL EVENT_SCHEDULER= OFF;
# WL#3698: Events: execution in local time zone
#
-SET @save_time_zone= @@TIME_ZONE;
-
-#----------------------------------------------------------------------
-
-# We will use a separate connection because SET TIMESTAMP will stop
-# the clock in that connection.
-
-connect (conn1, localhost, root, , mysqltest_db1);
-
-SET TIME_ZONE= '+00:00';
-SET TIMESTAMP= UNIX_TIMESTAMP('2005-12-31 23:58:59');
-
-
-# Test when event time zone is updated on ALTER EVENT.
-#
-
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
-SHOW EVENTS;
-
-# Test storing and updating of the event time zone.
-#
-SET TIME_ZONE= '-01:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2000-01-01 00:00:00';
-SHOW EVENTS;
-
-# This will update event time zone.
-SET TIME_ZONE= '+02:00';
-ALTER EVENT e1 ON SCHEDULE AT '2000-01-02 00:00:00'
- ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-
-# This will update event time zone.
-SET TIME_ZONE= '-03:00';
-ALTER EVENT e1 ON SCHEDULE EVERY 1 DAY ENDS '2030-01-03 00:00:00'
- ON COMPLETION PRESERVE DISABLE;
-SHOW EVENTS;
-
-# This will not update event time zone, as no time is being adjusted.
-SET TIME_ZONE= '+04:00';
-ALTER EVENT e1 DO SELECT 2;
-SHOW EVENTS;
-
-DROP EVENT e1;
-
-#----------------------------------------------------------------------
-
-# Create some events.
-SET TIME_ZONE='+05:00';
-CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
- SELECT 1;
-
-SET TIMESTAMP= @@TIMESTAMP + 1;
-
-SET TIME_ZONE='-05:00';
-CREATE EVENT e2 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
- SELECT 1;
-
-SET TIMESTAMP= @@TIMESTAMP + 1;
-
-SET TIME_ZONE='+00:00';
-CREATE EVENT e3 ON SCHEDULE EVERY 1 DAY STARTS '2006-01-01 00:00:00' DO
- SELECT 1;
-
-
-# Test INFORMATION_SCHEMA.EVENTS.
-#
-
-SELECT * FROM INFORMATION_SCHEMA.EVENTS;
-
-
-# Test SHOW EVENTS.
-#
-
-SHOW EVENTS;
-
-
-# Test SHOW CREATE EVENT.
-#
-
-SHOW CREATE EVENT e1;
-SHOW CREATE EVENT e2;
-SHOW CREATE EVENT e3;
-
-#----------------------------------------------------------------------
-
-# Test times in the past.
-#
-
---echo The following should fail, and nothing should be altered.
-
---error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00';
-
---error ER_EVENT_CANNOT_ALTER_IN_THE_PAST
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' DISABLE;
-
---echo The following should give warnings, and nothing should be created.
-
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00'
-DO
- SELECT 1;
-
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' DISABLE
-DO
- SELECT 1;
-
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DO
- SELECT 1;
-
-CREATE EVENT e4 ON SCHEDULE AT '1999-01-01 00:00:00' DISABLE
-DO
- SELECT 1;
-
-SHOW EVENTS;
-
---echo The following should succeed giving a warning.
-
-ALTER EVENT e1 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE;
-
-CREATE EVENT e4 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE
-DO
- SELECT 1;
-
-CREATE EVENT e5 ON SCHEDULE AT '1999-01-01 00:00:00'
- ON COMPLETION PRESERVE
-DO
- SELECT 1;
-
---echo The following should succeed without warnings.
-
-ALTER EVENT e2 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00';
-
-ALTER EVENT e3 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE;
-
-CREATE EVENT e6 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00' DO
- SELECT 1;
-
-CREATE EVENT e7 ON SCHEDULE EVERY 1 HOUR STARTS '1999-01-01 00:00:00'
- ENDS '1999-01-02 00:00:00' ON COMPLETION PRESERVE DISABLE
-DO
- SELECT 1;
-
-CREATE EVENT e8 ON SCHEDULE AT '1999-01-01 00:00:00'
- ON COMPLETION PRESERVE DISABLE
-DO
- SELECT 1;
-
-SHOW EVENTS;
-
-
-DROP EVENT e8;
-DROP EVENT e7;
-DROP EVENT e6;
-DROP EVENT e5;
-DROP EVENT e4;
-DROP EVENT e3;
-DROP EVENT e2;
-DROP EVENT e1;
-
-
-disconnect conn1;
-connection default;
-
#----------------------------------------------------------------------
# Create rounding function.
diff --git a/mysql-test/t/events_trans.test b/mysql-test/t/events_trans.test
new file mode 100644
index 00000000000..41044f9975f
--- /dev/null
+++ b/mysql-test/t/events_trans.test
@@ -0,0 +1,159 @@
+#
+# Tests that require transactions
+#
+-- source include/have_innodb.inc
+--disable_warnings
+drop database if exists events_test;
+drop database if exists mysqltest_db2;
+drop database if exists mysqltest_no_such_database;
+--enable_warnings
+create database events_test;
+use events_test;
+
+--echo
+--echo Test that Events DDL issue an implicit COMMIT
+--echo
+--echo
+set autocommit=off;
+# Sanity check
+select @@autocommit;
+create table t1 (a varchar(255)) engine=innodb;
+# Basic: check that successful Events DDL commits pending transaction
+begin work;
+insert into t1 (a) values ("OK: create event");
+create event e1 on schedule every 1 day do select 1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+begin work;
+insert into t1 (a) values ("OK: alter event");
+alter event e1 on schedule every 2 day do select 2;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+begin work;
+insert into t1 (a) values ("OK: alter event rename");
+alter event e1 rename to e2;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+begin work;
+insert into t1 (a) values ("OK: drop event");
+drop event e2;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+begin work;
+insert into t1 (a) values ("OK: drop event if exists");
+drop event if exists e2;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+create event e1 on schedule every 1 day do select 1;
+begin work;
+insert into t1 (a) values ("OK: create event if not exists");
+create event if not exists e1 on schedule every 2 day do select 2;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+--echo
+--echo Now check various error conditions: make sure we issue an
+--echo implicit commit anyway
+--echo
+#
+begin work;
+insert into t1 (a) values ("OK: create event: event already exists");
+--error ER_EVENT_ALREADY_EXISTS
+create event e1 on schedule every 2 day do select 2;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+begin work;
+insert into t1 (a) values ("OK: alter event rename: rename to same name");
+--error ER_EVENT_SAME_NAME
+alter event e1 rename to e1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+create event e2 on schedule every 3 day do select 3;
+begin work;
+insert into t1 (a) values ("OK: alter event rename: destination exists");
+--error ER_EVENT_ALREADY_EXISTS
+alter event e2 rename to e1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+begin work;
+insert into t1 (a) values ("OK: create event: database does not exist");
+--error ER_BAD_DB_ERROR
+create event mysqltest_no_such_database.e1 on schedule every 1 day do select 1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+#
+# Privilege checks
+#
+grant create, insert, select, delete on mysqltest_db2.*
+ to mysqltest_user1@localhost;
+create database mysqltest_db2;
+connect (conn1,localhost,mysqltest_user1,,mysqltest_db2);
+set autocommit=off;
+# Sanity check
+select @@autocommit;
+create table t1 (a varchar(255)) engine=innodb;
+# Not enough privileges to CREATE EVENT
+begin work;
+insert into t1 (a) values ("OK: create event: insufficient privileges");
+--error ER_DBACCESS_DENIED_ERROR
+create event e1 on schedule every 1 day do select 1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+# Not enough privileges to ALTER EVENT
+begin work;
+insert into t1 (a) values ("OK: alter event: insufficient privileges");
+--error ER_DBACCESS_DENIED_ERROR
+alter event e1 on schedule every 1 day do select 1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+# Not enough privileges to DROP EVENT
+begin work;
+insert into t1 (a) values ("OK: drop event: insufficient privileges");
+--error ER_DBACCESS_DENIED_ERROR
+drop event e1;
+rollback work;
+select * from t1;
+delete from t1;
+commit work;
+# Cleanup
+disconnect conn1;
+connection default;
+drop user mysqltest_user1@localhost;
+drop database mysqltest_db2;
+
+#
+# Cleanup
+#
+drop database events_test;
+
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index e0b5ec23418..95bfba548de 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -20,8 +20,6 @@
#include "event_db_repository.h"
#include "sp_head.h"
-/* That's a provisional solution */
-extern Event_db_repository events_event_db_repository;
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
@@ -101,8 +99,7 @@ Event_parse_data::new_instance(THD *thd)
*/
Event_parse_data::Event_parse_data()
- :on_completion(Event_basic::ON_COMPLETION_DROP),
- status(Event_basic::ENABLED), do_not_create(FALSE),
+ :on_completion(ON_COMPLETION_DROP), status(ENABLED), do_not_create(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)
@@ -216,7 +213,7 @@ Event_parse_data::init_body(THD *thd)
++body_begin;
--body.length;
}
- body.str= thd->strmake((char *)body_begin, body.length);
+ body.str= thd->strmake(body_begin, body.length);
DBUG_VOID_RETURN;
}
@@ -244,7 +241,7 @@ 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;
- if (on_completion == Event_basic::ON_COMPLETION_DROP)
+ if (on_completion == ON_COMPLETION_DROP)
{
switch (thd->lex->sql_command) {
case SQLCOM_CREATE_EVENT:
@@ -261,9 +258,9 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
do_not_create= TRUE;
}
- else if (status == Event_basic::ENABLED)
+ else if (status == ENABLED)
{
- status= Event_basic::DISABLED;
+ status= DISABLED;
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_EXEC_TIME_IN_THE_PAST,
ER(ER_EVENT_EXEC_TIME_IN_THE_PAST));
@@ -589,9 +586,9 @@ Event_parse_data::check_parse_data(THD *thd)
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);
}
@@ -639,31 +636,6 @@ Event_parse_data::init_definer(THD *thd)
/*
- 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 ((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_basic::ENABLED) ||
- (status == Event_basic::DISABLED))
- status = Event_basic::SLAVESIDE_DISABLED;
- originator = thd->server_id;
- }
- else
- originator = server_id;
-}
-
-
-/*
Constructor
SYNOPSIS
@@ -720,7 +692,7 @@ Event_basic::load_string_fields(Field **fields, ...)
va_start(args, fields);
field_name= (enum enum_events_table_field) va_arg(args, int);
- while (field_name != ET_FIELD_COUNT)
+ while (field_name < ET_FIELD_COUNT)
{
field_value= va_arg(args, LEX_STRING *);
if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
@@ -884,13 +856,19 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
if (!table)
goto error;
- if (table->s->fields != ET_FIELD_COUNT)
+ if (table->s->fields < ET_FIELD_COUNT)
goto error;
LEX_STRING tz_name;
- load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
- ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
- ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT);
+ if (load_string_fields(table->field,
+ ET_FIELD_DB, &dbname,
+ ET_FIELD_NAME, &name,
+ ET_FIELD_BODY, &body,
+ ET_FIELD_DEFINER, &definer,
+ ET_FIELD_TIME_ZONE, &tz_name,
+ ET_FIELD_COUNT))
+ goto error;
+
if (load_time_zone(thd, tz_name))
goto error;
@@ -936,19 +914,24 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
{
char *ptr;
TIME time;
+ LEX_STRING tz_name;
DBUG_ENTER("Event_queue_element::load_from_row");
if (!table)
goto error;
- if (table->s->fields != ET_FIELD_COUNT)
+ if (table->s->fields < ET_FIELD_COUNT)
+ goto error;
+
+ if (load_string_fields(table->field,
+ ET_FIELD_DB, &dbname,
+ ET_FIELD_NAME, &name,
+ ET_FIELD_DEFINER, &definer,
+ ET_FIELD_TIME_ZONE, &tz_name,
+ ET_FIELD_COUNT))
goto error;
- LEX_STRING tz_name;
- load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
- ET_FIELD_DEFINER, &definer,
- ET_FIELD_TIME_ZONE, &tz_name, ET_FIELD_COUNT);
if (load_time_zone(thd, tz_name))
goto error;
@@ -1021,23 +1004,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
goto error;
DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
-
- /* Set event status (ENABLED | SLAVESIDE_DISABLED | DISABLED) */
- switch (ptr[0])
- {
- case 'E' :
- status = Event_queue_element::ENABLED;
- break;
- case 'S' :
- status = Event_queue_element::SLAVESIDE_DISABLED;
- break;
- case 'D' :
- status = Event_queue_element::DISABLED;
- break;
- }
- if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS)
- goto error;
- originator = table->field[ET_FIELD_ORIGINATOR]->val_int();
+ status= (ptr[0]=='E'? Event_queue_element::ENABLED:
+ Event_queue_element::DISABLED);
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
if ((ptr= get_field(&mem_root,
@@ -1080,7 +1048,11 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
if (Event_queue_element::load_from_row(thd, table))
goto error;
- load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT);
+ if (load_string_fields(table->field,
+ ET_FIELD_BODY, &body,
+ ET_FIELD_COUNT))
+ goto error;
+
ptr= strchr(definer.str, '@');
@@ -1384,7 +1356,7 @@ Event_queue_element::compute_next_execution_time()
(long) starts, (long) ends, (long) last_executed,
(long) this));
- if (status != Event_queue_element::ENABLED)
+ if (status == Event_queue_element::DISABLED)
{
DBUG_PRINT("compute_next_execution_time",
("Event %s is DISABLED", name.str));
@@ -1635,10 +1607,8 @@ Event_queue_element::mark_last_executed(THD *thd)
bool
Event_queue_element::update_timing_fields(THD *thd)
{
- TABLE *table;
- Field **fields;
- Open_tables_state backup;
- int ret= FALSE;
+ Event_db_repository *db_repository= Events::get_db_repository();
+ int ret;
DBUG_ENTER("Event_queue_element::update_timing_fields");
@@ -1648,53 +1618,13 @@ Event_queue_element::update_timing_fields(THD *thd)
if (!(status_changed || last_executed_changed))
DBUG_RETURN(0);
- thd->reset_n_backup_open_tables_state(&backup);
-
- if (events_event_db_repository.open_event_table(thd, TL_WRITE, &table))
- {
- ret= TRUE;
- goto done;
- }
- fields= table->field;
- if ((ret= events_event_db_repository.
- find_named_event(thd, dbname, name, table)))
- goto done;
-
- store_record(table,record[1]);
- /* Don't update create on row update. */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
- if (last_executed_changed)
- {
- TIME time;
- my_tz_UTC->gmt_sec_to_TIME(&time, last_executed);
-
- fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
- fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
- MYSQL_TIMESTAMP_DATETIME);
- last_executed_changed= FALSE;
- }
- if (status_changed)
- {
- fields[ET_FIELD_STATUS]->set_notnull();
- fields[ET_FIELD_STATUS]->store((longlong)status, TRUE);
- status_changed= FALSE;
- }
-
- /*
- Turn off row binlogging of event timing updates. These are not used
- for RBR of events replicated to the slave.
- */
- if (thd->current_stmt_binlog_row_based)
- thd->clear_current_stmt_binlog_row_based();
-
- if ((table->file->ha_update_row(table->record[1], table->record[0])))
- ret= TRUE;
-
-done:
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
-
+ ret= db_repository->update_timing_fields_for_event(thd,
+ dbname, name,
+ last_executed_changed,
+ last_executed,
+ status_changed,
+ (ulonglong) status);
+ last_executed_changed= status_changed= FALSE;
DBUG_RETURN(ret);
}
@@ -1778,8 +1708,6 @@ Event_timed::get_create_event(THD *thd, String *buf)
if (status == Event_timed::ENABLED)
buf->append(STRING_WITH_LEN("ENABLE"));
- else if (status == Event_timed::SLAVESIDE_DISABLED)
- buf->append(STRING_WITH_LEN("SLAVESIDE_DISABLE"));
else
buf->append(STRING_WITH_LEN("DISABLE"));
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index c0ae389f967..4d58484e5c2 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -18,7 +18,6 @@
#define EVEX_GET_FIELD_FAILED -2
#define EVEX_COMPILE_ERROR -3
-#define EVEX_GENERAL_ERROR -4
#define EVEX_BAD_PARAMS -5
#define EVEX_MICROSECOND_UNSUP -6
@@ -178,8 +177,6 @@ public:
ulong sql_mode;
- uint execution_count;
-
Event_job_data();
virtual ~Event_job_data();
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index b6c9e4ea8e3..cc981c9cb69 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -18,12 +18,6 @@
#include "event_data_objects.h"
#include "events.h"
#include "sql_show.h"
-#include "sp.h"
-#include "sp_head.h"
-
-
-static
-time_t mysql_event_last_create_time= 0L;
static
const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
@@ -132,26 +126,21 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
};
-/*
+/**
Puts some data common to CREATE and ALTER EVENT into a row.
- SYNOPSIS
- mysql_event_fill_row()
- thd THD
- table The row to fill out
- et Event's data
- is_update CREATE EVENT or ALTER EVENT
+ Used both when an event is created and when it is altered.
- RETURN VALUE
- 0 OK
- EVEX_GENERAL_ERROR Bad data
- EVEX_GET_FIELD_FAILED Field count does not match. table corrupted?
+ @param thd THD
+ @param table The row to fill out
+ @param et Event's data
+ @param is_update CREATE EVENT or ALTER EVENT
- DESCRIPTION
- Used both when an event is created and when it is altered.
+ @retval FALSE success
+ @retval TRUE error
*/
-static int
+static bool
mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
my_bool is_update)
{
@@ -165,6 +154,17 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
DBUG_PRINT("info", ("name =[%s]", et->name.str));
DBUG_PRINT("info", ("body =[%s]", et->body.str));
+ if (table->s->fields < ET_FIELD_COUNT)
+ {
+ /*
+ Safety: this can only happen if someone started the server
+ and then altered mysql.event.
+ */
+ my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias,
+ (int) ET_FIELD_COUNT, table->s->fields);
+ DBUG_RETURN(TRUE);
+ }
+
if (fields[f_num= ET_FIELD_DEFINER]->
store(et->definer.str, et->definer.length, scs))
goto err_truncate;
@@ -271,11 +271,11 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
goto err_truncate;
}
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
err_truncate:
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
+ DBUG_RETURN(TRUE);
}
@@ -389,40 +389,33 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
}
-/*
+/**
Fills I_S.EVENTS with data loaded from mysql.event. Also used by
SHOW EVENTS
- SYNOPSIS
- Event_db_repository::fill_schema_events()
- thd Thread
- tables The schema table
- db If not NULL then get events only from this schema
+ The reason we reset and backup open tables here is that this
+ function may be called from any query that accesses
+ INFORMATION_SCHEMA - including a query that is issued from
+ a pre-locked statement, one that already has open and locked
+ tables.
- RETURN VALUE
- FALSE OK
- TRUE Error
+ @retval FALSE success
+ @retval TRUE error
*/
-int
+bool
Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
const char *db)
{
TABLE *schema_table= tables->table;
TABLE *event_table= NULL;
- Open_tables_state backup;
int ret= 0;
DBUG_ENTER("Event_db_repository::fill_schema_events");
DBUG_PRINT("info",("db=%s", db? db:"(null)"));
- thd->reset_n_backup_open_tables_state(&backup);
if (open_event_table(thd, TL_READ, &event_table))
- {
- sql_print_error("Table mysql.event is damaged.");
- thd->restore_backup_open_tables_state(&backup);
- DBUG_RETURN(1);
- }
+ DBUG_RETURN(TRUE);
/*
1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
@@ -439,163 +432,100 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
ret= table_scan_all_for_i_s(thd, schema_table, event_table);
close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
DBUG_PRINT("info", ("Return code=%d", ret));
DBUG_RETURN(ret);
}
-/*
- Open mysql.event table for read
+/**
+ Open mysql.event table for read.
- SYNOPSIS
- Events::open_event_table()
- thd [in] Thread context
- lock_type [in] How to lock the table
- table [out] We will store the open table here
+ It's assumed that the caller knows what they are doing:
+ - whether it was necessary to reset-and-backup the open tables state
+ - whether the requested lock does not lead to a deadlock
+ - whether this open mode would work under LOCK TABLES, or inside a
+ stored function or trigger.
- RETURN VALUE
- 1 Cannot lock table
- 2 The table is corrupted - different number of fields
- 0 OK
+ @param[in] thd Thread context
+ @param[in] lock_type How to lock the table
+ @param[out] table We will store the open table here
+
+ @retval TRUE open and lock failed - an error message is pushed into the
+ stack
+ @retval FALSE success
*/
-int
+bool
Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
TABLE **table)
{
TABLE_LIST tables;
DBUG_ENTER("Event_db_repository::open_event_table");
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "event";
- tables.lock_type= lock_type;
+ tables.init_one_table("mysql", "event", lock_type);
if (simple_open_n_lock_tables(thd, &tables))
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
- if (table_check_intact(tables.table, ET_FIELD_COUNT,
- event_table_fields,
- &mysql_event_last_create_time,
- ER_CANNOT_LOAD_FROM_TABLE))
- {
- close_thread_tables(thd);
- DBUG_RETURN(2);
- }
*table= tables.table;
tables.table->use_all_columns();
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
}
-/*
- Checks parameters which we got from the parsing phase.
-
- SYNOPSIS
- check_parse_params()
- thd Thread context
- parse_data Event's data
-
- RETURN VALUE
- FALSE OK
- TRUE Error (reported)
-*/
-
-static int
-check_parse_params(THD *thd, Event_parse_data *parse_data)
-{
- DBUG_ENTER("check_parse_params");
-
- if (parse_data->check_parse_data(thd))
- DBUG_RETURN(EVEX_BAD_PARAMS);
-
- if (!parse_data->dbname.str ||
- (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname &&
- !thd->lex->spname->m_db.str))
- {
- my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
- DBUG_RETURN(EVEX_BAD_PARAMS);
- }
+/**
+ Creates an event record in mysql.event table.
- if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
- is_schema_db(parse_data->dbname.str)) ||
- (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname &&
- (check_access(thd, EVENT_ACL, thd->lex->spname->m_db.str, 0, 0, 0,
- is_schema_db(thd->lex->spname->m_db.str)))))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ Creates an event. Relies on mysql_event_fill_row which is shared with
+ ::update_event.
- DBUG_RETURN(0);
-}
-
-
-/*
- Creates an event in mysql.event
-
- SYNOPSIS
- Event_db_repository::create_event()
- thd [in] THD
- parse_data [in] Object containing info about the event
- create_if_not [in] Whether to generate anwarning in case event exists
+ @pre All semantic checks must be performed outside. This function
+ only creates a record on disk.
+ @pre The thread handle has no open tables.
- RETURN VALUE
- 0 OK
- EVEX_GENERAL_ERROR Failure
+ @param[in,out] THD
+ @param[in] parse_data Parsed event definition
+ @param[in] create_if_not TRUE if IF NOT EXISTS clause was provided
+ to CREATE EVENT statement
- DESCRIPTION
- Creates an event. Relies on mysql_event_fill_row which is shared with
- ::update_event. The name of the event is inside "et".
+ @retval FALSE success
+ @retval TRUE error
*/
bool
Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
my_bool create_if_not)
{
- int ret= 0;
+ int ret= 1;
TABLE *table= NULL;
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged= FALSE;
DBUG_ENTER("Event_db_repository::create_event");
- 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))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto err;
- }
+ goto end;
DBUG_PRINT("info", ("name: %.*s", parse_data->name.length,
parse_data->name.str));
DBUG_PRINT("info", ("check existance of an event with the same name"));
- if (!find_named_event(thd, parse_data->dbname, parse_data->name, table))
+ if (!find_named_event(parse_data->dbname, parse_data->name, table))
{
if (create_if_not)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
parse_data->name.str);
- goto ok;
+ ret= 0;
}
- my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
- goto err;
+ else
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
+ goto end;
}
- DBUG_PRINT("info", ("non-existant, go forward"));
-
- if ((ret= sp_use_new_db(thd, parse_data->dbname, &old_db, 0, &dbchanged)))
- {
- my_error(ER_BAD_DB_ERROR, MYF(0));
- goto err;
- }
+ DBUG_PRINT("info", ("non-existent, go forward"));
restore_record(table, s->default_values); // Get default values for fields
@@ -605,7 +535,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
table->field[ET_FIELD_DB]->char_length())
{
my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
- goto err;
+ goto end;
}
if (system_charset_info->cset->
@@ -614,20 +544,13 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
table->field[ET_FIELD_NAME]->char_length())
{
my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
- goto err;
+ goto end;
}
if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length)
{
my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
- goto err;
- }
-
- 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));
- goto err;
+ goto end;
}
((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
@@ -636,95 +559,71 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here
*/
- if ((ret= mysql_event_fill_row(thd, table, parse_data, FALSE)))
- goto err;
+ if (mysql_event_fill_row(thd, table, parse_data, FALSE))
+ goto end;
table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE);
- /* Close active transaction only if We are going to modify disk */
- if (end_active_trans(thd))
- goto err;
-
- if (table->file->ha_write_row(table->record[0]))
+ if ((ret= table->file->ha_write_row(table->record[0])))
{
- my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret);
- goto err;
+ table->file->print_error(ret, MYF(0));
+ goto end;
}
+ ret= 0;
-ok:
- if (dbchanged)
- (void) mysql_change_db(thd, old_db.str, 1);
- /*
- This statement may cause a spooky valgrind warning at startup
- inside init_key_cache on my system (ahristov, 2006/08/10)
- */
- close_thread_tables(thd);
- DBUG_RETURN(FALSE);
-
-err:
- if (dbchanged)
- (void) mysql_change_db(thd, old_db.str, 1);
+end:
if (table)
close_thread_tables(thd);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(test(ret));
}
-/*
+/**
Used to execute ALTER EVENT. Pendant to Events::update_event().
- SYNOPSIS
- Event_db_repository::update_event()
- thd THD
- sp_name the name of the event to alter
- et event's data
+ @param[in,out] thd thread handle
+ @param[in] parse_data parsed event definition
+ @paran[in[ new_dbname not NULL if ALTER EVENT RENAME
+ points at a new database name
+ @param[in] new_name not NULL if ALTER EVENT RENAME
+ points at a new event name
- RETURN VALUE
- FALSE OK
- TRUE Error (reported)
+ @pre All semantic checks are performed outside this function,
+ it only updates the event definition on disk.
+ @pre We don't have any tables open in the given thread.
- NOTES
- sp_name is passed since this is the name of the event to
- alter in case of RENAME TO.
+ @retval FALSE success
+ @retval TRUE error (reported)
*/
bool
Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
- LEX_STRING *new_dbname, LEX_STRING *new_name)
+ LEX_STRING *new_dbname,
+ LEX_STRING *new_name)
{
CHARSET_INFO *scs= system_charset_info;
TABLE *table= NULL;
+ int ret= 1;
DBUG_ENTER("Event_db_repository::update_event");
- if (open_event_table(thd, TL_WRITE, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto err;
- }
+ /* None or both must be set */
+ DBUG_ASSERT(new_dbname && new_name || new_dbname == new_name);
- if (check_parse_params(thd, parse_data) || parse_data->do_not_create)
- goto err;
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
DBUG_PRINT("info", ("name: %s", parse_data->name.str));
DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
- if (new_dbname)
- DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
/* first look whether we overwrite */
if (new_name)
{
- if (!sortcmp_lex_string(parse_data->name, *new_name, scs) &&
- !sortcmp_lex_string(parse_data->dbname, *new_dbname, scs))
- {
- my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
- goto err;
- }
-
- if (!find_named_event(thd, *new_dbname, *new_name, table))
+ DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
+ if (!find_named_event(*new_dbname, *new_name, table))
{
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
- goto err;
+ goto end;
}
}
/*
@@ -733,10 +632,10 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
overwrite the key and SE will tell us that it cannot find the already found
row (copied into record[1] later
*/
- if (find_named_event(thd, parse_data->dbname, parse_data->name, table))
+ if (find_named_event(parse_data->dbname, parse_data->name, table))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
- goto err;
+ goto end;
}
store_record(table,record[1]);
@@ -749,7 +648,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
handle it here
*/
if (mysql_event_fill_row(thd, table, parse_data, TRUE))
- goto err;
+ goto end;
if (new_dbname)
{
@@ -757,42 +656,32 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
}
- /* Close active transaction only if We are going to modify disk */
- if (end_active_trans(thd))
- goto err;
-
- int res;
- if ((res= table->file->ha_update_row(table->record[1], table->record[0])))
+ if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
{
- my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, res);
- goto err;
+ table->file->print_error(ret, MYF(0));
+ goto end;
}
+ ret= 0;
- /* close mysql.event or we crash later when loading the event from disk */
- close_thread_tables(thd);
- DBUG_RETURN(FALSE);
-
-err:
+end:
if (table)
close_thread_tables(thd);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(test(ret));
}
-/*
- Drops an event
+/**
+ Delete event record from mysql.event table.
- SYNOPSIS
- Event_db_repository::drop_event()
- thd [in] THD
- db [in] Database name
- name [in] Event's name
- drop_if_exists [in] If set and the event not existing => warning
- onto the stack
+ @param[in,out] thd thread handle
+ @param[in] db Database name
+ @param[in] name Event name
+ @param[in] drop_if_exists DROP IF EXISTS clause was specified.
+ If set, and the event does not exist,
+ the error is downgraded to a warning.
- RETURN VALUE
- FALSE OK
- TRUE Error (reported)
+ @retval FALSE success
+ @retval TRUE error (reported)
*/
bool
@@ -800,66 +689,59 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
bool drop_if_exists)
{
TABLE *table= NULL;
- Open_tables_state backup;
- int ret;
+ int ret= 1;
DBUG_ENTER("Event_db_repository::drop_event");
DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
- thd->reset_n_backup_open_tables_state(&backup);
- if ((ret= open_event_table(thd, TL_WRITE, &table)))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto done;
- }
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
- if (!(ret= find_named_event(thd, db, name, table)))
+ if (!find_named_event(db, name, table))
{
- /* Close active transaction only if we are actually going to modify disk */
- if (!(ret= end_active_trans(thd)) &&
- (ret= table->file->ha_delete_row(table->record[0])))
- my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(ret, MYF(0));
+ goto end;
}
- else
+
+ /* Event not found */
+ if (!drop_if_exists)
{
- if (drop_if_exists)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
- "Event", name.str);
- ret= 0;
- } else
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ goto end;
}
-done:
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ "Event", name.str);
+ ret= 0;
+
+end:
if (table)
close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
- DBUG_RETURN(ret);
+ DBUG_RETURN(test(ret));
}
-/*
+/**
Positions the internal pointer of `table` to the place where (db, name)
is stored.
- SYNOPSIS
- Event_db_repository::find_named_event()
- thd Thread
- db Schema
- name Event name
- table Opened mysql.event
+ In case search succeeded, the table cursor points at the found row.
- RETURN VALUE
- FALSE OK
- TRUE No such event
+ @param[in] db database name
+ @param[in] name event name
+ @param[in,out] table mysql.event table
+
+
+ @retval FALSE an event with such db/name key exists
+ @reval TRUE no record found or an error occured.
*/
bool
-Event_db_repository::find_named_event(THD *thd, LEX_STRING db, LEX_STRING name,
- TABLE *table)
+Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
+ TABLE *table)
{
byte key[MAX_KEY_LENGTH];
DBUG_ENTER("Event_db_repository::find_named_event");
@@ -911,15 +793,15 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
}
-/*
- Drops all events by field which has specific value of the field
+/**
+ Drops all events which have a specific value of a field.
- SYNOPSIS
- Event_db_repository::drop_events_by_field()
- thd Thread
- table mysql.event TABLE
- field Which field of the row to use for matching
- field_value The value that should match
+ @pre The thread handle has no open tables.
+
+ @param[in,out] thd Thread
+ @param[in,out] table mysql.event TABLE
+ @param[in] field Which field of the row to use for matching
+ @param[in] field_value The value that should match
*/
void
@@ -934,16 +816,7 @@ Event_db_repository::drop_events_by_field(THD *thd,
DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
if (open_event_table(thd, TL_WRITE, &table))
- {
- /*
- Currently being used only for DROP DATABASE - In this case we don't need
- error message since the OK packet has been sent. But for DROP USER we
- could need it.
-
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- */
DBUG_VOID_RETURN;
- }
/* only enabled events are in memory, so we go now and delete the rest */
init_read_record(&read_record_info, thd, table, NULL, 1, 0);
@@ -951,14 +824,20 @@ Event_db_repository::drop_events_by_field(THD *thd,
{
char *et_field= get_field(thd->mem_root, table->field[field]);
- LEX_STRING et_field_lex= { et_field, strlen(et_field) };
- DBUG_PRINT("info", ("Current event %s name=%s", et_field,
- get_field(thd->mem_root, table->field[ET_FIELD_NAME])));
-
- if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
+ /* et_field may be NULL if the table is corrupted or out of memory */
+ if (et_field)
{
- DBUG_PRINT("info", ("Dropping"));
- ret= table->file->ha_delete_row(table->record[0]);
+ LEX_STRING et_field_lex= { et_field, strlen(et_field) };
+ DBUG_PRINT("info", ("Current event %s name=%s", et_field,
+ get_field(thd->mem_root,
+ table->field[ET_FIELD_NAME])));
+
+ if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
+ {
+ DBUG_PRINT("info", ("Dropping"));
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(ret, MYF(0));
+ }
}
}
end_read_record(&read_record_info);
@@ -968,20 +847,14 @@ Event_db_repository::drop_events_by_field(THD *thd,
}
-/*
+/**
Looks for a named event in mysql.event and then loads it from
- the table, compiles and inserts it into the cache.
+ the table.
- SYNOPSIS
- Event_db_repository::load_named_event()
- thd [in] Thread context
- dbname [in] Event's db name
- name [in] Event's name
- etn [out] The loaded event
+ @pre The given thread does not have open tables.
- RETURN VALUE
- FALSE OK
- TRUE Error (reported)
+ @retval FALSE success
+ @retval TRUE error
*/
bool
@@ -989,26 +862,169 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
LEX_STRING name, Event_basic *etn)
{
TABLE *table= NULL;
- int ret= 0;
- Open_tables_state backup;
+ bool ret;
DBUG_ENTER("Event_db_repository::load_named_event");
DBUG_PRINT("enter",("thd: 0x%lx name: %*s", (long) thd, name.length, name.str));
- thd->reset_n_backup_open_tables_state(&backup);
+ if (!(ret= open_event_table(thd, TL_READ, &table)))
+ {
+ if ((ret= find_named_event(dbname, name, table)))
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ else if ((ret= etn->load_from_row(thd, table)))
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
- if ((ret= open_event_table(thd, TL_READ, &table)))
- 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(thd, table)))
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+ close_thread_tables(thd);
+ }
+
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Update the event record in mysql.event table with a changed status
+ and/or last execution time.
+
+ @pre The thread handle does not have open tables.
+*/
+
+bool
+Event_db_repository::
+update_timing_fields_for_event(THD *thd,
+ LEX_STRING event_db_name,
+ LEX_STRING event_name,
+ bool update_last_executed,
+ my_time_t last_executed,
+ bool update_status,
+ ulonglong status)
+{
+ TABLE *table= NULL;
+ Field **fields;
+ int ret= 1;
+
+ DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
+
+ fields= table->field;
+
+ if (find_named_event(event_db_name, event_name, table))
+ goto end;
+
+ store_record(table, record[1]);
+ /* Don't update create on row update. */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ if (update_last_executed)
+ {
+ TIME time;
+ my_tz_UTC->gmt_sec_to_TIME(&time, last_executed);
+ fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
+ MYSQL_TIMESTAMP_DATETIME);
+ }
+ if (update_status)
+ {
+ fields[ET_FIELD_STATUS]->set_notnull();
+ fields[ET_FIELD_STATUS]->store(status, TRUE);
+ }
+
+ if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
+ {
+ table->file->print_error(ret, MYF(0));
+ goto end;
+ }
+
+ ret= 0;
+
+end:
if (table)
close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
- /* In this case no memory was allocated so we don't need to clean */
+ DBUG_RETURN(test(ret));
+}
- DBUG_RETURN(ret);
+
+/**
+ Open mysql.db, mysql.user and mysql.event and check whether:
+ - mysql.db exists and is up to date (or from a newer version of MySQL),
+ - mysql.user has column Event_priv at an expected position,
+ - mysql.event exists and is up to date (or from a newer version of
+ MySQL)
+
+ This function is called only when the server is started.
+ @pre The passed in thread handle has no open tables.
+
+ @retval FALSE OK
+ @retval TRUE Error, an error message is output to the error log.
+*/
+
+bool
+Event_db_repository::check_system_tables(THD *thd)
+{
+ TABLE_LIST tables;
+ int ret= FALSE;
+ const unsigned int event_priv_column_position= 29;
+
+ DBUG_ENTER("Event_db_repository::check_system_tables");
+ DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
+
+
+ /* Check mysql.db */
+ tables.init_one_table("mysql", "db", TL_READ);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ ret= 1;
+ sql_print_error("Cannot open mysql.db");
+ }
+ else
+ {
+ if (table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
+ mysql_db_table_fields))
+ ret= 1;
+ /* in case of an error, the message is printed inside table_check_intact */
+
+ close_thread_tables(thd);
+ }
+ /* Check mysql.user */
+ tables.init_one_table("mysql", "user", TL_READ);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ ret= 1;
+ sql_print_error("Cannot open mysql.user");
+ }
+ else
+ {
+ if (tables.table->s->fields < event_priv_column_position ||
+ strncmp(tables.table->field[event_priv_column_position]->field_name,
+ STRING_WITH_LEN("Event_priv")))
+ {
+ sql_print_error("mysql.user has no `Event_priv` column at position %d",
+ event_priv_column_position);
+ ret= 1;
+ }
+ close_thread_tables(thd);
+ }
+ /* Check mysql.event */
+ tables.init_one_table("mysql", "event", TL_READ);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ ret= 1;
+ sql_print_error("Cannot open mysql.event");
+ }
+ else
+ {
+ if (table_check_intact(tables.table, ET_FIELD_COUNT, event_table_fields))
+ ret= 1;
+ /* in case of an error, the message is printed inside table_check_intact */
+ close_thread_tables(thd);
+ }
+
+ DBUG_RETURN(test(ret));
}
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
index 10a10d7957a..64e19854933 100644
--- a/sql/event_db_repository.h
+++ b/sql/event_db_repository.h
@@ -15,7 +15,12 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#define EVEX_OPEN_TABLE_FAILED -1
+/*
+ @file
+ This is a private header file of Events module. Please do not include it
+ directly. All public declarations of Events module should be stored in
+ events.h and event_data_objects.h.
+*/
enum enum_events_table_field
{
@@ -70,17 +75,28 @@ public:
drop_schema_events(THD *thd, LEX_STRING schema);
bool
- find_named_event(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
+ find_named_event(LEX_STRING db, LEX_STRING name, TABLE *table);
bool
load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
- int
+ bool
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
- int
+ bool
fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db);
+ bool
+ update_timing_fields_for_event(THD *thd,
+ LEX_STRING event_db_name,
+ LEX_STRING event_name,
+ bool update_last_executed,
+ my_time_t last_executed,
+ bool update_status,
+ ulonglong status);
+public:
+ static bool
+ check_system_tables(THD *thd);
private:
void
drop_events_by_field(THD *thd, enum enum_events_table_field field,
@@ -92,9 +108,7 @@ private:
bool
table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
- static bool
- check_system_tables(THD *thd);
-
+private:
/* Prevent use of these */
Event_db_repository(const Event_db_repository &);
void operator=(Event_db_repository &);
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index f958102e269..9b14d3cda7e 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -72,39 +72,21 @@ event_queue_element_compare_q(void *vptr, byte* a, byte *b)
Event_queue::Event_queue()
:mutex_last_unlocked_at_line(0), mutex_last_locked_at_line(0),
mutex_last_attempted_lock_at_line(0),
- mutex_queue_data_locked(FALSE), mutex_queue_data_attempting_lock(FALSE)
+ mutex_queue_data_locked(FALSE),
+ mutex_queue_data_attempting_lock(FALSE),
+ next_activation_at(0)
{
mutex_last_unlocked_in_func= mutex_last_locked_in_func=
mutex_last_attempted_lock_in_func= "";
- next_activation_at= 0;
-}
-
-/*
- Inits mutexes.
-
- SYNOPSIS
- Event_queue::init_mutexes()
-*/
-
-void
-Event_queue::init_mutexes()
-{
pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_queue_state, NULL);
}
-/*
- Destroys mutexes.
-
- SYNOPSIS
- Event_queue::deinit_mutexes()
-*/
-
-void
-Event_queue::deinit_mutexes()
+Event_queue::~Event_queue()
{
+ deinit_queue();
pthread_mutex_destroy(&LOCK_event_queue);
pthread_cond_destroy(&COND_queue_state);
}
@@ -176,34 +158,47 @@ Event_queue::deinit_queue()
/**
Adds an event to the queue.
- SYNOPSIS
- Event_queue::create_event()
- dbname The schema of the new event
- name The name of the new event
+ Compute the next execution time for an event, and if it is still
+ active, add it to the queue. Otherwise delete it.
+ The object is left intact in case of an error. Otherwise
+ the queue container assumes ownership of it.
+
+ @param[in] thd thread handle
+ @param[in] new_element a new element to add to the queue
+ @param[out] created set to TRUE if no error and the element is
+ added to the queue, FALSE otherwise
+
+ @retval TRUE an error occured. The value of created is undefined,
+ the element was not deleted.
+ @retval FALSE success
*/
-void
-Event_queue::create_event(THD *thd, Event_queue_element *new_element)
+bool
+Event_queue::create_event(THD *thd, Event_queue_element *new_element,
+ bool *created)
{
DBUG_ENTER("Event_queue::create_event");
DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd,
new_element->dbname.str, new_element->name.str));
- if ((new_element->status == Event_queue_element::DISABLED)
- || (new_element->status == Event_queue_element::SLAVESIDE_DISABLED))
- delete new_element;
- else
+ /* Will do nothing if the event is disabled */
+ new_element->compute_next_execution_time();
+ if (new_element->status != Event_queue_element::ENABLED)
{
- new_element->compute_next_execution_time();
- DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
-
- LOCK_QUEUE_DATA();
- queue_insert_safe(&queue, (byte *) new_element);
- dbug_dump_queue(thd->query_start());
- pthread_cond_broadcast(&COND_queue_state);
- UNLOCK_QUEUE_DATA();
+ delete new_element;
+ *created= FALSE;
+ DBUG_RETURN(FALSE);
}
- DBUG_VOID_RETURN;
+
+ DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
+
+ LOCK_QUEUE_DATA();
+ *created= (queue_insert_safe(&queue, (byte *) new_element) == FALSE);
+ dbug_dump_queue(thd->query_start());
+ pthread_cond_broadcast(&COND_queue_state);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_RETURN(!*created);
}
diff --git a/sql/event_queue.h b/sql/event_queue.h
index 95f52b7b588..04bb8b93b06 100644
--- a/sql/event_queue.h
+++ b/sql/event_queue.h
@@ -25,23 +25,16 @@ class Event_queue
{
public:
Event_queue();
-
- void
- init_mutexes();
-
- void
- deinit_mutexes();
+ ~Event_queue();
bool
init_queue(THD *thd);
- void
- deinit_queue();
-
/* Methods for queue management follow */
- void
- create_event(THD *thd, Event_queue_element *new_element);
+ bool
+ create_event(THD *thd, Event_queue_element *new_element,
+ bool *created);
void
update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
@@ -64,9 +57,23 @@ public:
void
dump_internal_status();
+private:
void
empty_queue();
-protected:
+
+ void
+ deinit_queue();
+ /* helper functions for working with mutexes & conditionals */
+ void
+ lock_data(const char *func, uint line);
+
+ void
+ unlock_data(const char *func, uint line);
+
+ void
+ cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line);
+
void
find_n_remove_event(LEX_STRING db, LEX_STRING name);
@@ -98,16 +105,6 @@ protected:
bool mutex_queue_data_attempting_lock;
bool waiting_on_cond;
- /* helper functions for working with mutexes & conditionals */
- void
- lock_data(const char *func, uint line);
-
- void
- unlock_data(const char *func, uint line);
-
- void
- cond_wait(THD *thd, struct timespec *abstime, const char* msg,
- const char *func, uint line);
};
#endif /* _EVENT_QUEUE_H_ */
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index d50ea932596..a9a93cbc74b 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -37,7 +37,6 @@ extern pthread_attr_t connection_attrib;
Event_db_repository *Event_worker_thread::db_repository;
-Events *Event_worker_thread::events_facade;
static
@@ -80,11 +79,11 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
prefix.length(0);
prefix.append("Event Scheduler: [");
- append_identifier(thd, &prefix, et->definer.str, et->definer.length);
+ prefix.append(et->definer.str, et->definer.length, system_charset_info);
prefix.append("][", 2);
- append_identifier(thd,&prefix, et->dbname.str, et->dbname.length);
+ prefix.append(et->dbname.str, et->dbname.length, system_charset_info);
prefix.append('.');
- append_identifier(thd,&prefix, et->name.str, et->name.length);
+ prefix.append(et->name.str, et->name.length, system_charset_info);
prefix.append("] ", 2);
List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
@@ -95,7 +94,6 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
err_msg.length(0);
err_msg.append(prefix);
err_msg.append(err->msg, strlen(err->msg), system_charset_info);
- err_msg.append("]");
DBUG_ASSERT(err->level < 3);
(sql_print_message_handlers[err->level])("%*s", err_msg.length(),
err_msg.c_ptr());
@@ -239,7 +237,7 @@ event_scheduler_thread(void *arg)
}
-/*
+/**
Function that executes an event in a child thread. Setups the
environment for the event execution and cleans after that.
@@ -266,7 +264,7 @@ event_worker_thread(void *arg)
}
-/*
+/**
Function that executes an event in a child thread. Setups the
environment for the event execution and cleans after that.
@@ -315,17 +313,27 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
print_warnings(thd, job_data);
- sql_print_information("Event Scheduler: "
- "[%s.%s of %s] executed in thread %lu. "
- "RetCode=%d", job_data->dbname.str, job_data->name.str,
- job_data->definer.str, thd->thread_id, ret);
- if (ret == EVEX_COMPILE_ERROR)
+ switch (ret) {
+ case 0:
sql_print_information("Event Scheduler: "
- "COMPILE ERROR for event %s.%s of %s",
+ "[%s].[%s.%s] executed successfully in thread %lu.",
+ job_data->definer.str,
job_data->dbname.str, job_data->name.str,
- job_data->definer.str);
- else if (ret == EVEX_MICROSECOND_UNSUP)
- sql_print_information("Event Scheduler: MICROSECOND is not supported");
+ thd->thread_id);
+ break;
+ case EVEX_COMPILE_ERROR:
+ sql_print_information("Event Scheduler: "
+ "[%s].[%s.%s] event compilation failed.",
+ job_data->definer.str,
+ job_data->dbname.str, job_data->name.str);
+ break;
+ default:
+ sql_print_information("Event Scheduler: "
+ "[%s].[%s.%s] event execution failed.",
+ job_data->definer.str,
+ job_data->dbname.str, job_data->name.str);
+ break;
+ }
end:
delete job_data;
@@ -349,66 +357,34 @@ end:
problem. However, this comes at the price of introduction bi-directional
association between class Events and class Event_worker_thread.
*/
- events_facade->drop_event(thd, event->dbname, event->name, FALSE);
+ Events::drop_event(thd, event->dbname, event->name, FALSE);
}
DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
event->name.str));
delete event;
deinit_event_thread(thd);
- pthread_exit(0);
-}
-
-
-/*
- Performs initialization of the scheduler data, outside of the
- threading primitives.
-
- SYNOPSIS
- Event_scheduler::init_scheduler()
-*/
-
-void
-Event_scheduler::init_scheduler(Event_queue *q)
-{
- LOCK_DATA();
- queue= q;
- started_events= 0;
- scheduler_thd= NULL;
- state= INITIALIZED;
- UNLOCK_DATA();
+ /*
+ Do not pthread_exit since we want local destructors for stack objects
+ to be invoked.
+ */
}
-void
-Event_scheduler::deinit_scheduler() {}
-
-
-/*
- Inits scheduler's threading primitives.
-
- SYNOPSIS
- Event_scheduler::init_mutexes()
-*/
-
-void
-Event_scheduler::init_mutexes()
+Event_scheduler::Event_scheduler(Event_queue *queue_arg)
+ :state(UNINITIALIZED),
+ scheduler_thd(NULL),
+ queue(queue_arg),
+ started_events(0)
{
pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_state, NULL);
}
-/*
- Deinits scheduler's threading primitives.
-
- SYNOPSIS
- Event_scheduler::deinit_mutexes()
-*/
-
-void
-Event_scheduler::deinit_mutexes()
+Event_scheduler::~Event_scheduler()
{
+ stop(); /* does nothing if not running */
pthread_mutex_destroy(&LOCK_scheduler_state);
pthread_cond_destroy(&COND_state);
}
@@ -639,6 +615,9 @@ Event_scheduler::is_running()
Stops the scheduler (again). Waits for acknowledgement from the
scheduler that it has stopped - synchronous stopping.
+ Already running events will not be stopped. If the user needs
+ them stopped manual intervention is needed.
+
SYNOPSIS
Event_scheduler::stop()
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 74d53c4f63d..70635196745 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -15,7 +15,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
+ @file
This file is internal to Events module. Please do not include it directly.
All public declarations of Events module are in events.h and
event_data_objects.h.
@@ -41,10 +42,9 @@ class Event_worker_thread
{
public:
static void
- init(Events *events, Event_db_repository *db_repo)
+ init(Event_db_repository *db_repository_arg)
{
- db_repository= db_repo;
- events_facade= events;
+ db_repository= db_repository_arg;
}
void
@@ -55,15 +55,15 @@ private:
print_warnings(THD *thd, Event_job_data *et);
static Event_db_repository *db_repository;
- static Events *events_facade;
};
class Event_scheduler
{
public:
- Event_scheduler():state(UNINITIALIZED){}
- ~Event_scheduler(){}
+ Event_scheduler(Event_queue *event_queue_arg);
+ ~Event_scheduler();
+
/* State changing methods follow */
@@ -80,17 +80,6 @@ public:
bool
run(THD *thd);
- void
- init_scheduler(Event_queue *queue);
-
- void
- deinit_scheduler();
-
- void
- init_mutexes();
-
- void
- deinit_mutexes();
/* Information retrieving methods follow */
bool
diff --git a/sql/events.cc b/sql/events.cc
index 46111bcaa91..7c44116af55 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -19,7 +19,6 @@
#include "event_db_repository.h"
#include "event_queue.h"
#include "event_scheduler.h"
-#include "sp_head.h"
/*
TODO list :
@@ -66,7 +65,7 @@
static const char *opt_event_scheduler_state_names[]=
{ "OFF", "ON", "0", "1", "DISABLED", NullS };
-TYPELIB Events::opt_typelib=
+const TYPELIB Events::opt_typelib=
{
array_elements(opt_event_scheduler_state_names)-1,
"",
@@ -82,7 +81,7 @@ TYPELIB Events::opt_typelib=
*/
static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };
-TYPELIB Events::var_typelib=
+const TYPELIB Events::var_typelib=
{
array_elements(var_event_scheduler_state_names)-1,
"",
@@ -90,20 +89,13 @@ TYPELIB Events::var_typelib=
NULL
};
-
-static
-Event_queue events_event_queue;
-
-static
-Event_scheduler events_event_scheduler;
-
-
-Event_db_repository events_event_db_repository;
-
-Events Events::singleton;
-
-enum Events::enum_opt_event_scheduler Events::opt_event_scheduler=
- Events::EVENTS_OFF;
+Event_queue *Events::event_queue;
+Event_scheduler *Events::scheduler;
+Event_db_repository *Events::db_repository;
+enum Events::enum_opt_event_scheduler
+Events::opt_event_scheduler= Events::EVENTS_OFF;
+pthread_mutex_t Events::LOCK_event_metadata;
+bool Events::check_system_tables_error= FALSE;
/*
@@ -128,25 +120,89 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
}
-/*
- Accessor for the singleton instance.
+/**
+ @brief Initialize the start up option of the Events scheduler.
- SYNOPSIS
- Events::get_instance()
+ Do not initialize the scheduler subsystem yet - the initialization
+ is split into steps as it has to fit into the common MySQL
+ initialization framework.
+ No locking as this is called only at start up.
- RETURN VALUE
- address
+ @param[in,out] argument The value of the argument. If this value
+ is found in the typelib, the argument is
+ updated.
+
+ @retval TRUE unknown option value
+ @retval FALSE success
*/
-Events *
-Events::get_instance()
+bool
+Events::set_opt_event_scheduler(char *argument)
{
- DBUG_ENTER("Events::get_instance");
- DBUG_RETURN(&singleton);
+ if (argument == NULL)
+ opt_event_scheduler= Events::EVENTS_DISABLED;
+ else
+ {
+ int type;
+ /*
+ type= 1 2 3 4 5
+ (OFF | ON) - (0 | 1) (DISABLE )
+ */
+ const static enum enum_opt_event_scheduler type2state[]=
+ { EVENTS_OFF, EVENTS_ON, EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
+
+ type= find_type(argument, &opt_typelib, 1);
+
+ DBUG_ASSERT(type >= 0 && type <= 5); /* guaranteed by find_type */
+
+ if (type == 0)
+ {
+ fprintf(stderr, "Unknown option to event-scheduler: %s\n", argument);
+ return TRUE;
+ }
+ opt_event_scheduler= type2state[type-1];
+ }
+ return FALSE;
}
-/*
+/**
+ Return a string representation of the current scheduler mode.
+*/
+
+const char *
+Events::get_opt_event_scheduler_str()
+{
+ const char *str;
+
+ pthread_mutex_lock(&LOCK_event_metadata);
+ str= opt_typelib.type_names[(int) opt_event_scheduler];
+ pthread_mutex_unlock(&LOCK_event_metadata);
+
+ return str;
+}
+
+
+/**
+ Push an error into the error stack if the system tables are
+ not up to date.
+*/
+
+bool Events::check_if_system_tables_error()
+{
+ DBUG_ENTER("Events::check_if_system_tables_error");
+
+ if (check_system_tables_error)
+ {
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Reconstructs interval expression from interval type and expression
value that is in form of a value of the smalles entity:
For
@@ -278,52 +334,65 @@ common_1_lev_code:
return 0;
}
-/*
- Constructor of Events class. It's called when events.o
- is loaded. Assigning addressed of static variables in this
- object file.
- SYNOPSIS
- Events::Events()
+/**
+ Create a new event.
+
+ @param[in,out] thd THD
+ @param[in] parse_data Event's data from parsing stage
+ @param[in] if_not_exists Whether IF NOT EXISTS was
+ specified
+ In case there is an event with the same name (db) and
+ IF NOT EXISTS is specified, an warning is put into the stack.
+ @sa Events::drop_event for the notes about locking, pre-locking
+ and Events DDL.
+
+ @retval FALSE OK
+ @retval TRUE Error (reported)
*/
-Events::Events()
+bool
+Events::create_event(THD *thd, Event_parse_data *parse_data,
+ bool if_not_exists)
{
- scheduler= &events_event_scheduler;
- event_queue= &events_event_queue;
- db_repository= &events_event_db_repository;
-}
+ int ret;
+ DBUG_ENTER("Events::create_event");
+ /*
+ Let's commit the transaction first - MySQL manual specifies
+ that a DDL issues an implicit commit, and it doesn't say "successful
+ DDL", so that an implicit commit is a property of any successfully
+ parsed DDL statement.
+ */
+ if (end_active_trans(thd))
+ DBUG_RETURN(TRUE);
-/*
- The function exported to the world for creating of events.
+ if (check_if_system_tables_error())
+ DBUG_RETURN(TRUE);
- SYNOPSIS
- Events::create_event()
- thd [in] THD
- parse_data [in] Event's data from parsing stage
- if_not_exists [in] Whether IF NOT EXISTS was specified in the DDL
+ /*
+ Perform semantic checks outside of Event_db_repository:
+ once CREATE EVENT is supported in prepared statements, the
+ checks will be moved to PREPARE phase.
+ */
+ if (parse_data->check_parse_data(thd))
+ DBUG_RETURN(TRUE);
- RETURN VALUE
- FALSE OK
- TRUE Error (Reported)
+ /* At create, one of them must be set */
+ DBUG_ASSERT(parse_data->expression || parse_data->execute_at);
- NOTES
- In case there is an event with the same name (db) and
- IF NOT EXISTS is specified, an warning is put into the stack.
-*/
+ if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
+ is_schema_db(parse_data->dbname.str)))
+ DBUG_RETURN(TRUE);
-bool
-Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
-{
- int ret;
- DBUG_ENTER("Events::create_event");
- if (unlikely(check_system_tables_error))
+ if (check_db_dir_existence(parse_data->dbname.str))
{
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ my_error(ER_BAD_DB_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
+ if (parse_data->do_not_create)
+ DBUG_RETURN(FALSE);
/*
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for CREATE EVENT command.
@@ -334,8 +403,7 @@ 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)) &&
- !parse_data->do_not_create)
+ if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
{
Event_queue_element *new_element;
@@ -345,12 +413,17 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
parse_data->name,
new_element)))
{
- DBUG_ASSERT(ret == OP_LOAD_ERROR);
+ db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
+ TRUE);
delete new_element;
}
- else /* Binlog the create event. */
+ else
{
- event_queue->create_event(thd, new_element);
+ /* 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. */
if (mysql_bin_log.is_open() && (thd->query_length > 0))
{
thd->clear_error();
@@ -365,37 +438,79 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
}
-/*
- The function exported to the world for alteration of events.
-
- SYNOPSIS
- Events::update_event()
- thd [in] THD
- parse_data [in] Event's data from parsing stage
- rename_to [in] Set in case of RENAME TO.
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-
- NOTES
- et contains data about dbname and event name.
- new_name is the new name of the event, if not null this means
- that RENAME TO was specified in the query
+/**
+ Alter an event.
+
+ @param[in,out] thd THD
+ @param[in] parse_data Event's data from parsing stage
+ @param[in] new_dbname A new schema name for the event. Set in the case of
+ ALTER EVENT RENAME, otherwise is NULL.
+ @param[in] new_name A new name for the event. Set in the case of
+ ALTER EVENT RENAME
+
+ Parameter 'et' contains data about dbname and event name.
+ Parameter 'new_name' is the new name of the event, if not null
+ this means that RENAME TO was specified in the query
+ @sa Events::drop_event for the locking notes.
+
+ @retval FALSE OK
+ @retval TRUE error (reported)
*/
bool
-Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
+Events::update_event(THD *thd, Event_parse_data *parse_data,
+ LEX_STRING *new_dbname, LEX_STRING *new_name)
{
int ret;
Event_queue_element *new_element;
+
DBUG_ENTER("Events::update_event");
- LEX_STRING *new_dbname= rename_to ? &rename_to->m_db : NULL;
- LEX_STRING *new_name= rename_to ? &rename_to->m_name : NULL;
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
+
+ /*
+ For consistency, implicit COMMIT should be the first thing in the
+ execution chain.
+ */
+ if (end_active_trans(thd))
+ DBUG_RETURN(TRUE);
+
+ if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
+
+ if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
+ DBUG_RETURN(TRUE);
+
+ if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
+ is_schema_db(parse_data->dbname.str)))
+ DBUG_RETURN(TRUE);
+
+ if (new_dbname) /* It's a rename */
+ {
+ /* Check that the new and the old names differ. */
+ if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname,
+ system_charset_info) &&
+ !sortcmp_lex_string(parse_data->name, *new_name,
+ system_charset_info))
+ {
+ my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ And the user has sufficient privileges to use the target database.
+ Do it before checking whether the database exists: we don't want
+ to tell the user that a database doesn't exist if they can not
+ access it.
+ */
+ if (check_access(thd, EVENT_ACL, new_dbname->str, 0, 0, 0,
+ is_schema_db(new_dbname->str)))
+ DBUG_RETURN(TRUE);
+
+ /* Check that the target database exists */
+ if (check_db_dir_existence(new_dbname->str))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
}
/*
@@ -408,7 +523,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
pthread_mutex_lock(&LOCK_event_metadata);
/* On error conditions my_error() is called so no need to handle here */
- if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name)))
+ if (!(ret= db_repository->update_event(thd, parse_data,
+ new_dbname, new_name)))
{
LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
LEX_STRING name= new_name ? *new_name : parse_data->name;
@@ -421,10 +537,18 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
DBUG_ASSERT(ret == OP_LOAD_ERROR);
delete new_element;
}
- else /* Binlog the alter event. */
+ else
{
- event_queue->update_event(thd, parse_data->dbname, parse_data->name,
- new_element);
+ /*
+ TODO: check if an update actually has inserted an entry
+ into the queue.
+ If not, and the element is ON COMPLETION NOT PRESERVE, delete
+ it right away.
+ */
+ if (event_queue)
+ event_queue->update_event(thd, parse_data->dbname, parse_data->name,
+ new_element);
+ /* Binlog the alter event. */
if (mysql_bin_log.is_open() && (thd->query_length > 0))
{
thd->clear_error();
@@ -439,20 +563,28 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
}
-/*
+/**
Drops an event
- SYNOPSIS
- Events::drop_event()
- thd [in] THD
- dbname [in] Event's schema
- name [in] Event's name
- if_exists [in] When set and the event does not exist =>
- warning onto the stack
-
- RETURN VALUE
- FALSE OK
- TRUE Error (reported)
+ @param[in,out] thd THD
+ @param[in] dbname Event's schema
+ @param[in] name Event's name
+ @param[in] if_exists When this is set and the event does not exist
+ a warning is pushed into the warning stack.
+ Otherwise the operation produces an error.
+
+ @note Similarly to DROP PROCEDURE, we do not allow DROP EVENT
+ under LOCK TABLES mode, unless table mysql.event is locked. To
+ ensure that, we do not reset & backup the open tables state in
+ this function - if in LOCK TABLES or pre-locking mode, this will
+ lead to an error 'Table mysql.event is not locked with LOCK
+ TABLES' unless it _is_ locked. In pre-locked mode there is
+ another barrier - DROP EVENT commits the current transaction,
+ and COMMIT/ROLLBACK is not allowed in stored functions and
+ triggers.
+
+ @retval FALSE OK
+ @retval TRUE Error (reported)
*/
bool
@@ -460,14 +592,30 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
{
int ret;
DBUG_ENTER("Events::drop_event");
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
+
+ /*
+ In MySQL, DDL must always commit: since mysql.* tables are
+ non-transactional, we must modify them outside a transaction
+ to not break atomicity.
+ But the second and more important reason to commit here
+ regardless whether we're actually changing mysql.event table
+ or not is replication: end_active_trans syncs the binary log,
+ and unless we run DDL in it's own transaction it may simply
+ never appear on the slave in case the outside transaction
+ rolls back.
+ */
+ if (end_active_trans(thd))
DBUG_RETURN(TRUE);
- }
- /*
- Turn off row binlogging of this statement and use statement-based so
+ if (check_if_system_tables_error())
+ DBUG_RETURN(TRUE);
+
+ if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
+ is_schema_db(dbname.str)))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Turn off row binlogging of this statement and use statement-based so
that all supporting tables are updated for DROP EVENT command.
*/
if (thd->current_stmt_binlog_row_based)
@@ -477,7 +625,8 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
{
- event_queue->drop_event(thd, dbname, name);
+ if (event_queue)
+ event_queue->drop_event(thd, dbname, name);
/* Binlog the drop event. */
if (mysql_bin_log.is_open() && (thd->query_length > 0))
{
@@ -494,10 +643,12 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
/**
Drops all events from a schema
- SYNOPSIS
- Events::drop_schema_events()
- thd Thread
- db ASCIIZ schema name
+ @note We allow to drop all events in a schema even if the
+ scheduler is disabled. This is to not produce any warnings
+ in case of DROP DATABASE and a disabled scheduler.
+
+ @param[in,out] thd Thread
+ @param[in] db ASCIIZ schema name
*/
void
@@ -507,14 +658,15 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_ENTER("Events::drop_schema_events");
DBUG_PRINT("enter", ("dropping events from %s", db));
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
- DBUG_VOID_RETURN;
- }
+
+ /*
+ sic: no check if the scheduler is disabled or system tables
+ are damaged, as intended.
+ */
pthread_mutex_lock(&LOCK_event_metadata);
- event_queue->drop_schema_events(thd, db_lex);
+ if (event_queue)
+ event_queue->drop_schema_events(thd, db_lex);
db_repository->drop_schema_events(thd, db_lex);
pthread_mutex_unlock(&LOCK_event_metadata);
@@ -522,115 +674,137 @@ Events::drop_schema_events(THD *thd, char *db)
}
-/*
- SHOW CREATE EVENT
-
- SYNOPSIS
- Events::show_create_event()
- thd Thread context
- spn The name of the event (db, name)
-
- RETURN VALUE
- FALSE OK
- TRUE Error during writing to the wire
+/**
+ A helper function to generate SHOW CREATE EVENT output from
+ a named event
*/
-bool
-Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+static bool
+send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
{
- CHARSET_INFO *scs= system_charset_info;
- int ret;
- Event_timed *et= new Event_timed();
+ char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
+ String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
+ List<Item> field_list;
+ LEX_STRING sql_mode;
+ const String *tz_name;
- DBUG_ENTER("Events::show_create_event");
- DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_ENTER("send_show_create_event");
+
+ show_str.length(0);
+ if (et->get_create_event(thd, &show_str))
DBUG_RETURN(TRUE);
- }
- ret= db_repository->load_named_event(thd, dbname, name, et);
+ field_list.push_back(new Item_empty_string("Event", NAME_LEN));
- if (!ret)
- {
- Protocol *protocol= thd->protocol;
- char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
- String show_str(show_str_buf, sizeof(show_str_buf), scs);
- List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len=0;
+ if (sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
+ &sql_mode))
+ DBUG_RETURN(TRUE);
- show_str.length(0);
- show_str.set_charset(system_charset_info);
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length));
- if (et->get_create_event(thd, &show_str))
- goto err;
+ tz_name= et->time_zone->get_name();
- field_list.push_back(new Item_empty_string("Event", NAME_LEN));
+ field_list.push_back(new Item_empty_string("time_zone",
+ tz_name->length()));
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
- &sql_mode_len);
+ field_list.push_back(new Item_empty_string("Create Event",
+ show_str.length()));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
- const String *tz_name= et->time_zone->get_name();
- field_list.push_back(new Item_empty_string("time_zone",
- tz_name->length()));
+ protocol->prepare_for_resend();
- field_list.push_back(new Item_empty_string("Create Event",
- show_str.length()));
+ protocol->store(et->name.str, et->name.length, system_charset_info);
+ protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
+ protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info);
+ protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info);
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- goto err;
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
- protocol->prepare_for_resend();
- protocol->store(et->name.str, et->name.length, scs);
+ send_eof(thd);
- protocol->store((char*) sql_mode_str, sql_mode_len, scs);
+ DBUG_RETURN(FALSE);
+}
- 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);
- }
- delete et;
+/**
+ Implement SHOW CREATE EVENT statement
+
+ thd Thread context
+ spn The name of the event (db, name)
+
+ @retval FALSE OK
+ @retval TRUE error (reported)
+*/
+
+bool
+Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ Open_tables_state open_tables_backup;
+ Event_timed et;
+ bool ret;
+
+ DBUG_ENTER("Events::show_create_event");
+ DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
+
+ if (check_if_system_tables_error())
+ DBUG_RETURN(TRUE);
+
+ if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
+ is_schema_db(dbname.str)))
+ DBUG_RETURN(TRUE);
+
+ /*
+ We would like to allow SHOW CREATE EVENT under LOCK TABLES and
+ in pre-locked mode. mysql.event table is marked as a system table.
+ This flag reduces the set of its participation scenarios in LOCK TABLES
+ operation, and therefore an out-of-bound open of this table
+ for reading like the one below (sic, only for reading) is
+ more or less deadlock-free. For additional information about when a
+ deadlock can occur please refer to the description of 'system table'
+ flag.
+ */
+ thd->reset_n_backup_open_tables_state(&open_tables_backup);
+ ret= db_repository->load_named_event(thd, dbname, name, &et);
+ thd->restore_backup_open_tables_state(&open_tables_backup);
+
+ if (!ret)
+ ret= send_show_create_event(thd, &et, thd->protocol);
+
DBUG_RETURN(ret);
-err:
- delete et;
- DBUG_RETURN(TRUE);
}
-/*
- Proxy for Event_db_repository::fill_schema_events.
- Callback for I_S from sql_show.cc
+/**
+ Check access rights and fill INFORMATION_SCHEMA.events table.
- SYNOPSIS
- Events::fill_schema_events()
- thd Thread context
- tables The schema table
+ @param[in,out] thd Thread context
+ @param[in] table The temporary table to fill.
cond Unused
- RETURN VALUE
- 0 OK
- !0 Error
+ In MySQL INFORMATION_SCHEMA tables are temporary tables that are
+ created and filled on demand. In this function, we fill
+ INFORMATION_SCHEMA.events. It is a callback for I_S module, invoked from
+ sql_show.cc
+
+ @return Has to be integer, as such is the requirement of the I_S API
+ @retval 0 success
+ @retval 1 an error, pushed into the error stack
*/
int
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
char *db= NULL;
+ int ret;
+ Open_tables_state open_tables_backup;
DBUG_ENTER("Events::fill_schema_events");
- Events *myself= get_instance();
- if (unlikely(myself->check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
+
+ if (check_if_system_tables_error())
+ DBUG_RETURN(1);
/*
If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
@@ -644,7 +818,17 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
}
- DBUG_RETURN(myself->db_repository->fill_schema_events(thd, tables, db));
+ /*
+ Reset and backup of the currently open tables in this thread
+ is a way to allow SELECTs from INFORMATION_SCHEMA.events under
+ LOCK TABLES and in pre-locked mode. See also
+ Events::show_create_event for additional comments.
+ */
+ thd->reset_n_backup_open_tables_state(&open_tables_backup);
+ ret= db_repository->fill_schema_events(thd, tables, db);
+ thd->restore_backup_open_tables_state(&open_tables_backup);
+
+ DBUG_RETURN(ret);
}
@@ -663,14 +847,17 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
*/
bool
-Events::init()
+Events::init(my_bool opt_noacl)
{
THD *thd;
bool res= FALSE;
+
DBUG_ENTER("Events::init");
- if (opt_event_scheduler == Events::EVENTS_DISABLED)
- DBUG_RETURN(FALSE);
+ /* Disable the scheduler if running with --skip-grant-tables */
+ if (opt_noacl)
+ opt_event_scheduler= EVENTS_DISABLED;
+
/* We need a temporary THD during boot */
if (!(thd= new THD()))
@@ -686,30 +873,67 @@ Events::init()
thd->thread_stack= (char*) &thd;
thd->store_globals();
- if (check_system_tables(thd))
+ /*
+ We will need Event_db_repository anyway, even if the scheduler is
+ disabled - to perform events DDL.
+ */
+ if (!(db_repository= new Event_db_repository))
{
- check_system_tables_error= TRUE;
- sql_print_error("Event Scheduler: The system tables are damaged. "
- "The scheduler subsystem will be unusable during this run.");
+ res= TRUE; /* fatal error: request unireg_abort */
goto end;
}
- check_system_tables_error= FALSE;
- if (event_queue->init_queue(thd) || load_events_from_db(thd))
+ /*
+ Since we allow event DDL even if the scheduler is disabled,
+ check the system tables, as we might need them.
+ */
+ if (Event_db_repository::check_system_tables(thd))
{
- sql_print_error("Event Scheduler: Error while loading from disk.");
+ sql_print_error("Event Scheduler: An error occurred when initializing "
+ "system tables.%s",
+ opt_event_scheduler == EVENTS_DISABLED ?
+ "" : " Disabling the Event Scheduler.");
+
+ /* Disable the scheduler since the system tables are not up to date */
+ opt_event_scheduler= EVENTS_DISABLED;
+ check_system_tables_error= TRUE;
goto end;
}
- scheduler->init_scheduler(event_queue);
+ /*
+ Was disabled explicitly from the command line, or because we're running
+ with --skip-grant-tables, or because we have no system tables.
+ */
+ if (opt_event_scheduler == Events::EVENTS_DISABLED)
+ goto end;
+
DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
opt_event_scheduler == Events::EVENTS_OFF);
- if (opt_event_scheduler == Events::EVENTS_ON)
- res= scheduler->start();
- Event_worker_thread::init(this, db_repository);
+ if (!(event_queue= new Event_queue) ||
+ !(scheduler= new Event_scheduler(event_queue)))
+ {
+ res= TRUE; /* fatal error: request unireg_abort */
+ goto end;
+ }
+
+ if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
+ opt_event_scheduler == EVENTS_ON && scheduler->start())
+ {
+ sql_print_error("Event Scheduler: Error while loading from disk.");
+ res= TRUE; /* fatal error: request unireg_abort */
+ goto end;
+ }
+ Event_worker_thread::init(db_repository);
+
end:
+ if (res)
+ {
+ delete db_repository;
+ delete event_queue;
+ delete scheduler;
+ }
delete thd;
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, NULL);
@@ -732,14 +956,18 @@ void
Events::deinit()
{
DBUG_ENTER("Events::deinit");
- if (likely(!check_system_tables_error))
- {
- scheduler->stop();
- scheduler->deinit_scheduler();
- event_queue->deinit_queue();
+ if (opt_event_scheduler != EVENTS_DISABLED)
+ {
+ delete scheduler;
+ scheduler= NULL; /* safety */
+ delete event_queue;
+ event_queue= NULL; /* safety */
}
+ delete db_repository;
+ db_repository= NULL; /* safety */
+
DBUG_VOID_RETURN;
}
@@ -756,8 +984,6 @@ void
Events::init_mutexes()
{
pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
- event_queue->init_mutexes();
- scheduler->init_mutexes();
}
@@ -771,8 +997,6 @@ Events::init_mutexes()
void
Events::destroy_mutexes()
{
- event_queue->deinit_mutexes();
- scheduler->deinit_mutexes();
pthread_mutex_destroy(&LOCK_event_metadata);
}
@@ -795,283 +1019,163 @@ Events::dump_internal_status()
puts("LLA = Last Locked At LUA = Last Unlocked At");
puts("WOC = Waiting On Condition DL = Data Locked");
- scheduler->dump_internal_status();
- event_queue->dump_internal_status();
+ pthread_mutex_lock(&LOCK_event_metadata);
+ if (opt_event_scheduler == EVENTS_DISABLED)
+ puts("The Event Scheduler is disabled");
+ else
+ {
+ scheduler->dump_internal_status();
+ event_queue->dump_internal_status();
+ }
+ pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_VOID_RETURN;
}
/**
- Starts execution of events by the scheduler
+ Starts or stops the event scheduler thread.
- SYNOPSIS
- Events::start_execution_of_events()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
+ @retval FALSE success
+ @retval TRUE error
*/
bool
-Events::start_execution_of_events()
+Events::start_or_stop_event_scheduler(enum_opt_event_scheduler start_or_stop)
{
- DBUG_ENTER("Events::start_execution_of_events");
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
- DBUG_RETURN(scheduler->start());
-}
+ bool ret= FALSE;
+ DBUG_ENTER("Events::start_or_stop_event_scheduler");
-/*
- Stops execution of events by the scheduler.
- Already running events will not be stopped. If the user needs
- them stopped manual intervention is needed.
+ DBUG_ASSERT(start_or_stop == Events::EVENTS_ON ||
+ start_or_stop == Events::EVENTS_OFF);
- SYNOPSIS
- Events::stop_execution_of_events()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
-bool
-Events::stop_execution_of_events()
-{
- DBUG_ENTER("Events::stop_execution_of_events");
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ /*
+ If the scheduler was disabled because there are no/bad
+ system tables, produce a more meaningful error message
+ than ER_OPTION_PREVENTS_STATEMENT
+ */
+ if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
- }
- DBUG_RETURN(scheduler->stop());
-}
-
-
-/*
- Checks whether the scheduler is running or not.
-
- SYNOPSIS
- Events::is_started()
-
- RETURN VALUE
- TRUE Yes
- FALSE No
-*/
-
-bool
-Events::is_execution_of_events_started()
-{
- DBUG_ENTER("Events::is_execution_of_events_started");
- if (unlikely(check_system_tables_error))
- {
- my_error(ER_EVENTS_DB_ERROR, MYF(0));
- DBUG_RETURN(FALSE);
- }
- DBUG_RETURN(scheduler->is_running());
-}
+ pthread_mutex_lock(&LOCK_event_metadata);
-
-/*
- Opens mysql.db and mysql.user and checks whether:
- 1. mysql.db has column Event_priv at column 20 (0 based);
- 2. mysql.user has column Event_priv at column 29 (0 based);
-
- SYNOPSIS
- Events::check_system_tables()
- thd Thread
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
-bool
-Events::check_system_tables(THD *thd)
-{
- TABLE_LIST tables;
- Open_tables_state backup;
- bool ret= FALSE;
-
- DBUG_ENTER("Events::check_system_tables");
- DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
-
- thd->reset_n_backup_open_tables_state(&backup);
-
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "db";
- tables.lock_type= TL_READ;
-
- if ((ret= simple_open_n_lock_tables(thd, &tables)))
+ if (opt_event_scheduler == EVENTS_DISABLED)
{
- sql_print_error("Event Scheduler: Cannot open mysql.db");
+ my_error(ER_OPTION_PREVENTS_STATEMENT,
+ MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables");
ret= TRUE;
+ goto end;
}
- ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
- mysql_db_table_fields, &mysql_db_table_last_check,
- ER_CANNOT_LOAD_FROM_TABLE);
- close_thread_tables(thd);
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "user";
- tables.lock_type= TL_READ;
-
- if (simple_open_n_lock_tables(thd, &tables))
- {
- sql_print_error("Event Scheduler: Cannot open mysql.user");
- ret= TRUE;
- }
+ if (start_or_stop == EVENTS_ON)
+ ret= scheduler->start();
else
+ ret= scheduler->stop();
+
+ if (ret)
{
- if (tables.table->s->fields < 29 ||
- strncmp(tables.table->field[29]->field_name,
- STRING_WITH_LEN("Event_priv")))
- {
- sql_print_error("mysql.user has no `Event_priv` column at position %d",
- 29);
- ret= TRUE;
- }
- close_thread_tables(thd);
+ my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
+ goto end;
}
- thd->restore_backup_open_tables_state(&backup);
+ opt_event_scheduler= start_or_stop;
+end:
+ pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
-/*
- Loads all ENABLED events from mysql.event into the prioritized
- queue. Called during scheduler main thread initialization. Compiles
- the events. Creates Event_queue_element instances for every ENABLED event
- from mysql.event.
+/**
+ Loads all ENABLED events from mysql.event into a prioritized
+ queue.
- SYNOPSIS
- Events::load_events_from_db()
- thd Thread context. Used for memory allocation in some cases.
+ This function is called during the server start up. It reads
+ every event, computes the next execution time, and if the event
+ needs execution, adds it to a prioritized queue. Otherwise, if
+ ON COMPLETION DROP is specified, the event is automatically
+ removed from the table.
- RETURN VALUE
- 0 OK
- !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
- EVEX_COMPILE_ERROR) - in all these cases mysql.event was
- tampered.
+ @param[in,out] thd Thread context. Used for memory allocation in some cases.
- NOTES
- Reports the error to the console
+ @retval FALSE success
+ @retval TRUE error, the load is aborted
+
+ @note Reports the error to the console
*/
-int
+bool
Events::load_events_from_db(THD *thd)
{
TABLE *table;
READ_RECORD read_record_info;
- int ret= -1;
+ bool ret= TRUE;
uint count= 0;
- bool clean_the_queue= TRUE;
DBUG_ENTER("Events::load_events_from_db");
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
- if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
+ if ((ret= db_repository->open_event_table(thd, TL_WRITE, &table)))
{
- sql_print_error("Event Scheduler: Table mysql.event is damaged. Can not open");
- DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
+ sql_print_error("Event Scheduler: Failed to open table mysql.event");
+ DBUG_RETURN(TRUE);
}
- init_read_record(&read_record_info, thd, table ,NULL,1,0);
+ init_read_record(&read_record_info, thd, table, NULL, 0, 1);
while (!(read_record_info.read_record(&read_record_info)))
{
Event_queue_element *et;
+ bool created;
+ bool drop_on_completion;
+
if (!(et= new Event_queue_element))
- {
- DBUG_PRINT("info", ("Out of memory"));
- break;
- }
+ goto end;
+
DBUG_PRINT("info", ("Loading event from row."));
- if ((ret= et->load_from_row(thd, table)))
+ if (et->load_from_row(thd, table))
{
sql_print_error("Event Scheduler: "
"Error while reading from mysql.event. "
"The table is probably corrupted");
- break;
- }
- if (et->status != Event_queue_element::ENABLED)
- {
- DBUG_PRINT("info",("%s is disabled",et->name.str));
delete et;
- continue;
+ goto end;
}
+ drop_on_completion= (et->on_completion ==
+ Event_queue_element::ON_COMPLETION_DROP);
+
- /* let's find when to be executed */
- if (et->compute_next_execution_time())
+ if (event_queue->create_event(thd, et, &created))
{
- sql_print_error("Event Scheduler: Error while computing execution time of %s.%s."
- " Skipping", et->dbname.str, et->name.str);
- continue;
+ /* Out of memory */
+ delete et;
+ goto end;
}
-
+ if (created)
+ count++;
+ else if (drop_on_completion)
{
- Event_job_data temp_job_data;
- DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
-
- temp_job_data.load_from_row(thd, table);
-
/*
- We load only on scheduler root just to check whether the body
- compiles.
+ If not created, a stale event - drop if immediately if
+ ON COMPLETION NOT PRESERVE
*/
- switch (ret= temp_job_data.compile(thd, thd->mem_root)) {
- case EVEX_MICROSECOND_UNSUP:
- sql_print_error("Event Scheduler: mysql.event is tampered. MICROSECOND is not "
- "supported but found in mysql.event");
- break;
- case EVEX_COMPILE_ERROR:
- sql_print_error("Event Scheduler: Error while compiling %s.%s. Aborting load",
- et->dbname.str, et->name.str);
- break;
- default:
- break;
+ int rc= table->file->ha_delete_row(table->record[0]);
+ if (rc)
+ {
+ table->file->print_error(rc, MYF(0));
+ goto end;
}
- thd->end_statement();
- thd->cleanup_after_query();
}
- if (ret)
- {
- delete et;
- goto end;
- }
-
- DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.",
- (long) et));
- event_queue->create_event(thd, et);
- count++;
}
- clean_the_queue= FALSE;
+ sql_print_information("Event Scheduler: Loaded %d event%s",
+ count, (count == 1) ? "" : "s");
+ ret= FALSE;
+
end:
end_read_record(&read_record_info);
- if (clean_the_queue)
- {
- event_queue->empty_queue();
- ret= -1;
- }
- else
- {
- ret= 0;
- sql_print_information("Event Scheduler: Loaded %d event%s",
- count, (count == 1)?"":"s");
- }
-
close_thread_tables(thd);
- DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
DBUG_RETURN(ret);
}
diff --git a/sql/events.h b/sql/events.h
index f97a0c5f57e..db0e947ab6c 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -15,7 +15,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-class sp_name;
+/*
+ @file
+ A public interface of Events Scheduler module.
+*/
+
class Event_parse_data;
class Event_db_repository;
class Event_queue;
@@ -40,6 +44,22 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
/**
@class Events -- a facade to the functionality of the Event Scheduler.
+ Every public operation against the scheduler has to be executed via the
+ interface provided by a static method of this class. No instance of this
+ class is ever created and it has no non-static data members.
+
+ The life cycle of the Events module is the following:
+
+ At server start up:
+ set_opt_event_scheduler() -> init_mutexes() -> init()
+ When the server is running:
+ create_event(), drop_event(), start_or_stop_event_scheduler(), etc
+ At shutdown:
+ deinit(), destroy_mutexes().
+
+ The peculiar initialization and shutdown cycle is an adaptation to the
+ outside server startup/shutdown framework and mimics the rest of MySQL
+ subsystems (ACL, time zone tables, etc).
*/
class Events
@@ -53,47 +73,48 @@ public:
EVENTS_DISABLED= 4
};
- static enum_opt_event_scheduler opt_event_scheduler;
- static TYPELIB opt_typelib;
- static TYPELIB var_typelib;
+ /* Possible values of @@event_scheduler variable */
+ static const TYPELIB var_typelib;
- bool
- init();
+ static bool
+ set_opt_event_scheduler(char *argument);
- void
- deinit();
+ static const char *
+ get_opt_event_scheduler_str();
- void
- init_mutexes();
+ /* A hack needed for Event_queue_element */
+ static Event_db_repository *
+ get_db_repository() { return db_repository; }
- void
- destroy_mutexes();
+ static bool
+ init(my_bool opt_noacl);
- bool
- start_execution_of_events();
+ static void
+ deinit();
- bool
- stop_execution_of_events();
+ static void
+ init_mutexes();
- bool
- is_execution_of_events_started();
+ static void
+ destroy_mutexes();
- static Events *
- get_instance();
+ static bool
+ start_or_stop_event_scheduler(enum enum_opt_event_scheduler start_or_stop);
- bool
+ static bool
create_event(THD *thd, Event_parse_data *parse_data, bool if_exists);
- bool
- update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to);
+ static bool
+ update_event(THD *thd, Event_parse_data *parse_data,
+ LEX_STRING *new_dbname, LEX_STRING *new_name);
- bool
+ static bool
drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists);
- void
+ static void
drop_schema_events(THD *thd, char *db);
- bool
+ static bool
show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
/* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */
@@ -104,31 +125,28 @@ public:
static int
fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
- void
+ static void
dump_internal_status();
private:
- bool
- check_system_tables(THD *thd);
+ static bool check_if_system_tables_error();
- int
+ static bool
load_events_from_db(THD *thd);
- /* Singleton DP is used */
- Events();
- ~Events(){}
-
- /* Singleton instance */
- static Events singleton;
-
- Event_queue *event_queue;
- Event_scheduler *scheduler;
- Event_db_repository *db_repository;
-
- pthread_mutex_t LOCK_event_metadata;
-
- bool check_system_tables_error;
+private:
+ /* Command line option names */
+ static const TYPELIB opt_typelib;
+ static pthread_mutex_t LOCK_event_metadata;
+ static Event_queue *event_queue;
+ static Event_scheduler *scheduler;
+ static Event_db_repository *db_repository;
+ /* Current state of Event Scheduler */
+ static enum enum_opt_event_scheduler opt_event_scheduler;
+ /* Set to TRUE if an error at start up */
+ static bool check_system_tables_error;
+private:
/* Prevent use of these */
Events(const Events &);
void operator=(Events &);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 9bdf117f8f7..47f8e17dd93 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -885,7 +885,7 @@ static void close_connections(void)
}
(void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
- Events::get_instance()->deinit();
+ Events::deinit();
end_slave();
if (thread_count)
@@ -1330,7 +1330,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_bytes_sent);
(void) pthread_mutex_destroy(&LOCK_bytes_received);
(void) pthread_mutex_destroy(&LOCK_user_conn);
- Events::get_instance()->destroy_mutexes();
+ Events::destroy_mutexes();
#ifdef HAVE_OPENSSL
(void) pthread_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
@@ -3058,7 +3058,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_server_started,NULL);
sp_cache_init();
- Events::get_instance()->init_mutexes();
+ Events::init_mutexes();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -3844,21 +3844,15 @@ we force server id to 2, but this MySQL server will not act as a slave.");
create_shutdown_thread();
create_maintenance_thread();
+ if (Events::init(opt_noacl))
+ unireg_abort(1);
+
sql_print_information(ER(ER_STARTUP),my_progname,server_version,
((unix_sock == INVALID_SOCKET) ? (char*) ""
: mysqld_unix_port),
mysqld_port,
MYSQL_COMPILATION_COMMENT);
- if (!opt_noacl)
- {
- if (Events::get_instance()->init())
- unireg_abort(1);
- }
- else
- {
- Events::opt_event_scheduler = Events::EVENTS_DISABLED;
- }
/* Signal threads waiting for server to be started */
pthread_mutex_lock(&LOCK_server_started);
@@ -7585,32 +7579,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
}
#endif
case OPT_EVENT_SCHEDULER:
- if (!argument)
- Events::opt_event_scheduler= Events::EVENTS_DISABLED;
- else
- {
- int type;
- /*
- type= 5 1 2 3 4
- (DISABLE ) - (OFF | ON) - (0 | 1)
- */
- switch ((type=find_type(argument, &Events::opt_typelib, 1))) {
- case 0:
- fprintf(stderr, "Unknown option to event-scheduler: %s\n",argument);
- exit(1);
- case 5: /* OPT_DISABLED */
- Events::opt_event_scheduler= Events::EVENTS_DISABLED;
- break;
- case 2: /* OPT_ON */
- case 4: /* 1 */
- Events::opt_event_scheduler= Events::EVENTS_ON;
- break;
- case 1: /* OPT_OFF */
- case 3: /* 0 */
- Events::opt_event_scheduler= Events::EVENTS_OFF;
- break;
- }
- }
+ if (Events::set_opt_event_scheduler(argument))
+ exit(1);
break;
case (int) OPT_SKIP_NEW:
opt_specialflag|= SPECIAL_NO_NEW_FUNC;
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 7f55556c134..898d47e766e 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1686,7 +1686,7 @@ byte *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type,
}
-bool sys_var::check_enum(THD *thd, set_var *var, TYPELIB *enum_names)
+bool sys_var::check_enum(THD *thd, set_var *var, const TYPELIB *enum_names)
{
char buff[STRING_BUFFER_USUAL_SIZE];
const char *value;
@@ -3643,21 +3643,18 @@ bool sys_var_thd_table_type::update(THD *thd, set_var *var)
SYNOPSIS
thd in thread handler
val in sql_mode value
- len out pointer on length of string
-
- RETURN
- pointer to string with sql_mode representation
+ rep out pointer pointer to string with sql_mode representation
*/
-byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd,
- ulonglong val,
- ulong *len)
+bool
+sys_var_thd_sql_mode::
+symbolic_mode_representation(THD *thd, ulonglong val, LEX_STRING *rep)
{
- char buff[256];
+ char buff[STRING_BUFFER_USUAL_SIZE*8];
String tmp(buff, sizeof(buff), &my_charset_latin1);
- ulong length;
tmp.length(0);
+
for (uint i= 0; val; val>>= 1, i++)
{
if (val & 1)
@@ -3668,20 +3665,25 @@ byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd,
}
}
- if ((length= tmp.length()))
- length--;
- *len= length;
- return (byte*) thd->strmake(tmp.ptr(), length);
+ if (tmp.length())
+ tmp.length(tmp.length() - 1); /* trim the trailing comma */
+
+ rep->str= thd->strmake(tmp.ptr(), tmp.length());
+
+ rep->length= rep->str ? tmp.length() : 0;
+
+ return rep->length != tmp.length();
}
byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
+ LEX_STRING sql_mode;
ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
thd->variables.*offset);
- ulong length_unused;
- return symbolic_mode_representation(thd, val, &length_unused);
+ (void) symbolic_mode_representation(thd, val, &sql_mode);
+ return sql_mode.str;
}
@@ -4012,24 +4014,13 @@ sys_var_event_scheduler::update(THD *thd, set_var *var)
int res;
/* here start the thread if not running. */
DBUG_ENTER("sys_var_event_scheduler::update");
- if (Events::opt_event_scheduler == Events::EVENTS_DISABLED)
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables");
- DBUG_RETURN(TRUE);
- }
-
DBUG_PRINT("info", ("new_value: %d", (int) var->save_result.ulong_value));
- if (var->save_result.ulong_value == Events::EVENTS_ON)
- res= Events::get_instance()->start_execution_of_events();
- else if (var->save_result.ulong_value == Events::EVENTS_OFF)
- res= Events::get_instance()->stop_execution_of_events();
- else
- {
- assert(0); // Impossible
- }
- if (res)
- my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
+ enum Events::enum_opt_event_scheduler
+ new_state=
+ (enum Events::enum_opt_event_scheduler) var->save_result.ulong_value;
+
+ res= Events::start_or_stop_event_scheduler(new_state);
DBUG_RETURN((bool) res);
}
@@ -4038,15 +4029,7 @@ sys_var_event_scheduler::update(THD *thd, set_var *var)
byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- int state;
- if (Events::opt_event_scheduler == Events::EVENTS_DISABLED)
- state= Events::EVENTS_DISABLED; // This should be DISABLED
- else if (Events::get_instance()->is_execution_of_events_started())
- state= Events::EVENTS_ON; // This should be ON
- else
- state= Events::EVENTS_OFF; // This should be OFF
-
- return (byte*) Events::opt_typelib.type_names[state];
+ return (byte *) Events::get_opt_event_scheduler_str();
}
diff --git a/sql/set_var.h b/sql/set_var.h
index eac03fce610..a3963171192 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -61,7 +61,7 @@ public:
sys_vars++;
}
virtual bool check(THD *thd, set_var *var);
- bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names);
+ bool check_enum(THD *thd, set_var *var, const TYPELIB *enum_names);
bool check_set(THD *thd, set_var *var, TYPELIB *enum_names);
virtual bool update(THD *thd, set_var *var)=0;
virtual void set_default(THD *thd_arg, enum_var_type type) {}
@@ -450,8 +450,8 @@ public:
}
void set_default(THD *thd, enum_var_type type);
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- static byte *symbolic_mode_representation(THD *thd, ulonglong sql_mode,
- ulong *length);
+ static bool symbolic_mode_representation(THD *thd, ulonglong sql_mode,
+ LEX_STRING *rep);
};
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index b5357493e05..5fb2074db6e 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5884,8 +5884,8 @@ ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
eng "Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted"
ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt"
ER_CANNOT_LOAD_FROM_TABLE
- eng "Cannot load from mysql.%s. The table is probably corrupted. Please see the error log for details"
- ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt, siehe Fehlerlog"
+ eng "Cannot load from mysql.%s. The table is probably corrupted"
+ ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
ER_EVENT_CANNOT_DELETE
eng "Failed to delete the event from mysql.event"
ger "Löschen des Events aus mysql.event fehlgeschlagen"
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 8eeec741dcf..9bedc6fb38e 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2104,8 +2104,7 @@ sp_head::show_create_procedure(THD *thd)
String buffer(buff, sizeof(buff), system_charset_info);
int res;
List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len;
+ LEX_STRING sql_mode;
bool full_access;
DBUG_ENTER("sp_head::show_create_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
@@ -2116,12 +2115,10 @@ sp_head::show_create_procedure(THD *thd)
if (check_show_routine_access(thd, this, &full_access))
DBUG_RETURN(1);
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- m_sql_mode,
- &sql_mode_len);
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, m_sql_mode,
+ &sql_mode);
field_list.push_back(new Item_empty_string("Procedure", NAME_LEN));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length));
// 1024 is for not to confuse old clients
Item_empty_string *definition=
new Item_empty_string("Create Procedure", max(buffer.length(),1024));
@@ -2133,7 +2130,7 @@ sp_head::show_create_procedure(THD *thd)
DBUG_RETURN(1);
protocol->prepare_for_resend();
protocol->store(m_name.str, m_name.length, system_charset_info);
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ protocol->store((char*) sql_mode.str, sql_mode.length, system_charset_info);
if (full_access)
protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
else
@@ -2176,23 +2173,18 @@ sp_head::show_create_function(THD *thd)
String buffer(buff, sizeof(buff), system_charset_info);
int res;
List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len;
+ LEX_STRING sql_mode;
bool full_access;
DBUG_ENTER("sp_head::show_create_function");
DBUG_PRINT("info", ("procedure %s", m_name.str));
- LINT_INIT(sql_mode_str);
- LINT_INIT(sql_mode_len);
if (check_show_routine_access(thd, this, &full_access))
DBUG_RETURN(1);
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- m_sql_mode,
- &sql_mode_len);
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, m_sql_mode,
+ &sql_mode);
field_list.push_back(new Item_empty_string("Function",NAME_LEN));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode.length));
Item_empty_string *definition=
new Item_empty_string("Create Function", max(buffer.length(),1024));
definition->maybe_null= TRUE;
@@ -2203,7 +2195,7 @@ sp_head::show_create_function(THD *thd)
DBUG_RETURN(1);
protocol->prepare_for_resend();
protocol->store(m_name.str, m_name.length, system_charset_info);
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
if (full_access)
protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
else
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 4fd35b7e6e8..cc9dc4c1fec 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -953,7 +953,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
exit:
(void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */
- Events::get_instance()->drop_schema_events(thd, db);
+ Events::drop_schema_events(thd, db);
/*
If this database was the client's selected database, we silently
change the client's selected database to nothing (to have an empty
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index e59c675a678..e2a36b232c9 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -26,7 +26,6 @@
#include "sp.h"
#include "sp_cache.h"
#include "events.h"
-#include "event_data_objects.h"
#include "sql_trigger.h"
/* Used in error handling only */
@@ -3181,13 +3180,16 @@ end_with_restore_list:
switch (lex->sql_command) {
case SQLCOM_CREATE_EVENT:
- res= Events::get_instance()->
- create_event(thd, lex->event_parse_data,
- lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS);
+ {
+ bool if_not_exists= (lex->create_info.options &
+ HA_LEX_CREATE_IF_NOT_EXISTS);
+ res= Events::create_event(thd, lex->event_parse_data, if_not_exists);
break;
+ }
case SQLCOM_ALTER_EVENT:
- res= Events::get_instance()->update_event(thd, lex->event_parse_data,
- lex->spname);
+ res= Events::update_event(thd, lex->event_parse_data,
+ lex->spname ? &lex->spname->m_db : NULL,
+ lex->spname ? &lex->spname->m_name : NULL);
break;
default:
DBUG_ASSERT(0);
@@ -3205,39 +3207,16 @@ end_with_restore_list:
}
/* lex->unit.cleanup() is called outside, no need to call it here */
break;
- case SQLCOM_DROP_EVENT:
case SQLCOM_SHOW_CREATE_EVENT:
- {
- DBUG_ASSERT(lex->spname);
- if (! lex->spname->m_db.str)
- {
- my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
- goto error;
- }
- if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
- is_schema_db(lex->spname->m_db.str)))
- break;
-
- if (lex->spname->m_name.length > NAME_LEN)
- {
- my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
- /* this jumps to the end of the function and skips own messaging */
- goto error;
- }
-
- if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
- res= Events::get_instance()->show_create_event(thd, lex->spname->m_db,
- lex->spname->m_name);
- else
- {
- if (!(res= Events::get_instance()->drop_event(thd,
- lex->spname->m_db,
- lex->spname->m_name,
- lex->drop_if_exists)))
- send_ok(thd);
- }
+ res= Events::show_create_event(thd, lex->spname->m_db,
+ lex->spname->m_name);
+ break;
+ case SQLCOM_DROP_EVENT:
+ if (!(res= Events::drop_event(thd,
+ lex->spname->m_db, lex->spname->m_name,
+ lex->drop_if_exists)))
+ send_ok(thd);
break;
- }
case SQLCOM_CREATE_FUNCTION: // UDF function
{
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 445890adedb..4e89fb610cc 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3761,8 +3761,7 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db,
LEX_STRING *definer_buffer)
{
CHARSET_INFO *cs= system_charset_info;
- byte *sql_mode_str;
- ulong sql_mode_len;
+ LEX_STRING sql_mode_rep;
restore_record(table, s->default_values);
table->field[1]->store(db, strlen(db), cs);
@@ -3778,11 +3777,9 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db,
table->field[14]->store(STRING_WITH_LEN("OLD"), cs);
table->field[15]->store(STRING_WITH_LEN("NEW"), cs);
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- sql_mode,
- &sql_mode_len);
- table->field[17]->store((const char*)sql_mode_str, sql_mode_len, cs);
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, sql_mode,
+ &sql_mode_rep);
+ table->field[17]->store(sql_mode_rep.str, sql_mode_rep.length, cs);
table->field[18]->store((const char *)definer_buffer->str, definer_buffer->length, cs);
return schema_table_store_record(thd, table);
}
@@ -4308,13 +4305,13 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
CHARSET_INFO *scs= system_charset_info;
TIME time;
Event_timed et;
- DBUG_ENTER("fill_events_copy_to_schema_tab");
+ DBUG_ENTER("copy_event_to_schema_table");
restore_record(sch_table, s->default_values);
if (et.load_from_row(thd, event_table))
{
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias);
DBUG_RETURN(1);
}
@@ -4349,13 +4346,11 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
/* SQL_MODE */
{
- byte *sql_mode_str;
- ulong sql_mode_len= 0;
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode,
- &sql_mode_len);
+ LEX_STRING sql_mode;
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode,
+ &sql_mode);
sch_table->field[ISE_SQL_MODE]->
- store((const char*)sql_mode_str, sql_mode_len, scs);
+ store(sql_mode.str, sql_mode.length, scs);
}
int not_used=0;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 9e30cf5878c..d7573b42c5f 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -537,6 +537,6 @@ Estimated memory (with thread stack): %ld\n",
(long) (thread_count * thread_stack + info.hblkhd + info.arena));
#endif
- Events::get_instance()->dump_internal_status();
+ Events::dump_internal_status();
puts("");
}
diff --git a/sql/table.cc b/sql/table.cc
index 71e510bf0ac..5e901318919 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -250,7 +250,7 @@ void free_table_share(TABLE_SHARE *share)
Currently these are:
help_category, help_keyword, help_relation, help_topic,
- proc,
+ proc, event
time_zone, time_zone_leap_second, time_zone_name, time_zone_transition,
time_zone_transition_type
@@ -283,7 +283,14 @@ inline bool is_system_table_name(const char *name, uint length)
my_tolower(ci, name[0]) == 't' &&
my_tolower(ci, name[1]) == 'i' &&
my_tolower(ci, name[2]) == 'm' &&
- my_tolower(ci, name[3]) == 'e'
+ my_tolower(ci, name[3]) == 'e' ||
+
+ /* mysql.event table */
+ my_tolower(ci, name[0]) == 'e' &&
+ my_tolower(ci, name[1]) == 'v' &&
+ my_tolower(ci, name[2]) == 'e' &&
+ my_tolower(ci, name[3]) == 'n' &&
+ my_tolower(ci, name[4]) == 't'
)
);
}
@@ -2432,153 +2439,143 @@ bool check_column_name(const char *name)
}
-/*
+/**
Checks whether a table is intact. Should be done *just* after the table has
been opened.
-
- SYNOPSIS
- table_check_intact()
- table The table to check
- table_f_count Expected number of columns in the table
- table_def Expected structure of the table (column name and type)
- last_create_time The table->file->create_time of the table in memory
- we have checked last time
- error_num ER_XXXX from the error messages file. When 0 no error
- is sent to the client in case types does not match.
- If different col number either
- ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE or
- ER_COL_COUNT_DOESNT_MATCH_CORRUPTED is used
-
- RETURNS
- FALSE OK
- TRUE There was an error
+
+ @param[in] table The table to check
+ @param[in] table_f_count Expected number of columns in the table
+ @param[in] table_def Expected structure of the table (column name
+ and type)
+
+ @retval FALSE OK
+ @retval TRUE There was an error. An error message is output
+ to the error log. We do not push an error
+ message into the error stack because this
+ function is currently only called at start up,
+ and such errors never reach the user.
*/
my_bool
table_check_intact(TABLE *table, const uint table_f_count,
- const TABLE_FIELD_W_TYPE *table_def,
- time_t *last_create_time, int error_num)
+ const TABLE_FIELD_W_TYPE *table_def)
{
uint i;
my_bool error= FALSE;
my_bool fields_diff_count;
DBUG_ENTER("table_check_intact");
- DBUG_PRINT("info",("table: %s expected_count: %d last_create_time: %ld",
- table->alias, table_f_count, *last_create_time));
-
- if ((fields_diff_count= (table->s->fields != table_f_count)) ||
- (*last_create_time != table->file->stats.create_time))
+ DBUG_PRINT("info",("table: %s expected_count: %d",
+ table->alias, table_f_count));
+
+ fields_diff_count= (table->s->fields != table_f_count);
+ if (fields_diff_count)
{
- DBUG_PRINT("info", ("I am suspecting, checking table"));
- if (fields_diff_count)
+ DBUG_PRINT("info", ("Column count has changed, checking the definition"));
+
+ /* previous MySQL version */
+ if (MYSQL_VERSION_ID > table->s->mysql_version)
{
- /* previous MySQL version */
- error= TRUE;
- if (MYSQL_VERSION_ID > table->s->mysql_version)
- {
- my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), table->alias,
- table_f_count, table->s->fields, table->s->mysql_version,
- MYSQL_VERSION_ID);
- sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
- table->alias, table_f_count, table->s->fields,
- table->s->mysql_version, MYSQL_VERSION_ID);
- DBUG_RETURN(error);
+ sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
+ table->alias, table_f_count, table->s->fields,
+ table->s->mysql_version, MYSQL_VERSION_ID);
+ DBUG_RETURN(TRUE);
+ }
+ else if (MYSQL_VERSION_ID == table->s->mysql_version)
+ {
+ sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
+ table_f_count, table->s->fields);
+ DBUG_RETURN(TRUE);
+ }
+ /*
+ Something has definitely changed, but we're running an older
+ version of MySQL with new system tables.
+ Let's check column definitions. If a column was added at
+ the end of the table, then we don't care much since such change
+ is backward compatible.
+ */
+ }
+ char buffer[STRING_BUFFER_USUAL_SIZE];
+ for (i=0 ; i < table_f_count; i++, table_def++)
+ {
+ String sql_type(buffer, sizeof(buffer), system_charset_info);
+ sql_type.length(0);
+ if (i < table->s->fields)
+ {
+ Field *field= table->field[i];
- }
- else if (MYSQL_VERSION_ID == table->s->mysql_version)
- {
- my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,MYF(0), table->alias,
- table_f_count, table->s->fields);
- sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
- table_f_count, table->s->fields);
- }
- else
+ if (strncmp(field->field_name, table_def->name.str,
+ table_def->name.length))
{
/*
- Moving from newer mysql to older one -> let's say not an error but
- will check the definition afterwards. If a column was added at the
- end then we don't care much since it's not in the middle.
+ Name changes are not fatal, we use ordinal numbers to access columns.
+ Still this can be a sign of a tampered table, output an error
+ to the error log.
*/
- error= FALSE;
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected column '%s' at position %d, found '%s'.",
+ table->s->db.str, table->alias, table_def->name.str, i,
+ field->field_name);
}
- }
- /* definitely something has changed */
- char buffer[255];
- for (i=0 ; i < table_f_count; i++, table_def++)
- {
- String sql_type(buffer, sizeof(buffer), system_charset_info);
- sql_type.length(0);
+ field->sql_type(sql_type);
/*
- Name changes are not fatal, we use sequence numbers => no problem
- for us but this can show tampered table or broken table.
- */
- if (i < table->s->fields)
+ Generally, if column types don't match, then something is
+ wrong.
+
+ However, we only compare column definitions up to the
+ length of the original definition, since we consider the
+ following definitions compatible:
+
+ 1. DATETIME and DATETIM
+ 2. INT(11) and INT(11
+ 3. SET('one', 'two') and SET('one', 'two', 'more')
+
+ For SETs or ENUMs, if the same prefix is there it's OK to
+ add more elements - they will get higher ordinal numbers and
+ the new table definition is backward compatible with the
+ original one.
+ */
+ if (strncmp(sql_type.c_ptr_safe(), table_def->type.str,
+ table_def->type.length - 1))
{
- Field *field= table->field[i];
- if (strncmp(field->field_name, table_def->name.str,
- table_def->name.length))
- {
- sql_print_error("(%s) Expected field %s at position %d, found %s",
- table->alias, table_def->name.str, i,
- field->field_name);
- }
-
- /*
- If the type does not match than something is really wrong
- Check up to length - 1. Why?
- 1. datetime -> datetim -> the same
- 2. int(11) -> int(11 -> the same
- 3. set('one','two') -> set('one','two'
- so for sets if the same prefix is there it's ok if more are
- added as part of the set. The same is valid for enum. So a new
- table running on a old server will be valid.
- */
- field->sql_type(sql_type);
- if (strncmp(sql_type.c_ptr_safe(), table_def->type.str,
- table_def->type.length - 1))
- {
- sql_print_error("(%s) Expected field %s at position %d to have type "
- "%s, found %s", table->alias, table_def->name.str,
- i, table_def->type.str, sql_type.c_ptr_safe());
- error= TRUE;
- }
- else if (table_def->cset.str && !field->has_charset())
- {
- sql_print_error("(%s) Expected field %s at position %d to have "
- "character set '%s' but found no such", table->alias,
- table_def->name.str, i, table_def->cset.str);
- error= TRUE;
- }
- else if (table_def->cset.str &&
- strcmp(field->charset()->csname, table_def->cset.str))
- {
- sql_print_error("(%s) Expected field %s at position %d to have "
- "character set '%s' but found '%s'", table->alias,
- table_def->name.str, i, table_def->cset.str,
- field->charset()->csname);
- error= TRUE;
- }
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected column '%s' at position %d to have type "
+ "%s, found type %s.", table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->type.str,
+ sql_type.c_ptr_safe());
+ error= TRUE;
}
- else
+ else if (table_def->cset.str && !field->has_charset())
+ {
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected the type of column '%s' at position %d "
+ "to have character set '%s' but the type has no "
+ "character set.", table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->cset.str);
+ error= TRUE;
+ }
+ else if (table_def->cset.str &&
+ strcmp(field->charset()->csname, table_def->cset.str))
{
- sql_print_error("(%s) Expected field %s at position %d to have type %s "
- " but no field found.", table->alias,
- table_def->name.str, i, table_def->type.str);
- error= TRUE;
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected the type of column '%s' at position %d "
+ "to have character set '%s' but found "
+ "character set '%s'.", table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->cset.str,
+ field->charset()->csname);
+ error= TRUE;
}
}
- if (!error)
- *last_create_time= table->file->stats.create_time;
- else if (!fields_diff_count && error_num)
- my_error(error_num,MYF(0), table->alias, table_f_count, table->s->fields);
- }
- else
- {
- DBUG_PRINT("info", ("Table seems ok without thorough checking."));
- *last_create_time= table->file->stats.create_time;
+ else
+ {
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected column '%s' at position %d to have type %s "
+ " but the column is not found.",
+ table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->type.str);
+ error= TRUE;
+ }
}
-
- DBUG_RETURN(error);
+ DBUG_RETURN(error);
}
diff --git a/sql/table.h b/sql/table.h
index e05b444251c..bb9ced2e450 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -689,6 +689,21 @@ class index_hint;
typedef struct st_table_list
{
st_table_list() {} /* Remove gcc warning */
+
+ /**
+ Prepare TABLE_LIST that consists of one table instance to use in
+ simple_open_and_lock_tables
+ */
+ inline void init_one_table(const char *db_name_arg,
+ const char *table_name_arg,
+ enum thr_lock_type lock_type_arg)
+ {
+ bzero((char*) this, sizeof(*this));
+ db= (char*) db_name_arg;
+ table_name= alias= (char*) table_name_arg;
+ lock_type= lock_type_arg;
+ }
+
/*
List of tables local to a subquery (used by SQL_LIST). Considers
views as leaves (unlike 'next_leaf' below). Created at parse time
@@ -1097,8 +1112,7 @@ typedef struct st_table_field_w_type
my_bool
table_check_intact(TABLE *table, const uint table_f_count,
- const TABLE_FIELD_W_TYPE *table_def,
- time_t *last_create_time, int error_num);
+ const TABLE_FIELD_W_TYPE *table_def);
static inline my_bitmap_map *tmp_use_all_columns(TABLE *table,
MY_BITMAP *bitmap)