summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/events.result207
-rw-r--r--mysql-test/t/events.test132
-rw-r--r--sql/event.cc486
-rw-r--r--sql/event.h18
-rw-r--r--sql/event_executor.cc189
-rw-r--r--sql/event_priv.h1
-rw-r--r--sql/event_timed.cc159
-rw-r--r--sql/share/errmsg.txt8
-rw-r--r--sql/sql_acl.cc122
-rw-r--r--sql/sql_acl.h30
-rw-r--r--sql/sql_parse.cc11
-rw-r--r--sql/sql_show.cc31
-rw-r--r--sql/sql_yacc.yy4
-rw-r--r--sql/table.cc142
-rw-r--r--sql/table.h11
15 files changed, 1421 insertions, 130 deletions
diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result
index 41f944ab089..b9acdb540ed 100644
--- a/mysql-test/r/events.result
+++ b/mysql-test/r/events.result
@@ -24,7 +24,7 @@ sleep(3)
0
select db, name, body, status, interval_field, interval_value from mysql.event;
db name body status interval_field interval_value
-events_test e_43 set @a = 4 ENABLED SECOND 1
+events_test e_43 set @a = 4 ENABLED SECOND 1
drop event e_43;
select sleep(1);
sleep(1)
@@ -41,13 +41,178 @@ count(*)
0
drop event event3;
drop table t_event3;
+set names utf8;
+CREATE EVENT root6 ON SCHEDULE EVERY '10:20' MINUTE_SECOND ON COMPLETION PRESERVE ENABLE COMMENT 'some comment' DO select 1;
+SHOW CREATE EVENT root6;
+Event sql_mode Create Event
+root6 CREATE EVENT `events_test`.`root6` ON SCHEDULE EVERY '10:20' MINUTE_SECOND ON COMPLETION PRESERVE ENABLE COMMENT 'some comment' DO select 1
+create event root7 on schedule every 2 year do select 1;
+SHOW CREATE EVENT root7;
+Event sql_mode Create Event
+root7 CREATE EVENT `events_test`.`root7` ON SCHEDULE EVERY 2 YEAR ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root8 on schedule every '2:5' year_month do select 1;
+SHOW CREATE EVENT root8;
+Event sql_mode Create Event
+root8 CREATE EVENT `events_test`.`root8` ON SCHEDULE EVERY '2-5' YEAR_MONTH ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root8_1 on schedule every '2:15' year_month do select 1;
+SHOW CREATE EVENT root8_1;
+Event sql_mode Create Event
+root8_1 CREATE EVENT `events_test`.`root8_1` ON SCHEDULE EVERY '3-3' YEAR_MONTH ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root9 on schedule every 2 week ON COMPLETION PRESERVE DISABLE COMMENT 'коментар на кирилица' do select 1;
+SHOW CREATE EVENT root9;
+Event sql_mode Create Event
+root9 CREATE EVENT `events_test`.`root9` ON SCHEDULE EVERY 2 WEEK ON COMPLETION PRESERVE DISABLE COMMENT 'коментар на кирилица' DO select 1
+create event root10 on schedule every '20:5' day_hour do select 1;
+SHOW CREATE EVENT root10;
+Event sql_mode Create Event
+root10 CREATE EVENT `events_test`.`root10` ON SCHEDULE EVERY '20 5' DAY_HOUR ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root11 on schedule every '20:25' day_hour do select 1;
+SHOW CREATE EVENT root11;
+Event sql_mode Create Event
+root11 CREATE EVENT `events_test`.`root11` ON SCHEDULE EVERY '21 1' DAY_HOUR ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root12 on schedule every '20:25' hour_minute do select 1;
+SHOW CREATE EVENT root12;
+Event sql_mode Create Event
+root12 CREATE EVENT `events_test`.`root12` ON SCHEDULE EVERY '20:25' HOUR_MINUTE ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root13 on schedule every '25:25' hour_minute do select 1;
+SHOW CREATE EVENT root13;
+Event sql_mode Create Event
+root13 CREATE EVENT `events_test`.`root13` ON SCHEDULE EVERY '25:25' HOUR_MINUTE ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root13_1 on schedule every '11:65' hour_minute do select 1;
+SHOW CREATE EVENT root13_1;
+Event sql_mode Create Event
+root13_1 CREATE EVENT `events_test`.`root13_1` ON SCHEDULE EVERY '12:5' HOUR_MINUTE ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root14 on schedule every '35:35' minute_second do select 1;
+SHOW CREATE EVENT root14;
+Event sql_mode Create Event
+root14 CREATE EVENT `events_test`.`root14` ON SCHEDULE EVERY '35:35' MINUTE_SECOND ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root15 on schedule every '35:66' minute_second do select 1;
+SHOW CREATE EVENT root15;
+Event sql_mode Create Event
+root15 CREATE EVENT `events_test`.`root15` ON SCHEDULE EVERY '36:6' MINUTE_SECOND ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root16 on schedule every '35:56' day_minute do select 1;
+SHOW CREATE EVENT root16;
+Event sql_mode Create Event
+root16 CREATE EVENT `events_test`.`root16` ON SCHEDULE EVERY '1 11:56' DAY_MINUTE ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root17 on schedule every '35:12:45' day_minute do select 1;
+SHOW CREATE EVENT root17;
+Event sql_mode Create Event
+root17 CREATE EVENT `events_test`.`root17` ON SCHEDULE EVERY '35 12:45' DAY_MINUTE ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root17_1 on schedule every '35:25:65' day_minute do select 1;
+SHOW CREATE EVENT root17_1;
+Event sql_mode Create Event
+root17_1 CREATE EVENT `events_test`.`root17_1` ON SCHEDULE EVERY '36 2:5' DAY_MINUTE ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root18 on schedule every '35:12:45' hour_second do select 1;
+SHOW CREATE EVENT root18;
+Event sql_mode Create Event
+root18 CREATE EVENT `events_test`.`root18` ON SCHEDULE EVERY '35:12:45' HOUR_SECOND ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root19 on schedule every '15:59:85' hour_second do select 1;
+SHOW CREATE EVENT root19;
+Event sql_mode Create Event
+root19 CREATE EVENT `events_test`.`root19` ON SCHEDULE EVERY '16:0:25' HOUR_SECOND ON COMPLETION NOT PRESERVE ENABLE DO select 1
+create event root20 on schedule every '50:20:12:45' day_second do select 1;
+SHOW CREATE EVENT root20;
+Event sql_mode Create Event
+root20 CREATE EVENT `events_test`.`root20` ON SCHEDULE EVERY '50 20:12:45' DAY_SECOND ON COMPLETION NOT PRESERVE ENABLE DO select 1
+set names cp1251;
+create event ðóóò21 on schedule every '50:23:59:95' day_second COMMENT 'òîâà å 1251 êîìåíòàð' do select 1;
+SHOW CREATE EVENT ðóóò21;
+Event sql_mode Create Event
+ðóóò21 CREATE EVENT `events_test`.`ðóóò21` ON SCHEDULE EVERY '51 0:0:35' DAY_SECOND ON COMPLETION NOT PRESERVE ENABLE COMMENT 'òîâà å 1251 êîìåíòàð' DO select 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 HY000: Microseconds intervals are not yet supported
+SHOW EVENTS;
+ERROR HY000: Microseconds intervals are not yet supported
+drop event root22;
+drop event root6;
+drop event root7;
+drop event root8;
+drop event root8_1;
+drop event root9;
+drop event root10;
+drop event root11;
+drop event root12;
+drop event root13;
+drop event root13_1;
+drop event root14;
+drop event root15;
+drop event root16;
+drop event root17;
+drop event root17_1;
+drop event root18;
+drop event root19;
+drop event root20;
+drop event ðóóò21;
+set names latin1;
+CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing";
+SHOW EVENTS;
+Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
+events_test intact_check root@localhost RECURRING NULL 10 10 HOUR # # ENABLED
+ALTER TABLE mysql.event ADD dummy INT FIRST;
+SHOW EVENTS;
+ERROR HY000: Column count of mysql.event is wrong. Table probably corrupted. Expected 15, found 16.
+ALTER TABLE mysql.event DROP dummy, ADD dummy2 VARCHAR(64) FIRST;
+SHOW EVENTS;
+ERROR HY000: Column count of mysql.event is wrong. Table probably corrupted. Expected 15, found 16.
+ALTER TABLE mysql.event DROP dummy2;
+SHOW EVENTS;
+Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
+events_test intact_check root@localhost RECURRING NULL 10 10 HOUR # # ENABLED
+CREATE TABLE event_like LIKE mysql.event;
+INSERT INTO event_like SELECT * FROM mysql.event;
+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) character set utf8 collate utf8_bin 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') NOT NULL default 'ENABLED',
+ `on_completion` enum('DROP','PRESERVE') NOT NULL default 'DROP',
+ `comment` char(64) character set utf8 collate utf8_bin NOT NULL default '',
+ PRIMARY KEY (`definer`,`db`,`name`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events'
+SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+ERROR HY000: Cannot load from mysql.event. Table probably corrupted. See error log.
+ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default '';
+"This should work"
+SHOW EVENTS;
+Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
+events_test intact_check root@localhost RECURRING NULL 10 10 HOUR # # ENABLED
+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. Table probably corrupted. See error log.
+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. Table probably corrupted. See error log.
+ALTER TABLE mysql.event DROP comment, DROP starts;
+SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+ERROR HY000: Column count of mysql.event is wrong. Table probably corrupted. Expected 15, found 13.
+DROP TABLE mysql.event;
+CREATE TABLE mysql.event like event_like;
+INSERT INTO mysql.event SELECT * FROM event_like;
+DROP TABLE event_like;
+SHOW EVENTS;
+Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
+events_test intact_check root@localhost RECURRING NULL 10 10 HOUR # # ENABLED
+DROP EVENT intact_check;
create event one_event on schedule every 10 second do select 123;
SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
-events_test one_event root@localhost RECURRING NULL 10 INTERVAL_SECOND # # ENABLED
+events_test one_event root@localhost RECURRING NULL 10 10 SECOND # # ENABLED
SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events;
EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT
-NULL events_test one_event root@localhost select 123 RECURRING NULL 10 INTERVAL_SECOND ENABLED NOT PRESERVE
+NULL events_test one_event root@localhost select 123 RECURRING NULL 10 10 SECOND ENABLED NOT PRESERVE
CREATE DATABASE events_test2;
CREATE USER ev_test@localhost;
GRANT ALL ON events_test.* to ev_test@localhost;
@@ -81,20 +246,20 @@ create event three_event on schedule every 20 second on completion preserve comm
"Now we should see 3 events:";
SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
-events_test one_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test three_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test two_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
+events_test one_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test three_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test two_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
"This should show us only 3 events:";
SHOW FULL EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
-events_test one_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test three_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test two_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
+events_test one_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test three_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test two_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
"This should show us only 2 events:";
SHOW FULL EVENTS LIKE 't%event';
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
-events_test three_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test two_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
+events_test three_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test two_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
"This should show us no events:";
SHOW FULL EVENTS FROM test LIKE '%';
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
@@ -102,20 +267,20 @@ DROP DATABASE events_test2;
"should see 1 event:";
SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
-events_test one_event root@localhost RECURRING NULL 10 INTERVAL_SECOND # # ENABLED
+events_test one_event root@localhost RECURRING NULL 10 10 SECOND # # ENABLED
"we should see 4 events now:";
SHOW FULL EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
-events_test one_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test three_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test two_event ev_test@localhost RECURRING NULL 20 INTERVAL_SECOND # # ENABLED
-events_test one_event root@localhost RECURRING NULL 10 INTERVAL_SECOND # # ENABLED
+events_test one_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test three_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test two_event ev_test@localhost RECURRING NULL 20 20 SECOND # # ENABLED
+events_test one_event root@localhost RECURRING NULL 10 10 SECOND # # ENABLED
SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events;
EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT
-NULL events_test one_event ev_test@localhost select 123 RECURRING NULL 20 INTERVAL_SECOND ENABLED NOT PRESERVE
-NULL events_test three_event ev_test@localhost select 123 RECURRING NULL 20 INTERVAL_SECOND ENABLED PRESERVE three event
-NULL events_test two_event ev_test@localhost select 123 RECURRING NULL 20 INTERVAL_SECOND ENABLED NOT PRESERVE two event
-NULL events_test one_event root@localhost select 123 RECURRING NULL 10 INTERVAL_SECOND ENABLED NOT PRESERVE
+NULL events_test one_event ev_test@localhost select 123 RECURRING NULL 20 20 SECOND ENABLED NOT PRESERVE
+NULL events_test three_event ev_test@localhost select 123 RECURRING NULL 20 20 SECOND ENABLED PRESERVE three event
+NULL events_test two_event ev_test@localhost select 123 RECURRING NULL 20 20 SECOND ENABLED NOT PRESERVE two event
+NULL events_test one_event root@localhost select 123 RECURRING NULL 10 10 SECOND ENABLED NOT PRESERVE
drop event one_event;
drop event two_event;
drop event three_event;
@@ -124,7 +289,7 @@ drop event one_event;
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
-events_test e_26 set @a = 5 root@localhost 2017-01-01 00:00:00 DROP
+events_test e_26 set @a = 5 root@localhost 2017-01-01 00:00:00 DROP
drop event e_26;
create event e_26 on schedule at NULL disabled do set @a = 5;
ERROR HY000: Incorrect AT value: 'NULL'
diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test
index be24d490393..741ff904d13 100644
--- a/mysql-test/t/events.test
+++ b/mysql-test/t/events.test
@@ -33,6 +33,138 @@ select count(*) from t_event3;
drop event event3;
drop table t_event3;
+
+set names utf8;
+#
+# SHOW CREATE EVENT test begin
+#
+CREATE EVENT root6 ON SCHEDULE EVERY '10:20' MINUTE_SECOND ON COMPLETION PRESERVE ENABLE COMMENT 'some comment' DO select 1;
+SHOW CREATE EVENT root6;
+create event root7 on schedule every 2 year do select 1;
+SHOW CREATE EVENT root7;
+create event root8 on schedule every '2:5' year_month do select 1;
+SHOW CREATE EVENT root8;
+create event root8_1 on schedule every '2:15' year_month do select 1;
+SHOW CREATE EVENT root8_1;
+create event root9 on schedule every 2 week ON COMPLETION PRESERVE DISABLE COMMENT 'коментар на кирилица' do select 1;
+SHOW CREATE EVENT root9;
+create event root10 on schedule every '20:5' day_hour do select 1;
+SHOW CREATE EVENT root10;
+create event root11 on schedule every '20:25' day_hour do select 1;
+SHOW CREATE EVENT root11;
+create event root12 on schedule every '20:25' hour_minute do select 1;
+SHOW CREATE EVENT root12;
+create event root13 on schedule every '25:25' hour_minute do select 1;
+SHOW CREATE EVENT root13;
+create event root13_1 on schedule every '11:65' hour_minute do select 1;
+SHOW CREATE EVENT root13_1;
+create event root14 on schedule every '35:35' minute_second do select 1;
+SHOW CREATE EVENT root14;
+create event root15 on schedule every '35:66' minute_second do select 1;
+SHOW CREATE EVENT root15;
+create event root16 on schedule every '35:56' day_minute do select 1;
+SHOW CREATE EVENT root16;
+create event root17 on schedule every '35:12:45' day_minute do select 1;
+SHOW CREATE EVENT root17;
+create event root17_1 on schedule every '35:25:65' day_minute do select 1;
+SHOW CREATE EVENT root17_1;
+create event root18 on schedule every '35:12:45' hour_second do select 1;
+SHOW CREATE EVENT root18;
+create event root19 on schedule every '15:59:85' hour_second do select 1;
+SHOW CREATE EVENT root19;
+create event root20 on schedule every '50:20:12:45' day_second do select 1;
+SHOW CREATE EVENT root20;
+set names cp1251;
+create event ðóóò21 on schedule every '50:23:59:95' day_second COMMENT 'òîâà å 1251 êîìåíòàð' do select 1;
+SHOW CREATE EVENT ðóóò21;
+insert into mysql.event (db, name, body, definer, interval_value, interval_field) values (database(), "root22", "select 1", user(), 100, "SECOND_MICROSECOND");
+--error 1535
+show create event root22;
+--error 1535
+SHOW EVENTS;
+drop event root22;
+drop event root6;
+drop event root7;
+drop event root8;
+drop event root8_1;
+drop event root9;
+drop event root10;
+drop event root11;
+drop event root12;
+drop event root13;
+drop event root13_1;
+drop event root14;
+drop event root15;
+drop event root16;
+drop event root17;
+drop event root17_1;
+drop event root18;
+drop event root19;
+drop event root20;
+drop event ðóóò21;
+
+set names latin1;
+#
+# SHOW CREATE EVENT test end
+#
+
+#
+# mysql.event intact checking start
+#
+# There should be at least 1 second between the ALTERs or we can't catch the change of create_time!!
+#
+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 1525
+SHOW EVENTS;
+ALTER TABLE mysql.event DROP dummy, ADD dummy2 VARCHAR(64) FIRST;
+--error 1525
+SHOW EVENTS;
+ALTER TABLE mysql.event DROP dummy2;
+--replace_column 8 # 9 #
+SHOW EVENTS;
+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
+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
+SHOW CREATE TABLE mysql.event;
+--error 1526
+SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+--sleep 1
+ALTER TABLE mysql.event MODIFY db char(64) character set utf8 collate utf8_bin default '';
+--sleep 1
+--echo "This should work"
+--replace_column 8 # 9 #
+SHOW EVENTS;
+--sleep 1
+ALTER TABLE mysql.event MODIFY db char(64) character set cp1251 default '';
+--error 1526
+SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+--sleep 1
+ALTER TABLE mysql.event MODIFY db varchar(64) character set utf8 collate utf8_bin default '';
+--error 1526
+SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+--sleep 1
+ALTER TABLE mysql.event DROP comment, DROP starts;
+--sleep 1
+--error 1525
+SELECT event_name FROM INFORMATION_SCHEMA.EVENTS;
+DROP TABLE mysql.event;
+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
+#
+
#
#INFORMATION_SCHEMA.EVENTS test begin
#
diff --git a/sql/event.cc b/sql/event.cc
index abca622835a..a5cf76240bb 100644
--- a/sql/event.cc
+++ b/sql/event.cc
@@ -51,8 +51,6 @@
- Make event_timed::get_show_create_event() work
- - Add function documentation whenever needed.
-
- Add logging to file
- Move comparison code to class event_timed
@@ -66,8 +64,144 @@ Warning:
QUEUE EVEX_EQ_NAME;
MEM_ROOT evex_mem_root;
+time_t mysql_event_last_create_time= 0L;
+
+
+static TABLE_FIELD_W_TYPE event_table_fields[EVEX_FIELD_COUNT] = {
+ {
+ {(char *) STRING_WITH_LEN("db")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("name")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("body")},
+ {(char *) STRING_WITH_LEN("longblob")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("definer")},
+ {(char *) STRING_WITH_LEN("char(77)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("execute_at")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("interval_value")},
+ {(char *) STRING_WITH_LEN("int(11)")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("interval_field")},
+ {(char *) STRING_WITH_LEN("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')")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("created")},
+ {(char *) STRING_WITH_LEN("timestamp")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("modified")},
+ {(char *) STRING_WITH_LEN("timestamp")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("last_executed")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ },
+ {
+ {(char *) STRING_WITH_LEN("starts")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("ends")},
+ {(char *) STRING_WITH_LEN("datetime")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("status")},
+ {(char *) STRING_WITH_LEN("enum('ENABLED','DISABLED')")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("on_completion")},
+ {(char *) STRING_WITH_LEN("enum('DROP','PRESERVE')")},
+ {NULL, 0}
+ },
+/*
+ {
+ {(char *) STRING_WITH_LEN("sql_mode")},
+ {(char *) STRING_WITH_LEN("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')")},
+ {NULL, 0}
+ },
+*/
+ {
+ {(char *) STRING_WITH_LEN("comment")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ }
+};
+
+
+LEX_STRING interval_type_to_name[] = {
+ {(char *) STRING_WITH_LEN("YEAR")},
+ {(char *) STRING_WITH_LEN("QUARTER")},
+ {(char *) STRING_WITH_LEN("MONTH")},
+ {(char *) STRING_WITH_LEN("DAY")},
+ {(char *) STRING_WITH_LEN("HOUR")},
+ {(char *) STRING_WITH_LEN("MINUTE")},
+ {(char *) STRING_WITH_LEN("WEEK")},
+ {(char *) STRING_WITH_LEN("SECOND")},
+ {(char *) STRING_WITH_LEN("MICROSECOND")},
+ {(char *) STRING_WITH_LEN("YEAR_MONTH")},
+ {(char *) STRING_WITH_LEN("DAY_HOUR")},
+ {(char *) STRING_WITH_LEN("DAY_MINUTE")},
+ {(char *) STRING_WITH_LEN("DAY_SECOND")},
+ {(char *) STRING_WITH_LEN("HOUR_MINUTE")},
+ {(char *) STRING_WITH_LEN("HOUR_SECOND")},
+ {(char *) STRING_WITH_LEN("MINUTE_SECOND")},
+ {(char *) STRING_WITH_LEN("DAY_MICROSECOND")},
+ {(char *) STRING_WITH_LEN("HOUR_MICROSECOND")},
+ {(char *) STRING_WITH_LEN("MINUTE_MICROSECOND")},
+ {(char *) STRING_WITH_LEN("SECOND_MICROSECOND")}
+};
+
+/*
+ Inits the scheduler queue - prioritized queue from mysys/queue.c
+
+ Synopsis
+ evex_queue_init()
+
+ queue - pointer the the memory to be initialized as queue. has to be
+ allocated from the caller
+
+ Notes
+ During initialization the queue is sized for 30 events, and when is full
+ will auto extent with 30.
+*/
+
void
evex_queue_init(EVEX_QUEUE_TYPE *queue)
{
@@ -77,6 +211,24 @@ evex_queue_init(EVEX_QUEUE_TYPE *queue)
}
+/*
+ Compares 2 LEX strings regarding case.
+
+ Synopsis
+ my_time_compare()
+
+ s - first LEX_STRING
+ t - second LEX_STRING
+ cs - charset
+
+ RETURNS:
+ -1 - s < t
+ 0 - s == t
+ 1 - s > t
+
+ Notes
+ TIME.second_part is not considered during comparison
+*/
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
{
@@ -85,6 +237,24 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
}
+/*
+ Compares 2 TIME structures
+
+ Synopsis
+ my_time_compare()
+
+ a - first TIME
+ b - second time
+
+ RETURNS:
+ -1 - a < b
+ 0 - a == b
+ 1 - a > b
+
+ Notes
+ TIME.second_part is not considered during comparison
+*/
+
int
my_time_compare(TIME *a, TIME *b)
{
@@ -106,6 +276,24 @@ my_time_compare(TIME *a, TIME *b)
}
+/*
+ Compares the execute_at members of 2 event_timed instances
+
+ Synopsis
+ event_timed_compare()
+
+ a - first event_timed object
+ b - second event_timed object
+
+ RETURNS:
+ -1 - a->execute_at < b->execute_at
+ 0 - a->execute_at == b->execute_at
+ 1 - a->execute_at > b->execute_at
+
+ Notes
+ execute_at.second_part is not considered during comparison
+*/
+
int
event_timed_compare(event_timed *a, event_timed *b)
{
@@ -114,7 +302,24 @@ event_timed_compare(event_timed *a, event_timed *b)
/*
- Callback for the prio queue
+ Compares the execute_at members of 2 event_timed instances.
+ Used as callback for the prioritized queue when shifting
+ elements inside.
+
+ Synopsis
+ event_timed_compare()
+
+ vptr - not used (set it to NULL)
+ a - first event_timed object
+ b - second event_timed object
+
+ RETURNS:
+ -1 - a->execute_at < b->execute_at
+ 0 - a->execute_at == b->execute_at
+ 1 - a->execute_at > b->execute_at
+
+ Notes
+ execute_at.second_part is not considered during comparison
*/
int
@@ -125,6 +330,145 @@ event_timed_compare_q(void *vptr, byte* a, byte *b)
/*
+ Reconstructs interval expression from interval type and expression
+ value that is in form of a value of the smalles entity:
+ For
+ YEAR_MONTH - expression is in months
+ DAY_MINUTE - expression is in minutes
+
+ Synopsis
+ event_reconstruct_interval_expression()
+ buf - preallocated String buffer to add the value to
+ interval - the interval type (for instance YEAR_MONTH)
+ expression - the value in the lowest entity
+
+ RETURNS
+ 0 - OK
+ 1 - Error
+
+
+*/
+
+int
+event_reconstruct_interval_expression(String *buf,
+ interval_type interval,
+ longlong expression)
+{
+ ulonglong expr= expression;
+ char tmp_buff[128], *end;
+ bool close_quote= TRUE;
+ int multipl= 0;
+ char separator=':';
+
+ switch (interval) {
+ case INTERVAL_YEAR_MONTH:
+ multipl= 12;
+ separator= '-';
+ goto common_1_lev_code;
+ case INTERVAL_DAY_HOUR:
+ multipl= 24;
+ separator= ' ';
+ goto common_1_lev_code;
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_MINUTE_SECOND:
+ multipl= 60;
+common_1_lev_code:
+ buf->append('\'');
+ end= longlong10_to_str(expression/multipl, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));
+ expr= expr - (expr/multipl)*multipl;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ {
+ int tmp_expr= expr;
+
+ tmp_expr/=(24*60);
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// days
+ buf->append(' ');
+
+ tmp_expr= expr - tmp_expr*(24*60);//minutes left
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_HOUR_SECOND:
+ {
+ int tmp_expr= expr;
+
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+ buf->append(':');
+
+ tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_DAY_SECOND:
+ {
+ int tmp_expr= expr;
+
+ tmp_expr/=(24*3600);
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// days
+ buf->append(' ');
+
+ tmp_expr= expr - tmp_expr*(24*3600);//seconds left
+ end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+ buf->append(':');
+
+ tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0));
+ return 1;
+ break;
+ case INTERVAL_QUARTER:
+ expr/= 3;
+ close_quote= FALSE;
+ break;
+ case INTERVAL_WEEK:
+ expr/= 7;
+ default:
+ close_quote= FALSE;
+ break;
+ }
+ if (close_quote)
+ buf->append(separator);
+ end= longlong10_to_str(expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));
+ if (close_quote)
+ buf->append('\'');
+
+ buf->append(' ');
+ LEX_STRING *ival= &interval_type_to_name[interval];
+ buf->append(ival->str, ival->length);
+
+ return 0;
+}
+
+
+/*
Open mysql.event table for read
SYNOPSIS
@@ -132,6 +476,7 @@ event_timed_compare_q(void *vptr, byte* a, byte *b)
thd Thread context
lock_type How to lock the table
table The table pointer
+
RETURN
1 Cannot lock table
2 The table is corrupted - different number of fields
@@ -153,9 +498,10 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table)
if (simple_open_n_lock_tables(thd, &tables))
DBUG_RETURN(1);
- if (tables.table->s->fields != EVEX_FIELD_COUNT)
+ if (table_check_intact(tables.table, EVEX_FIELD_COUNT, event_table_fields,
+ &mysql_event_last_create_time,
+ ER_EVENT_CANNOT_LOAD_FROM_TABLE))
{
- my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
close_thread_tables(thd);
DBUG_RETURN(2);
}
@@ -219,7 +565,6 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
}
-
/*
Puts some data common to CREATE and ALTER EVENT into a row.
@@ -245,15 +590,9 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update)
DBUG_ENTER("evex_fill_row");
- if (table->s->fields != EVEX_FIELD_COUNT)
- {
- my_error(ER_EVENT_COL_COUNT_DOESNT_MATCH, MYF(0), "mysql", "event");
- DBUG_RETURN(EVEX_GET_FIELD_FAILED);
- }
-
- DBUG_PRINT("info", ("dbname.len=[%s]",et->dbname.str));
- DBUG_PRINT("info", ("name.len=[%s]",et->name.str));
- DBUG_PRINT("info", ("body=[%s]",et->body.str));
+ DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
+ DBUG_PRINT("info", ("name =[%s]", et->name.str));
+ DBUG_PRINT("info", ("body =[%s]", et->body.str));
if (table->field[field_num= EVEX_FIELD_DB]->
store(et->dbname.str, et->dbname.length, system_charset_info))
@@ -584,16 +923,19 @@ err:
*/
static int
-db_find_event(THD *thd, sp_name *name, LEX_STRING definer, event_timed **ett,
- TABLE *tbl)
+db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett,
+ TABLE *tbl, MEM_ROOT *root)
{
TABLE *table;
int ret;
char *ptr;
- event_timed *et;
+ event_timed *et=NULL;
DBUG_ENTER("db_find_event");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+ if (!root)
+ root= &evex_mem_root;
+
if (tbl)
table= tbl;
else if (evex_open_event_table(thd, TL_READ, &table))
@@ -603,7 +945,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING definer, event_timed **ett,
goto done;
}
- if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, definer,
+ if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, *definer,
table)))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
@@ -617,7 +959,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING definer, event_timed **ett,
2)::load_from_row() is silent on error therefore we emit error msg here
*/
- if ((ret= et->load_from_row(&evex_mem_root, table)))
+ if ((ret= et->load_from_row(root, table)))
{
my_error(ER_EVENT_CANNOT_LOAD_FROM_TABLE, MYF(0));
goto done;
@@ -671,10 +1013,11 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer,
thd->reset_n_backup_open_tables_state(&backup);
// no need to use my_error() here because db_find_event() has done it
- if ((ret= db_find_event(thd, spn, definer, &ett, NULL)))
+ ret= db_find_event(thd, spn, &definer, &ett, NULL, NULL);
+ thd->restore_backup_open_tables_state(&backup);
+ if (ret)
goto done;
- thd->restore_backup_open_tables_state(&backup);
/*
allocate on evex_mem_root. if you call without evex_mem_root
then sphead will not be cleared!
@@ -704,10 +1047,31 @@ done:
}
+/*
+ Removes from queue in memory the event which is identified by the tupple
+ (db, name).
+
+ SYNOPSIS
+ evex_remove_from_cache()
+
+ db - db name
+ name - event name
+ use_lock - whether to lock the mutex LOCK_event_arrays or not in case it
+ has been already locked outside
+ is_drop - if an event is currently being executed then we can also delete
+ the event_timed instance, so we alarm the event that it should
+ drop itself if this parameter is set to TRUE. It's false on
+ ALTER EVENT.
+
+ RETURNS
+ 0 - OK (always)
+*/
+
static int
evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock,
bool is_drop)
{
+ //ToDo : Add definer to the tuple (db, name) to become triple
uint i;
DBUG_ENTER("evex_remove_from_cache");
@@ -755,8 +1119,6 @@ done:
}
-
-
/*
The function exported to the world for creating of events.
@@ -814,8 +1176,8 @@ done:
NOTES
et contains data about dbname and event name.
- name is the new name of the event, if not null this means
- that RENAME TO was specified in the query.
+ new_name is the new name of the event, if not null (this means
+ that RENAME TO was specified in the query)
*/
int
@@ -923,3 +1285,75 @@ done:
DBUG_RETURN(ret);
}
+
+/*
+ SHOW CREATE EVENT
+
+ SYNOPSIS
+ evex_show_create_event()
+ thd THD
+ spn the name of the event (db, name)
+ definer the definer of the event
+
+ RETURNS
+ 0 - OK
+ 1 - Error during writing to the wire
+
+*/
+
+int
+evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer)
+{
+ int ret;
+ event_timed *et= NULL;
+ Open_tables_state backup;
+
+ DBUG_ENTER("evex_update_event");
+ DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
+
+ thd->reset_n_backup_open_tables_state(&backup);
+ ret= db_find_event(thd, spn, &definer, &et, NULL, thd->mem_root);
+ thd->restore_backup_open_tables_state(&backup);
+
+ if (!ret && et)
+ {
+ Protocol *protocol= thd->protocol;
+ char show_str_buf[768];
+ String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
+ List<Item> field_list;
+ const char *sql_mode_str;
+ ulong sql_mode_len=0;
+
+ show_str.length(0);
+ show_str.set_charset(system_charset_info);
+
+ if (et->get_create_event(thd, &show_str))
+ DBUG_RETURN(1);
+
+ field_list.push_back(new Item_empty_string("Event", NAME_LEN));
+
+ 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("sql_mode", sql_mode_len));
+
+ field_list.push_back(new Item_empty_string("Create Event",
+ show_str.length()));
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ DBUG_RETURN(1);
+
+ protocol->prepare_for_resend();
+ protocol->store(et->name.str, et->name.length, system_charset_info);
+
+ protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+
+
+ protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info);
+ ret= protocol->write();
+ send_eof(thd);
+ }
+
+ DBUG_RETURN(ret);
+}
diff --git a/sql/event.h b/sql/event.h
index 3d00ca388b9..ed75b68b305 100644
--- a/sql/event.h
+++ b/sql/event.h
@@ -108,6 +108,7 @@ public:
enum enum_event_on_completion on_completion;
enum enum_event_status status;
sp_head *sphead;
+ ulong sql_mode;
const uchar *body_begin;
@@ -118,8 +119,9 @@ public:
event_timed():running(0), status_changed(false), last_executed_changed(false),
expression(0), created(0), modified(0),
on_completion(MYSQL_EVENT_ON_COMPLETION_DROP),
- status(MYSQL_EVENT_ENABLED), sphead(0), dropped(false),
- free_sphead_on_delete(true), flags(0)
+ status(MYSQL_EVENT_ENABLED), sphead(0), sql_mode(0),
+ body_begin(0), dropped(false), free_sphead_on_delete(true),
+ flags(0)
{
pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST);
@@ -176,8 +178,8 @@ public:
bool
update_fields(THD *thd);
- char *
- get_show_create_event(THD *thd, uint32 *length);
+ int
+ get_create_event(THD *thd, String *buf);
int
execute(THD *thd, MEM_ROOT *mem_root= NULL);
@@ -220,9 +222,17 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
int
evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
+int
+evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer);
+
int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
int
+event_reconstruct_interval_expression(String *buf,
+ interval_type interval,
+ longlong expression);
+
+int
init_events();
void
diff --git a/sql/event_executor.cc b/sql/event_executor.cc
index 7960f1e1758..9c247f11dab 100644
--- a/sql/event_executor.cc
+++ b/sql/event_executor.cc
@@ -32,9 +32,9 @@
extern ulong thread_created;
extern const char *my_localhost;
-pthread_mutex_t LOCK_event_arrays,
- LOCK_workers_count,
- LOCK_evex_running;
+pthread_mutex_t LOCK_event_arrays, // mutex for when working with the queue
+ LOCK_workers_count, // mutex for when inc/dec uint workers_count
+ LOCK_evex_running; // mutes for managing bool evex_is_running
bool evex_is_running= false;
@@ -61,6 +61,19 @@ event_executor_worker(void *arg);
pthread_handler_t
event_executor_main(void *arg);
+
+/*
+ Returns the seconds difference of 2 TIME structs
+
+ SYNOPSIS
+ evex_time_diff()
+ a - TIME struct 1
+ b - TIME struct 2
+
+ Returns:
+ the seconds difference
+*/
+
static int
evex_time_diff(TIME *a, TIME *b)
{
@@ -68,6 +81,19 @@ evex_time_diff(TIME *a, TIME *b)
}
+/*
+ Inits the mutexes used by the scheduler module
+
+ SYNOPSIS
+ evex_init_mutexes()
+
+ NOTES
+ The mutexes are :
+ LOCK_event_arrays
+ LOCK_workers_count
+ LOCK_evex_running
+*/
+
static void
evex_init_mutexes()
{
@@ -83,6 +109,75 @@ evex_init_mutexes()
}
+/*
+ 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
+ evex_check_system_tables()
+*/
+
+void
+evex_check_system_tables()
+{
+ THD *thd= current_thd;
+ TABLE_LIST tables;
+ bool not_used;
+ Open_tables_state backup;
+
+ // thd is 0x0 during boot of the server. Later it's !=0x0
+ if (!thd)
+ return;
+
+ 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 (simple_open_n_lock_tables(thd, &tables))
+ sql_print_error("Cannot open mysql.db");
+ else
+ {
+ table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT, mysql_db_table_fields,
+ &mysql_db_table_last_check,ER_EVENT_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("Cannot open mysql.db");
+ else
+ {
+ 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 29");
+
+ close_thread_tables(thd);
+ }
+
+ thd->restore_backup_open_tables_state(&backup);
+}
+
+
+/*
+ Inits the scheduler. Called on server start and every time the scheduler
+ is started with switching the event_scheduler global variable to TRUE
+
+ SYNOPSIS
+ init_events()
+
+ NOTES
+ Inits the mutexes used by the scheduler. Done at server start.
+*/
+
int
init_events()
{
@@ -91,6 +186,8 @@ init_events()
DBUG_ENTER("init_events");
DBUG_PRINT("info",("Starting events main thread"));
+
+ evex_check_system_tables();
evex_init_mutexes();
@@ -113,6 +210,16 @@ init_events()
}
+/*
+ Cleans up scheduler memory. Called on server shutdown.
+
+ SYNOPSIS
+ shutdown_events()
+
+ NOTES
+ Destroys the mutexes.
+*/
+
void
shutdown_events()
{
@@ -129,6 +236,22 @@ shutdown_events()
}
+/*
+ Inits an scheduler thread handler, both the main and a worker
+
+ SYNOPSIS
+ init_event_thread()
+ thd - the THD of the thread. Has to be allocated by the caller.
+
+ NOTES
+ 1. The host of the thead is my_localhost
+ 2. thd->net is initted with NULL - no communication.
+
+ Returns
+ 0 - OK
+ -1 - Error
+*/
+
static int
init_event_thread(THD* thd)
{
@@ -165,6 +288,22 @@ init_event_thread(THD* thd)
DBUG_RETURN(0);
}
+
+/*
+ The main scheduler thread. Inits the priority queue on start and
+ destroys it on thread shutdown. Forks child threads for every event
+ execution. Sleeps between thread forking and does not do a busy wait.
+
+ SYNOPSIS
+ event_executor_main()
+ arg - unused
+
+ NOTES
+ 1. The host of the thead is my_localhost
+ 2. thd->net is initted with NULL - no communication.
+
+*/
+
pthread_handler_t
event_executor_main(void *arg)
{
@@ -436,6 +575,15 @@ err_no_thd:
}
+/*
+ Function that executes an event in a child thread. Setups the
+ environment for the event execution and cleans after that.
+
+ SYNOPSIS
+ event_executor_worker()
+ arg - the event_timed object to be processed
+*/
+
pthread_handler_t
event_executor_worker(void *event_void)
{
@@ -561,6 +709,24 @@ err_no_thd:
}
+/*
+ Loads all ENABLED events from mysql.event into the prioritized
+ queue. Called during scheduler main thread initialization. Compiles
+ the events. Creates event_timed instances for every ENABLED event
+ from mysql.event.
+
+ SYNOPSIS
+ evex_load_events_from_db()
+ thd - Thread context. Used for memory allocation in some cases.
+
+ RETURNS
+ 0 - OK
+ -1 - Error
+
+ NOTES
+ Reports the error to the console
+*/
+
static int
evex_load_events_from_db(THD *thd)
{
@@ -647,7 +813,22 @@ end:
}
-bool sys_var_event_executor::update(THD *thd, set_var *var)
+/*
+ The update method of the global variable event_scheduler.
+ If event_scheduler is switched from 0 to 1 then the scheduler main
+ thread is started.
+
+ SYNOPSIS
+ event_executor_worker()
+ thd - Thread context (unused)
+ car - the new value
+
+ Returns
+ 0 - OK (always)
+*/
+
+bool
+sys_var_event_executor::update(THD *thd, set_var *var)
{
// here start the thread if not running.
DBUG_ENTER("sys_var_event_executor::update");
diff --git a/sql/event_priv.h b/sql/event_priv.h
index 7d1cdbcd264..16e415bc19d 100644
--- a/sql/event_priv.h
+++ b/sql/event_priv.h
@@ -40,7 +40,6 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname,
int
event_timed_compare_q(void *vptr, byte* a, byte *b);
-
#define EXEC_QUEUE_QUEUE_NAME executing_queue
#define EXEC_QUEUE_DARR_NAME evex_executing_queue
diff --git a/sql/event_timed.cc b/sql/event_timed.cc
index 28d21089b74..3b98d66497b 100644
--- a/sql/event_timed.cc
+++ b/sql/event_timed.cc
@@ -116,7 +116,10 @@ event_timed::init_body(THD *thd)
while (body.length && body_begin[body.length-1] == '\0')
body.length--;
- body.str= strmake_root(root, (char *)body_begin, body.length);
+ //the first is always space which I cannot skip in the parser
+ DBUG_ASSERT(*body_begin == ' ');
+ body.length--;
+ body.str= strmake_root(root, (char *)body_begin + 1, body.length);
DBUG_VOID_RETURN;
}
@@ -847,6 +850,15 @@ err:
}
+/*
+ Set the internal last_executed TIME struct to now. NOW is the
+ time according to thd->query_start(), so the THD's clock.
+
+ Synopsis
+ event_timed::drop()
+ thd - thread context
+*/
+
void
event_timed::mark_last_executed(THD *thd)
{
@@ -864,7 +876,13 @@ event_timed::mark_last_executed(THD *thd)
/*
- Returns :
+ Drops the event
+
+ Synopsis
+ event_timed::drop()
+ thd - thread context
+
+ RETURNS :
0 - OK
-1 - Cannot open mysql.event
-2 - Cannot find the event in mysql.event (already deleted?)
@@ -894,6 +912,22 @@ event_timed::drop(THD *thd)
}
+/*
+ Saves status and last_executed_at to the disk if changed.
+
+ Synopsis
+ event_timed::drop()
+ thd - thread context
+
+ Returns :
+ 0 - OK
+ SP_OPEN_TABLE_FAILED - Error while opening mysql.event for writing
+ EVEX_WRITE_ROW_FAILED - On error to write to disk
+
+ others - return code from SE in case deletion of the event row
+ failed.
+*/
+
bool
event_timed::update_fields(THD *thd)
{
@@ -951,40 +985,75 @@ done:
}
-char *
-event_timed::get_show_create_event(THD *thd, uint32 *length)
+/*
+ Get SHOW CREATE EVENT as string
+
+ thd - Thread
+ buf - String*, should be already allocated. CREATE EVENT goes inside.
+
+ Returns:
+ 0 - OK
+ 1 - Error (for now if mysql.event has been tampered and MICROSECONDS
+ interval or derivative has been put there.
+*/
+
+int
+event_timed::get_create_event(THD *thd, String *buf)
{
- char *dst, *ret;
- uint len, tmp_len;
- DBUG_ENTER("get_show_create_event");
+ int multipl= 0;
+ char tmp_buff[128];
+ String expr_buf(tmp_buff, sizeof(tmp_buff), system_charset_info);
+ expr_buf.length(0);
+
+ DBUG_ENTER("get_create_event");
DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]", body.length, body.str));
- len = strlen("CREATE EVENT `") + dbname.length + strlen("`.`") + name.length +
- strlen("` ON SCHEDULE EVERY 5 MINUTE DO ") + body.length;// + strlen(";");
-
- ret= dst= (char*) alloc_root(thd->mem_root, len + 1);
- memcpy(dst, "CREATE EVENT `", tmp_len= strlen("CREATE EVENT `"));
- dst+= tmp_len;
- memcpy(dst, dbname.str, tmp_len=dbname.length);
- dst+= tmp_len;
- memcpy(dst, "`.`", tmp_len= strlen("`.`"));
- dst+= tmp_len;
- memcpy(dst, name.str, tmp_len= name.length);
- dst+= tmp_len;
- memcpy(dst, "` ON SCHEDULE EVERY 5 MINUTE DO ",
- tmp_len= strlen("` ON SCHEDULE EVERY 5 MINUTE DO "));
- dst+= tmp_len;
-
- memcpy(dst, body.str, tmp_len= body.length);
- dst+= tmp_len;
-// memcpy(dst, ";", 1);
-// ++dst;
- *dst= '\0';
-
- *length= len;
-
- DBUG_PRINT("ret_info",("len=%d",*length));
- DBUG_RETURN(ret);
+ if (expression &&
+ event_reconstruct_interval_expression(&expr_buf, interval, expression))
+ DBUG_RETURN(1);
+
+ buf->append(STRING_WITH_LEN("CREATE EVENT "));
+ append_identifier(thd, buf, dbname.str, dbname.length);
+ buf->append(STRING_WITH_LEN("."));
+ append_identifier(thd, buf, name.str, name.length);
+
+ buf->append(STRING_WITH_LEN(" ON SCHEDULE "));
+ if (expression)
+ {
+ buf->append(STRING_WITH_LEN("EVERY "));
+ buf->append(expr_buf);
+ }
+ else
+ {
+ char dtime_buff[20*2+32];// +32 to make my_snprintf_{8bit|ucs2} happy
+ buf->append(STRING_WITH_LEN("AT '"));
+ /*
+ pass the buffer and the second param tells fills the buffer and returns
+ the number of chars to copy
+ */
+ buf->append(dtime_buff, my_datetime_to_str(&execute_at, dtime_buff));
+ buf->append(STRING_WITH_LEN("'"));
+ }
+
+ if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
+ buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
+ else
+ buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));
+
+ if (status == MYSQL_EVENT_ENABLED)
+ buf->append(STRING_WITH_LEN("ENABLE"));
+ else
+ buf->append(STRING_WITH_LEN("DISABLE"));
+
+ if (comment.length)
+ {
+ buf->append(STRING_WITH_LEN(" COMMENT "));
+ append_unescaped(buf, comment.str, comment.length);
+ }
+ buf->append(STRING_WITH_LEN(" DO "));
+ buf->append(body.str, body.length);
+
+ DBUG_RETURN(0);
}
@@ -1045,13 +1114,21 @@ done:
/*
+ Compiles an event before it's execution. Compiles the anonymous
+ sp_head object held by the event
+
+ Synopsis
+ event_timed::compile()
+ thd - thread context, used for memory allocation mostly
+ mem_root - if != NULL then this memory root is used for allocs
+ instead of thd->mem_root
+
Returns
0 - Success
EVEX_COMPILE_ERROR - Error during compilation
*/
-
int
event_timed::compile(THD *thd, MEM_ROOT *mem_root)
{
@@ -1064,9 +1141,13 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root)
char *old_query;
uint old_query_len;
st_sp_chistics *p;
- CHARSET_INFO *old_character_set_client, *old_collation_connection,
+ char create_buf[2048];
+ String show_create(create_buf, sizeof(create_buf), system_charset_info);
+ CHARSET_INFO *old_character_set_client,
+ *old_collation_connection,
*old_character_set_results;
-
+
+ show_create.length(0);
old_character_set_client= thd->variables.character_set_client;
old_character_set_results= thd->variables.character_set_results;
old_collation_connection= thd->variables.collation_connection;
@@ -1089,7 +1170,11 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root)
old_query= thd->query;
old_db= thd->db;
thd->db= dbname.str;
- thd->query= get_show_create_event(thd, &thd->query_length);
+
+ get_create_event(thd, &show_create);
+
+ thd->query= show_create.c_ptr();
+ thd->query_length= show_create.length();
DBUG_PRINT("event_timed::compile", ("query:%s",thd->query));
thd->lex= &lex;
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 80910a8fd81..95b2ec739cf 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5779,10 +5779,10 @@ ER_EVENT_OPEN_TABLE_FAILED
eng "Failed to open mysql.event"
ER_EVENT_NEITHER_M_EXPR_NOR_M_AT
eng "No datetime expression provided"
-ER_EVENT_COL_COUNT_DOESNT_MATCH
- eng "Column count of %s.%s is wrong. Table probably corrupted"
+ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
+ eng "Column count of mysql.%s is wrong. Table probably corrupted. Expected %d, found %d."
ER_EVENT_CANNOT_LOAD_FROM_TABLE
- eng "Cannot load from mysql.event. Table probably corrupted"
+ eng "Cannot load from mysql.%s. Table probably corrupted. See error log."
ER_EVENT_CANNOT_DELETE
eng "Failed to delete the event from mysql.event"
ER_EVENT_COMPILE_ERROR
@@ -5798,3 +5798,5 @@ ER_CANT_WRITE_LOCK_LOG_TABLE
eng "You can't write-lock a log table. Only read access is possible."
ER_CANT_READ_LOCK_LOG_TABLE
eng "You can't use usual read lock with log tables. Try READ LOCAL instead."
+ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
+ eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use scripts/mysql_fix_privilege_tables"
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 8877d607e92..c0842c48e4b 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -36,6 +36,122 @@
#define FIRST_NON_YN_FIELD 26
+time_t mysql_db_table_last_check= 0L;
+
+TABLE_FIELD_W_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
+ {
+ {(char *) STRING_WITH_LEN("Host")},
+ {(char *) STRING_WITH_LEN("char(60)")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Db")},
+ {(char *) STRING_WITH_LEN("char(64)")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("User")},
+ {(char *) STRING_WITH_LEN("char(16)")},
+ {NULL, 0}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Select_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Insert_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Update_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Delete_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Create_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Drop_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Grant_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("References_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Index_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Alter_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Create_tmp_table_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Lock_tables_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Create_view_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Show_view_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Create_routine_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Alter_routine_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Execute_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Event_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ },
+ {
+ {(char *) STRING_WITH_LEN("Trigger_priv")},
+ {(char *) STRING_WITH_LEN("enum('N','Y')")},
+ {(char *) STRING_WITH_LEN("utf8")}
+ }
+};
+
+
class acl_entry :public hash_filo_element
{
public:
@@ -435,14 +551,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
- update_hostname(&db.host,get_field(&mem, table->field[0]));
- db.db=get_field(&mem, table->field[1]);
+ update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
+ db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
if (!db.db)
{
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
continue;
}
- db.user=get_field(&mem, table->field[2]);
+ db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
{
sql_print_warning("'db' entry '%s %s@%s' "
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index f42406ca1d2..6cd61a77c49 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -129,6 +129,36 @@
(((A) & ALTER_PROC_ACL) >> 23) | \
(((A) & GRANT_ACL) >> 8))
+enum mysql_db_table_field
+{
+ MYSQL_DB_FIELD_HOST = 0,
+ MYSQL_DB_FIELD_DB,
+ MYSQL_DB_FIELD_USER,
+ MYSQL_DB_FIELD_SELECT_PRIV,
+ MYSQL_DB_FIELD_INSERT_PRIV,
+ MYSQL_DB_FIELD_UPDATE_PRIV,
+ MYSQL_DB_FIELD_DELETE_PRIV,
+ MYSQL_DB_FIELD_CREATE_PRIV,
+ MYSQL_DB_FIELD_DROP_PRIV,
+ MYSQL_DB_FIELD_GRANT_PRIV,
+ MYSQL_DB_FIELD_REFERENCES_PRIV,
+ MYSQL_DB_FIELD_INDEX_PRIV,
+ MYSQL_DB_FIELD_ALTER_PRIV,
+ MYSQL_DB_FIELD_CREATE_TMP_TABLE_PRIV,
+ MYSQL_DB_FIELD_LOCK_TABLES_PRIV,
+ MYSQL_DB_FIELD_CREATE_VIEW_PRIV,
+ MYSQL_DB_FIELD_SHOW_VIEW_PRIV,
+ MYSQL_DB_FIELD_CREATE_ROUTINE_PRIV,
+ MYSQL_DB_FIELD_ALTER_ROUTINE_PRIV,
+ MYSQL_DB_FIELD_EXECUTE_PRIV,
+ MYSQL_DB_FIELD_EVENT_PRIV,
+ MYSQL_DB_FIELD_TRIGGER_PRIV,
+ MYSQL_DB_FIELD_COUNT
+};
+
+extern TABLE_FIELD_W_TYPE mysql_db_table_fields[];
+extern time_t mysql_db_table_last_check;
+
/* Classes */
struct acl_host_and_ip
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ed7e7dfb684..1ea09b9ab16 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3735,6 +3735,14 @@ end_with_restore_list:
}
case SQLCOM_SHOW_CREATE_EVENT:
{
+ DBUG_ASSERT(lex->spname);
+ DBUG_ASSERT(lex->et);
+ if (! lex->spname->m_db.str)
+ {
+ my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
+ res= true;
+ break;
+ }
if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
is_schema_db(lex->spname->m_db.str)))
break;
@@ -3744,8 +3752,7 @@ end_with_restore_list:
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
goto error;
}
- /* TODO : Implement it */
- send_ok(thd, 1);
+ res= evex_show_create_event(thd, lex->spname, lex->et->definer);
break;
}
case SQLCOM_CREATE_FUNCTION: // UDF function
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 58f94887ef6..620f8ab6022 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3772,30 +3772,6 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables,
}
-static LEX_STRING interval_type_to_name[] = {
- {(char *) STRING_WITH_LEN("INTERVAL_YEAR")},
- {(char *) STRING_WITH_LEN("INTERVAL_QUARTER")},
- {(char *) STRING_WITH_LEN("INTERVAL_MONTH")},
- {(char *) STRING_WITH_LEN("INTERVAL_DAY")},
- {(char *) STRING_WITH_LEN("INTERVAL_HOUR")},
- {(char *) STRING_WITH_LEN("INTERVAL_MINUTE")},
- {(char *) STRING_WITH_LEN("INTERVAL_WEEK")},
- {(char *) STRING_WITH_LEN("INTERVAL_SECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_MICROSECOND ")},
- {(char *) STRING_WITH_LEN("INTERVAL_YEAR_MONTH")},
- {(char *) STRING_WITH_LEN("INTERVAL_DAY_HOUR")},
- {(char *) STRING_WITH_LEN("INTERVAL_DAY_MINUTE")},
- {(char *) STRING_WITH_LEN("INTERVAL_DAY_SECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_HOUR_MINUTE")},
- {(char *) STRING_WITH_LEN("INTERVAL_HOUR_SECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_MINUTE_SECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_DAY_MICROSECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_HOUR_MICROSECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_MINUTE_MICROSECOND")},
- {(char *) STRING_WITH_LEN("INTERVAL_SECOND_MICROSECOND")}
-};
-
-
static interval_type get_real_interval_type(interval_type i_type)
{
switch (i_type) {
@@ -3869,6 +3845,7 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[9]->set_null();
if (et.expression)
{
+ String show_str;
//type
sch_table->field[5]->store(STRING_WITH_LEN("RECURRING"), scs);
//execute_at
@@ -3877,9 +3854,11 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
sch_table->field[7]->set_notnull();
sch_table->field[7]->store((longlong) et.expression);
//interval_type
- LEX_STRING *ival=&interval_type_to_name[get_real_interval_type(et.interval)];
+ if (event_reconstruct_interval_expression(&show_str, et.interval,
+ et.expression))
+ DBUG_RETURN(1);
sch_table->field[8]->set_notnull();
- sch_table->field[8]->store(ival->str, ival->length, scs);
+ sch_table->field[8]->store(show_str.c_ptr(), show_str.length(), scs);
//starts & ends
sch_table->field[10]->set_notnull();
sch_table->field[10]->store_time(&et.starts, MYSQL_TIMESTAMP_DATETIME);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a86eccb493f..3135c9ba216 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -8402,6 +8402,10 @@ show_param:
{
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
Lex->spname= $3;
+ Lex->et= new event_timed();
+ if (!Lex->et)
+ YYABORT;
+ Lex->et->init_definer(YYTHD);
}
;
diff --git a/sql/table.cc b/sql/table.cc
index b912683d371..2cd039c9e0d 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -343,9 +343,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
allow to lock such tables for writing with any other tables (even with
other system tables) and some privilege tables need this.
*/
- if (!my_strcasecmp(system_charset_info, share->table_name.str, "proc")
- || !my_strcasecmp(system_charset_info, share->table_name.str,
- "event"))
+ if (!my_strcasecmp(system_charset_info, share->table_name.str, "proc"))
share->system_table= 1;
else
{
@@ -2296,6 +2294,144 @@ bool check_column_name(const char *name)
return last_char_is_space || (uint) (name - start) > NAME_LEN;
}
+
+/*
+ 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
+ 0 - OK
+ 1 - There was an error
+*/
+
+my_bool
+table_check_intact(TABLE *table, uint table_f_count,
+ TABLE_FIELD_W_TYPE *table_def, time_t *last_create_time,
+ int error_num)
+{
+ uint i;
+ my_bool error= FALSE;
+ my_bool fields_diff_count;
+ DBUG_ENTER("table_check_intact");
+ DBUG_PRINT("info",("table=%s expected_count=%d",table->alias, table_f_count));
+ DBUG_PRINT("info",("last_create_time=%d", *last_create_time));
+
+ if ((fields_diff_count= (table->s->fields != table_f_count)) ||
+ (*last_create_time != table->file->create_time))
+ {
+ DBUG_PRINT("info", ("I am suspecting, checking table"));
+ if (fields_diff_count)
+ {
+ // 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);
+ 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);
+ else
+ /*
+ 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.
+ */
+ error= FALSE;
+ }
+ //definitely something has changed
+ char buffer[255];
+ for (i=0 ;i < table_f_count; ++i, ++table_def)
+ {
+ Field *field= table->field[i];
+ String sql_type(buffer, sizeof(buffer), system_charset_info);
+ sql_type.length(0);
+ /*
+ name changes are not fatal, we use sequence numbers => no prob for us
+ but this can show tampered table or broken table.
+ */
+ if (!fields_diff_count || i < table->s->fields)
+ {
+ 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(), 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());
+ 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;
+ }
+ }
+ else
+ {
+ sql_print_error("(%s) Expected field %s at position %d to have type %s "
+ " but no field found.", table_def->name.str,
+ table_def->name.str, i, table_def->type.str);
+ error= TRUE;
+ }
+ }
+ if (!error)
+ *last_create_time= table->file->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 through checking."));
+ *last_create_time= table->file->create_time;
+ }
+
+ DBUG_RETURN(error);
+}
+
+
/*
Create Item_field for each column in the table.
diff --git a/sql/table.h b/sql/table.h
index 213310a51dc..5305c9af005 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -864,4 +864,15 @@ typedef struct st_open_table_list{
uint32 in_use,locked;
} OPEN_TABLE_LIST;
+typedef struct st_table_field_w_type
+{
+ LEX_STRING name;
+ LEX_STRING type;
+ LEX_STRING cset;
+} TABLE_FIELD_W_TYPE;
+
+my_bool
+table_check_intact(TABLE *table, uint table_f_count,
+ TABLE_FIELD_W_TYPE *table_def, time_t *last_create_time,
+ int error_num);