diff --git a/mysql-test/lib/init_db.sql b/mysql-test/lib/init_db.sql index cef7933d808..290b383f5ea 100644 --- a/mysql-test/lib/init_db.sql +++ b/mysql-test/lib/init_db.sql @@ -596,7 +596,7 @@ CREATE TABLE event ( status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY (db,name) + PRIMARY KEY (definer, db, name) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; CREATE DATABASE IF NOT EXISTS cluster_replication; diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index 1fbeb75b963..9444a15c0e9 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -27,6 +27,102 @@ set event_scheduler=0; ERROR HY000: Variable 'event_scheduler' is a GLOBAL variable and should be set with SET GLOBAL set global event_scheduler=2; ERROR 42000: Variable 'event_scheduler' can't be set to the value of '2' +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 +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 +CREATE DATABASE events_test2; +CREATE USER ev_test@localhost; +GRANT ALL ON events_test.* to ev_test@localhost; +GRANT ALL on events_test2.* to ev_test@localhost; +REVOKE EVENT ON events_test2.* FROM ev_test@localhost; +REVOKE PROCESS on *.* from ev_test@localhost; +select "NEW CONNECTION"; +NEW CONNECTION +NEW CONNECTION +SELECT USER(), DATABASE(); +USER() DATABASE() +ev_test@localhost events_test2 +SHOW GRANTS; +Grants for ev_test@localhost +GRANT USAGE ON *.* TO 'ev_test'@'localhost' +GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost' +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `events_test2`.* TO 'ev_test'@'localhost' +select "Here comes an error:"; +Here comes an error: +Here comes an error: +SHOW EVENTS; +ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2' +USE events_test; +select "Now the list should be empty:"; +Now the list should be empty: +Now the list should be empty: +SHOW EVENTS; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +select concat("Let's create some new events from the name of ",user()); +concat("Let's create some new events from the name of ",user()) +Let's create some new events from the name of ev_test@localhost +create event one_event on schedule every 20 second do select 123; +create event two_event on schedule every 20 second on completion not preserve comment "two event" do select 123; +create event three_event on schedule every 20 second on completion preserve comment "three event" do select 123; +select "Now we should see 3 events:"; +Now we should see 3 events: +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 +select "This should show us only 3 events:"; +This should show us only 3 events: +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 +select "This should show us only 2 events:"; +This should show us only 2 events: +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 +select "This should show us no events:"; +This should show us no events: +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 +DROP DATABASE events_test2; +select "should see 1 event:"; +should see 1 event: +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 +select "we should see 4 events now:"; +we should see 4 events now: +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 +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 +drop event one_event; +drop event two_event; +drop event three_event; +drop user ev_test@localhost; +drop event one_event; set global event_scheduler=0; select count(*) from mysql.event; count(*) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 8778ded244f..40706fd9c55 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -44,6 +44,7 @@ COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS COLUMN_PRIVILEGES ENGINES +EVENTS KEY_COLUMN_USAGE PARTITIONS PLUGINS @@ -734,7 +735,7 @@ CREATE TABLE t_crashme ( f1 BIGINT); CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1; CREATE VIEW a2 AS SELECT t_CRASHME FROM a1; count(*) -109 +110 drop view a2, a1; drop table t_crashme; select table_schema,table_name, column_name from @@ -742,6 +743,8 @@ information_schema.columns where data_type = 'longtext'; table_schema table_name column_name information_schema COLUMNS COLUMN_TYPE +information_schema EVENTS EVENT_BODY +information_schema EVENTS SQL_MODE information_schema PARTITIONS PARTITION_EXPRESSION information_schema PARTITIONS SUBPARTITION_EXPRESSION information_schema PARTITIONS PARTITION_DESCRIPTION @@ -756,6 +759,12 @@ information_schema VIEWS VIEW_DEFINITION select table_name, column_name, data_type from information_schema.columns where data_type = 'datetime'; table_name column_name data_type +EVENTS EXECUTE_AT datetime +EVENTS STARTS datetime +EVENTS ENDS datetime +EVENTS CREATED datetime +EVENTS LAST_ALTERED datetime +EVENTS LAST_EXECUTED datetime PARTITIONS CREATE_TIME datetime PARTITIONS UPDATE_TIME datetime PARTITIONS CHECK_TIME datetime @@ -817,7 +826,7 @@ flush privileges; SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA; table_schema count(*) cluster_replication 1 -information_schema 19 +information_schema 20 mysql 21 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 7832e34dfd1..1ad996dedd5 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -7,6 +7,7 @@ COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS COLUMN_PRIVILEGES ENGINES +EVENTS KEY_COLUMN_USAGE PARTITIONS PLUGINS diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index e3abd7f5200..b5821e463df 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -186,6 +186,26 @@ proc CREATE TABLE `proc` ( `comment` char(64) character set utf8 collate utf8_bin NOT NULL default '', PRIMARY KEY (`db`,`name`,`type`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stored Procedures' +show create table event; +Table Create Table +event CREATE TABLE `event` ( + `db` char(64) 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` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', + PRIMARY KEY (`definer`,`db`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events' show create table general_log; Table Create Table general_log CREATE TABLE `general_log` ( @@ -211,25 +231,5 @@ slow_log CREATE TABLE `slow_log` ( `server_id` int(11) default NULL, `sql_text` mediumtext NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' -show create table event; -Table Create Table -event CREATE TABLE `event` ( - `db` char(64) 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` varchar(64) character set utf8 collate utf8_bin NOT NULL default '', - PRIMARY KEY (`db`,`name`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events' show tables; Tables_in_test diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index ee165ad8bd9..638d728af3f 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -28,6 +28,80 @@ set event_scheduler=0; --error 1231 set global event_scheduler=2; +# +#INFORMATION_SCHEMA.EVENTS test begin +# +create event one_event on schedule every 10 second do select 123; +--replace_column 8 # 9 # +SHOW EVENTS; +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; +CREATE DATABASE events_test2; +CREATE USER ev_test@localhost; +GRANT ALL ON events_test.* to ev_test@localhost; +GRANT ALL on events_test2.* to ev_test@localhost; +REVOKE EVENT ON events_test2.* FROM ev_test@localhost; +REVOKE PROCESS on *.* from ev_test@localhost; +#now we are on con1 +connect (ev_con1,localhost,ev_test,,events_test2); +select "NEW CONNECTION"; +SELECT USER(), DATABASE(); +SHOW GRANTS; + +select "Here comes an error:"; +#NO EVENT_ACL on events_test2 +--error 1044 +SHOW EVENTS; +USE events_test; + +select "Now the list should be empty:"; +--replace_column 8 # 9 # +SHOW EVENTS; +#now create an event with the same name but we are different user +select concat("Let's create some new events from the name of ",user()); +create event one_event on schedule every 20 second do select 123; +create event two_event on schedule every 20 second on completion not preserve comment "two event" do select 123; +create event three_event on schedule every 20 second on completion preserve comment "three event" do select 123; + +select "Now we should see 3 events:"; +--replace_column 8 # 9 # +SHOW EVENTS; + +select "This should show us only 3 events:"; +--replace_column 8 # 9 # +SHOW FULL EVENTS; + +select "This should show us only 2 events:"; +--replace_column 8 # 9 # +SHOW FULL EVENTS LIKE 't%event'; + +select "This should show us no events:"; +--replace_column 8 # 9 # +SHOW FULL EVENTS FROM test LIKE '%'; +#ok, we are back +connection default; +DROP DATABASE events_test2; + +select "should see 1 event:"; +--replace_column 8 # 9 # +SHOW EVENTS; + +select "we should see 4 events now:"; +--replace_column 8 # 9 # +SHOW FULL EVENTS; +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; + +connection ev_con1; +drop event one_event; +drop event two_event; +drop event three_event; +disconnect ev_con1; +connection default; +drop user ev_test@localhost; +drop event one_event; +# +##INFORMATION_SCHEMA.EVENTS test end +# + set global event_scheduler=0; select count(*) from mysql.event; select get_lock("test_lock1", 20); diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index eeb3e30e19a..d86eb22e04d 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -791,7 +791,7 @@ then c_ev="$c_ev status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED'," c_ev="$c_ev on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP'," c_ev="$c_ev comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default ''," - c_ev="$c_ev PRIMARY KEY (db,name)" + c_ev="$c_ev PRIMARY KEY (definer, db, name)" c_ev="$c_ev ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';" fi diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index d33339e4128..93bceecb38f 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -600,4 +600,6 @@ CREATE TABLE event ( ALTER TABLE user add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL AFTER Create_user_priv; ALTER TABLE db add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL; +ALTER TABLE event DROP PRIMARY KEY; +ALTER TABLE event ADD PRIMARY KEY(definer, db, name); diff --git a/sql/event.cc b/sql/event.cc index 6d62be903bd..5decbefe287 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -77,7 +77,7 @@ evex_queue_init(EVEX_QUEUE_TYPE *queue) } -static + int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) { return cs->coll->strnncollsp(cs, (unsigned char *) s.str,s.length, @@ -176,7 +176,9 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) int evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, TABLE *table) + const LEX_STRING ev_name, + const LEX_STRING user_name, + TABLE *table) { byte key[MAX_KEY_LENGTH]; DBUG_ENTER("evex_db_find_event_aux"); @@ -190,11 +192,17 @@ evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, same fields. */ if (dbname.length > table->field[EVEX_FIELD_DB]->field_length || - ev_name.length > table->field[EVEX_FIELD_NAME]->field_length) + ev_name.length > table->field[EVEX_FIELD_NAME]->field_length || + user_name.length > table->field[EVEX_FIELD_DEFINER]->field_length) + DBUG_RETURN(EVEX_KEY_NOT_FOUND); - table->field[0]->store(dbname.str, dbname.length, &my_charset_bin); - table->field[1]->store(ev_name.str, ev_name.length, &my_charset_bin); + table->field[EVEX_FIELD_DB]->store(dbname.str, dbname.length, &my_charset_bin); + table->field[EVEX_FIELD_NAME]->store(ev_name.str, ev_name.length, + &my_charset_bin); + table->field[EVEX_FIELD_DEFINER]->store(user_name.str, user_name.length, + &my_charset_bin); + key_copy(key, table->record[0], table->key_info, table->key_info->key_length); if (table->file->index_read_idx(table->record[0], 0, key, @@ -283,10 +291,15 @@ evex_fill_row(THD *thd, TABLE *table, event_timed *et, my_bool is_update) from 1. Thus +1 offset is needed! */ table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1); + + table->field[EVEX_FIELD_EXECUTE_AT]->set_null(); } else if (et->execute_at.year) { // fix_fields already called in init_execute_at + table->field[EVEX_FIELD_INTERVAL_EXPR]->set_null(); + table->field[EVEX_FIELD_TRANSIENT_INTERVAL]->set_null(); + table->field[EVEX_FIELD_EXECUTE_AT]->set_notnull(); table->field[EVEX_FIELD_EXECUTE_AT]->store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME); @@ -351,9 +364,9 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); goto err; } - + DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_event_aux(thd, et->dbname, et->name, table)) + if (!evex_db_find_event_aux(thd, et->dbname, et->name, et->definer, table)) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), @@ -398,10 +411,9 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, goto err; } - strxmov(definer, et->definer_user.str, "@", et->definer_host.str, NullS); - if ((ret=table->field[EVEX_FIELD_DEFINER]-> - store(definer, et->definer_user.length + 1 + et->definer_host.length, - system_charset_info))) + if ((ret=table->field[EVEX_FIELD_DEFINER]->store(et->definer.str, + et->definer.length, + system_charset_info))) { my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); goto err; @@ -464,7 +476,9 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) TABLE *table; int ret= EVEX_OPEN_TABLE_FAILED; DBUG_ENTER("db_update_event"); + DBUG_PRINT("enter", ("dbname: %.*s", et->dbname.length, et->dbname.str)); DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str)); + DBUG_PRINT("enter", ("user: %.*s", et->name.length, et->name.str)); if (new_name) DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length, new_name->m_name.str)); @@ -485,7 +499,8 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } - if (!evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, table)) + if (!evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, + et->definer, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); goto err; @@ -498,7 +513,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) row (copied into record[1] later */ if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et->dbname, et->name, - table)) + et->definer, table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); goto err; @@ -547,6 +562,7 @@ err: db_find_event() thd THD name the name of the event to find + definer who owns the event ett event's data if event is found tbl TABLE object to use when not NULL @@ -556,11 +572,11 @@ err: */ static int -db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) +db_find_event(THD *thd, sp_name *name, LEX_STRING definer, event_timed **ett, + TABLE *tbl) { TABLE *table; int ret; - const char *definer; char *ptr; event_timed *et; DBUG_ENTER("db_find_event"); @@ -575,7 +591,8 @@ db_find_event(THD *thd, sp_name *name, event_timed **ett, TABLE *tbl) goto done; } - if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, table))) + 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); goto done; @@ -616,6 +633,7 @@ done: evex_load_and_compile_event() thd THD spn the name of the event to alter + definer who is the owner use_lock whether to obtain a lock on LOCK_event_arrays or not RETURN VALUE @@ -625,7 +643,8 @@ done: */ static int -evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) +evex_load_and_compile_event(THD * thd, sp_name *spn, LEX_STRING definer, + bool use_lock) { int ret= 0; MEM_ROOT *tmp_mem_root; @@ -640,7 +659,7 @@ evex_load_and_compile_event(THD * thd, sp_name *spn, bool use_lock) 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, &ett, NULL))) + if ((ret= db_find_event(thd, spn, definer, &ett, NULL))) goto done; thd->restore_backup_open_tables_state(&backup); @@ -756,7 +775,7 @@ evex_create_event(THD *thd, event_timed *et, uint create_options, if (evex_is_running && et->status == MYSQL_EVENT_ENABLED) { sp_name spn(et->dbname, et->name); - ret= evex_load_and_compile_event(thd, &spn, true); + ret= evex_load_and_compile_event(thd, &spn, et->definer, true); } VOID(pthread_mutex_unlock(&LOCK_evex_running)); @@ -809,11 +828,11 @@ evex_update_event(THD *thd, event_timed *et, sp_name *new_name, if (et->status == MYSQL_EVENT_ENABLED) { if (new_name) - ret= evex_load_and_compile_event(thd, new_name, false); + ret= evex_load_and_compile_event(thd, new_name, et->definer, false); else { sp_name spn(et->dbname, et->name); - ret= evex_load_and_compile_event(thd, &spn, false); + ret= evex_load_and_compile_event(thd, &spn, et->definer, false); } if (ret == EVEX_COMPILE_ERROR) my_error(ER_EVENT_COMPILE_ERROR, MYF(0)); @@ -851,7 +870,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, goto done; } - if (!(ret= evex_db_find_event_aux(thd, et->dbname, et->name, table))) + if (!(ret= evex_db_find_event_aux(thd, et->dbname,et->name,et->definer,table))) { if ((ret= table->file->ha_delete_row(table->record[0]))) { diff --git a/sql/event.h b/sql/event.h index d302a2f70ab..6fdf2702212 100644 --- a/sql/event.h +++ b/sql/event.h @@ -54,6 +54,25 @@ enum enum_event_status MYSQL_EVENT_DISABLED }; +enum evex_table_field +{ + EVEX_FIELD_DB = 0, + EVEX_FIELD_NAME, + EVEX_FIELD_BODY, + EVEX_FIELD_DEFINER, + EVEX_FIELD_EXECUTE_AT, + EVEX_FIELD_INTERVAL_EXPR, + EVEX_FIELD_TRANSIENT_INTERVAL, + EVEX_FIELD_CREATED, + EVEX_FIELD_MODIFIED, + EVEX_FIELD_LAST_EXECUTED, + EVEX_FIELD_STARTS, + EVEX_FIELD_ENDS, + EVEX_FIELD_STATUS, + EVEX_FIELD_ON_COMPLETION, + EVEX_FIELD_COMMENT, + EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ +} ; class event_timed { @@ -64,9 +83,10 @@ class event_timed bool status_changed; bool last_executed_changed; - TIME last_executed; public: + TIME last_executed; + LEX_STRING dbname; LEX_STRING name; LEX_STRING body; @@ -83,8 +103,8 @@ public: longlong expression; interval_type interval; - longlong created; - longlong modified; + ulonglong created; + ulonglong modified; enum enum_event_on_completion on_completion; enum enum_event_status status; sp_head *sphead; @@ -197,6 +217,10 @@ int evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, uint *rows_affected); +int +evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); + +int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); int init_events(); @@ -210,6 +234,7 @@ int event_timed_compare(event_timed **a, event_timed **b); + /* CREATE TABLE event ( db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', @@ -233,7 +258,7 @@ CREATE TABLE event ( status ENUM('ENABLED','DISABLED') NOT NULL default 'ENABLED', on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', comment varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY (db,name) + PRIMARY KEY (definer,db,name) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; */ diff --git a/sql/event_executor.cc b/sql/event_executor.cc index dd426c32545..d19d53b0974 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -338,7 +338,8 @@ event_executor_main(void *arg) et->mark_last_executed(); et->compute_next_execution_time(); et->update_fields(thd); - DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num)); + ++iter_num; + DBUG_PRINT("info", (" Spawning a thread %d", iter_num)); #ifndef DBUG_FAULTY_THR if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) { @@ -451,7 +452,7 @@ event_executor_worker(void *event_void) thd->mem_root= &worker_mem_root; pthread_detach(pthread_self()); - + if (init_event_thread(thd)) goto err; diff --git a/sql/event_priv.h b/sql/event_priv.h index 05834383ae5..e4fa22e00e2 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -24,26 +24,6 @@ #define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ { VOID(pthread_mutex_unlock(&__mutex)); goto __label; } -enum evex_table_field -{ - EVEX_FIELD_DB = 0, - EVEX_FIELD_NAME, - EVEX_FIELD_BODY, - EVEX_FIELD_DEFINER, - EVEX_FIELD_EXECUTE_AT, - EVEX_FIELD_INTERVAL_EXPR, - EVEX_FIELD_TRANSIENT_INTERVAL, - EVEX_FIELD_CREATED, - EVEX_FIELD_MODIFIED, - EVEX_FIELD_LAST_EXECUTED, - EVEX_FIELD_STARTS, - EVEX_FIELD_ENDS, - EVEX_FIELD_STATUS, - EVEX_FIELD_ON_COMPLETION, - EVEX_FIELD_COMMENT, - EVEX_FIELD_COUNT /* a cool trick to count the number of fields :) */ -} ; - #define EVEX_DB_FIELD_LEN 64 #define EVEX_NAME_FIELD_LEN 64 @@ -52,11 +32,10 @@ my_time_compare(TIME *a, TIME *b); int evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING rname, TABLE *table); + const LEX_STRING rname, + const LEX_STRING definer, + TABLE *table); -int -evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); - int event_timed_compare_q(void *vptr, byte* a, byte *b); diff --git a/sql/event_timed.cc b/sql/event_timed.cc index cc8849364da..0305cbe7541 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -346,6 +346,16 @@ event_timed::init_definer(THD *thd) definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); definer_host.length= strlen(thd->security_ctx->priv_host); + + definer.length= definer_user.length + definer_host.length + 1; + definer.str= alloc_root(thd->mem_root, definer.length + 1); + + memcpy(definer.str, definer_user.str, definer_user.length); + definer.str[definer_user.length]= '@'; + + memcpy(definer.str + definer_user.length + 1, definer_host.str, + definer_host.length); + definer.str[definer.length]= '\0'; DBUG_RETURN(0); } @@ -419,7 +429,6 @@ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) et->definer_host.str= strmake_root(mem_root, ptr + 1, len);//1: because of @ et->definer_host.length= len; - res1= table->field[EVEX_FIELD_STARTS]-> get_date(&et->starts, TIME_NO_ZERO_DATE); @@ -475,8 +484,7 @@ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table) goto error; DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr)); - et->status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED: - MYSQL_EVENT_DISABLED); + et->status= (ptr[0]=='E'? MYSQL_EVENT_ENABLED:MYSQL_EVENT_DISABLED); // ToDo : Andrey . Find a way not to allocate ptr on event_mem_root if ((ptr= get_field(mem_root, @@ -539,7 +547,8 @@ event_timed::compute_next_execution_time() } time((time_t *)&now); my_tz_UTC->gmt_sec_to_TIME(&time_now, now); -/* + +#ifdef ANDREY_0 sql_print_information("[%s.%s]", dbname.str, name.str); sql_print_information("time_now : [%d-%d-%d %d:%d:%d ]", time_now.year, time_now.month, time_now.day, @@ -554,7 +563,8 @@ event_timed::compute_next_execution_time() last_executed.month, last_executed.day, last_executed.hour, last_executed.minute, last_executed.second); -*/ +#endif + //if time_now is after ends don't execute anymore if (ends.year && (tmp= my_time_compare(&ends, &time_now)) == -1) { @@ -734,7 +744,7 @@ event_timed::drop(THD *thd) if (evex_open_event_table(thd, TL_WRITE, &table)) DBUG_RETURN(-1); - if (evex_db_find_event_aux(thd, dbname, name, table)) + if (evex_db_find_event_aux(thd, dbname, name, definer, table)) DBUG_RETURN(-2); if ((ret= table->file->ha_delete_row(table->record[0]))) @@ -770,11 +780,12 @@ event_timed::update_fields(THD *thd) } - if ((ret= evex_db_find_event_aux(thd, dbname, name, table))) + if ((ret= evex_db_find_event_aux(thd, dbname, name, definer, table))) goto done; store_record(table,record[1]); - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; // Don't update create on row update. + // Don't update create on row update. + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; if (last_executed_changed) { diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5e1dfc089ed..419388e90c2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6699,6 +6699,7 @@ SHOW_VAR status_vars[]= { {"Com_show_engine_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_LOGS]), SHOW_LONG_STATUS}, {"Com_show_engine_mutex", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_MUTEX]), SHOW_LONG_STATUS}, {"Com_show_engine_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_STATUS]), SHOW_LONG_STATUS}, + {"Com_show_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EVENTS]), SHOW_LONG_STATUS}, {"Com_show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS}, {"Com_show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS}, {"Com_show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 28ba8fbf94a..1b21d5b6388 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -99,7 +99,7 @@ enum enum_sql_command { SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, SQLCOM_SHOW_PLUGINS, SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, - SQLCOM_SHOW_CREATE_EVENT, + SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, /* This should be the last !!! */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f7d687a6a98..482007a99f1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2168,6 +2168,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, case SCH_TABLES: case SCH_VIEWS: case SCH_TRIGGERS: + case SCH_EVENTS: #ifdef DONT_ALLOW_SHOW_COMMANDS my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ @@ -2456,11 +2457,15 @@ mysql_execute_command(THD *thd) if (all_tables) { if (lex->orig_sql_command != SQLCOM_SHOW_STATUS_PROC && - lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC) + lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC && + lex->orig_sql_command != SQLCOM_SHOW_EVENTS) res= check_table_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, all_tables, 0); + else if (lex->orig_sql_command == SQLCOM_SHOW_EVENTS) + res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, + is_schema_db(thd->lex->select_lex.db)); } else res= check_access(thd, diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 672f7fe8abe..58f94887ef6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -25,6 +25,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "authors.h" +#include "event.h" #include #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -1901,6 +1902,7 @@ void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values) case SQLCOM_SHOW_TABLES: case SQLCOM_SHOW_TABLE_STATUS: case SQLCOM_SHOW_TRIGGERS: + case SQLCOM_SHOW_EVENTS: index_field_values->db_value= lex->select_lex.db; index_field_values->table_value= wild; break; @@ -3770,6 +3772,269 @@ 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) { + case INTERVAL_YEAR: + return INTERVAL_YEAR; + + case INTERVAL_QUARTER: + case INTERVAL_YEAR_MONTH: + case INTERVAL_MONTH: + return INTERVAL_MONTH; + + case INTERVAL_WEEK: + case INTERVAL_DAY: + return INTERVAL_DAY; + + case INTERVAL_DAY_HOUR: + case INTERVAL_HOUR: + return INTERVAL_HOUR; + + case INTERVAL_DAY_MINUTE: + case INTERVAL_HOUR_MINUTE: + case INTERVAL_MINUTE: + return INTERVAL_MINUTE; + + case INTERVAL_DAY_SECOND: + case INTERVAL_HOUR_SECOND: + case INTERVAL_MINUTE_SECOND: + case INTERVAL_SECOND: + return INTERVAL_SECOND; + + case INTERVAL_DAY_MICROSECOND: + case INTERVAL_HOUR_MICROSECOND: + case INTERVAL_MINUTE_MICROSECOND: + case INTERVAL_SECOND_MICROSECOND: + case INTERVAL_MICROSECOND: + return INTERVAL_MICROSECOND; + } + DBUG_ASSERT(0); + return INTERVAL_SECOND; +} + + +static int +fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) +{ + const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; + CHARSET_INFO *scs= system_charset_info; + TIME time; + event_timed et; + DBUG_ENTER("fill_events_copy_to_schema_tab"); + + restore_record(sch_table, s->default_values); + + if (et.load_from_row(thd->mem_root, event_table)) + { + my_error(ER_EVENT_CANNOT_LOAD_FROM_TABLE, MYF(0)); + DBUG_RETURN(1); + } + + if (!(!wild || !wild[0] || !wild_compare(et.name.str, wild, 0))) + DBUG_RETURN(0); + + //->field[0] is EVENT_CATALOG and is by default NULL + + sch_table->field[1]->store(et.dbname.str, et.dbname.length, scs); + sch_table->field[2]->store(et.name.str, et.name.length, scs); + sch_table->field[3]->store(et.definer.str, et.definer.length, scs); + sch_table->field[4]->store(et.body.str, et.body.length, scs); + + // [9] is SQL_MODE and is NULL for now, will be fixed later + sch_table->field[9]->set_null(); + if (et.expression) + { + //type + sch_table->field[5]->store(STRING_WITH_LEN("RECURRING"), scs); + //execute_at + sch_table->field[6]->set_null(); + //interval_value + 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)]; + sch_table->field[8]->set_notnull(); + sch_table->field[8]->store(ival->str, ival->length, scs); + //starts & ends + sch_table->field[10]->set_notnull(); + sch_table->field[10]->store_time(&et.starts, MYSQL_TIMESTAMP_DATETIME); + sch_table->field[11]->set_notnull(); + sch_table->field[11]->store_time(&et.ends, MYSQL_TIMESTAMP_DATETIME); + } + else + { + //type + sch_table->field[5]->store(STRING_WITH_LEN("ONE TIME"), scs); + //execute_at + sch_table->field[6]->set_notnull(); + sch_table->field[6]->store_time(&et.execute_at, MYSQL_TIMESTAMP_DATETIME); + //interval + sch_table->field[7]->set_null(); + //interval_type + sch_table->field[8]->set_null(); + //starts & ends + sch_table->field[10]->set_null(); + sch_table->field[11]->set_null(); + } + + //status + if (et.status == MYSQL_EVENT_ENABLED) + sch_table->field[12]->store(STRING_WITH_LEN("ENABLED"), scs); + else + sch_table->field[12]->store(STRING_WITH_LEN("DISABLED"), scs); + + //on_completion + if (et.on_completion == MYSQL_EVENT_ON_COMPLETION_DROP) + sch_table->field[13]->store(STRING_WITH_LEN("NOT PRESERVE"), scs); + else + sch_table->field[13]->store(STRING_WITH_LEN("PRESERVE"), scs); + + int not_used=0; + number_to_datetime(et.created, &time, 0, ¬_used); + DBUG_ASSERT(not_used==0); + sch_table->field[14]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); + + number_to_datetime(et.modified, &time, 0, ¬_used); + DBUG_ASSERT(not_used==0); + sch_table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); + + if (et.last_executed.year) + sch_table->field[16]->store_time(&et.last_executed,MYSQL_TIMESTAMP_DATETIME); + else + sch_table->field[16]->set_null(); + + sch_table->field[17]->store(et.comment.str, et.comment.length, scs); + + if (schema_table_store_record(thd, sch_table)) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + + +int fill_schema_events(THD *thd, TABLE_LIST *tables, COND *cond) +{ + TABLE *table= tables->table; + CHARSET_INFO *scs= system_charset_info; + TABLE *event_table= NULL; + Open_tables_state backup; + int ret=0; + bool verbose= false; + char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; + bool use_prefix_scanning= true; + uint key_len= 0; + byte *key_buf= NULL; + LINT_INIT(key_buf); + + DBUG_ENTER("fill_schema_events"); + + strxmov(definer, thd->security_ctx->priv_user,"@",thd->security_ctx->priv_host, + NullS); + + DBUG_PRINT("info",("db=%s current_user=%s", thd->lex->select_lex.db, definer)); + + thd->reset_n_backup_open_tables_state(&backup); + + if ((ret= evex_open_event_table(thd, TL_READ, &event_table))) + { + sql_print_error("Table mysql.event is damaged."); + ret= 1; + goto err; + } + + event_table->file->ha_index_init(0, 1); + + /* + see others' events only if you have PROCESS_ACL !! + thd->lex->verbose is set either if SHOW FULL EVENTS or + in case of SELECT FROM I_S.EVENTS + */ + verbose= (thd->lex->verbose + && (thd->security_ctx->master_access & PROCESS_ACL)); + + if (verbose && thd->security_ctx->user) + { + ret= event_table->file->index_first(event_table->record[0]); + use_prefix_scanning= false; + } + else + { + event_table->field[EVEX_FIELD_DEFINER]->store(definer, strlen(definer), scs); + key_len= event_table->key_info->key_part[0].store_length; + + if (thd->lex->select_lex.db) + { + event_table->field[EVEX_FIELD_DB]-> + store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); + key_len+= event_table->key_info->key_part[1].store_length; + } + if (!(key_buf= alloc_root(thd->mem_root, key_len))) + { + ret= 1; + goto err; + } + + key_copy(key_buf, event_table->record[0], event_table->key_info, key_len); + ret= event_table->file->index_read(event_table->record[0], key_buf, key_len, + HA_READ_PREFIX); + } + + if (ret) + { + ret= (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) ? 0 : 1; + goto err; + } + + while (!ret) + { + if ((ret= fill_events_copy_to_schema_table(thd, table, event_table))) + goto err; + + if (use_prefix_scanning) + ret= event_table->file-> + index_next_same(event_table->record[0], key_buf, key_len); + else + ret= event_table->file->index_next(event_table->record[0]); + } + // ret is guaranteed to be != 0 + ret= (ret != HA_ERR_END_OF_FILE); +err: + if (event_table) + { + event_table->file->ha_index_end(); + close_thread_tables(thd); + } + + thd->restore_backup_open_tables_state(&backup); + DBUG_RETURN(ret); +} + + int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) { DBUG_ENTER("fill_open_tables"); @@ -4390,6 +4655,31 @@ ST_FIELD_INFO engines_fields_info[]= }; +ST_FIELD_INFO events_fields_info[]= +{ + {"EVENT_CATALOG", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"EVENT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Db"}, + {"EVENT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"}, + {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"}, + {"EVENT_BODY", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, + {"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"}, + {"EXECUTE_AT", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Execute at"}, + {"INTERVAL_VALUE", 11, MYSQL_TYPE_LONG, 0, 1, "Interval value"}, + {"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field"}, + {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, + {"STARTS", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Starts"}, + {"ENDS", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Ends"}, + {"STATUS", 8, MYSQL_TYPE_STRING, 0, 0, "Status"}, + {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0}, + {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, + {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, + {"LAST_EXECUTED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, + {"EVENT_COMMENT", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} +}; + + + ST_FIELD_INFO coll_charset_app_fields_info[]= { {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, @@ -4655,6 +4945,8 @@ ST_SCHEMA_TABLE schema_tables[]= fill_schema_column_privileges, 0, 0, -1, -1, 0}, {"ENGINES", engines_fields_info, create_schema_table, fill_schema_engines, make_old_format, 0, -1, -1, 0}, + {"EVENTS", events_fields_info, create_schema_table, + fill_schema_events, make_old_format, 0, -1, -1, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0}, {"OPEN_TABLES", open_tables_fields_info, create_schema_table, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 69c33a8e7d0..3acf27e038d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1357,8 +1357,11 @@ create: $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - if (!lex->et_compile_phase) + if (!lex->et_compile_phase) + { lex->et->init_name(YYTHD, $4); + lex->et->init_definer(YYTHD); + } } ON SCHEDULE_SYM ev_schedule_time ev_on_completion @@ -1569,7 +1572,6 @@ ev_sql_stmt: if (!lex->et_compile_phase) { lex->et->init_body(YYTHD); - lex->et->init_definer(YYTHD); } } ; @@ -4786,8 +4788,13 @@ alter: if (!(et= new event_timed()))// implicitly calls event_timed::init() YYABORT; lex->et = et; - et->init_name(YYTHD, $3); + if (!lex->et_compile_phase) + { + et->init_definer(YYTHD); + et->init_name(YYTHD, $3); + } + /* We have to turn of CLIENT_MULTI_QUERIES while parsing a stored procedure, otherwise yylex will chop it into pieces @@ -4795,7 +4802,6 @@ alter: */ $$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; - } ev_on_schedule ev_rename_to @@ -7637,7 +7643,6 @@ drop: if (lex->et) { - // ToDo Andrey : Change the error message /* Recursive events are not possible because recursive SPs are not also possible. lex->sp_head is not stacked. @@ -7648,8 +7653,13 @@ drop: if (!(lex->et= new event_timed())) YYABORT; - lex->et->init_name(YYTHD, $4); + if (!lex->et_compile_phase) + { + lex->et->init_name(YYTHD, $4); + lex->et->init_definer(YYTHD); + } + lex->sql_command = SQLCOM_DROP_EVENT; lex->drop_if_exists= $3; } @@ -8041,6 +8051,15 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS)) YYABORT; } + | opt_full EVENTS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SELECT; + lex->orig_sql_command= SQLCOM_SHOW_EVENTS; + lex->select_lex.db= $3; + if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS)) + YYABORT; + } | TABLE_SYM STATUS_SYM opt_db wild_and_where { LEX *lex= Lex; diff --git a/sql/table.h b/sql/table.h index eb0c0cf98d3..213310a51dc 100644 --- a/sql/table.h +++ b/sql/table.h @@ -338,6 +338,7 @@ enum enum_schema_tables SCH_COLUMNS, SCH_COLUMN_PRIVILEGES, SCH_ENGINES, + SCH_EVENTS, SCH_KEY_COLUMN_USAGE, SCH_OPEN_TABLES, SCH_PARTITIONS,