From 772eddf08111dbc5a365fabe3433060bb1fac595 Mon Sep 17 00:00:00 2001 From: unknown <andrey@lmy004.> Date: Mon, 10 Jul 2006 13:44:43 +0200 Subject: [PATCH] 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 --- mysql-test/r/events.result | 9 +- mysql-test/r/events_bugs.result | 48 +- mysql-test/r/events_logs_tests.result | 10 +- mysql-test/r/events_scheduling.result | 2 +- mysql-test/t/events.test | 16 +- mysql-test/t/events_bugs.test | 47 +- mysql-test/t/events_logs_tests.test | 15 +- mysql-test/t/events_scheduling.test | 2 +- mysql-test/t/events_stress.test | 4 +- sql/event_data_objects.cc | 766 ++++++++++++++++++-------- sql/event_data_objects.h | 202 ++++--- sql/event_db_repository.cc | 525 ++++++------------ sql/event_db_repository.h | 29 +- sql/event_queue.cc | 653 ++++++++++++---------- sql/event_queue.h | 49 +- sql/event_scheduler_ng.cc | 455 ++++++++++++--- sql/event_scheduler_ng.h | 14 +- sql/events.cc | 228 +++++--- sql/events.h | 17 +- sql/share/errmsg.txt | 2 +- sql/sql_parse.cc | 8 +- 21 files changed, 1852 insertions(+), 1249 deletions(-) diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index a6d385e17ef..2128624786d 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -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:" diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index 416312438b4..e6115161cbb 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -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 diff --git a/mysql-test/r/events_logs_tests.result b/mysql-test/r/events_logs_tests.result index 9202d63fd2c..885efb404f4 100644 --- a/mysql-test/r/events_logs_tests.result +++ b/mysql-test/r/events_logs_tests.result @@ -9,7 +9,7 @@ SELECT user_host, argument FROM mysql.general_log WHERE argument LIKE '%alabala% END| "Check General Query Log" SET GLOBAL event_scheduler=2; -create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual; +create event log_general on schedule every 1 minute do SELect 'alabala', sleep(1) from dual; TRUNCATE mysql.general_log; "1 row, the current statement!" call select_general_log(); @@ -19,7 +19,7 @@ SET GLOBAL event_scheduler=1; "Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log" call select_general_log(); user_host argument -USER_HOST SELect 'alabala', sleep(3) from dual +USER_HOST SELect 'alabala', sleep(1) from dual DROP PROCEDURE select_general_log; DROP EVENT log_general; SET GLOBAL event_scheduler=2; @@ -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" diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result index eb44751c176..d7b7241db51 100644 --- a/mysql-test/r/events_scheduling.result +++ b/mysql-test/r/events_scheduling.result @@ -39,7 +39,7 @@ DROP EVENT start_n_end; DROP EVENT only_one_time; ERROR HY000: Unknown event 'only_one_time' "Should be preserved" -SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS; +SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_NAME; EVENT_NAME STATUS E19170 ENABLED two_time DISABLED diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index 5dff48c203b..34c00a6387a 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -18,7 +18,7 @@ CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; connection default; SHOW DATABASES LIKE 'db_x'; SET GLOBAL event_scheduler=1; ---sleep 1.5 +--sleep 0.8 SHOW DATABASES LIKE 'db_x'; SHOW TABLES FROM db_x; SET GLOBAL event_scheduler=2; @@ -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; diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index 4ff114555ca..7fd4c684b4b 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -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; diff --git a/mysql-test/t/events_logs_tests.test b/mysql-test/t/events_logs_tests.test index 5f4ec852cd3..0cf073f2280 100644 --- a/mysql-test/t/events_logs_tests.test +++ b/mysql-test/t/events_logs_tests.test @@ -14,7 +14,7 @@ END| delimiter ;| --echo "Check General Query Log" SET GLOBAL event_scheduler=2; -create event log_general on schedule every 1 minute do SELect 'alabala', sleep(3) from dual; +create event log_general on schedule every 1 minute do SELect 'alabala', sleep(1) from dual; TRUNCATE mysql.general_log; --echo "1 row, the current statement!" --replace_column 1 USER_HOST @@ -22,13 +22,12 @@ call select_general_log(); SET GLOBAL event_scheduler=1; --echo "Wait the scheduler to start" --echo "Should see 3 rows - the 'SELect' is in the middle. The other two are selects from general_log" ---sleep 2 +--sleep 0.7 --replace_column 1 USER_HOST call select_general_log(); DROP PROCEDURE select_general_log; DROP EVENT log_general; SET GLOBAL event_scheduler=2; ---sleep 1 --echo "Check slow query log" --disable_query_log @@ -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" diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test index 987939bc162..dc4460999a7 100644 --- a/mysql-test/t/events_scheduling.test +++ b/mysql-test/t/events_scheduling.test @@ -34,7 +34,7 @@ DROP EVENT start_n_end; --error ER_EVENT_DOES_NOT_EXIST DROP EVENT only_one_time; --echo "Should be preserved" -SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS; +SELECT EVENT_NAME, STATUS FROM INFORMATION_SCHEMA.EVENTS ORDER BY EVENT_NAME; DROP EVENT two_time; DROP TABLE table_1; DROP TABLE table_2; diff --git a/mysql-test/t/events_stress.test b/mysql-test/t/events_stress.test index 7700a12efab..7b1efdea1ba 100644 --- a/mysql-test/t/events_stress.test +++ b/mysql-test/t/events_stress.test @@ -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; diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 0e984bd4c7a..7b88699273e 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -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(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(ER_WRONG_VALUE); + if ((not_used= item_execute_at->get_date(<ime, TIME_NO_ZERO_DATE))) + goto wrong_value; if (TIME_to_ulonglong_datetime(<ime) < 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(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); + if ((not_used= item_starts->get_date(<ime, 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(<ime))); if (TIME_to_ulonglong_datetime(<ime) < 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(<ime,t=TIME_to_timestamp(thd, <ime, ¬_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(<ime, TIME_NO_ZERO_DATE))) - DBUG_RETURN(EVEX_BAD_PARAMS); + if ((not_used= item_ends->get_date(<ime, 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(<ime,t=TIME_to_timestamp(thd, <ime, ¬_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, <ime) != -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(<ime_now, thd->query_start()); if (my_time_compare(<ime_now, <ime) == 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 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, 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 @@ bool get_next_time(TIME *next, TIME *start, TIME *time_now, TIME *last_exec, */ 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 @@ Event_timed::compute_next_execution_time() 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 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root) 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 @@ Event_timed::execute(THD *thd, MEM_ROOT *mem_root) /* 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 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) } -/* - 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); } - diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index a5aaf0e66fa..d405cafe42d 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -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 @@ class Event_timed 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 @@ class Event_timed 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 @@ class Event_timed 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 @@ class Event_parse_data : public Sql_alloc ON_COMPLETION_DROP = 1, ON_COMPLETION_PRESERVE }; - enum enum_on_completion on_completion; enum enum_status status; @@ -193,8 +243,8 @@ class Event_parse_data : public Sql_alloc 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 @@ class Event_parse_data : public Sql_alloc 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_ */ diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index a7daed113bb..22dcdd97a48 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -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 @@ evex_check_params(THD *thd, Event_parse_data *parse_data) 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 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, 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 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, 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); } diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index c000247cff2..d13538ce10b 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -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 @@ class Event_db_repository 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); diff --git a/sql/event_queue.cc b/sql/event_queue.cc index b42372ba6bd..bd8809ba708 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -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 @@ Event_queue::update_event(THD *thd, Event_parse_data *et, 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); +} diff --git a/sql/event_queue.h b/sql/event_queue.h index d253e3c7597..142a866e5ba 100644 --- a/sql/event_queue.h +++ b/sql/event_queue.h @@ -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 @@ class Event_queue /* 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 @@ class Event_queue 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 @@ class Event_queue 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 @@ class Event_queue 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_ */ diff --git a/sql/event_scheduler_ng.cc b/sql/event_scheduler_ng.cc index b256ef8411f..4e2820b8f65 100644 --- a/sql/event_scheduler_ng.cc +++ b/sql/event_scheduler_ng.cc @@ -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 @@ class Worker_thread_param 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 @@ event_scheduler_ng_thread(void *arg) 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 @@ Event_scheduler_ng::start() } +/* + 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 @@ Event_scheduler_ng::run(THD *thd) 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 @@ Event_scheduler_ng::execute_top(THD *thd, Event_timed *job_data) 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); +} diff --git a/sql/event_scheduler_ng.h b/sql/event_scheduler_ng.h index 41642161fc6..e4f3f0588f9 100644 --- a/sql/event_scheduler_ng.h +++ b/sql/event_scheduler_ng.h @@ -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 @@ class Event_scheduler_ng void queue_changed(); - static int + bool dump_internal_status(THD *thd); private: @@ -76,7 +76,7 @@ class Event_scheduler_ng /* 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 @@ class Event_scheduler_ng 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 @@ class Event_scheduler_ng 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 */ diff --git a/sql/events.cc b/sql/events.cc index d1bbb6be884..08e17a9578a 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -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() { diff --git a/sql/events.h b/sql/events.h index bb9fe7a8fc5..f825a75934f 100644 --- a/sql/events.h +++ b/sql/events.h @@ -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 @@ class Events 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 @@ class Events 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 @@ class Events /* Singleton instance */ static Events singleton; + Event_queue *event_queue; + Event_scheduler_ng *scheduler_ng; + Event_db_repository *db_repository; + + pthread_mutex_t LOCK_event_metadata; + /* Prevent use of these */ Events(const Events &); void operator=(Events &); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 40596d85f56..53006c8e094 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -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 diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a4987080b02..4276018d496 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3886,8 +3886,12 @@ mysql_execute_command(THD *thd) 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; -- 2.30.9