diff options
-rw-r--r-- | mysql-test/r/events.result | 207 | ||||
-rw-r--r-- | mysql-test/t/events.test | 132 | ||||
-rw-r--r-- | sql/event.cc | 486 | ||||
-rw-r--r-- | sql/event.h | 18 | ||||
-rw-r--r-- | sql/event_executor.cc | 189 | ||||
-rw-r--r-- | sql/event_priv.h | 1 | ||||
-rw-r--r-- | sql/event_timed.cc | 159 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 8 | ||||
-rw-r--r-- | sql/sql_acl.cc | 122 | ||||
-rw-r--r-- | sql/sql_acl.h | 30 | ||||
-rw-r--r-- | sql/sql_parse.cc | 11 | ||||
-rw-r--r-- | sql/sql_show.cc | 31 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 4 | ||||
-rw-r--r-- | sql/table.cc | 142 | ||||
-rw-r--r-- | sql/table.h | 11 |
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); |