Commit 974eecc2 authored by unknown's avatar unknown

WL#3337 (Event scheduler new architecture)

This patch introduces specialized Event data objects
Event_basic as parent.
Event_queue_element used for queue storage
Event_timed used for SHOW EVENTS/ I_S.EVENTS / SHOW CREATE EVENT
Event_job_data using during execution.
Methods were moved out of Event_timed to other classes.

This patch also introduces Events::LOCK_event_metadata.
This patch gives new implementation of Events::dump_internal_status().
Now both the Event_scheduler and Event_queue return information during
their ::dump_internal_status().

Shortened a bit the runtime for executing events test cases.


mysql-test/r/events.result:
  update results
mysql-test/r/events_bugs.result:
  update results
mysql-test/r/events_logs_tests.result:
  update results
mysql-test/r/events_scheduling.result:
  update results
mysql-test/t/events.test:
  update test
  make --sleep more appropriate . saving some time could mean failure on loaded boxes though :(
  add tests for previously uncovered branches.
mysql-test/t/events_bugs.test:
  update test
  make --sleep more appropriate . saving some time could mean failure on loaded boxes though :(
  add tests for previously uncovered branches.
mysql-test/t/events_logs_tests.test:
  make the test shorter by time
mysql-test/t/events_scheduling.test:
  when selecting always use ORDER BY
mysql-test/t/events_stress.test:
  sleep 2.5secs for shorter stress test
sql/event_data_objects.cc:
  Event_timed is no more used during execution.
  Event_timed is no more used during in the memory queue.
  Event_timed is only used for SHOW CREATE EVENT/ I_S.EVENTS/ SHOW EVENTS
  Event_basic is the parent of almost all Event data objects.
  Event_basic -> Event_queue_element (used for the memory queue) -> Event_timed
  Event_basic -> Event_job_data (the object used for execution)
  Sql_alloc -> Event_parse_data (used during parsing)
sql/event_data_objects.h:
  Event_timed is no more used during execution.
  Event_timed is no more used during in the memory queue.
  Event_timed is only used for SHOW CREATE EVENT/ I_S.EVENTS/ SHOW EVENTS
  Event_basic is the parent of almost all Event data objects.
  Event_basic -> Event_queue_element (used for the memory queue) -> Event_timed
  Event_basic -> Event_job_data (the object used for execution)
  Sql_alloc -> Event_parse_data (used during parsing)
sql/event_db_repository.cc:
  Cosmetics.
  load_named_event now uses Event_basic, for polymorphism
  find_event uses Event_basic, to be polymorphic.
  use Field **fields= table->field and then index fields[...]
  Add documentation.
  Fix documentation.
sql/event_db_repository.h:
  Event_db_repository depends only on Event_basic's interface
sql/event_queue.cc:
  Cosmetics.
  Don't use Event_timed for the queue and giving back object for execution.
  Event_queue_element is for the queue, Event_job_data is for execution.
  Add Event_queue::dump_internal_status() for SHOW SCHEDULER STATUS command
sql/event_queue.h:
  Cosmetics.
  Don't use Event_timed for the queue and giving back object for execution.
  Event_queue_element is for the queue, Event_job_data is for execution.
  Add Event_queue::dump_internal_status() for SHOW SCHEDULER STATUS command
sql/event_scheduler_ng.cc:
  Add back Event_scheduler::cond_wait()
  Add back Event_scheduler::dump_internal_status()
  Using Event_job_data for execution. Make the scheduler thread unkillable (thd->command= COM_DAEMON).
  Add a lot of documentation.
sql/event_scheduler_ng.h:
  Add back Event_scheduler::cond_wait()
  Add back Event_scheduler::dump_internal_status()
  Using Event_job_data for execution.
sql/events.cc:
  Documentation
  Add LOCK_event_metadata
sql/events.h:
  Change the signature of Events::drop_event() not to use sp_name but LEX_STRING
sql/share/errmsg.txt:
  Fix error message
sql/sql_parse.cc:
  Events::drop_event() has new signature
parent b9a7fe27
......@@ -87,7 +87,6 @@ events_test event_starts_test root@localhost RECURRING NULL 20 SECOND # # ENABLE
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
......@@ -207,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;
......@@ -342,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 Waiting for next activation 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."
......@@ -358,7 +361,7 @@ 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 Waiting for next activation 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:"
......
......@@ -24,8 +24,19 @@ create event e_55 on schedule every 10 hour starts 99990101000000 do drop table
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;
......@@ -41,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 Waiting for next activation 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')
......@@ -57,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;
......@@ -64,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')|
......@@ -72,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|
......@@ -80,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;
......@@ -87,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 Waiting for next activation 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*/
......@@ -103,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 Waiting for next activation 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
......@@ -121,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 Waiting for next activation 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');
......@@ -151,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 Waiting for next activation 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
......
......@@ -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;
......@@ -49,13 +49,13 @@ USER_HOST SLEEPVAL events_test SELECT SLEEP(2)
SET SESSION long_query_time=300;
"Make it quite long"
TRUNCATE mysql.slow_log;
SET SESSION long_query_time=1;
CREATE TABLE slow_event_test (slo_val tinyint, val tinyint);
"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 +64,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
......
......@@ -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;
......@@ -83,7 +83,6 @@ 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;
--error 1562
alter event e_43 do alter event e_43 do set @a = 4;
delimiter |;
......@@ -94,7 +93,7 @@ begin
end|
delimiter ;|
set global event_scheduler = 1;
--sleep 1
--sleep 3
select db, name, body, status, interval_field, interval_value from mysql.event;
drop event e_43;
drop table test_nested;
......@@ -102,7 +101,7 @@ 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;
......@@ -165,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;
......@@ -294,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."
......@@ -312,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;
......
......@@ -45,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
#
......@@ -53,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;
......@@ -67,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';
......@@ -79,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;
......@@ -87,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
......@@ -95,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|
......@@ -103,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;
......@@ -126,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|
......@@ -133,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;
......
......@@ -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
......@@ -69,18 +68,18 @@ SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
SET SESSION long_query_time=300;
--echo "Make it quite long"
TRUNCATE mysql.slow_log;
SET SESSION long_query_time=1;
CREATE TABLE slow_event_test (slo_val tinyint, val tinyint);
--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 +87,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;
......
......@@ -61,7 +61,7 @@ while ($1)
--enable_query_log
SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2';
SET GLOBAL event_scheduler=1;
--sleep 12
--sleep 2.5
DROP DATABASE events_conn1_test2;
SET GLOBAL event_scheduler=2;
......@@ -100,7 +100,7 @@ while ($1)
}
--enable_query_log
SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_conn1_test2';
--sleep 12
--sleep 2.5
connection conn2;
--send
DROP DATABASE events_conn2_db;
......
......@@ -22,7 +22,7 @@
#include "sp_head.h"
#define EVEX_MAX_INTERVAL_VALUE 2147483647L
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
/*
......@@ -54,6 +54,8 @@ Event_parse_data::new_instance(THD *thd)
Event_parse_data::Event_parse_data()
{
DBUG_ENTER("Event_parse_data::Event_parse_data");
item_execute_at= item_expression= item_starts= item_ends= NULL;
status= ENABLED;
on_completion= ON_COMPLETION_DROP;
......@@ -65,8 +67,10 @@ Event_parse_data::Event_parse_data()
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
starts_null= ends_null= execute_at_null= TRUE;
body.str= comment.str= 0;
body.str= comment.str= NULL;
body.length= comment.length= 0;
DBUG_VOID_RETURN;
}
......@@ -120,7 +124,7 @@ void
Event_parse_data::init_body(THD *thd)
{
DBUG_ENTER("Event_parse_data::init_body");
DBUG_PRINT("info", ("body=[%s] body_begin=0x%ld end=0x%ld", body_begin,
DBUG_PRINT("info", ("body=[%s] body_begin=0x%lx end=0x%lx", body_begin,
body_begin, thd->lex->ptr));
body.length= thd->lex->ptr - body_begin;
......@@ -138,7 +142,7 @@ Event_parse_data::init_body(THD *thd)
continue;
}
/*
/*
consume closing comments
This is arguably wrong, but it's the best we have until the parser is
......@@ -235,17 +239,20 @@ Event_parse_data::init_definer(THD *thd)
*/
int
Event_parse_data::init_execute_at(THD *thd, Item *expr)
Event_parse_data::init_execute_at(THD *thd)
{
my_bool not_used;
TIME ltime;
my_time_t t;
TIME time_tmp;
DBUG_ENTER("Event_parse_data::init_execute_at");
if (expr->fix_fields(thd, &expr))
DBUG_RETURN(EVEX_PARSE_ERROR);
if (!item_execute_at)
DBUG_RETURN(0);
if (item_execute_at->fix_fields(thd, &item_execute_at))
goto wrong_value;
/* no starts and/or ends in case of execute_at */
DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
......@@ -256,12 +263,15 @@ Event_parse_data::init_execute_at(THD *thd, Item *expr)
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
(my_time_t) thd->query_start());
if ((not_used= expr->get_date(&ltime, TIME_NO_ZERO_DATE)))
DBUG_RETURN(ER_WRONG_VALUE);
if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value;
if (TIME_to_ulonglong_datetime(&ltime) <
TIME_to_ulonglong_datetime(&time_tmp))
DBUG_RETURN(EVEX_BAD_PARAMS);
{
my_error(ER_EVENT_EXEC_TIME_IN_THE_PAST, MYF(0));
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
......@@ -273,12 +283,16 @@ Event_parse_data::init_execute_at(THD *thd, Item *expr)
if (!t)
{
DBUG_PRINT("error", ("Execute AT after year 2037"));
DBUG_RETURN(ER_WRONG_VALUE);
goto wrong_value;
}
execute_at_null= FALSE;
execute_at= ltime;
DBUG_RETURN(0);
wrong_value:
report_bad_value("AT", item_execute_at);
DBUG_RETURN(ER_WRONG_VALUE);
}
......@@ -291,30 +305,44 @@ Event_parse_data::init_execute_at(THD *thd, Item *expr)
new_interval what is the interval
RETURN VALUE
0 OK
EVEX_PARSE_ERROR fix_fields failed
EVEX_BAD_PARAMS Interval is not positive
EVEX_MICROSECOND_UNSUP Microseconds are not supported.
0 OK
EVEX_PARSE_ERROR fix_fields failed (reported)
EVEX_BAD_PARAMS Interval is not positive (reported)
EVEX_MICROSECOND_UNSUP Microseconds are not supported (reported)
*/
int
Event_parse_data::init_interval(THD *thd, Item *expr, interval_type new_interval)
Event_parse_data::init_interval(THD *thd)
{
String value;
INTERVAL interval_tmp;
DBUG_ENTER("Event_parse_data::init_interval");
if (!item_expression)
DBUG_RETURN(0);
switch (interval) {
case INTERVAL_MINUTE_MICROSECOND:
case INTERVAL_HOUR_MICROSECOND:
case INTERVAL_DAY_MICROSECOND:
case INTERVAL_SECOND_MICROSECOND:
case INTERVAL_MICROSECOND:
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
DBUG_RETURN(EVEX_BAD_PARAMS);
default:
break;
}
if (expr->fix_fields(thd, &expr))
DBUG_RETURN(EVEX_PARSE_ERROR);
if (item_expression->fix_fields(thd, &item_expression))
goto wrong_value;
value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
if (get_interval_value(expr, new_interval, &value, &interval_tmp))
DBUG_RETURN(EVEX_PARSE_ERROR);
if (get_interval_value(item_expression, interval, &value, &interval_tmp))
goto wrong_value;
expression= 0;
switch (new_interval) {
switch (interval) {
case INTERVAL_YEAR:
expression= interval_tmp.year;
break;
......@@ -352,39 +380,33 @@ Event_parse_data::init_interval(THD *thd, Item *expr, interval_type new_interval
interval_tmp.minute)*60
+ interval_tmp.second;
break;
case INTERVAL_MINUTE_MICROSECOND: /* day and hour are 0 */
case INTERVAL_HOUR_MICROSECOND: /* day is anyway 0 */
case INTERVAL_DAY_MICROSECOND:
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
expression= ((((interval_tmp.day*24) + interval_tmp.hour)*60+
interval_tmp.minute)*60 +
interval_tmp.second) * 1000000L + interval_tmp.second_part;
break;
case INTERVAL_HOUR_MINUTE:
expression= interval_tmp.hour * 60 + interval_tmp.minute;
break;
case INTERVAL_MINUTE_SECOND:
expression= interval_tmp.minute * 60 + interval_tmp.second;
break;
case INTERVAL_SECOND_MICROSECOND:
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
expression= interval_tmp.second * 1000000L + interval_tmp.second_part;
break;
case INTERVAL_MICROSECOND:
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
case INTERVAL_LAST:
DBUG_ASSERT(0);
default:
;/* these are the microsec stuff */
}
if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE)
{
my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
interval= new_interval;
DBUG_RETURN(0);
wrong_value:
report_bad_value("INTERVAL", item_execute_at);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Set activation time.
Sets activation time.
SYNOPSIS
Event_parse_data::init_starts()
......@@ -406,19 +428,21 @@ Event_parse_data::init_interval(THD *thd, Item *expr, interval_type new_interval
*/
int
Event_parse_data::init_starts(THD *thd, Item *new_starts)
Event_parse_data::init_starts(THD *thd)
{
my_bool not_used;
TIME ltime, time_tmp;
my_time_t t;
DBUG_ENTER("Event_parse_data::init_starts");
if (!item_starts)
DBUG_RETURN(0);
if (new_starts->fix_fields(thd, &new_starts))
DBUG_RETURN(EVEX_PARSE_ERROR);
if (item_starts->fix_fields(thd, &item_starts))
goto wrong_value;
if ((not_used= new_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
DBUG_RETURN(EVEX_BAD_PARAMS);
if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value;
/* Let's check whether time is in the past */
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
......@@ -428,7 +452,7 @@ Event_parse_data::init_starts(THD *thd, Item *new_starts)
DBUG_PRINT("info",("starts=%lld", TIME_to_ulonglong_datetime(&ltime)));
if (TIME_to_ulonglong_datetime(&ltime) <
TIME_to_ulonglong_datetime(&time_tmp))
DBUG_RETURN(EVEX_BAD_PARAMS);
goto wrong_value;
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
......@@ -438,24 +462,25 @@ Event_parse_data::init_starts(THD *thd, Item *new_starts)
*/
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
if (!t)
{
DBUG_PRINT("error", ("STARTS after year 2037"));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
goto wrong_value;
starts= ltime;
starts_null= FALSE;
DBUG_RETURN(0);
wrong_value:
report_bad_value("STARTS", item_starts);
DBUG_RETURN(ER_WRONG_VALUE);
}
/*
Set deactivation time.
Sets deactivation time.
SYNOPSIS
Event_parse_data::init_ends()
thd THD
new_ends when?
new_ends When?
NOTES
Note that activation time is not execution time.
......@@ -467,26 +492,26 @@ Event_parse_data::init_starts(THD *thd, Item *new_starts)
RETURN VALUE
0 OK
EVEX_PARSE_ERROR fix_fields failed
ER_WRONG_VALUE starts distant date (after year 2037)
EVEX_BAD_PARAMS ENDS before STARTS
EVEX_BAD_PARAMS Error (reported)
*/
int
Event_parse_data::init_ends(THD *thd, Item *new_ends)
Event_parse_data::init_ends(THD *thd)
{
TIME ltime, ltime_now;
my_bool not_used;
my_time_t t;
DBUG_ENTER("Event_parse_data::init_ends");
if (!item_ends)
DBUG_RETURN(0);
if (new_ends->fix_fields(thd, &new_ends))
DBUG_RETURN(EVEX_PARSE_ERROR);
if (item_ends->fix_fields(thd, &item_ends))
goto error_bad_params;
DBUG_PRINT("info", ("convert to TIME"));
if ((not_used= new_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
DBUG_RETURN(EVEX_BAD_PARAMS);
if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto error_bad_params;
/*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx.
......@@ -497,15 +522,12 @@ Event_parse_data::init_ends(THD *thd, Item *new_ends)
DBUG_PRINT("info", ("get the UTC time"));
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used));
if (!t)
{
DBUG_PRINT("error", ("ENDS after year 2037"));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
goto error_bad_params;
/* Check whether ends is after starts */
DBUG_PRINT("info", ("ENDS after STARTS?"));
if (!starts_null && my_time_compare(&starts, &ltime) != -1)
DBUG_RETURN(EVEX_BAD_PARAMS);
goto error_bad_params;
/*
The parser forces starts to be provided but one day STARTS could be
......@@ -515,11 +537,173 @@ Event_parse_data::init_ends(THD *thd, Item *new_ends)
DBUG_PRINT("info", ("ENDS after NOW?"));
my_tz_UTC->gmt_sec_to_TIME(&ltime_now, thd->query_start());
if (my_time_compare(&ltime_now, &ltime) == 1)
DBUG_RETURN(EVEX_BAD_PARAMS);
goto error_bad_params;
ends= ltime;
ends_null= FALSE;
DBUG_RETURN(0);
error_bad_params:
my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
/*
Prints an error message about invalid value. Internally used
during input data verification
SYNOPSIS
Event_parse_data::report_bad_value()
item_name The name of the parameter
bad_item The parameter
*/
void
Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
{
char buff[120];
String str(buff,(uint32) sizeof(buff), system_charset_info);
String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
}
/*
Performs checking of the data gathered during the parsing phase.
SYNOPSIS
Event_parse_data::check_parse_data()
thd Thread
RETURN VALUE
FALSE OK
TRUE Error (reported)
*/
bool
Event_parse_data::check_parse_data(THD *thd)
{
bool ret;
DBUG_ENTER("Event_parse_data::check_parse_data");
DBUG_PRINT("info", ("execute_at=0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
item_execute_at, item_expression, item_starts, item_ends));
init_name(thd, identifier);
init_definer(thd);
ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
init_ends(thd);
DBUG_RETURN(ret);
}
/*
Constructor
SYNOPSIS
Event_basic::Event_basic()
*/
Event_basic::Event_basic()
{
DBUG_ENTER("Event_basic::Event_basic");
/* init memory root */
init_alloc_root(&mem_root, 256, 512);
dbname.str= name.str= NULL;
dbname.length= name.length= 0;
DBUG_VOID_RETURN;
}
/*
Destructor
SYNOPSIS
Event_basic::Event_basic()
*/
Event_basic::~Event_basic()
{
DBUG_ENTER("Event_basic::~Event_basic");
free_root(&mem_root, MYF(0));
DBUG_VOID_RETURN;
}
/*
Short function to load a char column into a LEX_STRING
SYNOPSIS
Event_basic::load_string_field()
field_name The field( enum_events_table_field is not actually used
because it's unknown in event_data_objects.h)
fields The Field array
field_value The value
*/
bool
Event_basic::load_string_fields(Field **fields, ...)
{
bool ret= FALSE;
va_list args;
enum enum_events_table_field field_name;
LEX_STRING *field_value;
DBUG_ENTER("Event_basic::load_string_fields");
va_start(args, fields);
field_name= (enum enum_events_table_field) va_arg(args, int);
while (field_name != ET_FIELD_COUNT)
{
field_value= va_arg(args, LEX_STRING *);
if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
{
ret= TRUE;
break;
}
field_value->length= strlen(field_value->str);
field_name= (enum enum_events_table_field) va_arg(args, int);
}
va_end(args);
DBUG_RETURN(ret);
}
/*
Constructor
SYNOPSIS
Event_queue_element::Event_queue_element()
*/
Event_queue_element::Event_queue_element():
status_changed(FALSE), last_executed_changed(FALSE),
on_completion(ON_COMPLETION_DROP), status(ENABLED),
expression(0), dropped(FALSE), flags(0)
{
DBUG_ENTER("Event_queue_element::Event_queue_element");
set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
starts_null= ends_null= execute_at_null= TRUE;
DBUG_VOID_RETURN;
}
/*
Destructor
SYNOPSIS
Event_queue_element::Event_queue_element()
*/
Event_queue_element::~Event_queue_element()
{
}
......@@ -530,15 +714,12 @@ Event_parse_data::init_ends(THD *thd, Item *new_ends)
Event_timed::Event_timed()
*/
Event_timed::Event_timed():status_changed(false),
last_executed_changed(false), expression(0),
created(0), modified(0),
on_completion(Event_timed::ON_COMPLETION_DROP),
status(Event_timed::ENABLED), sphead(0),
sql_mode(0), dropped(false), flags(0)
Event_timed::Event_timed():
created(0), modified(0), sql_mode(0)
{
DBUG_ENTER("Event_timed::Event_timed");
init();
DBUG_VOID_RETURN;
}
......@@ -551,7 +732,31 @@ Event_timed::Event_timed():status_changed(false),
Event_timed::~Event_timed()
{
free_root(&mem_root, MYF(0));
}
/*
Constructor
SYNOPSIS
Event_job_data::Event_job_data()
*/
Event_job_data::Event_job_data():
thd(NULL), sphead(0), sql_mode(0)
{
}
/*
Destructor
SYNOPSIS
Event_timed::~Event_timed()
*/
Event_job_data::~Event_job_data()
{
free_sp();
}
......@@ -568,31 +773,23 @@ Event_timed::init()
{
DBUG_ENTER("Event_timed::init");
dbname.str= name.str= body.str= comment.str= 0;
dbname.length= name.length= body.length= comment.length= 0;
set_zero_time(&starts, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&ends, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
set_zero_time(&last_executed, MYSQL_TIMESTAMP_DATETIME);
starts_null= ends_null= execute_at_null= TRUE;
body.str= comment.str= NULL;
body.length= comment.length= 0;
definer_user.str= definer_host.str= 0;
definer_user.length= definer_host.length= 0;
sql_mode= 0;
/* init memory root */
init_alloc_root(&mem_root, 256, 512);
DBUG_VOID_RETURN;
}
/*
Loads an event from a row from mysql.event
Loads an event's body from a row from mysql.event
SYNOPSIS
Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
Event_job_data::load_from_row(MEM_ROOT *mem_root, TABLE *table)
RETURN VALUE
0 OK
......@@ -605,78 +802,127 @@ Event_timed::init()
*/
int
Event_timed::load_from_row(TABLE *table)
Event_job_data::load_from_row(TABLE *table)
{
char *ptr;
Event_timed *et;
uint len;
bool res1, res2;
DBUG_ENTER("Event_timed::load_from_row");
DBUG_ENTER("Event_job_data::load_from_row");
if (!table)
goto error;
et= this;
if (table->s->fields != ET_FIELD_COUNT)
goto error;
if ((et->dbname.str= get_field(&mem_root, table->field[ET_FIELD_DB])) == NULL)
goto error;
load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
ET_FIELD_BODY, &body, ET_FIELD_DEFINER, &definer,
ET_FIELD_COUNT);
et->dbname.length= strlen(et->dbname.str);
/*
if ((dbname.str= get_field(&mem_root, table->field[ET_FIELD_DB])) == NullS)
goto error;
dbname.length= strlen(dbname.str);
if ((et->name.str= get_field(&mem_root, table->field[ET_FIELD_NAME])) == NULL)
if ((name.str= get_field(&mem_root, table->field[ET_FIELD_NAME])) == NullS)
goto error;
name.length= strlen(name.str);
et->name.length= strlen(et->name.str);
if ((body.str= get_field(&mem_root, table->field[ET_FIELD_BODY])) == NullS)
goto error;
body.length= strlen(body.str);
if ((et->body.str= get_field(&mem_root, table->field[ET_FIELD_BODY])) == NULL)
if ((definer.str= get_field(&mem_root,
table->field[ET_FIELD_DEFINER])) == NullS)
goto error;
et->body.length= strlen(et->body.str);
definer.length= strlen(definer.str);
*/
ptr= strchr(definer.str, '@');
if (! ptr)
ptr= definer.str;
if ((et->definer.str= get_field(&mem_root,
table->field[ET_FIELD_DEFINER])) == NullS)
len= ptr - definer.str;
definer_user.str= strmake_root(&mem_root, definer.str, len);
definer_user.length= len;
len= definer.length - len - 1;
/* 1:because of @ */
definer_host.str= strmake_root(&mem_root, ptr + 1, len);
definer_host.length= len;
sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0);
error:
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}
/*
Loads an event from a row from mysql.event
SYNOPSIS
Event_queue_element::load_from_row(MEM_ROOT *mem_root, TABLE *table)
RETURN VALUE
0 OK
EVEX_GET_FIELD_FAILED Error
NOTES
This method is silent on errors and should behave like that. Callers
should handle throwing of error messages. The reason is that the class
should not know about how to deal with communication.
*/
int
Event_queue_element::load_from_row(TABLE *table)
{
char *ptr;
bool res1, res2;
DBUG_ENTER("Event_queue_element::load_from_row");
if (!table)
goto error;
et->definer.length= strlen(et->definer.str);
ptr= strchr(et->definer.str, '@');
if (table->s->fields != ET_FIELD_COUNT)
goto error;
if (! ptr)
ptr= et->definer.str;
load_string_fields(table->field, ET_FIELD_DB, &dbname, ET_FIELD_NAME, &name,
ET_FIELD_DEFINER, &definer, ET_FIELD_COUNT);
/*
if ((dbname.str= get_field(&mem_root, table->field[ET_FIELD_DB])) == NullS)
goto error;
dbname.length= strlen(dbname.str);
len= ptr - et->definer.str;
if ((name.str= get_field(&mem_root, table->field[ET_FIELD_NAME])) == NullS)
goto error;
name.length= strlen(name.str);
et->definer_user.str= strmake_root(&mem_root, et->definer.str, len);
et->definer_user.length= len;
len= et->definer.length - len - 1; //1 is because of @
et->definer_host.str= strmake_root(&mem_root, ptr + 1, len);/* 1:because of @*/
et->definer_host.length= len;
et->starts_null= table->field[ET_FIELD_STARTS]->is_null();
res1= table->field[ET_FIELD_STARTS]->
get_date(&et->starts,TIME_NO_ZERO_DATE);
if ((definer.str= get_field(&mem_root,
table->field[ET_FIELD_DEFINER])) == NullS)
goto error;
definer.length= strlen(definer.str);
*/
starts_null= table->field[ET_FIELD_STARTS]->is_null();
res1= table->field[ET_FIELD_STARTS]->get_date(&starts, TIME_NO_ZERO_DATE);
et->ends_null= table->field[ET_FIELD_ENDS]->is_null();
res2= table->field[ET_FIELD_ENDS]->get_date(&et->ends, TIME_NO_ZERO_DATE);
ends_null= table->field[ET_FIELD_ENDS]->is_null();
res2= table->field[ET_FIELD_ENDS]->get_date(&ends, TIME_NO_ZERO_DATE);
if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
et->expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
else
et->expression= 0;
expression= 0;
/*
If res1 and res2 are true then both fields are empty.
If res1 and res2 are TRUE then both fields are empty.
Hence if ET_FIELD_EXECUTE_AT is empty there is an error.
*/
et->execute_at_null=
table->field[ET_FIELD_EXECUTE_AT]->is_null();
DBUG_ASSERT(!(et->starts_null && et->ends_null && !et->expression &&
et->execute_at_null));
if (!et->expression &&
table->field[ET_FIELD_EXECUTE_AT]-> get_date(&et->execute_at,
TIME_NO_ZERO_DATE))
execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
if (!expression &&
table->field[ET_FIELD_EXECUTE_AT]->get_date(&execute_at,
TIME_NO_ZERO_DATE))
goto error;
/*
......@@ -684,41 +930,94 @@ Event_timed::load_from_row(TABLE *table)
from 0
*/
if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
et->interval= (interval_type) ((ulonglong)
interval= (interval_type) ((ulonglong)
table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1);
else
et->interval= (interval_type) 0;
interval= (interval_type) 0;
et->created= table->field[ET_FIELD_CREATED]->val_int();
et->modified= table->field[ET_FIELD_MODIFIED]->val_int();
table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed,
TIME_NO_ZERO_DATE);
last_executed_changed= FALSE;
table->field[ET_FIELD_LAST_EXECUTED]->
get_date(&et->last_executed, TIME_NO_ZERO_DATE);
last_executed_changed= false;
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
goto error;
DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", et->name.str, ptr));
et->status= (ptr[0]=='E'? Event_timed::ENABLED:Event_timed::DISABLED);
DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
status= (ptr[0]=='E'? Event_queue_element::ENABLED:
Event_queue_element::DISABLED);
/* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
if ((ptr= get_field(&mem_root,
table->field[ET_FIELD_ON_COMPLETION])) == NullS)
table->field[ET_FIELD_ON_COMPLETION])) == NullS)
goto error;
on_completion= (ptr[0]=='D'? Event_queue_element::ON_COMPLETION_DROP:
Event_queue_element::ON_COMPLETION_PRESERVE);
DBUG_RETURN(0);
error:
DBUG_RETURN(EVEX_GET_FIELD_FAILED);
}
/*
Loads an event from a row from mysql.event
SYNOPSIS
Event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
RETURN VALUE
0 OK
EVEX_GET_FIELD_FAILED Error
NOTES
This method is silent on errors and should behave like that. Callers
should handle throwing of error messages. The reason is that the class
should not know about how to deal with communication.
*/
int
Event_timed::load_from_row(TABLE *table)
{
char *ptr;
uint len;
DBUG_ENTER("Event_timed::load_from_row");
if (Event_queue_element::load_from_row(table))
goto error;
et->on_completion= (ptr[0]=='D'? Event_timed::ON_COMPLETION_DROP:
Event_timed::ON_COMPLETION_PRESERVE);
load_string_fields(table->field, ET_FIELD_BODY, &body, ET_FIELD_COUNT);
/*
if ((body.str= get_field(&mem_root, table->field[ET_FIELD_BODY])) == NullS)
goto error;
et->comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
if (et->comment.str != NullS)
et->comment.length= strlen(et->comment.str);
body.length= strlen(body.str);
*/
ptr= strchr(definer.str, '@');
if (! ptr)
ptr= definer.str;
len= ptr - definer.str;
definer_user.str= strmake_root(&mem_root, definer.str, len);
definer_user.length= len;
len= definer.length - len - 1;
/* 1:because of @ */
definer_host.str= strmake_root(&mem_root, ptr + 1, len);
definer_host.length= len;
created= table->field[ET_FIELD_CREATED]->val_int();
modified= table->field[ET_FIELD_MODIFIED]->val_int();
comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
if (comment.str != NullS)
comment.length= strlen(comment.str);
else
et->comment.length= 0;
comment.length= 0;
et->sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
DBUG_RETURN(0);
error:
......@@ -872,7 +1171,7 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec,
tmp= *start;
if ((ret= date_add_interval(&tmp, INTERVAL_MONTH, interval)))
goto done;
/* If `tmp` is still before time_now just add one more time the interval */
if (my_time_compare(&tmp, time_now) == -1)
{
......@@ -896,7 +1195,7 @@ done:
Computes next execution time.
SYNOPSIS
Event_timed::compute_next_execution_time()
Event_queue_element::compute_next_execution_time()
RETURN VALUE
FALSE OK
......@@ -908,18 +1207,18 @@ done:
*/
bool
Event_timed::compute_next_execution_time()
Event_queue_element::compute_next_execution_time()
{
TIME time_now;
int tmp;
DBUG_ENTER("Event_timed::compute_next_execution_time");
DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=%p",
DBUG_ENTER("Event_queue_element::compute_next_execution_time");
DBUG_PRINT("enter", ("starts=%llu ends=%llu last_executed=%llu this=0x%lx",
TIME_to_ulonglong_datetime(&starts),
TIME_to_ulonglong_datetime(&ends),
TIME_to_ulonglong_datetime(&last_executed), this));
if (status == Event_timed::DISABLED)
if (status == Event_queue_element::DISABLED)
{
DBUG_PRINT("compute_next_execution_time",
("Event %s is DISABLED", name.str));
......@@ -933,11 +1232,11 @@ Event_timed::compute_next_execution_time()
{
DBUG_PRINT("info",("One-time event %s.%s of was already executed",
dbname.str, name.str, definer.str));
dropped= (on_completion == Event_timed::ON_COMPLETION_DROP);
dropped= (on_completion == Event_queue_element::ON_COMPLETION_DROP);
DBUG_PRINT("info",("One-time event will be dropped=%d.", dropped));
status= Event_timed::DISABLED;
status_changed= true;
status= Event_queue_element::DISABLED;
status_changed= TRUE;
}
goto ret;
}
......@@ -953,11 +1252,11 @@ Event_timed::compute_next_execution_time()
/* time_now is after ends. don't execute anymore */
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
if (on_completion == Event_timed::ON_COMPLETION_DROP)
dropped= true;
if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
dropped= TRUE;
DBUG_PRINT("info", ("Dropped=%d", dropped));
status= Event_timed::DISABLED;
status_changed= true;
status= Event_queue_element::DISABLED;
status_changed= TRUE;
goto ret;
}
......@@ -1019,10 +1318,10 @@ Event_timed::compute_next_execution_time()
/* Next execution after ends. No more executions */
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
if (on_completion == Event_timed::ON_COMPLETION_DROP)
dropped= true;
status= Event_timed::DISABLED;
status_changed= true;
if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
dropped= TRUE;
status= Event_queue_element::DISABLED;
status_changed= TRUE;
}
else
{
......@@ -1112,10 +1411,10 @@ Event_timed::compute_next_execution_time()
DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
execute_at_null= TRUE;
status= Event_timed::DISABLED;
status_changed= true;
if (on_completion == Event_timed::ON_COMPLETION_DROP)
dropped= true;
status= Event_queue_element::DISABLED;
status_changed= TRUE;
if (on_completion == Event_queue_element::ON_COMPLETION_DROP)
dropped= TRUE;
}
else
{
......@@ -1131,10 +1430,10 @@ Event_timed::compute_next_execution_time()
ret:
DBUG_PRINT("info", ("ret=0 execute_at=%llu",
TIME_to_ulonglong_datetime(&execute_at)));
DBUG_RETURN(false);
DBUG_RETURN(FALSE);
err:
DBUG_PRINT("info", ("ret=1"));
DBUG_RETURN(true);
DBUG_RETURN(TRUE);
}
......@@ -1143,12 +1442,12 @@ err:
time according to thd->query_start(), so the THD's clock.
SYNOPSIS
Event_timed::drop()
Event_queue_element::mark_last_executed()
thd thread context
*/
void
Event_timed::mark_last_executed(THD *thd)
Event_queue_element::mark_last_executed(THD *thd)
{
TIME time_now;
......@@ -1156,16 +1455,15 @@ Event_timed::mark_last_executed(THD *thd)
my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
last_executed= time_now; /* was execute_at */
last_executed_changed= true;
last_executed_changed= TRUE;
}
/*
Drops the event
SYNOPSIS
Event_timed::drop()
Event_queue_element::drop()
thd thread context
RETURN VALUE
......@@ -1178,13 +1476,13 @@ Event_timed::mark_last_executed(THD *thd)
*/
int
Event_timed::drop(THD *thd)
Event_queue_element::drop(THD *thd)
{
uint tmp= 0;
DBUG_ENTER("Event_timed::drop");
DBUG_ENTER("Event_queue_element::drop");
DBUG_RETURN(Events::get_instance()->
db_repository->drop_event(thd, dbname, name, false, &tmp));
DBUG_RETURN(Events::get_instance()->drop_event(thd, dbname, name, FALSE,
&tmp, TRUE));
}
......@@ -1192,12 +1490,12 @@ Event_timed::drop(THD *thd)
Saves status and last_executed_at to the disk if changed.
SYNOPSIS
Event_timed::update_fields()
Event_queue_element::update_timing_fields()
thd - thread context
RETURN VALUE
0 OK
EVEX_OPEN_TABLE_FAILED Error while opening mysql.event for writing
EVEX_OPEN_TABLE_FAILED Error while opening mysql.event for writing
EVEX_WRITE_ROW_FAILED On error to write to disk
others return code from SE in case deletion of the event
......@@ -1205,13 +1503,13 @@ Event_timed::drop(THD *thd)
*/
bool
Event_timed::update_fields(THD *thd)
Event_queue_element::update_timing_fields(THD *thd)
{
TABLE *table;
Open_tables_state backup;
int ret;
DBUG_ENTER("Event_timed::update_fields");
DBUG_ENTER("Event_queue_element::update_timing_fields");
DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
......@@ -1227,8 +1525,8 @@ Event_timed::update_fields(THD *thd)
goto done;
}
if ((ret= evex_db_find_event_by_name(thd, dbname, name, table)))
if ((ret= Events::get_instance()->db_repository->
find_event_by_name(thd, dbname, name, table)))
goto done;
store_record(table,record[1]);
......@@ -1240,13 +1538,13 @@ Event_timed::update_fields(THD *thd)
table->field[ET_FIELD_LAST_EXECUTED]->set_notnull();
table->field[ET_FIELD_LAST_EXECUTED]->store_time(&last_executed,
MYSQL_TIMESTAMP_DATETIME);
last_executed_changed= false;
last_executed_changed= FALSE;
}
if (status_changed)
{
table->field[ET_FIELD_STATUS]->set_notnull();
table->field[ET_FIELD_STATUS]->store((longlong)status, true);
status_changed= false;
table->field[ET_FIELD_STATUS]->store((longlong)status, TRUE);
status_changed= FALSE;
}
if ((table->file->ha_update_row(table->record[1],table->record[0])))
......@@ -1279,8 +1577,8 @@ int
Event_timed::get_create_event(THD *thd, String *buf)
{
int multipl= 0;
char tmp_buff[128];
String expr_buf(tmp_buff, sizeof(tmp_buff), system_charset_info);
char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
expr_buf.length(0);
DBUG_ENTER("get_create_event");
......@@ -1336,11 +1634,38 @@ Event_timed::get_create_event(THD *thd, String *buf)
}
/*
Get SHOW CREATE EVENT as string
SYNOPSIS
Event_job_data::get_create_event(THD *thd, String *buf)
thd Thread
buf String*, should be already allocated. CREATE EVENT goes inside.
RETURN VALUE
0 OK
EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been
tampered and MICROSECONDS interval or
derivative has been put there.
*/
int
Event_job_data::get_fake_create_event(THD *thd, String *buf)
{
DBUG_ENTER("Event_job_data::get_create_event");
buf->append(STRING_WITH_LEN("CREATE EVENT test.anonymous ON SCHEDULE "
"EVERY 3337 HOUR DO "));
buf->append(body.str, body.length);
DBUG_RETURN(0);
}
/*
Executes the event (the underlying sp_head object);
SYNOPSIS
Event_timed::execute()
Event_job_data::execute()
thd THD
mem_root If != NULL use it to compile the event on it
......@@ -1352,16 +1677,15 @@ Event_timed::get_create_event(THD *thd, String *buf)
*/
int
Event_timed::execute(THD *thd, MEM_ROOT *mem_root)
Event_job_data::execute(THD *thd, MEM_ROOT *mem_root)
{
Security_context *save_ctx;
/* this one is local and not needed after exec */
Security_context security_ctx;
int ret= 0;
DBUG_ENTER("Event_timed::execute");
DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]",
dbname.str, name.str, (int) expression));
DBUG_ENTER("Event_job_data::execute");
DBUG_PRINT("info", ("EXECUTING %s.%s", dbname.str, name.str));
thd->change_security_context(definer_user, definer_host, dbname,
&security_ctx, &save_ctx);
......@@ -1406,8 +1730,7 @@ done:
delete sphead;
sphead= 0;
}
DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
dbname.str, name.str, (int) expression, ret));
DBUG_PRINT("info", ("EXECUTED %s.%s ret=%d", dbname.str, name.str, ret));
DBUG_RETURN(ret);
}
......@@ -1416,14 +1739,14 @@ done:
/*
Frees the memory of the sp_head object we hold
SYNOPSIS
Event_timed::free_sp()
Event_job_data::free_sp()
*/
void
Event_timed::free_sp()
Event_job_data::free_sp()
{
delete sphead;
sphead= 0;
sphead= NULL;
}
......@@ -1432,7 +1755,7 @@ Event_timed::free_sp()
sp_head object held by the event
SYNOPSIS
Event_timed::compile()
Event_job_data::compile()
thd thread context, used for memory allocation mostly
mem_root if != NULL then this memory root is used for allocs
instead of thd->mem_root
......@@ -1444,7 +1767,7 @@ Event_timed::free_sp()
*/
int
Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
{
int ret= 0;
MEM_ROOT *tmp_mem_root= 0;
......@@ -1454,7 +1777,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
char *old_query;
uint old_query_len;
ulong old_sql_mode= thd->variables.sql_mode;
char create_buf[2048];
char create_buf[15 * STRING_BUFFER_USUAL_SIZE];
String show_create(create_buf, sizeof(create_buf), system_charset_info);
CHARSET_INFO *old_character_set_client,
*old_collation_connection,
......@@ -1463,11 +1786,11 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
/* this one is local and not needed after exec */
Security_context security_ctx;
DBUG_ENTER("Event_timed::compile");
DBUG_ENTER("Event_job_data::compile");
show_create.length(0);
switch (get_create_event(thd, &show_create)) {
switch (get_fake_create_event(thd, &show_create)) {
case EVEX_MICROSECOND_UNSUP:
sql_print_error("Scheduler");
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
......@@ -1503,7 +1826,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root)
thd->db= dbname.str;
thd->db_length= dbname.length;
thd->query= show_create.c_ptr();
thd->query= show_create.c_ptr_safe();
thd->query_length= show_create.length();
DBUG_PRINT("info", ("query:%s",thd->query));
......@@ -1565,37 +1888,19 @@ done:
}
/*
Checks whether two events have the same name
SYNOPSIS
event_timed_name_equal()
RETURN VALUE
TRUE names are equal
FALSE names are not equal
*/
bool
event_timed_name_equal(Event_timed *et, LEX_STRING *name)
{
return !sortcmp_lex_string(et->name, *name, system_charset_info);
}
/*
Checks whether two events are in the same schema
SYNOPSIS
event_timed_db_equal()
event_basic_db_equal()
RETURN VALUE
TRUE schemas are equal
FALSE schemas are not equal
TRUE Equal
FALSE Not equal
*/
bool
event_timed_db_equal(Event_timed *et, LEX_STRING *db)
event_basic_db_equal(LEX_STRING *db, Event_basic *et)
{
return !sortcmp_lex_string(et->dbname, *db, system_charset_info);
}
......@@ -1605,17 +1910,16 @@ event_timed_db_equal(Event_timed *et, LEX_STRING *db)
Checks whether two events are equal by identifiers
SYNOPSIS
event_timed_identifier_equal()
event_basic_identifier_equal()
RETURN VALUE
TRUE equal
FALSE not equal
TRUE Equal
FALSE Not equal
*/
bool
event_timed_identifier_equal(LEX_STRING db, LEX_STRING name, Event_timed *b)
event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
{
return !sortcmp_lex_string(name, b->name, system_charset_info) &&
!sortcmp_lex_string(db, b->dbname, system_charset_info);
}
......@@ -47,30 +47,46 @@
class sp_head;
class Sql_alloc;
class Event_timed;
class Event_basic;
/* Compares only the schema part of the identifier */
bool
event_timed_db_equal(Event_timed *et, LEX_STRING *db);
event_basic_db_equal( LEX_STRING *db, Event_basic *et);
/* Compares the whole identifier*/
bool
event_timed_identifier_equal(LEX_STRING db, LEX_STRING name, Event_timed *b);
event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b);
class Event_timed
class Event_basic
{
Event_timed(const Event_timed &); /* Prevent use of these */
void operator=(Event_timed &);
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;
MEM_ROOT mem_root;
public:
THD *thd;
enum enum_status
{
ENABLED = 1,
......@@ -83,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;
......@@ -104,20 +113,32 @@ 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;
uint flags;//all kind of purposes
bool dropped;
uint flags;//all kind of purposes
Event_queue_element();
virtual ~Event_queue_element();
virtual int
load_from_row(TABLE *table);
bool
compute_next_execution_time();
int
drop(THD *thd);
void
mark_last_executed(THD *thd);
bool
update_timing_fields(THD *thd);
static void *operator new(size_t size)
{
void *p;
DBUG_ENTER("Event_timed::new(size)");
DBUG_ENTER("Event_queue_element::new(size)");
p= my_malloc(size, MYF(0));
DBUG_PRINT("info", ("alloc_ptr=0x%lx", p));
DBUG_RETURN(p);
......@@ -125,54 +146,84 @@ public:
static void operator delete(void *ptr, size_t size)
{
DBUG_ENTER("Event_timed::delete(ptr,size)");
DBUG_ENTER("Event_queue_element::delete(ptr,size)");
DBUG_PRINT("enter", ("free_ptr=0x%lx", ptr));
TRASH(ptr, size);
my_free((gptr) ptr, MYF(0));
DBUG_VOID_RETURN;
}
};
Event_timed();
~Event_timed();
class Event_timed : public Event_queue_element
{
Event_timed(const Event_timed &); /* Prevent use of these */
void operator=(Event_timed &);
void
init();
public:
LEX_STRING body;
int
load_from_row(TABLE *table);
LEX_STRING definer_user;
LEX_STRING definer_host;
bool
compute_next_execution_time();
LEX_STRING comment;
int
drop(THD *thd);
ulonglong created;
ulonglong modified;
ulong sql_mode;
Event_timed();
virtual ~Event_timed();
void
mark_last_executed(THD *thd);
init();
bool
update_fields(THD *thd);
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);
void
free_sp();
Event_job_data(const Event_job_data &); /* Prevent use of these */
void operator=(Event_job_data &);
};
class Event_parse_data : public Sql_alloc
{
Event_parse_data(const Event_parse_data &); /* Prevent use of these */
void operator=(Event_parse_data &);
public:
enum enum_status
{
......@@ -185,7 +236,6 @@ public:
ON_COMPLETION_DROP = 1,
ON_COMPLETION_PRESERVE
};
enum enum_on_completion on_completion;
enum enum_status status;
......@@ -193,8 +243,8 @@ public:
LEX_STRING dbname;
LEX_STRING name;
LEX_STRING body;
LEX_STRING definer;// combination of user and host
LEX_STRING body;
LEX_STRING comment;
Item* item_starts;
......@@ -216,59 +266,41 @@ public:
static Event_parse_data *
new_instance(THD *thd);
Event_parse_data();
~Event_parse_data();
bool
check_parse_data(THD *);
int
init_definer(THD *thd);
void
init_body(THD *thd);
int
init_execute_at(THD *thd, Item *expr);
private:
int
init_interval(THD *thd, Item *expr, interval_type new_interval);
init_definer(THD *thd);
void
init_name(THD *thd, sp_name *spn);
int
init_starts(THD *thd, Item *starts);
init_execute_at(THD *thd);
int
init_ends(THD *thd, Item *ends);
void
init_body(THD *thd);
};
class Event_job_data
{
public:
LEX_STRING dbname;
LEX_STRING name;
sp_head *sphead;
LEX_STRING definer;
LEX_STRING body;
ulong sql_mode;
THD *thd;
Event_job_data(){}
~Event_job_data(){}
init_interval(THD *thd);
int
execute();
init_starts(THD *thd);
private:
int
load_from_disk();
int
compile();
init_ends(THD *thd);
Event_parse_data();
~Event_parse_data();
void
report_bad_value(const char *item_name, Item *bad_item);
Event_job_data(const Event_job_data &); /* Prevent use of these */
void operator=(Event_job_data &);
Event_parse_data(const Event_parse_data &); /* Prevent use of these */
void operator=(Event_parse_data &);
};
#endif /* _EVENT_DATA_OBJECTS_H_ */
......@@ -29,7 +29,8 @@
time_t mysql_event_last_create_time= 0L;
static
TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = {
TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
{
{
{(char *) STRING_WITH_LEN("db")},
{(char *) STRING_WITH_LEN("char(64)")},
......@@ -128,50 +129,49 @@ TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = {
Puts some data common to CREATE and ALTER EVENT into a row.
SYNOPSIS
evex_fill_row()
mysql_event_fill_row()
thd THD
table The row to fill out
et Event's data
is_update CREATE EVENT or ALTER EVENT
RETURN VALUE
0 - OK
EVEX_GENERAL_ERROR - bad data
EVEX_GET_FIELD_FAILED - field count does not match. table corrupted?
0 OK
EVEX_GENERAL_ERROR Bad data
EVEX_GET_FIELD_FAILED Field count does not match. table corrupted?
DESCRIPTION
Used both when an event is created and when it is altered.
*/
static int
evex_fill_row(THD *thd, TABLE *table, Event_parse_data *et, my_bool is_update)
mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et,
my_bool is_update)
{
CHARSET_INFO *scs= system_charset_info;
enum enum_events_table_field field_num;
enum enum_events_table_field f_num;
Field **fields= table->field;
DBUG_ENTER("evex_fill_row");
DBUG_ENTER("mysql_event_fill_row");
DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
DBUG_PRINT("info", ("name =[%s]", et->name.str));
DBUG_PRINT("info", ("body =[%s]", et->body.str));
if (table->field[field_num= ET_FIELD_DEFINER]->
store(et->definer.str, et->definer.length, scs))
if (fields[f_num= ET_FIELD_DEFINER]->
store(et->definer.str, et->definer.length, scs))
goto err_truncate;
if (table->field[field_num= ET_FIELD_DB]->
store(et->dbname.str, et->dbname.length, scs))
if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
goto err_truncate;
if (table->field[field_num= ET_FIELD_NAME]->
store(et->name.str, et->name.length, scs))
if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
goto err_truncate;
/* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
table->field[ET_FIELD_ON_COMPLETION]->
store((longlong)et->on_completion, true);
fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
table->field[ET_FIELD_STATUS]->store((longlong)et->status, true);
fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
/*
Change the SQL_MODE only if body was present in an ALTER EVENT and of course
......@@ -179,52 +179,46 @@ evex_fill_row(THD *thd, TABLE *table, Event_parse_data *et, my_bool is_update)
*/
if (et->body.str)
{
table->field[ET_FIELD_SQL_MODE]->
store((longlong)thd->variables.sql_mode, true);
if (table->field[field_num= ET_FIELD_BODY]->
store(et->body.str, et->body.length, scs))
fields[ET_FIELD_SQL_MODE]->store((longlong)thd->variables.sql_mode, TRUE);
if (fields[f_num= ET_FIELD_BODY]->store(et->body.str, et->body.length, scs))
goto err_truncate;
}
if (et->expression)
{
table->field[ET_FIELD_INTERVAL_EXPR]->set_notnull();
table->field[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, true);
fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
/*
In the enum (C) intervals start from 0 but in mysql enum valid values start
from 1. Thus +1 offset is needed!
In the enum (C) intervals start from 0 but in mysql enum valid values
start from 1. Thus +1 offset is needed!
*/
table->field[ET_FIELD_TRANSIENT_INTERVAL]->
store((longlong)et->interval+1, true);
fields[ET_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, TRUE);
table->field[ET_FIELD_EXECUTE_AT]->set_null();
fields[ET_FIELD_EXECUTE_AT]->set_null();
if (!et->starts_null)
{
table->field[ET_FIELD_STARTS]->set_notnull();
table->field[ET_FIELD_STARTS]->
store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
fields[ET_FIELD_STARTS]->set_notnull();
fields[ET_FIELD_STARTS]->store_time(&et->starts, MYSQL_TIMESTAMP_DATETIME);
}
if (!et->ends_null)
{
table->field[ET_FIELD_ENDS]->set_notnull();
table->field[ET_FIELD_ENDS]->
store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
fields[ET_FIELD_ENDS]->set_notnull();
fields[ET_FIELD_ENDS]->store_time(&et->ends, MYSQL_TIMESTAMP_DATETIME);
}
}
else if (et->execute_at.year)
{
table->field[ET_FIELD_INTERVAL_EXPR]->set_null();
table->field[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
table->field[ET_FIELD_STARTS]->set_null();
table->field[ET_FIELD_ENDS]->set_null();
fields[ET_FIELD_INTERVAL_EXPR]->set_null();
fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
fields[ET_FIELD_STARTS]->set_null();
fields[ET_FIELD_ENDS]->set_null();
table->field[ET_FIELD_EXECUTE_AT]->set_notnull();
table->field[ET_FIELD_EXECUTE_AT]->
fields[ET_FIELD_EXECUTE_AT]->set_notnull();
fields[ET_FIELD_EXECUTE_AT]->
store_time(&et->execute_at, MYSQL_TIMESTAMP_DATETIME);
}
else
......@@ -236,47 +230,23 @@ evex_fill_row(THD *thd, TABLE *table, Event_parse_data *et, my_bool is_update)
*/
}
((Field_timestamp *)table->field[ET_FIELD_MODIFIED])->set_time();
((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time();
if (et->comment.str)
{
if (table->field[field_num= ET_FIELD_COMMENT]->
store(et->comment.str, et->comment.length, scs))
if (fields[f_num= ET_FIELD_COMMENT]->
store(et->comment.str, et->comment.length, scs))
goto err_truncate;
}
DBUG_RETURN(0);
err_truncate:
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name);
my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
DBUG_RETURN(EVEX_GENERAL_ERROR);
}
/*
Find row in open mysql.event table representing event
SYNOPSIS
evex_db_find_event_by_name()
thd Thread context
dbname Name of event's database
rname Name of the event inside the db
table TABLE object for open mysql.event table.
RETURN VALUE
0 - Routine found
EVEX_KEY_NOT_FOUND - No routine with given name
*/
int
evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name,
TABLE *table)
{
return Events::get_instance()->db_repository->
find_event_by_name(thd, dbname, ev_name, table);
}
/*
Performs an index scan of event_table (mysql.event) and fills schema_table.
......@@ -313,7 +283,7 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
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() */
/* Don't send error, it would be done by sql_alloc_error_handler() */
}
else
{
......@@ -440,100 +410,14 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, char *db)
}
/*
Looks for a named event in mysql.event and in case of success returns
an object will data loaded from the table.
SYNOPSIS
Event_db_repository::find_event()
thd THD
name the name of the event to find
ett event's data if event is found
tbl TABLE object to use when not NULL
root On which root to load the event
NOTES
1) Use sp_name for look up, return in **ett if found
2) tbl is not closed at exit
RETURN VALUE
0 ok In this case *ett is set to the event
# error *ett == 0
*/
int
Event_db_repository::find_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_timed **ett, TABLE *tbl)
{
TABLE *table;
int ret;
Event_timed *et= NULL;
DBUG_ENTER("Event_db_repository::find_event");
DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
if (tbl)
table= tbl;
else if (open_event_table(thd, TL_READ, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
ret= EVEX_GENERAL_ERROR;
goto done;
}
if ((ret= evex_db_find_event_by_name(thd, dbname, name, table)))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
goto done;
}
et= new Event_timed;
/*
1)The table should not be closed beforehand. ::load_from_row() only loads
and does not compile
2)::load_from_row() is silent on error therefore we emit error msg here
*/
if ((ret= et->load_from_row(table)))
{
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
goto done;
}
done:
if (ret)
{
delete et;
et= NULL;
}
/* don't close the table if we haven't opened it ourselves */
if (!tbl && table)
close_thread_tables(thd);
*ett= et;
DBUG_RETURN(ret);
}
int
Event_db_repository::init_repository()
{
return 0;
}
void
Event_db_repository::deinit_repository()
{
}
/*
Open mysql.event table for read
SYNOPSIS
Events::open_event_table()
thd Thread context
lock_type How to lock the table
table We will store the open table here
thd [in] Thread context
lock_type [in] How to lock the table
table [out] We will store the open table here
RETURN VALUE
1 Cannot lock table
......@@ -574,33 +458,26 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
Checks parameters which we got from the parsing phase.
SYNOPSIS
evex_check_params()
check_parse_params()
thd THD
et event's data
RETURNS
0 OK
EVEX_BAD_PARAMS Error
REMARKS
Issues error messages
EVEX_BAD_PARAMS Error (reported)
*/
int
evex_check_params(THD *thd, Event_parse_data *parse_data)
static int
check_parse_params(THD *thd, Event_parse_data *parse_data)
{
const char *pos= NULL;
Item *bad_item;
int res;
DBUG_ENTER("evex_check_params");
DBUG_PRINT("info", ("execute_at=0x%d expr=0x%d starts=0x%d ends=0x%d",
parse_data->item_execute_at,
parse_data->item_expression,
parse_data->item_starts,
parse_data->item_ends));
DBUG_ENTER("check_parse_params");
parse_data->init_name(thd, parse_data->identifier);
parse_data->init_definer(thd);
if (parse_data->check_parse_data(thd))
DBUG_RETURN(EVEX_BAD_PARAMS);
if (!parse_data->dbname.str ||
(thd->lex->sql_command == SQLCOM_ALTER_EVENT && thd->lex->spname &&
......@@ -617,68 +494,7 @@ evex_check_params(THD *thd, Event_parse_data *parse_data)
is_schema_db(thd->lex->spname->m_db.str)))))
DBUG_RETURN(EVEX_BAD_PARAMS);
if (parse_data->item_execute_at)
{
DBUG_PRINT("info", ("ONE TIME"));
if (parse_data->init_execute_at(thd, parse_data->item_execute_at))
{
pos= "AT";
bad_item= parse_data->item_execute_at;
goto wrong_value;
}
}
else
{
int res;
DBUG_PRINT("info", ("RECURRING"));
if (parse_data->item_expression &&
(res= parse_data->init_interval(thd, parse_data->item_expression,
parse_data->interval)))
{
switch (res) {
case EVEX_BAD_PARAMS:
my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
break;
case EVEX_MICROSECOND_UNSUP:
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
break;
default:
pos= "INTERVAL";
bad_item= parse_data->item_expression;
goto wrong_value;
}
DBUG_RETURN(EVEX_BAD_PARAMS);
}
if (parse_data->item_starts &&
parse_data->init_starts(thd, parse_data->item_starts))
{
pos= "STARTS";
bad_item= parse_data->item_starts;
goto wrong_value;
}
if (parse_data->item_ends &&
parse_data->init_ends(thd, parse_data->item_ends))
{
/*
despite the error name the value is
eng "ENDS is either invalid or before STARTS"
*/
my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
DBUG_RETURN(EVEX_BAD_PARAMS);
}
}
DBUG_RETURN(0);
wrong_value:
{
char buff[120];
String str(buff,(uint32) sizeof(buff), system_charset_info);
String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
my_error(ER_WRONG_VALUE, MYF(0), pos, str2? str2->c_ptr():"NULL");
DBUG_RETURN(EVEX_BAD_PARAMS);
}
}
......@@ -687,18 +503,18 @@ wrong_value:
SYNOPSIS
Event_db_repository::create_event()
thd THD
et Event_timed object containing information for the event
create_if_not If an warning should be generated in case event exists
rows_affected How many rows were affected
thd [in] THD
et [in] Object containing info about the event
create_if_not [in] Whether to generate anwarning in case event exists
rows_affected [out] How many rows were affected
RETURN VALUE
0 - OK
EVEX_GENERAL_ERROR - Failure
DESCRIPTION
Creates an event. Relies on evex_fill_row which is shared with
db_update_event. The name of the event is inside "et".
Creates an event. Relies on mysql_event_fill_row which is shared with
::update_event. The name of the event is inside "et".
*/
int
......@@ -709,7 +525,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
CHARSET_INFO *scs= system_charset_info;
TABLE *table;
char olddb[128];
bool dbchanged= false;
bool dbchanged= FALSE;
DBUG_ENTER("Event_db_repository::create_event");
*rows_affected= 0;
......@@ -720,15 +536,14 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
goto err;
}
if (evex_check_params(thd, parse_data))
if (check_parse_params(thd, parse_data))
goto err;
DBUG_PRINT("info", ("name: %.*s", parse_data->name.length,
parse_data->name.str));
DBUG_PRINT("info", ("check existance of an event with the same name"));
if (!evex_db_find_event_by_name(thd, parse_data->dbname,
parse_data->name, table))
if (!find_event_by_name(thd, parse_data->dbname, parse_data->name, table))
{
if (create_if_not)
{
......@@ -784,10 +599,10 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
/*
evex_fill_row() calls my_error() in case of error so no need to
mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here
*/
if ((ret= evex_fill_row(thd, table, parse_data, false)))
if ((ret= mysql_event_fill_row(thd, table, parse_data, FALSE)))
goto err;
/* Close active transaction only if We are going to modify disk */
......@@ -800,16 +615,6 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
goto err;
}
#ifdef USE_THIS_CODE_AS_TEMPLATE_WHEN_EVENT_REPLICATION_IS_AGREED
if (mysql_bin_log.is_open())
{
thd->clear_error();
/* Such a statement can always go directly to binlog, no trans cache */
thd->binlog_query(THD::MYSQL_QUERY_TYPE, thd->query, thd->query_length,
FALSE, FALSE);
}
#endif
*rows_affected= 1;
ok:
if (dbchanged)
......@@ -838,7 +643,7 @@ err:
RETURN VALUE
0 OK
EVEX_GENERAL_ERROR Error occured (my_error() called)
EVEX_GENERAL_ERROR Error occured and reported
NOTES
sp_name is passed since this is the name of the event to
......@@ -860,7 +665,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
goto err;
}
if (evex_check_params(thd, parse_data))
if (check_parse_params(thd, parse_data))
goto err;
DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
......@@ -879,7 +684,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
goto err;
}
if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table))
if (!find_event_by_name(thd, new_name->m_db, new_name->m_name, table))
{
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str);
goto err;
......@@ -904,10 +709,10 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
evex_fill_row() calls my_error() in case of error so no need to
mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here
*/
if ((ret= evex_fill_row(thd, table, parse_data, true)))
if ((ret= mysql_event_fill_row(thd, table, parse_data, TRUE)))
goto err;
if (new_name)
......@@ -944,11 +749,12 @@ err:
SYNOPSIS
Event_db_repository::drop_event()
thd THD
db database name
name event's name
drop_if_exists if set and the event not existing => warning onto the stack
rows_affected affected number of rows is returned heres
thd [in] THD
db [in] Database name
name [in] Event's name
drop_if_exists [in] If set and the event not existing => warning
onto the stack
rows_affected [out] Affected number of rows is returned heres
RETURN VALUE
0 OK
......@@ -974,17 +780,16 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
goto done;
}
if (!(ret= evex_db_find_event_by_name(thd, db, name, table)))
{
/* Close active transaction only if We are going to modify disk */
switch ((ret= find_event_by_name(thd, db, name, table))) {
case 0:
/* Close active transaction only if we are actually going to modify disk */
if ((ret= end_active_trans(thd)))
goto done;
break;
if ((ret= table->file->ha_delete_row(table->record[0])))
my_error(ER_EVENT_CANNOT_DELETE, MYF(0));
}
else if (ret == EVEX_KEY_NOT_FOUND)
{
break;
case EVEX_KEY_NOT_FOUND:
if (drop_if_exists)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
......@@ -993,19 +798,34 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
ret= 0;
} else
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
break;
default:
;
}
done:
/*
evex_drop_event() is used by Event_timed::drop therefore
we have to close our thread tables.
*/
close_thread_tables(thd);
thd->restore_backup_open_tables_state(&backup);
DBUG_RETURN(ret);
}
/*
Positions the internal pointer of `table` to the place where (db, name)
is stored.
SYNOPSIS
Event_db_repository::find_event_by_name()
thd Thread
db Schema
name Event name
table Opened mysql.event
RETURN VALUE
0 OK
EVEX_KEY_NOT_FOUND No such event
*/
int
Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db,
LEX_STRING name, TABLE *table)
......@@ -1043,25 +863,31 @@ Event_db_repository::find_event_by_name(THD *thd, LEX_STRING db,
}
int
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
{
return drop_events_by_field(thd, ET_FIELD_DB, schema);
}
/*
Drops all events in the selected database, from mysql.event.
SYNOPSIS
Event_db_repository::drop_schema_events()
thd Thread
schema The database to clean from events
RETURN VALUE
0 OK
!0 Error (Reported)
*/
int
Event_db_repository::drop_user_events(THD *thd, LEX_STRING definer)
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
{
return drop_events_by_field(thd, ET_FIELD_DEFINER, definer);
return drop_events_by_field(thd, ET_FIELD_DB, schema);
}
/*
Drops all events in the selected database, from mysql.event.
Drops all events by field which has specific value of the field
SYNOPSIS
drop_events_from_table_by_field()
Event_db_repository::drop_events_by_field()
thd Thread
table mysql.event TABLE
field Which field of the row to use for matching
......@@ -1116,56 +942,63 @@ Event_db_repository::drop_events_by_field(THD *thd,
/*
Looks for a named event in mysql.event and then loads it from
the table, compiles and inserts it into the cache.
Looks for a named event in mysql.event and in case of success returns
an object will data loaded from the table.
SYNOPSIS
Event_db_repository::load_named_event_timed()
thd THD
etn The name of the event to load and compile on scheduler's root
etn_new The loaded event
Event_db_repository::find_event()
thd [in] THD
name [in] The name of the event to find
ett [out] Event's data if event is found
tbl [in] TABLE object to use when not NULL
NOTES
1) Use sp_name for look up, return in **ett if found
2) tbl is not closed at exit
RETURN VALUE
NULL Error during compile or the event is non-enabled.
otherwise Address
0 ok In this case *ett is set to the event
# error *ett == 0
*/
int
Event_db_repository::load_named_event_timed(THD *thd, LEX_STRING dbname,
LEX_STRING name,
Event_timed **etn_new)
Event_db_repository::find_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_basic *et)
{
int ret= 0;
MEM_ROOT *tmp_mem_root;
Event_timed *et_loaded= NULL;
Open_tables_state backup;
TABLE *table;
int ret;
DBUG_ENTER("Event_db_repository::find_event");
DBUG_PRINT("enter", ("name: %*s", name.length, name.str));
DBUG_ENTER("Event_db_repository::load_named_event_timed");
DBUG_PRINT("enter",("thd=%p name:%*s",thd, name.length, name.str));
if (open_event_table(thd, TL_READ, &table))
{
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
ret= EVEX_GENERAL_ERROR;
goto done;
}
thd->reset_n_backup_open_tables_state(&backup);
/* No need to use my_error() here because db_find_event() has done it */
ret= find_event(thd, dbname, name, &et_loaded, NULL);
thd->restore_backup_open_tables_state(&backup);
/* In this case no memory was allocated so we don't need to clean */
if (ret)
DBUG_RETURN(OP_LOAD_ERROR);
if ((ret= find_event_by_name(thd, dbname, name, table)))
{
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
goto done;
}
/*
1)The table should not be closed beforehand. ::load_from_row() only loads
and does not compile
if (et_loaded->status != Event_timed::ENABLED)
2)::load_from_row() is silent on error therefore we emit error msg here
*/
if ((ret= et->load_from_row(table)))
{
/*
We don't load non-enabled events.
In db_find_event() `et_new` was allocated on the heap and not on
scheduler_root therefore we delete it here.
*/
delete et_loaded;
DBUG_RETURN(OP_DISABLED_EVENT);
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
goto done;
}
et_loaded->compute_next_execution_time();
*etn_new= et_loaded;
done:
if (table)
close_thread_tables(thd);
DBUG_RETURN(OP_OK);
DBUG_RETURN(ret);
}
......@@ -1174,50 +1007,34 @@ Event_db_repository::load_named_event_timed(THD *thd, LEX_STRING dbname,
the table, compiles and inserts it into the cache.
SYNOPSIS
Event_db_repository::load_named_event_job()
thd THD
etn The name of the event to load and compile on scheduler's root
etn_new The loaded event
Event_db_repository::load_named_event()
thd [in] THD
dbname [in] Event's db name
name [in] Event's name
etn_new [out] The loaded event
RETURN VALUE
NULL Error during compile or the event is non-enabled.
otherwise Address
OP_OK OK
OP_LOAD_ERROR Error during loading from disk
*/
int
Event_db_repository::load_named_event_job(THD *thd, LEX_STRING dbname,
LEX_STRING name,
Event_job_data **etn_new)
Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
LEX_STRING name, Event_basic *etn)
{
int ret= 0;
MEM_ROOT *tmp_mem_root;
Event_timed *et_loaded= NULL;
Open_tables_state backup;
DBUG_ENTER("Event_db_repository::load_named_event_job");
DBUG_PRINT("enter",("thd=%p name:%*s",thd, name.length, name.str));
#if 0
DBUG_ENTER("Event_db_repository::load_named_event");
DBUG_PRINT("enter",("thd=0x%lx name:%*s",thd, name.length, name.str));
thd->reset_n_backup_open_tables_state(&backup);
/* No need to use my_error() here because db_find_event() has done it */
ret= find_event(thd, dbname, name, &et_loaded, NULL);
/* No need to use my_error() here because find_event() has done it */
ret= find_event(thd, dbname, name, etn);
thd->restore_backup_open_tables_state(&backup);
/* In this case no memory was allocated so we don't need to clean */
if (ret)
DBUG_RETURN(OP_LOAD_ERROR);
if (et_loaded->status != Event_timed::ENABLED)
{
/*
We don't load non-enabled events.
In db_find_event() `et_new` was allocated on the heap and not on
scheduler_root therefore we delete it here.
*/
delete et_loaded;
DBUG_RETURN(OP_DISABLED_EVENT);
}
et_loaded->compute_next_execution_time();
*etn_new= et_loaded;
#endif
DBUG_RETURN(OP_OK);
}
......@@ -38,11 +38,6 @@ enum enum_events_table_field
};
int
evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name,
TABLE *table);
int
events_table_index_read_for_db(THD *thd, TABLE *schema_table,
TABLE *event_table);
......@@ -53,22 +48,13 @@ 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_timed;
class Event_basic;
class Event_parse_data;
class Event_queue_element;
class Event_job_data;
class Event_db_repository
{
public:
Event_db_repository(){}
~Event_db_repository(){}
int
init_repository();
void
deinit_repository();
int
create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not,
......@@ -85,19 +71,10 @@ public:
drop_schema_events(THD *thd, LEX_STRING schema);
int
drop_user_events(THD *thd, LEX_STRING definer);
int
find_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_timed **ett,
TABLE *tbl);
int
load_named_event_timed(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_timed **etn_new);
find_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
int
load_named_event_job(THD *thd, LEX_STRING dbname, LEX_STRING name,
Event_job_data **etn_new);
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);
......
......@@ -36,16 +36,16 @@
/*
Compares the execute_at members of 2 Event_timed instances.
Compares the execute_at members of 2 Event_queue_element instances.
Used as callback for the prioritized queue when shifting
elements inside.
SYNOPSIS
event_timed_compare_q()
event_queue_element_data_compare_q()
vptr - not used (set it to NULL)
a - first Event_timed object
b - second Event_timed object
a - first Event_queue_element object
b - second Event_queue_element object
RETURN VALUE
-1 - a->execute_at < b->execute_at
......@@ -57,14 +57,13 @@
*/
static int
event_timed_compare_q(void *vptr, byte* a, byte *b)
event_queue_element_compare_q(void *vptr, byte* a, byte *b)
{
return my_time_compare(&((Event_timed *)a)->execute_at,
&((Event_timed *)b)->execute_at);
return my_time_compare(&((Event_queue_element *)a)->execute_at,
&((Event_queue_element *)b)->execute_at);
}
/*
Constructor of class Event_queue.
......@@ -79,6 +78,120 @@ Event_queue::Event_queue()
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
......@@ -90,28 +203,31 @@ Event_queue::Event_queue()
RETURN VALUE
OP_OK OK or scheduler not working
OP_LOAD_ERROR Error during loading from disk
OP_ALREADY_EXISTS Event already in the queue
*/
int
Event_queue::create_event(THD *thd, Event_parse_data *et)
Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
{
int res;
Event_timed *et_new;
Event_queue_element *element_new;
DBUG_ENTER("Event_queue::create_event");
DBUG_PRINT("enter", ("thd=%p et=%p lock=%p",thd,et, &LOCK_event_queue));
DBUG_PRINT("enter", ("thd=0x%lx et=%s.%s",thd, dbname.str, name.str));
res= db_repository->load_named_event_timed(thd, et->dbname, et->name, &et_new);
LOCK_QUEUE_DATA();
if (!res)
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
{
DBUG_PRINT("info", ("new event in the queue %p", et_new));
queue_insert_safe(&queue, (byte *) et_new);
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();
}
else if (res == OP_DISABLED_EVENT)
res= OP_OK;
UNLOCK_QUEUE_DATA();
DBUG_RETURN(res);
}
......@@ -123,9 +239,10 @@ Event_queue::create_event(THD *thd, Event_parse_data *et)
SYNOPSIS
Event_queue::update_event()
thd Thread
et The event to replace(add) into the queue
new_schema New schema, in case of RENAME TO
new_name New name, in case of RENAME TO
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
......@@ -133,43 +250,57 @@ Event_queue::create_event(THD *thd, Event_parse_data *et)
*/
int
Event_queue::update_event(THD *thd, Event_parse_data *et,
Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
LEX_STRING *new_schema, LEX_STRING *new_name)
{
int res;
Event_timed *et_old= NULL, *et_new= NULL;
Event_queue_element *element_old= NULL,
*element_new;
DBUG_ENTER("Event_queue::update_event");
DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p",
thd, et, et->dbname.str, et->name.str, &LOCK_event_queue));
DBUG_PRINT("enter", ("thd=0x%lx et=[%s.%s]", thd, dbname.str, name.str));
res= db_repository->
load_named_event_timed(thd, new_schema?*new_schema:et->dbname,
new_name? *new_name:et->name, &et_new);
element_new= new Event_queue_element();
if (res && res != OP_DISABLED_EVENT)
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 (!(et_old= find_event(et->dbname, et->name, TRUE)))
if (!(element_old= find_event(dbname, name, TRUE)))
{
DBUG_PRINT("info", ("%s.%s not found cached, probably was DISABLED",
et->dbname.str, et->name.str));
DBUG_PRINT("info", ("%s.%s not cached, probably was DISABLED",
dbname.str, name.str));
}
if (!res)
/* If not disabled event */
if (element_new)
{
DBUG_PRINT("info", ("new event in the queue %p old %p", et_new, et_old));
queue_insert_safe(&queue, (byte *) et_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);
}
else if (res == OP_DISABLED_EVENT)
res= OP_OK;
UNLOCK_QUEUE_DATA();
notify_observers();
if (et_old)
delete et_old;
if (element_old)
delete element_old;
end:
DBUG_PRINT("info", ("res=%d", res));
DBUG_RETURN(res);
......@@ -181,35 +312,34 @@ end:
SYNOPSIS
Event_queue::drop_event()
thd Thread
name The event to drop
RETURN VALUE
FALSE OK (replaced or scheduler not working)
TRUE Failure
thd Thread
dbname Schema of the event to drop
name Name of the event to drop
*/
bool
Event_queue::drop_event(THD *thd, sp_name *name)
void
Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
{
int res;
Event_timed *et_old;
Event_queue_element *element;
DBUG_ENTER("Event_queue::drop_event");
DBUG_PRINT("enter", ("thd=%p name=%p lock=%p", thd, name,
&LOCK_event_queue));
DBUG_PRINT("enter", ("thd=0x%lx name=0x%lx", thd, name));
LOCK_QUEUE_DATA();
if (!(et_old= find_event(name->m_db, name->m_name, TRUE)))
DBUG_PRINT("info", ("No such event found, probably DISABLED"));
element= find_event(dbname, name, TRUE);
UNLOCK_QUEUE_DATA();
if (et_old)
delete et_old;
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_RETURN(FALSE);
DBUG_VOID_RETURN;
}
......@@ -232,7 +362,7 @@ Event_queue::drop_event(THD *thd, sp_name *name)
(signalling COND_new_work for instance).
*/
Event_timed *
Event_queue_element *
Event_queue::find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q)
{
uint i;
......@@ -240,10 +370,10 @@ Event_queue::find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q)
for (i= 0; i < queue.elements; ++i)
{
Event_timed *et= (Event_timed *) queue_element(&queue, 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_timed_identifier_equal(db, name, et))
if (event_basic_identifier_equal(db, name, et))
{
if (remove_from_q)
queue_remove(&queue, i);
......@@ -274,7 +404,7 @@ Event_queue::find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q)
void
Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*comparator)(Event_timed *,LEX_STRING *))
bool (*comparator)(LEX_STRING *, Event_basic *))
{
DBUG_ENTER("Event_queue::drop_matching_events");
DBUG_PRINT("enter", ("pattern=%*s state=%d", pattern.length, pattern.str));
......@@ -282,9 +412,9 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
uint i= 0;
while (i < queue.elements)
{
Event_timed *et= (Event_timed *) queue_element(&queue, i);
Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
if (comparator(et, &pattern))
if (comparator(&pattern, et))
{
/*
The queue is ordered. If we remove an element, then all elements after
......@@ -331,59 +461,12 @@ 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_timed_db_equal);
drop_matching_events(thd, schema, event_basic_db_equal);
UNLOCK_QUEUE_DATA();
DBUG_VOID_RETURN;
}
/*
Wrapper for pthread_mutex_lock
SYNOPSIS
Event_queue::lock_data()
mutex Mutex to lock
line The line number on which the lock is done
RETURN VALUE
Error code of pthread_mutex_lock()
*/
void
Event_queue::lock_data(const char *func, uint line)
{
DBUG_ENTER("Event_queue::lock_mutex");
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;
}
/*
Wrapper for pthread_mutex_unlock
SYNOPSIS
Event_queue::unlock_data()
mutex Mutex to unlock
line The line number on which the unlock is done
*/
void
Event_queue::unlock_data(const char *func, uint line)
{
DBUG_ENTER("Event_queue::unlock_mutex");
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;
}
/*
Returns the number of elements in the queue
......@@ -391,7 +474,7 @@ Event_queue::unlock_data(const char *func, uint line)
Event_queue::events_count()
RETURN VALUE
0 Number of Event_timed objects in the queue
Number of Event_queue_element objects in the queue
*/
uint
......@@ -407,32 +490,10 @@ Event_queue::events_count()
}
/*
Returns the number of elements in the queue
SYNOPSIS
Event_queue::events_count_no_lock()
RETURN VALUE
0 Number of Event_timed objects in the queue
*/
uint
Event_queue::events_count_no_lock()
{
uint n;
DBUG_ENTER("Event_queue::events_count_no_lock");
n= queue.elements;
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_timed instances for every ENABLED event
the events. Creates Event_queue_element instances for every ENABLED event
from mysql.event.
SYNOPSIS
......@@ -461,7 +522,7 @@ Event_queue::load_events_from_db(THD *thd)
MEM_ROOT boot_root;
DBUG_ENTER("Event_queue::load_events_from_db");
DBUG_PRINT("enter", ("thd=%p", thd));
DBUG_PRINT("enter", ("thd=0x%lx", thd));
if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
{
......@@ -469,12 +530,11 @@ Event_queue::load_events_from_db(THD *thd)
DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
}
init_alloc_root(&boot_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
init_read_record(&read_record_info, thd, table ,NULL,1,0);
while (!(read_record_info.read_record(&read_record_info)))
{
Event_timed *et;
if (!(et= new Event_timed))
Event_queue_element *et;
if (!(et= new Event_queue_element))
{
DBUG_PRINT("info", ("Out of memory"));
clean_the_queue= TRUE;
......@@ -489,13 +549,14 @@ Event_queue::load_events_from_db(THD *thd)
"Table probably corrupted");
break;
}
if (et->status != Event_timed::ENABLED)
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 */
......@@ -514,6 +575,7 @@ Event_queue::load_events_from_db(THD *thd)
et->free_sp();
break;
}
free_root(&boot_root, MYF(0));
/* let's find when to be executed */
if (et->compute_next_execution_time())
......@@ -522,19 +584,17 @@ Event_queue::load_events_from_db(THD *thd)
" Skipping", et->dbname.str, et->name.str);
continue;
}
DBUG_PRINT("load_events_from_db", ("Adding %p to the exec list."));
#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);
free_root(&boot_root, MYF(0));
if (clean_the_queue)
{
for (count= 0; count < queue.elements; ++count)
queue_remove(&queue, 0);
empty_queue();
ret= -1;
}
else
......@@ -571,7 +631,7 @@ Event_queue::check_system_tables(THD *thd)
bool ret;
DBUG_ENTER("Event_queue::check_system_tables");
DBUG_PRINT("enter", ("thd=%p", thd));
DBUG_PRINT("enter", ("thd=0x%lx", thd));
thd->reset_n_backup_open_tables_state(&backup);
......@@ -618,123 +678,48 @@ Event_queue::check_system_tables(THD *thd)
/*
Inits mutexes.
SYNOPSIS
Event_queue::init_mutexes()
*/
void
Event_queue::init_mutexes()
{
pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
}
/*
Destroys mutexes.
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::deinit_mutexes()
Event_queue::recalculate_activation_times()
thd Thread
*/
void
Event_queue::deinit_mutexes()
Event_queue::recalculate_activation_times(THD *thd)
{
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;
}
/*
The implementation of full-fledged initialization.
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=%p", this));
uint i;
DBUG_ENTER("Event_queue::recalculate_activation_times");
LOCK_QUEUE_DATA();
db_repository= db_repo;
scheduler= sched;
if (init_queue_ex(&queue, 30 /*num_el*/, 0 /*offset*/, 0 /*smallest_on_top*/,
event_timed_compare_q, NULL, 30 /*auto_extent*/))
{
sql_print_error("SCHEDULER: Can't initialize the execution queue");
ret= TRUE;
goto end;
}
if (sizeof(my_time_t) != sizeof(time_t))
DBUG_PRINT("info", ("%u loaded events to be recalculated", queue.elements));
for (i= 0; i < queue.elements; i++)
{
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;
((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
}
end:
UNLOCK_QUEUE_DATA();
DBUG_RETURN(ret);
}
void
Event_queue::deinit_queue()
{
DBUG_ENTER("Event_queue::deinit_queue");
LOCK_QUEUE_DATA();
empty_queue();
delete_queue(&queue);
queue_fix(&queue);
UNLOCK_QUEUE_DATA();
DBUG_VOID_RETURN;
}
void
Event_queue::recalculate_queue(THD *thd)
{
uint i;
for (i= 0; i < queue.elements; i++)
{
((Event_timed*)queue_element(&queue, i))->compute_next_execution_time();
((Event_timed*)queue_element(&queue, i))->update_fields(thd);
}
queue_fix(&queue);
}
/*
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()
......@@ -743,9 +728,9 @@ Event_queue::empty_queue()
DBUG_ENTER("Event_queue::empty_queue");
DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements));
/* empty the queue */
for (i= 0; i < events_count_no_lock(); ++i)
for (i= 0; i < queue.elements; ++i)
{
Event_timed *et= (Event_timed *) queue_element(&queue, i);
Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
delete et;
}
resize_queue(&queue, 0);
......@@ -753,38 +738,17 @@ Event_queue::empty_queue()
}
Event_timed*
Event_queue::get_top()
{
return (Event_timed *)queue_top(&queue);
}
void
Event_queue::remove_top()
{
queue_remove(&queue, 0);// 0 is top, internally 1
}
void
Event_queue::top_changed()
{
queue_replaced(&queue);
}
inline void
Event_queue::dbug_dump_queue(time_t now)
{
#ifndef DBUG_OFF
Event_timed *et;
Event_queue_element *et;
uint i;
DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements));
for (i = 0; i < queue.elements; i++)
{
et= ((Event_timed*)queue_element(&queue, i));
DBUG_PRINT("info",("et=%p db=%s name=%s",et, et->dbname.str, et->name.str));
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),
......@@ -797,14 +761,14 @@ Event_queue::dbug_dump_queue(time_t now)
#endif
}
Event_timed *
Event_job_data *
Event_queue::get_top_for_execution_if_time(THD *thd, time_t now,
struct timespec *abstime)
{
struct timespec top_time;
Event_timed *et_new= NULL;
Event_job_data *et_new= NULL;
DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
DBUG_PRINT("enter", ("thd=%p now=%d", thd, now));
DBUG_PRINT("enter", ("thd=0x%lx now=%d", thd, now));
abstime->tv_nsec= 0;
LOCK_QUEUE_DATA();
do {
......@@ -816,28 +780,31 @@ Event_queue::get_top_for_execution_if_time(THD *thd, time_t now,
}
dbug_dump_queue(now);
Event_timed *et= ((Event_timed*)queue_element(&queue, 0));
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;
if ((res= db_repository->load_named_event_timed(thd, et->dbname, et->name,
&et_new)))
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_timed::DISABLED;
et->status= Event_queue_element::DISABLED;
DBUG_PRINT("info", ("event's status is %d", et->status));
et->update_fields(thd);
et->update_timing_fields(thd);
if (((et->execute_at.year && !et->expression) || et->execute_at_null) ||
(et->status == Event_timed::DISABLED))
(et->status == Event_queue_element::DISABLED))
{
DBUG_PRINT("info", ("removing from the queue"));
if (et->dropped)
......@@ -857,12 +824,122 @@ Event_queue::get_top_for_execution_if_time(THD *thd, time_t now,
} while (0);
UNLOCK_QUEUE_DATA();
DBUG_PRINT("info", ("returning. et_new=%p abstime.tv_sec=%d ", et_new,
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.execute_at=%lld", et_new->dbname.str, et_new->name.str,
et_new->definer.str,
TIME_to_ulonglong_datetime(&et_new->execute_at)));
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);
}
......@@ -17,13 +17,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
class sp_name;
class Event_timed;
class Event_basic;
class Event_db_repository;
class Event_job_data;
class Event_queue_element;
class THD;
typedef bool * (*event_timed_identifier_comparator)(Event_timed*, Event_timed*);
class Event_scheduler_ng;
class Event_queue
......@@ -46,14 +45,14 @@ public:
/* Methods for queue management follow */
int
create_event(THD *thd, Event_parse_data *et);
create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
int
update_event(THD *thd, Event_parse_data *et, LEX_STRING *new_schema,
LEX_STRING *new_name);
update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
LEX_STRING *new_schema, LEX_STRING *new_name);
bool
drop_event(THD *thd, sp_name *name);
void
drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
void
drop_schema_events(THD *thd, LEX_STRING schema);
......@@ -61,32 +60,20 @@ public:
uint
events_count();
uint
events_count_no_lock();
static bool
check_system_tables(THD *thd);
void
recalculate_queue(THD *thd);
void
empty_queue();
recalculate_activation_times(THD *thd);
Event_timed *
Event_job_data *
get_top_for_execution_if_time(THD *thd, time_t now, struct timespec *abstime);
Event_timed*
get_top();
void
remove_top();
void
top_changed();
bool
dump_internal_status(THD *thd);
protected:
Event_timed *
Event_queue_element *
find_event(LEX_STRING db, LEX_STRING name, bool remove_from_q);
int
......@@ -94,14 +81,16 @@ protected:
void
drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*)(Event_timed *,LEX_STRING *));
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;
......@@ -123,10 +112,8 @@ protected:
Event_scheduler_ng *scheduler;
//public:
/* The sorted queue with the Event_timed objects */
/* The sorted queue with the Event_job_data objects */
QUEUE queue;
};
#endif /* _EVENT_QUEUE_H_ */
......@@ -30,6 +30,7 @@
#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;
......@@ -52,28 +53,6 @@ LEX_STRING scheduler_states_names[] =
};
class Worker_thread_param
{
public:
Event_timed *et;
pthread_mutex_t LOCK_started;
pthread_cond_t COND_started;
bool started;
Worker_thread_param(Event_timed *etn):et(etn), started(FALSE)
{
pthread_mutex_init(&LOCK_started, MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_started, NULL);
}
~Worker_thread_param()
{
pthread_mutex_destroy(&LOCK_started);
pthread_cond_destroy(&COND_started);
}
};
/*
Prints the stack of infos, warnings, errors from thd to
the console so it can be fetched by the logs-into-tables and
......@@ -81,12 +60,12 @@ public:
SYNOPSIS
evex_print_warnings
thd - thread used during the execution of the event
et - the event itself
thd Thread used during the execution of the event
et The event itself
*/
static void
evex_print_warnings(THD *thd, Event_timed *et)
evex_print_warnings(THD *thd, Event_job_data *et)
{
MYSQL_ERROR *err;
DBUG_ENTER("evex_print_warnings");
......@@ -148,23 +127,22 @@ init_scheduler_thread(THD* thd)
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, 0);
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;
VOID(pthread_mutex_lock(&LOCK_thread_count));
pthread_mutex_lock(&LOCK_thread_count);
thd->thread_id= thread_id++;
threads.append(thd);
thread_count++;
thread_running++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
pthread_mutex_unlock(&LOCK_thread_count);
/*
Guarantees that we will see the thread in SHOW PROCESSLIST though its
vio is NULL.
*/
thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
thd->proc_info= "Initialized";
thd->version= refresh_version;
......@@ -174,6 +152,41 @@ init_scheduler_thread(THD* thd)
}
/*
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)
{
......@@ -201,17 +214,8 @@ event_scheduler_ng_thread(void *arg)
((struct scheduler_param *) arg)->scheduler->run(thd);
end:
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);
deinit_event_thread(thd);
my_thread_end();
DBUG_RETURN(0); // Against gcc warnings
}
......@@ -222,7 +226,7 @@ end:
SYNOPSIS
event_worker_ng_thread()
arg The Event_timed object to be processed
arg The Event_job_data object to be processed
RETURN VALUE
0 OK
......@@ -233,14 +237,12 @@ event_worker_ng_thread(void *arg)
{
/* needs to be first for thread_stack */
THD *thd;
Event_timed *event= (Event_timed *)arg;
Event_job_data *event= (Event_job_data *)arg;
int ret;
thd= event->thd;
thd->thread_stack= (char *) &thd;
DBUG_ENTER("event_worker_thread");
DBUG_PRINT("enter", ("event=[%s.%s]", event->dbname.str, event->name.str));
my_thread_init();
pthread_detach_this_thread();
......@@ -251,62 +253,68 @@ event_worker_ng_thread(void *arg)
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->init_for_queries();
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. RetCode=%d",
sql_print_information("SCHEDULER: [%s.%s of %s] executed "
" in thread thread %lu. RetCode=%d",
event->dbname.str, event->name.str,
event->definer.str, ret);
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");
DBUG_PRINT("info", ("master_access=%d db_access=%d",
thd->security_ctx->master_access, thd->security_ctx->db_access));
end:
thd->proc_info= "Clearing";
DBUG_ASSERT(thd->net.buff != 0);
/*
Free it here because net.vio is NULL for us => THD::~THD will check it
and won't call net_end(&net); See also replication code.
*/
net_end(&thd->net);
DBUG_PRINT("info", ("Worker thread %lu exiting", thd->thread_id));
VOID(pthread_mutex_lock(&LOCK_thread_count));
thread_count--;
thread_running--;
delete thd;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str,
event->name.str));
delete event;
my_thread_end();
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;
}
......@@ -315,6 +323,13 @@ void
Event_scheduler_ng::deinit_scheduler() {}
/*
Inits scheduler's threading primitives.
SYNOPSIS
Event_scheduler_ng::init_mutexes()
*/
void
Event_scheduler_ng::init_mutexes()
{
......@@ -323,6 +338,13 @@ Event_scheduler_ng::init_mutexes()
}
/*
Deinits scheduler's threading primitives.
SYNOPSIS
Event_scheduler_ng::deinit_mutexes()
*/
void
Event_scheduler_ng::deinit_mutexes()
{
......@@ -331,6 +353,21 @@ Event_scheduler_ng::deinit_mutexes()
}
/*
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()
{
......@@ -340,6 +377,7 @@ Event_scheduler_ng::start()
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;
......@@ -349,10 +387,13 @@ Event_scheduler_ng::start()
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))
{
......@@ -360,13 +401,14 @@ Event_scheduler_ng::start()
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);
......@@ -380,14 +422,27 @@ end:
}
/*
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=%p", current_thd));
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;
......@@ -406,48 +461,75 @@ Event_scheduler_ng::stop()
"workers count=%d", scheduler_states_names[state].str,
workers_count()));
/* thd could be 0x0, when shutting down */
pthread_cond_wait(&COND_state, &LOCK_scheduler_state);
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_timed *job_data;
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=%p now=%d abs_time.tv_sec=%d",
job_data, thd->query_start(), abstime.tv_sec));
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");
pthread_cond_wait(&COND_state, &LOCK_scheduler_state);
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");
pthread_cond_timedwait(&COND_state, &LOCK_scheduler_state, &abstime);
COND_STATE_WAIT(&abstime);
/*
If we get signal we should recalculate the whether it's the right time
because there could be :
......@@ -461,12 +543,12 @@ Event_scheduler_ng::run(THD *thd)
}
else
{
int res;
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));
}
......@@ -477,29 +559,47 @@ error:
UNLOCK_SCHEDULER_DATA();
sql_print_information("SCHEDULER: Stopped");
return FALSE;
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_timed *job_data)
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;
/* Major failure */
new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
job_data->thd= new_thd;
DBUG_PRINT("info", ("Starting new thread for %s@%s",
DBUG_PRINT("info", ("BURAN %s@%s ready for start t-3..2..1..0..ignition",
job_data->dbname.str, job_data->name.str));
if (pthread_create(&th, &connection_attrib, event_worker_ng_thread, job_data))
/* 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";
......@@ -511,10 +611,21 @@ error:
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()
{
......@@ -526,13 +637,12 @@ Event_scheduler_ng::get_state()
}
int
Event_scheduler_ng::dump_internal_status(THD *thd)
{
return 1;
}
/*
Returns the number of living event worker threads.
SYNOPSIS
Event_scheduler_ng::workers_count()
*/
uint
Event_scheduler_ng::workers_count()
......@@ -541,7 +651,7 @@ Event_scheduler_ng::workers_count()
uint count= 0;
DBUG_ENTER("Event_scheduler_ng::workers_count");
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
......@@ -550,7 +660,7 @@ Event_scheduler_ng::workers_count()
if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
++count;
}
VOID(pthread_mutex_unlock(&LOCK_thread_count));
pthread_mutex_unlock(&LOCK_thread_count);
DBUG_PRINT("exit", ("%d", count));
DBUG_RETURN(count);
}
......@@ -568,16 +678,27 @@ void
Event_scheduler_ng::queue_changed()
{
DBUG_ENTER("Event_scheduler_ng::queue_changed");
DBUG_PRINT("info", ("Sending COND_state"));
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_mutex");
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;
......@@ -587,10 +708,20 @@ Event_scheduler_ng::lock_data(const char *func, uint line)
}
/*
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_mutex");
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;
......@@ -598,3 +729,147 @@ Event_scheduler_ng::unlock_data(const char *func, uint line)
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);
}
......@@ -16,8 +16,8 @@
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 Event_queue;
class Event_job_data;
class Event_scheduler_ng
{
......@@ -67,7 +67,7 @@ public:
void
queue_changed();
static int
bool
dump_internal_status(THD *thd);
private:
......@@ -76,7 +76,7 @@ private:
/* helper functions */
bool
execute_top(THD *thd, Event_timed *job_data);
execute_top(THD *thd, Event_job_data *job_data);
/* helper functions for working with mutexes & conditionals */
void
......@@ -84,7 +84,10 @@ private:
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. */
......@@ -107,6 +110,9 @@ private:
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 */
......
......@@ -291,11 +291,11 @@ Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
RETURN VALUE
0 OK
!0 Error
!0 Error (Reported)
NOTES
- in case there is an event with the same name (db) and
IF NOT EXISTS is specified, an warning is put into the W stack.
In case there is an event with the same name (db) and
IF NOT EXISTS is specified, an warning is put into the stack.
*/
int
......@@ -304,13 +304,20 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists,
{
int ret;
DBUG_ENTER("Events::create_event");
pthread_mutex_lock(&LOCK_event_metadata);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists,
rows_affected)))
{
if ((ret= event_queue->create_event(thd, parse_data)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
if ((ret= event_queue->create_event(thd, parse_data->dbname,
parse_data->name)))
{
DBUG_ASSERT(ret == OP_LOAD_ERROR);
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
}
}
/* No need to close the table, it will be closed in sql_parse::do_command */
pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
......@@ -322,8 +329,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists,
SYNOPSIS
Events::update_event()
thd THD
et event's data
new_name set in case of RENAME TO.
et Event's data from parsing stage
new_name Set in case of RENAME TO.
RETURN VALUE
0 OK
......@@ -341,18 +348,23 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
{
int ret;
DBUG_ENTER("Events::update_event");
/*
db_update_event() opens & closes the table to prevent
crash later in the code when loading and compiling the new definition.
Also on error conditions my_error() is called so no need to handle here
*/
pthread_mutex_lock(&LOCK_event_metadata);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->update_event(thd, parse_data, new_name)))
{
if ((ret= event_queue->update_event(thd, parse_data,
if ((ret= event_queue->update_event(thd,
parse_data->dbname,
parse_data->name,
new_name? &new_name->m_db: NULL,
new_name? &new_name->m_name: NULL)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
{
DBUG_ASSERT(ret == OP_LOAD_ERROR);
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
}
}
pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
......@@ -363,10 +375,15 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
SYNOPSIS
Events::drop_event()
thd THD
dbname Event's schema
name Event's name
if_exists When set and the event does not exist => warning onto
the stack
rows_affected Affected number of rows is returned heres
only_from_disk Whether to remove the event from the queue too. In case
of Event_job_data::drop() it's needed to do only disk
drop because Event_queue will handle removal from memory
queue.
RETURN VALUE
0 OK
......@@ -374,17 +391,52 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *new_name,
*/
int
Events::drop_event(THD *thd, sp_name *name, bool if_exists, uint *rows_affected)
Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
uint *rows_affected, bool only_from_disk)
{
int ret;
DBUG_ENTER("Events::drop_event");
if (!(ret= db_repository->drop_event(thd, name->m_db, name->m_name, if_exists,
pthread_mutex_lock(&LOCK_event_metadata);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists,
rows_affected)))
{
if ((ret= event_queue->drop_event(thd, name)))
my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0), ret);
if (!only_from_disk)
event_queue->drop_event(thd, dbname, name);
}
pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
/*
Drops all events from a schema
SYNOPSIS
Events::drop_schema_events()
thd Thread
db ASCIIZ schema name
RETURN VALUE
0 OK
!0 Error
*/
int
Events::drop_schema_events(THD *thd, char *db)
{
int ret= 0;
LEX_STRING db_lex= {db, strlen(db)};
DBUG_ENTER("evex_drop_db_events");
DBUG_PRINT("enter", ("dropping events from %s", db));
pthread_mutex_lock(&LOCK_event_metadata);
event_queue->drop_schema_events(thd, db_lex);
ret= db_repository->drop_schema_events(thd, db_lex);
pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
......@@ -406,14 +458,14 @@ int
Events::show_create_event(THD *thd, sp_name *spn)
{
int ret;
Event_timed *et= NULL;
Event_timed *et= new Event_timed();
Open_tables_state backup;
DBUG_ENTER("Events::show_create_event");
DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str));
thd->reset_n_backup_open_tables_state(&backup);
ret= db_repository->find_event(thd, spn->m_db, spn->m_name, &et, NULL);
ret= db_repository->find_event(thd, spn->m_db, spn->m_name, et);
thd->restore_backup_open_tables_state(&backup);
if (!ret)
......@@ -458,36 +510,43 @@ Events::show_create_event(THD *thd, sp_name *spn)
DBUG_RETURN(ret);
err:
delete et;
DBUG_RETURN(1);
DBUG_RETURN(1);
}
/*
Drops all events from a schema
Proxy for Event_db_repository::fill_schema_events.
Callback for I_S from sql_show.cc
SYNOPSIS
Events::drop_schema_events()
thd Thread
db ASCIIZ schema name
Events::fill_schema_events()
thd Thread
tables The schema table
cond Unused
RETURN VALUE
0 OK
!0 Error
0 OK
!0 Error
*/
int
Events::drop_schema_events(THD *thd, char *db)
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
int ret= 0;
LEX_STRING db_lex= {db, strlen(db)};
DBUG_ENTER("evex_drop_db_events");
DBUG_PRINT("enter", ("dropping events from %s", db));
event_queue->drop_schema_events(thd, db_lex);
ret= db_repository->drop_schema_events(thd, db_lex);
DBUG_RETURN(ret);
char *db= NULL;
DBUG_ENTER("Events::fill_schema_events");
/*
If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
be NULL. Let's do an assert anyway.
*/
if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
DBUG_ASSERT(thd->lex->select_lex.db);
if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
is_schema_db(thd->lex->select_lex.db)))
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
}
DBUG_RETURN(get_instance()->db_repository->fill_schema_events(thd, tables, db));
}
......@@ -511,7 +570,6 @@ Events::init()
int ret= 0;
Event_db_repository *db_repo;
DBUG_ENTER("Events::init");
db_repository->init_repository();
event_queue->init_queue(db_repository, scheduler_ng);
scheduler_ng->init_scheduler(event_queue);
......@@ -546,7 +604,6 @@ Events::deinit()
scheduler_ng->deinit_scheduler();
event_queue->deinit_queue();
db_repository->deinit_repository();
DBUG_VOID_RETURN;
}
......@@ -563,6 +620,8 @@ Events::deinit()
void
Events::init_mutexes()
{
pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
db_repository= new Event_db_repository;
event_queue= new Event_queue;
......@@ -585,9 +644,11 @@ Events::destroy_mutexes()
{
event_queue->deinit_mutexes();
scheduler_ng->deinit_mutexes();
delete scheduler_ng;
delete db_repository;
pthread_mutex_destroy(&LOCK_event_metadata);
}
......@@ -597,55 +658,45 @@ Events::destroy_mutexes()
SYNOPSIS
Events::dump_internal_status()
thd Thread
RETURN VALUE
0 OK
!0 Error
FALSE OK
TRUE Error
*/
int
bool
Events::dump_internal_status(THD *thd)
{
return Event_scheduler_ng::dump_internal_status(thd);
DBUG_ENTER("Events::dump_internal_status");
Protocol *protocol= thd->protocol;
List<Item> field_list;
field_list.push_back(new Item_empty_string("Name", 30));
field_list.push_back(new Item_empty_string("Value",20));
if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
if (scheduler_ng->dump_internal_status(thd) ||
event_queue->dump_internal_status(thd))
DBUG_RETURN(TRUE);
send_eof(thd);
DBUG_RETURN(FALSE);
}
/*
Proxy for Event_db_repository::fill_schema_events.
Callback for I_S from sql_show.cc
Starts execution of events by the scheduler
SYNOPSIS
Events::fill_schema_events()
thd Thread
tables The schema table
cond Unused
Events::start_execution_of_events()
RETURN VALUE
0 OK
!0 Error
FALSE OK
TRUE Error
*/
int
Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
char *db= NULL;
DBUG_ENTER("Events::fill_schema_events");
/*
If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
be NULL. Let's do an assert anyway.
*/
if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
DBUG_ASSERT(thd->lex->select_lex.db);
if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
is_schema_db(thd->lex->select_lex.db)))
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
}
DBUG_RETURN(get_instance()->db_repository->fill_schema_events(thd, tables, db));
}
bool
Events::start_execution_of_events()
{
......@@ -654,6 +705,19 @@ Events::start_execution_of_events()
}
/*
Stops execution of events by the scheduler.
Already running events will not be stopped. If the user needs
them stopped manual intervention is needed.
SYNOPSIS
Events::stop_execution_of_events()
RETURN VALUE
FALSE OK
TRUE Error
*/
bool
Events::stop_execution_of_events()
{
......@@ -661,6 +725,18 @@ Events::stop_execution_of_events()
DBUG_RETURN(scheduler_ng->stop());
}
/*
Checks whether the scheduler is running or not.
SYNOPSIS
Events::is_started()
RETURN VALUE
TRUE Yes
FALSE No
*/
bool
Events::is_started()
{
......
......@@ -20,6 +20,7 @@ class sp_name;
class Event_parse_data;
class Event_db_repository;
class Event_queue;
class Event_queue_element;
class Event_scheduler_ng;
/* Return codes */
......@@ -41,6 +42,7 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
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
......@@ -83,7 +85,8 @@ public:
uint *rows_affected);
int
drop_event(THD *thd, sp_name *name, bool if_exists, uint *rows_affected);
drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
uint *rows_affected, bool only_from_disk);
int
drop_schema_events(THD *thd, char *db);
......@@ -102,13 +105,9 @@ public:
static int
fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
int
bool
dump_internal_status(THD *thd);
Event_queue *event_queue;
Event_scheduler_ng *scheduler_ng;
Event_db_repository *db_repository;
private:
/* Singleton DP is used */
Events(){}
......@@ -117,6 +116,12 @@ private:
/* 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 &);
......
......@@ -5829,7 +5829,7 @@ 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."
ER_PARTITION_MERGE_ERROR
......
......@@ -3886,8 +3886,12 @@ end_with_restore_list:
else
{
uint affected= 1;
if (!(res= Events::get_instance()->
drop_event(thd, lex->spname, lex->drop_if_exists, &affected)))
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment