mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 21:12:26 +01:00
fa1d637e89
when there are no up-to-date system tables to support it: - initialize the scheduler before reporting "Ready for connections". This ensures that warnings, if any, are printed before "Ready for connections", and this message is not mangled. - do not abort the scheduler if there are no system tables - check the tables once at start up, remember the status and disable the scheduler if the tables are not up to date. If one attempts to use the scheduler with bad tables, issue an error message. - clean up the behaviour of the module under LOCK TABLES and pre-locking mode - make sure implicit commit of Events DDL works as expected. - add more tests Collateral clean ups in the events code. This patch fixes Bug#23631 Events: SHOW VARIABLES doesn't work when mysql.event is damaged mysql-test/r/events.result: Update results. mysql-test/r/events_bugs.result: Update results. mysql-test/r/events_restart_phase1.result: Update results. mysql-test/r/events_restart_phase2.result: Update results. mysql-test/r/events_restart_phase3.result: Update results. mysql-test/r/events_scheduling.result: Update results. mysql-test/r/events_time_zone.result: Update results. mysql-test/t/events.test: Add new tests for tampering with mysql.event and some more tests for sub-statements, LOCK TABLES mode and pre-locking. mysql-test/t/events_bugs.test: Move the non-concurrent part of test for Bug 16420 to this file. mysql-test/t/events_restart_phase1.test: Rewrite events_restart_* tests to take into account that now we check mysql.event table only once, at server startup. mysql-test/t/events_restart_phase2.test: Rewrite events_restart_* tests to take into account that now we check mysql.event table only once, at server startup. mysql-test/t/events_restart_phase3.test: Rewrite events_restart_* tests to take into account that now we check mysql.event table only once, at server startup. mysql-test/t/events_scheduling.test: Add more coverage for event_scheduler global variable. mysql-test/t/events_time_zone.test: Move the non-concurrent part of the tests for Bug 16420 to events_bugs.test sql/event_data_objects.cc: Move update_timing_fields functionality to Event_db_repository. Make loading of events from a table record more robust to tampering with the table - now we do not check mysql.event on every table open. sql/event_data_objects.h: Cleanup. sql/event_db_repository.cc: Now Event_db_repository is responsible for table I/O only. All the logic of events DDL is handled outside, in Events class please refer to the added test coverage to see how this change affected the behavior of Event Scheduler. Dependency on sp_head.h and sp.h removed. Make this module robust to tweaks with mysql.event table. Move check_system_tables from events.cc to this file sql/event_db_repository.h: Cleanup declarations (remove unused ones, change return type to bool from int). sql/event_queue.cc: Update to adapt to the new start up scheme of the Event Scheduler. sql/event_queue.h: Cleanup declarations. sql/event_scheduler.cc: Make all the error messages uniform: [SEVERITY] Event Scheduler: [user][schema.event] message Using append_identifier for error logging was an overkill - we may need it only if the system character set may have NUL (null character) as part of a valid identifier, this is currently never the case, whereas additional quoting did not look nice in the log. sql/event_scheduler.h: Cleanup the headers. sql/events.cc: Use a different start up procedure of Event Scheduler: - at start up, try to check the system tables first. If they are not up-to-date, disable the scheduler. - try to load all the active events. In case of a load error, abort start up. - do not parse an event on start up. Parsing only gives some information about event validity, but far not all. Consolidate the business logic of Events DDL in this module. Now opt_event_scheduler may change after start up and thus is protected by LOCK_event_metadata mutex. sql/events.h: Use all-static-data-members approach to implement Singleton pattern. sql/mysqld.cc: New invocation scheme of Events. Move some logic to events.cc. Initialize the scheduler before reporting "Ready for connections". sql/set_var.cc: Clean up sys_var_thd_sql_mode::symbolic_mode_representation to work with a LEX_STRING. Move more logic related to @@events_scheduler global variable to Events module. sql/set_var.h: Update declarations. sql/share/errmsg.txt: If someone tampered with mysql.event table after the server has started we no longer give him/her a complete report what was actually broken. Do not send the user to look at the error log in such case, as there is nothing there (check_table_intact is not executed). sql/sp_head.cc: Update to a new declaration of sys_var_thd_sql_mode::symbolic_mode_representation sql/sql_db.cc: New invocation scheme of Events module. sql/sql_parse.cc: Move more logic to Events module. Make sure that we are consistent in the way access rights are checked for Events DDL: always after committing the current transaction and checking the system tables. sql/sql_show.cc: Update to the new declarations of sys_var_thd_sql_mode::symbolic_mode_representation sql/sql_test.cc: New invocation scheme of events. sql/table.cc: mysql.event is a system table. Update check_table_intact to be concurrent, more verbose, and less smart. sql/table.h: Add a helper method. mysql-test/r/events_trans.result: New BitKeeper file ``mysql-test/r/events_trans.result'' mysql-test/t/events_trans.test: New BitKeeper file ``mysql-test/t/events_trans.test'': test cases for Event Scheduler that require a transactional storage engine.
294 lines
9 KiB
Text
294 lines
9 KiB
Text
# This test case is sensitive to execution timing. You may control
|
|
# this sensitivity by the parameter below. Small values will result
|
|
# in fast but more unstable execution, large values will improve
|
|
# stability at the cost of speed. Basically, N is a number of seconds
|
|
# to wait for operation to complete. Should be positive. Test runs
|
|
# about 25*N seconds (it sleeps most of the time, so CPU speed is not
|
|
# relevant).
|
|
let $N = 5;
|
|
|
|
--source include/big_test.inc
|
|
|
|
|
|
--disable_warnings
|
|
DROP DATABASE IF EXISTS mysqltest_db1;
|
|
--enable_warnings
|
|
|
|
CREATE DATABASE mysqltest_db1;
|
|
|
|
let $old_db= `SELECT DATABASE()`;
|
|
USE mysqltest_db1;
|
|
|
|
SET GLOBAL EVENT_SCHEDULER= OFF;
|
|
SET @save_time_zone= @@TIME_ZONE;
|
|
|
|
|
|
#
|
|
# BUG#16420: Events: timestamps become UTC
|
|
# BUG#26429: SHOW CREATE EVENT is incorrect for an event that
|
|
# STARTS NOW()
|
|
# BUG#26431: Impossible to re-create an event from backup if its
|
|
# STARTS clause is in the past
|
|
# WL#3698: Events: execution in local time zone
|
|
#
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# Create rounding function.
|
|
|
|
# Disable query log to hide actual value of $N.
|
|
--disable_query_log
|
|
eval SET @step= $N;
|
|
--enable_query_log
|
|
|
|
# Since we are working in a separate database, we may use any names we
|
|
# like.
|
|
CREATE TABLE t_step (step INT);
|
|
INSERT INTO t_step VALUES (@step);
|
|
|
|
# We can't use @variables in function, because it will be called from
|
|
# the event thread, and 'eval' doesn't work for multi-statements, so
|
|
# we can't interpolate $variables either, hence we fetch the step
|
|
# value from the table.
|
|
delimiter //;
|
|
CREATE FUNCTION round_to_step(i INT, n INT) RETURNS INT
|
|
BEGIN
|
|
DECLARE step INT;
|
|
|
|
SELECT * INTO step FROM t_step;
|
|
|
|
# We add 0.1 as a protection from inexact division.
|
|
RETURN FLOOR((i % (step * n) + 0.1) / step);
|
|
END//
|
|
delimiter ;//
|
|
|
|
|
|
# Test time computations wrt Daylight Saving Time shifts. We also
|
|
# test here that the event operates in its time zone (see what NOW()
|
|
# returns).
|
|
#
|
|
|
|
# Create a fake time zone with time transitions every 3*$N second.
|
|
|
|
SET @step3= @step * 3;
|
|
SET @step6= @step * 6;
|
|
|
|
# Disable query log to hide current time.
|
|
--disable_query_log
|
|
SET @unix_time= UNIX_TIMESTAMP() - 1;
|
|
--enable_query_log
|
|
|
|
SET @unix_time= @unix_time - @unix_time % @step6;
|
|
|
|
INSERT INTO mysql.time_zone VALUES (NULL, 'N');
|
|
SET @tzid= LAST_INSERT_ID();
|
|
INSERT INTO mysql.time_zone_transition_type
|
|
VALUES (@tzid, 0, 0, 0, 'b16420_0');
|
|
INSERT INTO mysql.time_zone_transition_type
|
|
VALUES (@tzid, 1, @step3 - @step, 1, 'b16420_1');
|
|
|
|
let $transition_unix_time= `SELECT @unix_time`;
|
|
let $count= 30;
|
|
--disable_query_log
|
|
while ($count)
|
|
{
|
|
eval INSERT INTO mysql.time_zone_transition
|
|
VALUES (@tzid, $transition_unix_time,
|
|
$transition_unix_time % @step6 = 0);
|
|
let $transition_unix_time= `SELECT $transition_unix_time + @step3`;
|
|
dec $count;
|
|
}
|
|
--enable_query_log
|
|
INSERT INTO mysql.time_zone_name VALUES ('bug16420', @tzid);
|
|
|
|
CREATE TABLE t1 (count INT, unix_time INT, local_time INT, comment CHAR(80));
|
|
CREATE TABLE t2 (count INT);
|
|
INSERT INTO t2 VALUES (1);
|
|
|
|
delimiter //;
|
|
CREATE FUNCTION f1(comment CHAR(80)) RETURNS INT
|
|
BEGIN
|
|
DECLARE orig_tz CHAR(64);
|
|
DECLARE unix_time INT;
|
|
DECLARE local_now DATETIME;
|
|
DECLARE utc_now DATETIME;
|
|
DECLARE local_time INT;
|
|
|
|
SET unix_time= UNIX_TIMESTAMP();
|
|
SET local_now= FROM_UNIXTIME(unix_time);
|
|
SET orig_tz= @@TIME_ZONE;
|
|
SET TIME_ZONE = '+00:00';
|
|
SET utc_now= FROM_UNIXTIME(unix_time);
|
|
SET TIME_ZONE= orig_tz;
|
|
SET local_time = unix_time + TIMESTAMPDIFF(SECOND, utc_now, local_now);
|
|
|
|
SET unix_time= round_to_step(unix_time, 6);
|
|
SET local_time= round_to_step(local_time, 6);
|
|
|
|
INSERT INTO t1 VALUES ((SELECT count FROM t2),
|
|
unix_time, local_time, comment);
|
|
RETURN 0;
|
|
END//
|
|
delimiter ;//
|
|
|
|
SET TIME_ZONE= '+00:00';
|
|
CREATE EVENT e1 ON SCHEDULE EVERY @step SECOND
|
|
STARTS FROM_UNIXTIME(@unix_time) DO SELECT f1("<e1>");
|
|
|
|
SET TIME_ZONE= 'bug16420';
|
|
CREATE EVENT e2 ON SCHEDULE EVERY @step SECOND
|
|
STARTS FROM_UNIXTIME(@unix_time) DO SELECT f1("<e2>");
|
|
|
|
# We want to start at the beginning of the DST cycle, so we wait
|
|
# untill current time divides by @step6.
|
|
let $wait_timeout= `SELECT @step6 + 1`;
|
|
let $wait_condition= SELECT UNIX_TIMESTAMP() % @step6 = @step6 - 1;
|
|
--source include/wait_condition.inc
|
|
# The second wait is needed because after the first wait we may end up
|
|
# on the ending edge of a second. Second wait will bring us to the
|
|
# beginning edge.
|
|
let $wait_timeout= `SELECT @step + 1`;
|
|
let $wait_condition= SELECT UNIX_TIMESTAMP() % @step6 = 0;
|
|
--source include/wait_condition.inc
|
|
|
|
# Note that after the scheduler is enabled, the event will be
|
|
# scheduled only for the next second.
|
|
SET GLOBAL EVENT_SCHEDULER= ON;
|
|
|
|
# We want to run after the events are executed.
|
|
SELECT SLEEP(@step / 2);
|
|
|
|
let $count= 7;
|
|
--disable_query_log
|
|
--disable_result_log
|
|
while ($count)
|
|
{
|
|
SELECT SLEEP(@step);
|
|
|
|
eval SELECT CASE $count
|
|
WHEN 5 THEN f1(CONCAT("Second pass after backward -2 step shift,",
|
|
" e2 should not be executed"))
|
|
WHEN 4 THEN f1(CONCAT("Second pass after backward -2 step shift,",
|
|
" e2 should not be executed"))
|
|
WHEN 2 THEN f1(CONCAT("Forward +2 step shift, local 0, 1 are skipped,",
|
|
" e2 should be executed"))
|
|
ELSE f1("e2 should be executed")
|
|
END;
|
|
UPDATE t2 SET count= count + 1;
|
|
|
|
dec $count;
|
|
}
|
|
--enable_result_log
|
|
--enable_query_log
|
|
|
|
SET GLOBAL EVENT_SCHEDULER= OFF;
|
|
|
|
SELECT * FROM t1 ORDER BY count, comment;
|
|
|
|
SET TIME_ZONE= @save_time_zone;
|
|
|
|
DROP EVENT e2;
|
|
DROP EVENT e1;
|
|
DROP FUNCTION f1;
|
|
DROP TABLE t1, t2;
|
|
|
|
DELETE FROM mysql.time_zone_name WHERE time_zone_id = @tzid;
|
|
DELETE FROM mysql.time_zone_transition_type WHERE time_zone_id = @tzid;
|
|
DELETE FROM mysql.time_zone_transition WHERE time_zone_id = @tzid;
|
|
DELETE FROM mysql.time_zone WHERE time_zone_id = @tzid;
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# Test MONTH interval.
|
|
#
|
|
|
|
SET TIME_ZONE= '+00:00';
|
|
|
|
CREATE TABLE t1 (event CHAR(2), dt DATE, offset INT);
|
|
|
|
INSERT INTO mysql.time_zone VALUES (NULL, 'N');
|
|
SET @tzid= LAST_INSERT_ID();
|
|
|
|
SET @now= UNIX_TIMESTAMP();
|
|
SET @offset_month_01= UNIX_TIMESTAMP('2030-01-31 12:00:00') - @now;
|
|
SET @offset_month_02= UNIX_TIMESTAMP('2030-02-28 12:00:00') - @now - 5*@step;
|
|
SET @offset_month_03= UNIX_TIMESTAMP('2030-03-31 12:00:00') - @now - 5*@step;
|
|
SET @offset_month_04= UNIX_TIMESTAMP('2030-04-30 12:00:00') - @now - 13*@step;
|
|
|
|
INSERT INTO mysql.time_zone_transition_type
|
|
VALUES (@tzid, 0, @offset_month_01, 0, 'b16420_0');
|
|
INSERT INTO mysql.time_zone_transition_type
|
|
VALUES (@tzid, 1, @offset_month_02, 1, 'b16420_1');
|
|
INSERT INTO mysql.time_zone_transition_type
|
|
VALUES (@tzid, 2, @offset_month_03, 1, 'b16420_2');
|
|
INSERT INTO mysql.time_zone_transition_type
|
|
VALUES (@tzid, 3, @offset_month_04, 1, 'b16420_3');
|
|
INSERT INTO mysql.time_zone_transition
|
|
VALUES (@tzid, @now, 0);
|
|
INSERT INTO mysql.time_zone_transition
|
|
VALUES (@tzid, @now + 3 * @step, 1);
|
|
INSERT INTO mysql.time_zone_transition
|
|
VALUES (@tzid, @now + 7 * @step, 2);
|
|
INSERT INTO mysql.time_zone_transition
|
|
VALUES (@tzid, @now + 12 * @step, 3);
|
|
# We have to user a new time zone name, because 'bug16420' has been
|
|
# cached already.
|
|
INSERT INTO mysql.time_zone_name VALUES ('bug16420_2', @tzid);
|
|
|
|
SET TIME_ZONE= 'bug16420_2';
|
|
|
|
SET GLOBAL EVENT_SCHEDULER= ON;
|
|
|
|
let $now= `SELECT @now`;
|
|
--disable_query_log
|
|
eval CREATE EVENT e1 ON SCHEDULE EVERY 1 MONTH
|
|
STARTS FROM_UNIXTIME($now - @step) DO
|
|
INSERT INTO t1 VALUES
|
|
("e1", NOW(), round_to_step(UNIX_TIMESTAMP() - $now, 4) - 1);
|
|
eval CREATE EVENT e2 ON SCHEDULE EVERY 1 MONTH
|
|
STARTS FROM_UNIXTIME($now + @step) DO
|
|
INSERT INTO t1 VALUES
|
|
("e2", NOW(), round_to_step(UNIX_TIMESTAMP() - $now, 4) - 1);
|
|
--enable_query_log
|
|
|
|
let $wait_timeout= `SELECT 16 * @step`;
|
|
let $wait_condition= SELECT COUNT(*) = 7 FROM t1;
|
|
--source include/wait_condition.inc
|
|
|
|
SET GLOBAL EVENT_SCHEDULER= OFF;
|
|
|
|
--echo Below we should see the following:
|
|
--echo - On Jan 31 only e2 is executed, because we started later than
|
|
--echo e1 should have been executed. Offset of e2 is 0 because of
|
|
--echo the late start, not 1.
|
|
--echo - The next execution is on Feb 28 (last day of Feb). Both events
|
|
--echo are executed in their times, offsets are -1 and 1.
|
|
--echo - The next time is Mar 31. Because the time of event
|
|
--echo execution was skipped over, events are executed right away,
|
|
--echo offsets are 2 and 2.
|
|
--echo - The next time is Apr 30. Events are again executed in their
|
|
--echo appointed times, offsets are -1 and 1.
|
|
SELECT * FROM t1 ORDER BY dt, event;
|
|
|
|
DROP EVENT e2;
|
|
DROP EVENT e1;
|
|
DROP TABLE t1;
|
|
|
|
SET TIME_ZONE= @save_time_zone;
|
|
|
|
DELETE FROM mysql.time_zone_name WHERE time_zone_id = @tzid;
|
|
DELETE FROM mysql.time_zone_transition_type WHERE time_zone_id = @tzid;
|
|
DELETE FROM mysql.time_zone_transition WHERE time_zone_id = @tzid;
|
|
DELETE FROM mysql.time_zone WHERE time_zone_id = @tzid;
|
|
|
|
DROP FUNCTION round_to_step;
|
|
DROP TABLE t_step;
|
|
|
|
|
|
DROP DATABASE mysqltest_db1;
|
|
--disable_query_log
|
|
eval USE $old_db;
|
|
--enable_query_log
|
|
|
|
|
|
--echo End of 5.1 tests.
|