mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
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
This commit is contained in:
commit
5d91dc2560
43 changed files with 4908 additions and 5066 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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 2
|
||||
SHOW STATUS LIKE 'max_used_connections';
|
||||
Variable_name Value
|
||||
Max_used_connections 3
|
||||
SHOW STATUS LIKE 'max_used_connections';
|
||||
Variable_name Value
|
||||
Max_used_connections 4
|
||||
SHOW STATUS LIKE 'max_used_connections';
|
||||
Variable_name Value
|
||||
Max_used_connections 5
|
||||
SET GLOBAL thread_cache_size=@save_thread_cache_size;
|
||||
show status like 'com_show_status';
|
||||
Variable_name Value
|
||||
|
|
|
@ -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;
|
||||
#
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
|
||||
class Event_timed
|
||||
/* Compares only the schema part of the identifier */
|
||||
bool
|
||||
event_basic_db_equal( LEX_STRING *db, Event_basic *et);
|
||||
|
||||
/* 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,86 +113,15 @@ 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;
|
||||
|
||||
bool dropped;
|
||||
bool free_sphead_on_delete;
|
||||
uint flags;//all kind of purposes
|
||||
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
void *p;
|
||||
DBUG_ENTER("Event_timed::new(size)");
|
||||
p= my_malloc(size, MYF(0));
|
||||
DBUG_PRINT("info", ("alloc_ptr=0x%lx", p));
|
||||
DBUG_RETURN(p);
|
||||
}
|
||||
bool dropped;
|
||||
|
||||
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_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();
|
||||
|
||||
~Event_timed();
|
||||
|
||||
void
|
||||
init();
|
||||
|
||||
void
|
||||
deinit_mutexes();
|
||||
|
||||
int
|
||||
init_definer(THD *thd);
|
||||
|
||||
int
|
||||
init_execute_at(THD *thd, Item *expr);
|
||||
|
||||
int
|
||||
init_interval(THD *thd, Item *expr, interval_type new_interval);
|
||||
|
||||
void
|
||||
init_name(THD *thd, sp_name *spn);
|
||||
|
||||
int
|
||||
init_starts(THD *thd, Item *starts);
|
||||
|
||||
int
|
||||
init_ends(THD *thd, Item *ends);
|
||||
|
||||
void
|
||||
init_body(THD *thd);
|
||||
|
||||
void
|
||||
init_comment(THD *thd, LEX_STRING *set_comment);
|
||||
|
||||
int
|
||||
load_from_row(MEM_ROOT *mem_root, TABLE *table);
|
||||
Event_queue_element();
|
||||
virtual ~Event_queue_element();
|
||||
|
||||
virtual int
|
||||
load_from_row(TABLE *table);
|
||||
|
||||
bool
|
||||
compute_next_execution_time();
|
||||
|
@ -181,37 +133,174 @@ public:
|
|||
mark_last_executed(THD *thd);
|
||||
|
||||
bool
|
||||
update_fields(THD *thd);
|
||||
update_timing_fields(THD *thd);
|
||||
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
void *p;
|
||||
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 delete(void *ptr, size_t 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
|
||||
ulong sql_mode;
|
||||
|
||||
Event_timed();
|
||||
virtual ~Event_timed();
|
||||
|
||||
void
|
||||
init();
|
||||
|
||||
virtual int
|
||||
load_from_row(TABLE *table);
|
||||
|
||||
int
|
||||
get_create_event(THD *thd, String *buf);
|
||||
};
|
||||
|
||||
|
||||
class Event_job_data : public Event_basic
|
||||
{
|
||||
public:
|
||||
THD *thd;
|
||||
sp_head *sphead;
|
||||
|
||||
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
|
||||
get_fake_create_event(THD *thd, String *buf);
|
||||
|
||||
int
|
||||
compile(THD *thd, MEM_ROOT *mem_root);
|
||||
|
||||
bool
|
||||
is_running();
|
||||
|
||||
int
|
||||
spawn_now(void * (*thread_func)(void*), void *arg);
|
||||
|
||||
bool
|
||||
spawn_thread_finish(THD *thd);
|
||||
|
||||
void
|
||||
free_sp();
|
||||
|
||||
bool
|
||||
has_equal_db(Event_timed *etn);
|
||||
Event_job_data(const Event_job_data &); /* Prevent use of these */
|
||||
void operator=(Event_job_data &);
|
||||
};
|
||||
|
||||
int
|
||||
kill_thread(THD *thd);
|
||||
|
||||
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
|
||||
check_parse_data(THD *);
|
||||
|
||||
void
|
||||
set_thread_id(ulong tid) { thread_id= tid; }
|
||||
init_body(THD *thd);
|
||||
|
||||
private:
|
||||
|
||||
int
|
||||
init_definer(THD *thd);
|
||||
|
||||
void
|
||||
init_name(THD *thd, sp_name *spn);
|
||||
|
||||
int
|
||||
init_execute_at(THD *thd);
|
||||
|
||||
int
|
||||
init_interval(THD *thd);
|
||||
|
||||
int
|
||||
init_starts(THD *thd);
|
||||
|
||||
int
|
||||
init_ends(THD *thd);
|
||||
|
||||
Event_parse_data();
|
||||
~Event_parse_data();
|
||||
|
||||
void
|
||||
report_bad_value(const char *item_name, Item *bad_item);
|
||||
|
||||
Event_parse_data(const Event_parse_data &); /* Prevent use of these */
|
||||
void operator=(Event_parse_data &);
|
||||
};
|
||||
|
||||
#endif /* _EVENT_H_ */
|
||||
|
||||
|
||||
#endif /* _EVENT_DATA_OBJECTS_H_ */
|
1040
sql/event_db_repository.cc
Normal file
1040
sql/event_db_repository.cc
Normal file
File diff suppressed because it is too large
Load diff
107
sql/event_db_repository.h
Normal file
107
sql/event_db_repository.h
Normal file
|
@ -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_ */
|
945
sql/event_queue.cc
Normal file
945
sql/event_queue.cc
Normal file
|
@ -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);
|
||||
}
|
119
sql/event_queue.h
Normal file
119
sql/event_queue.h
Normal file
|
@ -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_ */
|
File diff suppressed because it is too large
Load diff
|
@ -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_ */
|
||||
|
|
875
sql/event_scheduler_ng.cc
Normal file
875
sql/event_scheduler_ng.cc
Normal file
|
@ -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);
|
||||
}
|
123
sql/event_scheduler_ng.h
Normal file
123
sql/event_scheduler_ng.h
Normal file
|
@ -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_ */
|
1139
sql/events.cc
1139
sql/events.cc
File diff suppressed because it is too large
Load diff
124
sql/events.h
124
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;
|
||||
|
||||
/* Prevent use of these */
|
||||
Events(const Events &);
|
||||
void operator=(Events &);
|
||||
|
|
|
@ -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_ */
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
/*
|
||||
|
|
114
sql/sql_parse.cc
114
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
/* 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();
|
||||
|
|
185
sql/sql_show.cc
185
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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
392
sql/sql_yacc.yy
392
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,73 +1258,13 @@ create:
|
|||
lex->name=$4.str;
|
||||
lex->create_info.options=$3;
|
||||
}
|
||||
| CREATE 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;
|
||||
|
||||
if (!(lex->et= new(YYTHD->mem_root) Event_timed())) // implicitly calls Event_timed::init()
|
||||
YYABORT;
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
if (!lex->et_compile_phase)
|
||||
{
|
||||
lex->et->init_name(YYTHD, $4);
|
||||
lex->et->init_definer(YYTHD);
|
||||
}
|
||||
}
|
||||
ON SCHEDULE_SYM ev_schedule_time
|
||||
opt_ev_on_completion
|
||||
opt_ev_status
|
||||
opt_ev_comment
|
||||
DO_SYM ev_sql_stmt
|
||||
{
|
||||
/*
|
||||
Restore flag if it was cleared above
|
||||
$1 - CREATE
|
||||
$2 - EVENT_SYM
|
||||
$3 - opt_if_not_exists
|
||||
$4 - sp_name
|
||||
$5 - the block above
|
||||
*/
|
||||
YYTHD->client_capabilities |= $<ulong_num>5;
|
||||
|
||||
/*
|
||||
sql_command is set here because some rules in ev_sql_stmt
|
||||
can overwrite it
|
||||
*/
|
||||
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
|
||||
view_or_trigger_or_sp_or_event
|
||||
{}
|
||||
| CREATE USER clear_privileges grant_list
|
||||
{
|
||||
|
@ -1342,121 +1283,93 @@ create:
|
|||
;
|
||||
|
||||
|
||||
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->create_info.options= $2;
|
||||
|
||||
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
|
||||
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_CREATE_EVENT;
|
||||
}
|
||||
ON SCHEDULE_SYM ev_schedule_time
|
||||
opt_ev_on_completion
|
||||
opt_ev_status
|
||||
opt_ev_comment
|
||||
DO_SYM ev_sql_stmt
|
||||
{
|
||||
/*
|
||||
Restore flag if it was cleared above
|
||||
$1 - EVENT_SYM
|
||||
$2 - opt_if_not_exists
|
||||
$3 - sp_name
|
||||
$4 - the block above
|
||||
*/
|
||||
YYTHD->client_capabilities |= $<ulong_num>4;
|
||||
|
||||
/*
|
||||
sql_command is set here because some rules in ev_sql_stmt
|
||||
can overwrite it
|
||||
*/
|
||||
Lex->sql_command= SQLCOM_CREATE_EVENT;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
sp->m_type= TYPE_ENUM_PROCEDURE;
|
||||
|
||||
lex->sphead= sp;
|
||||
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
|
||||
lex->sphead->m_body_begin= lex->ptr;
|
||||
my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0));
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
if (!(lex->sphead= new sp_head()))
|
||||
YYABORT;
|
||||
|
||||
lex->sphead->reset_thd_mem_root(YYTHD);
|
||||
lex->sphead->init(lex);
|
||||
|
||||
lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
|
||||
|
||||
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
|
||||
lex->sphead->m_chistics= &lex->sp_chistics;
|
||||
|
||||
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
|
||||
{}
|
||||
;
|
||||
|
||||
/**************************************************************************
|
||||
|
|
Loading…
Reference in a new issue