summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <andrey@lmy004.>2006-07-10 14:42:29 +0200
committerunknown <andrey@lmy004.>2006-07-10 14:42:29 +0200
commit5d91dc2560836ae28351baf446df7bee97e2cd05 (patch)
tree28a786dfaf77132889807ad9f7ff5dbd568f9707
parente8699b56140571305b898c29bdc45a5bab117a45 (diff)
parent974eecc246db18e29e5ef06db6f48a0f4b3f7951 (diff)
downloadmariadb-git-5d91dc2560836ae28351baf446df7bee97e2cd05.tar.gz
Manual merge
BitKeeper/etc/ignore: auto-union mysql-test/r/events_logs_tests.result: Auto merged mysql-test/t/events_logs_tests.test: Auto merged mysql-test/t/events_stress.test: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_db.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Auto merged sql/share/errmsg.txt: Auto merged sql/sql_yacc.yy: Auto merged sql/event_data_objects.cc: manual merge sql/events.cc: manual merge
-rw-r--r--.bzrignore4
-rw-r--r--libmysqld/Makefile.am3
-rw-r--r--mysql-test/r/events.result31
-rw-r--r--mysql-test/r/events_bugs.result69
-rw-r--r--mysql-test/r/events_logs_tests.result9
-rw-r--r--mysql-test/r/events_scheduling.result2
-rw-r--r--mysql-test/r/ps_1general.result2
-rw-r--r--mysql-test/r/skip_name_resolve.result1
-rw-r--r--mysql-test/r/sp-threads.result1
-rw-r--r--mysql-test/r/sp_notembedded.result2
-rw-r--r--mysql-test/r/status.result10
-rw-r--r--mysql-test/t/events.test35
-rw-r--r--mysql-test/t/events_bugs.test75
-rw-r--r--mysql-test/t/events_logs_tests.test14
-rw-r--r--mysql-test/t/events_scheduling.test2
-rw-r--r--mysql-test/t/events_stress.test4
-rw-r--r--sql/CMakeLists.txt3
-rw-r--r--sql/Makefile.am12
-rw-r--r--sql/event_data_objects.cc (renamed from sql/event_timed.cc)1250
-rw-r--r--sql/event_data_objects.h (renamed from sql/event_timed.h)265
-rw-r--r--sql/event_db_repository.cc1040
-rw-r--r--sql/event_db_repository.h107
-rw-r--r--sql/event_queue.cc945
-rw-r--r--sql/event_queue.h119
-rw-r--r--sql/event_scheduler.cc2447
-rw-r--r--sql/event_scheduler.h236
-rw-r--r--sql/event_scheduler_ng.cc875
-rw-r--r--sql/event_scheduler_ng.h123
-rw-r--r--sql/events.cc1113
-rw-r--r--sql/events.h122
-rw-r--r--sql/events_priv.h79
-rw-r--r--sql/mysqld.cc10
-rw-r--r--sql/set_var.cc32
-rw-r--r--sql/share/errmsg.txt7
-rw-r--r--sql/sql_class.cc57
-rw-r--r--sql/sql_class.h8
-rw-r--r--sql/sql_db.cc2
-rw-r--r--sql/sql_lex.cc4
-rw-r--r--sql/sql_lex.h5
-rw-r--r--sql/sql_parse.cc114
-rw-r--r--sql/sql_show.cc185
-rw-r--r--sql/sql_show.h2
-rw-r--r--sql/sql_yacc.yy364
43 files changed, 4816 insertions, 4974 deletions
diff --git a/.bzrignore b/.bzrignore
index 2fdd5b3e68f..3cb00e96541 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -444,6 +444,7 @@ libmysql/mysys_priv.h
libmysql/net.c
libmysql/release/libmysql.exp
libmysql/vio_priv.h
+libmysql/viosocket.o.6WmSJk
libmysql_r/*.c
libmysql_r/acconfig.h
libmysql_r/conf_to_src
@@ -461,7 +462,10 @@ libmysqld/discover.cc
libmysqld/emb_qcache.cpp
libmysqld/errmsg.c
libmysqld/event.cc
+libmysqld/event_data_objects.cc
+libmysqld/event_db_repository.cc
libmysqld/event_executor.cc
+libmysqld/event_queue.cc
libmysqld/event_scheduler.cc
libmysqld/event_timed.cc
libmysqld/events.cc
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index b91944ac258..2e03ba0f0a9 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -68,7 +68,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
- event_scheduler.cc events.cc event_timed.cc \
+ event_scheduler.cc events.cc event_data_objects.cc \
+ event_queue.cc event_db_repository.cc \
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
sql_tablespace.cc \
rpl_injector.cc my_user.c partition_info.cc
diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result
index e115e077535..2128624786d 100644
--- a/mysql-test/r/events.result
+++ b/mysql-test/r/events.result
@@ -85,13 +85,24 @@ SHOW EVENTS;
Db Name Definer Type Execute at Interval value Interval field Starts Ends Status
events_test event_starts_test root@localhost RECURRING NULL 20 SECOND # # ENABLED
DROP EVENT event_starts_test;
+create table test_nested(a int);
create event e_43 on schedule every 1 second do set @a = 5;
-set global event_scheduler = 1;
alter event e_43 do alter event e_43 do set @a = 4;
+ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present
+alter event e_43 do
+begin
+alter event e_43 on schedule every 5 minute;
+insert into test_nested values(1);
+end|
+set global event_scheduler = 1;
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 begin
+alter event e_43 on schedule every 5 minute;
+insert into test_nested values(1);
+end ENABLED MINUTE 5
drop event e_43;
+drop table test_nested;
"Let's check whether we can use non-qualified names"
create table non_qualif(a int);
create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219);
@@ -195,6 +206,10 @@ ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
SHOW EVENTS;
ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND'
drop event root22;
+create event root23 on schedule every -100 year do select 1;
+ERROR HY000: INTERVAL is either not positive or too big
+create event root23 on schedule every 222222222222222222222 year do select 1;
+ERROR HY000: INTERVAL is either not positive or too big
drop event root6;
drop event root7;
drop event root8;
@@ -285,9 +300,9 @@ select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_comp
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
drop event e_26;
-create event e_26 on schedule at NULL disabled do set @a = 5;
+create event e_26 on schedule at NULL disable do set @a = 5;
ERROR HY000: Incorrect AT value: 'NULL'
-create event e_26 on schedule at 'definitely not a datetime' disabled do set @a = 5;
+create event e_26 on schedule at 'definitely not a datetime' disable do set @a = 5;
ERROR HY000: Incorrect AT value: 'definitely not a datetime'
set names utf8;
create event задачка on schedule every 123 minute starts now() ends now() + interval 1 month do select 1;
@@ -311,7 +326,6 @@ root@localhost закачка events_test
"Should be only 1 process"
select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Suspended NULL
select release_lock("test_lock1");
release_lock("test_lock1")
1
@@ -331,7 +345,7 @@ create event закачка on schedule every 10 hour do select get_lock("test_l
"Should have only 2 processes: the scheduler and the locked event"
select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
root localhost events_test Connect User lock select get_lock("test_lock2", 20)
"Release the mutex, the event worker should finish."
"Release the mutex, the event worker should finish."
@@ -347,18 +361,17 @@ create event закачка21 on schedule every 10 hour do select get_lock("test
"Should have only 3 processes: the scheduler, our conn and the locked event"
select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
root localhost events_test Connect User lock select get_lock("test_lock2_1", 20)
set global event_scheduler=2;
"Should have only our process now:"
select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Suspended NULL
root localhost events_test Connect User lock select get_lock("test_lock2_1", 20)
drop event закачка21;
create table t_16 (s1 int);
create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5;
-ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
+ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present
drop table t_16;
create event white_space
on schedule every 10 hour
diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result
index a7c2964a253..e6115161cbb 100644
--- a/mysql-test/r/events_bugs.result
+++ b/mysql-test/r/events_bugs.result
@@ -17,26 +17,26 @@ DROP EVENT ДОЛЕÐ_региÑÑ‚ÑŠÑ€_утф8;
SET NAMES latin1;
set @a=3;
CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5;
-call p_16();
-"Here we used to crash!"
-call p_16();
-ERROR HY000: Event 'e_16' already exists
-call p_16();
-ERROR HY000: Event 'e_16' already exists
-DROP EVENT e_16;
-CALL p_16();
-CALL p_16();
-ERROR HY000: Event 'e_16' already exists
-DROP PROCEDURE p_16;
-DROP EVENT e_16;
+ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present
create event e_55 on schedule at 99990101000000 do drop table t;
ERROR HY000: Incorrect AT value: '99990101000000'
create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t;
ERROR HY000: Incorrect STARTS value: '99990101000000'
create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t;
ERROR HY000: ENDS is either invalid or before STARTS
+create event e_55 on schedule at 10000101000000 do drop table t;
+ERROR HY000: Activation (AT) time is in the past
+create event e_55 on schedule at 20000101000000 do drop table t;
+ERROR HY000: Activation (AT) time is in the past
+create event e_55 on schedule at 20200101000000 starts 10000101000000 do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'starts 10000101000000 do drop table t' at line 1
+create event e_55 on schedule at 20200101000000 ends 10000101000000 do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ends 10000101000000 do drop table t' at line 1
+create event e_55 on schedule at 20200101000000 starts 10000101000000 ends 10000101000000 do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'starts 10000101000000 ends 10000101000000 do drop table t' at line 1
+create event e_55 on schedule every 10 hour starts 10000101000000 do drop table t;
+ERROR HY000: Incorrect STARTS value: '10000101000000'
set global event_scheduler=2;
-"Wait a bit to settle down"
delete from mysql.event;
set global event_scheduler= 1;
set @old_sql_mode:=@@sql_mode;
@@ -52,7 +52,7 @@ end|
"Now if everything is fine the event has compiled and is locked
select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
root localhost events_test Connect User lock select get_lock('test_bug16407', 60)
select release_lock('test_bug16407');
release_lock('test_bug16407')
@@ -68,6 +68,11 @@ select event_schema, event_name, sql_mode from information_schema.events order b
event_schema event_name sql_mode
events_test e_16407 STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER
drop event e_16407;
+set sql_mode="ansi";
+select get_lock('ee_16407_2', 60);
+get_lock('ee_16407_2', 60)
+1
+set global event_scheduler= 1;
"Another sql_mode test"
set sql_mode="traditional";
create table events_smode_test(ev_name char(10), a date) engine=myisam;
@@ -75,6 +80,7 @@ create table events_smode_test(ev_name char(10), a date) engine=myisam;
create event ee_16407_2 on schedule every 60 second do
begin
select get_lock('ee_16407_2', 60) /*ee_16407_2*/;
+select release_lock('ee_16407_2');
insert into events_test.events_smode_test values('ee_16407_2','1980-19-02');
end|
insert into events_smode_test values ('test','1980-19-02')|
@@ -83,6 +89,7 @@ ERROR 22007: Incorrect date value: '1980-19-02' for column 'a' at row 1
create event ee_16407_3 on schedule every 60 second do
begin
select get_lock('ee_16407_2', 60) /*ee_16407_3*/;
+select release_lock('ee_16407_2');
insert into events_test.events_smode_test values ('ee_16407_3','1980-02-19');
insert into events_test.events_smode_test values ('ee_16407_3','1980-02-29');
end|
@@ -91,6 +98,7 @@ set sql_mode=""|
create event ee_16407_4 on schedule every 60 second do
begin
select get_lock('ee_16407_2', 60) /*ee_16407_4*/;
+select release_lock('ee_16407_2');
insert into events_test.events_smode_test values ('ee_16407_4','10-11-1956');
end|
select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name;
@@ -98,14 +106,9 @@ event_schema event_name sql_mode
events_test ee_16407_2 STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER
events_test ee_16407_3 STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER
events_test ee_16407_4
-set sql_mode="ansi";
-select get_lock('ee_16407_2', 60);
-get_lock('ee_16407_2', 60)
-1
-set global event_scheduler= 1;
select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
root localhost events_test Connect User lock select get_lock('ee_16407_2', 60) /*ee_16407_2*/
root localhost events_test Connect User lock select get_lock('ee_16407_2', 60) /*ee_16407_3*/
root localhost events_test Connect User lock select get_lock('ee_16407_2', 60) /*ee_16407_4*/
@@ -114,7 +117,7 @@ release_lock('ee_16407_2')
1
select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
set global event_scheduler= 2;
select * from events_smode_test order by ev_name, a;
ev_name a
@@ -132,28 +135,30 @@ drop event ee_16407_3;
drop event ee_16407_4;
"And now one last test regarding sql_mode and call of SP from an event"
delete from events_smode_test;
+set sql_mode='ansi';
+select get_lock('ee_16407_5', 60);
+get_lock('ee_16407_5', 60)
+1
+set global event_scheduler= 1;
set sql_mode='traditional';
create procedure ee_16407_5_pendant() begin insert into events_test.events_smode_test values('ee_16407_5','2001-02-29'); end|
create procedure ee_16407_6_pendant() begin insert into events_test.events_smode_test values('ee_16407_6','2004-02-29'); end|
create event ee_16407_5 on schedule every 60 second do
begin
select get_lock('ee_16407_5', 60) /*ee_16407_5*/;
+select release_lock('ee_16407_5');
call events_test.ee_16407_5_pendant();
end|
create event ee_16407_6 on schedule every 60 second do
begin
select get_lock('ee_16407_5', 60) /*ee_16407_6*/;
+select release_lock('ee_16407_5');
call events_test.ee_16407_6_pendant();
end|
-set sql_mode='ansi';
-select get_lock('ee_16407_5', 60);
-get_lock('ee_16407_5', 60)
-1
-set global event_scheduler= 1;
"Should have 2 locked processes"
select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
root localhost events_test Connect User lock select get_lock('ee_16407_5', 60) /*ee_16407_5*/
root localhost events_test Connect User lock select get_lock('ee_16407_5', 60) /*ee_16407_6*/
select release_lock('ee_16407_5');
@@ -162,7 +167,7 @@ release_lock('ee_16407_5')
"Should have 0 processes locked"
select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
user host db command state info
-event_scheduler localhost NULL Connect Sleeping NULL
+event_scheduler localhost NULL Daemon Waiting for next activation NULL
select * from events_smode_test order by ev_name, a;
ev_name a
ee_16407_6 2004-02-29
@@ -201,4 +206,12 @@ events_test mysqltest_user1 mysqltest_user1@localhost RECURRING ENABLED
drop event events_test.mysqltest_user1;
drop user mysqltest_user1@localhost;
drop database mysqltest_db1;
+create event e_53 on schedule at (select s1 from ttx) do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) do drop table t' at line 1
+create event e_53 on schedule every (select s1 from ttx) second do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) second do drop table t' at line 1
+create event e_53 on schedule every 5 second starts (select s1 from ttx) do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) do drop table t' at line 1
+create event e_53 on schedule every 5 second ends (select s1 from ttx) do drop table t;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select s1 from ttx) do drop table t' at line 1
drop database events_test;
diff --git a/mysql-test/r/events_logs_tests.result b/mysql-test/r/events_logs_tests.result
index 950090399d5..f56ebe3e472 100644
--- a/mysql-test/r/events_logs_tests.result
+++ b/mysql-test/r/events_logs_tests.result
@@ -9,7 +9,7 @@ SELECT user_host, argument FROM mysql.general_log WHERE argument LIKE '%alabala%
END|
"Check General Query Log"
SET GLOBAL event_scheduler=2;
-create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual;
+create event log_general on schedule every 1 minute do SELect 'alabala', sleep(1) from dual;
TRUNCATE mysql.general_log;
"1 row, the current statement!"
call select_general_log();
@@ -19,7 +19,7 @@ SET GLOBAL event_scheduler=1;
"Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log"
call select_general_log();
user_host argument
-USER_HOST SELect 'alabala', sleep(3) from dual
+USER_HOST SELect 'alabala', sleep(1) from dual
DROP PROCEDURE select_general_log;
DROP EVENT log_general;
SET GLOBAL event_scheduler=2;
@@ -52,10 +52,11 @@ TRUNCATE mysql.slow_log;
CREATE TABLE slow_event_test (slo_val tinyint, val tinyint);
SET SESSION long_query_time=1;
"This won't go to the slow log"
-CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(3);
SELECT * FROM slow_event_test;
slo_val val
+SET SESSION long_query_time=1;
SET GLOBAL event_scheduler=1;
+CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(1.5);
"Sleep some more time than the actual event run will take"
SHOW VARIABLES LIKE 'event_scheduler';
Variable_name Value
@@ -64,7 +65,7 @@ event_scheduler 1
SELECT * FROM slow_event_test;
slo_val val
4 0
-"Check slow log. Should not see anything because 3 is under the threshold of 4 for GLOBAL, though over SESSION which is 2"
+"Check slow log. Should not see anything because 1.5 is under the threshold of 300 for GLOBAL, though over SESSION which is 2"
SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
user_host query_time db sql_text
"This should go to the slow log"
diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result
index eb44751c176..d7b7241db51 100644
--- a/mysql-test/r/events_scheduling.result
+++ b/mysql-test/r/events_scheduling.result
@@ -39,7 +39,7 @@ DROP EVENT start_n_end;
DROP EVENT only_one_time;
ERROR HY000: Unknown event 'only_one_time'
"Should be preserved"
-SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS;
+SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_NAME;
EVENT_NAME STATUS
E19170 ENABLED
two_time DISABLED
diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result
index 1a1d6432411..d0b773dfe34 100644
--- a/mysql-test/r/ps_1general.result
+++ b/mysql-test/r/ps_1general.result
@@ -299,7 +299,7 @@ t9 MyISAM 10 Dynamic 2 216 432 # 2048 0 NULL # # # latin1_swedish_ci NULL
prepare stmt4 from ' show status like ''Threads_running'' ';
execute stmt4;
Variable_name Value
-Threads_running 2
+Threads_running 1
prepare stmt4 from ' show variables like ''sql_mode'' ';
execute stmt4;
Variable_name Value
diff --git a/mysql-test/r/skip_name_resolve.result b/mysql-test/r/skip_name_resolve.result
index 855876825ad..8ef52e75238 100644
--- a/mysql-test/r/skip_name_resolve.result
+++ b/mysql-test/r/skip_name_resolve.result
@@ -10,6 +10,5 @@ user()
#
show processlist;
Id User Host db Command Time State Info
-<id> event_scheduler <host> NULL <command> <time> <state> <info>
<id> root <host> test <command> <time> <state> <info>
<id> root <host> test <command> <time> <state> <info>
diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result
index 3cba437e0a6..c516d7a643f 100644
--- a/mysql-test/r/sp-threads.result
+++ b/mysql-test/r/sp-threads.result
@@ -34,7 +34,6 @@ lock tables t2 write;
call bug9486();
show processlist;
Id User Host db Command Time State Info
-# event_scheduler localhost NULL Connect # Suspended NULL
# root localhost test Sleep # NULL
# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2
# root localhost test Query # NULL show processlist
diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result
index c5d60446e0a..c8cafe5ace1 100644
--- a/mysql-test/r/sp_notembedded.result
+++ b/mysql-test/r/sp_notembedded.result
@@ -18,11 +18,9 @@ show processlist;
end|
call bug4902_2()|
Id User Host db Command Time State Info
-# event_scheduler localhost NULL Connect # Suspended NULL
# root localhost test Query # NULL show processlist
call bug4902_2()|
Id User Host db Command Time State Info
-# event_scheduler localhost NULL Connect # Suspended NULL
# root localhost test Query # NULL show processlist
drop procedure bug4902_2|
drop function if exists bug5278|
diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result
index 83c6a6f5288..45b84219f2a 100644
--- a/mysql-test/r/status.result
+++ b/mysql-test/r/status.result
@@ -52,22 +52,22 @@ drop table t1;
FLUSH STATUS;
SHOW STATUS LIKE 'max_used_connections';
Variable_name Value
-Max_used_connections 2
+Max_used_connections 1
SET @save_thread_cache_size=@@thread_cache_size;
SET GLOBAL thread_cache_size=3;
SHOW STATUS LIKE 'max_used_connections';
Variable_name Value
-Max_used_connections 4
+Max_used_connections 3
FLUSH STATUS;
SHOW STATUS LIKE 'max_used_connections';
Variable_name Value
-Max_used_connections 3
+Max_used_connections 2
SHOW STATUS LIKE 'max_used_connections';
Variable_name Value
-Max_used_connections 4
+Max_used_connections 3
SHOW STATUS LIKE 'max_used_connections';
Variable_name Value
-Max_used_connections 5
+Max_used_connections 4
SET GLOBAL thread_cache_size=@save_thread_cache_size;
show status like 'com_show_status';
Variable_name Value
diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test
index aac13a55dd3..34c00a6387a 100644
--- a/mysql-test/t/events.test
+++ b/mysql-test/t/events.test
@@ -18,7 +18,7 @@ CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table;
connection default;
SHOW DATABASES LIKE 'db_x';
SET GLOBAL event_scheduler=1;
---sleep 1.5
+--sleep 0.8
SHOW DATABASES LIKE 'db_x';
SHOW TABLES FROM db_x;
SET GLOBAL event_scheduler=2;
@@ -81,19 +81,27 @@ SHOW EVENTS;
DROP EVENT event_starts_test;
#
#
+create table test_nested(a int);
create event e_43 on schedule every 1 second do set @a = 5;
-set global event_scheduler = 1;
---sleep 2
+--error 1562
alter event e_43 do alter event e_43 do set @a = 4;
---sleep 2
+delimiter |;
+alter event e_43 do
+begin
+ alter event e_43 on schedule every 5 minute;
+ insert into test_nested values(1);
+end|
+delimiter ;|
+set global event_scheduler = 1;
+--sleep 3
select db, name, body, status, interval_field, interval_value from mysql.event;
drop event e_43;
---sleep 1
+drop table test_nested;
--echo "Let's check whether we can use non-qualified names"
create table non_qualif(a int);
create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219);
---sleep 1
+--sleep 0.5
select * from non_qualif;
drop event non_qualif_ev;
drop table non_qualif;
@@ -156,6 +164,10 @@ show create event root22;
--error ER_NOT_SUPPORTED_YET
SHOW EVENTS;
drop event root22;
+--error ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG
+create event root23 on schedule every -100 year do select 1;
+--error ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG
+create event root23 on schedule every 222222222222222222222 year do select 1;
drop event root6;
drop event root7;
drop event root8;
@@ -246,9 +258,9 @@ 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;
drop event e_26;
--error ER_WRONG_VALUE
-create event e_26 on schedule at NULL disabled do set @a = 5;
+create event e_26 on schedule at NULL disable do set @a = 5;
--error ER_WRONG_VALUE
-create event e_26 on schedule at 'definitely not a datetime' disabled do set @a = 5;
+create event e_26 on schedule at 'definitely not a datetime' disable do set @a = 5;
set names utf8;
create event задачка on schedule every 123 minute starts now() ends now() + interval 1 month do select 1;
@@ -285,7 +297,7 @@ select get_lock("test_lock2", 20);
--echo "Create an event which tries to acquire a mutex. The event locks on the mutex"
create event закачка on schedule every 10 hour do select get_lock("test_lock2", 20);
--echo "Let some time pass to the event starts"
---sleep 1
+--sleep 0.5
--echo "Should have only 2 processes: the scheduler and the locked event"
select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;--echo "Release the mutex, the event worker should finish."
--echo "Release the mutex, the event worker should finish."
@@ -303,10 +315,11 @@ drop event закачка;
set global event_scheduler=1;
select get_lock("test_lock2_1", 20);
create event закачка21 on schedule every 10 hour do select get_lock("test_lock2_1", 20);
---sleep 1
+--sleep 0.5
--echo "Should have only 3 processes: the scheduler, our conn and the locked event"
select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
set global event_scheduler=2;
+--sleep 0.3
--echo "Should have only our process now:"
select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
drop event закачка21;
@@ -315,7 +328,7 @@ drop event закачка21;
# Bug #16410 Events: CREATE EVENT is legal in a CREATE TRIGGER statement
#
create table t_16 (s1 int);
---error 1422
+--error 1562
create trigger t_16_bi before insert on t_16 for each row create event e_16 on schedule every 1 second do set @a=5;
drop table t_16;
#
diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test
index 9434de7be7d..7fd4c684b4b 100644
--- a/mysql-test/t/events_bugs.test
+++ b/mysql-test/t/events_bugs.test
@@ -30,19 +30,8 @@ SET NAMES latin1;
# START - BUG#16408: Events: crash for an event in a procedure
#
set @a=3;
+--error 1562
CREATE PROCEDURE p_16 () CREATE EVENT e_16 ON SCHEDULE EVERY @a SECOND DO SET @a=5;
-call p_16();
---echo "Here we used to crash!"
---error ER_EVENT_ALREADY_EXISTS
-call p_16();
---error ER_EVENT_ALREADY_EXISTS
-call p_16();
-DROP EVENT e_16;
-CALL p_16();
---error ER_EVENT_ALREADY_EXISTS
-CALL p_16();
-DROP PROCEDURE p_16;
-DROP EVENT e_16;
#
# END - BUG#16408: Events: crash for an event in a procedure
#
@@ -56,6 +45,19 @@ create event e_55 on schedule at 99990101000000 do drop table t;
create event e_55 on schedule every 10 hour starts 99990101000000 do drop table t;
--error ER_EVENT_ENDS_BEFORE_STARTS
create event e_55 on schedule every 10 minute ends 99990101000000 do drop table t;
+--error ER_EVENT_EXEC_TIME_IN_THE_PAST
+create event e_55 on schedule at 10000101000000 do drop table t;
+--error ER_EVENT_EXEC_TIME_IN_THE_PAST
+create event e_55 on schedule at 20000101000000 do drop table t;
+--error ER_PARSE_ERROR
+create event e_55 on schedule at 20200101000000 starts 10000101000000 do drop table t;
+--error ER_PARSE_ERROR
+create event e_55 on schedule at 20200101000000 ends 10000101000000 do drop table t;
+--error ER_PARSE_ERROR
+create event e_55 on schedule at 20200101000000 starts 10000101000000 ends 10000101000000 do drop table t;
+--error ER_WRONG_VALUE
+create event e_55 on schedule every 10 hour starts 10000101000000 do drop table t;
+
#
# End - 16396: Events: Distant-future dates become past dates
#
@@ -64,8 +66,6 @@ create event e_55 on schedule every 10 minute ends 99990101000000 do drop table
# Start - 16407: Events: Changes in sql_mode won't be taken into account
#
set global event_scheduler=2;
---echo "Wait a bit to settle down"
---sleep 1
delete from mysql.event;
set global event_scheduler= 1;
set @old_sql_mode:=@@sql_mode;
@@ -78,11 +78,13 @@ begin
drop table "hashed_num";
end|
delimiter ;|
---sleep 1
+--sleep 0.5
--echo "Now if everything is fine the event has compiled and is locked
select /*1*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
select release_lock('test_bug16407');
+
set global event_scheduler= 2;
+
select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name;
--echo "Let's check whether we change the sql_mode on ALTER EVENT"
set sql_mode='traditional';
@@ -90,6 +92,10 @@ alter event e_16407 do select 1;
select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name;
drop event e_16407;
+set sql_mode="ansi";
+select get_lock('ee_16407_2', 60);
+
+set global event_scheduler= 1;
--echo "Another sql_mode test"
set sql_mode="traditional";
create table events_smode_test(ev_name char(10), a date) engine=myisam;
@@ -98,6 +104,7 @@ delimiter |;
create event ee_16407_2 on schedule every 60 second do
begin
select get_lock('ee_16407_2', 60) /*ee_16407_2*/;
+ select release_lock('ee_16407_2');
insert into events_test.events_smode_test values('ee_16407_2','1980-19-02');
end|
--error ER_TRUNCATED_WRONG_VALUE
@@ -106,6 +113,7 @@ insert into events_smode_test values ('test','1980-19-02')|
create event ee_16407_3 on schedule every 60 second do
begin
select get_lock('ee_16407_2', 60) /*ee_16407_3*/;
+ select release_lock('ee_16407_2');
insert into events_test.events_smode_test values ('ee_16407_3','1980-02-19');
insert into events_test.events_smode_test values ('ee_16407_3','1980-02-29');
end|
@@ -114,17 +122,15 @@ set sql_mode=""|
create event ee_16407_4 on schedule every 60 second do
begin
select get_lock('ee_16407_2', 60) /*ee_16407_4*/;
+ select release_lock('ee_16407_2');
insert into events_test.events_smode_test values ('ee_16407_4','10-11-1956');
end|
delimiter ;|
select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name;
-set sql_mode="ansi";
-select get_lock('ee_16407_2', 60);
-set global event_scheduler= 1;
---sleep 1
+--sleep 0.5
select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
select release_lock('ee_16407_2');
---sleep 2
+--sleep 0.8
select /*3*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
set global event_scheduler= 2;
select * from events_smode_test order by ev_name, a;
@@ -137,6 +143,11 @@ drop event ee_16407_4;
--echo "And now one last test regarding sql_mode and call of SP from an event"
delete from events_smode_test;
+set sql_mode='ansi';
+select get_lock('ee_16407_5', 60);
+
+set global event_scheduler= 1;
+
set sql_mode='traditional';
delimiter |;
create procedure ee_16407_5_pendant() begin insert into events_test.events_smode_test values('ee_16407_5','2001-02-29'); end|
@@ -144,22 +155,21 @@ create procedure ee_16407_6_pendant() begin insert into events_test.events_smode
create event ee_16407_5 on schedule every 60 second do
begin
select get_lock('ee_16407_5', 60) /*ee_16407_5*/;
+ select release_lock('ee_16407_5');
call events_test.ee_16407_5_pendant();
end|
create event ee_16407_6 on schedule every 60 second do
begin
select get_lock('ee_16407_5', 60) /*ee_16407_6*/;
+ select release_lock('ee_16407_5');
call events_test.ee_16407_6_pendant();
end|
delimiter ;|
-set sql_mode='ansi';
-select get_lock('ee_16407_5', 60);
-set global event_scheduler= 1;
---sleep 1
+--sleep 0.5
--echo "Should have 2 locked processes"
select /*4*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
select release_lock('ee_16407_5');
---sleep 2
+--sleep 0.8
--echo "Should have 0 processes locked"
select /*5*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;
select * from events_smode_test order by ev_name, a;
@@ -209,4 +219,19 @@ drop database mysqltest_db1;
# END - 18897: Events: unauthorized action possible with alter event rename
#
+#
+# START - BUG#16394: Events: Crash if schedule contains SELECT
+#
+--error ER_PARSE_ERROR
+create event e_53 on schedule at (select s1 from ttx) do drop table t;
+--error ER_PARSE_ERROR
+create event e_53 on schedule every (select s1 from ttx) second do drop table t;
+--error ER_PARSE_ERROR
+create event e_53 on schedule every 5 second starts (select s1 from ttx) do drop table t;
+--error ER_PARSE_ERROR
+create event e_53 on schedule every 5 second ends (select s1 from ttx) do drop table t;
+#
+# END - BUG#16394: Events: Crash if schedule contains SELECT
+#
+
drop database events_test;
diff --git a/mysql-test/t/events_logs_tests.test b/mysql-test/t/events_logs_tests.test
index 5c252b1174b..28818083efa 100644
--- a/mysql-test/t/events_logs_tests.test
+++ b/mysql-test/t/events_logs_tests.test
@@ -14,7 +14,7 @@ END|
delimiter ;|
--echo "Check General Query Log"
SET GLOBAL event_scheduler=2;
-create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual;
+create event log_general on schedule every 1 minute do SELect 'alabala', sleep(1) from dual;
TRUNCATE mysql.general_log;
--echo "1 row, the current statement!"
--replace_column 1 USER_HOST
@@ -22,13 +22,12 @@ call select_general_log();
SET GLOBAL event_scheduler=1;
--echo "Wait the scheduler to start"
--echo "Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log"
---sleep 2
+--sleep 0.7
--replace_column 1 USER_HOST
call select_general_log();
DROP PROCEDURE select_general_log;
DROP EVENT log_general;
SET GLOBAL event_scheduler=2;
---sleep 1
--echo "Check slow query log"
--disable_query_log
@@ -72,15 +71,16 @@ TRUNCATE mysql.slow_log;
CREATE TABLE slow_event_test (slo_val tinyint, val tinyint);
SET SESSION long_query_time=1;
--echo "This won't go to the slow log"
-CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(3);
SELECT * FROM slow_event_test;
+SET SESSION long_query_time=1;
SET GLOBAL event_scheduler=1;
+CREATE EVENT long_event ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(1.5);
--echo "Sleep some more time than the actual event run will take"
---sleep 5
+--sleep 2
SHOW VARIABLES LIKE 'event_scheduler';
--echo "Check our table. Should see 1 row"
SELECT * FROM slow_event_test;
---echo "Check slow log. Should not see anything because 3 is under the threshold of 4 for GLOBAL, though over SESSION which is 2"
+--echo "Check slow log. Should not see anything because 1.5 is under the threshold of 300 for GLOBAL, though over SESSION which is 2"
SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
--echo "This should go to the slow log"
DROP EVENT long_event;
@@ -88,7 +88,7 @@ SET SESSION long_query_time=10;
SET GLOBAL long_query_time=1;
CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2);
--echo "Sleep some more time than the actual event run will take"
---sleep 3
+--sleep 2.5
--echo "Check our table. Should see 2 rows"
SELECT * FROM slow_event_test;
--echo "Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10"
diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test
index 987939bc162..dc4460999a7 100644
--- a/mysql-test/t/events_scheduling.test
+++ b/mysql-test/t/events_scheduling.test
@@ -34,7 +34,7 @@ DROP EVENT start_n_end;
--error ER_EVENT_DOES_NOT_EXIST
DROP EVENT only_one_time;
--echo "Should be preserved"
-SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS;
+SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_NAME;
DROP EVENT two_time;
DROP TABLE table_1;
DROP TABLE table_2;
diff --git a/mysql-test/t/events_stress.test b/mysql-test/t/events_stress.test
index 6546bce3a76..a0101861282 100644
--- a/mysql-test/t/events_stress.test
+++ b/mysql-test/t/events_stress.test
@@ -62,7 +62,7 @@ while ($1)
--enable_query_log
SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2';
SET GLOBAL event_scheduler=1;
---sleep 6
+--sleep 2.5
DROP DATABASE events_conn1_test2;
SET GLOBAL event_scheduler=2;
@@ -101,7 +101,7 @@ while ($1)
}
--enable_query_log
SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2';
---sleep 6
+--sleep 2.5
connection conn2;
--send
DROP DATABASE events_conn2_db;
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 9a97b79813b..58f3fb4409e 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -51,7 +51,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
- rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_timed.cc
+ rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc
+ event_queue.cc event_db_repository.cc
sql_tablespace.cc events.cc ../sql-common/my_user.c
partition_info.cc rpl_injector.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 387f18c2ae9..4d76cdb5080 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -64,9 +64,10 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
tztime.h my_decimal.h\
sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
parse_file.h sql_view.h sql_trigger.h \
- sql_array.h sql_cursor.h events.h events_priv.h \
- sql_plugin.h authors.h sql_partition.h event_timed.h \
- partition_info.h partition_element.h event_scheduler.h \
+ sql_array.h sql_cursor.h events.h \
+ sql_plugin.h authors.h sql_partition.h event_data_objects.h \
+ event_queue.h event_db_repository.h \
+ partition_info.h partition_element.h event_scheduler_ng.h \
contributors.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -103,8 +104,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
tztime.cc my_time.c my_user.c my_decimal.cc\
sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
- sp_cache.cc parse_file.cc sql_trigger.cc \
- event_scheduler.cc events.cc event_timed.cc \
+ sp_cache.cc parse_file.cc sql_trigger.cc event_scheduler.cc\
+ event_scheduler_ng.cc events.cc event_data_objects.cc \
+ event_queue.cc event_db_repository.cc \
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc
diff --git a/sql/event_timed.cc b/sql/event_data_objects.cc
index 98369e0e055..5153553d06b 100644
--- a/sql/event_timed.cc
+++ b/sql/event_data_objects.cc
@@ -16,111 +16,59 @@
#define MYSQL_LEX 1
#include "mysql_priv.h"
-#include "events_priv.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
#include "sp_head.h"
-/*
- Constructor
-
- SYNOPSIS
- Event_timed::Event_timed()
-*/
-
-Event_timed::Event_timed():in_spawned_thread(0),locked_by_thread_id(0),
- running(0), thread_id(0), status_changed(false),
- last_executed_changed(false), expression(0),
- created(0), modified(0),
- on_completion(Event_timed::ON_COMPLETION_DROP),
- status(Event_timed::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);
- pthread_cond_init(&this->COND_finished, NULL);
- init();
-}
-
-
-/*
- Destructor
-
- SYNOPSIS
- Event_timed::~Event_timed()
-*/
-
-Event_timed::~Event_timed()
-{
- deinit_mutexes();
-
- if (free_sphead_on_delete)
- free_sp();
-}
+#define EVEX_MAX_INTERVAL_VALUE 1000000000L
/*
- Destructor
+ Returns a new instance
SYNOPSIS
- Event_timed::~deinit_mutexes()
-*/
-
-void
-Event_timed::deinit_mutexes()
-{
- pthread_mutex_destroy(&this->LOCK_running);
- pthread_cond_destroy(&this->COND_finished);
-}
-
-
-/*
- Checks whether the event is running
+ Event_parse_data::new_instance()
- SYNOPSIS
- Event_timed::is_running()
+ RETURN VALUE
+ Address or NULL in case of error
+
+ NOTE
+ Created on THD's mem_root
*/
-bool
-Event_timed::is_running()
+Event_parse_data *
+Event_parse_data::new_instance(THD *thd)
{
- bool ret;
-
- VOID(pthread_mutex_lock(&this->LOCK_running));
- ret= running;
- VOID(pthread_mutex_unlock(&this->LOCK_running));
-
- return ret;
+ return new (thd->mem_root) Event_parse_data;
}
/*
- Init all member variables
+ Constructor
SYNOPSIS
- Event_timed::init()
+ Event_parse_data::Event_parse_data()
*/
-void
-Event_timed::init()
+Event_parse_data::Event_parse_data()
{
- DBUG_ENTER("Event_timed::init");
+ DBUG_ENTER("Event_parse_data::Event_parse_data");
- dbname.str= name.str= body.str= comment.str= 0;
- dbname.length= name.length= body.length= comment.length= 0;
+ item_execute_at= item_expression= item_starts= item_ends= NULL;
+ status= ENABLED;
+ on_completion= ON_COMPLETION_DROP;
+ expression= 0;
+ /* Actually in the parser STARTS is always set */
set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
- set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
starts_null= ends_null= execute_at_null= TRUE;
- definer_user.str= definer_host.str= 0;
- definer_user.length= definer_host.length= 0;
-
- sql_mode= 0;
+ body.str= comment.str= NULL;
+ body.length= comment.length= 0;
DBUG_VOID_RETURN;
}
@@ -130,15 +78,15 @@ Event_timed::init()
Set a name of the event
SYNOPSIS
- Event_timed::init_name()
+ Event_parse_data::init_name()
thd THD
spn the name extracted in the parser
*/
void
-Event_timed::init_name(THD *thd, sp_name *spn)
+Event_parse_data::init_name(THD *thd, sp_name *spn)
{
- DBUG_ENTER("Event_timed::init_name");
+ DBUG_ENTER("Event_parse_data::init_name");
/* During parsing, we must use thd->mem_root */
MEM_ROOT *root= thd->mem_root;
@@ -162,7 +110,7 @@ Event_timed::init_name(THD *thd, sp_name *spn)
Set body of the event - what should be executed.
SYNOPSIS
- Event_timed::init_body()
+ Event_parse_data::init_body()
thd THD
NOTE
@@ -174,10 +122,10 @@ Event_timed::init_name(THD *thd, sp_name *spn)
*/
void
-Event_timed::init_body(THD *thd)
+Event_parse_data::init_body(THD *thd)
{
- DBUG_ENTER("Event_timed::init_body");
- DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin,
+ DBUG_ENTER("Event_parse_data::init_body");
+ DBUG_PRINT("info", ("body=[%s] body_begin=0x%lx end=0x%lx", body_begin,
body_begin, thd->lex->ptr));
body.length= thd->lex->ptr - body_begin;
@@ -195,7 +143,7 @@ Event_timed::init_body(THD *thd)
continue;
}
- /*
+ /*
consume closing comments
This is arguably wrong, but it's the best we have until the parser is
@@ -228,17 +176,60 @@ Event_timed::init_body(THD *thd)
++body_begin;
--body.length;
}
- body.str= strmake_root(thd->mem_root, (char *)body_begin, body.length);
+ body.str= thd->strmake((char *)body_begin, body.length);
DBUG_VOID_RETURN;
}
/*
+ Inits definer (definer_user and definer_host) during parsing.
+
+ SYNOPSIS
+ Event_parse_data::init_definer()
+
+ RETURN VALUE
+ 0 OK
+*/
+
+int
+Event_parse_data::init_definer(THD *thd)
+{
+ int definer_user_len;
+ int definer_host_len;
+ DBUG_ENTER("Event_parse_data::init_definer");
+
+ DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx "
+ "thd->sec_ctx->priv_user=0x%lx", thd->mem_root,
+ thd->security_ctx->priv_user));
+
+ definer_user_len= strlen(thd->security_ctx->priv_user);
+ definer_host_len= strlen(thd->security_ctx->priv_host);
+
+ /* + 1 for @ */
+ DBUG_PRINT("info",("init definer as whole"));
+ definer.length= definer_user_len + definer_host_len + 1;
+ definer.str= thd->alloc(definer.length + 1);
+
+ DBUG_PRINT("info",("copy the user"));
+ memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len);
+ definer.str[definer_user_len]= '@';
+
+ DBUG_PRINT("info",("copy the host"));
+ memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host,
+ definer_host_len);
+ definer.str[definer.length]= '\0';
+ DBUG_PRINT("info",("definer [%s] initted", definer.str));
+
+ DBUG_RETURN(0);
+}
+
+
+/*
Set time for execution for one time events.
SYNOPSIS
- Event_timed::init_execute_at()
+ Event_parse_data::init_execute_at()
expr when (datetime)
RETURN VALUE
@@ -249,33 +240,39 @@ Event_timed::init_body(THD *thd)
*/
int
-Event_timed::init_execute_at(THD *thd, Item *expr)
+Event_parse_data::init_execute_at(THD *thd)
{
my_bool not_used;
TIME ltime;
my_time_t t;
-
TIME time_tmp;
- DBUG_ENTER("Event_timed::init_execute_at");
- if (expr->fix_fields(thd, &expr))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ DBUG_ENTER("Event_parse_data::init_execute_at");
+
+ if (!item_execute_at)
+ DBUG_RETURN(0);
+
+ if (item_execute_at->fix_fields(thd, &item_execute_at))
+ goto wrong_value;
/* no starts and/or ends in case of execute_at */
DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
(starts_null && ends_null)));
DBUG_ASSERT(starts_null && ends_null);
-
+
/* let's check whether time is in the past */
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
(my_time_t) thd->query_start());
- if ((not_used= expr->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(ER_WRONG_VALUE);
+ if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto wrong_value;
if (TIME_to_ulonglong_datetime(&ltime) <
TIME_to_ulonglong_datetime(&time_tmp))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ {
+ my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
+ DBUG_RETURN(ER_WRONG_VALUE);
+ }
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
@@ -287,12 +284,16 @@ Event_timed::init_execute_at(THD *thd, Item *expr)
if (!t)
{
DBUG_PRINT("error", ("Execute AT after year 2037"));
- DBUG_RETURN(ER_WRONG_VALUE);
+ goto wrong_value;
}
execute_at_null= FALSE;
execute_at= ltime;
DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("AT", item_execute_at);
+ DBUG_RETURN(ER_WRONG_VALUE);
}
@@ -300,35 +301,49 @@ Event_timed::init_execute_at(THD *thd, Item *expr)
Set time for execution for transient events.
SYNOPSIS
- Event_timed::init_interval()
+ Event_parse_data::init_interval()
expr how much?
new_interval what is the interval
RETURN VALUE
- 0 OK
- EVEX_PARSE_ERROR fix_fields failed
- EVEX_BAD_PARAMS Interval is not positive
- EVEX_MICROSECOND_UNSUP Microseconds are not supported.
+ 0 OK
+ EVEX_PARSE_ERROR fix_fields failed (reported)
+ EVEX_BAD_PARAMS Interval is not positive (reported)
+ EVEX_MICROSECOND_UNSUP Microseconds are not supported (reported)
*/
int
-Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
+Event_parse_data::init_interval(THD *thd)
{
String value;
INTERVAL interval_tmp;
- DBUG_ENTER("Event_timed::init_interval");
+ DBUG_ENTER("Event_parse_data::init_interval");
+ if (!item_expression)
+ DBUG_RETURN(0);
+
+ switch (interval) {
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+ default:
+ break;
+ }
- if (expr->fix_fields(thd, &expr))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (item_expression->fix_fields(thd, &item_expression))
+ goto wrong_value;
value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
- if (get_interval_value(expr, new_interval, &value, &interval_tmp))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (get_interval_value(item_expression, interval, &value, &interval_tmp))
+ goto wrong_value;
expression= 0;
- switch (new_interval) {
+ switch (interval) {
case INTERVAL_YEAR:
expression= interval_tmp.year;
break;
@@ -366,44 +381,38 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
interval_tmp.minute)*60
+ interval_tmp.second;
break;
- case INTERVAL_MINUTE_MICROSECOND: /* day and hour are 0 */
- case INTERVAL_HOUR_MICROSECOND: /* day is anyway 0 */
- case INTERVAL_DAY_MICROSECOND:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
- expression= ((((interval_tmp.day*24) + interval_tmp.hour)*60+
- interval_tmp.minute)*60 +
- interval_tmp.second) * 1000000L + interval_tmp.second_part;
- break;
case INTERVAL_HOUR_MINUTE:
expression= interval_tmp.hour * 60 + interval_tmp.minute;
break;
case INTERVAL_MINUTE_SECOND:
expression= interval_tmp.minute * 60 + interval_tmp.second;
break;
- case INTERVAL_SECOND_MICROSECOND:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
- expression= interval_tmp.second * 1000000L + interval_tmp.second_part;
- break;
- case INTERVAL_MICROSECOND:
- DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
case INTERVAL_LAST:
DBUG_ASSERT(0);
+ default:
+ ;/* these are the microsec stuff */
}
if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE)
+ {
+ my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
+ }
- interval= new_interval;
DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("INTERVAL", item_execute_at);
+ DBUG_RETURN(ER_WRONG_VALUE);
}
/*
- Set activation time.
+ Sets activation time.
SYNOPSIS
- Event_timed::init_starts()
- expr how much?
- interval what is the interval
+ Event_parse_data::init_starts()
+ expr how much?
+ interval what is the interval
NOTES
Note that activation time is not execution time.
@@ -420,19 +429,21 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
*/
int
-Event_timed::init_starts(THD *thd, Item *new_starts)
+Event_parse_data::init_starts(THD *thd)
{
my_bool not_used;
TIME ltime, time_tmp;
my_time_t t;
- DBUG_ENTER("Event_timed::init_starts");
+ DBUG_ENTER("Event_parse_data::init_starts");
+ if (!item_starts)
+ DBUG_RETURN(0);
- if (new_starts->fix_fields(thd, &new_starts))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (item_starts->fix_fields(thd, &item_starts))
+ goto wrong_value;
- if ((not_used= new_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto wrong_value;
/* Let's check whether time is in the past */
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
@@ -442,7 +453,7 @@ Event_timed::init_starts(THD *thd, Item *new_starts)
DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(&ltime)));
if (TIME_to_ulonglong_datetime(&ltime) <
TIME_to_ulonglong_datetime(&time_tmp))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ goto wrong_value;
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
@@ -452,24 +463,25 @@ Event_timed::init_starts(THD *thd, Item *new_starts)
*/
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
if (!t)
- {
- DBUG_PRINT("error", ("STARTS after year 2037"));
- DBUG_RETURN(EVEX_BAD_PARAMS);
- }
+ goto wrong_value;
starts= ltime;
starts_null= FALSE;
DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("STARTS", item_starts);
+ DBUG_RETURN(ER_WRONG_VALUE);
}
/*
- Set deactivation time.
+ Sets deactivation time.
SYNOPSIS
- Event_timed::init_ends()
+ Event_parse_data::init_ends()
thd THD
- new_ends when?
+ new_ends When?
NOTES
Note that activation time is not execution time.
@@ -481,26 +493,26 @@ Event_timed::init_starts(THD *thd, Item *new_starts)
RETURN VALUE
0 OK
- EVEX_PARSE_ERROR fix_fields failed
- ER_WRONG_VALUE starts distant date (after year 2037)
- EVEX_BAD_PARAMS ENDS before STARTS
+ EVEX_BAD_PARAMS Error (reported)
*/
int
-Event_timed::init_ends(THD *thd, Item *new_ends)
+Event_parse_data::init_ends(THD *thd)
{
TIME ltime, ltime_now;
my_bool not_used;
my_time_t t;
- DBUG_ENTER("Event_timed::init_ends");
+ DBUG_ENTER("Event_parse_data::init_ends");
+ if (!item_ends)
+ DBUG_RETURN(0);
- if (new_ends->fix_fields(thd, &new_ends))
- DBUG_RETURN(EVEX_PARSE_ERROR);
+ if (item_ends->fix_fields(thd, &item_ends))
+ goto error_bad_params;
DBUG_PRINT("info", ("convert to TIME"));
- if ((not_used= new_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto error_bad_params;
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
@@ -511,15 +523,12 @@ Event_timed::init_ends(THD *thd, Item *new_ends)
DBUG_PRINT("info", ("get the UTC time"));
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
if (!t)
- {
- DBUG_PRINT("error", ("ENDS after year 2037"));
- DBUG_RETURN(EVEX_BAD_PARAMS);
- }
+ goto error_bad_params;
/* Check whether ends is after starts */
DBUG_PRINT("info", ("ENDS after STARTS?"));
if (!starts_null && my_time_compare(&starts, &ltime) != -1)
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ goto error_bad_params;
/*
The parser forces starts to be provided but one day STARTS could be
@@ -529,84 +538,259 @@ Event_timed::init_ends(THD *thd, Item *new_ends)
DBUG_PRINT("info", ("ENDS after NOW?"));
my_tz_UTC->gmt_sec_to_TIME(&ltime_now, thd->query_start());
if (my_time_compare(&ltime_now, &ltime) == 1)
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ goto error_bad_params;
ends= ltime;
ends_null= FALSE;
DBUG_RETURN(0);
+
+error_bad_params:
+ my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
+ DBUG_RETURN(EVEX_BAD_PARAMS);
}
/*
- Sets comment.
+ Prints an error message about invalid value. Internally used
+ during input data verification
SYNOPSIS
- Event_timed::init_comment()
- thd THD - used for memory allocation
- comment the string.
+ Event_parse_data::report_bad_value()
+ item_name The name of the parameter
+ bad_item The parameter
*/
void
-Event_timed::init_comment(THD *thd, LEX_STRING *set_comment)
+Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
+{
+ char buff[120];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
+ my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
+}
+
+
+/*
+ Performs checking of the data gathered during the parsing phase.
+
+ SYNOPSIS
+ Event_parse_data::check_parse_data()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (reported)
+*/
+
+bool
+Event_parse_data::check_parse_data(THD *thd)
+{
+ bool ret;
+ DBUG_ENTER("Event_parse_data::check_parse_data");
+ DBUG_PRINT("info", ("execute_at=0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
+ item_execute_at, item_expression, item_starts, item_ends));
+
+ init_name(thd, identifier);
+
+ init_definer(thd);
+
+ ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
+ init_ends(thd);
+ DBUG_RETURN(ret);
+}
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_basic::Event_basic()
+*/
+
+Event_basic::Event_basic()
{
- DBUG_ENTER("Event_timed::init_comment");
+ DBUG_ENTER("Event_basic::Event_basic");
+ /* init memory root */
+ init_alloc_root(&mem_root, 256, 512);
+ dbname.str= name.str= NULL;
+ dbname.length= name.length= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
- comment.str= strmake_root(thd->mem_root, set_comment->str,
- comment.length= set_comment->length);
+ SYNOPSIS
+ Event_basic::Event_basic()
+*/
+Event_basic::~Event_basic()
+{
+ DBUG_ENTER("Event_basic::~Event_basic");
+ free_root(&mem_root, MYF(0));
DBUG_VOID_RETURN;
}
/*
- Inits definer (definer_user and definer_host) during parsing.
+ Short function to load a char column into a LEX_STRING
SYNOPSIS
- Event_timed::init_definer()
+ Event_basic::load_string_field()
+ field_name The field( enum_events_table_field is not actually used
+ because it's unknown in event_data_objects.h)
+ fields The Field array
+ field_value The value
+*/
+
+bool
+Event_basic::load_string_fields(Field **fields, ...)
+{
+ bool ret= FALSE;
+ va_list args;
+ enum enum_events_table_field field_name;
+ LEX_STRING *field_value;
+
+ DBUG_ENTER("Event_basic::load_string_fields");
- RETURN VALUE
- 0 OK
+ va_start(args, fields);
+ field_name= (enum enum_events_table_field) va_arg(args, int);
+ while (field_name != ET_FIELD_COUNT)
+ {
+ field_value= va_arg(args, LEX_STRING *);
+ if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
+ {
+ ret= TRUE;
+ break;
+ }
+ field_value->length= strlen(field_value->str);
+
+ field_name= (enum enum_events_table_field) va_arg(args, int);
+ }
+ va_end(args);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_queue_element::Event_queue_element()
*/
-int
-Event_timed::init_definer(THD *thd)
+Event_queue_element::Event_queue_element():
+ status_changed(FALSE), last_executed_changed(FALSE),
+ on_completion(ON_COMPLETION_DROP), status(ENABLED),
+ expression(0), dropped(FALSE), flags(0)
{
- DBUG_ENTER("Event_timed::init_definer");
+ DBUG_ENTER("Event_queue_element::Event_queue_element");
- DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx "
- "thd->sec_ctx->priv_user=0x%lx", thd->mem_root,
- thd->security_ctx->priv_user));
- definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
- definer_user.length= strlen(thd->security_ctx->priv_user);
+ set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
+ set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
+ set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
+ set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
+ starts_null= ends_null= execute_at_null= TRUE;
- DBUG_PRINT("info",("init definer_host thd->s_c->priv_host=0x%lx",
- thd->security_ctx->priv_host));
- definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
- definer_host.length= strlen(thd->security_ctx->priv_host);
+ DBUG_VOID_RETURN;
+}
- DBUG_PRINT("info",("init definer as whole"));
- definer.length= definer_user.length + definer_host.length + 1;
- definer.str= alloc_root(thd->mem_root, definer.length + 1);
- DBUG_PRINT("info",("copy the user"));
- memcpy(definer.str, definer_user.str, definer_user.length);
- definer.str[definer_user.length]= '@';
+/*
+ Destructor
- DBUG_PRINT("info",("copy the host"));
- memcpy(definer.str + definer_user.length + 1, definer_host.str,
- definer_host.length);
- definer.str[definer.length]= '\0';
- DBUG_PRINT("info",("definer initted"));
+ SYNOPSIS
+ Event_queue_element::Event_queue_element()
+*/
+Event_queue_element::~Event_queue_element()
+{
+}
- DBUG_RETURN(0);
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_timed::Event_timed()
+*/
+
+Event_timed::Event_timed():
+ created(0), modified(0), sql_mode(0)
+{
+ DBUG_ENTER("Event_timed::Event_timed");
+ init();
+ DBUG_VOID_RETURN;
}
/*
- Loads an event from a row from mysql.event
+ Destructor
SYNOPSIS
- Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+ Event_timed::~Event_timed()
+*/
+
+Event_timed::~Event_timed()
+{
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_job_data::Event_job_data()
+*/
+
+Event_job_data::Event_job_data():
+ thd(NULL), sphead(0), sql_mode(0)
+{
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_timed::~Event_timed()
+*/
+
+Event_job_data::~Event_job_data()
+{
+ free_sp();
+}
+
+
+/*
+ Init all member variables
+
+ SYNOPSIS
+ Event_timed::init()
+*/
+
+void
+Event_timed::init()
+{
+ DBUG_ENTER("Event_timed::init");
+
+ body.str= comment.str= NULL;
+ body.length= comment.length= 0;
+
+ definer_user.str= definer_host.str= 0;
+ definer_user.length= definer_host.length= 0;
+
+ sql_mode= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Loads an event's body from a row from mysql.event
+
+ SYNOPSIS
+ Event_job_data::load_from_row(MEM_ROOT *mem_root, TABLE *table)
RETURN VALUE
0 OK
@@ -619,124 +803,222 @@ Event_timed::init_definer(THD *thd)
*/
int
-Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+Event_job_data::load_from_row(TABLE *table)
{
char *ptr;
- Event_timed *et;
uint len;
- bool res1, res2;
-
- DBUG_ENTER("Event_timed::load_from_row");
+ DBUG_ENTER("Event_job_data::load_from_row");
if (!table)
goto error;
- et= this;
+ if (table->s->fields != ET_FIELD_COUNT)
+ goto error;
+
+ load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
+ ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
+ ET_FIELD_COUNT);
- if (table->s->fields != Events::FIELD_COUNT)
+/*
+ if ((dbname.str= get_field(&mem_root, table->field[ET_FIELD_DB])) == NullS)
goto error;
+ dbname.length= strlen(dbname.str);
- if ((et->dbname.str= get_field(mem_root,
- table->field[Events::FIELD_DB])) == NULL)
+ if ((name.str= get_field(&mem_root, table->field[ET_FIELD_NAME])) == NullS)
goto error;
+ name.length= strlen(name.str);
- et->dbname.length= strlen(et->dbname.str);
+ if ((body.str= get_field(&mem_root, table->field[ET_FIELD_BODY])) == NullS)
+ goto error;
+ body.length= strlen(body.str);
- if ((et->name.str= get_field(mem_root,
- table->field[Events::FIELD_NAME])) == NULL)
+ if ((definer.str= get_field(&mem_root,
+ table->field[ET_FIELD_DEFINER])) == NullS)
goto error;
- et->name.length= strlen(et->name.str);
+ definer.length= strlen(definer.str);
+*/
+ ptr= strchr(definer.str, '@');
+
+ if (! ptr)
+ ptr= definer.str;
+
+ len= ptr - definer.str;
+ definer_user.str= strmake_root(&mem_root, definer.str, len);
+ definer_user.length= len;
+ len= definer.length - len - 1;
+ /* 1:because of @ */
+ definer_host.str= strmake_root(&mem_root, ptr + 1, len);
+ definer_host.length= len;
- if ((et->body.str= get_field(mem_root,
- table->field[Events::FIELD_BODY])) == NULL)
- goto error;
+ sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
+
+ DBUG_RETURN(0);
+error:
+ DBUG_RETURN(EVEX_GET_FIELD_FAILED);
+}
+
+
+/*
+ Loads an event from a row from mysql.event
+
+ SYNOPSIS
+ Event_queue_element::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GET_FIELD_FAILED Error
+
+ NOTES
+ This method is silent on errors and should behave like that. Callers
+ should handle throwing of error messages. The reason is that the class
+ should not know about how to deal with communication.
+*/
+
+int
+Event_queue_element::load_from_row(TABLE *table)
+{
+ char *ptr;
+ bool res1, res2;
- et->body.length= strlen(et->body.str);
+ DBUG_ENTER("Event_queue_element::load_from_row");
- if ((et->definer.str= get_field(mem_root,
- table->field[Events::FIELD_DEFINER])) == NullS)
+ if (!table)
goto error;
- et->definer.length= strlen(et->definer.str);
- ptr= strchr(et->definer.str, '@');
+ if (table->s->fields != ET_FIELD_COUNT)
+ goto error;
- if (! ptr)
- ptr= et->definer.str;
+ load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
+ ET_FIELD_DEFINER, &definer, ET_FIELD_COUNT);
+/*
+ if ((dbname.str= get_field(&mem_root, table->field[ET_FIELD_DB])) == NullS)
+ goto error;
+ dbname.length= strlen(dbname.str);
- len= ptr - et->definer.str;
+ if ((name.str= get_field(&mem_root, table->field[ET_FIELD_NAME])) == NullS)
+ goto error;
+ name.length= strlen(name.str);
- et->definer_user.str= strmake_root(mem_root, et->definer.str, len);
- et->definer_user.length= len;
- len= et->definer.length - len - 1; //1 is because of @
- et->definer_host.str= strmake_root(mem_root, ptr + 1, len);/* 1:because of @*/
- et->definer_host.length= len;
-
- et->starts_null= table->field[Events::FIELD_STARTS]->is_null();
- res1= table->field[Events::FIELD_STARTS]->
- get_date(&et->starts,TIME_NO_ZERO_DATE);
+ if ((definer.str= get_field(&mem_root,
+ table->field[ET_FIELD_DEFINER])) == NullS)
+ goto error;
+ definer.length= strlen(definer.str);
+*/
+ starts_null= table->field[ET_FIELD_STARTS]->is_null();
+ res1= table->field[ET_FIELD_STARTS]->get_date(&starts, TIME_NO_ZERO_DATE);
- et->ends_null= table->field[Events::FIELD_ENDS]->is_null();
- res2= table->field[Events::FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
+ ends_null= table->field[ET_FIELD_ENDS]->is_null();
+ res2= table->field[ET_FIELD_ENDS]->get_date(&ends, TIME_NO_ZERO_DATE);
- if (!table->field[Events::FIELD_INTERVAL_EXPR]->is_null())
- et->expression= table->field[Events::FIELD_INTERVAL_EXPR]->val_int();
+ if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
+ expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
else
- et->expression= 0;
+ expression= 0;
/*
- If res1 and res2 are true then both fields are empty.
- Hence if Events::FIELD_EXECUTE_AT is empty there is an error.
+ If res1 and res2 are TRUE then both fields are empty.
+ Hence if ET_FIELD_EXECUTE_AT is empty there is an error.
*/
- et->execute_at_null=
- table->field[Events::FIELD_EXECUTE_AT]->is_null();
- DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression &&
- et->execute_at_null));
- if (!et->expression &&
- table->field[Events::FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
- TIME_NO_ZERO_DATE))
+ execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
+ DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
+ if (!expression &&
+ table->field[ET_FIELD_EXECUTE_AT]->get_date(&execute_at,
+ TIME_NO_ZERO_DATE))
goto error;
/*
In DB the values start from 1 but enum interval_type starts
from 0
*/
- if (!table->field[Events::FIELD_TRANSIENT_INTERVAL]->is_null())
- et->interval= (interval_type) ((ulonglong)
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
+ if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
+ interval= (interval_type) ((ulonglong)
+ table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
else
- et->interval= (interval_type) 0;
+ interval= (interval_type) 0;
+
+ table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed,
+ TIME_NO_ZERO_DATE);
+ last_executed_changed= FALSE;
- et->created= table->field[Events::FIELD_CREATED]->val_int();
- et->modified= table->field[Events::FIELD_MODIFIED]->val_int();
- table->field[Events::FIELD_LAST_EXECUTED]->
- get_date(&et->last_executed, TIME_NO_ZERO_DATE);
+ if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
+ goto error;
- last_executed_changed= false;
+ DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
+ status= (ptr[0]=='E'? Event_queue_element::ENABLED:
+ Event_queue_element::DISABLED);
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
- if ((ptr= get_field(mem_root, table->field[Events::FIELD_STATUS])) == NullS)
+ if ((ptr= get_field(&mem_root,
+ table->field[ET_FIELD_ON_COMPLETION])) == NullS)
goto error;
- DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
- et->status= (ptr[0]=='E'? Event_timed::ENABLED:Event_timed::DISABLED);
+ on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
+ Event_queue_element::ON_COMPLETION_PRESERVE);
- /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
- if ((ptr= get_field(mem_root,
- table->field[Events::FIELD_ON_COMPLETION])) == NullS)
+ DBUG_RETURN(0);
+error:
+ DBUG_RETURN(EVEX_GET_FIELD_FAILED);
+}
+
+
+/*
+ Loads an event from a row from mysql.event
+
+ SYNOPSIS
+ Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GET_FIELD_FAILED Error
+
+ NOTES
+ This method is silent on errors and should behave like that. Callers
+ should handle throwing of error messages. The reason is that the class
+ should not know about how to deal with communication.
+*/
+
+int
+Event_timed::load_from_row(TABLE *table)
+{
+ char *ptr;
+ uint len;
+
+ DBUG_ENTER("Event_timed::load_from_row");
+
+ if (Event_queue_element::load_from_row(table))
+ goto error;
+
+ load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT);
+/*
+ if ((body.str= get_field(&mem_root, table->field[ET_FIELD_BODY])) == NullS)
goto error;
- et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP:
- Event_timed::ON_COMPLETION_PRESERVE);
+ body.length= strlen(body.str);
+*/
+ ptr= strchr(definer.str, '@');
- et->comment.str= get_field(mem_root, table->field[Events::FIELD_COMMENT]);
- if (et->comment.str != NullS)
- et->comment.length= strlen(et->comment.str);
+ if (! ptr)
+ ptr= definer.str;
+
+ len= ptr - definer.str;
+ definer_user.str= strmake_root(&mem_root, definer.str, len);
+ definer_user.length= len;
+ len= definer.length - len - 1;
+ /* 1:because of @ */
+ definer_host.str= strmake_root(&mem_root, ptr + 1, len);
+ definer_host.length= len;
+
+ created= table->field[ET_FIELD_CREATED]->val_int();
+ modified= table->field[ET_FIELD_MODIFIED]->val_int();
+
+ comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
+ if (comment.str != NullS)
+ comment.length= strlen(comment.str);
else
- et->comment.length= 0;
-
+ comment.length= 0;
- et->sql_mode= (ulong) table->field[Events::FIELD_SQL_MODE]->val_int();
+ sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0);
error:
@@ -755,7 +1037,7 @@ error:
time_now current time
i_value quantity of time type interval to add
i_type type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
-
+
RETURN VALUE
0 OK
1 Error
@@ -890,7 +1172,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
tmp= *start;
if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval)))
goto done;
-
+
/* If `tmp` is still before time_now just add one more time the interval */
if (my_time_compare(&tmp, time_now) == -1)
{
@@ -914,7 +1196,7 @@ done:
Computes next execution time.
SYNOPSIS
- Event_timed::compute_next_execution_time()
+ Event_queue_element::compute_next_execution_time()
RETURN VALUE
FALSE OK
@@ -926,18 +1208,18 @@ done:
*/
bool
-Event_timed::compute_next_execution_time()
+Event_queue_element::compute_next_execution_time()
{
TIME time_now;
int tmp;
- DBUG_ENTER("Event_timed::compute_next_execution_time");
- DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu",
+ DBUG_ENTER("Event_queue_element::compute_next_execution_time");
+ DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx",
TIME_to_ulonglong_datetime(&starts),
TIME_to_ulonglong_datetime(&ends),
- TIME_to_ulonglong_datetime(&last_executed)));
+ TIME_to_ulonglong_datetime(&last_executed), this));
- if (status == Event_timed::DISABLED)
+ if (status == Event_queue_element::DISABLED)
{
DBUG_PRINT("compute_next_execution_time",
("Event %s is DISABLED", name.str));
@@ -951,11 +1233,11 @@ Event_timed::compute_next_execution_time()
{
DBUG_PRINT("info",("One-time event %s.%s of was already executed",
dbname.str, name.str, definer.str));
- dropped= (on_completion == Event_timed::ON_COMPLETION_DROP);
+ dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP);
DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));
- status= Event_timed::DISABLED;
- status_changed= true;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
}
goto ret;
}
@@ -971,11 +1253,11 @@ Event_timed::compute_next_execution_time()
/* time_now is after ends. don't execute anymore */
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
- if (on_completion == Event_timed::ON_COMPLETION_DROP)
- dropped= true;
+ if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
+ dropped= TRUE;
DBUG_PRINT("info", ("Dropped=%d", dropped));
- status= Event_timed::DISABLED;
- status_changed= true;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
goto ret;
}
@@ -1037,10 +1319,10 @@ Event_timed::compute_next_execution_time()
/* Next execution after ends. No more executions */
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
- if (on_completion == Event_timed::ON_COMPLETION_DROP)
- dropped= true;
- status= Event_timed::DISABLED;
- status_changed= true;
+ if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
+ dropped= TRUE;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
}
else
{
@@ -1130,10 +1412,10 @@ Event_timed::compute_next_execution_time()
DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
- status= Event_timed::DISABLED;
- status_changed= true;
- if (on_completion == Event_timed::ON_COMPLETION_DROP)
- dropped= true;
+ status= Event_queue_element::DISABLED;
+ status_changed= TRUE;
+ if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
+ dropped= TRUE;
}
else
{
@@ -1147,11 +1429,12 @@ Event_timed::compute_next_execution_time()
goto ret;
}
ret:
- DBUG_PRINT("info", ("ret=0"));
- DBUG_RETURN(false);
+ DBUG_PRINT("info", ("ret=0 execute_at=%llu",
+ TIME_to_ulonglong_datetime(&execute_at)));
+ DBUG_RETURN(FALSE);
err:
DBUG_PRINT("info", ("ret=1"));
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
}
@@ -1160,12 +1443,12 @@ err:
time according to thd->query_start(), so the THD's clock.
SYNOPSIS
- Event_timed::drop()
+ Event_queue_element::mark_last_executed()
thd thread context
*/
void
-Event_timed::mark_last_executed(THD *thd)
+Event_queue_element::mark_last_executed(THD *thd)
{
TIME time_now;
@@ -1173,7 +1456,7 @@ Event_timed::mark_last_executed(THD *thd)
my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
last_executed= time_now; /* was execute_at */
- last_executed_changed= true;
+ last_executed_changed= TRUE;
}
@@ -1181,7 +1464,7 @@ Event_timed::mark_last_executed(THD *thd)
Drops the event
SYNOPSIS
- Event_timed::drop()
+ Event_queue_element::drop()
thd thread context
RETURN VALUE
@@ -1194,12 +1477,13 @@ Event_timed::mark_last_executed(THD *thd)
*/
int
-Event_timed::drop(THD *thd)
+Event_queue_element::drop(THD *thd)
{
uint tmp= 0;
- DBUG_ENTER("Event_timed::drop");
+ DBUG_ENTER("Event_queue_element::drop");
- DBUG_RETURN(db_drop_event(thd, this, false, &tmp));
+ DBUG_RETURN(Events::get_instance()->drop_event(thd, dbname, name, FALSE,
+ &tmp, TRUE));
}
@@ -1207,12 +1491,12 @@ Event_timed::drop(THD *thd)
Saves status and last_executed_at to the disk if changed.
SYNOPSIS
- Event_timed::update_fields()
+ Event_queue_element::update_timing_fields()
thd - thread context
RETURN VALUE
0 OK
- EVEX_OPEN_TABLE_FAILED Error while opening mysql.event for writing
+ EVEX_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
@@ -1220,13 +1504,13 @@ Event_timed::drop(THD *thd)
*/
bool
-Event_timed::update_fields(THD *thd)
+Event_queue_element::update_timing_fields(THD *thd)
{
TABLE *table;
Open_tables_state backup;
int ret;
- DBUG_ENTER("Event_timed::update_time_fields");
+ DBUG_ENTER("Event_queue_element::update_timing_fields");
DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
@@ -1236,14 +1520,14 @@ Event_timed::update_fields(THD *thd)
thd->reset_n_backup_open_tables_state(&backup);
- if (Events::open_event_table(thd, TL_WRITE, &table))
+ if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table))
{
ret= EVEX_OPEN_TABLE_FAILED;
goto done;
}
-
- if ((ret= evex_db_find_event_by_name(thd, dbname, name, table)))
+ if ((ret= Events::get_instance()->db_repository->
+ find_event_by_name(thd, dbname, name, table)))
goto done;
store_record(table,record[1]);
@@ -1252,16 +1536,16 @@ Event_timed::update_fields(THD *thd)
if (last_executed_changed)
{
- table->field[Events::FIELD_LAST_EXECUTED]->set_notnull();
- table->field[Events::FIELD_LAST_EXECUTED]->store_time(&last_executed,
+ table->field[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ table->field[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
MYSQL_TIMESTAMP_DATETIME);
- last_executed_changed= false;
+ last_executed_changed= FALSE;
}
if (status_changed)
{
- table->field[Events::FIELD_STATUS]->set_notnull();
- table->field[Events::FIELD_STATUS]->store((longlong)status, true);
- status_changed= false;
+ table->field[ET_FIELD_STATUS]->set_notnull();
+ table->field[ET_FIELD_STATUS]->store((longlong)status, TRUE);
+ status_changed= FALSE;
}
if ((table->file->ha_update_row(table->record[1],table->record[0])))
@@ -1294,8 +1578,8 @@ int
Event_timed::get_create_event(THD *thd, String *buf)
{
int multipl= 0;
- char tmp_buff[128];
- String expr_buf(tmp_buff, sizeof(tmp_buff), system_charset_info);
+ char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
+ String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
expr_buf.length(0);
DBUG_ENTER("get_create_event");
@@ -1352,10 +1636,37 @@ Event_timed::get_create_event(THD *thd, String *buf)
/*
+ Get SHOW CREATE EVENT as string
+
+ SYNOPSIS
+ Event_job_data::get_create_event(THD *thd, String *buf)
+ thd Thread
+ buf String*, should be already allocated. CREATE EVENT goes inside.
+
+ RETURN VALUE
+ 0 OK
+ EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been
+ tampered and MICROSECONDS interval or
+ derivative has been put there.
+*/
+
+int
+Event_job_data::get_fake_create_event(THD *thd, String *buf)
+{
+ DBUG_ENTER("Event_job_data::get_create_event");
+ buf->append(STRING_WITH_LEN("CREATE EVENT test.anonymous ON SCHEDULE "
+ "EVERY 3337 HOUR DO "));
+ buf->append(body.str, body.length);
+
+ DBUG_RETURN(0);
+}
+
+
+/*
Executes the event (the underlying sp_head object);
SYNOPSIS
- evex_fill_row()
+ Event_job_data::execute()
thd THD
mem_root If != NULL use it to compile the event on it
@@ -1367,24 +1678,18 @@ Event_timed::get_create_event(THD *thd, String *buf)
*/
int
-Event_timed::execute(THD *thd, MEM_ROOT *mem_root)
+Event_job_data::execute(THD *thd, MEM_ROOT *mem_root)
{
+ Security_context *save_ctx;
/* this one is local and not needed after exec */
Security_context security_ctx;
int ret= 0;
- DBUG_ENTER("Event_timed::execute");
- DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]",
- dbname.str, name.str, (int) expression));
+ DBUG_ENTER("Event_job_data::execute");
+ DBUG_PRINT("info", ("EXECUTING %s.%s", dbname.str, name.str));
- VOID(pthread_mutex_lock(&this->LOCK_running));
- if (running)
- {
- VOID(pthread_mutex_unlock(&this->LOCK_running));
- DBUG_RETURN(-100);
- }
- running= true;
- VOID(pthread_mutex_unlock(&this->LOCK_running));
+ thd->change_security_context(definer_user, definer_host, dbname,
+ &security_ctx, &save_ctx);
if (!sphead && (ret= compile(thd, mem_root)))
goto done;
@@ -1411,14 +1716,11 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root)
definer_host.str, dbname.str));
ret= -99;
}
-
- VOID(pthread_mutex_lock(&this->LOCK_running));
- running= false;
/* Will compile every time a new sp_head on different root */
free_sp();
- VOID(pthread_mutex_unlock(&this->LOCK_running));
done:
+ thd->restore_security_context(save_ctx);
/*
1. Don't cache sphead if allocated on another mem_root
2. Don't call security_ctx.destroy() because this will free our dbname.str
@@ -1429,8 +1731,7 @@ done:
delete sphead;
sphead= 0;
}
- DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
- dbname.str, name.str, (int) expression, ret));
+ DBUG_PRINT("info", ("EXECUTED %s.%s ret=%d", dbname.str, name.str, ret));
DBUG_RETURN(ret);
}
@@ -1439,14 +1740,14 @@ done:
/*
Frees the memory of the sp_head object we hold
SYNOPSIS
- Event_timed::free_sp()
+ Event_job_data::free_sp()
*/
void
-Event_timed::free_sp()
+Event_job_data::free_sp()
{
delete sphead;
- sphead= 0;
+ sphead= NULL;
}
@@ -1455,7 +1756,7 @@ Event_timed::free_sp()
sp_head object held by the event
SYNOPSIS
- Event_timed::compile()
+ Event_job_data::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
@@ -1467,7 +1768,7 @@ Event_timed::free_sp()
*/
int
-Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
+Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
{
int ret= 0;
MEM_ROOT *tmp_mem_root= 0;
@@ -1477,7 +1778,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
char *old_query;
uint old_query_len;
ulong old_sql_mode= thd->variables.sql_mode;
- char create_buf[2048];
+ char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
String show_create(create_buf, sizeof(create_buf), system_charset_info);
CHARSET_INFO *old_character_set_client,
*old_collation_connection,
@@ -1486,11 +1787,11 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
/* this one is local and not needed after exec */
Security_context security_ctx;
- DBUG_ENTER("Event_timed::compile");
+ DBUG_ENTER("Event_job_data::compile");
show_create.length(0);
- switch (get_create_event(thd, &show_create)) {
+ switch (get_fake_create_event(thd, &show_create)) {
case EVEX_MICROSECOND_UNSUP:
sql_print_error("Scheduler");
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
@@ -1526,15 +1827,14 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
thd->db= dbname.str;
thd->db_length= dbname.length;
- thd->query= show_create.c_ptr();
+ thd->query= show_create.c_ptr_safe();
thd->query_length= show_create.length();
DBUG_PRINT("info", ("query:%s",thd->query));
- change_security_context(thd, definer_user, definer_host, dbname,
- &security_ctx, &save_ctx);
+ thd->change_security_context(definer_user, definer_host, dbname,
+ &security_ctx, &save_ctx);
thd->lex= &lex;
lex_start(thd, (uchar*)thd->query, thd->query_length);
- lex.et_compile_phase= TRUE;
if (MYSQLparse((void *)thd) || thd->is_fatal_error)
{
DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d",
@@ -1557,7 +1857,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
}
DBUG_PRINT("note", ("success compiling %s.%s", dbname.str, name.str));
- sphead= lex.et->sphead;
+ sphead= lex.sphead;
sphead->m_db= dbname;
sphead->set_definer(definer.str, definer.length);
@@ -1565,11 +1865,9 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
sphead->optimize();
ret= 0;
done:
- lex.et->free_sphead_on_delete= false;
- lex.et->deinit_mutexes();
lex_end(&lex);
- restore_security_context(thd, save_ctx);
+ thd->restore_security_context(save_ctx);
DBUG_PRINT("note", ("return old data on its place. set back NAMES"));
thd->lex= old_lex;
@@ -1591,276 +1889,38 @@ done:
}
-extern pthread_attr_t connection_attrib;
-
-/*
- Checks whether is possible and forks a thread. Passes self as argument.
-
- RETURN VALUE
- EVENT_EXEC_STARTED OK
- EVENT_EXEC_ALREADY_EXEC Thread not forked, already working
- EVENT_EXEC_CANT_FORK Unable to spawn thread (error)
-*/
-
-int
-Event_timed::spawn_now(void * (*thread_func)(void*), void *arg)
-{
- THD *thd= current_thd;
- int ret= EVENT_EXEC_STARTED;
- DBUG_ENTER("Event_timed::spawn_now");
- DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str));
-
- VOID(pthread_mutex_lock(&this->LOCK_running));
-
- DBUG_PRINT("info", ("SCHEDULER: execute_at of %s is %lld", name.str,
- TIME_to_ulonglong_datetime(&execute_at)));
- mark_last_executed(thd);
- if (compute_next_execution_time())
- {
- sql_print_error("SCHEDULER: Error while computing time of %s.%s . "
- "Disabling after execution.", dbname.str, name.str);
- status= DISABLED;
- }
- DBUG_PRINT("evex manager", ("[%10s] next exec at [%llu]", name.str,
- TIME_to_ulonglong_datetime(&execute_at)));
- /*
- 1. For one-time event : year is > 0 and expression is 0
- 2. For recurring, expression is != -=> check execute_at_null in this case
- */
- if ((execute_at.year && !expression) || execute_at_null)
- {
- sql_print_information("SCHEDULER: [%s.%s of %s] no more executions "
- "after this one", dbname.str, name.str,
- definer.str);
- flags |= EVENT_EXEC_NO_MORE | EVENT_FREE_WHEN_FINISHED;
- }
-
- update_fields(thd);
-
- if (!in_spawned_thread)
- {
- pthread_t th;
- in_spawned_thread= true;
-
- if (pthread_create(&th, &connection_attrib, thread_func, arg))
- {
- DBUG_PRINT("info", ("problem while spawning thread"));
- ret= EVENT_EXEC_CANT_FORK;
- in_spawned_thread= false;
- }
- }
- else
- {
- DBUG_PRINT("info", ("already in spawned thread. skipping"));
- ret= EVENT_EXEC_ALREADY_EXEC;
- }
- VOID(pthread_mutex_unlock(&this->LOCK_running));
-
- DBUG_RETURN(ret);
-}
-
-
-bool
-Event_timed::spawn_thread_finish(THD *thd)
-{
- bool should_free;
- DBUG_ENTER("Event_timed::spawn_thread_finish");
- VOID(pthread_mutex_lock(&LOCK_running));
- in_spawned_thread= false;
- DBUG_PRINT("info", ("Sending COND_finished for thread %d", thread_id));
- thread_id= 0;
- if (dropped)
- drop(thd);
- pthread_cond_broadcast(&COND_finished);
- should_free= flags & EVENT_FREE_WHEN_FINISHED;
- VOID(pthread_mutex_unlock(&LOCK_running));
- DBUG_RETURN(should_free);
-}
-
-
-/*
- Kills a running event
- SYNOPSIS
- Event_timed::kill_thread()
-
- RETURN VALUE
- 0 OK
- -1 EVEX_CANT_KILL
- !0 Error
-*/
-
-int
-Event_timed::kill_thread(THD *thd)
-{
- int ret= 0;
- DBUG_ENTER("Event_timed::kill_thread");
- pthread_mutex_lock(&LOCK_running);
- DBUG_PRINT("info", ("thread_id=%lu", thread_id));
-
- if (thread_id == thd->thread_id)
- {
- /*
- We don't kill ourselves in cases like :
- alter event e_43 do alter event e_43 do set @a = 4 because
- we will never receive COND_finished.
- */
- DBUG_PRINT("info", ("It's not safe to kill ourselves in self altering queries"));
- ret= EVEX_CANT_KILL;
- }
- else if (thread_id && !(ret= kill_one_thread(thd, thread_id, false)))
- {
- thd->enter_cond(&COND_finished, &LOCK_running, "Waiting for finished");
- DBUG_PRINT("info", ("Waiting for COND_finished from thread %d", thread_id));
- while (thread_id)
- pthread_cond_wait(&COND_finished, &LOCK_running);
-
- DBUG_PRINT("info", ("Got COND_finished"));
- /* This will implicitly unlock LOCK_running. Hence we return before that */
- thd->exit_cond("");
-
- DBUG_RETURN(0);
- }
- else if (!thread_id && in_spawned_thread)
- {
- /*
- Because the manager thread waits for the forked thread to update thread_id
- this situation is impossible.
- */
- DBUG_ASSERT(0);
- }
- pthread_mutex_unlock(&LOCK_running);
- DBUG_PRINT("exit", ("%d", ret));
- DBUG_RETURN(ret);
-}
-
-
-/*
- Checks whether two events have the same name
-
- SYNOPSIS
- event_timed_name_equal()
-
- RETURN VALUE
- TRUE names are equal
- FALSE names are not equal
-*/
-
-bool
-event_timed_name_equal(Event_timed *et, LEX_STRING *name)
-{
- return !sortcmp_lex_string(et->name, *name, system_charset_info);
-}
-
-
/*
Checks whether two events are in the same schema
SYNOPSIS
- event_timed_db_equal()
+ event_basic_db_equal()
RETURN VALUE
- TRUE schemas are equal
- FALSE schemas are not equal
+ TRUE Equal
+ FALSE Not equal
*/
bool
-event_timed_db_equal(Event_timed *et, LEX_STRING *db)
+event_basic_db_equal(LEX_STRING *db, Event_basic *et)
{
return !sortcmp_lex_string(et->dbname, *db, system_charset_info);
}
/*
- Checks whether two events have the same definer
-
- SYNOPSIS
- event_timed_definer_equal()
-
- Returns
- TRUE definers are equal
- FALSE definers are not equal
-*/
-
-bool
-event_timed_definer_equal(Event_timed *et, LEX_STRING *definer)
-{
- return !sortcmp_lex_string(et->definer, *definer, system_charset_info);
-}
-
-
-/*
Checks whether two events are equal by identifiers
SYNOPSIS
- event_timed_identifier_equal()
+ event_basic_identifier_equal()
RETURN VALUE
- TRUE equal
- FALSE not equal
+ TRUE Equal
+ FALSE Not equal
*/
bool
-event_timed_identifier_equal(Event_timed *a, Event_timed *b)
+event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
{
- return event_timed_name_equal(a, &b->name) &&
- event_timed_db_equal(a, &b->dbname) &&
- event_timed_definer_equal(a, &b->definer);
-}
-
-
-/*
- Switches the security context
- SYNOPSIS
- change_security_context()
- thd Thread
- user The user
- host The host of the user
- db The schema for which the security_ctx will be loaded
- s_ctx Security context to load state into
- backup Where to store the old context
-
- RETURN VALUE
- 0 - OK
- 1 - Error (generates error too)
-*/
-
-bool
-change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
- LEX_STRING db, Security_context *s_ctx,
- Security_context **backup)
-{
- DBUG_ENTER("change_security_context");
- DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- s_ctx->init();
- *backup= 0;
- if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str))
- {
- my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
- DBUG_RETURN(TRUE);
- }
- *backup= thd->security_ctx;
- thd->security_ctx= s_ctx;
-#endif
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- Restores the security context
- SYNOPSIS
- restore_security_context()
- thd - thread
- backup - switch to this context
-*/
-
-void
-restore_security_context(THD *thd, Security_context *backup)
-{
- DBUG_ENTER("restore_security_context");
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (backup)
- thd->security_ctx= backup;
-#endif
- DBUG_VOID_RETURN;
+ return !sortcmp_lex_string(name, b->name, system_charset_info) &&
+ !sortcmp_lex_string(db, b->dbname, system_charset_info);
}
diff --git a/sql/event_timed.h b/sql/event_data_objects.h
index 0652cece361..d405cafe42d 100644
--- a/sql/event_timed.h
+++ b/sql/event_data_objects.h
@@ -1,5 +1,5 @@
-#ifndef _EVENT_TIMED_H_
-#define _EVENT_TIMED_H_
+#ifndef _EVENT_DATA_OBJECTS_H_
+#define _EVENT_DATA_OBJECTS_H_
/* Copyright (C) 2004-2006 MySQL AB
This program is free software; you can redistribute it and/or modify
@@ -40,19 +40,49 @@
#define EVENT_FREE_WHEN_FINISHED (1L << 2)
+#define EVENT_EXEC_STARTED 0
+#define EVENT_EXEC_ALREADY_EXEC 1
+#define EVENT_EXEC_CANT_FORK 2
+
+
class sp_head;
+class Sql_alloc;
+class Event_basic;
+
+/* Compares only the schema part of the identifier */
+bool
+event_basic_db_equal( LEX_STRING *db, Event_basic *et);
-class Event_timed
+/* Compares the whole identifier*/
+bool
+event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b);
+
+class Event_basic
{
- Event_timed(const Event_timed &); /* Prevent use of these */
- void operator=(Event_timed &);
- my_bool in_spawned_thread;
- ulong locked_by_thread_id;
- my_bool running;
- ulong thread_id;
- pthread_mutex_t LOCK_running;
- pthread_cond_t COND_finished;
+protected:
+ MEM_ROOT mem_root;
+public:
+ LEX_STRING dbname;
+ LEX_STRING name;
+ LEX_STRING definer;// combination of user and host
+
+ Event_basic();
+ virtual ~Event_basic();
+
+ virtual int
+ load_from_row(TABLE *table) = 0;
+
+protected:
+ bool
+ load_string_fields(Field **fields, ...);
+};
+
+
+
+class Event_queue_element : public Event_basic
+{
+protected:
bool status_changed;
bool last_executed_changed;
@@ -69,17 +99,10 @@ public:
ON_COMPLETION_PRESERVE
};
+ enum enum_on_completion on_completion;
+ enum enum_status status;
TIME last_executed;
- LEX_STRING dbname;
- LEX_STRING name;
- LEX_STRING body;
-
- LEX_STRING definer_user;
- LEX_STRING definer_host;
- LEX_STRING definer;// combination of user and host
-
- LEX_STRING comment;
TIME starts;
TIME ends;
TIME execute_at;
@@ -90,128 +113,194 @@ public:
longlong expression;
interval_type interval;
- ulonglong created;
- ulonglong modified;
- enum enum_on_completion on_completion;
- enum enum_status status;
- sp_head *sphead;
- ulong sql_mode;
- const uchar *body_begin;
+ uint flags;//all kind of purposes
bool dropped;
- bool free_sphead_on_delete;
- uint flags;//all kind of purposes
+
+ Event_queue_element();
+ virtual ~Event_queue_element();
+
+ virtual int
+ load_from_row(TABLE *table);
+
+ bool
+ compute_next_execution_time();
+
+ int
+ drop(THD *thd);
+
+ void
+ mark_last_executed(THD *thd);
+
+ bool
+ update_timing_fields(THD *thd);
static void *operator new(size_t size)
{
void *p;
- DBUG_ENTER("Event_timed::new(size)");
+ DBUG_ENTER("Event_queue_element::new(size)");
p= my_malloc(size, MYF(0));
DBUG_PRINT("info", ("alloc_ptr=0x%lx", p));
DBUG_RETURN(p);
}
- static void *operator new(size_t size, MEM_ROOT *mem_root)
- { return (void*) alloc_root(mem_root, (uint) size); }
-
static void operator delete(void *ptr, size_t size)
{
- DBUG_ENTER("Event_timed::delete(ptr,size)");
+ DBUG_ENTER("Event_queue_element::delete(ptr,size)");
DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr));
TRASH(ptr, size);
my_free((gptr) ptr, MYF(0));
DBUG_VOID_RETURN;
}
+};
- static void operator delete(void *ptr, MEM_ROOT *mem_root)
- {
- /*
- Don't free the memory it will be done by the mem_root but
- we need to call the destructor because we free other resources
- which are not allocated on the root but on the heap, or we
- deinit mutexes.
- */
- DBUG_ASSERT(0);
- }
- Event_timed();
+class Event_timed : public Event_queue_element
+{
+ Event_timed(const Event_timed &); /* Prevent use of these */
+ void operator=(Event_timed &);
+
+public:
+ LEX_STRING body;
+
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+
+ LEX_STRING comment;
+
+ ulonglong created;
+ ulonglong modified;
- ~Event_timed();
+ ulong sql_mode;
+
+ Event_timed();
+ virtual ~Event_timed();
void
init();
- void
- deinit_mutexes();
+ virtual int
+ load_from_row(TABLE *table);
int
- init_definer(THD *thd);
+ get_create_event(THD *thd, String *buf);
+};
- int
- init_execute_at(THD *thd, Item *expr);
- int
- init_interval(THD *thd, Item *expr, interval_type new_interval);
+class Event_job_data : public Event_basic
+{
+public:
+ THD *thd;
+ sp_head *sphead;
- void
- init_name(THD *thd, sp_name *spn);
+ LEX_STRING body;
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+
+ ulong sql_mode;
+
+ Event_job_data();
+ virtual ~Event_job_data();
+ virtual int
+ load_from_row(TABLE *table);
+
+ int
+ execute(THD *thd, MEM_ROOT *mem_root);
+private:
int
- init_starts(THD *thd, Item *starts);
+ get_fake_create_event(THD *thd, String *buf);
int
- init_ends(THD *thd, Item *ends);
+ compile(THD *thd, MEM_ROOT *mem_root);
void
- init_body(THD *thd);
+ free_sp();
- void
- init_comment(THD *thd, LEX_STRING *set_comment);
+ Event_job_data(const Event_job_data &); /* Prevent use of these */
+ void operator=(Event_job_data &);
+};
- int
- load_from_row(MEM_ROOT *mem_root, TABLE *table);
+
+class Event_parse_data : public Sql_alloc
+{
+public:
+ enum enum_status
+ {
+ ENABLED = 1,
+ DISABLED
+ };
+
+ enum enum_on_completion
+ {
+ ON_COMPLETION_DROP = 1,
+ ON_COMPLETION_PRESERVE
+ };
+ enum enum_on_completion on_completion;
+ enum enum_status status;
+
+ const uchar *body_begin;
+
+ LEX_STRING dbname;
+ LEX_STRING name;
+ LEX_STRING definer;// combination of user and host
+ LEX_STRING body;
+ LEX_STRING comment;
+
+ Item* item_starts;
+ Item* item_ends;
+ Item* item_execute_at;
+
+ TIME starts;
+ TIME ends;
+ TIME execute_at;
+ my_bool starts_null;
+ my_bool ends_null;
+ my_bool execute_at_null;
+
+ sp_name *identifier;
+ Item* item_expression;
+ longlong expression;
+ interval_type interval;
+
+ static Event_parse_data *
+ new_instance(THD *thd);
bool
- compute_next_execution_time();
+ check_parse_data(THD *);
+
+ void
+ init_body(THD *thd);
+
+private:
int
- drop(THD *thd);
+ init_definer(THD *thd);
void
- mark_last_executed(THD *thd);
+ init_name(THD *thd, sp_name *spn);
- bool
- update_fields(THD *thd);
+ int
+ init_execute_at(THD *thd);
int
- get_create_event(THD *thd, String *buf);
+ init_interval(THD *thd);
int
- execute(THD *thd, MEM_ROOT *mem_root);
+ init_starts(THD *thd);
int
- compile(THD *thd, MEM_ROOT *mem_root);
+ init_ends(THD *thd);
- bool
- is_running();
+ Event_parse_data();
+ ~Event_parse_data();
- int
- spawn_now(void * (*thread_func)(void*), void *arg);
-
- bool
- spawn_thread_finish(THD *thd);
-
void
- free_sp();
+ report_bad_value(const char *item_name, Item *bad_item);
- bool
- has_equal_db(Event_timed *etn);
+ Event_parse_data(const Event_parse_data &); /* Prevent use of these */
+ void operator=(Event_parse_data &);
+};
- int
- kill_thread(THD *thd);
- void
- set_thread_id(ulong tid) { thread_id= tid; }
-};
-
-#endif /* _EVENT_H_ */
+#endif /* _EVENT_DATA_OBJECTS_H_ */
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
new file mode 100644
index 00000000000..22dcdd97a48
--- /dev/null
+++ b/sql/event_db_repository.cc
@@ -0,0 +1,1040 @@
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "event_db_repository.h"
+#include "event_data_objects.h"
+#include "sp_head.h"
+#include "sp.h"
+#include "events.h"
+#include "sql_show.h"
+
+#define EVEX_DB_FIELD_LEN 64
+#define EVEX_NAME_FIELD_LEN 64
+
+
+time_t mysql_event_last_create_time= 0L;
+
+static
+TABLE_FIELD_W_TYPE event_table_fields[ET_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")},
+ {NULL, 0}
+ },
+ {
+ {(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")}
+ }
+};
+
+
+/*
+ Puts some data common to CREATE and ALTER EVENT into a row.
+
+ SYNOPSIS
+ mysql_event_fill_row()
+ thd THD
+ table The row to fill out
+ et Event's data
+ is_update CREATE EVENT or ALTER EVENT
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GENERAL_ERROR Bad data
+ EVEX_GET_FIELD_FAILED Field count does not match. table corrupted?
+
+ DESCRIPTION
+ Used both when an event is created and when it is altered.
+*/
+
+static int
+mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
+ my_bool is_update)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ enum enum_events_table_field f_num;
+ Field **fields= table->field;
+
+ DBUG_ENTER("mysql_event_fill_row");
+
+ 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 (fields[f_num= ET_FIELD_DEFINER]->
+ store(et->definer.str, et->definer.length, scs))
+ goto err_truncate;
+
+ if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
+ goto err_truncate;
+
+ if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
+ goto err_truncate;
+
+ /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
+ fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
+
+ fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
+
+ /*
+ Change the SQL_MODE only if body was present in an ALTER EVENT and of course
+ always during CREATE EVENT.
+ */
+ if (et->body.str)
+ {
+ fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE);
+ if (fields[f_num= ET_FIELD_BODY]->store(et->body.str, et->body.length, scs))
+ goto err_truncate;
+ }
+
+ if (et->expression)
+ {
+ fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
+ fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
+
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
+ /*
+ In the enum (C) intervals start from 0 but in mysql enum valid values
+ start from 1. Thus +1 offset is needed!
+ */
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, TRUE);
+
+ fields[ET_FIELD_EXECUTE_AT]->set_null();
+
+ if (!et->starts_null)
+ {
+ fields[ET_FIELD_STARTS]->set_notnull();
+ fields[ET_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
+ }
+
+ if (!et->ends_null)
+ {
+ fields[ET_FIELD_ENDS]->set_notnull();
+ fields[ET_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
+ }
+ }
+ else if (et->execute_at.year)
+ {
+ fields[ET_FIELD_INTERVAL_EXPR]->set_null();
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
+ fields[ET_FIELD_STARTS]->set_null();
+ fields[ET_FIELD_ENDS]->set_null();
+
+ fields[ET_FIELD_EXECUTE_AT]->set_notnull();
+ fields[ET_FIELD_EXECUTE_AT]->
+ store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
+ }
+ else
+ {
+ DBUG_ASSERT(is_update);
+ /*
+ it is normal to be here when the action is update
+ this is an error if the action is create. something is borked
+ */
+ }
+
+ ((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time();
+
+ if (et->comment.str)
+ {
+ if (fields[f_num= ET_FIELD_COMMENT]->
+ store(et->comment.str, et->comment.length, scs))
+ goto err_truncate;
+ }
+
+ DBUG_RETURN(0);
+
+err_truncate:
+ my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ Performs an index scan of event_table (mysql.event) and fills schema_table.
+
+ Synopsis
+ Event_db_repository::index_read_for_db_for_i_s()
+ thd Thread
+ schema_table The I_S.EVENTS table
+ event_table The event table to use for loading (mysql.event)
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
+ TABLE *event_table, char *db)
+{
+ int ret=0;
+ CHARSET_INFO *scs= system_charset_info;
+ KEY *key_info;
+ uint key_len;
+ byte *key_buf= NULL;
+ LINT_INIT(key_buf);
+
+ DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");
+
+ DBUG_PRINT("info", ("Using prefix scanning on PK"));
+ event_table->file->ha_index_init(0, 1);
+ event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs);
+ key_info= event_table->key_info;
+ key_len= key_info->key_part[0].store_length;
+
+ if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len)))
+ {
+ ret= 1;
+ /* Don't send error, it would be done by sql_alloc_error_handler() */
+ }
+ else
+ {
+ key_copy(key_buf, event_table->record[0], key_info, key_len);
+ if (!(ret= event_table->file->index_read(event_table->record[0], key_buf,
+ key_len, HA_READ_PREFIX)))
+ {
+ DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
+ do
+ {
+ ret= copy_event_to_schema_table(thd, schema_table, event_table);
+ if (ret == 0)
+ ret= event_table->file->index_next_same(event_table->record[0],
+ key_buf, key_len);
+ } while (ret == 0);
+ }
+ DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
+ }
+ event_table->file->ha_index_end();
+ /* ret is guaranteed to be != 0 */
+ if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(0);
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Performs a table scan of event_table (mysql.event) and fills schema_table.
+
+ Synopsis
+ Events_db_repository::table_scan_all_for_i_s()
+ thd Thread
+ schema_table The I_S.EVENTS in memory table
+ event_table The event table to use for loading.
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
+ TABLE *event_table)
+{
+ int ret;
+ READ_RECORD read_record_info;
+
+ DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
+ init_read_record(&read_record_info, thd, event_table, NULL, 1, 0);
+
+ /*
+ rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
+ but rr_handle_error returns -1 for that reason. Thus, read_record()
+ returns -1 eventually.
+ */
+ do
+ {
+ ret= read_record_info.read_record(&read_record_info);
+ if (ret == 0)
+ ret= copy_event_to_schema_table(thd, schema_table, event_table);
+ } while (ret == 0);
+
+ DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
+ end_read_record(&read_record_info);
+
+ /* ret is guaranteed to be != 0 */
+ DBUG_RETURN(ret == -1? 0:1);
+}
+
+
+/*
+ Fills I_S.EVENTS with data loaded from mysql.event. Also used by
+ SHOW EVENTS
+
+ Synopsis
+ Event_db_repository::fill_schema_events()
+ thd Thread
+ tables The schema table
+ db If not NULL then get events only from this schema
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, char *db)
+{
+ TABLE *schema_table= tables->table;
+ TABLE *event_table= NULL;
+ Open_tables_state backup;
+ int ret= 0;
+
+ DBUG_ENTER("Event_db_repository::fill_schema_events");
+ DBUG_PRINT("info",("db=%s", db? db:"(null)"));
+
+ thd->reset_n_backup_open_tables_state(&backup);
+ if (open_event_table(thd, TL_READ, &event_table))
+ {
+ sql_print_error("Table mysql.event is damaged.");
+ thd->restore_backup_open_tables_state(&backup);
+ DBUG_RETURN(1);
+ }
+
+ /*
+ 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
+ thus we won't order it. OTOH, SHOW EVENTS will be
+ ordered.
+ 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
+ Reasoning: Events are per schema, therefore a scan over an index
+ will save use from doing a table scan and comparing
+ every single row's `db` with the schema which we show.
+ */
+ if (db)
+ ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db);
+ else
+ ret= table_scan_all_for_i_s(thd, schema_table, event_table);
+
+ close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(&backup);
+
+ DBUG_PRINT("info", ("Return code=%d", ret));
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Open mysql.event table for read
+
+ SYNOPSIS
+ Events::open_event_table()
+ thd [in] Thread context
+ lock_type [in] How to lock the table
+ table [out] We will store the open table here
+
+ RETURN VALUE
+ 1 Cannot lock table
+ 2 The table is corrupted - different number of fields
+ 0 OK
+*/
+
+int
+Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
+ TABLE **table)
+{
+ TABLE_LIST tables;
+ DBUG_ENTER("Event_db_repository::open_event_table");
+
+ bzero((char*) &tables, sizeof(tables));
+ tables.db= (char*) "mysql";
+ tables.table_name= tables.alias= (char*) "event";
+ tables.lock_type= lock_type;
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ DBUG_RETURN(1);
+
+ if (table_check_intact(tables.table, ET_FIELD_COUNT,
+ event_table_fields,
+ &mysql_event_last_create_time,
+ ER_CANNOT_LOAD_FROM_TABLE))
+ {
+ close_thread_tables(thd);
+ DBUG_RETURN(2);
+ }
+ *table= tables.table;
+ tables.table->use_all_columns();
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Checks parameters which we got from the parsing phase.
+
+ SYNOPSIS
+ check_parse_params()
+ thd THD
+ et event's data
+
+ RETURNS
+ 0 OK
+ EVEX_BAD_PARAMS Error (reported)
+*/
+
+static int
+check_parse_params(THD *thd, Event_parse_data *parse_data)
+{
+ const char *pos= NULL;
+ Item *bad_item;
+ int res;
+
+ DBUG_ENTER("check_parse_params");
+
+ if (parse_data->check_parse_data(thd))
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+
+ if (!parse_data->dbname.str ||
+ (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname &&
+ !thd->lex->spname->m_db.str))
+ {
+ my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+ }
+
+ if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
+ is_schema_db(parse_data->dbname.str)) ||
+ (thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname &&
+ (check_access(thd, EVENT_ACL, thd->lex->spname->m_db.str, 0, 0, 0,
+ is_schema_db(thd->lex->spname->m_db.str)))))
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Creates an event in mysql.event
+
+ SYNOPSIS
+ Event_db_repository::create_event()
+ thd [in] THD
+ et [in] Object containing info about the event
+ create_if_not [in] Whether to generate anwarning in case event exists
+ rows_affected [out] How many rows were affected
+
+ RETURN VALUE
+ 0 - OK
+ EVEX_GENERAL_ERROR - Failure
+
+ DESCRIPTION
+ Creates an event. Relies on mysql_event_fill_row which is shared with
+ ::update_event. The name of the event is inside "et".
+*/
+
+int
+Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
+ my_bool create_if_not, uint *rows_affected)
+{
+ int ret= 0;
+ CHARSET_INFO *scs= system_charset_info;
+ TABLE *table;
+ char olddb[128];
+ bool dbchanged= FALSE;
+ DBUG_ENTER("Event_db_repository::create_event");
+
+ *rows_affected= 0;
+ DBUG_PRINT("info", ("open mysql.event for update"));
+ if (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ goto err;
+ }
+
+ if (check_parse_params(thd, parse_data))
+ goto err;
+
+ DBUG_PRINT("info", ("name: %.*s", parse_data->name.length,
+ parse_data->name.str));
+
+ DBUG_PRINT("info", ("check existance of an event with the same name"));
+ if (!find_event_by_name(thd, parse_data->dbname, parse_data->name, table))
+ {
+ if (create_if_not)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
+ parse_data->name.str);
+ goto ok;
+ }
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ DBUG_PRINT("info", ("non-existant, go forward"));
+ if ((ret= sp_use_new_db(thd, parse_data->dbname.str, olddb, sizeof(olddb), 0,
+ &dbchanged)))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0));
+ goto err;
+ }
+
+ restore_record(table, s->default_values); // Get default values for fields
+
+ if (system_charset_info->cset->
+ numchars(system_charset_info, parse_data->dbname.str,
+ parse_data->dbname.str +
+ parse_data->dbname.length) > EVEX_DB_FIELD_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
+ goto err;
+ }
+ if (system_charset_info->cset->
+ numchars(system_charset_info, parse_data->name.str,
+ parse_data->name.str +
+ parse_data->name.length) > EVEX_DB_FIELD_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ if (parse_data->body.length > table->field[ET_FIELD_BODY]->field_length)
+ {
+ my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ if (!(parse_data->expression) && !(parse_data->execute_at.year))
+ {
+ DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
+ my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
+ goto err;
+ }
+
+ ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
+
+ /*
+ mysql_event_fill_row() calls my_error() in case of error so no need to
+ handle it here
+ */
+ if ((ret= mysql_event_fill_row(thd, table, parse_data, FALSE)))
+ goto err;
+
+ /* Close active transaction only if We are going to modify disk */
+ if (end_active_trans(thd))
+ goto err;
+
+ if (table->file->ha_write_row(table->record[0]))
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret);
+ goto err;
+ }
+
+ *rows_affected= 1;
+ok:
+ if (dbchanged)
+ (void) mysql_change_db(thd, olddb, 1);
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(EVEX_OK);
+
+err:
+ if (dbchanged)
+ (void) mysql_change_db(thd, olddb, 1);
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ Used to execute ALTER EVENT. Pendant to Events::update_event().
+
+ SYNOPSIS
+ Event_db_repository::update_event()
+ thd THD
+ sp_name the name of the event to alter
+ et event's data
+
+ RETURN VALUE
+ 0 OK
+ EVEX_GENERAL_ERROR Error occured and reported
+
+ NOTES
+ sp_name is passed since this is the name of the event to
+ alter in case of RENAME TO.
+*/
+
+int
+Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
+ sp_name *new_name)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ TABLE *table;
+ int ret= EVEX_OPEN_TABLE_FAILED;
+ DBUG_ENTER("Event_db_repository::update_event");
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ goto err;
+ }
+
+ if (check_parse_params(thd, parse_data))
+ goto err;
+
+ DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
+ DBUG_PRINT("info", ("name: %s", parse_data->name.str));
+ DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
+ if (new_name)
+ DBUG_PRINT("info", ("rename to: %s", new_name->m_name.str));
+
+ /* first look whether we overwrite */
+ if (new_name)
+ {
+ if (!sortcmp_lex_string(parse_data->name, new_name->m_name, scs) &&
+ !sortcmp_lex_string(parse_data->dbname, new_name->m_db, scs))
+ {
+ my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ if (!find_event_by_name(thd, new_name->m_db, new_name->m_name, table))
+ {
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
+ goto err;
+ }
+ }
+ /*
+ ...and then if there is such an event. Don't exchange the blocks
+ because you will get error 120 from table handler because new_name will
+ overwrite the key and SE will tell us that it cannot find the already found
+ row (copied into record[1] later
+ */
+ if (EVEX_KEY_NOT_FOUND == find_event_by_name(thd, parse_data->dbname,
+ parse_data->name, table))
+ {
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
+ goto err;
+ }
+
+ store_record(table,record[1]);
+
+ /* Don't update create on row update. */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ /*
+ mysql_event_fill_row() calls my_error() in case of error so no need to
+ handle it here
+ */
+ if ((ret= mysql_event_fill_row(thd, table, parse_data, TRUE)))
+ goto err;
+
+ if (new_name)
+ {
+ table->field[ET_FIELD_DB]->
+ store(new_name->m_db.str, new_name->m_db.length, scs);
+ table->field[ET_FIELD_NAME]->
+ store(new_name->m_name.str, new_name->m_name.length, scs);
+ }
+
+ /* Close active transaction only if We are going to modify disk */
+ if (end_active_trans(thd))
+ goto err;
+
+ if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), parse_data->name.str, ret);
+ goto err;
+ }
+
+ /* close mysql.event or we crash later when loading the event from disk */
+ close_thread_tables(thd);
+ DBUG_RETURN(0);
+
+err:
+ if (table)
+ close_thread_tables(thd);
+ DBUG_RETURN(EVEX_GENERAL_ERROR);
+}
+
+
+/*
+ Drops an event
+
+ SYNOPSIS
+ Event_db_repository::drop_event()
+ thd [in] THD
+ db [in] Database name
+ name [in] Event's name
+ drop_if_exists [in] If set and the event not existing => warning
+ onto the stack
+ rows_affected [out] Affected number of rows is returned heres
+
+ RETURN VALUE
+ 0 OK
+ !0 Error (my_error() called)
+*/
+
+int
+Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool drop_if_exists, uint *rows_affected)
+{
+ TABLE *table;
+ Open_tables_state backup;
+ int ret;
+
+ DBUG_ENTER("Event_db_repository::drop_event");
+ DBUG_PRINT("enter", ("db=%s name=%s", db.str, name.str));
+ ret= EVEX_OPEN_TABLE_FAILED;
+
+ thd->reset_n_backup_open_tables_state(&backup);
+ if (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ goto done;
+ }
+
+ switch ((ret= find_event_by_name(thd, db, name, table))) {
+ case 0:
+ /* Close active transaction only if we are actually going to modify disk */
+ if ((ret= end_active_trans(thd)))
+ break;
+
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
+ break;
+ case EVEX_KEY_NOT_FOUND:
+ if (drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ "Event", name.str);
+ ret= 0;
+ } else
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ break;
+ default:
+ ;
+ }
+
+done:
+ close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(&backup);
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Positions the internal pointer of `table` to the place where (db, name)
+ is stored.
+
+ SYNOPSIS
+ Event_db_repository::find_event_by_name()
+ thd Thread
+ db Schema
+ name Event name
+ table Opened mysql.event
+
+ RETURN VALUE
+ 0 OK
+ EVEX_KEY_NOT_FOUND No such event
+*/
+
+int
+Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db,
+ LEX_STRING name, TABLE *table)
+{
+ byte key[MAX_KEY_LENGTH];
+ DBUG_ENTER("Event_db_repository::find_event_by_name");
+ DBUG_PRINT("enter", ("name: %.*s", name.length, name.str));
+
+ /*
+ Create key to find row. We have to use field->store() to be able to
+ handle VARCHAR and CHAR fields.
+ Assumption here is that the two first fields in the table are
+ 'db' and 'name' and the first key is the primary key over the
+ same fields.
+ */
+ if (db.length > table->field[ET_FIELD_DB]->field_length ||
+ name.length > table->field[ET_FIELD_NAME]->field_length)
+ DBUG_RETURN(EVEX_KEY_NOT_FOUND);
+
+ table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin);
+ table->field[ET_FIELD_NAME]->store(name.str, 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,
+ table->key_info->key_length,
+ HA_READ_KEY_EXACT))
+ {
+ DBUG_PRINT("info", ("Row not found"));
+ DBUG_RETURN(EVEX_KEY_NOT_FOUND);
+ }
+
+ DBUG_PRINT("info", ("Row found!"));
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Drops all events in the selected database, from mysql.event.
+
+ SYNOPSIS
+ Event_db_repository::drop_schema_events()
+ thd Thread
+ schema The database to clean from events
+
+ RETURN VALUE
+ 0 OK
+ !0 Error (Reported)
+*/
+
+int
+Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
+{
+ return drop_events_by_field(thd, ET_FIELD_DB, schema);
+}
+
+
+/*
+ Drops all events by field which has specific value of the field
+
+ SYNOPSIS
+ Event_db_repository::drop_events_by_field()
+ thd Thread
+ table mysql.event TABLE
+ field Which field of the row to use for matching
+ field_value The value that should match
+
+ RETURN VALUE
+ 0 OK
+ !0 Error from ha_delete_row
+*/
+
+int
+Event_db_repository::drop_events_by_field(THD *thd,
+ enum enum_events_table_field field,
+ LEX_STRING field_value)
+{
+ int ret= 0;
+ TABLE *table;
+ Open_tables_state backup;
+ READ_RECORD read_record_info;
+ DBUG_ENTER("Event_db_repository::drop_events_by_field");
+ DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ /* only enabled events are in memory, so we go now and delete the rest */
+ init_read_record(&read_record_info, thd, table, NULL, 1, 0);
+ while (!ret && !(read_record_info.read_record(&read_record_info)) )
+ {
+ char *et_field= get_field(thd->mem_root, table->field[field]);
+
+ LEX_STRING et_field_lex= {et_field, strlen(et_field)};
+ DBUG_PRINT("info", ("Current event %s name=%s", et_field,
+ get_field(thd->mem_root, table->field[ET_FIELD_NAME])));
+
+ if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
+ {
+ DBUG_PRINT("info", ("Dropping"));
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ my_error(ER_EVENT_DROP_FAILED, MYF(0),
+ get_field(thd->mem_root, table->field[ET_FIELD_NAME]));
+ }
+ }
+ end_read_record(&read_record_info);
+ thd->version--; /* Force close to free memory */
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Looks for a named event in mysql.event and in case of success returns
+ an object will data loaded from the table.
+
+ SYNOPSIS
+ Event_db_repository::find_event()
+ thd [in] THD
+ name [in] The name of the event to find
+ ett [out] Event's data if event is found
+ tbl [in] TABLE object to use when not NULL
+
+ NOTES
+ 1) Use sp_name for look up, return in **ett if found
+ 2) tbl is not closed at exit
+
+ RETURN VALUE
+ 0 ok In this case *ett is set to the event
+ # error *ett == 0
+*/
+
+int
+Event_db_repository::find_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ Event_basic *et)
+{
+ TABLE *table;
+ int ret;
+ DBUG_ENTER("Event_db_repository::find_event");
+ DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
+
+ if (open_event_table(thd, TL_READ, &table))
+ {
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ ret= EVEX_GENERAL_ERROR;
+ goto done;
+ }
+
+ if ((ret= find_event_by_name(thd, dbname, name, table)))
+ {
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ goto done;
+ }
+ /*
+ 1)The table should not be closed beforehand. ::load_from_row() only loads
+ and does not compile
+
+ 2)::load_from_row() is silent on error therefore we emit error msg here
+ */
+ if ((ret= et->load_from_row(table)))
+ {
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+ goto done;
+ }
+
+done:
+ if (table)
+ close_thread_tables(thd);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Looks for a named event in mysql.event and then loads it from
+ the table, compiles and inserts it into the cache.
+
+ SYNOPSIS
+ Event_db_repository::load_named_event()
+ thd [in] THD
+ dbname [in] Event's db name
+ name [in] Event's name
+ etn_new [out] The loaded event
+
+ RETURN VALUE
+ OP_OK OK
+ OP_LOAD_ERROR Error during loading from disk
+*/
+
+int
+Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
+ LEX_STRING name, Event_basic *etn)
+{
+ int ret= 0;
+ Open_tables_state backup;
+
+ DBUG_ENTER("Event_db_repository::load_named_event");
+ DBUG_PRINT("enter",("thd=0x%lx name:%*s",thd, name.length, name.str));
+
+ thd->reset_n_backup_open_tables_state(&backup);
+ /* No need to use my_error() here because find_event() has done it */
+ ret= find_event(thd, dbname, name, etn);
+ thd->restore_backup_open_tables_state(&backup);
+ /* In this case no memory was allocated so we don't need to clean */
+ if (ret)
+ DBUG_RETURN(OP_LOAD_ERROR);
+
+ DBUG_RETURN(OP_OK);
+}
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
new file mode 100644
index 00000000000..d13538ce10b
--- /dev/null
+++ b/sql/event_db_repository.h
@@ -0,0 +1,107 @@
+#ifndef _EVENT_DB_REPOSITORY_H_
+#define _EVENT_DB_REPOSITORY_H_
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+enum enum_events_table_field
+{
+ ET_FIELD_DB = 0,
+ ET_FIELD_NAME,
+ ET_FIELD_BODY,
+ ET_FIELD_DEFINER,
+ ET_FIELD_EXECUTE_AT,
+ ET_FIELD_INTERVAL_EXPR,
+ ET_FIELD_TRANSIENT_INTERVAL,
+ ET_FIELD_CREATED,
+ ET_FIELD_MODIFIED,
+ ET_FIELD_LAST_EXECUTED,
+ ET_FIELD_STARTS,
+ ET_FIELD_ENDS,
+ ET_FIELD_STATUS,
+ ET_FIELD_ON_COMPLETION,
+ ET_FIELD_SQL_MODE,
+ ET_FIELD_COMMENT,
+ ET_FIELD_COUNT /* a cool trick to count the number of fields :) */
+};
+
+
+int
+events_table_index_read_for_db(THD *thd, TABLE *schema_table,
+ TABLE *event_table);
+
+int
+events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+int
+fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
+
+class Event_basic;
+class Event_parse_data;
+
+class Event_db_repository
+{
+public:
+ Event_db_repository(){}
+
+ int
+ create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not,
+ uint *rows_affected);
+
+ int
+ update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name);
+
+ int
+ drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists,
+ uint *rows_affected);
+
+ int
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ int
+ find_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
+
+ int
+ load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
+
+ int
+ find_event_by_name(THD *thd, LEX_STRING db, LEX_STRING name, TABLE *table);
+
+ int
+ open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
+
+ int
+ fill_schema_events(THD *thd, TABLE_LIST *tables, char *db);
+
+private:
+ int
+ drop_events_by_field(THD *thd, enum enum_events_table_field field,
+ LEX_STRING field_value);
+ int
+ index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
+ char *db);
+
+ int
+ table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+ static bool
+ check_system_tables(THD *thd);
+
+ /* Prevent use of these */
+ Event_db_repository(const Event_db_repository &);
+ void operator=(Event_db_repository &);
+};
+
+#endif /* _EVENT_DB_REPOSITORY_H_ */
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
new file mode 100644
index 00000000000..bd8809ba708
--- /dev/null
+++ b/sql/event_queue.cc
@@ -0,0 +1,945 @@
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "events.h"
+#include "event_scheduler_ng.h"
+#include "event_queue.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "sp_head.h"
+
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#define SCHED_FUNC __FUNCTION__
+#endif
+#else
+#define SCHED_FUNC "<unknown>"
+#endif
+
+#define LOCK_QUEUE_DATA() lock_data(SCHED_FUNC, __LINE__)
+#define UNLOCK_QUEUE_DATA() unlock_data(SCHED_FUNC, __LINE__)
+
+
+/*
+ Compares the execute_at members of 2 Event_queue_element instances.
+ Used as callback for the prioritized queue when shifting
+ elements inside.
+
+ SYNOPSIS
+ event_queue_element_data_compare_q()
+
+ vptr - not used (set it to NULL)
+ a - first Event_queue_element object
+ b - second Event_queue_element object
+
+ RETURN VALUE
+ -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
+*/
+
+static int
+event_queue_element_compare_q(void *vptr, byte* a, byte *b)
+{
+ return my_time_compare(&((Event_queue_element *)a)->execute_at,
+ &((Event_queue_element *)b)->execute_at);
+}
+
+
+/*
+ Constructor of class Event_queue.
+
+ SYNOPSIS
+ Event_queue::Event_queue()
+*/
+
+Event_queue::Event_queue()
+{
+ mutex_last_unlocked_at_line= mutex_last_locked_at_line= 0;
+ mutex_last_unlocked_in_func= mutex_last_locked_in_func= "";
+ mutex_queue_data_locked= FALSE;
+}
+
+
+/*
+ Inits mutexes.
+
+ SYNOPSIS
+ Event_queue::init_mutexes()
+*/
+
+void
+Event_queue::init_mutexes()
+{
+ pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
+}
+
+
+/*
+ Destroys mutexes.
+
+ SYNOPSIS
+ Event_queue::deinit_mutexes()
+*/
+
+void
+Event_queue::deinit_mutexes()
+{
+ pthread_mutex_destroy(&LOCK_event_queue);
+}
+
+
+/*
+ Signals the main scheduler thread that the queue has changed
+ its state.
+
+ SYNOPSIS
+ Event_queue::notify_observers()
+*/
+
+void
+Event_queue::notify_observers()
+{
+ DBUG_ENTER("Event_queue::notify_observers");
+ DBUG_PRINT("info", ("Signalling change of the queue"));
+ scheduler->queue_changed();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Inits the queue
+
+ SYNOPSIS
+ Event_queue::init()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_queue::init_queue(Event_db_repository *db_repo, Event_scheduler_ng *sched)
+{
+ int i= 0;
+ bool ret= FALSE;
+ DBUG_ENTER("Event_queue::init_queue");
+ DBUG_PRINT("enter", ("this=0x%lx", this));
+
+ LOCK_QUEUE_DATA();
+ db_repository= db_repo;
+ scheduler= sched;
+
+ if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
+ event_queue_element_compare_q, NULL, 30 /*auto_extent*/))
+ {
+ sql_print_error("SCHEDULER: Can't initialize the execution queue");
+ ret= TRUE;
+ goto end;
+ }
+
+ if (sizeof(my_time_t) != sizeof(time_t))
+ {
+ sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ."
+ "The scheduler may not work correctly. Stopping.");
+ DBUG_ASSERT(0);
+ ret= TRUE;
+ goto end;
+ }
+
+end:
+ UNLOCK_QUEUE_DATA();
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Deinits the queue
+
+ SYNOPSIS
+ Event_queue::deinit_queue()
+*/
+
+void
+Event_queue::deinit_queue()
+{
+ DBUG_ENTER("Event_queue::deinit_queue");
+
+ LOCK_QUEUE_DATA();
+ empty_queue();
+ delete_queue(&queue);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Creates an event in the scheduler queue
+
+ SYNOPSIS
+ Event_queue::create_event()
+ et The event to add
+ check_existence Whether to check if already loaded.
+
+ RETURN VALUE
+ OP_OK OK or scheduler not working
+ OP_LOAD_ERROR Error during loading from disk
+*/
+
+int
+Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ int res;
+ Event_queue_element *element_new;
+ DBUG_ENTER("Event_queue::create_event");
+ DBUG_PRINT("enter", ("thd=0x%lx et=%s.%s",thd, dbname.str, name.str));
+
+ element_new= new Event_queue_element();
+ res= db_repository->load_named_event(thd, dbname, name, element_new);
+ if (res || element_new->status == Event_queue_element::DISABLED)
+ delete element_new;
+ else
+ {
+ element_new->compute_next_execution_time();
+
+ LOCK_QUEUE_DATA();
+ DBUG_PRINT("info", ("new event in the queue 0x%lx", element_new));
+ queue_insert_safe(&queue, (byte *) element_new);
+ UNLOCK_QUEUE_DATA();
+
+ notify_observers();
+ }
+
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Updates an event from the scheduler queue
+
+ SYNOPSIS
+ Event_queue::update_event()
+ thd Thread
+ dbname Schema of the event
+ name Name of the event
+ new_schema New schema, in case of RENAME TO, otherwise NULL
+ new_name New name, in case of RENAME TO, otherwise NULL
+
+ RETURN VALUE
+ OP_OK OK or scheduler not working
+ OP_LOAD_ERROR Error during loading from disk
+*/
+
+int
+Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ LEX_STRING *new_schema, LEX_STRING *new_name)
+{
+ int res;
+ Event_queue_element *element_old= NULL,
+ *element_new;
+
+ DBUG_ENTER("Event_queue::update_event");
+ DBUG_PRINT("enter", ("thd=0x%lx et=[%s.%s]", thd, dbname.str, name.str));
+
+ element_new= new Event_queue_element();
+
+ res= db_repository->load_named_event(thd, new_schema? *new_schema:dbname,
+ new_name? *new_name:name, element_new);
+ if (res)
+ {
+ delete element_new;
+ goto end;
+ }
+ else if (element_new->status == Event_queue_element::DISABLED)
+ {
+ DBUG_PRINT("info", ("The event is disabled."));
+ /*
+ Destroy the object but don't skip to end: because we may have to remove
+ object from the cache.
+ */
+ delete element_new;
+ element_new= NULL;
+ }
+ else
+ element_new->compute_next_execution_time();
+
+ LOCK_QUEUE_DATA();
+ if (!(element_old= find_event(dbname, name, TRUE)))
+ {
+ DBUG_PRINT("info", ("%s.%s not cached, probably was DISABLED",
+ dbname.str, name.str));
+ }
+ /* If not disabled event */
+ if (element_new)
+ {
+ DBUG_PRINT("info", ("new event in the Q 0x%lx old 0x%lx",
+ element_new, element_old));
+ queue_insert_safe(&queue, (byte *) element_new);
+ }
+ UNLOCK_QUEUE_DATA();
+
+ notify_observers();
+
+ if (element_old)
+ delete element_old;
+end:
+ DBUG_PRINT("info", ("res=%d", res));
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Drops an event from the queue
+
+ SYNOPSIS
+ Event_queue::drop_event()
+ thd Thread
+ dbname Schema of the event to drop
+ name Name of the event to drop
+*/
+
+void
+Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ int res;
+ Event_queue_element *element;
+ DBUG_ENTER("Event_queue::drop_event");
+ DBUG_PRINT("enter", ("thd=0x%lx name=0x%lx", thd, name));
+
+ LOCK_QUEUE_DATA();
+ element= find_event(dbname, name, TRUE);
+ UNLOCK_QUEUE_DATA();
+
+ if (element)
+ delete element;
+ else
+ DBUG_PRINT("info", ("No such event found, probably DISABLED"));
+
+ /*
+ We don't signal here because the scheduler will catch the change
+ next time it wakes up.
+ */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Searches for an event in the queue
+
+ SYNOPSIS
+ Event_queue::find_event()
+ db The schema of the event to find
+ name The event to find
+ remove_from_q If found whether to remove from the Q
+
+ RETURN VALUE
+ NULL Not found
+ otherwise Address
+
+ NOTE
+ The caller should do the locking also the caller is responsible for
+ actual signalling in case an event is removed from the queue
+ (signalling COND_new_work for instance).
+*/
+
+Event_queue_element *
+Event_queue::find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q)
+{
+ uint i;
+ DBUG_ENTER("Event_queue::find_event");
+
+ for (i= 0; i < queue.elements; ++i)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
+ et->dbname.str, et->name.str));
+ if (event_basic_identifier_equal(db, name, et))
+ {
+ if (remove_from_q)
+ queue_remove(&queue, i);
+ DBUG_RETURN(et);
+ }
+ }
+
+ DBUG_RETURN(NULL);
+}
+
+
+/*
+ Drops all events from the in-memory queue and disk that match
+ certain pattern evaluated by a comparator function
+
+ SYNOPSIS
+ Event_queue::drop_matching_events()
+ thd THD
+ pattern A pattern string
+ comparator The function to use for comparing
+
+ RETURN VALUE
+ >=0 Number of dropped events
+
+ NOTE
+ Expected is the caller to acquire lock on LOCK_event_queue
+*/
+
+void
+Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
+ bool (*comparator)(LEX_STRING *, Event_basic *))
+{
+ DBUG_ENTER("Event_queue::drop_matching_events");
+ DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str));
+
+ uint i= 0;
+ while (i < queue.elements)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
+ if (comparator(&pattern, et))
+ {
+ /*
+ The queue is ordered. If we remove an element, then all elements after
+ it will shift one position to the left, if we imagine it as an array
+ from left to the right. In this case we should not increment the
+ counter and the (i < queue.elements) condition is ok.
+ */
+ queue_remove(&queue, i);
+ delete et;
+ }
+ else
+ i++;
+ }
+ /*
+ We don't call notify_observers() . If we remove the top event:
+ 1. The queue is empty. The scheduler will wake up at some time and realize
+ that the queue is empty. If create_event() comes inbetween it will
+ signal the scheduler
+ 2. The queue is not empty, but the next event after the previous top, won't
+ be executed any time sooner than the element we removed. Hence, we may
+ not notify the scheduler and it will realize the change when it
+ wakes up from timedwait.
+ */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Drops all events from the in-memory queue and disk that are from
+ certain schema.
+
+ SYNOPSIS
+ Event_queue::drop_schema_events()
+ thd THD
+ db The schema name
+
+ RETURN VALUE
+ >=0 Number of dropped events
+*/
+
+void
+Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
+{
+ DBUG_ENTER("Event_queue::drop_schema_events");
+ LOCK_QUEUE_DATA();
+ drop_matching_events(thd, schema, event_basic_db_equal);
+ UNLOCK_QUEUE_DATA();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Returns the number of elements in the queue
+
+ SYNOPSIS
+ Event_queue::events_count()
+
+ RETURN VALUE
+ Number of Event_queue_element objects in the queue
+*/
+
+uint
+Event_queue::events_count()
+{
+ uint n;
+ DBUG_ENTER("Event_scheduler::events_count");
+ LOCK_QUEUE_DATA();
+ n= queue.elements;
+ UNLOCK_QUEUE_DATA();
+ DBUG_PRINT("info", ("n=%u", n));
+ DBUG_RETURN(n);
+}
+
+
+/*
+ Loads all ENABLED events from mysql.event into the prioritized
+ queue. Called during scheduler main thread initialization. Compiles
+ the events. Creates Event_queue_element instances for every ENABLED event
+ from mysql.event.
+
+ SYNOPSIS
+ Event_queue::load_events_from_db()
+ thd - Thread context. Used for memory allocation in some cases.
+
+ RETURN VALUE
+ 0 OK
+ !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
+ EVEX_COMPILE_ERROR) - in all these cases mysql.event was
+ tampered.
+
+ NOTES
+ Reports the error to the console
+*/
+
+int
+Event_queue::load_events_from_db(THD *thd)
+{
+ TABLE *table;
+ READ_RECORD read_record_info;
+ int ret= -1;
+ uint count= 0;
+ bool clean_the_queue= FALSE;
+ /* Compile the events on this root but only for syntax check, then discard */
+ MEM_ROOT boot_root;
+
+ DBUG_ENTER("Event_queue::load_events_from_db");
+ DBUG_PRINT("enter", ("thd=0x%lx", thd));
+
+ if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
+ {
+ sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open.");
+ DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
+ }
+
+ init_read_record(&read_record_info, thd, table ,NULL,1,0);
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ Event_queue_element *et;
+ if (!(et= new Event_queue_element))
+ {
+ DBUG_PRINT("info", ("Out of memory"));
+ clean_the_queue= TRUE;
+ break;
+ }
+ DBUG_PRINT("info", ("Loading event from row."));
+
+ if ((ret= et->load_from_row(table)))
+ {
+ clean_the_queue= TRUE;
+ sql_print_error("SCHEDULER: Error while loading from mysql.event. "
+ "Table probably corrupted");
+ break;
+ }
+ if (et->status != Event_queue_element::ENABLED)
+ {
+ DBUG_PRINT("info",("%s is disabled",et->name.str));
+ delete et;
+ continue;
+ }
+#if 0
+ init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
+
+ /* We load only on scheduler root just to check whether the body compiles */
+ switch (ret= et->compile(thd, &boot_root)) {
+ case EVEX_MICROSECOND_UNSUP:
+ et->free_sp();
+ sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
+ "supported but found in mysql.event");
+ goto end;
+ case EVEX_COMPILE_ERROR:
+ sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.",
+ et->dbname.str, et->name.str);
+ goto end;
+ default:
+ /* Free it, it will be compiled again on the worker thread */
+ et->free_sp();
+ break;
+ }
+ free_root(&boot_root, MYF(0));
+
+ /* let's find when to be executed */
+ if (et->compute_next_execution_time())
+ {
+ sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
+ " Skipping", et->dbname.str, et->name.str);
+ continue;
+ }
+#endif
+ DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list."));
+ queue_insert_safe(&queue, (byte *) et);
+ count++;
+ }
+end:
+ end_read_record(&read_record_info);
+
+ if (clean_the_queue)
+ {
+ empty_queue();
+ ret= -1;
+ }
+ else
+ {
+ ret= 0;
+ sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s");
+ }
+
+ /* Force close to free memory */
+ thd->version--;
+
+ close_thread_tables(thd);
+
+ DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ 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
+ Event_queue::check_system_tables()
+*/
+
+bool
+Event_queue::check_system_tables(THD *thd)
+{
+ TABLE_LIST tables;
+ bool not_used;
+ Open_tables_state backup;
+ bool ret;
+
+ DBUG_ENTER("Event_queue::check_system_tables");
+ DBUG_PRINT("enter", ("thd=0x%lx", thd));
+
+ thd->reset_n_backup_open_tables_state(&backup);
+
+ bzero((char*) &tables, sizeof(tables));
+ tables.db= (char*) "mysql";
+ tables.table_name= tables.alias= (char*) "db";
+ tables.lock_type= TL_READ;
+
+ if ((ret= simple_open_n_lock_tables(thd, &tables)))
+ sql_print_error("Cannot open mysql.db");
+ else
+ {
+ ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
+ mysql_db_table_fields, &mysql_db_table_last_check,
+ ER_CANNOT_LOAD_FROM_TABLE);
+ close_thread_tables(thd);
+ }
+ if (ret)
+ DBUG_RETURN(TRUE);
+
+ bzero((char*) &tables, sizeof(tables));
+ tables.db= (char*) "mysql";
+ tables.table_name= tables.alias= (char*) "user";
+ tables.lock_type= TL_READ;
+
+ if ((ret= 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");
+ ret= TRUE;
+ }
+ close_thread_tables(thd);
+ }
+
+ thd->restore_backup_open_tables_state(&backup);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Recalculates activation times in the queue. There is one reason for
+ that. Because the values (execute_at) by which the queue is ordered are
+ changed by calls to compute_next_execution_time() on a request from the
+ scheduler thread, if it is not running then the values won't be updated.
+ Once the scheduler is started again the values has to be recalculated
+ so they are right for the current time.
+
+ SYNOPSIS
+ Event_queue::recalculate_activation_times()
+ thd Thread
+*/
+
+void
+Event_queue::recalculate_activation_times(THD *thd)
+{
+ uint i;
+ DBUG_ENTER("Event_queue::recalculate_activation_times");
+
+ LOCK_QUEUE_DATA();
+ DBUG_PRINT("info", ("%u loaded events to be recalculated", queue.elements));
+ for (i= 0; i < queue.elements; i++)
+ {
+ ((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
+ ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
+ }
+ queue_fix(&queue);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Empties the queue and destroys the Event_queue_element objects in the
+ queue.
+
+ SYNOPSIS
+ Event_queue::empty_queue()
+
+ NOTE
+ Should be called with LOCK_event_queue locked
+*/
+
+void
+Event_queue::empty_queue()
+{
+ uint i;
+ DBUG_ENTER("Event_queue::empty_queue");
+ DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements));
+ /* empty the queue */
+ for (i= 0; i < queue.elements; ++i)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ delete et;
+ }
+ resize_queue(&queue, 0);
+ DBUG_VOID_RETURN;
+}
+
+
+inline void
+Event_queue::dbug_dump_queue(time_t now)
+{
+#ifndef DBUG_OFF
+ Event_queue_element *et;
+ uint i;
+ DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements));
+ for (i = 0; i < queue.elements; i++)
+ {
+ et= ((Event_queue_element*)queue_element(&queue, i));
+ DBUG_PRINT("info",("et=0x%lx db=%s name=%s",et, et->dbname.str, et->name.str));
+ DBUG_PRINT("info", ("exec_at=%llu starts=%llu ends=%llu "
+ " expr=%lld et.exec_at=%d now=%d (et.exec_at - now)=%d if=%d",
+ TIME_to_ulonglong_datetime(&et->execute_at),
+ TIME_to_ulonglong_datetime(&et->starts),
+ TIME_to_ulonglong_datetime(&et->ends),
+ et->expression, sec_since_epoch_TIME(&et->execute_at), now,
+ (int)(sec_since_epoch_TIME(&et->execute_at) - now),
+ sec_since_epoch_TIME(&et->execute_at) <= now));
+ }
+#endif
+}
+
+Event_job_data *
+Event_queue::get_top_for_execution_if_time(THD *thd, time_t now,
+ struct timespec *abstime)
+{
+ struct timespec top_time;
+ Event_job_data *et_new= NULL;
+ DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
+ DBUG_PRINT("enter", ("thd=0x%lx now=%d", thd, now));
+ abstime->tv_nsec= 0;
+ LOCK_QUEUE_DATA();
+ do {
+ int res;
+ if (!queue.elements)
+ {
+ abstime->tv_sec= 0;
+ break;
+ }
+ dbug_dump_queue(now);
+
+ Event_queue_element *et= ((Event_queue_element*) queue_element(&queue, 0));
+ top_time.tv_sec= sec_since_epoch_TIME(&et->execute_at);
+
+ if (top_time.tv_sec <= now)
+ {
+ DBUG_PRINT("info", ("Ready for execution"));
+ abstime->tv_sec= 0;
+ et_new= new Event_job_data();
+ if ((res= db_repository->load_named_event(thd, et->dbname, et->name,
+ et_new)))
+ {
+ delete et_new;
+ et_new= NULL;
+ DBUG_ASSERT(0);
+ break;
+ }
+
+ et->mark_last_executed(thd);
+ if (et->compute_next_execution_time())
+ et->status= Event_queue_element::DISABLED;
+ DBUG_PRINT("info", ("event's status is %d", et->status));
+
+ et->update_timing_fields(thd);
+ if (((et->execute_at.year && !et->expression) || et->execute_at_null) ||
+ (et->status == Event_queue_element::DISABLED))
+ {
+ DBUG_PRINT("info", ("removing from the queue"));
+ if (et->dropped)
+ et->drop(thd);
+ delete et;
+ queue_remove(&queue, 0);
+ }
+ else
+ queue_replaced(&queue);
+ }
+ else
+ {
+ abstime->tv_sec= top_time.tv_sec;
+ DBUG_PRINT("info", ("Have to wait %d till %d", abstime->tv_sec - now,
+ abstime->tv_sec));
+ }
+ } while (0);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_PRINT("info", ("returning. et_new=0x%lx abstime.tv_sec=%d ", et_new,
+ abstime->tv_sec));
+ if (et_new)
+ DBUG_PRINT("info", ("db=%s name=%s definer=%s",
+ et_new->dbname.str, et_new->name.str, et_new->definer.str));
+ DBUG_RETURN(et_new);
+}
+
+
+/*
+ Auxiliary function for locking LOCK_event_queue. Used by the
+ LOCK_QUEUE_DATA macro
+
+ SYNOPSIS
+ Event_queue::lock_data()
+ func Which function is requesting mutex lock
+ line On which line mutex lock is requested
+*/
+
+void
+Event_queue::lock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::lock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ pthread_mutex_lock(&LOCK_event_queue);
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_queue_data_locked= TRUE;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Auxiliary function for unlocking LOCK_event_queue. Used by the
+ UNLOCK_QUEUE_DATA macro
+
+ SYNOPSIS
+ Event_queue::unlock_data()
+ func Which function is requesting mutex unlock
+ line On which line mutex unlock is requested
+*/
+
+void
+Event_queue::unlock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::unlock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ mutex_last_unlocked_at_line= line;
+ mutex_queue_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ pthread_mutex_unlock(&LOCK_event_queue);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the internal status of the queue
+
+ SYNOPSIS
+ Event_queue::dump_internal_status()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_queue::dump_internal_status(THD *thd)
+{
+ DBUG_ENTER("Event_queue::dump_internal_status");
+#ifndef DBUG_OFF
+ CHARSET_INFO *scs= system_charset_info;
+ Protocol *protocol= thd->protocol;
+ List<Item> field_list;
+ int ret;
+ char tmp_buff[5*STRING_BUFFER_USUAL_SIZE];
+ char int_buff[STRING_BUFFER_USUAL_SIZE];
+ String tmp_string(tmp_buff, sizeof(tmp_buff), scs);
+ String int_string(int_buff, sizeof(int_buff), scs);
+ tmp_string.length(0);
+ int_string.length(0);
+
+ /* workers_count */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("queue element count"), scs);
+ int_string.set((longlong) queue.elements, scs);
+ protocol->store(&int_string);
+ ret= protocol->write();
+
+ /* queue_data_locked */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("queue data locked"), scs);
+ int_string.set((longlong) mutex_queue_data_locked, scs);
+ protocol->store(&int_string);
+ ret= protocol->write();
+
+ /* last locked at*/
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("queue last locked at"), scs);
+ tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
+ tmp_string.alloced_length(), "%s::%d",
+ mutex_last_locked_in_func,
+ mutex_last_locked_at_line));
+ protocol->store(&tmp_string);
+ ret= protocol->write();
+
+ /* last unlocked at*/
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("queue last unlocked at"), scs);
+ tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
+ tmp_string.alloced_length(), "%s::%d",
+ mutex_last_unlocked_in_func,
+ mutex_last_unlocked_at_line));
+ protocol->store(&tmp_string);
+ ret= protocol->write();
+#endif
+ DBUG_RETURN(FALSE);
+}
diff --git a/sql/event_queue.h b/sql/event_queue.h
new file mode 100644
index 00000000000..142a866e5ba
--- /dev/null
+++ b/sql/event_queue.h
@@ -0,0 +1,119 @@
+#ifndef _EVENT_QUEUE_H_
+#define _EVENT_QUEUE_H_
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+class sp_name;
+class Event_basic;
+class Event_db_repository;
+class Event_job_data;
+class Event_queue_element;
+
+class THD;
+class Event_scheduler_ng;
+
+class Event_queue
+{
+public:
+ Event_queue();
+
+ void
+ init_mutexes();
+
+ void
+ deinit_mutexes();
+
+ bool
+ init_queue(Event_db_repository *db_repo, Event_scheduler_ng *sched);
+
+ void
+ deinit_queue();
+
+ /* Methods for queue management follow */
+
+ int
+ create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+
+ int
+ update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ LEX_STRING *new_schema, LEX_STRING *new_name);
+
+ void
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+
+ void
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ uint
+ events_count();
+
+ static bool
+ check_system_tables(THD *thd);
+
+ void
+ recalculate_activation_times(THD *thd);
+
+ Event_job_data *
+ get_top_for_execution_if_time(THD *thd, time_t now, struct timespec *abstime);
+
+ bool
+ dump_internal_status(THD *thd);
+
+protected:
+ Event_queue_element *
+ find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q);
+
+ int
+ load_events_from_db(THD *thd);
+
+ void
+ drop_matching_events(THD *thd, LEX_STRING pattern,
+ bool (*)(LEX_STRING *, Event_basic *));
+
+ void
+ empty_queue();
+
+ /* LOCK_event_queue is the mutex which protects the access to the queue. */
+ pthread_mutex_t LOCK_event_queue;
+
+ Event_db_repository *db_repository;
+
+ uint mutex_last_locked_at_line;
+ uint mutex_last_unlocked_at_line;
+ const char* mutex_last_locked_in_func;
+ const char* mutex_last_unlocked_in_func;
+ bool mutex_queue_data_locked;
+
+ /* helper functions for working with mutexes & conditionals */
+ void
+ lock_data(const char *func, uint line);
+
+ void
+ unlock_data(const char *func, uint line);
+
+ void
+ notify_observers();
+
+ void
+ dbug_dump_queue(time_t now);
+
+ Event_scheduler_ng *scheduler;
+
+ /* The sorted queue with the Event_job_data objects */
+ QUEUE queue;
+};
+
+#endif /* _EVENT_QUEUE_H_ */
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 1b4a0d290e6..f1c7d8394e3 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -14,2450 +14,3 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include "mysql_priv.h"
-#include "events_priv.h"
-#include "events.h"
-#include "event_timed.h"
-#include "event_scheduler.h"
-#include "sp_head.h"
-
-/*
- ToDo:
- 1. Talk to Alik to get a check for configure.in for my_time_t and time_t
- 2. Look at guardian.h|cc to see its life cycle, has similarities.
-*/
-
-
-/*
- The scheduler is implemented as class Event_scheduler. Only one instance is
- kept during the runtime of the server, by implementing the Singleton DP.
- Object instance is always there because the memory is allocated statically
- and initialized when the OS loader loads mysqld. This initialization is
- bare. Extended initialization is done during the call to
- Event_scheduler::init() in Events::init(). The reason for that late initialization
- is that some subsystems needed to boot the Scheduler are not available at
- earlier stages of the mysqld boot procedure. Events::init() is called in
- mysqld.cc . If the mysqld is started with --event-scheduler=0 then
- no initialization takes place and the scheduler is unavailable during this
- server run. The server should be started with --event-scheduler=1 to have
- the scheduler initialized and able to execute jobs. This starting alwa
- s implies that the jobs execution will start immediately. If the server
- is started with --event-scheduler=2 then the scheduler is started in suspended
- state. Default state, if --event-scheduler is not specified is 2.
-
- The scheduler only manages execution of the events. Their creation,
- alteration and deletion is delegated to other routines found in event.cc .
- These routines interact with the scheduler :
- - CREATE EVENT -> Event_scheduler::create_event()
- - ALTER EVENT -> Event_scheduler::update_event()
- - DROP EVENT -> Event_scheduler::drop_event()
-
- There is one mutex in the single Event_scheduler object which controls
- the simultaneous access to the objects invariants. Using one lock makes
- it easy to follow the workflow. This mutex is LOCK_scheduler_data. It is
- initialized in Event_scheduler::init(). Which in turn is called by the
- Facade class Events in event.cc, coming from init_thread_environment() from
- mysqld.cc -> no concurrency at this point. It's destroyed in
- Events::destroy_mutexes() called from clean_up_mutexes() in mysqld.cc .
-
- The full initialization is done in Event_scheduler::init() called from
- Events::init(). It's done before any requests coming in, so this is a
- guarantee for not having concurrency.
-
- The scheduler is started with Event_scheduler::start() and stopped with
- Event_scheduler::stop(). When the scheduler starts it loads all events
- from mysql.event table. Unfortunately, there is a race condition between
- the event disk management functions and the scheduler ones
- (add/replace/drop_event & load_events_from_db()), because the operations
- do not happen under one global lock but the disk operations are guarded
- by the MYISAM lock on mysql.event. In the same time, the queue operations
- are guarded by LOCK_scheduler_data. If the scheduler is start()-ed during
- server startup and stopped()-ed during server shutdown (in Events::shutdown()
- called by kill_server() in mysqld.cc) these races does not exist.
-
- Since the user may want to temporarily inhibit execution of events the
- scheduler can be suspended and then it can be forced to resume its
- operations. The API call to perform these is
- Event_scheduler::suspend_or_resume(enum enum_suspend_or_resume) .
- When the scheduler is suspended the main scheduler thread, which ATM
- happens to have thread_id 1, locks on a condition COND_suspend_or_resume.
- When this is signal is sent for the reverse operation the main scheduler
- loops continues to roll and execute events.
-
- When the scheduler is suspended all add/replace/drop_event() operations
- work as expected and the modify the queue but no events execution takes
- place.
-
- In contrast to the previous scheduler implementation, found in
- event_executor.cc, the start, shutdown, suspend and resume are synchronous
- operations. As a whole all operations are synchronized and no busy waits
- are used except in stop_all_running_events(), which waits until all
- running event worker threads have finished. It would have been nice to
- use a conditional on which this method will wait and the last thread to
- finish would signal it but this implies subclassing THD.
-
- The scheduler does not keep a counter of how many event worker threads are
- running, at any specific moment, because this will copy functionality
- already existing in the server. Namely, all THDs are registered in the
- global `threads` array. THD has member variable system_thread which
- identifies the type of thread. Connection threads being NON_SYSTEM_THREAD,
- all other have their enum value. Important for the scheduler are
- SYSTEM_THREAD_EVENT_SCHEDULER and SYSTEM_THREAD_EVENT_WORKER.
-
- Class THD subclasses class ilink, which is the linked list of all threads.
- When a THD instance is destroyed it's being removed from threads, thus
- no manual intervention is needed. On the contrary registering is manual
- with threads.append() . Traversing the threads array every time a subclass
- of THD, for instance if we would have had THD_scheduler_worker to see
- how many events we have and whether the scheduler is shutting down will
- take much time and lead to a deadlock. stop_all_running_events() is called
- under LOCK_scheduler_data. If the THD_scheduler_worker was aware of
- the single Event_scheduler instance it will try to check
- Event_scheduler::state but for this it would need to acquire
- LOCK_scheduler_data => deadlock. Thus stop_all_running_events() uses a
- busy wait.
-
- DROP DATABASE DDL should drop all events defined in a specific schema.
- DROP USER also should drop all events who has as definer the user being
- dropped (this one is not addressed at the moment but a hook exists). For
- this specific needs Event_scheduler::drop_matching_events() is
- implemented. Which expects a callback to be applied on every object in
- the queue. Thus events that match specific schema or user, will be
- removed from the queue. The exposed interface is :
- - Event_scheduler::drop_schema_events()
- - Event_scheduler::drop_user_events()
-
- This bulk dropping happens under LOCK_scheduler_data, thus no two or
- more threads can execute it in parallel. However, DROP DATABASE is also
- synchronized, currently, in the server thus this does not impact the
- overall performance. In addition, DROP DATABASE is not that often
- executed DDL.
-
- Though the interface to the scheduler is only through the public methods
- of class Event_scheduler, there are currently few functions which are
- used during its operations. Namely :
- - static evex_print_warnings()
- After every event execution all errors/warnings are dumped, so the user
- can see in case of a problem what the problem was.
-
- - static init_event_thread()
- This function is both used by event_scheduler_thread() and
- event_worker_thread(). It initializes the THD structure. The
- initialization looks pretty similar to the one in slave.cc done for the
- replication threads. However, though the similarities it cannot be
- factored out to have one routine.
-
- - static event_scheduler_thread()
- Because our way to register functions to be used by the threading library
- does not allow usage of static methods this function is used to start the
- scheduler in it. It does THD initialization and then calls
- Event_scheduler::run().
-
- - static event_worker_thread()
- With already stated the reason for not being able to use methods, this
- function executes the worker threads.
-
- The execution of events is, to some extent, synchronized to inhibit race
- conditions when Event_timed::thread_id is being updated with the thread_id of
- the THD in which the event is being executed. The thread_id is in the
- Event_timed object because we need to be able to kill quickly a specific
- event during ALTER/DROP EVENT without traversing the global `threads` array.
- However, this makes the scheduler's code more complicated. The event worker
- thread is started by Event_timed::spawn_now(), which in turn calls
- pthread_create(). The thread_id which will be associated in init_event_thread
- is not known in advance thus the registering takes place in
- event_worker_thread(). This registering has to be synchronized under
- LOCK_scheduler_data, so no kill_event() on a object in
- replace_event/drop_event/drop_matching_events() could take place.
-
- This synchronization is done through class Worker_thread_param that is
- local to this file. Event_scheduler::execute_top() is called under
- LOCK_scheduler_data. This method :
- 1. Creates an instance of Worker_thread_param on the stack
- 2. Locks Worker_thread_param::LOCK_started
- 3. Calls Event_timed::spawn_now() which in turn creates a new thread.
- 4. Locks on Worker_thread_param::COND_started_or_stopped and waits till the
- worker thread send signal. The code is spurious wake-up safe because
- Worker_thread_param::started is checked.
- 5. The worker thread initializes its THD, then sets Event_timed::thread_id,
- sets Worker_thread_param::started to TRUE and sends back
- Worker_thread_param::COND_started. From this moment on, the event
- is being executed and could be killed by using Event_timed::thread_id.
- When Event_timed::spawn_thread_finish() is called in the worker thread,
- it sets thread_id to 0. From this moment on, the worker thread should not
- touch the Event_timed instance.
-
-
- The life-cycle of the server is a FSA.
- enum enum_state Event_scheduler::state keeps the state of the scheduler.
-
- The states are:
-
- |---UNINITIALIZED
- |
- | |------------------> IN_SHUTDOWN
- --> INITIALIZED -> COMMENCING ---> RUNNING ----------|
- ^ ^ | | ^ |
- | |- CANTSTART <--| | |- SUSPENDED <-|
- |______________________________|
-
- - UNINITIALIZED :The object is created and only the mutex is initialized
- - INITIALIZED :All member variables are initialized
- - COMMENCING :The scheduler is starting, no other attempt to start
- should succeed before the state is back to INITIALIZED.
- - CANTSTART :Set by the ::run() method in case it can't start for some
- reason. In this case the connection thread that tries to
- start the scheduler sees that some error has occurred and
- returns an error to the user. Finally, the connection
- thread sets the state to INITIALIZED, so further attempts
- to start the scheduler could be made.
- - RUNNING :The scheduler is running. New events could be added,
- dropped, altered. The scheduler could be stopped.
- - SUSPENDED :Like RUNNING but execution of events does not take place.
- Operations on the memory queue are possible.
- - IN_SHUTDOWN :The scheduler is shutting down, due to request by setting
- the global event_scheduler to 0/FALSE, or because of a
- KILL command sent by a user to the master thread.
-
- In every method the macros LOCK_SCHEDULER_DATA() and UNLOCK_SCHEDULER_DATA()
- are used for (un)locking purposes. They are used to save the programmer
- from typing everytime
- lock_data(__FUNCTION__, __LINE__);
- All locking goes through Event_scheduler::lock_data() and ::unlock_data().
- These two functions then record in variables where for last time
- LOCK_scheduler_data was locked and unlocked (two different variables). In
- multithreaded environment, in some cases they make no sense but are useful for
- inspecting deadlocks without having the server debug log turned on and the
- server is still running.
-
- The same strategy is used for conditional variables.
- Event_scheduler::cond_wait() is invoked from all places with parameter
- an enum enum_cond_vars. In this manner, it's possible to inspect the last
- on which condition the last call to cond_wait() was waiting. If the server
- was started with debug trace switched on, the trace file also holds information
- about conditional variables used.
-*/
-
-#ifdef __GNUC__
-#if __GNUC__ >= 2
-#define SCHED_FUNC __FUNCTION__
-#endif
-#else
-#define SCHED_FUNC "<unknown>"
-#endif
-
-#define LOCK_SCHEDULER_DATA() lock_data(SCHED_FUNC, __LINE__)
-#define UNLOCK_SCHEDULER_DATA() unlock_data(SCHED_FUNC, __LINE__)
-
-
-#ifndef DBUG_OFF
-static
-LEX_STRING states_names[] =
-{
- {(char*) STRING_WITH_LEN("UNINITIALIZED")},
- {(char*) STRING_WITH_LEN("INITIALIZED")},
- {(char*) STRING_WITH_LEN("COMMENCING")},
- {(char*) STRING_WITH_LEN("CANTSTART")},
- {(char*) STRING_WITH_LEN("RUNNING")},
- {(char*) STRING_WITH_LEN("SUSPENDED")},
- {(char*) STRING_WITH_LEN("IN_SHUTDOWN")}
-};
-#endif
-
-
-Event_scheduler
-Event_scheduler::singleton;
-
-
-const char * const
-Event_scheduler::cond_vars_names[Event_scheduler::COND_LAST] =
-{
- "new work",
- "started or stopped",
- "suspend or resume"
-};
-
-
-class Worker_thread_param
-{
-public:
- Event_timed *et;
- pthread_mutex_t LOCK_started;
- pthread_cond_t COND_started;
- bool started;
-
- Worker_thread_param(Event_timed *etn):et(etn), started(FALSE)
- {
- pthread_mutex_init(&LOCK_started, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_started, NULL);
- }
-
- ~Worker_thread_param()
- {
- pthread_mutex_destroy(&LOCK_started);
- pthread_cond_destroy(&COND_started);
- }
-};
-
-
-/*
- 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_q()
-
- vptr - not used (set it to NULL)
- a - first Event_timed object
- b - second Event_timed object
-
- RETURN VALUE
- -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
-*/
-
-static int
-event_timed_compare_q(void *vptr, byte* a, byte *b)
-{
- return my_time_compare(&((Event_timed *)a)->execute_at,
- &((Event_timed *)b)->execute_at);
-}
-
-
-/*
- Prints the stack of infos, warnings, errors from thd to
- the console so it can be fetched by the logs-into-tables and
- checked later.
-
- SYNOPSIS
- evex_print_warnings
- thd - thread used during the execution of the event
- et - the event itself
-*/
-
-static void
-evex_print_warnings(THD *thd, Event_timed *et)
-{
- MYSQL_ERROR *err;
- DBUG_ENTER("evex_print_warnings");
- if (!thd->warn_list.elements)
- DBUG_VOID_RETURN;
-
- char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
- char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE];
- String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info);
- prefix.length(0);
- prefix.append("SCHEDULER: [");
-
- append_identifier(thd, &prefix, et->definer_user.str, et->definer_user.length);
- prefix.append('@');
- append_identifier(thd, &prefix, et->definer_host.str, et->definer_host.length);
- prefix.append("][", 2);
- append_identifier(thd,&prefix, et->dbname.str, et->dbname.length);
- prefix.append('.');
- append_identifier(thd,&prefix, et->name.str, et->name.length);
- prefix.append("] ", 2);
-
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
- while ((err= it++))
- {
- String err_msg(msg_buf, sizeof(msg_buf), system_charset_info);
- /* set it to 0 or we start adding at the end. That's the trick ;) */
- err_msg.length(0);
- err_msg.append(prefix);
- err_msg.append(err->msg, strlen(err->msg), system_charset_info);
- err_msg.append("]");
- DBUG_ASSERT(err->level < 3);
- (sql_print_message_handlers[err->level])("%*s", err_msg.length(),
- err_msg.c_ptr());
- }
- DBUG_VOID_RETURN;
-}
-
-
-/*
- 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.
-
- RETURN VALUE
- 0 OK
- -1 Error
-*/
-
-static int
-init_event_thread(THD** t, enum enum_thread_type thread_type)
-{
- THD *thd= *t;
- thd->thread_stack= (char*)t; // remember where our stack is
- DBUG_ENTER("init_event_thread");
- thd->client_capabilities= 0;
- thd->security_ctx->master_access= 0;
- thd->security_ctx->db_access= 0;
- thd->security_ctx->host_or_ip= (char*)my_localhost;
- my_net_init(&thd->net, 0);
- thd->net.read_timeout= slave_net_timeout;
- thd->slave_thread= 0;
- thd->options|= OPTION_AUTO_IS_NULL;
- thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- thd->real_id=pthread_self();
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->thread_id= thread_id++;
- threads.append(thd);
- thread_count++;
- thread_running++;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- if (init_thr_lock() || thd->store_globals())
- {
- thd->cleanup();
- DBUG_RETURN(-1);
- }
-
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
-
- /*
- Guarantees that we will see the thread in SHOW PROCESSLIST though its
- vio is NULL.
- */
- thd->system_thread= thread_type;
-
- thd->proc_info= "Initialized";
- thd->version= refresh_version;
- thd->set_time();
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Inits the main scheduler thread and then calls Event_scheduler::run()
- of arg.
-
- SYNOPSIS
- event_scheduler_thread()
- arg void* ptr to Event_scheduler
-
- NOTES
- 1. The host of the thead is my_localhost
- 2. thd->net is initted with NULL - no communication.
- 3. The reason to have a proxy function is that it's not possible to
- use a method as function to be executed in a spawned thread:
- - our pthread_hander_t macro uses extern "C"
- - separating thread setup from the real execution loop is also to be
- considered good.
-
- RETURN VALUE
- 0 OK
-*/
-
-pthread_handler_t
-event_scheduler_thread(void *arg)
-{
- /* needs to be first for thread_stack */
- THD *thd= NULL;
- Event_scheduler *scheduler= (Event_scheduler *) arg;
-
- DBUG_ENTER("event_scheduler_thread");
-
- my_thread_init();
- pthread_detach_this_thread();
-
- /* note that constructor of THD uses DBUG_ ! */
- if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_SCHEDULER))
- {
- sql_print_error("SCHEDULER: Cannot init manager event thread.");
- scheduler->report_error_during_start();
- }
- else
- {
- thd->security_ctx->set_user((char*)"event_scheduler");
-
- sql_print_information("SCHEDULER: Manager thread booting");
- if (Event_scheduler::check_system_tables(thd))
- scheduler->report_error_during_start();
- else
- scheduler->run(thd);
-
- /*
- NOTE: Don't touch `scheduler` after this point because we have notified
- the
- thread which shuts us down that we have finished cleaning. In this
- very moment a new scheduler thread could be started and a crash is
- not welcome.
- */
- }
-
- /*
- If we cannot create THD then don't decrease because we haven't touched
- thread_count and thread_running in init_event_thread() which was never
- called. In init_event_thread() thread_count and thread_running are
- always increased even in the case the method returns an error.
- */
- if (thd)
- {
- thd->proc_info= "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net);
- pthread_mutex_lock(&LOCK_thread_count);
- thread_count--;
- thread_running--;
- delete thd;
- pthread_mutex_unlock(&LOCK_thread_count);
- }
- my_thread_end();
- DBUG_RETURN(0); // Can't return anything here
-}
-
-
-/*
- Function that executes an event in a child thread. Setups the
- environment for the event execution and cleans after that.
-
- SYNOPSIS
- event_worker_thread()
- arg The Event_timed object to be processed
-
- RETURN VALUE
- 0 OK
-*/
-
-pthread_handler_t
-event_worker_thread(void *arg)
-{
- THD *thd; /* needs to be first for thread_stack */
- Worker_thread_param *param= (Worker_thread_param *) arg;
- Event_timed *event= param->et;
- int ret;
- bool startup_error= FALSE;
- Security_context *save_ctx;
- /* this one is local and not needed after exec */
- Security_context security_ctx;
-
- DBUG_ENTER("event_worker_thread");
- DBUG_PRINT("enter", ("event=[%s.%s]", event->dbname.str, event->name.str));
-
- my_thread_init();
- pthread_detach_this_thread();
-
- if (!(thd= new THD) || init_event_thread(&thd, SYSTEM_THREAD_EVENT_WORKER))
- {
- sql_print_error("SCHEDULER: Startup failure.");
- startup_error= TRUE;
- event->spawn_thread_finish(thd);
- }
- else
- event->set_thread_id(thd->thread_id);
-
- DBUG_PRINT("info", ("master_access=%d db_access=%d",
- thd->security_ctx->master_access, thd->security_ctx->db_access));
- /*
- If we don't change it before we send the signal back, then an intermittent
- DROP EVENT will take LOCK_scheduler_data and try to kill this thread, because
- event->thread_id is already real. However, because thd->security_ctx->user
- is not initialized then a crash occurs in kill_one_thread(). Thus, we have
- to change the context before sending the signal. We are under
- LOCK_scheduler_data being held by Event_scheduler::run() -> ::execute_top().
- */
- change_security_context(thd, event->definer_user, event->definer_host,
- event->dbname, &security_ctx, &save_ctx);
- DBUG_PRINT("info", ("master_access=%d db_access=%d",
- thd->security_ctx->master_access, thd->security_ctx->db_access));
-
- /* Signal the scheduler thread that we have started successfully */
- pthread_mutex_lock(&param->LOCK_started);
- param->started= TRUE;
- pthread_cond_signal(&param->COND_started);
- pthread_mutex_unlock(&param->LOCK_started);
-
- if (!startup_error)
- {
- thd->init_for_queries();
- thd->enable_slow_log= TRUE;
-
- event->set_thread_id(thd->thread_id);
- sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu",
- event->dbname.str, event->name.str,
- event->definer.str, thd->thread_id);
-
- ret= event->execute(thd, thd->mem_root);
- evex_print_warnings(thd, event);
- sql_print_information("SCHEDULER: [%s.%s of %s] executed. RetCode=%d",
- event->dbname.str, event->name.str,
- event->definer.str, ret);
- if (ret == EVEX_COMPILE_ERROR)
- sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s",
- event->dbname.str, event->name.str,
- event->definer.str);
- else if (ret == EVEX_MICROSECOND_UNSUP)
- sql_print_information("SCHEDULER: MICROSECOND is not supported");
-
- DBUG_PRINT("info", ("master_access=%d db_access=%d",
- thd->security_ctx->master_access, thd->security_ctx->db_access));
-
- /* If true is returned, we are expected to free it */
- if (event->spawn_thread_finish(thd))
- {
- DBUG_PRINT("info", ("Freeing object pointer"));
- delete event;
- }
- }
-
- if (thd)
- {
- thd->proc_info= "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- /*
- Free it here because net.vio is NULL for us => THD::~THD will check it
- and won't call net_end(&net); See also replication code.
- */
- net_end(&thd->net);
- DBUG_PRINT("info", ("Worker thread %lu exiting", thd->thread_id));
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thread_count--;
- thread_running--;
- delete thd;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- }
-
- my_thread_end();
- DBUG_RETURN(0); // Can't return anything here
-}
-
-
-/*
- Constructor of class Event_scheduler.
-
- SYNOPSIS
- Event_scheduler::Event_scheduler()
-*/
-
-Event_scheduler::Event_scheduler()
- :state(UNINITIALIZED), start_scheduler_suspended(FALSE),
- thread_id(0), mutex_last_locked_at_line(0),
- mutex_last_unlocked_at_line(0), mutex_last_locked_in_func(""),
- mutex_last_unlocked_in_func(""), cond_waiting_on(COND_NONE),
- mutex_scheduler_data_locked(FALSE)
-{
-}
-
-
-/*
- Returns the singleton instance of the class.
-
- SYNOPSIS
- Event_scheduler::get_instance()
-
- RETURN VALUE
- address
-*/
-
-Event_scheduler*
-Event_scheduler::get_instance()
-{
- DBUG_ENTER("Event_scheduler::get_instance");
- DBUG_RETURN(&singleton);
-}
-
-
-/*
- The implementation of full-fledged initialization.
-
- SYNOPSIS
- Event_scheduler::init()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
-bool
-Event_scheduler::init()
-{
- int i= 0;
- bool ret= FALSE;
- DBUG_ENTER("Event_scheduler::init");
- DBUG_PRINT("enter", ("this=%p", this));
-
- LOCK_SCHEDULER_DATA();
- for (;i < COND_LAST; i++)
- if (pthread_cond_init(&cond_vars[i], NULL))
- {
- sql_print_error("SCHEDULER: Unable to initalize conditions");
- ret= TRUE;
- goto end;
- }
-
- /* init memory root */
- init_alloc_root(&scheduler_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
-
- if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
- event_timed_compare_q, NULL, 30 /*auto_extent*/))
- {
- sql_print_error("SCHEDULER: Can't initialize the execution queue");
- ret= TRUE;
- goto end;
- }
-
- if (sizeof(my_time_t) != sizeof(time_t))
- {
- sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ."
- "The scheduler may not work correctly. Stopping.");
- DBUG_ASSERT(0);
- ret= TRUE;
- goto end;
- }
-
- state= INITIALIZED;
-end:
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(ret);
-}
-
-
-/*
- Frees all memory allocated by the scheduler object.
-
- SYNOPSIS
- Event_scheduler::destroy()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
-void
-Event_scheduler::destroy()
-{
- DBUG_ENTER("Event_scheduler");
-
- LOCK_SCHEDULER_DATA();
- switch (state) {
- case UNINITIALIZED:
- break;
- case INITIALIZED:
- delete_queue(&queue);
- free_root(&scheduler_root, MYF(0));
- int i;
- for (i= 0; i < COND_LAST; i++)
- pthread_cond_destroy(&cond_vars[i]);
- state= UNINITIALIZED;
- break;
- default:
- sql_print_error("SCHEDULER: Destroying while state is %d", state);
- /* I trust my code but ::safe() > ::sorry() */
- DBUG_ASSERT(0);
- break;
- }
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Creates an event in the scheduler queue
-
- SYNOPSIS
- Event_scheduler::create_event()
- et The event to add
- check_existence Whether to check if already loaded.
-
- RETURN VALUE
- OP_OK OK or scheduler not working
- OP_LOAD_ERROR Error during loading from disk
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::create_event(THD *thd, Event_timed *et, bool check_existence)
-{
- enum enum_error_code res;
- Event_timed *et_new;
- DBUG_ENTER("Event_scheduler::create_event");
- DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
- }
- if (check_existence && find_event(et, FALSE))
- {
- res= OP_ALREADY_EXISTS;
- goto end;
- }
-
- /* We need to load the event on scheduler_root */
- if (!(res= load_named_event(thd, et, &et_new)))
- {
- queue_insert_safe(&queue, (byte *) et_new);
- DBUG_PRINT("info", ("Sending COND_new_work"));
- pthread_cond_signal(&cond_vars[COND_new_work]);
- }
- else if (res == OP_DISABLED_EVENT)
- res= OP_OK;
-end:
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(res);
-}
-
-
-/*
- Drops an event from the scheduler queue
-
- SYNOPSIS
- Event_scheduler::drop_event()
- etn The event to drop
- state Wait the event or kill&drop
-
- RETURN VALUE
- FALSE OK (replaced or scheduler not working)
- TRUE Failure
-*/
-
-bool
-Event_scheduler::drop_event(THD *thd, Event_timed *et)
-{
- int res;
- Event_timed *et_old;
- DBUG_ENTER("Event_scheduler::drop_event");
- DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et,&LOCK_scheduler_data));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
- }
-
- if (!(et_old= find_event(et, TRUE)))
- DBUG_PRINT("info", ("No such event found, probably DISABLED"));
-
- UNLOCK_SCHEDULER_DATA();
-
- /* See comments in ::replace_event() why this is split in two parts. */
- if (et_old)
- {
- switch ((res= et_old->kill_thread(thd))) {
- case EVEX_CANT_KILL:
- /* Don't delete but continue */
- et_old->flags |= EVENT_FREE_WHEN_FINISHED;
- break;
- case 0:
- /*
- kill_thread() waits till the spawned thread finishes after it's
- killed. Hence, we delete here memory which is no more referenced from
- a running thread.
- */
- delete et_old;
- /*
- We don't signal COND_new_work here because:
- 1. Even if the dropped event is on top of the queue this will not
- move another one to be executed before the time the one on the
- top (but could be at the same second as the dropped one)
- 2. If this was the last event on the queue, then pthread_cond_timedwait
- in ::run() will finish and then see that the queue is empty and
- call cond_wait(). Hence, no need to interrupt the blocked
- ::run() thread.
- */
- break;
- default:
- sql_print_error("SCHEDULER: Got unexpected error %d", res);
- DBUG_ASSERT(0);
- }
- }
-
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- Updates an event from the scheduler queue
-
- SYNOPSIS
- Event_scheduler::replace_event()
- et The event to replace(add) into the queue
- state Async or sync stopping
-
- RETURN VALUE
- OP_OK OK or scheduler not working
- OP_LOAD_ERROR Error during loading from disk
- OP_ALREADY_EXISTS Event already in the queue
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::update_event(THD *thd, Event_timed *et,
- LEX_STRING *new_schema,
- LEX_STRING *new_name)
-{
- enum enum_error_code res;
- Event_timed *et_old, *et_new= NULL;
- LEX_STRING old_schema, old_name;
-
- LINT_INIT(old_schema.str);
- LINT_INIT(old_schema.length);
- LINT_INIT(old_name.str);
- LINT_INIT(old_name.length);
-
- DBUG_ENTER("Event_scheduler::update_event");
- DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p",
- thd, et, et->dbname.str, et->name.str, &LOCK_scheduler_data));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- DBUG_PRINT("info", ("scheduler not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
- }
-
- if (!(et_old= find_event(et, TRUE)))
- DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED",
- et->dbname.str, et->name.str));
-
- if (new_schema && new_name)
- {
- old_schema= et->dbname;
- old_name= et->name;
- et->dbname= *new_schema;
- et->name= *new_name;
- }
- /*
- We need to load the event (it's strings but on the object itself)
- on scheduler_root. et_new could be NULL :
- 1. Error occured
- 2. If the replace is DISABLED, we don't load it into the queue.
- */
- if (!(res= load_named_event(thd, et, &et_new)))
- {
- queue_insert_safe(&queue, (byte *) et_new);
- DBUG_PRINT("info", ("Sending COND_new_work"));
- pthread_cond_signal(&cond_vars[COND_new_work]);
- }
- else if (res == OP_DISABLED_EVENT)
- res= OP_OK;
-
- if (new_schema && new_name)
- {
- et->dbname= old_schema;
- et->name= old_name;
- }
-
- UNLOCK_SCHEDULER_DATA();
- /*
- Andrey: Is this comment still truthful ???
-
- We don't move this code above because a potential kill_thread will call
- THD::awake(). Which in turn will try to acqure mysys_var->current_mutex,
- which is LOCK_scheduler_data on which the COND_new_work in ::run() locks.
- Hence, we try to acquire a lock which we have already acquired and we run
- into an assert. Holding LOCK_scheduler_data however is not needed because
- we don't touch any invariant of the scheduler anymore. ::drop_event() does
- the same.
- */
- if (et_old)
- {
- switch (et_old->kill_thread(thd)) {
- case EVEX_CANT_KILL:
- /* Don't delete but continue */
- et_old->flags |= EVENT_FREE_WHEN_FINISHED;
- break;
- case 0:
- /*
- kill_thread() waits till the spawned thread finishes after it's
- killed. Hence, we delete here memory which is no more referenced from
- a running thread.
- */
- delete et_old;
- /*
- We don't signal COND_new_work here because:
- 1. Even if the dropped event is on top of the queue this will not
- move another one to be executed before the time the one on the
- top (but could be at the same second as the dropped one)
- 2. If this was the last event on the queue, then pthread_cond_timedwait
- in ::run() will finish and then see that the queue is empty and
- call cond_wait(). Hence, no need to interrupt the blocked
- ::run() thread.
- */
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
-
- DBUG_RETURN(res);
-}
-
-
-/*
- Searches for an event in the scheduler queue
-
- SYNOPSIS
- Event_scheduler::find_event()
- etn The event to find
- comparator The function to use for comparing
- remove_from_q If found whether to remove from the Q
-
- RETURN VALUE
- NULL Not found
- otherwise Address
-
- NOTE
- The caller should do the locking also the caller is responsible for
- actual signalling in case an event is removed from the queue
- (signalling COND_new_work for instance).
-*/
-
-Event_timed *
-Event_scheduler::find_event(Event_timed *etn, bool remove_from_q)
-{
- uint i;
- DBUG_ENTER("Event_scheduler::find_event");
-
- for (i= 0; i < queue.elements; ++i)
- {
- Event_timed *et= (Event_timed *) queue_element(&queue, i);
- DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", etn->dbname.str, etn->name.str,
- et->dbname.str, et->name.str));
- if (event_timed_identifier_equal(etn, et))
- {
- if (remove_from_q)
- queue_remove(&queue, i);
- DBUG_RETURN(et);
- }
- }
-
- DBUG_RETURN(NULL);
-}
-
-
-/*
- Drops all events from the in-memory queue and disk that match
- certain pattern evaluated by a comparator function
-
- SYNOPSIS
- Event_scheduler::drop_matching_events()
- thd THD
- pattern A pattern string
- comparator The function to use for comparing
-
- RETURN VALUE
- -1 Scheduler not working
- >=0 Number of dropped events
-
- NOTE
- Expected is the caller to acquire lock on LOCK_scheduler_data
-*/
-
-void
-Event_scheduler::drop_matching_events(THD *thd, LEX_STRING *pattern,
- bool (*comparator)(Event_timed *,LEX_STRING *))
-{
- DBUG_ENTER("Event_scheduler::drop_matching_events");
- DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern->length, pattern->str,
- state));
- if (is_running_or_suspended())
- {
- uint i= 0, dropped= 0;
- while (i < queue.elements)
- {
- Event_timed *et= (Event_timed *) queue_element(&queue, i);
- DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
- if (comparator(et, pattern))
- {
- /*
- The queue is ordered. If we remove an element, then all elements after
- it will shift one position to the left, if we imagine it as an array
- from left to the right. In this case we should not increment the
- counter and the (i < queue.elements) condition is ok.
- */
- queue_remove(&queue, i);
-
- /* See replace_event() */
- switch (et->kill_thread(thd)) {
- case EVEX_CANT_KILL:
- /* Don't delete but continue */
- et->flags |= EVENT_FREE_WHEN_FINISHED;
- ++dropped;
- break;
- case 0:
- delete et;
- ++dropped;
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
- else
- i++;
- }
- DBUG_PRINT("info", ("Dropped %lu", dropped));
- }
- /*
- Don't send COND_new_work because no need to wake up the scheduler thread.
- When it wakes next time up it will recalculate how much more it should
- sleep if the top of the queue has been changed by this method.
- */
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Drops all events from the in-memory queue and disk that are from
- certain schema.
-
- SYNOPSIS
- Event_scheduler::drop_schema_events()
- thd THD
- db The schema name
-
- RETURN VALUE
- -1 Scheduler not working
- >=0 Number of dropped events
-*/
-
-int
-Event_scheduler::drop_schema_events(THD *thd, LEX_STRING *schema)
-{
- int ret;
- DBUG_ENTER("Event_scheduler::drop_schema_events");
- LOCK_SCHEDULER_DATA();
- if (is_running_or_suspended())
- drop_matching_events(thd, schema, event_timed_db_equal);
-
- ret= db_drop_events_from_table(thd, schema);
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_RETURN(ret);
-}
-
-
-extern pthread_attr_t connection_attrib;
-
-
-/*
- Starts the event scheduler
-
- SYNOPSIS
- Event_scheduler::start()
-
- RETURN VALUE
- FALSE OK
- TRUE Error
-*/
-
-bool
-Event_scheduler::start()
-{
- bool ret= FALSE;
- pthread_t th;
- DBUG_ENTER("Event_scheduler::start");
-
- LOCK_SCHEDULER_DATA();
- /* If already working or starting don't make another attempt */
- DBUG_ASSERT(state == INITIALIZED);
- if (state > INITIALIZED)
- {
- DBUG_PRINT("info", ("scheduler is already running or starting"));
- ret= TRUE;
- goto end;
- }
-
- /*
- Now if another thread calls start it will bail-out because the branch
- above will be executed. Thus no two or more child threads will be forked.
- If the child thread cannot start for some reason then `state` is set
- to CANTSTART and COND_started is also signaled. In this case we
- set `state` back to INITIALIZED so another attempt to start the scheduler
- can be made.
- */
- state= COMMENCING;
- /* Fork */
- if (pthread_create(&th, &connection_attrib, event_scheduler_thread,
- (void*)this))
- {
- DBUG_PRINT("error", ("cannot create a new thread"));
- state= INITIALIZED;
- ret= TRUE;
- goto end;
- }
-
- /* Wait till the child thread has booted (w/ or wo success) */
- while (!is_running_or_suspended() && state != CANTSTART)
- cond_wait(COND_started_or_stopped, &LOCK_scheduler_data);
-
- /*
- If we cannot start for some reason then don't prohibit further attempts.
- Set back to INITIALIZED.
- */
- if (state == CANTSTART)
- {
- state= INITIALIZED;
- ret= TRUE;
- goto end;
- }
-
-end:
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(ret);
-}
-
-
-/*
- Starts the event scheduler in suspended mode.
-
- SYNOPSIS
- Event_scheduler::start_suspended()
-
- RETURN VALUE
- TRUE OK
- FALSE Error
-*/
-
-bool
-Event_scheduler::start_suspended()
-{
- DBUG_ENTER("Event_scheduler::start_suspended");
- start_scheduler_suspended= TRUE;
- DBUG_RETURN(start());
-}
-
-
-
-/*
- Report back that we cannot start. Used for ocasions where
- we can't go into ::run() and have to report externally.
-
- SYNOPSIS
- Event_scheduler::report_error_during_start()
-*/
-
-inline void
-Event_scheduler::report_error_during_start()
-{
- DBUG_ENTER("Event_scheduler::report_error_during_start");
-
- LOCK_SCHEDULER_DATA();
- state= CANTSTART;
- DBUG_PRINT("info", ("Sending back COND_started_or_stopped"));
- pthread_cond_signal(&cond_vars[COND_started_or_stopped]);
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- The internal loop of the event scheduler
-
- SYNOPSIS
- Event_scheduler::run()
- thd Thread
-
- RETURN VALUE
- FALSE OK
- TRUE Failure
-*/
-
-bool
-Event_scheduler::run(THD *thd)
-{
- int ret;
- struct timespec abstime;
- DBUG_ENTER("Event_scheduler::run");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- LOCK_SCHEDULER_DATA();
- ret= load_events_from_db(thd);
-
- if (!ret)
- {
- thread_id= thd->thread_id;
- state= start_scheduler_suspended? SUSPENDED:RUNNING;
- start_scheduler_suspended= FALSE;
- }
- else
- state= CANTSTART;
-
- DBUG_PRINT("info", ("Sending back COND_started_or_stopped"));
- pthread_cond_signal(&cond_vars[COND_started_or_stopped]);
- if (ret)
- {
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(TRUE);
- }
- if (!check_n_suspend_if_needed(thd))
- UNLOCK_SCHEDULER_DATA();
-
- sql_print_information("SCHEDULER: Manager thread started with id %lu",
- thd->thread_id);
- abstime.tv_nsec= 0;
- while (is_running_or_suspended())
- {
- Event_timed *et;
-
- LOCK_SCHEDULER_DATA();
- if (check_n_wait_for_non_empty_queue(thd))
- continue;
-
- /* On TRUE data is unlocked, go back to the beginning */
- if (check_n_suspend_if_needed(thd))
- continue;
-
- /* Guaranteed locked here */
- if (state == IN_SHUTDOWN || shutdown_in_progress)
- {
- UNLOCK_SCHEDULER_DATA();
- break;
- }
- DBUG_ASSERT(state == RUNNING);
-
- et= (Event_timed *)queue_top(&queue);
-
- /* Skip disabled events */
- if (et->status != Event_timed::ENABLED)
- {
- /*
- It could be a one-timer scheduled for a time, already in the past when the
- scheduler was suspended.
- */
- sql_print_information("SCHEDULER: Found a disabled event %*s.%*s in the queue",
- et->dbname.length, et->dbname.str, et->name.length,
- et->name.str);
- queue_remove(&queue, 0);
- /* ToDo: check this again */
- if (et->dropped)
- et->drop(thd);
- delete et;
- UNLOCK_SCHEDULER_DATA();
- continue;
- }
- thd->proc_info= (char *)"Computing";
- DBUG_PRINT("evex manager",("computing time to sleep till next exec"));
- /* Timestamp is in UTC */
- abstime.tv_sec= sec_since_epoch_TIME(&et->execute_at);
-
- thd->end_time();
- if (abstime.tv_sec > thd->query_start())
- {
- /* Event trigger time is in the future */
- thd->proc_info= (char *)"Sleep";
- DBUG_PRINT("info", ("Going to sleep. Should wakeup after approx %d secs",
- abstime.tv_sec - thd->query_start()));
- DBUG_PRINT("info", ("Entering condition because waiting for activation"));
- /*
- Use THD::enter_cond()/exit_cond() or we won't be able to kill a
- sleeping thread. Though ::stop() can do it by sending COND_new_work
- an user can't by just issuing 'KILL x'; . In the latter case
- pthread_cond_timedwait() will wait till `abstime`.
- "Sleeping until next time"
- */
- thd->enter_cond(&cond_vars[COND_new_work],&LOCK_scheduler_data,"Sleeping");
-
- pthread_cond_timedwait(&cond_vars[COND_new_work], &LOCK_scheduler_data,
- &abstime);
-
- DBUG_PRINT("info", ("Manager woke up. state is %d", state));
- /*
- If we get signal we should recalculate the whether it's the right time
- because there could be :
- 1. Spurious wake-up
- 2. The top of the queue was changed (new one becase of add/drop/replace)
- */
- /* This will do implicit UNLOCK_SCHEDULER_DATA() */
- thd->exit_cond("");
- }
- else
- {
- thd->proc_info= (char *)"Executing";
- /*
- Execute the event. An error may occur if a thread cannot be forked.
- In this case stop the manager.
- We should enter ::execute_top() with locked LOCK_scheduler_data.
- */
- int ret= execute_top(thd);
- UNLOCK_SCHEDULER_DATA();
- if (ret)
- break;
- }
- }
-
- thd->proc_info= (char *)"Cleaning";
-
- LOCK_SCHEDULER_DATA();
- /*
- It's possible that a user has used (SQL)COM_KILL. Hence set the appropriate
- state because it is only set by ::stop().
- */
- if (state != IN_SHUTDOWN)
- {
- DBUG_PRINT("info", ("We got KILL but the but not from ::stop()"));
- state= IN_SHUTDOWN;
- }
- UNLOCK_SCHEDULER_DATA();
-
- sql_print_information("SCHEDULER: Shutting down");
-
- thd->proc_info= (char *)"Cleaning queue";
- clean_queue(thd);
- THD_CHECK_SENTRY(thd);
-
- /* free mamager_root memory but don't destroy the root */
- thd->proc_info= (char *)"Cleaning memory root";
- free_root(&scheduler_root, MYF(0));
- THD_CHECK_SENTRY(thd);
-
- /*
- We notify the waiting thread which shutdowns us that we have cleaned.
- There are few more instructions to be executed in this pthread but
- they don't affect manager structures thus it's safe to signal already
- at this point.
- */
- LOCK_SCHEDULER_DATA();
- thd->proc_info= (char *)"Sending shutdown signal";
- DBUG_PRINT("info", ("Sending COND_started_or_stopped"));
- if (state == IN_SHUTDOWN)
- pthread_cond_signal(&cond_vars[COND_started_or_stopped]);
-
- state= INITIALIZED;
- /*
- We set it here because ::run() can stop not only because of ::stop()
- call but also because of `KILL x`
- */
- thread_id= 0;
- sql_print_information("SCHEDULER: Stopped");
- UNLOCK_SCHEDULER_DATA();
-
- /* We have modified, we set back */
- thd->query= NULL;
- thd->query_length= 0;
-
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- Executes the top element of the queue. Auxiliary method for ::run().
-
- SYNOPSIS
- Event_scheduler::execute_top()
-
- RETURN VALUE
- FALSE OK
- TRUE Failure
-
- NOTE
- NO locking is done. EXPECTED is that the caller should have locked
- the queue (w/ LOCK_scheduler_data).
-*/
-
-bool
-Event_scheduler::execute_top(THD *thd)
-{
- int spawn_ret_code;
- bool ret= FALSE;
- DBUG_ENTER("Event_scheduler::execute_top");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- Event_timed *et= (Event_timed *)queue_top(&queue);
-
- /* Is it good idea to pass a stack address ?*/
- Worker_thread_param param(et);
-
- pthread_mutex_lock(&param.LOCK_started);
- /*
- We don't lock LOCK_scheduler_data fpr workers_increment() because it's a
- pre-requisite for calling the current_method.
- */
- switch ((spawn_ret_code= et->spawn_now(event_worker_thread, &param))) {
- case EVENT_EXEC_CANT_FORK:
- /*
- We don't lock LOCK_scheduler_data here because it's a pre-requisite
- for calling the current_method.
- */
- sql_print_error("SCHEDULER: Problem while trying to create a thread");
- ret= TRUE;
- break;
- case EVENT_EXEC_ALREADY_EXEC:
- /*
- We don't lock LOCK_scheduler_data here because it's a pre-requisite
- for calling the current_method.
- */
- sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.",
- et->dbname.str, et->name.str);
- if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED)
- queue_remove(&queue, 0);// 0 is top, internally 1
- else
- queue_replaced(&queue);
- break;
- default:
- DBUG_ASSERT(!spawn_ret_code);
- if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == Event_timed::DISABLED)
- queue_remove(&queue, 0);// 0 is top, internally 1
- else
- queue_replaced(&queue);
- /*
- We don't lock LOCK_scheduler_data here because it's a pre-requisite
- for calling the current_method.
- */
- if (likely(!spawn_ret_code))
- {
- /* Wait the forked thread to start */
- do {
- pthread_cond_wait(&param.COND_started, &param.LOCK_started);
- } while (!param.started);
- }
- /*
- param was allocated on the stack so no explicit delete as well as
- in this moment it's no more used in the spawned thread so it's safe
- to be deleted.
- */
- break;
- }
- pthread_mutex_unlock(&param.LOCK_started);
- /* `param` is on the stack and will be destructed by the compiler */
-
- DBUG_RETURN(ret);
-}
-
-
-/*
- Cleans the scheduler's queue. Auxiliary method for ::run().
-
- SYNOPSIS
- Event_scheduler::clean_queue()
- thd Thread
-*/
-
-void
-Event_scheduler::clean_queue(THD *thd)
-{
- CHARSET_INFO *scs= system_charset_info;
- uint i;
- DBUG_ENTER("Event_scheduler::clean_queue");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- LOCK_SCHEDULER_DATA();
- stop_all_running_events(thd);
- UNLOCK_SCHEDULER_DATA();
-
- sql_print_information("SCHEDULER: Emptying the queue");
-
- /* empty the queue */
- for (i= 0; i < queue.elements; ++i)
- {
- Event_timed *et= (Event_timed *) queue_element(&queue, i);
- et->free_sp();
- delete et;
- }
- resize_queue(&queue, 0);
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Stops all running events
-
- SYNOPSIS
- Event_scheduler::stop_all_running_events()
- thd Thread
-
- NOTE
- LOCK_scheduler data must be acquired prior to call to this method
-*/
-
-void
-Event_scheduler::stop_all_running_events(THD *thd)
-{
- CHARSET_INFO *scs= system_charset_info;
- uint i;
- DYNAMIC_ARRAY running_threads;
- THD *tmp;
- DBUG_ENTER("Event_scheduler::stop_all_running_events");
- DBUG_PRINT("enter", ("workers_count=%d", workers_count()));
-
- my_init_dynamic_array(&running_threads, sizeof(ulong), 10, 10);
-
- bool had_super= FALSE;
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
- {
- if (tmp->command == COM_DAEMON)
- continue;
- if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
- push_dynamic(&running_threads, (gptr) &tmp->thread_id);
- }
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- /* We need temporarily SUPER_ACL to be able to kill our offsprings */
- if (!(thd->security_ctx->master_access & SUPER_ACL))
- thd->security_ctx->master_access|= SUPER_ACL;
- else
- had_super= TRUE;
-
- char tmp_buff[10*STRING_BUFFER_USUAL_SIZE];
- char int_buff[STRING_BUFFER_USUAL_SIZE];
- String tmp_string(tmp_buff, sizeof(tmp_buff), scs);
- String int_string(int_buff, sizeof(int_buff), scs);
- tmp_string.length(0);
-
- for (i= 0; i < running_threads.elements; ++i)
- {
- int ret;
- ulong thd_id= *dynamic_element(&running_threads, i, ulong*);
-
- int_string.set((longlong) thd_id,scs);
- tmp_string.append(int_string);
- if (i < running_threads.elements - 1)
- tmp_string.append(' ');
-
- if ((ret= kill_one_thread(thd, thd_id, FALSE)))
- {
- sql_print_error("SCHEDULER: Error killing %lu code=%d", thd_id, ret);
- break;
- }
- }
- if (running_threads.elements)
- sql_print_information("SCHEDULER: Killing workers :%s", tmp_string.c_ptr());
-
- if (!had_super)
- thd->security_ctx->master_access &= ~SUPER_ACL;
-
- delete_dynamic(&running_threads);
-
- sql_print_information("SCHEDULER: Waiting for worker threads to finish");
-
- while (workers_count())
- my_sleep(100000);
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Stops the event scheduler
-
- SYNOPSIS
- Event_scheduler::stop()
-
- RETURN VALUE
- OP_OK OK
- OP_CANT_KILL Error during stopping of manager thread
- OP_NOT_RUNNING Manager not working
-
- NOTE
- The caller must have acquited LOCK_scheduler_data.
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::stop()
-{
- THD *thd= current_thd;
- DBUG_ENTER("Event_scheduler::stop");
- DBUG_PRINT("enter", ("thd=%p", current_thd));
-
- LOCK_SCHEDULER_DATA();
- if (!is_running_or_suspended())
- {
- /*
- One situation to be here is if there was a start that forked a new
- thread but the new thread did not acquire yet LOCK_scheduler_data.
- Hence, in this case return an error.
- */
- DBUG_PRINT("info", ("manager not running but %d. doing nothing", state));
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_NOT_RUNNING);
- }
- state= IN_SHUTDOWN;
-
- DBUG_PRINT("info", ("Manager thread has id %d", thread_id));
- sql_print_information("SCHEDULER: Killing manager thread %lu", thread_id);
-
- /*
- Sending the COND_new_work to ::run() is a way to get this working without
- race conditions. If we use kill_one_thread() it will call THD::awake() and
- because in ::run() both THD::enter_cond()/::exit_cond() are used,
- THD::awake() will try to lock LOCK_scheduler_data. If we UNLOCK it before,
- then the pthread_cond_signal(COND_started_or_stopped) could be signaled in
- ::run() and we can miss the signal before we relock. A way is to use
- another mutex for this shutdown procedure but better not.
- */
- pthread_cond_signal(&cond_vars[COND_new_work]);
- /* Or we are suspended - then we should wake up */
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
-
- /* Guarantee we don't catch spurious signals */
- sql_print_information("SCHEDULER: Waiting the manager thread to reply");
- while (state != INITIALIZED)
- {
- DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from the manager "
- "thread. Current value of state is %d . "
- "workers count=%d", state, workers_count()));
- cond_wait(COND_started_or_stopped, &LOCK_scheduler_data);
- }
- DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT"));
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_RETURN(OP_OK);
-}
-
-
-/*
- Suspends or resumes the scheduler.
- SUSPEND - it won't execute any event till resumed.
- RESUME - it will resume if suspended.
-
- SYNOPSIS
- Event_scheduler::suspend_or_resume()
-
- RETURN VALUE
- OP_OK OK
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::suspend_or_resume(
- enum Event_scheduler::enum_suspend_or_resume action)
-{
- DBUG_ENTER("Event_scheduler::suspend_or_resume");
- DBUG_PRINT("enter", ("action=%d", action));
-
- LOCK_SCHEDULER_DATA();
-
- if ((action == SUSPEND && state == SUSPENDED) ||
- (action == RESUME && state == RUNNING))
- {
- DBUG_PRINT("info", ("Either trying to suspend suspended or resume "
- "running scheduler. Doing nothing."));
- }
- else
- {
- /* Wake the main thread up if he is asleep */
- DBUG_PRINT("info", ("Sending signal"));
- if (action==SUSPEND)
- {
- state= SUSPENDED;
- pthread_cond_signal(&cond_vars[COND_new_work]);
- }
- else
- {
- state= RUNNING;
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
- }
- DBUG_PRINT("info", ("Waiting on COND_suspend_or_resume"));
- cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data);
- DBUG_PRINT("info", ("Got response"));
- }
- UNLOCK_SCHEDULER_DATA();
- DBUG_RETURN(OP_OK);
-}
-
-
-/*
- Returns the number of executing events.
-
- SYNOPSIS
- Event_scheduler::workers_count()
-*/
-
-uint
-Event_scheduler::workers_count()
-{
- THD *tmp;
- uint count= 0;
-
- DBUG_ENTER("Event_scheduler::workers_count");
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
- {
- if (tmp->command == COM_DAEMON)
- continue;
- if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
- ++count;
- }
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- DBUG_PRINT("exit", ("%d", count));
- DBUG_RETURN(count);
-}
-
-
-/*
- Checks and suspends if needed
-
- SYNOPSIS
- Event_scheduler::check_n_suspend_if_needed()
- thd Thread
-
- RETURN VALUE
- FALSE Not suspended, we haven't slept
- TRUE We were suspended. LOCK_scheduler_data is unlocked.
-
- NOTE
- The caller should have locked LOCK_scheduler_data!
- The mutex will be unlocked in case this function returns TRUE
-*/
-
-bool
-Event_scheduler::check_n_suspend_if_needed(THD *thd)
-{
- bool was_suspended= FALSE;
- DBUG_ENTER("Event_scheduler::check_n_suspend_if_needed");
- if (thd->killed && !shutdown_in_progress)
- {
- state= SUSPENDED;
- thd->killed= THD::NOT_KILLED;
- }
- if (state == SUSPENDED)
- {
- thd->enter_cond(&cond_vars[COND_suspend_or_resume], &LOCK_scheduler_data,
- "Suspended");
- /* Send back signal to the thread that asked us to suspend operations */
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
- sql_print_information("SCHEDULER: Suspending operations");
- was_suspended= TRUE;
- }
- while (state == SUSPENDED)
- {
- cond_wait(COND_suspend_or_resume, &LOCK_scheduler_data);
- DBUG_PRINT("info", ("Woke up after waiting on COND_suspend_or_resume"));
- if (state != SUSPENDED)
- {
- pthread_cond_signal(&cond_vars[COND_suspend_or_resume]);
- sql_print_information("SCHEDULER: Resuming operations");
- }
- }
- if (was_suspended)
- {
- if (queue.elements)
- {
- uint i;
- DBUG_PRINT("info", ("We have to recompute the execution times"));
-
- for (i= 0; i < queue.elements; i++)
- {
- ((Event_timed*)queue_element(&queue, i))->compute_next_execution_time();
- ((Event_timed*)queue_element(&queue, i))->update_fields(thd);
- }
- queue_fix(&queue);
- }
- /* This will implicitly unlock LOCK_scheduler_data */
- thd->exit_cond("");
- }
- DBUG_RETURN(was_suspended);
-}
-
-
-/*
- Checks for empty queue and waits till new element gets in
-
- SYNOPSIS
- Event_scheduler::check_n_wait_for_non_empty_queue()
- thd Thread
-
- RETURN VALUE
- FALSE Did not wait - LOCK_scheduler_data still locked.
- TRUE Waited - LOCK_scheduler_data unlocked.
-
- NOTE
- The caller should have locked LOCK_scheduler_data!
-*/
-
-bool
-Event_scheduler::check_n_wait_for_non_empty_queue(THD *thd)
-{
- bool slept= FALSE;
- DBUG_ENTER("Event_scheduler::check_n_wait_for_non_empty_queue");
- DBUG_PRINT("enter", ("q.elements=%lu state=%s",
- queue.elements, states_names[state]));
-
- if (!queue.elements)
- thd->enter_cond(&cond_vars[COND_new_work], &LOCK_scheduler_data,
- "Empty queue, sleeping");
-
- /* Wait in a loop protecting against catching spurious signals */
- while (!queue.elements && state == RUNNING)
- {
- slept= TRUE;
- DBUG_PRINT("info", ("Entering condition because of empty queue"));
- cond_wait(COND_new_work, &LOCK_scheduler_data);
- DBUG_PRINT("info", ("Manager woke up. Hope we have events now. state=%d",
- state));
- /*
- exit_cond does implicit mutex_UNLOCK, we needed it locked if
- 1. we loop again
- 2. end the current loop and start doing calculations
- */
- }
- if (slept)
- thd->exit_cond("");
-
- DBUG_PRINT("exit", ("q.elements=%lu state=%s thd->killed=%d",
- queue.elements, states_names[state], thd->killed));
-
- DBUG_RETURN(slept);
-}
-
-
-/*
- Wrapper for pthread_mutex_lock
-
- SYNOPSIS
- Event_scheduler::lock_data()
- mutex Mutex to lock
- line The line number on which the lock is done
-
- RETURN VALUE
- Error code of pthread_mutex_lock()
-*/
-
-inline void
-Event_scheduler::lock_data(const char *func, uint line)
-{
- DBUG_ENTER("Event_scheduler::lock_mutex");
- DBUG_PRINT("enter", ("mutex_lock=%p func=%s line=%u",
- &LOCK_scheduler_data, func, line));
- pthread_mutex_lock(&LOCK_scheduler_data);
- mutex_last_locked_in_func= func;
- mutex_last_locked_at_line= line;
- mutex_scheduler_data_locked= TRUE;
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Wrapper for pthread_mutex_unlock
-
- SYNOPSIS
- Event_scheduler::unlock_data()
- mutex Mutex to unlock
- line The line number on which the unlock is done
-*/
-
-inline void
-Event_scheduler::unlock_data(const char *func, uint line)
-{
- DBUG_ENTER("Event_scheduler::UNLOCK_mutex");
- DBUG_PRINT("enter", ("mutex_unlock=%p func=%s line=%u",
- &LOCK_scheduler_data, func, line));
- mutex_last_unlocked_at_line= line;
- mutex_scheduler_data_locked= FALSE;
- mutex_last_unlocked_in_func= func;
- pthread_mutex_unlock(&LOCK_scheduler_data);
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Wrapper for pthread_cond_wait
-
- SYNOPSIS
- Event_scheduler::cond_wait()
- cond Conditional to wait for
- mutex Mutex of the conditional
-
- RETURN VALUE
- Error code of pthread_cond_wait()
-*/
-
-inline int
-Event_scheduler::cond_wait(enum Event_scheduler::enum_cond_vars cond,
- pthread_mutex_t *mutex)
-{
- int ret;
- DBUG_ENTER("Event_scheduler::cond_wait");
- DBUG_PRINT("enter", ("cond=%s mutex=%p", cond_vars_names[cond], mutex));
- ret= pthread_cond_wait(&cond_vars[cond_waiting_on=cond], mutex);
- cond_waiting_on= COND_NONE;
- DBUG_RETURN(ret);
-}
-
-
-/*
- Checks whether the scheduler is in a running or suspended state.
-
- SYNOPSIS
- Event_scheduler::is_running_or_suspended()
-
- RETURN VALUE
- TRUE Either running or suspended
- FALSE IN_SHUTDOWN, not started, etc.
-*/
-
-inline bool
-Event_scheduler::is_running_or_suspended()
-{
- return (state == SUSPENDED || state == RUNNING);
-}
-
-
-/*
- Returns the current state of the scheduler
-
- SYNOPSIS
- Event_scheduler::get_state()
-*/
-
-enum Event_scheduler::enum_state
-Event_scheduler::get_state()
-{
- enum Event_scheduler::enum_state ret;
- DBUG_ENTER("Event_scheduler::get_state");
- /* lock_data & unlock_data are not static */
- pthread_mutex_lock(&singleton.LOCK_scheduler_data);
- ret= singleton.state;
- pthread_mutex_unlock(&singleton.LOCK_scheduler_data);
- DBUG_RETURN(ret);
-}
-
-
-/*
- Returns whether the scheduler was initialized.
-
- SYNOPSIS
- Event_scheduler::initialized()
-
- RETURN VALUE
- FALSE Was not initialized so far
- TRUE Was initialized
-*/
-
-bool
-Event_scheduler::initialized()
-{
- DBUG_ENTER("Event_scheduler::initialized");
- DBUG_RETURN(Event_scheduler::get_state() != UNINITIALIZED);
-}
-
-
-/*
- Returns the number of elements in the queue
-
- SYNOPSIS
- Event_scheduler::events_count()
-
- RETURN VALUE
- 0 Number of Event_timed objects in the queue
-*/
-
-uint
-Event_scheduler::events_count()
-{
- uint n;
- DBUG_ENTER("Event_scheduler::events_count");
- LOCK_SCHEDULER_DATA();
- n= queue.elements;
- UNLOCK_SCHEDULER_DATA();
-
- DBUG_RETURN(n);
-}
-
-
-/*
- Looks for a named event in mysql.event and then loads it from
- the table, compiles and inserts it into the cache.
-
- SYNOPSIS
- Event_scheduler::load_named_event()
- thd THD
- etn The name of the event to load and compile on scheduler's root
- etn_new The loaded event
-
- RETURN VALUE
- NULL Error during compile or the event is non-enabled.
- otherwise Address
-*/
-
-enum Event_scheduler::enum_error_code
-Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new)
-{
- int ret= 0;
- MEM_ROOT *tmp_mem_root;
- Event_timed *et_loaded= NULL;
- Open_tables_state backup;
-
- DBUG_ENTER("Event_scheduler::load_and_compile_event");
- DBUG_PRINT("enter",("thd=%p name:%*s",thd, etn->name.length, etn->name.str));
-
- thd->reset_n_backup_open_tables_state(&backup);
- /* No need to use my_error() here because db_find_event() has done it */
- {
- sp_name spn(etn->dbname, etn->name);
- ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root);
- }
- thd->restore_backup_open_tables_state(&backup);
- /* In this case no memory was allocated so we don't need to clean */
- if (ret)
- DBUG_RETURN(OP_LOAD_ERROR);
-
- if (et_loaded->status != Event_timed::ENABLED)
- {
- /*
- We don't load non-enabled events.
- In db_find_event() `et_new` was allocated on the heap and not on
- scheduler_root therefore we delete it here.
- */
- delete et_loaded;
- DBUG_RETURN(OP_DISABLED_EVENT);
- }
-
- et_loaded->compute_next_execution_time();
- *etn_new= et_loaded;
-
- DBUG_RETURN(OP_OK);
-}
-
-
-/*
- 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
- Event_scheduler::load_events_from_db()
- thd - Thread context. Used for memory allocation in some cases.
-
- RETURN VALUE
- 0 OK
- !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
- EVEX_COMPILE_ERROR) - in all these cases mysql.event was
- tampered.
-
- NOTES
- Reports the error to the console
-*/
-
-int
-Event_scheduler::load_events_from_db(THD *thd)
-{
- TABLE *table;
- READ_RECORD read_record_info;
- int ret= -1;
- uint count= 0;
- bool clean_the_queue= FALSE;
- /* Compile the events on this root but only for syntax check, then discard */
- MEM_ROOT boot_root;
-
- DBUG_ENTER("Event_scheduler::load_events_from_db");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- if (state > COMMENCING)
- {
- DBUG_ASSERT(0);
- sql_print_error("SCHEDULER: Trying to load events while already running.");
- DBUG_RETURN(EVEX_GENERAL_ERROR);
- }
-
- if ((ret= Events::open_event_table(thd, TL_READ, &table)))
- {
- sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open.");
- DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
- }
-
- init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
- init_read_record(&read_record_info, thd, table ,NULL,1,0);
- while (!(read_record_info.read_record(&read_record_info)))
- {
- Event_timed *et;
- if (!(et= new Event_timed))
- {
- DBUG_PRINT("info", ("Out of memory"));
- clean_the_queue= TRUE;
- break;
- }
- DBUG_PRINT("info", ("Loading event from row."));
-
- if ((ret= et->load_from_row(&scheduler_root, table)))
- {
- clean_the_queue= TRUE;
- sql_print_error("SCHEDULER: Error while loading from mysql.event. "
- "Table probably corrupted");
- break;
- }
- if (et->status != Event_timed::ENABLED)
- {
- DBUG_PRINT("info",("%s is disabled",et->name.str));
- delete et;
- continue;
- }
-
- DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
-
- /* We load only on scheduler root just to check whether the body compiles */
- switch (ret= et->compile(thd, &boot_root)) {
- case EVEX_MICROSECOND_UNSUP:
- et->free_sp();
- sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
- "supported but found in mysql.event");
- goto end;
- case EVEX_COMPILE_ERROR:
- sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.",
- et->dbname.str, et->name.str);
- goto end;
- default:
- /* Free it, it will be compiled again on the worker thread */
- et->free_sp();
- break;
- }
-
- /* let's find when to be executed */
- if (et->compute_next_execution_time())
- {
- sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
- " Skipping", et->dbname.str, et->name.str);
- continue;
- }
-
- DBUG_PRINT("load_events_from_db", ("Adding %p to the exec list."));
- queue_insert_safe(&queue, (byte *) et);
- count++;
- }
-end:
- end_read_record(&read_record_info);
- free_root(&boot_root, MYF(0));
-
- if (clean_the_queue)
- {
- for (count= 0; count < queue.elements; ++count)
- queue_remove(&queue, 0);
- ret= -1;
- }
- else
- {
- ret= 0;
- sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s");
- }
-
- /* Force close to free memory */
- thd->version--;
-
- close_thread_tables(thd);
-
- DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
- DBUG_RETURN(ret);
-}
-
-
-/*
- 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
- Event_scheduler::check_system_tables()
-*/
-
-bool
-Event_scheduler::check_system_tables(THD *thd)
-{
- TABLE_LIST tables;
- bool not_used;
- Open_tables_state backup;
- bool ret;
-
- DBUG_ENTER("Event_scheduler::check_system_tables");
- DBUG_PRINT("enter", ("thd=%p", thd));
-
- thd->reset_n_backup_open_tables_state(&backup);
-
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "db";
- tables.lock_type= TL_READ;
-
- if ((ret= simple_open_n_lock_tables(thd, &tables)))
- sql_print_error("Cannot open mysql.db");
- else
- {
- ret= table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
- mysql_db_table_fields, &mysql_db_table_last_check,
- ER_CANNOT_LOAD_FROM_TABLE);
- close_thread_tables(thd);
- }
- if (ret)
- DBUG_RETURN(TRUE);
-
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "user";
- tables.lock_type= TL_READ;
-
- if ((ret= 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");
- ret= TRUE;
- }
- close_thread_tables(thd);
- }
-
- thd->restore_backup_open_tables_state(&backup);
-
- DBUG_RETURN(ret);
-}
-
-
-/*
- Inits mutexes.
-
- SYNOPSIS
- Event_scheduler::init_mutexes()
-*/
-
-void
-Event_scheduler::init_mutexes()
-{
- pthread_mutex_init(&singleton.LOCK_scheduler_data, MY_MUTEX_INIT_FAST);
-}
-
-
-/*
- Destroys mutexes.
-
- SYNOPSIS
- Event_scheduler::destroy_mutexes()
-*/
-
-void
-Event_scheduler::destroy_mutexes()
-{
- pthread_mutex_destroy(&singleton.LOCK_scheduler_data);
-}
-
-
-/*
- Dumps some data about the internal status of the scheduler.
-
- SYNOPSIS
- Event_scheduler::dump_internal_status()
- thd THD
-
- RETURN VALUE
- 0 OK
- 1 Error
-*/
-
-int
-Event_scheduler::dump_internal_status(THD *thd)
-{
- DBUG_ENTER("dump_internal_status");
-#ifndef DBUG_OFF
- CHARSET_INFO *scs= system_charset_info;
- Protocol *protocol= thd->protocol;
- List<Item> field_list;
- int ret;
- char tmp_buff[5*STRING_BUFFER_USUAL_SIZE];
- char int_buff[STRING_BUFFER_USUAL_SIZE];
- String tmp_string(tmp_buff, sizeof(tmp_buff), scs);
- String int_string(int_buff, sizeof(int_buff), scs);
- tmp_string.length(0);
- int_string.length(0);
-
- field_list.push_back(new Item_empty_string("Name", 20));
- field_list.push_back(new Item_empty_string("Value",20));
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- DBUG_RETURN(1);
-
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("state"), scs);
- protocol->store(states_names[singleton.state].str,
- states_names[singleton.state].length,
- scs);
-
- ret= protocol->write();
- /*
- If not initialized - don't show anything else. get_instance()
- will otherwise implicitly initialize it. We don't want that.
- */
- if (singleton.state >= INITIALIZED)
- {
- /* last locked at*/
- /*
- The first thing to do, or get_instance() will overwrite the values.
- mutex_last_locked_at_line / mutex_last_unlocked_at_line
- */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("last locked at"), scs);
- tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
- tmp_string.alloced_length(), "%s::%d",
- singleton.mutex_last_locked_in_func,
- singleton.mutex_last_locked_at_line));
- protocol->store(&tmp_string);
- ret= protocol->write();
-
- /* last unlocked at*/
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("last unlocked at"), scs);
- tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
- tmp_string.alloced_length(), "%s::%d",
- singleton.mutex_last_unlocked_in_func,
- singleton.mutex_last_unlocked_at_line));
- protocol->store(&tmp_string);
- ret= protocol->write();
-
- /* waiting on */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("waiting on condition"), scs);
- tmp_string.length(scs->cset->
- snprintf(scs, (char*) tmp_string.ptr(),
- tmp_string.alloced_length(), "%s",
- (singleton.cond_waiting_on != COND_NONE) ?
- cond_vars_names[singleton.cond_waiting_on]:
- "NONE"));
- protocol->store(&tmp_string);
- ret= protocol->write();
-
- Event_scheduler *scheduler= get_instance();
-
- /* workers_count */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("workers_count"), scs);
- int_string.set((longlong) scheduler->workers_count(), scs);
- protocol->store(&int_string);
- ret= protocol->write();
-
- /* queue.elements */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("queue.elements"), scs);
- int_string.set((longlong) scheduler->queue.elements, scs);
- protocol->store(&int_string);
- ret= protocol->write();
-
- /* scheduler_data_locked */
- protocol->prepare_for_resend();
- protocol->store(STRING_WITH_LEN("scheduler data locked"), scs);
- int_string.set((longlong) scheduler->mutex_scheduler_data_locked, scs);
- protocol->store(&int_string);
- ret= protocol->write();
- }
- send_eof(thd);
-#endif
- DBUG_RETURN(0);
-}
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 5ae310bab2a..acd0debe391 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -16,240 +16,4 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-class Event_timed;
-
-class THD;
-typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
-
-int
-events_init();
-
-void
-events_shutdown();
-
-class Event_scheduler
-{
-public:
- /* Return codes */
- enum enum_error_code
- {
- OP_OK= 0,
- OP_NOT_RUNNING,
- OP_CANT_KILL,
- OP_CANT_INIT,
- OP_DISABLED_EVENT,
- OP_LOAD_ERROR,
- OP_ALREADY_EXISTS
- };
-
- enum enum_state
- {
- UNINITIALIZED= 0,
- INITIALIZED,
- COMMENCING,
- CANTSTART,
- RUNNING,
- SUSPENDED,
- IN_SHUTDOWN
- };
-
- enum enum_suspend_or_resume
- {
- SUSPEND= 1,
- RESUME= 2
- };
-
- /* Singleton access */
- static Event_scheduler*
- get_instance();
-
- /* Methods for queue management follow */
-
- enum enum_error_code
- create_event(THD *thd, Event_timed *et, bool check_existence);
-
- enum enum_error_code
- update_event(THD *thd, Event_timed *et, LEX_STRING *new_schema,
- LEX_STRING *new_name);
-
- bool
- drop_event(THD *thd, Event_timed *et);
-
-
- int
- drop_schema_events(THD *thd, LEX_STRING *schema);
-
- int
- drop_user_events(THD *thd, LEX_STRING *definer, uint *dropped_num)
- { DBUG_ASSERT(0); return 0;}
-
- uint
- events_count();
-
- /* State changing methods follow */
-
- bool
- start();
-
- enum enum_error_code
- stop();
-
- bool
- start_suspended();
-
- bool
- run(THD *thd);
-
- enum enum_error_code
- suspend_or_resume(enum enum_suspend_or_resume action);
-
- bool
- init();
-
- void
- destroy();
-
- static void
- init_mutexes();
-
- static void
- destroy_mutexes();
-
- void
- report_error_during_start();
-
- /* Information retrieving methods follow */
-
- enum enum_state
- get_state();
-
- bool
- initialized();
-
- static int
- dump_internal_status(THD *thd);
-
- static bool
- check_system_tables(THD *thd);
-
-private:
- Event_timed *
- find_event(Event_timed *etn, bool remove_from_q);
-
- uint
- workers_count();
-
- bool
- is_running_or_suspended();
-
- /* helper functions */
- bool
- execute_top(THD *thd);
-
- void
- clean_queue(THD *thd);
-
- void
- stop_all_running_events(THD *thd);
-
- enum enum_error_code
- load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_new);
-
- int
- load_events_from_db(THD *thd);
-
- void
- drop_matching_events(THD *thd, LEX_STRING *pattern,
- bool (*)(Event_timed *,LEX_STRING *));
-
- bool
- check_n_suspend_if_needed(THD *thd);
-
- bool
- check_n_wait_for_non_empty_queue(THD *thd);
-
- /* Singleton DP is used */
- Event_scheduler();
-
- enum enum_cond_vars
- {
- COND_NONE= -1,
- /*
- COND_new_work is a conditional used to signal that there is a change
- of the queue that should inform the executor thread that new event should
- be executed sooner than previously expected, because of add/replace event.
- */
- COND_new_work= 0,
- /*
- COND_started is a conditional used to synchronize the thread in which
- ::start() was called and the spawned thread. ::start() spawns a new thread
- and then waits on COND_started but also checks when awaken that `state` is
- either RUNNING or CANTSTART. Then it returns back.
- */
- COND_started_or_stopped,
- /*
- Conditional used for signalling from the scheduler thread back to the
- thread that calls ::suspend() or ::resume. Synchronizing the calls.
- */
- COND_suspend_or_resume,
- /* Must be always last */
- COND_LAST
- };
-
- /* Singleton instance */
- static Event_scheduler singleton;
-
- /* This is the current status of the life-cycle of the manager. */
- enum enum_state state;
-
- /* Set to start the scheduler in suspended state */
- bool start_scheduler_suspended;
-
- /*
- LOCK_scheduler_data is the mutex which protects the access to the
- manager's queue as well as used when signalling COND_new_work,
- COND_started and COND_shutdown.
- */
- pthread_mutex_t LOCK_scheduler_data;
-
- /*
- Holds the thread id of the executor thread or 0 if the executor is not
- running. It is used by ::shutdown() to know which thread to kill with
- kill_one_thread(). The latter wake ups a thread if it is waiting on a
- conditional variable and sets thd->killed to non-zero.
- */
- ulong thread_id;
-
- pthread_cond_t cond_vars[COND_LAST];
- static const char * const cond_vars_names[COND_LAST];
-
- /* The MEM_ROOT of the object */
- MEM_ROOT scheduler_root;
-
- /* The sorted queue with the Event_timed objects */
- QUEUE queue;
-
- uint mutex_last_locked_at_line;
- uint mutex_last_unlocked_at_line;
- const char* mutex_last_locked_in_func;
- const char* mutex_last_unlocked_in_func;
- enum enum_cond_vars cond_waiting_on;
- bool mutex_scheduler_data_locked;
-
- /* helper functions for working with mutexes & conditionals */
- void
- lock_data(const char *func, uint line);
-
- void
- unlock_data(const char *func, uint line);
-
- int
- cond_wait(enum enum_cond_vars, pthread_mutex_t *mutex);
-
-private:
- /* Prevent use of these */
- Event_scheduler(const Event_scheduler &);
- void operator=(Event_scheduler &);
-};
-
#endif /* _EVENT_SCHEDULER_H_ */
diff --git a/sql/event_scheduler_ng.cc b/sql/event_scheduler_ng.cc
new file mode 100644
index 00000000000..4e2820b8f65
--- /dev/null
+++ b/sql/event_scheduler_ng.cc
@@ -0,0 +1,875 @@
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "events.h"
+#include "event_data_objects.h"
+#include "event_scheduler_ng.h"
+#include "event_queue.h"
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#define SCHED_FUNC __FUNCTION__
+#endif
+#else
+#define SCHED_FUNC "<unknown>"
+#endif
+
+#define LOCK_SCHEDULER_DATA() lock_data(SCHED_FUNC, __LINE__)
+#define UNLOCK_SCHEDULER_DATA() unlock_data(SCHED_FUNC, __LINE__)
+#define COND_STATE_WAIT(timer) cond_wait(timer, SCHED_FUNC, __LINE__)
+
+extern pthread_attr_t connection_attrib;
+
+struct scheduler_param
+{
+ THD *thd;
+ Event_scheduler_ng *scheduler;
+};
+
+struct scheduler_param scheduler_param_value;
+
+
+
+static
+LEX_STRING scheduler_states_names[] =
+{
+ { C_STRING_WITH_LEN("INITIALIZED")},
+ { C_STRING_WITH_LEN("RUNNING")},
+ { C_STRING_WITH_LEN("STOPPING")}
+};
+
+
+/*
+ Prints the stack of infos, warnings, errors from thd to
+ the console so it can be fetched by the logs-into-tables and
+ checked later.
+
+ SYNOPSIS
+ evex_print_warnings
+ thd Thread used during the execution of the event
+ et The event itself
+*/
+
+static void
+evex_print_warnings(THD *thd, Event_job_data *et)
+{
+ MYSQL_ERROR *err;
+ DBUG_ENTER("evex_print_warnings");
+ if (!thd->warn_list.elements)
+ DBUG_VOID_RETURN;
+
+ char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
+ char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE];
+ String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info);
+ prefix.length(0);
+ prefix.append("SCHEDULER: [");
+
+ append_identifier(thd, &prefix, et->definer.str, et->definer.length);
+ prefix.append("][", 2);
+ append_identifier(thd,&prefix, et->dbname.str, et->dbname.length);
+ prefix.append('.');
+ append_identifier(thd,&prefix, et->name.str, et->name.length);
+ prefix.append("] ", 2);
+
+ List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ while ((err= it++))
+ {
+ String err_msg(msg_buf, sizeof(msg_buf), system_charset_info);
+ /* set it to 0 or we start adding at the end. That's the trick ;) */
+ err_msg.length(0);
+ err_msg.append(prefix);
+ err_msg.append(err->msg, strlen(err->msg), system_charset_info);
+ err_msg.append("]");
+ DBUG_ASSERT(err->level < 3);
+ (sql_print_message_handlers[err->level])("%*s", err_msg.length(),
+ err_msg.c_ptr());
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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.
+
+ RETURN VALUE
+ 0 OK
+ -1 Error
+*/
+
+static int
+init_scheduler_thread(THD* thd)
+{
+ DBUG_ENTER("init_event_thread");
+ thd->client_capabilities= 0;
+ thd->security_ctx->master_access= 0;
+ thd->security_ctx->db_access= 0;
+ thd->security_ctx->host_or_ip= (char*)my_localhost;
+ thd->security_ctx->set_user((char*)"event_scheduler");
+ my_net_init(&thd->net, NULL);
+ thd->net.read_timeout= slave_net_timeout;
+ thd->slave_thread= 0;
+ thd->options|= OPTION_AUTO_IS_NULL;
+ thd->client_capabilities|= CLIENT_MULTI_RESULTS;
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thread_id++;
+ threads.append(thd);
+ thread_count++;
+ thread_running++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ /*
+ Guarantees that we will see the thread in SHOW PROCESSLIST though its
+ vio is NULL.
+ */
+
+ thd->proc_info= "Initialized";
+ thd->version= refresh_version;
+ thd->set_time();
+
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Cleans up the THD and the threaded environment of the thread.
+
+ SYNOPSIS
+ deinit_event_thread()
+ thd Thread
+*/
+
+static void
+deinit_event_thread(THD *thd)
+{
+ thd->proc_info= "Clearing";
+ DBUG_ASSERT(thd->net.buff != 0);
+ net_end(&thd->net);
+ DBUG_PRINT("exit", ("Scheduler thread finishing"));
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ my_thread_end();
+}
+
+/*
+ Function that executes the scheduler,
+
+ SYNOPSIS
+ event_scheduler_ng_thread()
+ arg Pointer to `struct scheduler_param`
+
+ RETURN VALUE
+ 0 OK
+*/
+
+pthread_handler_t
+event_scheduler_ng_thread(void *arg)
+{
+ /* needs to be first for thread_stack */
+ THD *thd= (THD *)(*(struct scheduler_param *) arg).thd;
+
+ thd->thread_stack= (char *)&thd; // remember where our stack is
+ DBUG_ENTER("event_scheduler_ng_thread");
+
+ my_thread_init();
+ pthread_detach_this_thread();
+ thd->real_id=pthread_self();
+ if (init_thr_lock() || thd->store_globals())
+ {
+ thd->cleanup();
+ goto end;
+ }
+
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+
+ ((struct scheduler_param *) arg)->scheduler->run(thd);
+
+end:
+ deinit_event_thread(thd);
+
+ DBUG_RETURN(0); // Against gcc warnings
+}
+
+
+/*
+ Function that executes an event in a child thread. Setups the
+ environment for the event execution and cleans after that.
+
+ SYNOPSIS
+ event_worker_ng_thread()
+ arg The Event_job_data object to be processed
+
+ RETURN VALUE
+ 0 OK
+*/
+
+pthread_handler_t
+event_worker_ng_thread(void *arg)
+{
+ /* needs to be first for thread_stack */
+ THD *thd;
+ Event_job_data *event= (Event_job_data *)arg;
+ int ret;
+
+ thd= event->thd;
+ thd->thread_stack= (char *) &thd;
+
+
+ my_thread_init();
+ pthread_detach_this_thread();
+ thd->real_id=pthread_self();
+ if (init_thr_lock() || thd->store_globals())
+ {
+ thd->cleanup();
+ goto end;
+ }
+
+
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK, &set, &thd->block_signals));
+#endif
+ thd->init_for_queries();
+
+ DBUG_ENTER("event_worker_ng_thread");
+ DBUG_PRINT("info", ("Baikonur, time is %d, BURAN reporting and operational."
+ "THD=0x%lx", time(NULL), thd));
+
+ sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu",
+ event->dbname.str, event->name.str,
+ event->definer.str, thd->thread_id);
+
+ thd->enable_slow_log= TRUE;
+
+ ret= event->execute(thd, thd->mem_root);
+
+ evex_print_warnings(thd, event);
+
+ sql_print_information("SCHEDULER: [%s.%s of %s] executed "
+ " in thread thread %lu. RetCode=%d",
+ event->dbname.str, event->name.str,
+ event->definer.str, thd->thread_id, ret);
+ if (ret == EVEX_COMPILE_ERROR)
+ sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s",
+ event->dbname.str, event->name.str,
+ event->definer.str);
+ else if (ret == EVEX_MICROSECOND_UNSUP)
+ sql_print_information("SCHEDULER: MICROSECOND is not supported");
+
+end:
+ DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str,
+ event->name.str));
+ delete event;
+
+ deinit_event_thread(thd);
+
+ DBUG_RETURN(0); // Against gcc warnings
+}
+
+
+/*
+ Performs initialization of the scheduler data, outside of the
+ threading primitives.
+
+ SYNOPSIS
+ Event_scheduler_ng::init_scheduler()
+*/
+
+bool
+Event_scheduler_ng::init_scheduler(Event_queue *q)
+{
+ LOCK_SCHEDULER_DATA();
+ thread_id= 0;
+ state= INITIALIZED;
+ queue= q;
+ started_events= 0;
+ UNLOCK_SCHEDULER_DATA();
+
+ return FALSE;
+}
+
+
+void
+Event_scheduler_ng::deinit_scheduler() {}
+
+
+/*
+ Inits scheduler's threading primitives.
+
+ SYNOPSIS
+ Event_scheduler_ng::init_mutexes()
+*/
+
+void
+Event_scheduler_ng::init_mutexes()
+{
+ pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&COND_state, NULL);
+}
+
+
+/*
+ Deinits scheduler's threading primitives.
+
+ SYNOPSIS
+ Event_scheduler_ng::deinit_mutexes()
+*/
+
+void
+Event_scheduler_ng::deinit_mutexes()
+{
+ pthread_mutex_destroy(&LOCK_scheduler_state);
+ pthread_cond_destroy(&COND_state);
+}
+
+
+/*
+ Starts the scheduler (again). Creates a new THD and passes it to
+ a forked thread. Does not wait for acknowledgement from the new
+ thread that it has started. Asynchronous starting. Most of the
+ needed initializations are done in the current thread to minimize
+ the chance of failure in the spawned thread.
+
+ SYNOPSIS
+ Event_scheduler_ng::start()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (not reported)
+*/
+
+bool
+Event_scheduler_ng::start()
+{
+ THD *new_thd= NULL;
+ bool ret= FALSE;
+ pthread_t th;
+ DBUG_ENTER("Event_scheduler_ng::start");
+
+ LOCK_SCHEDULER_DATA();
+ DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state]));
+ if (state > INITIALIZED)
+ goto end;
+
+ if (!(new_thd= new THD) || init_scheduler_thread(new_thd))
+ {
+ sql_print_error("SCHEDULER: Cannot init manager event thread.");
+ ret= TRUE;
+ goto end;
+ }
+ new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
+ new_thd->command= COM_DAEMON;
+
+ scheduler_param_value.thd= new_thd;
+ scheduler_param_value.scheduler= this;
+
+ DBUG_PRINT("info", ("Forking new thread for scheduduler. THD=0x%lx", new_thd));
+ if (pthread_create(&th, &connection_attrib, event_scheduler_ng_thread,
+ (void*)&scheduler_param_value))
+ {
+ DBUG_PRINT("error", ("cannot create a new thread"));
+ state= INITIALIZED;
+ ret= TRUE;
+ }
+ DBUG_PRINT("info", ("Setting state go RUNNING"));
+ state= RUNNING;
+end:
+ UNLOCK_SCHEDULER_DATA();
+
+ if (ret && new_thd)
+ {
+ DBUG_PRINT("info", ("There was an error during THD creation. Clean up"));
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete new_thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ }
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Stops the scheduler (again). Waits for acknowledgement from the
+ scheduler that it has stopped - synchronous stopping.
+
+ SYNOPSIS
+ Event_scheduler_ng::stop()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (not reported)
+*/
+
+bool
+Event_scheduler_ng::stop()
+{
+ THD *thd= current_thd;
+ DBUG_ENTER("Event_scheduler_ng::stop");
+ DBUG_PRINT("enter", ("thd=0x%lx", current_thd));
+
+ LOCK_SCHEDULER_DATA();
+ DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state]));
+ if (state != RUNNING)
+ goto end;
+
+ state= STOPPING;
+
+ DBUG_PRINT("info", ("Manager thread has id %d", thread_id));
+ sql_print_information("SCHEDULER: Killing manager thread %lu", thread_id);
+
+ pthread_cond_signal(&COND_state);
+
+ /* Guarantee we don't catch spurious signals */
+ sql_print_information("SCHEDULER: Waiting the manager thread to reply");
+ do {
+ DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from the manager "
+ "thread. Current value of state is %s . "
+ "workers count=%d", scheduler_states_names[state].str,
+ workers_count()));
+ /* thd could be 0x0, when shutting down */
+ COND_STATE_WAIT(NULL);
+ } while (state == STOPPING);
+ DBUG_PRINT("info", ("Manager thread has cleaned up. Set state to INIT"));
+
+ thread_id= 0;
+end:
+ UNLOCK_SCHEDULER_DATA();
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ The main loop of the scheduler.
+
+ SYNOPSIS
+ Event_scheduler_ng::run()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (Serious error)
+*/
+
+bool
+Event_scheduler_ng::run(THD *thd)
+{
+ int res;
+ struct timespec abstime;
+ Event_job_data *job_data;
+ DBUG_ENTER("Event_scheduler_ng::run");
+
+ LOCK_SCHEDULER_DATA();
+
+ thread_id= thd->thread_id;
+ sql_print_information("SCHEDULER: Manager thread started with id %lu",
+ thread_id);
+ /*
+ Recalculate the values in the queue because there could have been stops
+ in executions of the scheduler and some times could have passed by.
+ */
+ queue->recalculate_activation_times(thd);
+ while (state == RUNNING)
+ {
+ thd->end_time();
+ /* Gets a minimized version */
+ job_data= queue->
+ get_top_for_execution_if_time(thd, thd->query_start(), &abstime);
+
+ DBUG_PRINT("info", ("get_top returned job_data=0x%lx now=%d "
+ "abs_time.tv_sec=%d",
+ job_data, thd->query_start(), abstime.tv_sec));
+ if (!job_data && !abstime.tv_sec)
+ {
+ DBUG_PRINT("info", ("The queue is empty. Going to sleep"));
+ thd->enter_cond(&COND_state, &LOCK_scheduler_state,
+ "Waiting on empty queue");
+ COND_STATE_WAIT(NULL);
+ thd->exit_cond("");
+ DBUG_PRINT("info", ("Woke up. Got COND_state"));
+ LOCK_SCHEDULER_DATA();
+ }
+ else if (abstime.tv_sec)
+ {
+ DBUG_PRINT("info", ("Have to sleep some time %u till",
+ abstime.tv_sec - thd->query_start(), abstime.tv_sec));
+
+ thd->enter_cond(&COND_state, &LOCK_scheduler_state,
+ "Waiting for next activation");
+ COND_STATE_WAIT(&abstime);
+ /*
+ If we get signal we should recalculate the whether it's the right time
+ because there could be :
+ 1. Spurious wake-up
+ 2. The top of the queue was changed (new one becase of create/update)
+ */
+ /* This will do implicit UNLOCK_SCHEDULER_DATA() */
+ thd->exit_cond("");
+ DBUG_PRINT("info", ("Woke up. Got COND_stat or time for execution."));
+ LOCK_SCHEDULER_DATA();
+ }
+ else
+ {
+ UNLOCK_SCHEDULER_DATA();
+ res= execute_top(thd, job_data);
+ LOCK_SCHEDULER_DATA();
+ if (res)
+ break;
+ ++started_events;
+ }
+ DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
+ }
+ DBUG_PRINT("info", ("Signalling back to the stopper COND_state"));
+ pthread_cond_signal(&COND_state);
+error:
+ state= INITIALIZED;
+ UNLOCK_SCHEDULER_DATA();
+ sql_print_information("SCHEDULER: Stopped");
+
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Creates a new THD instance and then forks a new thread, while passing
+ the THD pointer and job_data to it.
+
+ SYNOPSIS
+ Event_scheduler_ng::execute_top()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (Serious error)
+*/
+
+bool
+Event_scheduler_ng::execute_top(THD *thd, Event_job_data *job_data)
+{
+ THD *new_thd;
+ pthread_t th;
+ int res= 0;
+ DBUG_ENTER("Event_scheduler_ng::execute_top");
+ if (!(new_thd= new THD) || init_scheduler_thread(new_thd))
+ goto error;
+
+ new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
+ job_data->thd= new_thd;
+ DBUG_PRINT("info", ("BURAN %s@%s ready for start t-3..2..1..0..ignition",
+ job_data->dbname.str, job_data->name.str));
+
+ /* Major failure */
+ if ((res= pthread_create(&th, &connection_attrib, event_worker_ng_thread,
+ job_data)))
+ goto error;
+
+ DBUG_PRINT("info", ("Launch succeeded. BURAN is in THD=0x%lx", new_thd));
+ DBUG_RETURN(FALSE);
+
+error:
+ DBUG_PRINT("error", ("Baikonur, we have a problem! res=%d", res));
+ if (new_thd)
+ {
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete new_thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ }
+ delete job_data;
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Returns the current state of the scheduler
+
+ SYNOPSIS
+ Event_scheduler_ng::get_state()
+
+ RETURN VALUE
+ The state of the scheduler (INITIALIZED | RUNNING | STOPPING)
+*/
+
+enum Event_scheduler_ng::enum_state
+Event_scheduler_ng::get_state()
+{
+ enum Event_scheduler_ng::enum_state ret;
+ LOCK_SCHEDULER_DATA();
+ ret= state;
+ UNLOCK_SCHEDULER_DATA();
+ return ret;
+}
+
+
+/*
+ Returns the number of living event worker threads.
+
+ SYNOPSIS
+ Event_scheduler_ng::workers_count()
+*/
+
+uint
+Event_scheduler_ng::workers_count()
+{
+ THD *tmp;
+ uint count= 0;
+
+ DBUG_ENTER("Event_scheduler_ng::workers_count");
+ pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+ I_List_iterator<THD> it(threads);
+ while ((tmp=it++))
+ {
+ if (tmp->command == COM_DAEMON)
+ continue;
+ if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
+ ++count;
+ }
+ pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_PRINT("exit", ("%d", count));
+ DBUG_RETURN(count);
+}
+
+
+/*
+ Signals the main scheduler thread that the queue has changed
+ its state.
+
+ SYNOPSIS
+ Event_scheduler_ng::queue_changed()
+*/
+
+void
+Event_scheduler_ng::queue_changed()
+{
+ DBUG_ENTER("Event_scheduler_ng::queue_changed");
+ DBUG_PRINT("info", ("Sending COND_state. state (read wo lock)=%s ",
+ scheduler_states_names[state].str));
+ pthread_cond_signal(&COND_state);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Auxiliary function for locking LOCK_scheduler_state. Used
+ by the LOCK_SCHEDULER_DATA macro.
+
+ SYNOPSIS
+ Event_scheduler_ng::lock_data()
+ func Which function is requesting mutex lock
+ line On which line mutex lock is requested
+*/
+
+void
+Event_scheduler_ng::lock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_scheduler_ng::lock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ pthread_mutex_lock(&LOCK_scheduler_state);
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_scheduler_data_locked= TRUE;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Auxiliary function for unlocking LOCK_scheduler_state. Used
+ by the UNLOCK_SCHEDULER_DATA macro.
+
+ SYNOPSIS
+ Event_scheduler_ng::unlock_data()
+ func Which function is requesting mutex unlock
+ line On which line mutex unlock is requested
+*/
+
+void
+Event_scheduler_ng::unlock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_scheduler_ng::unlock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ mutex_last_unlocked_at_line= line;
+ mutex_scheduler_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ pthread_mutex_unlock(&LOCK_scheduler_state);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Wrapper for pthread_cond_wait/timedwait
+
+ SYNOPSIS
+ Event_scheduler_ng::cond_wait()
+ cond Conditional to wait for
+ mutex Mutex of the conditional
+
+ RETURN VALUE
+ Error code of pthread_cond_wait()
+*/
+
+void
+Event_scheduler_ng::cond_wait(struct timespec *abstime,
+ const char *func, uint line)
+{
+ DBUG_ENTER("Event_scheduler_ng::cond_wait");
+ waiting_on_cond= TRUE;
+ mutex_last_unlocked_at_line= line;
+ mutex_scheduler_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+
+ if (abstime)
+ pthread_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
+ else
+ pthread_cond_wait(&COND_state, &LOCK_scheduler_state);
+
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_scheduler_data_locked= TRUE;
+ waiting_on_cond= FALSE;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the internal status of the scheduler
+
+ SYNOPSIS
+ Event_scheduler_ng::dump_internal_status()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_scheduler_ng::dump_internal_status(THD *thd)
+{
+ int ret= 0;
+ DBUG_ENTER("Event_scheduler_ng::dump_internal_status");
+
+#ifndef DBUG_OFF
+ CHARSET_INFO *scs= system_charset_info;
+ Protocol *protocol= thd->protocol;
+ char tmp_buff[5*STRING_BUFFER_USUAL_SIZE];
+ char int_buff[STRING_BUFFER_USUAL_SIZE];
+ String tmp_string(tmp_buff, sizeof(tmp_buff), scs);
+ String int_string(int_buff, sizeof(int_buff), scs);
+ tmp_string.length(0);
+ int_string.length(0);
+
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler state"), scs);
+ protocol->store(scheduler_states_names[state].str,
+ scheduler_states_names[state].length, scs);
+
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* thread_id */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("thread_id"), scs);
+ if (thread_id)
+ {
+ int_string.set((longlong) thread_id, scs);
+ protocol->store(&int_string);
+ }
+ else
+ protocol->store_null();
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* last locked at*/
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler last locked at"), scs);
+ tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
+ tmp_string.alloced_length(), "%s::%d",
+ mutex_last_locked_in_func,
+ mutex_last_locked_at_line));
+ protocol->store(&tmp_string);
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* last unlocked at*/
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler last unlocked at"), scs);
+ tmp_string.length(scs->cset->snprintf(scs, (char*) tmp_string.ptr(),
+ tmp_string.alloced_length(), "%s::%d",
+ mutex_last_unlocked_in_func,
+ mutex_last_unlocked_at_line));
+ protocol->store(&tmp_string);
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* waiting on */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler waiting on condition"), scs);
+ int_string.set((longlong) waiting_on_cond, scs);
+ protocol->store(&int_string);
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* workers_count */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler workers count"), scs);
+ int_string.set((longlong) workers_count(), scs);
+ protocol->store(&int_string);
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* workers_count */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler executed events"), scs);
+ int_string.set((longlong) started_events, scs);
+ protocol->store(&int_string);
+ if ((ret= protocol->write()))
+ goto end;
+
+ /* scheduler_data_locked */
+ protocol->prepare_for_resend();
+ protocol->store(STRING_WITH_LEN("scheduler data locked"), scs);
+ int_string.set((longlong) mutex_scheduler_data_locked, scs);
+ protocol->store(&int_string);
+ ret= protocol->write();
+end:
+#endif
+
+ DBUG_RETURN(ret);
+}
diff --git a/sql/event_scheduler_ng.h b/sql/event_scheduler_ng.h
new file mode 100644
index 00000000000..e4f3f0588f9
--- /dev/null
+++ b/sql/event_scheduler_ng.h
@@ -0,0 +1,123 @@
+#ifndef _EVENT_SCHEDULER_NG_H_
+#define _EVENT_SCHEDULER_NG_H_
+/* Copyright (C) 2004-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+class Event_queue;
+class Event_job_data;
+
+class Event_scheduler_ng
+{
+public:
+ Event_scheduler_ng(){}
+ ~Event_scheduler_ng(){}
+
+ enum enum_state
+ {
+ INITIALIZED = 0,
+ RUNNING,
+ STOPPING
+ };
+
+ /* State changing methods follow */
+
+ bool
+ start();
+
+ bool
+ stop();
+
+ /*
+ Need to be public because has to be called from the function
+ passed to pthread_create.
+ */
+ bool
+ run(THD *thd);
+
+ bool
+ init_scheduler(Event_queue *queue);
+
+ void
+ deinit_scheduler();
+
+ void
+ init_mutexes();
+
+ void
+ deinit_mutexes();
+
+ /* Information retrieving methods follow */
+
+ enum enum_state
+ get_state();
+
+ void
+ queue_changed();
+
+ bool
+ dump_internal_status(THD *thd);
+
+private:
+ uint
+ workers_count();
+
+ /* helper functions */
+ bool
+ execute_top(THD *thd, Event_job_data *job_data);
+
+ /* helper functions for working with mutexes & conditionals */
+ void
+ lock_data(const char *func, uint line);
+
+ void
+ unlock_data(const char *func, uint line);
+
+ void
+ cond_wait(struct timespec *abstime, const char *func, uint line);
+
+ pthread_mutex_t LOCK_scheduler_state;
+
+ /* This is the current status of the life-cycle of the scheduler. */
+ enum enum_state state;
+
+ /*
+ Holds the thread id of the executor thread or 0 if the scheduler is not
+ running. It is used by ::shutdown() to know which thread to kill with
+ kill_one_thread(). The latter wake ups a thread if it is waiting on a
+ conditional variable and sets thd->killed to non-zero.
+ */
+ ulong thread_id;
+
+ pthread_cond_t COND_state;
+
+ Event_queue *queue;
+
+ uint mutex_last_locked_at_line;
+ uint mutex_last_unlocked_at_line;
+ const char* mutex_last_locked_in_func;
+ const char* mutex_last_unlocked_in_func;
+ bool mutex_scheduler_data_locked;
+ bool waiting_on_cond;
+
+ ulonglong started_events;
+
+private:
+ /* Prevent use of these */
+ Event_scheduler_ng(const Event_scheduler_ng &);
+ void operator=(Event_scheduler_ng &);
+};
+
+#endif /* _EVENT_SCHEDULER_NG_H_ */
diff --git a/sql/events.cc b/sql/events.cc
index 210cc2c4735..08e17a9578a 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -15,11 +15,11 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
-#include "events_priv.h"
#include "events.h"
-#include "event_timed.h"
-#include "event_scheduler.h"
-#include "sp.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "event_queue.h"
+#include "event_scheduler_ng.h"
#include "sp_head.h"
/*
@@ -48,9 +48,20 @@ Warning:
*/
-MEM_ROOT evex_mem_root;
-time_t mysql_event_last_create_time= 0L;
-
+/*
+ If the user (un)intentionally removes an event directly from mysql.event
+ the following sequence has to be used to be able to remove the in-memory
+ counterpart.
+ 1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
+ 2. DROP EVENT the_name
+
+ In other words, the first one will create a row in mysql.event . In the
+ second step because there will be a line, disk based drop will pass and
+ the scheduler will remove the memory counterpart. The reason is that
+ in-memory queue does not check whether the event we try to drop from memory
+ is disabled. Disabled events are not kept in-memory because they are not
+ eligible for execution.
+*/
const char *event_scheduler_state_names[]=
{ "OFF", "0", "ON", "1", "SUSPEND", "2", NullS };
@@ -63,104 +74,10 @@ TYPELIB Events::opt_typelib=
NULL
};
+Events Events::singleton;
ulong Events::opt_event_scheduler= 2;
-static
-TABLE_FIELD_W_TYPE event_table_fields[Events::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")},
- {NULL, 0}
- },
- {
- {(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")}
- }
-};
-
/*
Compares 2 LEX strings regarding case.
@@ -187,6 +104,23 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
(uchar *) t.str,t.length, 0);
}
+/*
+ Accessor for the singleton instance.
+
+ SYNOPSIS
+ Events::get_instance()
+
+ RETURN VALUE
+ address
+*/
+
+Events *
+Events::get_instance()
+{
+ DBUG_ENTER("Events::get_instance");
+ DBUG_RETURN(&singleton);
+}
+
/*
Reconstructs interval expression from interval type and expression
@@ -201,15 +135,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
interval - the interval type (for instance YEAR_MONTH)
expression - the value in the lowest entity
- RETURNS
+ RETURN VALUE
0 - OK
1 - Error
*/
int
-Events::reconstruct_interval_expression(String *buf,
- interval_type interval,
- longlong expression)
+Events::reconstruct_interval_expression(String *buf, interval_type interval,
+ longlong expression)
{
ulonglong expr= expression;
char tmp_buff[128], *end;
@@ -269,7 +202,7 @@ common_1_lev_code:
expr= tmp_expr - (tmp_expr/60)*60;
/* the code after the switch will finish */
}
- break;
+ break;
case INTERVAL_DAY_SECOND:
{
ulonglong tmp_expr= expr;
@@ -341,545 +274,7 @@ int
Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
TABLE **table)
{
- TABLE_LIST tables;
- DBUG_ENTER("open_events_table");
-
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "event";
- tables.lock_type= lock_type;
-
- if (simple_open_n_lock_tables(thd, &tables))
- DBUG_RETURN(1);
-
- if (table_check_intact(tables.table, Events::FIELD_COUNT,
- event_table_fields,
- &mysql_event_last_create_time,
- ER_CANNOT_LOAD_FROM_TABLE))
- {
- close_thread_tables(thd);
- DBUG_RETURN(2);
- }
- *table= tables.table;
- tables.table->use_all_columns();
- DBUG_RETURN(0);
-}
-
-
-/*
- Find row in open mysql.event table representing event
-
- SYNOPSIS
- evex_db_find_event_aux()
- thd Thread context
- et event_timed object containing dbname & name
- table TABLE object for open mysql.event table.
-
- RETURN VALUE
- 0 - Routine found
- EVEX_KEY_NOT_FOUND - No routine with given name
-*/
-
-inline int
-evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table)
-{
- return evex_db_find_event_by_name(thd, et->dbname, et->name, table);
-}
-
-
-/*
- Find row in open mysql.event table representing event
-
- SYNOPSIS
- evex_db_find_event_by_name()
- thd Thread context
- dbname Name of event's database
- rname Name of the event inside the db
- table TABLE object for open mysql.event table.
-
- RETURN VALUE
- 0 - Routine found
- EVEX_KEY_NOT_FOUND - No routine with given name
-*/
-
-int
-evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
- const LEX_STRING ev_name,
- TABLE *table)
-{
- byte key[MAX_KEY_LENGTH];
- DBUG_ENTER("evex_db_find_event_by_name");
- DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str));
-
- /*
- Create key to find row. We have to use field->store() to be able to
- handle VARCHAR and CHAR fields.
- Assumption here is that the two first fields in the table are
- 'db' and 'name' and the first key is the primary key over the
- same fields.
- */
- if (dbname.length > table->field[Events::FIELD_DB]->field_length ||
- ev_name.length > table->field[Events::FIELD_NAME]->field_length)
-
- DBUG_RETURN(EVEX_KEY_NOT_FOUND);
-
- table->field[Events::FIELD_DB]->store(dbname.str, dbname.length,
- &my_charset_bin);
- table->field[Events::FIELD_NAME]->store(ev_name.str, ev_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,
- table->key_info->key_length,
- HA_READ_KEY_EXACT))
- {
- DBUG_PRINT("info", ("Row not found"));
- DBUG_RETURN(EVEX_KEY_NOT_FOUND);
- }
-
- DBUG_PRINT("info", ("Row found!"));
- DBUG_RETURN(0);
-}
-
-
-/*
- Puts some data common to CREATE and ALTER EVENT into a row.
-
- SYNOPSIS
- evex_fill_row()
- thd THD
- table the row to fill out
- et Event's data
-
- RETURN VALUE
- 0 - OK
- EVEX_GENERAL_ERROR - bad data
- EVEX_GET_FIELD_FAILED - field count does not match. table corrupted?
-
- DESCRIPTION
- Used both when an event is created and when it is altered.
-*/
-
-static int
-evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update)
-{
- CHARSET_INFO *scs= system_charset_info;
- enum Events::enum_table_field field_num;
-
- DBUG_ENTER("evex_fill_row");
-
- 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= Events::FIELD_DEFINER]->
- store(et->definer.str, et->definer.length, scs))
- goto err_truncate;
-
- if (table->field[field_num= Events::FIELD_DB]->
- store(et->dbname.str, et->dbname.length, scs))
- goto err_truncate;
-
- if (table->field[field_num= Events::FIELD_NAME]->
- store(et->name.str, et->name.length, scs))
- goto err_truncate;
-
- /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
- table->field[Events::FIELD_ON_COMPLETION]->
- store((longlong)et->on_completion, true);
-
- table->field[Events::FIELD_STATUS]->store((longlong)et->status, true);
-
- /*
- Change the SQL_MODE only if body was present in an ALTER EVENT and of course
- always during CREATE EVENT.
- */
- if (et->body.str)
- {
- table->field[Events::FIELD_SQL_MODE]->
- store((longlong)thd->variables.sql_mode, true);
-
- if (table->field[field_num= Events::FIELD_BODY]->
- store(et->body.str, et->body.length, scs))
- goto err_truncate;
- }
-
- if (et->expression)
- {
- table->field[Events::FIELD_INTERVAL_EXPR]->set_notnull();
- table->field[Events::FIELD_INTERVAL_EXPR]->
- store((longlong)et->expression, true);
-
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_notnull();
- /*
- In the enum (C) intervals start from 0 but in mysql enum valid values start
- from 1. Thus +1 offset is needed!
- */
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->
- store((longlong)et->interval+1, true);
-
- table->field[Events::FIELD_EXECUTE_AT]->set_null();
-
- if (!et->starts_null)
- {
- table->field[Events::FIELD_STARTS]->set_notnull();
- table->field[Events::FIELD_STARTS]->
- store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
- }
-
- if (!et->ends_null)
- {
- table->field[Events::FIELD_ENDS]->set_notnull();
- table->field[Events::FIELD_ENDS]->
- store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
- }
- }
- else if (et->execute_at.year)
- {
- table->field[Events::FIELD_INTERVAL_EXPR]->set_null();
- table->field[Events::FIELD_TRANSIENT_INTERVAL]->set_null();
- table->field[Events::FIELD_STARTS]->set_null();
- table->field[Events::FIELD_ENDS]->set_null();
-
- table->field[Events::FIELD_EXECUTE_AT]->set_notnull();
- table->field[Events::FIELD_EXECUTE_AT]->
- store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
- }
- else
- {
- DBUG_ASSERT(is_update);
- /*
- it is normal to be here when the action is update
- this is an error if the action is create. something is borked
- */
- }
-
- ((Field_timestamp *)table->field[Events::FIELD_MODIFIED])->set_time();
-
- if (et->comment.str)
- {
- if (table->field[field_num= Events::FIELD_COMMENT]->
- store(et->comment.str, et->comment.length, scs))
- goto err_truncate;
- }
-
- DBUG_RETURN(0);
-err_truncate:
- my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
-}
-
-
-/*
- Creates an event in mysql.event
-
- SYNOPSIS
- db_create_event()
- thd THD
- et Event_timed object containing information for the event
- create_if_not If an warning should be generated in case event exists
- rows_affected How many rows were affected
-
- RETURN VALUE
- 0 - OK
- EVEX_GENERAL_ERROR - Failure
-
- DESCRIPTION
- Creates an event. Relies on evex_fill_row which is shared with
- db_update_event. The name of the event is inside "et".
-*/
-
-int
-db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
- uint *rows_affected)
-{
- int ret= 0;
- CHARSET_INFO *scs= system_charset_info;
- TABLE *table;
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged= FALSE;
- DBUG_ENTER("db_create_event");
- DBUG_PRINT("enter", ("name: %.*s", et->name.length, et->name.str));
-
- *rows_affected= 0;
- DBUG_PRINT("info", ("open mysql.event for update"));
- if (Events::open_event_table(thd, TL_WRITE, &table))
- {
- 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, table))
- {
- if (create_if_not)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
- et->name.str);
- goto ok;
- }
- my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str);
- goto err;
- }
-
- DBUG_PRINT("info", ("non-existant, go forward"));
- if ((ret= sp_use_new_db(thd, et->dbname, &old_db, 0, &dbchanged)))
- {
- my_error(ER_BAD_DB_ERROR, MYF(0));
- goto err;
- }
-
- restore_record(table, s->default_values); // Get default values for fields
-
- if (system_charset_info->cset->numchars(system_charset_info, et->dbname.str,
- et->dbname.str + et->dbname.length)
- > EVEX_DB_FIELD_LEN)
- {
- my_error(ER_TOO_LONG_IDENT, MYF(0), et->dbname.str);
- goto err;
- }
- if (system_charset_info->cset->numchars(system_charset_info, et->name.str,
- et->name.str + et->name.length)
- > EVEX_DB_FIELD_LEN)
- {
- my_error(ER_TOO_LONG_IDENT, MYF(0), et->name.str);
- goto err;
- }
-
- if (et->body.length > table->field[Events::FIELD_BODY]->field_length)
- {
- my_error(ER_TOO_LONG_BODY, MYF(0), et->name.str);
- goto err;
- }
-
- if (!(et->expression) && !(et->execute_at.year))
- {
- DBUG_PRINT("error", ("neither expression nor execute_at are set!"));
- my_error(ER_EVENT_NEITHER_M_EXPR_NOR_M_AT, MYF(0));
- goto err;
- }
-
- ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time();
-
- /*
- evex_fill_row() calls my_error() in case of error so no need to
- handle it here
- */
- if ((ret= evex_fill_row(thd, table, et, false)))
- goto err;
-
- if (table->file->ha_write_row(table->record[0]))
- {
- my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
- goto err;
- }
-
-#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- /* Such a statement can always go directly to binlog, no trans cache */
- thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length,
- FALSE, FALSE);
- }
-#endif
-
- *rows_affected= 1;
-ok:
- if (dbchanged)
- (void) mysql_change_db(thd, old_db.str, 1);
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_OK);
-
-err:
- if (dbchanged)
- (void) mysql_change_db(thd, old_db.str, 1);
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
-}
-
-
-/*
- Used to execute ALTER EVENT. Pendant to Events::update_event().
-
- SYNOPSIS
- db_update_event()
- thd THD
- sp_name the name of the event to alter
- et event's data
-
- RETURN VALUE
- 0 OK
- EVEX_GENERAL_ERROR Error occured (my_error() called)
-
- NOTES
- sp_name is passed since this is the name of the event to
- alter in case of RENAME TO.
-*/
-
-static int
-db_update_event(THD *thd, Event_timed *et, sp_name *new_name)
-{
- CHARSET_INFO *scs= system_charset_info;
- 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->definer.length, et->definer.str));
- if (new_name)
- DBUG_PRINT("enter", ("rename to: %.*s", new_name->m_name.length,
- new_name->m_name.str));
-
- if (Events::open_event_table(thd, TL_WRITE, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto err;
- }
-
- /* first look whether we overwrite */
- if (new_name)
- {
- if (!sortcmp_lex_string(et->name, new_name->m_name, scs) &&
- !sortcmp_lex_string(et->dbname, new_name->m_db, scs))
- {
- my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str);
- goto err;
- }
-
- if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table))
- {
- my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
- goto err;
- }
- }
- /*
- ...and then if there is such an event. Don't exchange the blocks
- because you will get error 120 from table handler because new_name will
- overwrite the key and SE will tell us that it cannot find the already found
- row (copied into record[1] later
- */
- if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table))
- {
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
- goto err;
- }
-
- store_record(table,record[1]);
-
- /* Don't update create on row update. */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
- /*
- evex_fill_row() calls my_error() in case of error so no need to
- handle it here
- */
- if ((ret= evex_fill_row(thd, table, et, true)))
- goto err;
-
- if (new_name)
- {
- table->field[Events::FIELD_DB]->
- store(new_name->m_db.str, new_name->m_db.length, scs);
- table->field[Events::FIELD_NAME]->
- store(new_name->m_name.str, new_name->m_name.length, scs);
- }
-
- if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
- {
- my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret);
- goto err;
- }
-
- /* close mysql.event or we crash later when loading the event from disk */
- close_thread_tables(thd);
- DBUG_RETURN(0);
-
-err:
- if (table)
- close_thread_tables(thd);
- DBUG_RETURN(EVEX_GENERAL_ERROR);
-}
-
-
-/*
- Looks for a named event in mysql.event and in case of success returns
- an object will data loaded from the table.
-
- SYNOPSIS
- db_find_event()
- thd THD
- name the name of the event to find
- ett event's data if event is found
- tbl TABLE object to use when not NULL
-
- NOTES
- 1) Use sp_name for look up, return in **ett if found
- 2) tbl is not closed at exit
-
- RETURN VALUE
- 0 ok In this case *ett is set to the event
- # error *ett == 0
-*/
-
-int
-db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
- MEM_ROOT *root)
-{
- TABLE *table;
- int ret;
- 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 (Events::open_event_table(thd, TL_READ, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- ret= EVEX_GENERAL_ERROR;
- goto done;
- }
-
- if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table)))
- {
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
- goto done;
- }
- et= new Event_timed;
-
- /*
- 1)The table should not be closed beforehand. ::load_from_row() only loads
- and does not compile
-
- 2)::load_from_row() is silent on error therefore we emit error msg here
- */
- if ((ret= et->load_from_row(root, table)))
- {
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
- goto done;
- }
-
-done:
- if (ret)
- {
- delete et;
- et= 0;
- }
- /* don't close the table if we haven't opened it ourselves */
- if (!tbl && table)
- close_thread_tables(thd);
- *ett= et;
- DBUG_RETURN(ret);
+ return db_repository->open_event_table(thd, lock_type, table);
}
@@ -896,33 +291,33 @@ done:
RETURN VALUE
0 OK
- !0 Error
+ !0 Error (Reported)
NOTES
- - in case there is an event with the same name (db) and
- IF NOT EXISTS is specified, an warning is put into the W stack.
+ In case there is an event with the same name (db) and
+ IF NOT EXISTS is specified, an warning is put into the stack.
*/
int
-Events::create_event(THD *thd, Event_timed *et, uint create_options,
+Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists,
uint *rows_affected)
{
int ret;
-
DBUG_ENTER("Events::create_event");
- DBUG_PRINT("enter", ("name: %*s options:%d", et->name.length,
- et->name.str, create_options));
- if (!(ret = db_create_event(thd, et,
- create_options & HA_LEX_CREATE_IF_NOT_EXISTS,
- rows_affected)))
+ pthread_mutex_lock(&LOCK_event_metadata);
+ /* On error conditions my_error() is called so no need to handle here */
+ if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists,
+ rows_affected)))
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() &&
- (ret= scheduler->create_event(thd, et, true)))
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
+ if ((ret= event_queue->create_event(thd, parse_data->dbname,
+ parse_data->name)))
+ {
+ DBUG_ASSERT(ret == OP_LOAD_ERROR);
+ my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
+ }
}
- /* No need to close the table, it will be closed in sql_parse::do_command */
+ pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
@@ -934,8 +329,8 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options,
SYNOPSIS
Events::update_event()
thd THD
- et event's data
- new_name set in case of RENAME TO.
+ et Event's data from parsing stage
+ new_name Set in case of RENAME TO.
RETURN VALUE
0 OK
@@ -948,27 +343,28 @@ Events::create_event(THD *thd, Event_timed *et, uint create_options,
*/
int
-Events::update_event(THD *thd, Event_timed *et, sp_name *new_name,
- uint *rows_affected)
+Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
+ uint *rows_affected)
{
int ret;
-
DBUG_ENTER("Events::update_event");
- DBUG_PRINT("enter", ("name: %*s", et->name.length, et->name.str));
- /*
- db_update_event() opens & closes the table to prevent
- crash later in the code when loading and compiling the new definition.
- Also on error conditions my_error() is called so no need to handle here
- */
- if (!(ret= db_update_event(thd, et, new_name)))
+
+ pthread_mutex_lock(&LOCK_event_metadata);
+ /* On error conditions my_error() is called so no need to handle here */
+ if (!(ret= db_repository->update_event(thd, parse_data, new_name)))
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() &&
- (ret= scheduler->update_event(thd, et,
- new_name? &new_name->m_db: NULL,
- new_name? &new_name->m_name: NULL)))
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
+ if ((ret= event_queue->update_event(thd,
+ parse_data->dbname,
+ parse_data->name,
+ new_name? &new_name->m_db: NULL,
+ new_name? &new_name->m_name: NULL)))
+ {
+ DBUG_ASSERT(ret == OP_LOAD_ERROR);
+ my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
+ }
}
+ pthread_mutex_unlock(&LOCK_event_metadata);
+
DBUG_RETURN(ret);
}
@@ -977,95 +373,69 @@ Events::update_event(THD *thd, Event_timed *et, sp_name *new_name,
Drops an event
SYNOPSIS
- db_drop_event()
+ Events::drop_event()
thd THD
- et event's name
- drop_if_exists if set and the event not existing => warning onto the stack
- rows_affected affected number of rows is returned heres
+ dbname Event's schema
+ name Event's name
+ if_exists When set and the event does not exist => warning onto
+ the stack
+ rows_affected Affected number of rows is returned heres
+ only_from_disk Whether to remove the event from the queue too. In case
+ of Event_job_data::drop() it's needed to do only disk
+ drop because Event_queue will handle removal from memory
+ queue.
RETURN VALUE
- 0 OK
- !0 Error (my_error() called)
+ 0 OK
+ !0 Error (reported)
*/
-int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected)
+int
+Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
+ uint *rows_affected, bool only_from_disk)
{
- TABLE *table;
- Open_tables_state backup;
int ret;
+ DBUG_ENTER("Events::drop_event");
- DBUG_ENTER("db_drop_event");
- ret= EVEX_OPEN_TABLE_FAILED;
-
- thd->reset_n_backup_open_tables_state(&backup);
- if (Events::open_event_table(thd, TL_WRITE, &table))
- {
- my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- goto done;
- }
-
- if (!(ret= evex_db_find_event_aux(thd, et, table)))
+ pthread_mutex_lock(&LOCK_event_metadata);
+ /* On error conditions my_error() is called so no need to handle here */
+ if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists,
+ rows_affected)))
{
- if ((ret= table->file->ha_delete_row(table->record[0])))
- {
- my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
- goto done;
- }
- }
- else if (ret == EVEX_KEY_NOT_FOUND)
- {
- if (drop_if_exists)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
- "Event", et->name.str);
- ret= 0;
- } else
- my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
- goto done;
+ if (!only_from_disk)
+ event_queue->drop_event(thd, dbname, name);
}
-
-
-done:
- /*
- evex_drop_event() is used by Event_timed::drop therefore
- we have to close our thread tables.
- */
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
+ pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
/*
- Drops an event
+ Drops all events from a schema
SYNOPSIS
- Events::drop_event()
- thd THD
- et event's name
- drop_if_exists if set and the event not existing => warning onto the stack
- rows_affected affected number of rows is returned heres
+ Events::drop_schema_events()
+ thd Thread
+ db ASCIIZ schema name
RETURN VALUE
- 0 OK
- !0 Error (reported)
+ 0 OK
+ !0 Error
*/
int
-Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected)
+Events::drop_schema_events(THD *thd, char *db)
{
- int ret;
+ int ret= 0;
+ LEX_STRING db_lex= {db, strlen(db)};
+
+ DBUG_ENTER("evex_drop_db_events");
+ DBUG_PRINT("enter", ("dropping events from %s", db));
- DBUG_ENTER("Events::drop_event");
- if (!(ret= db_drop_event(thd, et, drop_if_exists, rows_affected)))
- {
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized() && (ret= scheduler->drop_event(thd, et)))
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
- }
+ pthread_mutex_lock(&LOCK_event_metadata);
+ event_queue->drop_schema_events(thd, db_lex);
+ ret= db_repository->drop_schema_events(thd, db_lex);
+ pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
@@ -1088,14 +458,14 @@ int
Events::show_create_event(THD *thd, sp_name *spn)
{
int ret;
- Event_timed *et= NULL;
+ Event_timed *et= new Event_timed();
Open_tables_state backup;
- DBUG_ENTER("evex_update_event");
+ DBUG_ENTER("Events::show_create_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, &et, NULL, thd->mem_root);
+ ret= db_repository->find_event(thd, spn->m_db, spn->m_name, et);
thd->restore_backup_open_tables_state(&backup);
if (!ret)
@@ -1140,102 +510,46 @@ Events::show_create_event(THD *thd, sp_name *spn)
DBUG_RETURN(ret);
err:
delete et;
- DBUG_RETURN(1);
-}
-
-
-/*
- Drops all events from a schema
-
- SYNOPSIS
- Events::drop_schema_events()
- thd Thread
- db ASCIIZ schema name
-
- RETURN VALUE
- 0 OK
- !0 Error
-*/
-
-int
-Events::drop_schema_events(THD *thd, char *db)
-{
- int ret= 0;
- LEX_STRING db_lex= {db, strlen(db)};
-
- DBUG_ENTER("evex_drop_db_events");
- DBUG_PRINT("enter", ("dropping events from %s", db));
-
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized())
- ret= scheduler->drop_schema_events(thd, &db_lex);
- else
- ret= db_drop_events_from_table(thd, &db_lex);
-
- DBUG_RETURN(ret);
+ DBUG_RETURN(1);
}
/*
- Drops all events in the selected database, from mysql.event.
+ Proxy for Event_db_repository::fill_schema_events.
+ Callback for I_S from sql_show.cc
SYNOPSIS
- evex_drop_db_events_from_table()
- thd Thread
- db Schema name
+ Events::fill_schema_events()
+ thd Thread
+ tables The schema table
+ cond Unused
RETURN VALUE
- 0 OK
- !0 Error from ha_delete_row
+ 0 OK
+ !0 Error
*/
int
-db_drop_events_from_table(THD *thd, LEX_STRING *db)
+Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
- int ret;
- TABLE *table;
- READ_RECORD read_record_info;
- DBUG_ENTER("db_drop_events_from_table");
- DBUG_PRINT("info", ("dropping events from %s", db->str));
-
- if ((ret= Events::open_event_table(thd, TL_WRITE, &table)))
- {
- if (my_errno != ENOENT)
- sql_print_error("Table mysql.event is damaged. Got error %d on open",
- my_errno);
- DBUG_RETURN(ret);
- }
- /* only enabled events are in memory, so we go now and delete the rest */
- init_read_record(&read_record_info, thd, table, NULL, 1, 0);
- while (!(read_record_info.read_record(&read_record_info)) && !ret)
+ char *db= NULL;
+ DBUG_ENTER("Events::fill_schema_events");
+ /*
+ If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
+ be NULL. Let's do an assert anyway.
+ */
+ if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
- char *et_db= get_field(thd->mem_root,
- table->field[Events::FIELD_DB]);
-
- LEX_STRING et_db_lex= {et_db, strlen(et_db)};
- DBUG_PRINT("info", ("Current event %s.%s", et_db,
- get_field(thd->mem_root,
- table->field[Events::FIELD_NAME])));
-
- if (!sortcmp_lex_string(et_db_lex, *db, system_charset_info))
- {
- DBUG_PRINT("info", ("Dropping"));
- if ((ret= table->file->ha_delete_row(table->record[0])))
- my_error(ER_EVENT_DROP_FAILED, MYF(0),
- get_field(thd->mem_root,
- table->field[Events::FIELD_NAME]));
- }
+ DBUG_ASSERT(thd->lex->select_lex.db);
+ if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
+ is_schema_db(thd->lex->select_lex.db)))
+ DBUG_RETURN(1);
+ db= thd->lex->select_lex.db;
}
- end_read_record(&read_record_info);
- thd->version--; /* Force close to free memory */
-
- close_thread_tables(thd);
-
- DBUG_RETURN(ret);
+ DBUG_RETURN(get_instance()->db_repository->fill_schema_events(thd, tables, db));
}
-
/*
Inits the scheduler's structures.
@@ -1254,17 +568,19 @@ int
Events::init()
{
int ret= 0;
+ Event_db_repository *db_repo;
DBUG_ENTER("Events::init");
+ event_queue->init_queue(db_repository, scheduler_ng);
+ scheduler_ng->init_scheduler(event_queue);
/* it should be an assignment! */
if (opt_event_scheduler)
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
DBUG_ASSERT(opt_event_scheduler == 1 || opt_event_scheduler == 2);
- DBUG_RETURN(scheduler->init() ||
- (opt_event_scheduler == 1? scheduler->start():
- scheduler->start_suspended()));
+ if (opt_event_scheduler == 1)
+ DBUG_RETURN(scheduler_ng->start());
}
+
DBUG_RETURN(0);
}
@@ -1273,70 +589,157 @@ Events::init()
Cleans up scheduler's resources. Called at server shutdown.
SYNOPSIS
- Events::shutdown()
+ Events::deinit()
NOTES
This function is not synchronized.
*/
void
-Events::shutdown()
+Events::deinit()
{
- DBUG_ENTER("Events::shutdown");
- Event_scheduler *scheduler= Event_scheduler::get_instance();
- if (scheduler->initialized())
- {
- scheduler->stop();
- scheduler->destroy();
- }
+ DBUG_ENTER("Events::deinit");
+
+ scheduler_ng->stop();
+ scheduler_ng->deinit_scheduler();
+
+ event_queue->deinit_queue();
DBUG_VOID_RETURN;
}
/*
+ Inits Events mutexes
+
+ SYNOPSIS
+ Events::init_mutexes()
+ thd Thread
+*/
+
+void
+Events::init_mutexes()
+{
+ pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
+
+ db_repository= new Event_db_repository;
+
+ event_queue= new Event_queue;
+ event_queue->init_mutexes();
+
+ scheduler_ng= new Event_scheduler_ng();
+ scheduler_ng->init_mutexes();
+}
+
+
+/*
+ Destroys Events mutexes
+
+ SYNOPSIS
+ Events::destroy_mutexes()
+*/
+
+void
+Events::destroy_mutexes()
+{
+ event_queue->deinit_mutexes();
+ scheduler_ng->deinit_mutexes();
+
+ delete scheduler_ng;
+ delete db_repository;
+
+ pthread_mutex_destroy(&LOCK_event_metadata);
+}
+
+
+/*
Proxy for Event_scheduler::dump_internal_status
SYNOPSIS
Events::dump_internal_status()
thd Thread
-
+
RETURN VALUE
- 0 OK
- !0 Error
+ FALSE OK
+ TRUE Error
*/
-int
+bool
Events::dump_internal_status(THD *thd)
{
- return Event_scheduler::dump_internal_status(thd);
+ DBUG_ENTER("Events::dump_internal_status");
+ Protocol *protocol= thd->protocol;
+ List<Item> field_list;
+
+ field_list.push_back(new Item_empty_string("Name", 30));
+ field_list.push_back(new Item_empty_string("Value",20));
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ if (scheduler_ng->dump_internal_status(thd) ||
+ event_queue->dump_internal_status(thd))
+ DBUG_RETURN(TRUE);
+
+ send_eof(thd);
+ DBUG_RETURN(FALSE);
}
/*
- Inits Events mutexes
+ Starts execution of events by the scheduler
SYNOPSIS
- Events::init_mutexes()
- thd Thread
+ Events::start_execution_of_events()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
*/
-void
-Events::init_mutexes()
+bool
+Events::start_execution_of_events()
{
- Event_scheduler::init_mutexes();
+ DBUG_ENTER("Events::start_execution_of_events");
+ DBUG_RETURN(scheduler_ng->start());
}
/*
- Destroys Events mutexes
+ Stops execution of events by the scheduler.
+ Already running events will not be stopped. If the user needs
+ them stopped manual intervention is needed.
SYNOPSIS
- Events::destroy_mutexes()
+ Events::stop_execution_of_events()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
*/
-void
-Events::destroy_mutexes()
+bool
+Events::stop_execution_of_events()
+{
+ DBUG_ENTER("Events::stop_execution_of_events");
+ DBUG_RETURN(scheduler_ng->stop());
+}
+
+
+/*
+ Checks whether the scheduler is running or not.
+
+ SYNOPSIS
+ Events::is_started()
+
+ RETURN VALUE
+ TRUE Yes
+ FALSE No
+*/
+
+bool
+Events::is_started()
{
- Event_scheduler::destroy_mutexes();
+ DBUG_ENTER("Events::is_started");
+ DBUG_RETURN(scheduler_ng->get_state() == Event_scheduler_ng::RUNNING);
}
diff --git a/sql/events.h b/sql/events.h
index 66cce6e7777..f825a75934f 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -16,78 +16,112 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+class sp_name;
+class Event_parse_data;
+class Event_db_repository;
+class Event_queue;
+class Event_queue_element;
+class Event_scheduler_ng;
+
+/* Return codes */
+enum enum_events_error_code
+{
+ OP_OK= 0,
+ OP_NOT_RUNNING,
+ OP_CANT_KILL,
+ OP_CANT_INIT,
+ OP_DISABLED_EVENT,
+ OP_LOAD_ERROR,
+ OP_ALREADY_EXISTS
+};
+
+int
+sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
-class Event_timed;
class Events
{
public:
+ friend class Event_queue_element;
+ /*
+ Quite NOT the best practice and will be removed once
+ Event_timed::drop() and Event_timed is fixed not do drop directly
+ or other scheme will be found.
+ */
+
static ulong opt_event_scheduler;
static TYPELIB opt_typelib;
- enum enum_table_field
- {
- FIELD_DB = 0,
- FIELD_NAME,
- FIELD_BODY,
- FIELD_DEFINER,
- FIELD_EXECUTE_AT,
- FIELD_INTERVAL_EXPR,
- FIELD_TRANSIENT_INTERVAL,
- FIELD_CREATED,
- FIELD_MODIFIED,
- FIELD_LAST_EXECUTED,
- FIELD_STARTS,
- FIELD_ENDS,
- FIELD_STATUS,
- FIELD_ON_COMPLETION,
- FIELD_SQL_MODE,
- FIELD_COMMENT,
- FIELD_COUNT /* a cool trick to count the number of fields :) */
- };
+ int
+ init();
+
+ void
+ deinit();
- static int
- create_event(THD *thd, Event_timed *et, uint create_options,
+ void
+ init_mutexes();
+
+ void
+ destroy_mutexes();
+
+ bool
+ start_execution_of_events();
+
+ bool
+ stop_execution_of_events();
+
+ bool
+ is_started();
+
+ static Events*
+ get_instance();
+
+ int
+ create_event(THD *thd, Event_parse_data *parse_data, bool if_exists,
uint *rows_affected);
- static int
- update_event(THD *thd, Event_timed *et, sp_name *new_name,
+ int
+ update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
uint *rows_affected);
- static int
- drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected);
+ int
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
+ uint *rows_affected, bool only_from_disk);
- static int
+ int
+ drop_schema_events(THD *thd, char *db);
+
+ int
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
- static int
+ int
show_create_event(THD *thd, sp_name *spn);
+ /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */
static int
reconstruct_interval_expression(String *buf, interval_type interval,
longlong expression);
static int
- drop_schema_events(THD *thd, char *db);
+ fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
- static int
+ bool
dump_internal_status(THD *thd);
-
- static int
- init();
-
- static void
- shutdown();
- static void
- init_mutexes();
-
- static void
- destroy_mutexes();
+private:
+ /* Singleton DP is used */
+ Events(){}
+ ~Events(){}
+ /* Singleton instance */
+ static Events singleton;
+
+ Event_queue *event_queue;
+ Event_scheduler_ng *scheduler_ng;
+ Event_db_repository *db_repository;
+
+ pthread_mutex_t LOCK_event_metadata;
-private:
/* Prevent use of these */
Events(const Events &);
void operator=(Events &);
diff --git a/sql/events_priv.h b/sql/events_priv.h
deleted file mode 100644
index ed02cb7055b..00000000000
--- a/sql/events_priv.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef _EVENT_PRIV_H_
-#define _EVENT_PRIV_H_
-/* Copyright (C) 2004-2006 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#define EVENT_EXEC_STARTED 0
-#define EVENT_EXEC_ALREADY_EXEC 1
-#define EVENT_EXEC_CANT_FORK 2
-
-#define EVEX_DB_FIELD_LEN 64
-#define EVEX_NAME_FIELD_LEN 64
-#define EVEX_MAX_INTERVAL_VALUE 2147483647L
-
-class Event_timed;
-
-int
-evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
- const LEX_STRING ev_name,
- TABLE *table);
-
-int
-db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists,
- uint *rows_affected);
-int
-db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl,
- MEM_ROOT *root);
-
-int
-db_create_event(THD *thd, Event_timed *et, my_bool create_if_not,
- uint *rows_affected);
-
-int
-db_drop_events_from_table(THD *thd, LEX_STRING *db);
-
-int
-sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
-
-/* Compares only the name part of the identifier */
-bool
-event_timed_name_equal(Event_timed *et, LEX_STRING *name);
-
-/* Compares only the schema part of the identifier */
-bool
-event_timed_db_equal(Event_timed *et, LEX_STRING *db);
-
-/*
- Compares only the definer part of the identifier. Use during DROP USER
- to drop user's events. (Still not implemented)
-*/
-bool
-event_timed_definer_equal(Event_timed *et, LEX_STRING *definer);
-
-/* Compares the whole identifier*/
-bool
-event_timed_identifier_equal(Event_timed *a, Event_timed *b);
-
-
-bool
-change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
- LEX_STRING db, Security_context *s_ctx,
- Security_context **backup);
-
-void
-restore_security_context(THD *thd, Security_context *backup);
-
-#endif /* _EVENT_PRIV_H_ */
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 6e57993a61a..8fc6155087e 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -864,7 +864,7 @@ static void close_connections(void)
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
/* We skip slave threads & scheduler on this first loop through. */
- if (tmp->slave_thread || tmp->system_thread == SYSTEM_THREAD_EVENT_SCHEDULER)
+ if (tmp->slave_thread)
continue;
tmp->killed= THD::KILL_CONNECTION;
@@ -883,7 +883,7 @@ static void close_connections(void)
}
(void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
- Events::shutdown();
+ Events::get_instance()->deinit();
end_slave();
if (thread_count)
@@ -1321,7 +1321,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_bytes_sent);
(void) pthread_mutex_destroy(&LOCK_bytes_received);
(void) pthread_mutex_destroy(&LOCK_user_conn);
- Events::destroy_mutexes();
+ Events::get_instance()->destroy_mutexes();
#ifdef HAVE_OPENSSL
(void) pthread_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
@@ -2884,7 +2884,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_server_started,NULL);
sp_cache_init();
- Events::init_mutexes();
+ Events::get_instance()->init_mutexes();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -3675,7 +3675,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (!opt_noacl)
{
- Events::init();
+ Events::get_instance()->init();
}
#if defined(__NT__) || defined(HAVE_SMEM)
handle_connections_methods();
diff --git a/sql/set_var.cc b/sql/set_var.cc
index bb9ef4d453f..4347ca84a75 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -57,7 +57,7 @@
#include <myisam.h>
#include <my_dir.h>
-#include "event_scheduler.h"
+#include "events.h"
/* WITH_BERKELEY_STORAGE_ENGINE */
extern bool berkeley_shared_data;
@@ -3892,30 +3892,32 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
bool
sys_var_event_scheduler::update(THD *thd, set_var *var)
{
- enum Event_scheduler::enum_error_code res;
- Event_scheduler *scheduler= Event_scheduler::get_instance();
+ int res;
/* here start the thread if not running. */
DBUG_ENTER("sys_var_event_scheduler::update");
-
- DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value));
- if (!scheduler->initialized())
+ if (Events::opt_event_scheduler == 0)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--event-scheduler=0");
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
}
+ DBUG_PRINT("new_value", ("%lu", (bool)var->save_result.ulong_value));
+
if (var->save_result.ulonglong_value < 1 ||
var->save_result.ulonglong_value > 2)
{
char buf[64];
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "event_scheduler",
llstr(var->save_result.ulonglong_value, buf));
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
}
- if ((res= scheduler->suspend_or_resume(var->save_result.ulonglong_value == 1?
- Event_scheduler::RESUME :
- Event_scheduler::SUSPEND)))
- my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), (uint) res);
+ if (var->save_result.ulonglong_value == 1)
+ res= Events::get_instance()->start_execution_of_events();
+ else
+ res= Events::get_instance()->stop_execution_of_events();
+
+ if (res)
+ my_error(ER_EVENT_SET_VAR_ERROR, MYF(0));
DBUG_RETURN((bool) res);
}
@@ -3923,11 +3925,9 @@ sys_var_event_scheduler::update(THD *thd, set_var *var)
byte *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- Event_scheduler *scheduler= Event_scheduler::get_instance();
-
- if (!scheduler->initialized())
+ if (Events::opt_event_scheduler == 0)
thd->sys_var_tmp.long_value= 0;
- else if (scheduler->get_state() == Event_scheduler::RUNNING)
+ else if (Events::get_instance()->is_started())
thd->sys_var_tmp.long_value= 1;
else
thd->sys_var_tmp.long_value= 2;
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 8d2822370f2..dbac6863fb3 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5829,9 +5829,9 @@ ER_CANT_CHANGE_TX_ISOLATION 25001
ER_DUP_ENTRY_AUTOINCREMENT_CASE
eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.64s' for key '%-.64s'"
ER_EVENT_MODIFY_QUEUE_ERROR
- eng "Internal scheduler error %d"
+ eng "Error during loading event from disk. mysql.event damaged?"
ER_EVENT_SET_VAR_ERROR
- eng "Error during starting/stopping of the scheduler. Error code %u"
+ eng "Error during starting/stopping of the scheduler."
ER_PARTITION_MERGE_ERROR
eng "%s handler cannot be used in partitioned tables"
swe "%s kan inte användas i en partitionerad tabell"
@@ -5839,3 +5839,6 @@ ER_CANT_ACTIVATE_LOG
eng "Cannot activate '%-.64s' log."
ER_RBR_NOT_AVAILABLE
eng "The server was not built with row-based replication"
+ER_EVENT_RECURSIVITY_FORBIDDEN
+ eng "Recursivity of EVENT DDL statements is forbidden when body is present"
+
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c8c8ff16199..47381c6aecb 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2068,6 +2068,63 @@ bool Security_context::set_user(char *user_arg)
return user == 0;
}
+/*
+ Switches the security context
+ SYNOPSIS
+ THD::change_security_context()
+ user The user
+ host The host of the user
+ db The schema for which the security_ctx will be loaded
+ s_ctx Security context to load state into
+ backup Where to store the old context
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (generates error too)
+*/
+
+bool
+THD::change_security_context(LEX_STRING user, LEX_STRING host,
+ LEX_STRING db, Security_context *s_ctx,
+ Security_context **backup)
+{
+ DBUG_ENTER("change_security_context");
+ DBUG_PRINT("info",("%s@%s@%s", user.str, host.str, db.str));
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ s_ctx->init();
+ *backup= 0;
+ if (acl_getroot_no_password(s_ctx, user.str, host.str, host.str, db.str))
+ {
+ my_error(ER_NO_SUCH_USER, MYF(0), user.str, host.str);
+ DBUG_RETURN(TRUE);
+ }
+ *backup= security_ctx;
+ security_ctx= s_ctx;
+#endif
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Restores the security context
+ SYNOPSIS
+ restore_security_context()
+ thd Thread
+ backup Context to switch to
+*/
+
+void
+THD::restore_security_context(Security_context *backup)
+{
+ DBUG_ENTER("restore_security_context");
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (backup)
+ security_ctx= backup;
+#endif
+ DBUG_VOID_RETURN;
+}
+
+
/****************************************************************************
Handling of open and locked tables states.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b79f0753603..18cc53daf6d 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -868,6 +868,14 @@ public:
char *db, *catalog;
Security_context main_security_ctx;
Security_context *security_ctx;
+
+ bool
+ change_security_context(LEX_STRING user, LEX_STRING host,
+ LEX_STRING db, Security_context *s_ctx,
+ Security_context **backup);
+
+ void
+ restore_security_context(Security_context *backup);
/* remote (peer) port */
uint16 peer_port;
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 41e7e5df1f7..541ed9c7cc4 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -904,7 +904,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
exit:
(void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */
- error= Events::drop_schema_events(thd, db);
+ error= Events::get_instance()->drop_schema_events(thd, db);
/*
If this database was the client's selected database, we silently change the
client's selected database to nothing (to have an empty SELECT DATABASE()
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index f6031a1f2fd..386dece8813 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -174,11 +174,11 @@ void lex_start(THD *thd, const uchar *buf, uint length)
lex->sphead= NULL;
lex->spcont= NULL;
lex->proc_list.first= 0;
- lex->escape_used= lex->et_compile_phase= FALSE;
+ lex->escape_used= FALSE;
lex->reset_query_tables_list(FALSE);
lex->name= 0;
- lex->et= NULL;
+ lex->event_parse_data= NULL;
lex->nest_level=0 ;
lex->allow_sum_func= 0;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index c889c2c5f94..77630065d3a 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -27,7 +27,7 @@ class sp_instr;
class sp_pcontext;
class st_alter_tablespace;
class partition_info;
-class Event_timed;
+class Event_parse_data;
#ifdef MYSQL_SERVER
/*
@@ -1021,8 +1021,7 @@ typedef struct st_lex : public Query_tables_list
st_sp_chistics sp_chistics;
- Event_timed *et;
- bool et_compile_phase;
+ Event_parse_data *event_parse_data;
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
/*
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 5b860cc0623..e68d23cfdcd 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -27,7 +27,7 @@
#include "sp.h"
#include "sp_cache.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#ifdef HAVE_OPENSSL
/*
@@ -3875,73 +3875,44 @@ end_with_restore_list:
}
case SQLCOM_CREATE_EVENT:
case SQLCOM_ALTER_EVENT:
- case SQLCOM_DROP_EVENT:
{
- uint rows_affected= 1;
- DBUG_ASSERT(lex->et);
- do {
- if (! lex->et->dbname.str ||
- (lex->sql_command == SQLCOM_ALTER_EVENT && lex->spname &&
- !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->et->dbname.str, 0, 0, 0,
- is_schema_db(lex->et->dbname.str)) ||
- (lex->sql_command == SQLCOM_ALTER_EVENT && lex->spname &&
- (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
- is_schema_db(lex->spname->m_db.str)))))
- break;
-
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
-
- switch (lex->sql_command) {
- case SQLCOM_CREATE_EVENT:
- res= Events::create_event(thd, lex->et,
- (uint) lex->create_info.options,
- &rows_affected);
- break;
- case SQLCOM_ALTER_EVENT:
- res= Events::update_event(thd, lex->et, lex->spname,
- &rows_affected);
- break;
- case SQLCOM_DROP_EVENT:
- res= Events::drop_event(thd, lex->et, lex->drop_if_exists,
- &rows_affected);
- default:;
- }
- DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d",
- res, rows_affected));
- if (!res)
- send_ok(thd, rows_affected);
+ uint affected= 1;
+ DBUG_ASSERT(lex->event_parse_data);
+ switch (lex->sql_command) {
+ case SQLCOM_CREATE_EVENT:
+ res= Events::get_instance()->
+ create_event(thd, lex->event_parse_data,
+ lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS,
+ &affected);
+ break;
+ case SQLCOM_ALTER_EVENT:
+ res= Events::get_instance()->
+ update_event(thd, lex->event_parse_data, lex->spname, &affected);
+ break;
+ default:;
+ }
+ DBUG_PRINT("info",("DDL error code=%d affected=%d", res, affected));
+ if (!res)
+ send_ok(thd, affected);
- /* lex->unit.cleanup() is called outside, no need to call it here */
- } while (0);
+ /* Don't do it, if we are inside a SP */
if (!thd->spcont)
{
- lex->et->free_sphead_on_delete= true;
- lex->et->free_sp();
- lex->et->deinit_mutexes();
+ delete lex->sphead;
+ lex->sphead= NULL;
}
-
+
+ /* lex->unit.cleanup() is called outside, no need to call it here */
break;
}
+ case SQLCOM_DROP_EVENT:
case SQLCOM_SHOW_CREATE_EVENT:
{
DBUG_ASSERT(lex->spname);
- 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;
+ goto error;
}
if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
is_schema_db(lex->spname->m_db.str)))
@@ -3950,15 +3921,29 @@ end_with_restore_list:
if (lex->spname->m_name.length > NAME_LEN)
{
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
+ /* this jumps to the end of the function and skips own messaging */
goto error;
}
- res= Events::show_create_event(thd, lex->spname);
+
+ if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
+ res= Events::get_instance()->show_create_event(thd, lex->spname);
+ else
+ {
+ uint affected= 1;
+ if (!(res= Events::get_instance()->drop_event(thd,
+ lex->spname->m_db,
+ lex->spname->m_name,
+ lex->drop_if_exists,
+ &affected,
+ FALSE)))
+ send_ok(thd, affected);
+ }
break;
}
#ifndef DBUG_OFF
case SQLCOM_SHOW_SCHEDULER_STATUS:
{
- res= Events::dump_internal_status(thd);
+ res= Events::get_instance()->dump_internal_status(thd);
break;
}
#endif
@@ -6036,14 +6021,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
{
delete lex->sphead;
lex->sphead= NULL;
- if (lex->et)
- {
- lex->et->free_sphead_on_delete= true;
- /* alloced on thd->mem_root so no real memory free but dtor call */
- lex->et->free_sp();
- lex->et->deinit_mutexes();
- lex->et= NULL;
- }
}
else
{
@@ -6080,13 +6057,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
delete lex->sphead;
lex->sphead= NULL;
}
- if (lex->et)
- {
- lex->et->free_sphead_on_delete= true;
- lex->et->free_sp();
- lex->et->deinit_mutexes();
- lex->et= NULL;
- }
}
thd->proc_info="freeing items";
thd->end_statement();
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 8f8c84c2db5..52bd8da3785 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -27,7 +27,7 @@
#include "authors.h"
#include "contributors.h"
#include "events.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#include <my_dir.h>
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -4166,7 +4166,7 @@ extern LEX_STRING interval_type_to_name[];
1 Error
*/
-static int
+int
copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
{
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
@@ -4177,7 +4177,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
restore_record(sch_table, s->default_values);
- if (et.load_from_row(thd->mem_root, event_table))
+ if (et.load_from_row(event_table))
{
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0));
DBUG_RETURN(1);
@@ -4301,183 +4301,6 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
}
-/*
- Performs an index scan of event_table (mysql.event) and fills schema_table.
-
- Synopsis
- events_table_index_read_for_db()
- thd Thread
- schema_table The I_S.EVENTS table
- event_table The event table to use for loading (mysql.event)
-
- Returns
- 0 OK
- 1 Error
-*/
-
-static
-int events_table_index_read_for_db(THD *thd, TABLE *schema_table,
- TABLE *event_table)
-{
- int ret=0;
- CHARSET_INFO *scs= system_charset_info;
- KEY *key_info;
- uint key_len;
- byte *key_buf= NULL;
- LINT_INIT(key_buf);
-
- DBUG_ENTER("schema_events_do_index_scan");
-
- DBUG_PRINT("info", ("Using prefix scanning on PK"));
- event_table->file->ha_index_init(0, 1);
- event_table->field[Events::FIELD_DB]->
- store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs);
- key_info= event_table->key_info;
- key_len= key_info->key_part[0].store_length;
-
- if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len)))
- {
- ret= 1;
- /* don't send error, it would be done by sql_alloc_error_handler() */
- }
- else
- {
- key_copy(key_buf, event_table->record[0], key_info, key_len);
- if (!(ret= event_table->file->index_read(event_table->record[0], key_buf,
- key_len, HA_READ_PREFIX)))
- {
- DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
- do
- {
- ret= copy_event_to_schema_table(thd, schema_table, event_table);
- if (ret == 0)
- ret= event_table->file->index_next_same(event_table->record[0],
- key_buf, key_len);
- } while (ret == 0);
- }
- DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
- }
- event_table->file->ha_index_end();
- /* ret is guaranteed to be != 0 */
- if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
- DBUG_RETURN(0);
- DBUG_RETURN(1);
-}
-
-
-/*
- Performs a table scan of event_table (mysql.event) and fills schema_table.
-
- Synopsis
- events_table_scan_all()
- thd Thread
- schema_table The I_S.EVENTS in memory table
- event_table The event table to use for loading.
-
- Returns
- 0 OK
- 1 Error
-*/
-
-static
-int events_table_scan_all(THD *thd, TABLE *schema_table,
- TABLE *event_table)
-{
- int ret;
- READ_RECORD read_record_info;
-
- DBUG_ENTER("schema_events_do_table_scan");
- init_read_record(&read_record_info, thd, event_table, NULL, 1, 0);
-
- /*
- rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
- but rr_handle_error returns -1 for that reason. Thus, read_record()
- returns -1 eventually.
- */
- do
- {
- ret= read_record_info.read_record(&read_record_info);
- if (ret == 0)
- ret= copy_event_to_schema_table(thd, schema_table, event_table);
- }
- while (ret == 0);
-
- DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
- end_read_record(&read_record_info);
-
- /* ret is guaranteed to be != 0 */
- DBUG_RETURN(ret == -1? 0:1);
-}
-
-
-/*
- Fills I_S.EVENTS with data loaded from mysql.event. Also used by
- SHOW EVENTS
-
- Synopsis
- fill_schema_events()
- thd Thread
- tables The schema table
- cond Unused
-
- Returns
- 0 OK
- 1 Error
-*/
-
-int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
-{
- TABLE *schema_table= tables->table;
- TABLE *event_table= NULL;
- Open_tables_state backup;
- int ret= 0;
-
- DBUG_ENTER("fill_schema_events");
- /*
- If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
- be NULL. Let's do an assert anyway.
- */
- if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
- {
- DBUG_ASSERT(thd->lex->select_lex.db);
- if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
- is_schema_db(thd->lex->select_lex.db)))
- DBUG_RETURN(1);
- }
-
- DBUG_PRINT("info",("db=%s", thd->lex->select_lex.db?
- thd->lex->select_lex.db:"(null)"));
-
- thd->reset_n_backup_open_tables_state(&backup);
- if (Events::open_event_table(thd, TL_READ, &event_table))
- {
- sql_print_error("Table mysql.event is damaged.");
- thd->restore_backup_open_tables_state(&backup);
- DBUG_RETURN(1);
- }
-
- /*
- 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
- thus we won't order it. OTOH, SHOW EVENTS will be
- ordered.
- 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
- Reasoning: Events are per schema, therefore a scan over an index
- will save use from doing a table scan and comparing
- every single row's `db` with the schema which we show.
- */
- if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
- ret= events_table_index_read_for_db(thd, schema_table, event_table);
- else
- ret= events_table_scan_all(thd, schema_table, event_table);
-
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(&backup);
-
- DBUG_PRINT("info", ("Return code=%d", ret));
- DBUG_RETURN(ret);
-}
-
-
int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
DBUG_ENTER("fill_open_tables");
@@ -5574,7 +5397,7 @@ ST_SCHEMA_TABLE schema_tables[]=
{"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},
+ Events::fill_schema_events, make_old_format, 0, -1, -1, 0},
{"FILES", files_fields_info, create_schema_table,
fill_schema_files, 0, 0, -1, -1, 0},
{"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 6fce5e94ca3..681d1232b39 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -14,4 +14,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
HA_CREATE_INFO *create_info_arg);
int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
+int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
+
#endif /* SQL_SHOW_H */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 68aeefc1278..1cf16a051e3 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -38,7 +38,7 @@
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp.h"
-#include "event_timed.h"
+#include "event_data_objects.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -880,7 +880,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
definer view_replace_or_algorithm view_replace view_algorithm_opt
- view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail
+ view_algorithm view_or_trigger_or_sp_or_event
+ view_or_trigger_or_sp_or_event_tail
view_suid view_tail view_list_opt view_list view_select
view_check_option trigger_tail sp_tail
install uninstall partition_entry binlog_base64_event
@@ -1257,29 +1258,43 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
- | CREATE EVENT_SYM opt_if_not_exists sp_name
+ | CREATE
+ {
+ Lex->create_view_mode= VIEW_CREATE_NEW;
+ Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
+ Lex->create_view_suid= TRUE;
+ }
+ view_or_trigger_or_sp_or_event
+ {}
+ | CREATE USER clear_privileges grant_list
+ {
+ Lex->sql_command = SQLCOM_CREATE_USER;
+ }
+ | CREATE LOGFILE_SYM GROUP logfile_group_info
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
+ }
+ | CREATE TABLESPACE tablespace_info
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE;
+ }
+ ;
+
+
+event_tail:
+ EVENT_SYM opt_if_not_exists sp_name
/*
BE CAREFUL when you add a new rule to update the block where
YYTHD->client_capabilities is set back to original value
*/
{
- LEX *lex=Lex;
-
- if (lex->et)
- {
- /*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
- */
- // ToDo Andrey : Change the error message
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
- YYABORT;
- }
-
- lex->create_info.options= $3;
+ Lex->create_info.options= $2;
- if (!(lex->et= new(YYTHD->mem_root) Event_timed())) // implicitly calls Event_timed::init()
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT;
+ Lex->event_parse_data->identifier= $3;
/*
We have to turn of CLIENT_MULTI_QUERIES while parsing a
@@ -1289,11 +1304,8 @@ create:
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
- if (!lex->et_compile_phase)
- {
- lex->et->init_name(YYTHD, $4);
- lex->et->init_definer(YYTHD);
- }
+ /* We need that for disallowing subqueries */
+ Lex->sql_command= SQLCOM_CREATE_EVENT;
}
ON SCHEDULE_SYM ev_schedule_time
opt_ev_on_completion
@@ -1303,13 +1315,12 @@ create:
{
/*
Restore flag if it was cleared above
- $1 - CREATE
- $2 - EVENT_SYM
- $3 - opt_if_not_exists
- $4 - sp_name
- $5 - the block above
+ $1 - EVENT_SYM
+ $2 - opt_if_not_exists
+ $3 - sp_name
+ $4 - the block above
*/
- YYTHD->client_capabilities |= $<ulong_num>5;
+ YYTHD->client_capabilities |= $<ulong_num>4;
/*
sql_command is set here because some rules in ev_sql_stmt
@@ -1317,146 +1328,48 @@ create:
*/
Lex->sql_command= SQLCOM_CREATE_EVENT;
}
- | CREATE
- {
- Lex->create_view_mode= VIEW_CREATE_NEW;
- Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
- Lex->create_view_suid= TRUE;
- }
- view_or_trigger_or_sp
- {}
- | CREATE USER clear_privileges grant_list
- {
- Lex->sql_command = SQLCOM_CREATE_USER;
- }
- | CREATE LOGFILE_SYM GROUP logfile_group_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
- }
- | CREATE TABLESPACE tablespace_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE;
- }
- ;
ev_schedule_time: EVERY_SYM expr interval
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- {
- switch (lex->et->init_interval(YYTHD , $2, $3)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
- case EVEX_MICROSECOND_UNSUP:
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
- YYABORT;
- break;
- }
- }
+ Lex->event_parse_data->item_expression= $2;
+ Lex->event_parse_data->interval= $3;
}
ev_starts
ev_ends
| AT_SYM expr
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- {
- switch (lex->et->init_execute_at(YYTHD, $2)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case ER_WRONG_VALUE:
- {
- char buff[120];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- String *str2= $2->val_str(&str);
- my_error(ER_WRONG_VALUE, MYF(0), "AT",
- str2? str2->c_ptr_safe():"NULL");
- YYABORT;
- break;
- }
- case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
- YYABORT;
- break;
- }
- }
+ Lex->event_parse_data->item_execute_at= $2;
}
;
opt_ev_status: /* empty */ { $$= 0; }
| ENABLE_SYM
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- lex->et->status= Event_timed::ENABLED;
+ Lex->event_parse_data->status= Event_parse_data::ENABLED;
$$= 1;
}
| DISABLE_SYM
{
- LEX *lex=Lex;
-
- if (!lex->et_compile_phase)
- lex->et->status= Event_timed::DISABLED;
+ Lex->event_parse_data->status= Event_parse_data::DISABLED;
$$= 1;
}
;
ev_starts: /* empty */
{
- Lex->et->init_starts(YYTHD, new Item_func_now_local());
+ Lex->event_parse_data->item_starts= new Item_func_now_local();
}
| STARTS_SYM expr
{
- LEX *lex= Lex;
- if (!lex->et_compile_phase)
- {
-
- switch (lex->et->init_starts(YYTHD, $2)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case EVEX_BAD_PARAMS:
- {
- char buff[20];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- String *str2= $2->val_str(&str);
- my_error(ER_WRONG_VALUE, MYF(0), "STARTS",
- str2 ? str2->c_ptr_safe() : NULL);
- YYABORT;
- break;
- }
- }
- }
+ Lex->event_parse_data->item_starts= $2;
}
;
ev_ends: /* empty */
| ENDS_SYM expr
{
- LEX *lex= Lex;
- if (!lex->et_compile_phase)
- {
- switch (lex->et->init_ends(YYTHD, $2)) {
- case EVEX_PARSE_ERROR:
- yyerror(ER(ER_SYNTAX_ERROR));
- YYABORT;
- break;
- case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
- YYABORT;
- break;
- }
- }
+ Lex->event_parse_data->item_ends= $2;
}
;
@@ -1467,16 +1380,14 @@ opt_ev_on_completion: /* empty */ { $$= 0; }
ev_on_completion:
ON COMPLETION_SYM PRESERVE_SYM
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- lex->et->on_completion= Event_timed::ON_COMPLETION_PRESERVE;
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_PRESERVE;
$$= 1;
}
| ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
{
- LEX *lex=Lex;
- if (!lex->et_compile_phase)
- lex->et->on_completion= Event_timed::ON_COMPLETION_DROP;
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_DROP;
$$= 1;
}
;
@@ -1484,64 +1395,64 @@ ev_on_completion:
opt_ev_comment: /* empty */ { $$= 0; }
| COMMENT_SYM TEXT_STRING_sys
{
- LEX *lex= Lex;
- if (!lex->et_compile_phase)
- {
- lex->comment= $2;
- lex->et->init_comment(YYTHD, &$2);
- }
- $$= 1;
+ Lex->comment= Lex->event_parse_data->comment= $2;
}
;
ev_sql_stmt:
{
LEX *lex= Lex;
- sp_head *sp;
-
- $<sphead>$= lex->sphead;
- if (!lex->sphead)
+ /*
+ This stops the following :
+ - CREATE EVENT ... DO CREATE EVENT ...;
+ - ALTER EVENT ... DO CREATE EVENT ...;
+ - CREATE EVENT ... DO ALTER EVENT DO ....;
+ - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END|
+ This allows:
+ - CREATE EVENT ... DO DROP EVENT yyy;
+ - CREATE EVENT ... DO ALTER EVENT yyy;
+ (the nested ALTER EVENT can have anything but DO clause)
+ - ALTER EVENT ... DO ALTER EVENT yyy;
+ (the nested ALTER EVENT can have anything but DO clause)
+ - ALTER EVENT ... DO DROP EVENT yyy;
+ - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END|
+ (the nested ALTER EVENT can have anything but DO clause)
+ - CREATE PROCEDURE ... BEGIN DROP EVENT ... END|
+ */
+ if (lex->sphead)
{
- if (!(sp= new sp_head()))
- YYABORT;
-
- sp->reset_thd_mem_root(YYTHD);
- sp->init(lex);
+ my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0));
+ YYABORT;
+ }
+
+ if (!(lex->sphead= new sp_head()))
+ YYABORT;
- sp->m_type= TYPE_ENUM_PROCEDURE;
+ lex->sphead->reset_thd_mem_root(YYTHD);
+ lex->sphead->init(lex);
- lex->sphead= sp;
+ lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- lex->sphead->m_chistics= &lex->sp_chistics;
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sphead->m_chistics= &lex->sp_chistics;
- lex->sphead->m_body_begin= lex->ptr;
- }
+ lex->sphead->m_body_begin= lex->ptr;
+
+ Lex->event_parse_data->body_begin= lex->ptr;
- if (!lex->et_compile_phase)
- lex->et->body_begin= lex->ptr;
}
ev_sql_stmt_inner
{
LEX *lex=Lex;
- if (!$<sphead>1)
- {
- sp_head *sp= lex->sphead;
- // return back to the original memory root ASAP
- sp->init_strings(YYTHD, lex, NULL);
- sp->restore_thd_mem_root(YYTHD);
+ // return back to the original memory root ASAP
+ lex->sphead->init_strings(YYTHD, lex, NULL);
+ lex->sphead->restore_thd_mem_root(YYTHD);
- lex->sp_chistics.suid= SP_IS_SUID;//always the definer!
+ lex->sp_chistics.suid= SP_IS_SUID;//always the definer!
- lex->et->sphead= lex->sphead;
- lex->sphead= NULL;
- }
- if (!lex->et_compile_phase)
- {
- lex->et->init_body(YYTHD);
- }
+ Lex->event_parse_data->init_body(YYTHD);
}
;
@@ -4740,37 +4651,22 @@ alter:
YYTHD->client_capabilities is set back to original value
*/
{
- LEX *lex=Lex;
- Event_timed *et;
+ Lex->spname= NULL;
- if (lex->et)
- {
- /*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
- */
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT;
- }
- lex->spname= 0;//defensive programming
-
- if (!(et= new (YYTHD->mem_root) Event_timed()))// implicitly calls Event_timed::init()
- YYABORT;
- lex->et = et;
-
- if (!lex->et_compile_phase)
- {
- et->init_definer(YYTHD);
- et->init_name(YYTHD, $3);
- }
+ Lex->event_parse_data->identifier= $3;
/*
- We have to turn of CLIENT_MULTI_QUERIES while parsing a
- stored procedure, otherwise yylex will chop it into pieces
- at each ';'.
+ We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ stored procedure, otherwise yylex will chop it into pieces
+ at each ';'.
*/
$<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+
+ /* we need that for disallowing subqueries */
+ Lex->sql_command= SQLCOM_ALTER_EVENT;
}
ev_alter_on_schedule_completion
opt_ev_rename_to
@@ -4786,15 +4682,15 @@ alter:
*/
YYTHD->client_capabilities |= $<ulong_num>4;
- /*
- sql_command is set here because some rules in ev_sql_stmt
- can overwrite it
- */
if (!($5 || $6 || $7 || $8 || $9))
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
+ /*
+ sql_command is set here because some rules in ev_sql_stmt
+ can overwrite it
+ */
Lex->sql_command= SQLCOM_ALTER_EVENT;
}
| ALTER TABLESPACE alter_tablespace_info
@@ -4830,7 +4726,7 @@ opt_ev_rename_to: /* empty */ { $$= 0;}
{
LEX *lex=Lex;
lex->spname= $3; //use lex's spname to hold the new name
- //the original name is in the Event_timed object
+ //the original name is in the Event_parse_data object
$$= 1;
}
;
@@ -7104,8 +7000,10 @@ select_derived2:
{
LEX *lex= Lex;
lex->derived_tables|= DERIVED_SUBQUERY;
- if (lex->sql_command == (int)SQLCOM_HA_READ ||
- lex->sql_command == (int)SQLCOM_KILL)
+ if (lex->sql_command == SQLCOM_HA_READ ||
+ lex->sql_command == SQLCOM_KILL ||
+ lex->sql_command == SQLCOM_CREATE_EVENT ||
+ lex->sql_command == SQLCOM_ALTER_EVENT)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
@@ -7699,29 +7597,9 @@ drop:
}
| DROP EVENT_SYM if_exists sp_name
{
- LEX *lex=Lex;
-
- if (lex->et)
- {
- /*
- Recursive events are not possible because recursive SPs
- are not also possible. lex->sp_head is not stacked.
- */
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "EVENT");
- YYABORT;
- }
-
- if (!(lex->et= new (YYTHD->mem_root) Event_timed()))
- YYABORT;
-
- 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;
+ Lex->drop_if_exists= $3;
+ Lex->spname= $4;
+ Lex->sql_command = SQLCOM_DROP_EVENT;
}
| DROP TRIGGER_SYM sp_name
{
@@ -8451,12 +8329,8 @@ show_param:
}
| CREATE EVENT_SYM sp_name
{
- Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
Lex->spname= $3;
- Lex->et= new (YYTHD->mem_root) Event_timed();
- if (!Lex->et)
- YYABORT;
- Lex->et->init_definer(YYTHD);
+ Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
}
;
@@ -10763,8 +10637,10 @@ subselect_start:
'(' SELECT_SYM
{
LEX *lex=Lex;
- if (lex->sql_command == (int)SQLCOM_HA_READ ||
- lex->sql_command == (int)SQLCOM_KILL)
+ if (lex->sql_command == SQLCOM_HA_READ ||
+ lex->sql_command == SQLCOM_KILL ||
+ lex->sql_command == SQLCOM_CREATE_EVENT ||
+ lex->sql_command == SQLCOM_ALTER_EVENT)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
@@ -10788,20 +10664,22 @@ subselect_end:
**************************************************************************/
-view_or_trigger_or_sp:
- definer view_or_trigger_or_sp_tail
+view_or_trigger_or_sp_or_event:
+ definer view_or_trigger_or_sp_or_event_tail
{}
| view_replace_or_algorithm definer view_tail
{}
;
-view_or_trigger_or_sp_tail:
+view_or_trigger_or_sp_or_event_tail:
view_tail
{}
| trigger_tail
{}
| sp_tail
{}
+ | event_tail
+ {}
;
/**************************************************************************