mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-30645: CREATE TRIGGER FOR { STARTUP | SHUTDOWN }
This patch add support of system triggers fired on server start up and shutdown. On start up triggers can be associated only with AFTER event time, on shutdown triggers associated with BEFORE event time. Several triggers can be created to be run on the same time/event type. Core implementation - Extended the sql grammar to support creation of system triggers on startup/shutdown - System triggers metadata is stored in the table mysql.event - Events and system triggers share the same name space since both are stored in the table mysql.event declared with the PRIMARY KEY (`db`,`name`). In result, attempt to create an event with the same name as existent trigger and vice versa results in failure with the new error ER_TRG_EVENT_CONFLICTS_NAME - Since system triggers metadata is stored in the mysql.event table, the table mysql.event is now opened on handling the DROP TRIGGER statement - In case server is started against old version of mysql.event table that missing columns required for support of system triggers, system trigger feature is disabled - System triggers are loaded from the table mysql.event right before their execution - System triggers can be created only by users with the SUPER privilege - Added the option --sys_triggers for mysqldump to dump system triggers. The option is turned off by default. In case this option is on, mysqldump first dump the table 'mysql' unconditionally, regardless whether it was listed in the option --ignore-database
This commit is contained in:
@@ -47,6 +47,8 @@
|
||||
*/
|
||||
#define FIRST_SEQUENCE_VERSION 100300
|
||||
|
||||
#define FIRST_SYS_TRIGGER_VERSION 130200
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_user.h>
|
||||
@@ -135,7 +137,8 @@ static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0, opt_no_data_m
|
||||
opt_events= 0, opt_comments_used= 0,
|
||||
opt_alltspcs=0, opt_notspcs= 0, opt_logging,
|
||||
opt_header=0, opt_update_history= 0,
|
||||
opt_drop_trigger= 0, opt_dump_history= 0, opt_wildcards= 0;
|
||||
opt_drop_trigger= 0, opt_dump_history= 0, opt_wildcards= 0,
|
||||
opt_dump_sys_triggers= 0;
|
||||
static my_bool opt_galera_info= 0;
|
||||
|
||||
#define OPT_SYSTEM_ALL 1
|
||||
@@ -612,6 +615,9 @@ static struct my_option my_long_options[] =
|
||||
{"triggers", 0, "Dump triggers for each dumped table.",
|
||||
&opt_dump_triggers, &opt_dump_triggers, 0, GET_BOOL,
|
||||
NO_ARG, 1, 0, 0, 0, 0, 0},
|
||||
{"sys-triggers", 0, "Dump system triggers.",
|
||||
&opt_dump_sys_triggers, &opt_dump_sys_triggers, 0, GET_BOOL,
|
||||
NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"tz-utc", 0,
|
||||
"Set connection time zone to UTC before commencing the dump and add "
|
||||
"SET TIME_ZONE=´+00:00´ to the top of the dump file.",
|
||||
@@ -5471,12 +5477,32 @@ static int dump_all_databases()
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
int result=0;
|
||||
char mysql_db[]= "mysql";
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres, "SHOW DATABASES"))
|
||||
return 1;
|
||||
|
||||
const bool dump_sys_triggers=
|
||||
opt_dump_sys_triggers &&
|
||||
mysql_get_server_version(mysql) >= FIRST_SYS_TRIGGER_VERSION;
|
||||
|
||||
/*
|
||||
In case it is requested to dump system triggers, first dump
|
||||
the mysql database since it contains the table mysql.event where
|
||||
triggers' metadata stored
|
||||
*/
|
||||
if (dump_sys_triggers &&
|
||||
dump_all_tables_in_db(mysql_db))
|
||||
return 1;
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
if (is_IS_or_PS(row[0]) || is_SyS(row[0]))
|
||||
if (is_IS_or_PS(row[0]) || is_SyS(row[0]) ||
|
||||
/*
|
||||
In case system triggers to be dumped, skip the `mysql` database
|
||||
since one is dumped before entering into the while loop
|
||||
*/
|
||||
(dump_sys_triggers && !cmp_database(row[0], mysql_db)))
|
||||
continue;
|
||||
|
||||
if (include_database(row[0]))
|
||||
@@ -7480,6 +7506,83 @@ free_buf_and_exit:
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
static int init_dumping_sys_triggers(char *qdatabase __attribute__((unused)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_sys_triggers()
|
||||
{
|
||||
char query_buff[QUERY_LENGTH];
|
||||
MYSQL_RES *triggers_list_rs= nullptr;
|
||||
|
||||
my_snprintf(query_buff, sizeof(query_buff),
|
||||
"SELECT db, name FROM mysql.event WHERE kind IN "
|
||||
"('STARTUP', 'SHUTDOWN')");
|
||||
if (mysql_query_with_error_report(mysql, &triggers_list_rs, query_buff) ||
|
||||
switch_character_set_results(mysql, "binary"))
|
||||
{
|
||||
mysql_free_result(triggers_list_rs);
|
||||
maybe_exit(EX_MYSQLERR);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
my_bool opt_databases_saved= opt_databases;
|
||||
opt_databases= true;
|
||||
|
||||
if (mysql_num_rows(triggers_list_rs))
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
while ((row= mysql_fetch_row(triggers_list_rs)))
|
||||
{
|
||||
MYSQL_RES *show_create_trigger_rs;
|
||||
char name_buff[NAME_LEN*4+3];
|
||||
char db_cl_name[MY_CS_COLLATION_NAME_SIZE];
|
||||
char *db_name;
|
||||
|
||||
my_snprintf(query_buff, sizeof (query_buff), "SHOW CREATE TRIGGER %s",
|
||||
quote_name(row[1], name_buff, true));
|
||||
|
||||
db_name= row[0];
|
||||
|
||||
if (init_dumping(db_name, init_dumping_sys_triggers) ||
|
||||
fetch_db_collation(db_name, db_cl_name, sizeof (db_cl_name)) ||
|
||||
mysql_query_with_error_report(mysql, &show_create_trigger_rs,
|
||||
query_buff))
|
||||
{
|
||||
mysql_free_result(triggers_list_rs);
|
||||
|
||||
(void)switch_character_set_results(mysql, default_charset);
|
||||
opt_databases= opt_databases_saved;
|
||||
|
||||
maybe_exit(EX_MYSQLERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dump_trigger(md_result_file, show_create_trigger_rs,
|
||||
db_name, db_cl_name))
|
||||
{
|
||||
mysql_free_result(triggers_list_rs);
|
||||
mysql_free_result(show_create_trigger_rs);
|
||||
|
||||
(void)switch_character_set_results(mysql, default_charset);
|
||||
opt_databases= opt_databases_saved;
|
||||
|
||||
maybe_exit(EX_MYSQLERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mysql_free_result(show_create_trigger_rs);
|
||||
}
|
||||
}
|
||||
|
||||
(void)switch_character_set_results(mysql, default_charset);
|
||||
opt_databases= opt_databases_saved;
|
||||
|
||||
mysql_free_result(triggers_list_rs);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@@ -7676,6 +7779,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_dump_sys_triggers &&
|
||||
mysql_get_server_version(mysql) >= FIRST_SYS_TRIGGER_VERSION)
|
||||
{
|
||||
DBUG_PRINT("info", ("Dumping system trigger"));
|
||||
dump_sys_triggers();
|
||||
}
|
||||
|
||||
if ((opt_system & OPT_SYSTEM_PLUGINS) && dump_all_plugins() && !first_error)
|
||||
first_error= EX_MYSQLERR;
|
||||
|
||||
|
||||
@@ -169,6 +169,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
|
||||
../sql/opt_context_store_replay.cc ../sql/opt_context_store_replay.h
|
||||
../sql/sql_path.cc
|
||||
../sql/sql_json_lib.cc ../sql/sql_json_lib.h
|
||||
../sql/sql_sys_or_ddl_trigger.cc
|
||||
../sql/event_common.cc
|
||||
${GEN_SOURCES}
|
||||
${MYSYS_LIBWRAP_SOURCE}
|
||||
)
|
||||
|
||||
@@ -2131,7 +2131,7 @@ insert into t2 values (1),(2);
|
||||
delete from t1 where i=1;
|
||||
show status like 'Opened_tables';
|
||||
Variable_name Value
|
||||
Opened_tables 6
|
||||
Opened_tables 7
|
||||
select * from t1;
|
||||
i
|
||||
2
|
||||
|
||||
577
mysql-test/main/trigger_system.result
Normal file
577
mysql-test/main/trigger_system.result
Normal file
@@ -0,0 +1,577 @@
|
||||
call mtr.add_suppression('Column count of mysql.event is wrong.');
|
||||
call mtr.add_suppression('Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.');
|
||||
call mtr.add_suppression('An error occurred when loading data from the table mysql.event. System triggers not loaded');
|
||||
call mtr.add_suppression('Aborting');
|
||||
call mtr.add_suppression('Incorrect definition of table mysql.event: expected column');
|
||||
call mtr.add_suppression('Cannot open mysql.event');
|
||||
call mtr.add_suppression('System trigger (.*)execution failing 1146.*');
|
||||
call mtr.add_suppression('Fail to load metadata for trigger (.*). Skip it.');
|
||||
call mtr.add_suppression('System trigger (.*) execution failing, failed to authenticate the user');
|
||||
#
|
||||
# MDEV-30645: CREATE TRIGGER FOR { STARTUP | SHUTDOWN }
|
||||
#
|
||||
# Test 1: Check that AFTER STARTUP trigger and BEFORE SHUTDOWN trigger
|
||||
# are fired at right time
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP INSERT INTO t1 VALUES('Startup');
|
||||
# BEFORE SHUTDOWN should be in action just after it has been created.
|
||||
# So, after restarting the server the record about server shutdown should
|
||||
# be inserted into the table t1.
|
||||
CREATE TRIGGER IF NOT EXISTS trg2 BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown');
|
||||
# restart
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
Shutdown
|
||||
Startup
|
||||
# Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
#
|
||||
# Test 2: Check that ON STARTUP/ON SHUTDOWN can't be created
|
||||
# w/o the SUPER privilege
|
||||
#
|
||||
# Check that ON STARTUP/ON SHUTDOWN can be created w/o the SUPER privilege
|
||||
CREATE USER u1;
|
||||
GRANT SELECT ON test.* TO u1;
|
||||
connect con1, localhost, u1, , test;
|
||||
# The following CREATE TRIGGER AFTER STARTUP statement should fail with
|
||||
# the error ER_SPECIFIC_ACCESS_DENIED_ERROR since the user `u1` doesn't
|
||||
# have the SUPER privilege
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP SET @a=1;
|
||||
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
|
||||
# The following CREATE TRIGGER BEFORE SHUTDOWN statement should fail with
|
||||
# the error ER_SPECIFIC_ACCESS_DENIED_ERROR since the user `u1` doesn't
|
||||
# have the SUPER privilege
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 BEFORE SHUTDOWN SET @b=1;
|
||||
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
|
||||
connection default;
|
||||
disconnect con1;
|
||||
DROP USER u1;
|
||||
#
|
||||
# Test 3: Check that a user with the explicitly granted the SUPER privilege
|
||||
# can create a system trigger on STARTUP/on SHUTDOWN
|
||||
#
|
||||
CREATE USER u1;
|
||||
GRANT SELECT ON test.* TO u1;
|
||||
GRANT SUPER ON *.* TO u1;
|
||||
connect con1, localhost, u1, , test;
|
||||
# The following CREATE TRIGGER AFTER STARTUP statement and
|
||||
# CREATE TRIGGER BEFORE SHUTDOWN statement should succeed
|
||||
# since the SUPER privilege is granted to the user `u1`
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP INSERT INTO t1 VALUES('Startup');
|
||||
CREATE TRIGGER IF NOT EXISTS trg2 BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown');
|
||||
connection default;
|
||||
disconnect con1;
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
DROP USER u1;
|
||||
#
|
||||
# Test 4: Check that it is not possible to create a System trigger
|
||||
# and a DML trigger with the same name
|
||||
#
|
||||
CREATE TABLE t1 (a INT);
|
||||
# First check that DML trigger can 't be created with the same name
|
||||
# as the existing system trigger
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
ERROR HY000: Trigger 'test.trg1' already exists
|
||||
DROP TRIGGER trg1;
|
||||
# And then check in reverse order: a system trigger can't be created
|
||||
# on presence of DML trigger with the same name
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
ERROR HY000: Trigger 'test.trg1' already exists
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Test 5: Check that multiple triggers can be created on the same
|
||||
# system event
|
||||
#
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER IF NOT EXISTS trg1_ast AFTER STARTUP INSERT INTO t1 VALUES('Startup: first action');
|
||||
CREATE TRIGGER IF NOT EXISTS trg2_ast AFTER STARTUP INSERT INTO t1 VALUES('Startup: second action');
|
||||
# BEFORE SHUTDOWN should be in action just after it has been created.
|
||||
# So, after restarting the server the record about server shutdown should
|
||||
# be inserted into the table t1.
|
||||
CREATE TRIGGER IF NOT EXISTS trg3_bshd BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown: first action');
|
||||
CREATE TRIGGER IF NOT EXISTS trg4_bshd BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown: second action');
|
||||
# restart
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
Shutdown: first action
|
||||
Shutdown: second action
|
||||
Startup: first action
|
||||
Startup: second action
|
||||
# Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TRIGGER trg1_ast;
|
||||
DROP TRIGGER trg2_ast;
|
||||
DROP TRIGGER trg3_bshd;
|
||||
DROP TRIGGER trg4_bshd;
|
||||
#
|
||||
# Test 6: SHOW TRIGGERS on a system trigger
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg1 STARTUP SET @b=1 AFTER # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# Test 7: SHOW CREATE TRIGGER on a system trigger
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
|
||||
trg1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER `trg1` AFTER STARTUP SET @b=1 latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# Test 8: Check that triggers and events have the same namespace
|
||||
#
|
||||
# Test 8.1 Check that attempt to create a trigger with the same name
|
||||
# as existent event result in error
|
||||
CREATE EVENT ev1 ON SCHEDULE EVERY 1 YEAR DO SET @aaa=1;
|
||||
Warnings:
|
||||
Warning 1105 Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.
|
||||
CREATE TRIGGER ev1 AFTER STARTUP SET @bbb=1;
|
||||
ERROR HY000: Event with the same name 'ev1' already exists
|
||||
# Clean up
|
||||
DROP EVENT ev1;
|
||||
# Test 8.2 Check that attempt to create an event with the same name as
|
||||
# as existen trigger result in error
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @bbb=1;
|
||||
CREATE EVENT trg1 ON SCHEDULE EVERY 1 YEAR DO SET @aaa=1;
|
||||
ERROR HY000: Trigger with the same name 'trg1' already exists
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# Test 9: Check that server can operate on database with
|
||||
# broken table mysql.event.
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @bbb=1;
|
||||
# Backup the mysql.event table
|
||||
CREATE TABLE event_copy LIKE mysql.event;
|
||||
INSERT INTO event_copy SELECT * FROM mysql.event;
|
||||
# Drop column 'kind' in the table mysql.event to simulate a failure on
|
||||
# loading triggers metadata from the table mysql.event
|
||||
ALTER TABLE mysql.event DROP COLUMN kind;
|
||||
# Shutdown server and restart it to see how it handle broken
|
||||
# table mysql.event
|
||||
# Restart server. Expect start up success
|
||||
# restart
|
||||
# Restore original mysql.event from backup
|
||||
DROP TABLE mysql.event;
|
||||
RENAME TABLE event_copy TO mysql.event;
|
||||
# Restart server. Should be successful.
|
||||
# restart
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# Test 10: Check that server can be started up on the data dictionary
|
||||
# having the table mysql.event from previous version of
|
||||
# server (missing mandatory columns for support of system triggers)
|
||||
#
|
||||
RENAME TABLE mysql.event TO mysql.ev_bk;
|
||||
# Copy event table's files from previous server version (12.3)
|
||||
# that doesn't contain columns required for system triggers support
|
||||
# restart
|
||||
CREATE TRIGGER ev1 AFTER STARTUP SET @bbb=1;
|
||||
ERROR HY000: The table mysql.event doesn't contain all mandatory columns required for support of system triggers. Operations with system triggers are disabled.
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
SELECT * FROM information_schema.triggers WHERE trigger_schema != 'mtr';
|
||||
TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED SQL_MODE DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION
|
||||
CREATE TABLE t1 (a INT);
|
||||
# DROP for ON DML trigger, first go through the system trigger code path,
|
||||
# and only then through code path for dml triggers
|
||||
# Expect the following statement completes with success
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
DROP TRIGGER trg1;
|
||||
# Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TABLE mysql.event;
|
||||
RENAME TABLE mysql.ev_bk TO mysql.event;
|
||||
# Restart server in order to reload a defintion of
|
||||
# the table mysql.event in memory
|
||||
# restart
|
||||
#
|
||||
# Test 11: Check that trigger time/event combinations BEFORE STARTUP and
|
||||
# AFTER SHUTDOWN are invalid
|
||||
#
|
||||
CREATE TRIGGER ev1 BEFORE STARTUP SET @a=1;
|
||||
ERROR HY000: Incorrect definition of the trigger test.ev1: action 'BEFORE' is not compatible with event 'STARTUP'
|
||||
CREATE TRIGGER ev1 AFTER SHUTDOWN SET @a=1;
|
||||
ERROR HY000: Incorrect definition of the trigger test.ev1: action 'AFTER' is not compatible with event 'SHUTDOWN'
|
||||
#
|
||||
# Test 12: Check that it is not possible to hang the server by
|
||||
# executing DROP TRIGGER inside the body of ON SHUTDOWN TRIGGER
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @a=1;
|
||||
CREATE TRIGGER trg2 BEFORE SHUTDOWN
|
||||
BEGIN
|
||||
DROP TRIGGER trg1;
|
||||
END;
|
||||
$
|
||||
ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger
|
||||
# restart
|
||||
# Expected only the trigger trg2 in output
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg1 STARTUP SET @a=1 AFTER # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# Test 13: Check that 'IF NOT EXISTS' clause handled correctly
|
||||
# on presence of ON DML trigger with the same name
|
||||
#
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
ERROR HY000: Trigger 'test.trg1' already exists
|
||||
# On presence IF NOT EXISTS the CREATE TRIGGER statememt
|
||||
# completes with success
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP SET @b=1;
|
||||
Warnings:
|
||||
Note 1359 Trigger 'trg1' already exists
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg1 INSERT t1 SET @a=1 BEFORE # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Test 14: Check that OR REPLACE clause allows to create
|
||||
# a system trigger in case there is already a system trigger
|
||||
# with the same name
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
CREATE TRIGGER trg1 BEFORE SHUTDOWN SET @b=1;
|
||||
ERROR HY000: Trigger 'test.trg1' already exists
|
||||
# OR REPLACE clause results in sucessful execution
|
||||
CREATE OR REPLACE TRIGGER trg1 BEFORE SHUTDOWN SET @b=1;
|
||||
# It is expected 'BEFORE SHUTDOWN' trigger in output of
|
||||
# SHOW TRIGGERS statememt
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg1 SHUTDOWN SET @b=1 BEFORE # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# Test 15. Check that ALTER EVENT RENAME TO reports correct error in case there is
|
||||
# names conflict between system trigger and event
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @a=1;
|
||||
CREATE EVENT ev1 ON SCHEDULE EVERY 1 YEAR DO SET @b=1;
|
||||
Warnings:
|
||||
Warning 1105 Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.
|
||||
ALTER EVENT ev1 RENAME TO trg1;
|
||||
ERROR HY000: Trigger with the same name 'trg1' already exists
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP EVENT ev1;
|
||||
#
|
||||
# Test 16. Check that neither INSERTING/UPDATING/DELETING predicates
|
||||
# nor NEW/OLD clauses are accepted inside a system trigger body
|
||||
#
|
||||
CREATE TRIGGER trg1 BEFORE SHUTDOWN
|
||||
BEGIN
|
||||
IF INSERTING THEN
|
||||
SET @a=100;
|
||||
END IF;
|
||||
END;
|
||||
$
|
||||
ERROR HY000: Invalid conditional predicate INSERTING inside a system trigger body
|
||||
CREATE TRIGGER trg2 BEFORE SHUTDOWN
|
||||
BEGIN
|
||||
SET NEW.a = OLD.b;
|
||||
END;
|
||||
$
|
||||
ERROR HY000: There is no OLD row in system trigger
|
||||
#
|
||||
# Test 17: Check that unknown database and the 'mysql' database for
|
||||
# trigger name in CREATE TRIGGER statement is handled correctly.
|
||||
#
|
||||
CREATE TRIGGER not_existent_db.trg1 BEFORE SHUTDOWN SET @s=1;
|
||||
ERROR 42000: Unknown database 'not_existent_db'
|
||||
CREATE TRIGGER mysql.trg1 BEFORE SHUTDOWN SET @s=1;
|
||||
ERROR HY000: Triggers can not be created on system tables
|
||||
#
|
||||
# Test 18: CREATE / DROP TRIGGER under LOCK TABLES
|
||||
#
|
||||
CREATE TABLE t1 (a INT);
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
CREATE TRIGGER trg2 AFTER STARTUP SET @b=1;
|
||||
LOCK TABLES t1 WRITE;
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
DROP TRIGGER non_existent_sys_trg1;
|
||||
ERROR HY000: Trigger does not exist
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg2 STARTUP SET @b=1 AFTER # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
# Clean up
|
||||
UNLOCK TABLES;
|
||||
DROP TRIGGER trg2;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Test 19: Check that attempt to run a trigger owned by the dropped user
|
||||
is hanlded correctly - trigger is not invoked and an error message
|
||||
# is output into the error log
|
||||
#
|
||||
CREATE USER u1;
|
||||
GRANT SELECT ON test.* TO u1;
|
||||
GRANT INSERT ON test.* TO u1;
|
||||
GRANT CREATE ON test.* TO u1;
|
||||
GRANT SUPER ON *.* TO u1;
|
||||
connect con1, localhost, u1, , test;
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP INSERT INTO t1 VALUES('Startup');
|
||||
CREATE TRIGGER IF NOT EXISTS trg2 BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown');
|
||||
connection default;
|
||||
disconnect con1;
|
||||
DROP USER u1;
|
||||
# restart
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Test 20: Check that both full table scan and index scan are supported
|
||||
# for querying metadata about system triggers
|
||||
#
|
||||
CREATE DATABASE newdb;
|
||||
CREATE TRIGGER newdb.trg1 AFTER STARTUP SET @a=1;
|
||||
CREATE TRIGGER trg2 BEFORE SHUTDOWN SET @a=2;
|
||||
# SHOW TRIGGERS and SHOW TRIGGERS IN are handled as index scan
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg2 SHUTDOWN SET @a=2 BEFORE # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
SHOW TRIGGERS IN newdb;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
trg1 STARTUP SET @a=1 AFTER # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
# Direct querying from information_schema.triggers is handled
|
||||
# as full table scan
|
||||
SELECT * FROM information_schema.triggers WHERE trigger_name IN ('trg1', 'trg2');
|
||||
TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED SQL_MODE DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION
|
||||
def newdb trg1 STARTUP def 0 NULL SET @a=1 STATEMENT AFTER NULL NULL # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
def test trg2 SHUTDOWN def 0 NULL SET @a=2 STATEMENT BEFORE NULL NULL # STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION root@localhost latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci
|
||||
# Clean up
|
||||
DROP TRIGGER newdb.trg1;
|
||||
DROP TRIGGER trg2;
|
||||
DROP DATABASE newdb;
|
||||
# End of Test 20.
|
||||
#
|
||||
# MDEV-40064: Memory leak with unparseble trigger for startup body
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
UPDATE mysql.event SET body='not valid sql ;;;' WHERE name='trg1';
|
||||
# restart
|
||||
SELECT db, name, status, kind, `when`, body FROM mysql.event WHERE name='trg1';
|
||||
db name status kind when body
|
||||
test trg1 ENABLED STARTUP AFTER not valid sql ;;;
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# MDEV-40074: Create or replace does not work for trigger for startup/shutdown
|
||||
#
|
||||
CREATE OR REPLACE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
CREATE OR REPLACE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# MDEV-40076: ASAN global-buffer-overflow in reconstruct_create_trigger_stmt
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
SET @save_sql_mode= @@sql_mode;
|
||||
SET sql_mode='';
|
||||
UPDATE mysql.event SET `when`=0 WHERE name='trg1';
|
||||
Warnings:
|
||||
Warning 1265 Data truncated for column 'when' at row 1
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
ERROR HY000: Wrong metadata for trigger 'test.trg1': 'Invalid event.when value 0'
|
||||
# No triggers be output since triggers having invalid metadata are skipped
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
Warnings:
|
||||
Warning 4277 Wrong metadata for trigger 'test.trg1': 'Invalid event.when value 0'
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
UPDATE mysql.event SET `kind`=255 WHERE name='trg1';
|
||||
Warnings:
|
||||
Warning 1265 Data truncated for column 'kind' at row 1
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
ERROR HY000: Wrong metadata for trigger 'test.trg1': 'Invalid event.kind value 63'
|
||||
# No triggers be output since triggers having invalid metadata are skipped
|
||||
SHOW TRIGGERS;
|
||||
Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation
|
||||
Warnings:
|
||||
Warning 4277 Wrong metadata for trigger 'test.trg1': 'Invalid event.kind value 63'
|
||||
UPDATE mysql.event SET `kind`=0 WHERE name='trg1';
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
ERROR HY000: Wrong metadata for trigger 'test.trg1': 'Invalid event.kind value 0'
|
||||
SET @@sql_mode= @save_sql_mode;
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# MDEV-40083: Compound triggers for startup/shutdown are stored without END, which leads to silent non-firing after restart
|
||||
#
|
||||
CREATE TABLE t1 (a INT);
|
||||
CREATE TRIGGER c1 AFTER STARTUP BEGIN INSERT INTO t1 VALUES (1); END|
|
||||
CREATE TRIGGER c2 BEFORE SHUTDOWN BEGIN INSERT INTO t1 VALUES (2); INSERT INTO t1 VALUES (3); END|
|
||||
SELECT name, body FROM mysql.event WHERE name IN ('c1','c2') ORDER BY name;
|
||||
name body
|
||||
c1 BEGIN INSERT INTO t1 VALUES (1); END
|
||||
c2 BEGIN INSERT INTO t1 VALUES (2); INSERT INTO t1 VALUES (3); END
|
||||
SHOW CREATE TRIGGER c1;
|
||||
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
|
||||
c1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER `c1` AFTER STARTUP BEGIN INSERT INTO t1 VALUES (1); END latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
|
||||
SHOW CREATE TRIGGER c2;
|
||||
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
|
||||
c2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER `c2` BEFORE SHUTDOWN BEGIN INSERT INTO t1 VALUES (2); INSERT INTO t1 VALUES (3); END latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
|
||||
# Restart server. The trigger c2 must be fired before shutdown, the trigger c1
|
||||
# Must be fired after start up.
|
||||
# restart
|
||||
# In result, the rows (2), (3), (1) must be returned by the following query
|
||||
SELECT * from t1;
|
||||
a
|
||||
2
|
||||
3
|
||||
1
|
||||
# Clean up
|
||||
DROP TRIGGER c1;
|
||||
DROP TRIGGER c2;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# MDEV-40106 Sever crashes in store_trigger_metadata after 2nd
|
||||
# execution of ps with create system trigger
|
||||
#
|
||||
PREPARE stmt FROM 'CREATE TRIGGER tr1 AFTER STARTUP SET @x=1';
|
||||
EXECUTE stmt;
|
||||
DROP TRIGGER tr1;
|
||||
# Without the fix, the second execution of create trigger in PS mode
|
||||
# would result in crash
|
||||
EXECUTE stmt;
|
||||
# Clean up
|
||||
DEALLOCATE PREPARE stmt;
|
||||
DROP TRIGGER tr1;
|
||||
#
|
||||
# MDEV-40947: ASAN global-buffer-overflow in events_to_string/reconstruct_create_trigger_stmt
|
||||
#
|
||||
CREATE TABLE t (a INT);
|
||||
CREATE TRIGGER x1 AFTER STARTUP INSERT INTO test.t VALUES (1);
|
||||
UPDATE mysql.event SET kind='DDL' WHERE name='x1';
|
||||
SHOW CREATE TRIGGER x1;
|
||||
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
|
||||
x1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER `x1` AFTER DDL INSERT INTO test.t VALUES (1) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
|
||||
DROP TRIGGER x1;
|
||||
DROP TABLE t;
|
||||
#
|
||||
# MDEV-40952: CREATE TRIGGER fails with ER_EVENT_STORE_FAILED after unrelated failed ALTER
|
||||
#
|
||||
CREATE TABLE t1 (v ENUM('a','b'));
|
||||
INSERT INTO t1 VALUES ('a'),('b');
|
||||
ALTER TABLE t1 MODIFY COLUMN v ENUM('c','d');
|
||||
ERROR 01000: Data truncated for column 'v' at row 1
|
||||
# The following statement should finish successfully
|
||||
CREATE TRIGGER trg AFTER STARTUP SET @x = 1;
|
||||
# Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TRIGGER trg;
|
||||
#
|
||||
# MDEV-40105: Information_schema.triggers doesn't show schema for system triggers
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x = 1;
|
||||
SELECT trigger_schema, event_manipulation FROM information_schema.triggers WHERE trigger_name = 'trg1';
|
||||
trigger_schema event_manipulation
|
||||
test STARTUP
|
||||
# Query the same data from the base system table
|
||||
SELECT db, kind FROM mysql.event WHERE name = 'trg1';
|
||||
db kind
|
||||
test STARTUP
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# MDEV-40095: Server doesn't start if system triggers not loaded (if cannot open mysql.event)
|
||||
#
|
||||
RENAME TABLE mysql.event TO mysql.ev_bk;
|
||||
# restart
|
||||
# Support of system triggers relies on presence of
|
||||
# the table mysql.event. Since the table mysql.event were
|
||||
# explicitly renamed, attempt to create a system trigger
|
||||
# results in the error ER_NO_SUCH_TABLE
|
||||
CREATE TRIGGER ev1 AFTER STARTUP SET @bbb=1;
|
||||
ERROR 42S02: Table 'mysql.event' doesn't exist
|
||||
RENAME TABLE mysql.ev_bk TO mysql.event;
|
||||
# Restart server in order to reload a defintion of
|
||||
# the table mysql.event in memory
|
||||
# restart
|
||||
#
|
||||
# MDEV-40067: mysqldump --sys-triggers creates unloadable dump
|
||||
#
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
DROP TRIGGER trg1;
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
|
||||
trg1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER `trg1` AFTER STARTUP SET @x=1 latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
|
||||
DROP TRIGGER trg1;
|
||||
#
|
||||
# MDEV-40072: Server crashes in my_hash_first/name_hash_search after load_system_triggers
|
||||
#
|
||||
CREATE USER a1@localhost;
|
||||
CREATE TABLE t1 (a VARCHAR(30));
|
||||
GRANT SUPER ON *.* TO a1@localhost;
|
||||
GRANT INSERT ON test.* TO a1@localhost;
|
||||
connect c1, localhost, a1,, test;
|
||||
CREATE TRIGGER tr1 AFTER STARTUP INSERT INTO t1 VALUES ('Fired on startup trigger');
|
||||
CREATE TRIGGER tr2 BEFORE SHUTDOWN INSERT INTO t1 VALUES ('Fired on shutdown trigger');
|
||||
connection default;
|
||||
disconnect c1;
|
||||
# restart
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
Fired on shutdown trigger
|
||||
Fired on startup trigger
|
||||
DROP TRIGGER tr1;
|
||||
DROP TRIGGER tr2;
|
||||
DROP TABLE t1;
|
||||
DROP USER a1@localhost;
|
||||
#
|
||||
# MDEV-40951: System triggers give no visibility into slow/failed execution and misreport server readiness
|
||||
#
|
||||
CREATE TRIGGER tr1 AFTER STARTUP INSERT INTO t1 VALUES ('Shutdown');
|
||||
# restart
|
||||
# Clean up
|
||||
DROP TRIGGER tr1;
|
||||
#
|
||||
# Check that DROP TRIGGER and SHOW CREATE TRIGGER statement
|
||||
# don't touch event with the same name
|
||||
#
|
||||
CREATE EVENT trg1 ON SCHEDULE EVERY 1 YEAR STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH DO SELECT 1;
|
||||
Warnings:
|
||||
Warning 1105 Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
ERROR HY000: Trigger does not exist
|
||||
DROP TRIGGER trg1;
|
||||
ERROR HY000: Trigger does not exist
|
||||
DROP EVENT trg1;
|
||||
#
|
||||
# Check that the statements SHOW CREATE EVENT, DROP EVENT, ALTER EVENT
|
||||
# SHOW EVENTS don't touch triggers with the name
|
||||
#
|
||||
CREATE TRIGGER trg1 BEFORE SHUTDOWN SET @a=1;
|
||||
SHOW CREATE EVENT trg1;
|
||||
ERROR HY000: Unknown event 'trg1'
|
||||
ALTER EVENT trg1 COMMENT 'The trigger';
|
||||
ERROR HY000: Unknown event 'trg1'
|
||||
DROP EVENT trg1;
|
||||
ERROR HY000: Unknown event 'trg1'
|
||||
# No events in the output
|
||||
SHOW EVENTS;
|
||||
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
683
mysql-test/main/trigger_system.test
Normal file
683
mysql-test/main/trigger_system.test
Normal file
@@ -0,0 +1,683 @@
|
||||
--source include/not_embedded.inc
|
||||
|
||||
--source include/count_sessions.inc
|
||||
|
||||
call mtr.add_suppression('Column count of mysql.event is wrong.');
|
||||
call mtr.add_suppression('Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.');
|
||||
call mtr.add_suppression('An error occurred when loading data from the table mysql.event. System triggers not loaded');
|
||||
call mtr.add_suppression('Aborting');
|
||||
call mtr.add_suppression('Incorrect definition of table mysql.event: expected column');
|
||||
call mtr.add_suppression('Cannot open mysql.event');
|
||||
call mtr.add_suppression('System trigger (.*)execution failing 1146.*');
|
||||
call mtr.add_suppression('Fail to load metadata for trigger (.*). Skip it.');
|
||||
call mtr.add_suppression('System trigger (.*) execution failing, failed to authenticate the user');
|
||||
--echo #
|
||||
--echo # MDEV-30645: CREATE TRIGGER FOR { STARTUP | SHUTDOWN }
|
||||
--echo #
|
||||
--echo # Test 1: Check that AFTER STARTUP trigger and BEFORE SHUTDOWN trigger
|
||||
--echo # are fired at right time
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP INSERT INTO t1 VALUES('Startup');
|
||||
--echo # BEFORE SHUTDOWN should be in action just after it has been created.
|
||||
--echo # So, after restarting the server the record about server shutdown should
|
||||
--echo # be inserted into the table t1.
|
||||
CREATE TRIGGER IF NOT EXISTS trg2 BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown');
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT * FROM t1;
|
||||
--echo # Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
|
||||
--echo #
|
||||
--echo # Test 2: Check that ON STARTUP/ON SHUTDOWN can't be created
|
||||
--echo # w/o the SUPER privilege
|
||||
--echo #
|
||||
|
||||
--echo # Check that ON STARTUP/ON SHUTDOWN can be created w/o the SUPER privilege
|
||||
CREATE USER u1;
|
||||
GRANT SELECT ON test.* TO u1;
|
||||
--connect (con1, localhost, u1, , test)
|
||||
--echo # The following CREATE TRIGGER AFTER STARTUP statement should fail with
|
||||
--echo # the error ER_SPECIFIC_ACCESS_DENIED_ERROR since the user `u1` doesn't
|
||||
--echo # have the SUPER privilege
|
||||
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP SET @a=1;
|
||||
|
||||
--echo # The following CREATE TRIGGER BEFORE SHUTDOWN statement should fail with
|
||||
--echo # the error ER_SPECIFIC_ACCESS_DENIED_ERROR since the user `u1` doesn't
|
||||
--echo # have the SUPER privilege
|
||||
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 BEFORE SHUTDOWN SET @b=1;
|
||||
|
||||
--connection default
|
||||
--disconnect con1
|
||||
--source include/wait_until_count_sessions.inc
|
||||
DROP USER u1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 3: Check that a user with the explicitly granted the SUPER privilege
|
||||
--echo # can create a system trigger on STARTUP/on SHUTDOWN
|
||||
--echo #
|
||||
CREATE USER u1;
|
||||
GRANT SELECT ON test.* TO u1;
|
||||
GRANT SUPER ON *.* TO u1;
|
||||
|
||||
--connect (con1, localhost, u1, , test)
|
||||
--echo # The following CREATE TRIGGER AFTER STARTUP statement and
|
||||
--echo # CREATE TRIGGER BEFORE SHUTDOWN statement should succeed
|
||||
--echo # since the SUPER privilege is granted to the user `u1`
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP INSERT INTO t1 VALUES('Startup');
|
||||
CREATE TRIGGER IF NOT EXISTS trg2 BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown');
|
||||
|
||||
--connection default
|
||||
--disconnect con1
|
||||
--source include/wait_until_count_sessions.inc
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
DROP USER u1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 4: Check that it is not possible to create a System trigger
|
||||
--echo # and a DML trigger with the same name
|
||||
--echo #
|
||||
CREATE TABLE t1 (a INT);
|
||||
--echo # First check that DML trigger can 't be created with the same name
|
||||
--echo # as the existing system trigger
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
--error ER_TRG_ALREADY_EXISTS
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo # And then check in reverse order: a system trigger can't be created
|
||||
--echo # on presence of DML trigger with the same name
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
--error ER_TRG_ALREADY_EXISTS
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 5: Check that multiple triggers can be created on the same
|
||||
--echo # system event
|
||||
--echo #
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER IF NOT EXISTS trg1_ast AFTER STARTUP INSERT INTO t1 VALUES('Startup: first action');
|
||||
CREATE TRIGGER IF NOT EXISTS trg2_ast AFTER STARTUP INSERT INTO t1 VALUES('Startup: second action');
|
||||
--echo # BEFORE SHUTDOWN should be in action just after it has been created.
|
||||
--echo # So, after restarting the server the record about server shutdown should
|
||||
--echo # be inserted into the table t1.
|
||||
CREATE TRIGGER IF NOT EXISTS trg3_bshd BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown: first action');
|
||||
CREATE TRIGGER IF NOT EXISTS trg4_bshd BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown: second action');
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT * FROM t1;
|
||||
--echo # Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TRIGGER trg1_ast;
|
||||
DROP TRIGGER trg2_ast;
|
||||
DROP TRIGGER trg3_bshd;
|
||||
DROP TRIGGER trg4_bshd;
|
||||
|
||||
--echo #
|
||||
--echo # Test 6: SHOW TRIGGERS on a system trigger
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS;
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 7: SHOW CREATE TRIGGER on a system trigger
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
--replace_column 7 #
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 8: Check that triggers and events have the same namespace
|
||||
--echo #
|
||||
|
||||
--echo # Test 8.1 Check that attempt to create a trigger with the same name
|
||||
--echo # as existent event result in error
|
||||
CREATE EVENT ev1 ON SCHEDULE EVERY 1 YEAR DO SET @aaa=1;
|
||||
--error ER_TRG_EVENT_CONFLICTS_NAME
|
||||
CREATE TRIGGER ev1 AFTER STARTUP SET @bbb=1;
|
||||
--echo # Clean up
|
||||
DROP EVENT ev1;
|
||||
|
||||
--echo # Test 8.2 Check that attempt to create an event with the same name as
|
||||
--echo # as existen trigger result in error
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @bbb=1;
|
||||
--error ER_TRG_EVENT_CONFLICTS_NAME
|
||||
CREATE EVENT trg1 ON SCHEDULE EVERY 1 YEAR DO SET @aaa=1;
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 9: Check that server can operate on database with
|
||||
--echo # broken table mysql.event.
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @bbb=1;
|
||||
|
||||
--echo # Backup the mysql.event table
|
||||
CREATE TABLE event_copy LIKE mysql.event;
|
||||
INSERT INTO event_copy SELECT * FROM mysql.event;
|
||||
|
||||
--echo # Drop column 'kind' in the table mysql.event to simulate a failure on
|
||||
--echo # loading triggers metadata from the table mysql.event
|
||||
ALTER TABLE mysql.event DROP COLUMN kind;
|
||||
|
||||
--echo # Shutdown server and restart it to see how it handle broken
|
||||
--echo # table mysql.event
|
||||
|
||||
--echo # Restart server. Expect start up success
|
||||
--let $restart_parameters=
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--echo # Restore original mysql.event from backup
|
||||
DROP TABLE mysql.event;
|
||||
RENAME TABLE event_copy TO mysql.event;
|
||||
|
||||
--echo # Restart server. Should be successful.
|
||||
--let $restart_parameters=
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 10: Check that server can be started up on the data dictionary
|
||||
--echo # having the table mysql.event from previous version of
|
||||
--echo # server (missing mandatory columns for support of system triggers)
|
||||
--echo #
|
||||
RENAME TABLE mysql.event TO mysql.ev_bk;
|
||||
--let $MYSQLD_DATADIR= `select @@datadir`
|
||||
|
||||
--source include/shutdown_mysqld.inc
|
||||
|
||||
--echo # Copy event table's files from previous server version (12.3)
|
||||
--echo # that doesn't contain columns required for system triggers support
|
||||
--copy_file $MYSQL_TEST_DIR/std_data/mdev-30645-event.MAI $MYSQLD_DATADIR/mysql/event.MAI
|
||||
--copy_file $MYSQL_TEST_DIR/std_data/mdev-30645-event.MAD $MYSQLD_DATADIR/mysql/event.MAD
|
||||
--copy_file $MYSQL_TEST_DIR/std_data/mdev-30645-event.frm $MYSQLD_DATADIR/mysql/event.frm
|
||||
|
||||
#--let $restart_parameters=--skip-grant-tables
|
||||
--source include/start_mysqld.inc
|
||||
|
||||
--error ER_SYSTEM_TRG_DISABLED
|
||||
CREATE TRIGGER ev1 AFTER STARTUP SET @bbb=1;
|
||||
|
||||
SHOW TRIGGERS;
|
||||
SELECT * FROM information_schema.triggers WHERE trigger_schema != 'mtr';
|
||||
|
||||
CREATE TABLE t1 (a INT);
|
||||
--echo # DROP for ON DML trigger, first go through the system trigger code path,
|
||||
--echo # and only then through code path for dml triggers
|
||||
--echo # Expect the following statement completes with success
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TABLE t1;
|
||||
|
||||
# back to mariadb default
|
||||
DROP TABLE mysql.event;
|
||||
RENAME TABLE mysql.ev_bk TO mysql.event;
|
||||
|
||||
--echo # Restart server in order to reload a defintion of
|
||||
--echo # the table mysql.event in memory
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--echo #
|
||||
--echo # Test 11: Check that trigger time/event combinations BEFORE STARTUP and
|
||||
--echo # AFTER SHUTDOWN are invalid
|
||||
--echo #
|
||||
|
||||
--error ER_SYS_TRG_SEMANTIC_ERROR
|
||||
CREATE TRIGGER ev1 BEFORE STARTUP SET @a=1;
|
||||
--error ER_SYS_TRG_SEMANTIC_ERROR
|
||||
CREATE TRIGGER ev1 AFTER SHUTDOWN SET @a=1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 12: Check that it is not possible to hang the server by
|
||||
--echo # executing DROP TRIGGER inside the body of ON SHUTDOWN TRIGGER
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @a=1;
|
||||
|
||||
--delimiter $
|
||||
--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
CREATE TRIGGER trg2 BEFORE SHUTDOWN
|
||||
BEGIN
|
||||
DROP TRIGGER trg1;
|
||||
END;
|
||||
$
|
||||
--delimiter ;
|
||||
--source include/restart_mysqld.inc
|
||||
--echo # Expected only the trigger trg2 in output
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS;
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 13: Check that 'IF NOT EXISTS' clause handled correctly
|
||||
--echo # on presence of ON DML trigger with the same name
|
||||
--echo #
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
|
||||
--error ER_TRG_ALREADY_EXISTS
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
|
||||
--echo # On presence IF NOT EXISTS the CREATE TRIGGER statememt
|
||||
--echo # completes with success
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP SET @b=1;
|
||||
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 14: Check that OR REPLACE clause allows to create
|
||||
--echo # a system trigger in case there is already a system trigger
|
||||
--echo # with the same name
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
|
||||
--error ER_TRG_ALREADY_EXISTS
|
||||
CREATE TRIGGER trg1 BEFORE SHUTDOWN SET @b=1;
|
||||
--echo # OR REPLACE clause results in sucessful execution
|
||||
CREATE OR REPLACE TRIGGER trg1 BEFORE SHUTDOWN SET @b=1;
|
||||
--echo # It is expected 'BEFORE SHUTDOWN' trigger in output of
|
||||
--echo # SHOW TRIGGERS statememt
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 15. Check that ALTER EVENT RENAME TO reports correct error in case there is
|
||||
--echo # names conflict between system trigger and event
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @a=1;
|
||||
CREATE EVENT ev1 ON SCHEDULE EVERY 1 YEAR DO SET @b=1;
|
||||
--error ER_TRG_EVENT_CONFLICTS_NAME
|
||||
ALTER EVENT ev1 RENAME TO trg1;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP EVENT ev1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 16. Check that neither INSERTING/UPDATING/DELETING predicates
|
||||
--echo # nor NEW/OLD clauses are accepted inside a system trigger body
|
||||
--echo #
|
||||
--delimiter $
|
||||
|
||||
--error ER_SYSTEM_TRG_INVALID_PREDICATE
|
||||
CREATE TRIGGER trg1 BEFORE SHUTDOWN
|
||||
BEGIN
|
||||
IF INSERTING THEN
|
||||
SET @a=100;
|
||||
END IF;
|
||||
END;
|
||||
$
|
||||
|
||||
--error ER_TRG_NO_SUCH_ROW_IN_TRG
|
||||
CREATE TRIGGER trg2 BEFORE SHUTDOWN
|
||||
BEGIN
|
||||
SET NEW.a = OLD.b;
|
||||
END;
|
||||
$
|
||||
|
||||
--delimiter ;
|
||||
|
||||
--echo #
|
||||
--echo # Test 17: Check that unknown database and the 'mysql' database for
|
||||
--echo # trigger name in CREATE TRIGGER statement is handled correctly.
|
||||
--echo #
|
||||
--error ER_BAD_DB_ERROR
|
||||
CREATE TRIGGER not_existent_db.trg1 BEFORE SHUTDOWN SET @s=1;
|
||||
|
||||
--error ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
|
||||
CREATE TRIGGER mysql.trg1 BEFORE SHUTDOWN SET @s=1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 18: CREATE / DROP TRIGGER under LOCK TABLES
|
||||
--echo #
|
||||
CREATE TABLE t1 (a INT);
|
||||
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a=1;
|
||||
CREATE TRIGGER trg2 AFTER STARTUP SET @b=1;
|
||||
LOCK TABLES t1 WRITE;
|
||||
|
||||
DROP TRIGGER trg1;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
DROP TRIGGER trg2;
|
||||
--error ER_TRG_DOES_NOT_EXIST
|
||||
DROP TRIGGER non_existent_sys_trg1;
|
||||
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @b=1;
|
||||
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS;
|
||||
|
||||
--echo # Clean up
|
||||
UNLOCK TABLES;
|
||||
DROP TRIGGER trg2;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 19: Check that attempt to run a trigger owned by the dropped user
|
||||
--echo is hanlded correctly - trigger is not invoked and an error message
|
||||
--echo # is output into the error log
|
||||
--echo #
|
||||
CREATE USER u1;
|
||||
GRANT SELECT ON test.* TO u1;
|
||||
GRANT INSERT ON test.* TO u1;
|
||||
GRANT CREATE ON test.* TO u1;
|
||||
GRANT SUPER ON *.* TO u1;
|
||||
|
||||
--connect (con1, localhost, u1, , test)
|
||||
CREATE TABLE t1 (a VARCHAR(100));
|
||||
CREATE TRIGGER IF NOT EXISTS trg1 AFTER STARTUP INSERT INTO t1 VALUES('Startup');
|
||||
CREATE TRIGGER IF NOT EXISTS trg2 BEFORE SHUTDOWN INSERT INTO t1 VALUES('Shutdown');
|
||||
|
||||
--connection default
|
||||
--disconnect con1
|
||||
|
||||
DROP USER u1;
|
||||
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
SELECT * FROM t1;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
DROP TRIGGER trg2;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Test 20: Check that both full table scan and index scan are supported
|
||||
--echo # for querying metadata about system triggers
|
||||
--echo #
|
||||
CREATE DATABASE newdb;
|
||||
|
||||
CREATE TRIGGER newdb.trg1 AFTER STARTUP SET @a=1;
|
||||
CREATE TRIGGER trg2 BEFORE SHUTDOWN SET @a=2;
|
||||
|
||||
--echo # SHOW TRIGGERS and SHOW TRIGGERS IN are handled as index scan
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS;
|
||||
|
||||
--replace_column 6 #
|
||||
SHOW TRIGGERS IN newdb;
|
||||
|
||||
--echo # Direct querying from information_schema.triggers is handled
|
||||
--echo # as full table scan
|
||||
--replace_column 17 #
|
||||
SELECT * FROM information_schema.triggers WHERE trigger_name IN ('trg1', 'trg2');
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER newdb.trg1;
|
||||
DROP TRIGGER trg2;
|
||||
|
||||
DROP DATABASE newdb;
|
||||
|
||||
--echo # End of Test 20.
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40064: Memory leak with unparseble trigger for startup body
|
||||
--echo #
|
||||
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
|
||||
UPDATE mysql.event SET body='not valid sql ;;;' WHERE name='trg1';
|
||||
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT db, name, status, kind, `when`, body FROM mysql.event WHERE name='trg1';
|
||||
|
||||
# Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40074: Create or replace does not work for trigger for startup/shutdown
|
||||
--echo #
|
||||
CREATE OR REPLACE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
CREATE OR REPLACE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40076: ASAN global-buffer-overflow in reconstruct_create_trigger_stmt
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
|
||||
SET @save_sql_mode= @@sql_mode;
|
||||
# Reset the sql_mode to allow UPDATE mysql.event
|
||||
SET sql_mode='';
|
||||
UPDATE mysql.event SET `when`=0 WHERE name='trg1';
|
||||
|
||||
--error ER_SYSTEM_TRG_INVALID_METADATA
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
|
||||
--echo # No triggers be output since triggers having invalid metadata are skipped
|
||||
SHOW TRIGGERS;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
UPDATE mysql.event SET `kind`=255 WHERE name='trg1';
|
||||
|
||||
--error ER_SYSTEM_TRG_INVALID_METADATA
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
|
||||
--echo # No triggers be output since triggers having invalid metadata are skipped
|
||||
SHOW TRIGGERS;
|
||||
|
||||
UPDATE mysql.event SET `kind`=0 WHERE name='trg1';
|
||||
--error ER_SYSTEM_TRG_INVALID_METADATA
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
|
||||
SET @@sql_mode= @save_sql_mode;
|
||||
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40083: Compound triggers for startup/shutdown are stored without END, which leads to silent non-firing after restart
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (a INT);
|
||||
|
||||
--delimiter |
|
||||
|
||||
CREATE TRIGGER c1 AFTER STARTUP BEGIN INSERT INTO t1 VALUES (1); END|
|
||||
CREATE TRIGGER c2 BEFORE SHUTDOWN BEGIN INSERT INTO t1 VALUES (2); INSERT INTO t1 VALUES (3); END|
|
||||
|
||||
--delimiter ;
|
||||
|
||||
SELECT name, body FROM mysql.event WHERE name IN ('c1','c2') ORDER BY name;
|
||||
|
||||
--replace_column 7 #
|
||||
SHOW CREATE TRIGGER c1;
|
||||
--replace_column 7 #
|
||||
SHOW CREATE TRIGGER c2;
|
||||
--echo # Restart server. The trigger c2 must be fired before shutdown, the trigger c1
|
||||
--echo # Must be fired after start up.
|
||||
--source include/restart_mysqld.inc
|
||||
--echo # In result, the rows (2), (3), (1) must be returned by the following query
|
||||
SELECT * from t1;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER c1;
|
||||
DROP TRIGGER c2;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40106 Sever crashes in store_trigger_metadata after 2nd
|
||||
--echo # execution of ps with create system trigger
|
||||
--echo #
|
||||
PREPARE stmt FROM 'CREATE TRIGGER tr1 AFTER STARTUP SET @x=1';
|
||||
EXECUTE stmt;
|
||||
DROP TRIGGER tr1;
|
||||
--echo # Without the fix, the second execution of create trigger in PS mode
|
||||
--echo # would result in crash
|
||||
EXECUTE stmt;
|
||||
|
||||
--echo # Clean up
|
||||
DEALLOCATE PREPARE stmt;
|
||||
DROP TRIGGER tr1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40947: ASAN global-buffer-overflow in events_to_string/reconstruct_create_trigger_stmt
|
||||
--echo #
|
||||
CREATE TABLE t (a INT);
|
||||
|
||||
CREATE TRIGGER x1 AFTER STARTUP INSERT INTO test.t VALUES (1);
|
||||
UPDATE mysql.event SET kind='DDL' WHERE name='x1';
|
||||
|
||||
--replace_column 7 #
|
||||
SHOW CREATE TRIGGER x1;
|
||||
|
||||
DROP TRIGGER x1;
|
||||
DROP TABLE t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40952: CREATE TRIGGER fails with ER_EVENT_STORE_FAILED after unrelated failed ALTER
|
||||
--echo #
|
||||
CREATE TABLE t1 (v ENUM('a','b'));
|
||||
|
||||
INSERT INTO t1 VALUES ('a'),('b');
|
||||
|
||||
--error WARN_DATA_TRUNCATED
|
||||
ALTER TABLE t1 MODIFY COLUMN v ENUM('c','d');
|
||||
|
||||
--echo # The following statement should finish successfully
|
||||
CREATE TRIGGER trg AFTER STARTUP SET @x = 1;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TABLE t1;
|
||||
DROP TRIGGER trg;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40105: Information_schema.triggers doesn't show schema for system triggers
|
||||
--echo #
|
||||
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x = 1;
|
||||
SELECT trigger_schema, event_manipulation FROM information_schema.triggers WHERE trigger_name = 'trg1';
|
||||
--echo # Query the same data from the base system table
|
||||
SELECT db, kind FROM mysql.event WHERE name = 'trg1';
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40095: Server doesn't start if system triggers not loaded (if cannot open mysql.event)
|
||||
--echo #
|
||||
RENAME TABLE mysql.event TO mysql.ev_bk;
|
||||
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--echo # Support of system triggers relies on presence of
|
||||
--echo # the table mysql.event. Since the table mysql.event were
|
||||
--echo # explicitly renamed, attempt to create a system trigger
|
||||
--echo # results in the error ER_NO_SUCH_TABLE
|
||||
--error ER_NO_SUCH_TABLE
|
||||
CREATE TRIGGER ev1 AFTER STARTUP SET @bbb=1;
|
||||
|
||||
# back to mariadb default
|
||||
RENAME TABLE mysql.ev_bk TO mysql.event;
|
||||
|
||||
--echo # Restart server in order to reload a defintion of
|
||||
--echo # the table mysql.event in memory
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40067: mysqldump --sys-triggers creates unloadable dump
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 AFTER STARTUP SET @x=1;
|
||||
--exec $MYSQL_DUMP --sys-triggers test > $MYSQLTEST_VARDIR/tmp/dump.sql
|
||||
DROP TRIGGER trg1;
|
||||
--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/dump.sql
|
||||
--replace_column 7 #
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump.sql
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40072: Server crashes in my_hash_first/name_hash_search after load_system_triggers
|
||||
--echo #
|
||||
CREATE USER a1@localhost;
|
||||
CREATE TABLE t1 (a VARCHAR(30));
|
||||
GRANT SUPER ON *.* TO a1@localhost;
|
||||
|
||||
GRANT INSERT ON test.* TO a1@localhost;
|
||||
|
||||
--connect (c1, localhost, a1,, test)
|
||||
|
||||
CREATE TRIGGER tr1 AFTER STARTUP INSERT INTO t1 VALUES ('Fired on startup trigger');
|
||||
CREATE TRIGGER tr2 BEFORE SHUTDOWN INSERT INTO t1 VALUES ('Fired on shutdown trigger');
|
||||
|
||||
--connection default
|
||||
|
||||
--disconnect c1
|
||||
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
SELECT * FROM t1;
|
||||
DROP TRIGGER tr1;
|
||||
DROP TRIGGER tr2;
|
||||
DROP TABLE t1;
|
||||
DROP USER a1@localhost;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40951: System triggers give no visibility into slow/failed execution and misreport server readiness
|
||||
--echo #
|
||||
# The trigger 'trg1' references to not-existent table, so on server restart
|
||||
# the error should be output in error log
|
||||
CREATE TRIGGER tr1 AFTER STARTUP INSERT INTO t1 VALUES ('Shutdown');
|
||||
--error 1
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER tr1;
|
||||
|
||||
--echo #
|
||||
--echo # Check that DROP TRIGGER and SHOW CREATE TRIGGER statement
|
||||
--echo # don't touch event with the same name
|
||||
--echo #
|
||||
CREATE EVENT trg1 ON SCHEDULE EVERY 1 YEAR STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH DO SELECT 1;
|
||||
--error ER_TRG_DOES_NOT_EXIST
|
||||
SHOW CREATE TRIGGER trg1;
|
||||
|
||||
--error ER_TRG_DOES_NOT_EXIST
|
||||
DROP TRIGGER trg1;
|
||||
|
||||
DROP EVENT trg1;
|
||||
|
||||
--echo #
|
||||
--echo # Check that the statements SHOW CREATE EVENT, DROP EVENT, ALTER EVENT
|
||||
--echo # SHOW EVENTS don't touch triggers with the name
|
||||
--echo #
|
||||
CREATE TRIGGER trg1 BEFORE SHUTDOWN SET @a=1;
|
||||
--error ER_EVENT_DOES_NOT_EXIST
|
||||
SHOW CREATE EVENT trg1;
|
||||
--error ER_EVENT_DOES_NOT_EXIST
|
||||
ALTER EVENT trg1 COMMENT 'The trigger';
|
||||
--error ER_EVENT_DOES_NOT_EXIST
|
||||
DROP EVENT trg1;
|
||||
--echo # No events in the output
|
||||
SHOW EVENTS;
|
||||
|
||||
--echo # Clean up
|
||||
DROP TRIGGER trg1;
|
||||
BIN
mysql-test/std_data/mdev-30645-event.MAD
Normal file
BIN
mysql-test/std_data/mdev-30645-event.MAD
Normal file
Binary file not shown.
BIN
mysql-test/std_data/mdev-30645-event.MAI
Normal file
BIN
mysql-test/std_data/mdev-30645-event.MAI
Normal file
Binary file not shown.
BIN
mysql-test/std_data/mdev-30645-event.frm
Normal file
BIN
mysql-test/std_data/mdev-30645-event.frm
Normal file
Binary file not shown.
@@ -20,38 +20,42 @@ alter table mysql.event
|
||||
change column body body longtext character set utf8 collate utf8_bin;
|
||||
# Now we restart the server
|
||||
call mtr.add_suppression("Incorrect definition of table mysql.event:.*");
|
||||
# restart
|
||||
# Turn the option --skip-grant-tables on in order to be able
|
||||
# to start server on broken mysql.event table
|
||||
# restart: --skip-grant-tables
|
||||
use events_test;
|
||||
select @@event_scheduler;
|
||||
@@event_scheduler
|
||||
OFF
|
||||
DISABLED
|
||||
show events;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
select event_name from information_schema.events;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
event_name
|
||||
show create event intact_check;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
drop event no_such_event;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
create event intact_check_1 on schedule every 5 hour do select 5;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
alter event intact_check_1 on schedule every 8 hour do select 8;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
alter event intact_check_1 rename to intact_check_2;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
drop event intact_check_1;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
drop event intact_check_2;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
drop event intact_check;
|
||||
ERROR HY000: Cannot proceed, because event scheduler is disabled
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
# Since server is started with --skip-grant-tables option the following
|
||||
# two statements be failed with the error ER_OPTION_PREVENTS_STATEMENT
|
||||
set global event_scheduler=on;
|
||||
ERROR HY000: Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
set global event_scheduler=off;
|
||||
ERROR HY000: Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler.
|
||||
ERROR HY000: The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement
|
||||
show variables like 'event_scheduler';
|
||||
Variable_name Value
|
||||
event_scheduler OFF
|
||||
event_scheduler DISABLED
|
||||
# Make sure that we still can create and drop databases,
|
||||
# and no warnings are produced.
|
||||
drop database if exists mysqltest_database_not_exists;
|
||||
@@ -64,23 +68,9 @@ Error 1545 Failed to open mysql.event
|
||||
# Restore the original mysql.event table
|
||||
drop table mysql.event;
|
||||
rename table event_like to mysql.event;
|
||||
# check that we can now enable events without restart
|
||||
set global event_scheduler=original;
|
||||
Warnings:
|
||||
Note 1408 Event Scheduler: Loaded 3 events
|
||||
select @@global.event_scheduler;
|
||||
@@global.event_scheduler
|
||||
ON
|
||||
set global event_scheduler=on;
|
||||
select @@global.event_scheduler;
|
||||
@@global.event_scheduler
|
||||
ON
|
||||
show events;
|
||||
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation
|
||||
events_test abc1 root@localhost SYSTEM RECURRING # 1 SECOND # # ENABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci
|
||||
events_test abc2 root@localhost SYSTEM RECURRING # 1 SECOND # # ENABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci
|
||||
events_test abc3 root@localhost SYSTEM RECURRING # 1 SECOND # # ENABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci
|
||||
# Now let's restart the server again
|
||||
# clean the flag '--skip-grant-tables' previously set
|
||||
# to start server on broken mysql.event table
|
||||
# restart
|
||||
use events_test;
|
||||
select @@event_scheduler;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
call mtr.add_suppression('Column count of mysql.event is wrong. Expected .*, found .*\. The table is probably corrupted');
|
||||
call mtr.add_suppression('Event Scheduler: An error occurred when initializing system tables. Disabling the Event Scheduler');
|
||||
call mtr.add_suppression('Event Scheduler.*shutdown.*');
|
||||
call mtr.add_suppression('Event Scheduler will not function when starting with --skip-grant-tables');
|
||||
call mtr.add_suppression('An error occurred when loading data from the table mysql.event. System triggers not loaded');
|
||||
--enable_query_log
|
||||
|
||||
let $collation_server=`select @@collation_server`;
|
||||
@@ -47,6 +49,10 @@ alter table mysql.event
|
||||
--echo # Now we restart the server
|
||||
|
||||
call mtr.add_suppression("Incorrect definition of table mysql.event:.*");
|
||||
|
||||
--echo # Turn the option --skip-grant-tables on in order to be able
|
||||
--echo # to start server on broken mysql.event table
|
||||
--let $restart_parameters=--skip-grant-tables
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
use events_test;
|
||||
@@ -55,27 +61,28 @@ select @@event_scheduler;
|
||||
# Try various Event Scheduler operation and check the output.
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
show events;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
select event_name from information_schema.events;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
show create event intact_check;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
drop event no_such_event;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
create event intact_check_1 on schedule every 5 hour do select 5;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
alter event intact_check_1 on schedule every 8 hour do select 8;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
alter event intact_check_1 rename to intact_check_2;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
drop event intact_check_1;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
drop event intact_check_2;
|
||||
--error ER_EVENTS_DB_ERROR
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
drop event intact_check;
|
||||
--error ER_STARTUP
|
||||
--echo # Since server is started with --skip-grant-tables option the following
|
||||
--echo # two statements be failed with the error ER_OPTION_PREVENTS_STATEMENT
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
set global event_scheduler=on;
|
||||
--error ER_STARTUP
|
||||
--error ER_OPTION_PREVENTS_STATEMENT
|
||||
set global event_scheduler=off;
|
||||
show variables like 'event_scheduler';
|
||||
--echo # Make sure that we still can create and drop databases,
|
||||
@@ -87,18 +94,11 @@ drop database mysqltest_db1;
|
||||
drop table mysql.event;
|
||||
rename table event_like to mysql.event;
|
||||
|
||||
--echo # check that we can now enable events without restart
|
||||
set global event_scheduler=original;
|
||||
select @@global.event_scheduler;
|
||||
set global event_scheduler=on;
|
||||
select @@global.event_scheduler;
|
||||
--sorted_result
|
||||
--replace_column 6 # 9 # 10 #
|
||||
--replace_result $collation_server latin1_swedish_ci
|
||||
show events;
|
||||
|
||||
--echo # Now let's restart the server again
|
||||
|
||||
--echo # clean the flag '--skip-grant-tables' previously set
|
||||
--echo # to start server on broken mysql.event table
|
||||
--let $restart_parameters=
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
# We need this file primarily to make sure that the scheduler is restarted
|
||||
|
||||
@@ -191,17 +191,17 @@ SELECT SCHEMA_NAME, DIGEST, DIGEST_TEXT, COUNT_STAR
|
||||
FROM performance_schema.events_statements_summary_by_digest
|
||||
ORDER BY DIGEST_TEXT;
|
||||
SCHEMA_NAME DIGEST DIGEST_TEXT COUNT_STAR
|
||||
test 8fd77e42aa215f1c392b12404e1f326f EXPLAIN SELECT * FROM `test` . `v1` 1
|
||||
test d192506b4d7388fde34be0d045d4da28 EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 1
|
||||
test 70895eb62262c5cade510431bf74f623 EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 1
|
||||
test 812a7de7fda86d147fd519d2b761190f EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 1
|
||||
test 138f04079728d0beedc1f6e9775251b1 EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 1
|
||||
test 3dedb3adc3d28e9413564b8fa2e46092 SELECT * FROM `test` . `v1` 1
|
||||
test 7ebd4b8fe4b1a9964824e393fd812f8a SELECT * FROM `test` . `v1` WHERE `a` = ? 1
|
||||
test 9f5ab5996307a97866c9f9f8363c9dc4 SELECT * FROM `test` . `v1` WHERE `b` > ? 1
|
||||
test faa8cac3db2ca32b1789b3c8e2da5d42 SELECT `a` , `b` FROM `test` . `v1` 1
|
||||
test 8e4121e961fd6095fe63f0f1813c848b SELECT `b` , `a` FROM `test` . `v1` 1
|
||||
test 5d31a5e3e18ae1ff1b65b95ceca583ee TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
|
||||
test 0085248d4e1af076d18d2160a58940ef EXPLAIN SELECT * FROM `test` . `v1` 1
|
||||
test cda077a86a30a5cd2c451901c5e2f07d EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 1
|
||||
test 5ff99a0556b9d56d5680dad48234ce4f EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 1
|
||||
test b2eb7f8d378d838afa17136caf1ee251 EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 1
|
||||
test c40f511deb778d9ef5e23f5c396b9925 EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 1
|
||||
test 22076071b95c3ed251ef7e9e38b43906 SELECT * FROM `test` . `v1` 1
|
||||
test dea7f6818a780a6a17e4b4ba6a6ef5be SELECT * FROM `test` . `v1` WHERE `a` = ? 1
|
||||
test 902dfc7a92cedc7dff2dc4582b6a5a40 SELECT * FROM `test` . `v1` WHERE `b` > ? 1
|
||||
test f3a15c1689e4d703e606b0841e9d13dc SELECT `a` , `b` FROM `test` . `v1` 1
|
||||
test f0391135abedb25a138daa7d13d03914 SELECT `b` , `a` FROM `test` . `v1` 1
|
||||
test ef23939ca43c2fc25b538402f3b2cf73 TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
|
||||
DROP TABLE test.v1;
|
||||
CREATE VIEW test.v1 AS SELECT * FROM test.t1;
|
||||
EXPLAIN SELECT * from test.v1;
|
||||
@@ -248,19 +248,19 @@ SELECT SCHEMA_NAME, DIGEST, DIGEST_TEXT, COUNT_STAR
|
||||
FROM performance_schema.events_statements_summary_by_digest
|
||||
ORDER BY DIGEST_TEXT;
|
||||
SCHEMA_NAME DIGEST DIGEST_TEXT COUNT_STAR
|
||||
test 5262694dd83cb5c820b21d451cd74a78 CREATE VIEW `test` . `v1` AS SELECT * FROM `test` . `t1` 1
|
||||
test 67a82e6286878f1cfa3ea515a3ce6a3a DROP TABLE `test` . `v1` 1
|
||||
test 8fd77e42aa215f1c392b12404e1f326f EXPLAIN SELECT * FROM `test` . `v1` 2
|
||||
test d192506b4d7388fde34be0d045d4da28 EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 2
|
||||
test 70895eb62262c5cade510431bf74f623 EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 2
|
||||
test 812a7de7fda86d147fd519d2b761190f EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 2
|
||||
test 138f04079728d0beedc1f6e9775251b1 EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 2
|
||||
test 3dedb3adc3d28e9413564b8fa2e46092 SELECT * FROM `test` . `v1` 2
|
||||
test 7ebd4b8fe4b1a9964824e393fd812f8a SELECT * FROM `test` . `v1` WHERE `a` = ? 2
|
||||
test 9f5ab5996307a97866c9f9f8363c9dc4 SELECT * FROM `test` . `v1` WHERE `b` > ? 2
|
||||
test 64a00b61a5804ffa08fd7f5cfcc39c9a SELECT SCHEMA_NAME , `DIGEST` , `DIGEST_TEXT` , `COUNT_STAR` FROM `performance_schema` . `events_statements_summary_by_digest` ORDER BY `DIGEST_TEXT` 1
|
||||
test faa8cac3db2ca32b1789b3c8e2da5d42 SELECT `a` , `b` FROM `test` . `v1` 2
|
||||
test 8e4121e961fd6095fe63f0f1813c848b SELECT `b` , `a` FROM `test` . `v1` 2
|
||||
test 5d31a5e3e18ae1ff1b65b95ceca583ee TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
|
||||
test d1cdccd02792c280a506126e7d4ca49d CREATE VIEW `test` . `v1` AS SELECT * FROM `test` . `t1` 1
|
||||
test c5b0cfd7ac16f6e1a3c2993491bceece DROP TABLE `test` . `v1` 1
|
||||
test 0085248d4e1af076d18d2160a58940ef EXPLAIN SELECT * FROM `test` . `v1` 2
|
||||
test cda077a86a30a5cd2c451901c5e2f07d EXPLAIN SELECT * FROM `test` . `v1` WHERE `a` = ? 2
|
||||
test 5ff99a0556b9d56d5680dad48234ce4f EXPLAIN SELECT * FROM `test` . `v1` WHERE `b` > ? 2
|
||||
test b2eb7f8d378d838afa17136caf1ee251 EXPLAIN SELECT `a` , `b` FROM `test` . `v1` 2
|
||||
test c40f511deb778d9ef5e23f5c396b9925 EXPLAIN SELECT `b` , `a` FROM `test` . `v1` 2
|
||||
test 22076071b95c3ed251ef7e9e38b43906 SELECT * FROM `test` . `v1` 2
|
||||
test dea7f6818a780a6a17e4b4ba6a6ef5be SELECT * FROM `test` . `v1` WHERE `a` = ? 2
|
||||
test 902dfc7a92cedc7dff2dc4582b6a5a40 SELECT * FROM `test` . `v1` WHERE `b` > ? 2
|
||||
test 51f2da27677448f84fe0b055e2354e24 SELECT SCHEMA_NAME , `DIGEST` , `DIGEST_TEXT` , `COUNT_STAR` FROM `performance_schema` . `events_statements_summary_by_digest` ORDER BY `DIGEST_TEXT` 1
|
||||
test f3a15c1689e4d703e606b0841e9d13dc SELECT `a` , `b` FROM `test` . `v1` 2
|
||||
test f0391135abedb25a138daa7d13d03914 SELECT `b` , `a` FROM `test` . `v1` 2
|
||||
test ef23939ca43c2fc25b538402f3b2cf73 TRUNCATE TABLE `performance_schema` . `events_statements_summary_by_digest` 1
|
||||
DROP VIEW test.v1;
|
||||
DROP TABLE test.t1;
|
||||
|
||||
@@ -8,5 +8,5 @@ SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
|
||||
####################################
|
||||
SELECT event_name, digest, digest_text, sql_text FROM events_statements_history_long;
|
||||
event_name digest digest_text sql_text
|
||||
statement/sql/select 17e96b3c92c5add229adb20206fb9024 SELECT ? + ? + SELECT ...
|
||||
statement/sql/truncate 1748b5cb1c4ab9fc57433da8b5b86084 TRUNCATE TABLE truncat...
|
||||
statement/sql/select 230bcebe6d9fc89976beb2ae8883d5e8 SELECT ? + ? + SELECT ...
|
||||
statement/sql/truncate 04c2fbfba0a854a0efcc94d15251df44 TRUNCATE TABLE truncat...
|
||||
|
||||
@@ -1159,7 +1159,7 @@ FROM performance_schema.events_transactions_current
|
||||
WHERE ((THREAD_ID = @con1_thread_id) OR (@all_threads = 1))
|
||||
ORDER BY thread_id, event_id;
|
||||
THREAD_ID R_EVENT_ID R_END_EVENT_ID EVENT_NAME STATE ACCESS_MODE ISOLATION_LEVEL AUTO R_NESTING_EVENT_ID NESTING_EVENT_TYPE
|
||||
thread_id 21 21 transaction COMMITTED READ WRITE REPEATABLE READ YES 20 STATEMENT
|
||||
thread_id 22 22 transaction COMMITTED READ WRITE REPEATABLE READ YES 20 STATEMENT
|
||||
#
|
||||
# EVENTS_TRANSACTIONS_HISTORY_LONG
|
||||
#
|
||||
@@ -1179,6 +1179,7 @@ ORDER BY thread_id, event_id;
|
||||
THREAD_ID R_EVENT_ID R_END_EVENT_ID EVENT_NAME STATE ACCESS_MODE ISOLATION_LEVEL AUTO R_NESTING_EVENT_ID NESTING_EVENT_TYPE
|
||||
thread_id 2 19 transaction COMMITTED READ WRITE REPEATABLE READ NO 1 STATEMENT
|
||||
thread_id 21 21 transaction COMMITTED READ WRITE REPEATABLE READ YES 20 STATEMENT
|
||||
thread_id 22 22 transaction COMMITTED READ WRITE REPEATABLE READ YES 20 STATEMENT
|
||||
#
|
||||
# EVENTS_STATEMENTS_HISTORY_LONG
|
||||
#
|
||||
@@ -1213,7 +1214,7 @@ thread_id 16 16 statement/sp/jump_if_not trigger_befo
|
||||
thread_id 17 17 statement/sp/stmt trigger_befo 10 STATEMENT 1 SIGNAL sqlstate '45001' SET message_text = "FORCE ERROR"
|
||||
thread_id 18 18 statement/sql/select NULL 2 TRANSACTION 0 SELECT * FROM t1 ORDER BY s1
|
||||
thread_id 19 19 statement/sql/commit NULL 2 TRANSACTION 0 COMMIT
|
||||
thread_id 20 21 statement/sql/drop_trigger NULL NULL NULL 0 DROP TRIGGER trigger_before_update
|
||||
thread_id 20 22 statement/sql/drop_trigger NULL NULL NULL 0 DROP TRIGGER trigger_before_update
|
||||
#
|
||||
### Combined statement and transaction event history ordered by event id
|
||||
#
|
||||
@@ -1259,8 +1260,9 @@ thread_id 16 16 statement/sp/jump_if_not 10
|
||||
thread_id 17 17 statement/sp/stmt 10 STATEMENT SIGNAL sqlstate '45001' SET message_text = "FORCE ERROR"
|
||||
thread_id 18 18 statement/sql/select 2 TRANSACTION SELECT * FROM t1 ORDER BY s1
|
||||
thread_id 19 19 statement/sql/commit 2 TRANSACTION COMMIT
|
||||
thread_id 20 21 statement/sql/drop_trigge NULL NULL DROP TRIGGER trigger_before_update
|
||||
thread_id 20 22 statement/sql/drop_trigge NULL NULL DROP TRIGGER trigger_before_update
|
||||
thread_id 21 21 transaction 20 STATEMENT <transaction started>
|
||||
thread_id 22 22 transaction 20 STATEMENT <transaction started>
|
||||
#
|
||||
### Clear statement and transaction history
|
||||
# CALL test.clear_history();
|
||||
|
||||
@@ -695,8 +695,6 @@ alter table event MODIFY definer varchar(384) collate utf8mb3_bin NOT NULL DEFAU
|
||||
# the tasks MENT-2355, MENT-2291 will be done later
|
||||
ALTER TABLE event ADD kind SET ('SCHEDULE', 'STARTUP', 'SHUTDOWN',
|
||||
'LOGON', 'LOGOFF', 'DDL') NOT NULL DEFAULT 'SCHEDULE';
|
||||
# The column `when` is used only for triggers on DDL statements (kind = 'DDL').
|
||||
# For other kind of system triggers this column has the value NULL.
|
||||
ALTER TABLE event ADD `when` ENUM ('BEFORE', 'AFTER') DEFAULT NULL;
|
||||
# The column `ddl_type` is used only for triggers on DDL statements (kind = 'DDL').
|
||||
# For other kind of system triggers this column has the value NULL.
|
||||
|
||||
@@ -151,7 +151,7 @@ SET (SQL_SOURCE
|
||||
sql_time.cc tztime.cc unireg.cc item_xmlfunc.cc
|
||||
uniques.cc
|
||||
rpl_tblmap.cc sql_binlog.cc event_scheduler.cc
|
||||
event_data_objects.cc
|
||||
event_data_objects.cc event_common.cc
|
||||
event_queue.cc event_db_repository.cc
|
||||
events.cc ../sql-common/my_user.c
|
||||
partition_info.cc rpl_utility.cc rpl_utility_server.cc
|
||||
@@ -207,6 +207,7 @@ SET (SQL_SOURCE
|
||||
sql_type_row.cc
|
||||
sql_type_composite.cc sql_type_composite.h
|
||||
item_composite.cc item_composite.h
|
||||
sql_sys_or_ddl_trigger.cc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
|
||||
${GEN_SOURCES}
|
||||
|
||||
424
sql/event_common.cc
Normal file
424
sql/event_common.cc
Normal file
@@ -0,0 +1,424 @@
|
||||
#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
|
||||
|
||||
#include <my_alloc.h> // MEM_ROOT
|
||||
#include "event_common.h"
|
||||
#include "event_db_repository.h" // enum enum_events_table_field
|
||||
#include "log.h" // sql_print_warning
|
||||
#include "sp.h" // load_charset
|
||||
#include "sql_base.h" // MYSQL_LOCK_IGNORE_TIMEOUT
|
||||
#include "sql_class.h" // THD
|
||||
#include "sql_db.h" // get_default_db_collation
|
||||
#include "sql_show.h" // append_definer()
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Event_creation_ctx implementation.
|
||||
**************************************************************************/
|
||||
|
||||
bool
|
||||
Event_creation_ctx::load_from_db(THD *thd,
|
||||
MEM_ROOT *event_mem_root,
|
||||
const char *db_name,
|
||||
const char *event_name,
|
||||
TABLE *event_tbl,
|
||||
Stored_program_creation_ctx **ctx)
|
||||
{
|
||||
/* Load character set/collation attributes. */
|
||||
|
||||
CHARSET_INFO *client_cs;
|
||||
CHARSET_INFO *connection_cl;
|
||||
CHARSET_INFO *db_cl;
|
||||
|
||||
bool invalid_creation_ctx= false;
|
||||
|
||||
if (load_charset(thd, event_mem_root,
|
||||
event_tbl->field[ET_FIELD_CHARACTER_SET_CLIENT],
|
||||
thd->variables.character_set_client,
|
||||
&client_cs))
|
||||
{
|
||||
sql_print_warning("Event '%s'.'%s': invalid value "
|
||||
"in column mysql.event.character_set_client.",
|
||||
(const char *) db_name,
|
||||
(const char *) event_name);
|
||||
|
||||
invalid_creation_ctx= true;
|
||||
}
|
||||
|
||||
if (load_collation(thd, event_mem_root,
|
||||
event_tbl->field[ET_FIELD_COLLATION_CONNECTION],
|
||||
thd->variables.collation_connection,
|
||||
&connection_cl))
|
||||
{
|
||||
sql_print_warning("Event '%s'.'%s': invalid value "
|
||||
"in column mysql.event.collation_connection.",
|
||||
(const char *) db_name,
|
||||
(const char *) event_name);
|
||||
|
||||
invalid_creation_ctx= true;
|
||||
}
|
||||
|
||||
if (load_collation(thd, event_mem_root,
|
||||
event_tbl->field[ET_FIELD_DB_COLLATION],
|
||||
NULL,
|
||||
&db_cl))
|
||||
{
|
||||
sql_print_warning("Event '%s'.'%s': invalid value "
|
||||
"in column mysql.event.db_collation.",
|
||||
(const char *) db_name,
|
||||
(const char *) event_name);
|
||||
|
||||
invalid_creation_ctx= true;
|
||||
}
|
||||
|
||||
/*
|
||||
If we failed to resolve the database collation, load the default one
|
||||
from the disk.
|
||||
*/
|
||||
|
||||
if (!db_cl)
|
||||
db_cl= get_default_db_collation(thd, db_name);
|
||||
|
||||
/* Create the context. */
|
||||
|
||||
if (likely(ctx))
|
||||
*ctx= new Event_creation_ctx(client_cs, connection_cl, db_cl);
|
||||
|
||||
return invalid_creation_ctx;
|
||||
}
|
||||
|
||||
|
||||
static const
|
||||
TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
|
||||
{
|
||||
{
|
||||
{ STRING_WITH_LEN("db") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("name") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("body") },
|
||||
{ STRING_WITH_LEN("longblob") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("definer") },
|
||||
{ STRING_WITH_LEN("varchar(") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("execute_at") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("interval_value") },
|
||||
{ STRING_WITH_LEN("int(11)") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("interval_field") },
|
||||
{ STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
|
||||
"'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
|
||||
"'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
|
||||
"'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
|
||||
"'SECOND_MICROSECOND')") + CAN_BE_NULL},
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("created") },
|
||||
{ STRING_WITH_LEN("timestamp") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("modified") },
|
||||
{ STRING_WITH_LEN("timestamp") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("last_executed") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("starts") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("ends") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("status") },
|
||||
{ STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("on_completion") },
|
||||
{ STRING_WITH_LEN("enum('DROP','PRESERVE')") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("sql_mode") },
|
||||
{ STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
|
||||
"'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
|
||||
"'NO_UNSIGNED_SUBTRACTION',"
|
||||
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
|
||||
"'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
|
||||
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
|
||||
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
|
||||
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
|
||||
"'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
|
||||
"'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("comment") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("originator") },
|
||||
{ STRING_WITH_LEN("int(10)") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("time_zone") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("latin1") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("character_set_client") },
|
||||
{ STRING_WITH_LEN("char(32)") + CAN_BE_NULL },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("collation_connection") },
|
||||
{ STRING_WITH_LEN("char(") + CAN_BE_NULL },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("db_collation") },
|
||||
{ STRING_WITH_LEN("char(") + CAN_BE_NULL },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("body_utf8") },
|
||||
{ STRING_WITH_LEN("longblob") + CAN_BE_NULL },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("kind") },
|
||||
{ STRING_WITH_LEN("set('SCHEDULE','STARTUP','SHUTDOWN',"
|
||||
"'LOGON','LOGOFF','DDL')") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("when") },
|
||||
{ STRING_WITH_LEN("enum('BEFORE','AFTER')") + CAN_BE_NULL },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("ddl_type") },
|
||||
{ STRING_WITH_LEN("set('CREATE','ALTER','DROP','TRUNCATE',"
|
||||
"'ANALYZE','RENAME','GRANT','REVOKE',"
|
||||
"'COMMENT','ANY_DDL')") + CAN_BE_NULL },
|
||||
{ NULL, 0 }
|
||||
}
|
||||
};
|
||||
|
||||
LEX_CSTRING
|
||||
Event_db_repository_common::MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") };
|
||||
|
||||
|
||||
const TABLE_FIELD_DEF
|
||||
Event_db_repository_common::event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
|
||||
|
||||
const TABLE_FIELD_DEF
|
||||
Event_db_repository_common::event_table_def_wo_trg= {ET_FIELD_COUNT_WO_TRG,
|
||||
event_table_fields,
|
||||
0, (uint*) 0};
|
||||
|
||||
/**
|
||||
Open mysql.event table for read.
|
||||
|
||||
It's assumed that the caller knows what they are doing:
|
||||
- whether it was necessary to reset-and-backup the open tables state
|
||||
- whether the requested lock does not lead to a deadlock
|
||||
- whether this open mode would work under LOCK TABLES, or inside a
|
||||
stored function or trigger.
|
||||
|
||||
Note that if the table can't be locked successfully this operation will
|
||||
close it. Therefore it provides guarantee that it either opens and locks
|
||||
table or fails without leaving any tables open.
|
||||
|
||||
@param[in] thd Thread context
|
||||
@param[in] lock_type How to lock the table
|
||||
@param[out] table We will store the open table here
|
||||
@param[out] enable_sys_trg True on return in case system triggers supported,
|
||||
else false
|
||||
|
||||
@retval TRUE open and lock failed - an error message is pushed into the
|
||||
stack
|
||||
@retval FALSE success
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_db_repository_common::open_event_table(THD *thd,
|
||||
enum thr_lock_type lock_type,
|
||||
TABLE **table,
|
||||
bool *enable_sys_trg)
|
||||
{
|
||||
TABLE_LIST tables;
|
||||
DBUG_ENTER("Event_db_repository::open_event_table");
|
||||
|
||||
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type);
|
||||
|
||||
if (open_and_lock_tables(thd, &tables, false, MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||
{
|
||||
*enable_sys_trg= false;
|
||||
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
*table= tables.table;
|
||||
tables.table->use_all_columns();
|
||||
/* NOTE: &tables pointer will be invalid after return */
|
||||
tables.table->pos_in_table_list= NULL;
|
||||
|
||||
if (check_event_table_for_validness(*table, enable_sys_trg))
|
||||
{
|
||||
thd->commit_whole_transaction_and_close_tables();
|
||||
*table= 0; // Table is now closed
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Open mysql.event and check whether mysql.event exists and is up to date
|
||||
(or from a newer version of MariaDB)
|
||||
|
||||
This function is called only when the server is started.
|
||||
@pre The passed in thread handle has no open tables.
|
||||
|
||||
@retval FALSE OK
|
||||
@retval TRUE Error, an error message is output to the error log.
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_db_repository_common::check_system_tables(THD *thd)
|
||||
{
|
||||
TABLE_LIST tables;
|
||||
bool ret= false;
|
||||
DBUG_ENTER("Event_db_repository::check_system_tables");
|
||||
DBUG_PRINT("enter", ("thd: %p", thd));
|
||||
|
||||
/* Check mysql.event */
|
||||
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
|
||||
|
||||
if (open_and_lock_tables(thd, &tables, false, MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||
{
|
||||
ret= true;
|
||||
sql_print_error("Cannot open mysql.event");
|
||||
}
|
||||
else
|
||||
{
|
||||
bool system_triggers_enabled __attribute__((unused));
|
||||
ret= check_event_table_for_validness(tables.table,
|
||||
&system_triggers_enabled);
|
||||
|
||||
close_mysql_tables(thd);
|
||||
}
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Create a new query string for removing executable comments
|
||||
for avoiding leak and keeping consistency of the execution
|
||||
on master and slave.
|
||||
|
||||
@param[in] thd Thread handler
|
||||
@param[in] buf Query string
|
||||
|
||||
@return false on success, true on error
|
||||
*/
|
||||
|
||||
bool
|
||||
create_query_string(THD *thd, String *buf)
|
||||
{
|
||||
buf->length(0);
|
||||
/* Append the "CREATE" part of the query */
|
||||
if (thd->lex->create_info.or_replace())
|
||||
{
|
||||
if (buf->append(STRING_WITH_LEN("CREATE OR REPLACE ")))
|
||||
return true;
|
||||
}
|
||||
else if (buf->append(STRING_WITH_LEN("CREATE ")))
|
||||
return true;
|
||||
/* Append definer */
|
||||
append_definer(thd, buf, &(thd->lex->definer->user),
|
||||
&(thd->lex->definer->host));
|
||||
/* Append the left part of thd->query after "DEFINER" part */
|
||||
if (buf->append(thd->lex->stmt_definition_begin,
|
||||
thd->lex->stmt_definition_end -
|
||||
thd->lex->stmt_definition_begin))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check structure of the mysql.event table for validness - the table
|
||||
must contains either all columns required for support of events or
|
||||
all columns required for event plus columns required for support of
|
||||
system triggers
|
||||
|
||||
@param ev opened mysql.event table
|
||||
|
||||
@return false in case structure of the table mysql.event is valid,
|
||||
else return true;
|
||||
*/
|
||||
|
||||
bool Event_db_repository_common::check_event_table_for_validness(
|
||||
TABLE *ev,
|
||||
bool *system_triggers_enabled)
|
||||
{
|
||||
/*
|
||||
First, check that the most probable case: the table mysql.event
|
||||
contains all columns required for support of system triggers, that is
|
||||
in presence all columns of the table mysql.event version 13.1 plus
|
||||
extra columns added for storing system triggers metadata
|
||||
*/
|
||||
if (table_intact.check(ev, &event_table_def))
|
||||
{
|
||||
/*
|
||||
Disable system triggers since the base structure of the table
|
||||
mysql.event misses mandatory columns
|
||||
*/
|
||||
*system_triggers_enabled= false;
|
||||
|
||||
/*
|
||||
Check that the core subset of mysql.event table's columns are present.
|
||||
*/
|
||||
return table_intact.check(ev, &event_table_def_wo_trg);
|
||||
}
|
||||
*system_triggers_enabled= true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Event_table_check_intact_log_error Event_db_repository_common::table_intact;
|
||||
ulong Events_common::startup_state= Events_common::EVENTS_OFF;
|
||||
93
sql/event_common.h
Normal file
93
sql/event_common.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#ifndef _EVENT_COMMON_H_
|
||||
#define _EVENT_COMMON_H_
|
||||
|
||||
#include "sp_head.h" // Stored_program_creation_ctx
|
||||
#include "sql_alloc.h" // Sql_alloc
|
||||
|
||||
/**
|
||||
Event_creation_ctx -- creation context of events.
|
||||
*/
|
||||
|
||||
class Event_creation_ctx :public Stored_program_creation_ctx,
|
||||
public Sql_alloc
|
||||
{
|
||||
public:
|
||||
static bool load_from_db(THD *thd,
|
||||
MEM_ROOT *event_mem_root,
|
||||
const char *db_name,
|
||||
const char *event_name,
|
||||
TABLE *event_tbl,
|
||||
Stored_program_creation_ctx **ctx);
|
||||
|
||||
public:
|
||||
Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) override
|
||||
{
|
||||
return new (mem_root)
|
||||
Event_creation_ctx(m_client_cs, m_connection_cl, m_db_cl);
|
||||
}
|
||||
|
||||
protected:
|
||||
Object_creation_ctx *create_backup_ctx(THD *thd) const override
|
||||
{
|
||||
/*
|
||||
We can avoid usual backup/restore employed in stored programs since we
|
||||
know that this is a top level statement and the worker thread is
|
||||
allocated exclusively to execute this event.
|
||||
*/
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
Event_creation_ctx(CHARSET_INFO *client_cs,
|
||||
CHARSET_INFO *connection_cl,
|
||||
CHARSET_INFO *db_cl)
|
||||
: Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
|
||||
{ }
|
||||
};
|
||||
|
||||
class Event_table_check_intact_log_error : public Table_check_intact_log_error
|
||||
{
|
||||
public:
|
||||
bool handle_event_table() const override { return true; }
|
||||
};
|
||||
|
||||
class Event_db_repository_common
|
||||
{
|
||||
public:
|
||||
static bool open_event_table(THD *thd, enum thr_lock_type lock_type,
|
||||
TABLE **table,
|
||||
bool *enable_sys_trg);
|
||||
|
||||
static bool check_system_tables(THD *thd);
|
||||
|
||||
/** In case of an error, a message is printed to the error log. */
|
||||
static Event_table_check_intact_log_error table_intact;
|
||||
|
||||
static LEX_CSTRING MYSQL_EVENT_NAME;
|
||||
|
||||
static const TABLE_FIELD_DEF event_table_def;
|
||||
|
||||
static const TABLE_FIELD_DEF event_table_def_wo_trg;
|
||||
|
||||
static bool check_event_table_for_validness(TABLE *ev,
|
||||
bool *system_triggers_enabled);
|
||||
};
|
||||
|
||||
class Events_common
|
||||
{
|
||||
public:
|
||||
static ulong startup_state;
|
||||
|
||||
/*
|
||||
the following block is to support --event-scheduler command line option
|
||||
and the @@global.event_scheduler SQL variable.
|
||||
See sys_var.cc
|
||||
*/
|
||||
enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED,
|
||||
EVENTS_ORIGINAL };
|
||||
};
|
||||
|
||||
bool create_query_string(THD *thd, String *buf);
|
||||
|
||||
#endif
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
|
||||
#include "sp.h" // load_charset, load_collation
|
||||
#include "events.h"
|
||||
#include "event_common.h" // Event_creation_ctx
|
||||
#include "event_data_objects.h"
|
||||
#include "event_db_repository.h"
|
||||
#include "sp_head.h"
|
||||
@@ -55,124 +56,6 @@ PSI_statement_info Event_queue_element_for_exec::psi_info=
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
Event_creation_ctx -- creation context of events.
|
||||
*/
|
||||
|
||||
class Event_creation_ctx :public Stored_program_creation_ctx,
|
||||
public Sql_alloc
|
||||
{
|
||||
public:
|
||||
static bool load_from_db(THD *thd,
|
||||
MEM_ROOT *event_mem_root,
|
||||
const char *db_name,
|
||||
const char *event_name,
|
||||
TABLE *event_tbl,
|
||||
Stored_program_creation_ctx **ctx);
|
||||
|
||||
public:
|
||||
Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) override
|
||||
{
|
||||
return new (mem_root)
|
||||
Event_creation_ctx(m_client_cs, m_connection_cl, m_db_cl);
|
||||
}
|
||||
|
||||
protected:
|
||||
Object_creation_ctx *create_backup_ctx(THD *thd) const override
|
||||
{
|
||||
/*
|
||||
We can avoid usual backup/restore employed in stored programs since we
|
||||
know that this is a top level statement and the worker thread is
|
||||
allocated exclusively to execute this event.
|
||||
*/
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
Event_creation_ctx(CHARSET_INFO *client_cs,
|
||||
CHARSET_INFO *connection_cl,
|
||||
CHARSET_INFO *db_cl)
|
||||
: Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
Event_creation_ctx implementation.
|
||||
**************************************************************************/
|
||||
|
||||
bool
|
||||
Event_creation_ctx::load_from_db(THD *thd,
|
||||
MEM_ROOT *event_mem_root,
|
||||
const char *db_name,
|
||||
const char *event_name,
|
||||
TABLE *event_tbl,
|
||||
Stored_program_creation_ctx **ctx)
|
||||
{
|
||||
/* Load character set/collation attributes. */
|
||||
|
||||
CHARSET_INFO *client_cs;
|
||||
CHARSET_INFO *connection_cl;
|
||||
CHARSET_INFO *db_cl;
|
||||
|
||||
bool invalid_creation_ctx= FALSE;
|
||||
|
||||
if (load_charset(thd, event_mem_root,
|
||||
event_tbl->field[ET_FIELD_CHARACTER_SET_CLIENT],
|
||||
thd->variables.character_set_client,
|
||||
&client_cs))
|
||||
{
|
||||
sql_print_warning("Event '%s'.'%s': invalid value "
|
||||
"in column mysql.event.character_set_client.",
|
||||
(const char *) db_name,
|
||||
(const char *) event_name);
|
||||
|
||||
invalid_creation_ctx= TRUE;
|
||||
}
|
||||
|
||||
if (load_collation(thd, event_mem_root,
|
||||
event_tbl->field[ET_FIELD_COLLATION_CONNECTION],
|
||||
thd->variables.collation_connection,
|
||||
&connection_cl))
|
||||
{
|
||||
sql_print_warning("Event '%s'.'%s': invalid value "
|
||||
"in column mysql.event.collation_connection.",
|
||||
(const char *) db_name,
|
||||
(const char *) event_name);
|
||||
|
||||
invalid_creation_ctx= TRUE;
|
||||
}
|
||||
|
||||
if (load_collation(thd, event_mem_root,
|
||||
event_tbl->field[ET_FIELD_DB_COLLATION],
|
||||
NULL,
|
||||
&db_cl))
|
||||
{
|
||||
sql_print_warning("Event '%s'.'%s': invalid value "
|
||||
"in column mysql.event.db_collation.",
|
||||
(const char *) db_name,
|
||||
(const char *) event_name);
|
||||
|
||||
invalid_creation_ctx= TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
If we failed to resolve the database collation, load the default one
|
||||
from the disk.
|
||||
*/
|
||||
|
||||
if (!db_cl)
|
||||
db_cl= get_default_db_collation(thd, db_name);
|
||||
|
||||
/* Create the context. */
|
||||
|
||||
*ctx= new Event_creation_ctx(client_cs, connection_cl, db_cl);
|
||||
|
||||
return invalid_creation_ctx;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/*
|
||||
Initializes dbname and name of an Event_queue_element_for_exec
|
||||
object
|
||||
@@ -273,7 +156,7 @@ Event_basic::load_string_fields(Field **fields, ...)
|
||||
|
||||
va_start(args, fields);
|
||||
field_name= (enum enum_events_table_field) va_arg(args, int);
|
||||
while (field_name < ET_FIELD_COUNT)
|
||||
while (field_name < ET_FIELD_KIND)
|
||||
{
|
||||
field_value= va_arg(args, LEX_CSTRING *);
|
||||
if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
|
||||
@@ -403,7 +286,8 @@ Event_timed::init()
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_job_data::load_from_row(THD *thd, TABLE *table)
|
||||
Event_job_data::load_from_row(THD *thd, TABLE *table,
|
||||
bool sys_triggers_enabled)
|
||||
{
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
@@ -414,7 +298,7 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
|
||||
if (!table)
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (table->s->fields < ET_FIELD_COUNT)
|
||||
if (table->s->fields < ET_FIELD_KIND)
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_string_fields(table->field,
|
||||
@@ -423,7 +307,7 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
|
||||
ET_FIELD_BODY, &body,
|
||||
ET_FIELD_DEFINER, &definer,
|
||||
ET_FIELD_TIME_ZONE, &tz_name,
|
||||
ET_FIELD_COUNT))
|
||||
ET_FIELD_KIND))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_time_zone(thd, &tz_name))
|
||||
@@ -464,7 +348,8 @@ Event_job_data::load_from_row(THD *thd, TABLE *table)
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
||||
Event_queue_element::load_from_row(THD *thd, TABLE *table,
|
||||
bool sys_triggers_enabled)
|
||||
{
|
||||
const char *ptr;
|
||||
MYSQL_TIME time;
|
||||
@@ -475,15 +360,26 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
||||
if (!table)
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (table->s->fields < ET_FIELD_COUNT)
|
||||
if (table->s->fields < ET_FIELD_KIND)
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (sys_triggers_enabled)
|
||||
{
|
||||
if (is_sys_trigger_row(table))
|
||||
{
|
||||
trigger_event= true;
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
trigger_event= false;
|
||||
|
||||
if (load_string_fields(table->field,
|
||||
ET_FIELD_DB, &dbname,
|
||||
ET_FIELD_NAME, &name,
|
||||
ET_FIELD_DEFINER, &definer,
|
||||
ET_FIELD_TIME_ZONE, &tz_name,
|
||||
ET_FIELD_COUNT))
|
||||
ET_FIELD_KIND))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (load_time_zone(thd, &tz_name))
|
||||
@@ -612,20 +508,27 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_timed::load_from_row(THD *thd, TABLE *table)
|
||||
Event_timed::load_from_row(THD *thd, TABLE *table, bool sys_triggers_enabled)
|
||||
{
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
|
||||
DBUG_ENTER("Event_timed::load_from_row");
|
||||
|
||||
if (Event_queue_element::load_from_row(thd, table))
|
||||
if (Event_queue_element::load_from_row(thd, table, sys_triggers_enabled))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (trigger_event)
|
||||
/*
|
||||
Don't do loading of event metadata in case the current row represents
|
||||
a trigger.
|
||||
*/
|
||||
DBUG_RETURN(false);
|
||||
|
||||
if (load_string_fields(table->field,
|
||||
ET_FIELD_BODY, &body,
|
||||
ET_FIELD_BODY_UTF8, &body_utf8,
|
||||
ET_FIELD_COUNT))
|
||||
ET_FIELD_KIND))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str,
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
virtual ~Event_basic();
|
||||
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table) = 0;
|
||||
load_from_row(THD *thd, TABLE *table, bool sys_triggers_enabled) = 0;
|
||||
|
||||
protected:
|
||||
bool
|
||||
@@ -111,13 +111,20 @@ public:
|
||||
|
||||
bool dropped;
|
||||
|
||||
/**
|
||||
This flag indicates whether the loaded object represents a record
|
||||
containing trigger or event metadata. True in case the loaded record of
|
||||
the table mysq.event represents trigger, false for the real event.
|
||||
*/
|
||||
bool trigger_event= {false};
|
||||
|
||||
uint execution_count;
|
||||
|
||||
Event_queue_element();
|
||||
virtual ~Event_queue_element();
|
||||
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table) override;
|
||||
load_from_row(THD *thd, TABLE *table, bool sys_triggers_enabled) override;
|
||||
|
||||
bool
|
||||
compute_next_execution_time();
|
||||
@@ -155,7 +162,7 @@ public:
|
||||
init();
|
||||
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table) override;
|
||||
load_from_row(THD *thd, TABLE *table, bool sys_triggers_enabled) override;
|
||||
|
||||
int
|
||||
get_create_event(THD *thd, String *buf);
|
||||
@@ -176,7 +183,8 @@ public:
|
||||
Event_job_data();
|
||||
|
||||
virtual bool
|
||||
load_from_row(THD *thd, TABLE *table) override;
|
||||
load_from_row(THD *thd, TABLE *table,
|
||||
bool sys_triggers_enabled __attribute__((unused))) override;
|
||||
|
||||
bool
|
||||
execute(THD *thd, bool drop);
|
||||
|
||||
@@ -37,160 +37,6 @@
|
||||
@{
|
||||
*/
|
||||
|
||||
static
|
||||
const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
|
||||
{
|
||||
{
|
||||
{ STRING_WITH_LEN("db") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("name") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("body") },
|
||||
{ STRING_WITH_LEN("longblob") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("definer") },
|
||||
{ STRING_WITH_LEN("varchar(") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("execute_at") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("interval_value") },
|
||||
{ STRING_WITH_LEN("int(11)") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("interval_field") },
|
||||
{ STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
|
||||
"'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
|
||||
"'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
|
||||
"'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
|
||||
"'SECOND_MICROSECOND')") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("created") },
|
||||
{ STRING_WITH_LEN("timestamp") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("modified") },
|
||||
{ STRING_WITH_LEN("timestamp") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("last_executed") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("starts") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("ends") },
|
||||
{ STRING_WITH_LEN("datetime") + CAN_BE_NULL },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("status") },
|
||||
{ STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("on_completion") },
|
||||
{ STRING_WITH_LEN("enum('DROP','PRESERVE')") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("sql_mode") },
|
||||
{ STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
|
||||
"'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY',"
|
||||
"'NO_UNSIGNED_SUBTRACTION',"
|
||||
"'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB',"
|
||||
"'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40',"
|
||||
"'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES',"
|
||||
"'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',"
|
||||
"'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
|
||||
"'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH',"
|
||||
"'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("comment") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("originator") },
|
||||
{ STRING_WITH_LEN("int(10)") },
|
||||
{NULL, 0}
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("time_zone") },
|
||||
{ STRING_WITH_LEN("char(64)") },
|
||||
{ STRING_WITH_LEN("latin1") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("character_set_client") },
|
||||
{ STRING_WITH_LEN("char(32)") + CAN_BE_NULL },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("collation_connection") },
|
||||
{ STRING_WITH_LEN("char(") + CAN_BE_NULL },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("db_collation") },
|
||||
{ STRING_WITH_LEN("char(") + CAN_BE_NULL },
|
||||
{ STRING_WITH_LEN("utf8mb") }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("body_utf8") },
|
||||
{ STRING_WITH_LEN("longblob") + CAN_BE_NULL },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("kind") },
|
||||
{ STRING_WITH_LEN("set('SCHEDULE','STARTUP','SHUTDOWN',"
|
||||
"'LOGON','LOGOFF','DDL')") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("when") },
|
||||
{ STRING_WITH_LEN("enum('BEFORE','AFTER')") },
|
||||
{ NULL, 0 }
|
||||
},
|
||||
{
|
||||
{ STRING_WITH_LEN("ddl_type") },
|
||||
{ STRING_WITH_LEN("set('CREATE','ALTER','DROP','TRUNCATE',"
|
||||
"'ANALYZE','RENAME','GRANT','REVOKE')") },
|
||||
{ NULL, 0 }
|
||||
}
|
||||
};
|
||||
|
||||
static LEX_CSTRING MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") };
|
||||
|
||||
static const TABLE_FIELD_DEF
|
||||
event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
|
||||
|
||||
/** In case of an error, a message is printed to the error log. */
|
||||
static Table_check_intact_log_error table_intact;
|
||||
|
||||
|
||||
/**
|
||||
Puts some data common to CREATE and ALTER EVENT into a row.
|
||||
|
||||
@@ -226,7 +72,7 @@ mysql_event_fill_row(THD *thd,
|
||||
|
||||
DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT);
|
||||
|
||||
if (table->s->fields < ET_FIELD_COUNT)
|
||||
if (table->s->fields < ET_FIELD_KIND)
|
||||
{
|
||||
/*
|
||||
Safety: this can only happen if someone started the server
|
||||
@@ -234,7 +80,7 @@ mysql_event_fill_row(THD *thd,
|
||||
*/
|
||||
my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, MYF(0),
|
||||
table->s->db.str, table->alias.c_ptr(),
|
||||
(int) ET_FIELD_COUNT, table->s->fields);
|
||||
(int) ET_FIELD_KIND, table->s->fields);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
@@ -414,7 +260,8 @@ err_truncate:
|
||||
bool
|
||||
Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
|
||||
TABLE *event_table,
|
||||
const char *db)
|
||||
const char *db,
|
||||
bool sys_triggers_enabled)
|
||||
{
|
||||
CHARSET_INFO *scs= system_charset_info;
|
||||
KEY *key_info;
|
||||
@@ -461,7 +308,8 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
|
||||
DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
|
||||
do
|
||||
{
|
||||
ret= copy_event_to_schema_table(thd, schema_table, event_table);
|
||||
ret= copy_event_to_schema_table(thd, schema_table, event_table,
|
||||
sys_triggers_enabled);
|
||||
if (ret == 0)
|
||||
ret= event_table->file->ha_index_next_same(event_table->record[0],
|
||||
key_buf, key_len);
|
||||
@@ -498,7 +346,8 @@ end:
|
||||
|
||||
bool
|
||||
Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
|
||||
TABLE *event_table)
|
||||
TABLE *event_table,
|
||||
bool sys_triggers_enabled)
|
||||
{
|
||||
int ret;
|
||||
READ_RECORD read_record_info;
|
||||
@@ -517,7 +366,8 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
|
||||
{
|
||||
ret= read_record_info.read_record();
|
||||
if (ret == 0)
|
||||
ret= copy_event_to_schema_table(thd, schema_table, event_table);
|
||||
ret= copy_event_to_schema_table(thd, schema_table, event_table,
|
||||
sys_triggers_enabled);
|
||||
} while (ret == 0);
|
||||
|
||||
DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
|
||||
@@ -562,7 +412,9 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (table_intact.check(event_table.table, &event_table_def))
|
||||
bool system_triggers_enabled;
|
||||
if (check_event_table_for_validness(event_table.table,
|
||||
&system_triggers_enabled))
|
||||
{
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
ret= 1;
|
||||
@@ -579,9 +431,11 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
|
||||
every single row's `db` with the schema which we show.
|
||||
*/
|
||||
if (db)
|
||||
ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
|
||||
ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db,
|
||||
system_triggers_enabled);
|
||||
else
|
||||
ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
|
||||
ret= table_scan_all_for_i_s(thd, schema_table, event_table.table,
|
||||
system_triggers_enabled);
|
||||
|
||||
err:
|
||||
thd->commit_whole_transaction_and_close_tables();
|
||||
@@ -592,57 +446,6 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Open mysql.event table for read.
|
||||
|
||||
It's assumed that the caller knows what they are doing:
|
||||
- whether it was necessary to reset-and-backup the open tables state
|
||||
- whether the requested lock does not lead to a deadlock
|
||||
- whether this open mode would work under LOCK TABLES, or inside a
|
||||
stored function or trigger.
|
||||
|
||||
Note that if the table can't be locked successfully this operation will
|
||||
close it. Therefore it provides guarantee that it either opens and locks
|
||||
table or fails without leaving any tables open.
|
||||
|
||||
@param[in] thd Thread context
|
||||
@param[in] lock_type How to lock the table
|
||||
@param[out] table We will store the open table here
|
||||
|
||||
@retval TRUE open and lock failed - an error message is pushed into the
|
||||
stack
|
||||
@retval FALSE success
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
|
||||
TABLE **table)
|
||||
{
|
||||
TABLE_LIST tables;
|
||||
DBUG_ENTER("Event_db_repository::open_event_table");
|
||||
|
||||
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type);
|
||||
|
||||
if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
*table= tables.table;
|
||||
tables.table->use_all_columns();
|
||||
/* NOTE: &tables pointer will be invalid after return */
|
||||
tables.table->pos_in_table_list= NULL;
|
||||
|
||||
if (table_intact.check(*table, &event_table_def))
|
||||
{
|
||||
thd->commit_whole_transaction_and_close_tables();
|
||||
*table= 0; // Table is now closed
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Creates an event record in mysql.event table.
|
||||
|
||||
@@ -687,15 +490,26 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
|
||||
/* Reset sql_mode during data dictionary operations. */
|
||||
thd->variables.sql_mode= 0;
|
||||
|
||||
if (open_event_table(thd, TL_WRITE, &table))
|
||||
bool sys_triggers_enabled;
|
||||
if (open_event_table(thd, TL_WRITE, &table, &sys_triggers_enabled))
|
||||
goto end;
|
||||
|
||||
DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
|
||||
parse_data->name.str));
|
||||
|
||||
DBUG_PRINT("info", ("check existence of an event with the same name"));
|
||||
if (!find_named_event(&parse_data->dbname, &parse_data->name, table))
|
||||
if (!find_named_event(&parse_data->dbname, &parse_data->name, table, false,
|
||||
sys_triggers_enabled))
|
||||
{
|
||||
if (sys_triggers_enabled)
|
||||
{
|
||||
if (is_sys_trigger_row(table))
|
||||
{
|
||||
my_error(ER_TRG_EVENT_CONFLICTS_NAME, MYF(0),
|
||||
"Trigger", parse_data->name.str);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (thd->lex->create_info.or_replace())
|
||||
{
|
||||
*event_already_exists= false; // Force the caller to update event_queue
|
||||
@@ -815,7 +629,8 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
|
||||
/* Reset sql_mode during data dictionary operations. */
|
||||
thd->variables.sql_mode= 0;
|
||||
|
||||
if (open_event_table(thd, TL_WRITE, &table))
|
||||
bool sys_triggers_enabled;
|
||||
if (open_event_table(thd, TL_WRITE, &table, &sys_triggers_enabled))
|
||||
goto end;
|
||||
|
||||
DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
|
||||
@@ -826,9 +641,20 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
|
||||
if (new_name)
|
||||
{
|
||||
DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
|
||||
if (!find_named_event(new_dbname, new_name, table))
|
||||
if (!find_named_event(new_dbname, new_name, table, false,
|
||||
sys_triggers_enabled))
|
||||
{
|
||||
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
|
||||
if (sys_triggers_enabled)
|
||||
{
|
||||
if (is_sys_trigger_row(table))
|
||||
my_error(ER_TRG_EVENT_CONFLICTS_NAME, MYF(0),
|
||||
"Trigger", new_name->str);
|
||||
else
|
||||
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
|
||||
}
|
||||
else
|
||||
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
@@ -838,7 +664,8 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
|
||||
overwrite the key and SE will tell us that it cannot find the already found
|
||||
row (copied into record[1] later
|
||||
*/
|
||||
if (find_named_event(&parse_data->dbname, &parse_data->name, table))
|
||||
if (find_named_event(&parse_data->dbname, &parse_data->name, table, true,
|
||||
sys_triggers_enabled))
|
||||
{
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
|
||||
goto end;
|
||||
@@ -919,10 +746,11 @@ Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db,
|
||||
DBUG_ENTER("Event_db_repository::drop_event");
|
||||
DBUG_PRINT("enter", ("%s@%s", db->str, name->str));
|
||||
|
||||
if (open_event_table(thd, TL_WRITE, &table))
|
||||
bool sys_triggers_enabled;
|
||||
if (open_event_table(thd, TL_WRITE, &table, &sys_triggers_enabled))
|
||||
goto end;
|
||||
|
||||
if (!find_named_event(db, name, table))
|
||||
if (!find_named_event(db, name, table, true, sys_triggers_enabled))
|
||||
{
|
||||
if ((ret= table->file->ha_delete_row(table->record[0])))
|
||||
table->file->print_error(ret, MYF(0));
|
||||
@@ -959,7 +787,10 @@ end:
|
||||
@param[in] db database name
|
||||
@param[in] name event name
|
||||
@param[in,out] table mysql.event table
|
||||
|
||||
@param[in] check_kind_match whether to check the event is the trigger
|
||||
and not the real event
|
||||
@param[in] sys_triggers_enabled true in case system triggers enabled,
|
||||
else false
|
||||
|
||||
@retval FALSE an event with such db/name key exists
|
||||
@retval TRUE no record found or an error occurred.
|
||||
@@ -968,7 +799,9 @@ end:
|
||||
bool
|
||||
Event_db_repository::find_named_event(const LEX_CSTRING *db,
|
||||
const LEX_CSTRING *name,
|
||||
TABLE *table)
|
||||
TABLE *table,
|
||||
bool check_kind_match,
|
||||
bool sys_triggers_enabled)
|
||||
{
|
||||
uchar key[MAX_KEY_LENGTH];
|
||||
DBUG_ENTER("Event_db_repository::find_named_event");
|
||||
@@ -1002,6 +835,11 @@ Event_db_repository::find_named_event(const LEX_CSTRING *db,
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (sys_triggers_enabled &&
|
||||
check_kind_match &&
|
||||
is_sys_trigger_row(table))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
DBUG_PRINT("info", ("Row found!"));
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
@@ -1028,7 +866,8 @@ Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
|
||||
|
||||
start_new_trans new_trans(thd);
|
||||
|
||||
if (open_event_table(thd, TL_WRITE, &table))
|
||||
bool sys_triggers_enabled __attribute__((unused));
|
||||
if (open_event_table(thd, TL_WRITE, &table, &sys_triggers_enabled))
|
||||
{
|
||||
new_trans.restore_old_transaction();
|
||||
DBUG_VOID_RETURN;
|
||||
@@ -1102,17 +941,21 @@ Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
|
||||
*/
|
||||
if (!(ret= open_system_tables_for_read(thd, &event_table)))
|
||||
{
|
||||
if (table_intact.check(event_table.table, &event_table_def))
|
||||
bool system_triggers_enabled;
|
||||
if (check_event_table_for_validness(event_table.table,
|
||||
&system_triggers_enabled))
|
||||
{
|
||||
thd->commit_whole_transaction_and_close_tables();
|
||||
new_trans.restore_old_transaction();
|
||||
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
if ((ret= find_named_event(dbname, name, event_table.table)))
|
||||
if ((ret= find_named_event(dbname, name, event_table.table, true,
|
||||
system_triggers_enabled)))
|
||||
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str);
|
||||
else if ((ret= etn->load_from_row(thd, event_table.table)))
|
||||
else if ((ret= etn->load_from_row(thd, event_table.table,
|
||||
system_triggers_enabled)))
|
||||
my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
|
||||
thd->commit_whole_transaction_and_close_tables();
|
||||
}
|
||||
@@ -1151,7 +994,8 @@ update_timing_fields_for_event(THD *thd,
|
||||
possible other locks taken by the caller.
|
||||
*/
|
||||
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
|
||||
if (open_event_table(thd, TL_WRITE, &table))
|
||||
bool sys_triggers_enabled;
|
||||
if (open_event_table(thd, TL_WRITE, &table, &sys_triggers_enabled))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
fields= table->field;
|
||||
@@ -1161,7 +1005,8 @@ update_timing_fields_for_event(THD *thd,
|
||||
*/
|
||||
table->file->row_logging= 0;
|
||||
|
||||
if (find_named_event(event_db_name, event_name, table))
|
||||
if (find_named_event(event_db_name, event_name, table, true,
|
||||
sys_triggers_enabled))
|
||||
goto end;
|
||||
|
||||
store_record(table, record[1]);
|
||||
@@ -1189,46 +1034,6 @@ end:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Open mysql.db, mysql.user and mysql.event and check whether:
|
||||
- mysql.db exists and is up to date (or from a newer version of MySQL),
|
||||
- mysql.user has column Event_priv at an expected position,
|
||||
- mysql.event exists and is up to date (or from a newer version of
|
||||
MySQL)
|
||||
|
||||
This function is called only when the server is started.
|
||||
@pre The passed in thread handle has no open tables.
|
||||
|
||||
@retval FALSE OK
|
||||
@retval TRUE Error, an error message is output to the error log.
|
||||
*/
|
||||
|
||||
bool
|
||||
Event_db_repository::check_system_tables(THD *thd)
|
||||
{
|
||||
TABLE_LIST tables;
|
||||
int ret= FALSE;
|
||||
DBUG_ENTER("Event_db_repository::check_system_tables");
|
||||
DBUG_PRINT("enter", ("thd: %p", thd));
|
||||
|
||||
/* Check mysql.event */
|
||||
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
|
||||
|
||||
if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||
{
|
||||
ret= 1;
|
||||
sql_print_error("Cannot open mysql.event");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (table_intact.check(tables.table, &event_table_def))
|
||||
ret= 1;
|
||||
close_mysql_tables(thd);
|
||||
}
|
||||
|
||||
DBUG_RETURN(MY_TEST(ret));
|
||||
}
|
||||
|
||||
/**
|
||||
@} (End of group Event_Scheduler)
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#ifndef _EVENT_DB_REPOSITORY_H_
|
||||
#define _EVENT_DB_REPOSITORY_H_
|
||||
|
||||
#include "event_common.h"
|
||||
#include "event_parse_data.h"
|
||||
|
||||
/* Copyright (c) 2006, 2011, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -56,7 +60,12 @@ enum enum_events_table_field
|
||||
ET_FIELD_KIND,
|
||||
ET_FIELD_WHEN,
|
||||
ET_FIELD_DDL_TYPE,
|
||||
ET_FIELD_COUNT /* a cool trick to count the number of fields :) */
|
||||
ET_FIELD_COUNT /* a cool trick to count the number of fields :) */,
|
||||
/*
|
||||
Number of columns necessary and sufficient for support of events.
|
||||
For such table layout, support of system triggers are disabled.
|
||||
*/
|
||||
ET_FIELD_COUNT_WO_TRG = ET_FIELD_COUNT - 3
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +80,7 @@ events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table);
|
||||
class Event_basic;
|
||||
class Event_parse_data;
|
||||
|
||||
class Event_db_repository
|
||||
class Event_db_repository : public Event_db_repository_common
|
||||
{
|
||||
public:
|
||||
Event_db_repository() = default;
|
||||
@@ -92,16 +101,14 @@ public:
|
||||
|
||||
bool
|
||||
find_named_event(const LEX_CSTRING *db, const LEX_CSTRING *name,
|
||||
TABLE *table);
|
||||
TABLE *table, bool check_kind_match,
|
||||
bool sys_triggers_enabled);
|
||||
|
||||
bool
|
||||
load_named_event(THD *thd, const LEX_CSTRING *dbname,
|
||||
const LEX_CSTRING *name,
|
||||
Event_basic *et);
|
||||
|
||||
static bool
|
||||
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
|
||||
|
||||
bool
|
||||
fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db);
|
||||
|
||||
@@ -111,16 +118,15 @@ public:
|
||||
const LEX_CSTRING *event_name,
|
||||
my_time_t last_executed,
|
||||
ulonglong status);
|
||||
public:
|
||||
static bool
|
||||
check_system_tables(THD *thd);
|
||||
|
||||
private:
|
||||
bool
|
||||
index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
|
||||
const char *db);
|
||||
const char *db, bool sys_triggers_enabled);
|
||||
|
||||
bool
|
||||
table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
|
||||
table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
|
||||
bool sys_triggers_enabled);
|
||||
|
||||
private:
|
||||
/* Prevent use of these */
|
||||
@@ -128,6 +134,23 @@ private:
|
||||
void operator=(Event_db_repository &);
|
||||
};
|
||||
|
||||
/**
|
||||
Check whether the current fetched row of the table mysql.event
|
||||
describes a system trigger, not an event
|
||||
|
||||
@param event_table Table descriptor for opened mysql.event table
|
||||
|
||||
@return true in case the `kind` field of the current row != SCHEDULE_EVENT,
|
||||
else false
|
||||
*/
|
||||
|
||||
static inline bool is_sys_trigger_row(TABLE *event_table)
|
||||
{
|
||||
Event_parse_data::enum_kind trg_kind=
|
||||
(Event_parse_data::enum_kind)event_table->field[ET_FIELD_KIND]->val_int();
|
||||
return trg_kind != Event_parse_data::SCHEDULE_EVENT;
|
||||
}
|
||||
|
||||
/**
|
||||
@} (End of group Event_Scheduler)
|
||||
*/
|
||||
|
||||
@@ -54,6 +54,12 @@ public:
|
||||
ON_COMPLETION_PRESERVE
|
||||
};
|
||||
|
||||
enum enum_kind
|
||||
{
|
||||
SCHEDULE_EVENT= 0x01, SYS_TRG_ON_STARTUP= 0x02, SYS_TRG_ON_SHUTDOWN= 0x04,
|
||||
SYS_TRG_ON_LOGON= 0x08, SYS_TRG_ON_LOGOFF= 0x10, SYS_TRG_ON_DDL= 0x20
|
||||
};
|
||||
|
||||
int on_completion;
|
||||
int status;
|
||||
bool status_changed;
|
||||
|
||||
@@ -83,7 +83,6 @@ Event_queue *Events::event_queue;
|
||||
Event_scheduler *Events::scheduler;
|
||||
Event_db_repository *Events::db_repository;
|
||||
ulong Events::opt_event_scheduler= Events::EVENTS_OFF;
|
||||
ulong Events::startup_state= Events::EVENTS_OFF;
|
||||
ulong Events::inited;
|
||||
|
||||
|
||||
@@ -110,6 +109,20 @@ int sortcmp_lex_string(const LEX_CSTRING *s, const LEX_CSTRING *t,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Return true for any sql command except SHOW EVENTS and SHOW CREATE EVENT
|
||||
statement; for SHOW EVENTS and SHOW CREATE EVENT commands return false.
|
||||
|
||||
@param sql_command command id
|
||||
*/
|
||||
|
||||
static inline bool is_not_show_event_related_cmd(enum_sql_command sql_command)
|
||||
{
|
||||
return sql_command != SQLCOM_SHOW_EVENTS &&
|
||||
sql_command != SQLCOM_SHOW_CREATE_EVENT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Push an error into the error stack if the system tables are
|
||||
not up to date.
|
||||
@@ -119,7 +132,15 @@ bool Events::check_if_system_tables_error()
|
||||
{
|
||||
DBUG_ENTER("Events::check_if_system_tables_error");
|
||||
|
||||
if (opt_noacl)
|
||||
/*
|
||||
check_if_system_tables_error() is called for every DDL, DML event-related
|
||||
commands (CREATE, ALTER, DROP, SHOW) including regular select from the
|
||||
table information_schema.events. In case server is run with the option
|
||||
--skip-grant-tables, any of events-related statements should be failed
|
||||
except SHOW EVENTS/SHOW CREATE EVENT that is read-only statements
|
||||
by its nature.
|
||||
*/
|
||||
if (opt_noacl && is_not_show_event_related_cmd(current_thd->lex->sql_command))
|
||||
{
|
||||
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
|
||||
opt_bootstrap ? "--bootstrap" : "--skip-grant-tables");
|
||||
@@ -269,42 +290,6 @@ common_1_lev_code:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Create a new query string for removing executable comments
|
||||
for avoiding leak and keeping consistency of the execution
|
||||
on master and slave.
|
||||
|
||||
@param[in] thd Thread handler
|
||||
@param[in] buf Query string
|
||||
|
||||
@return
|
||||
0 ok
|
||||
1 error
|
||||
*/
|
||||
static int
|
||||
create_query_string(THD *thd, String *buf)
|
||||
{
|
||||
buf->length(0);
|
||||
/* Append the "CREATE" part of the query */
|
||||
if (thd->lex->create_info.or_replace())
|
||||
{
|
||||
if (buf->append(STRING_WITH_LEN("CREATE OR REPLACE ")))
|
||||
return 1;
|
||||
}
|
||||
else if (buf->append(STRING_WITH_LEN("CREATE ")))
|
||||
return 1;
|
||||
/* Append definer */
|
||||
append_definer(thd, buf, &(thd->lex->definer->user), &(thd->lex->definer->host));
|
||||
/* Append the left part of thd->query after "DEFINER" part */
|
||||
if (buf->append(thd->lex->stmt_definition_begin,
|
||||
thd->lex->stmt_definition_end -
|
||||
thd->lex->stmt_definition_begin))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Create a new event.
|
||||
|
||||
@@ -822,10 +807,10 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
|
||||
DBUG_ENTER("Events::fill_schema_events");
|
||||
|
||||
/*
|
||||
If we didn't start events because of --skip-grant-tables, return an
|
||||
empty set
|
||||
If we didn't start events because of --skip-grant-tables and
|
||||
the current statement is not SHOW EVENTS, return an empty set
|
||||
*/
|
||||
if (opt_noacl)
|
||||
if (opt_noacl && thd->lex->sql_command != SQLCOM_SHOW_EVENTS)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
if (unlikely(check_if_system_tables_error()))
|
||||
@@ -1178,7 +1163,9 @@ Events::load_events_from_db(THD *thd)
|
||||
bool save_tx_read_only= thd->tx_read_only;
|
||||
thd->tx_read_only= false;
|
||||
|
||||
ret= db_repository->open_event_table(thd, TL_WRITE, &table);
|
||||
bool sys_triggers_enabled;
|
||||
ret= db_repository->open_event_table(thd, TL_WRITE, &table,
|
||||
&sys_triggers_enabled);
|
||||
|
||||
thd->tx_read_only= save_tx_read_only;
|
||||
thd->security_ctx->master_access= saved_master_access;
|
||||
@@ -1207,7 +1194,7 @@ Events::load_events_from_db(THD *thd)
|
||||
|
||||
DBUG_PRINT("info", ("Loading event from row."));
|
||||
|
||||
if (et->load_from_row(thd, table))
|
||||
if (et->load_from_row(thd, table, sys_triggers_enabled))
|
||||
{
|
||||
my_message(ER_STARTUP,
|
||||
"Event Scheduler: "
|
||||
@@ -1218,6 +1205,14 @@ Events::load_events_from_db(THD *thd)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (et->trigger_event)
|
||||
{
|
||||
/*
|
||||
Skip records of the table mysql.event for entries representing triggers
|
||||
*/
|
||||
delete et;
|
||||
continue;
|
||||
}
|
||||
#ifdef WITH_WSREP
|
||||
/**
|
||||
If SST is done from a galera node that is also acting as MASTER
|
||||
|
||||
14
sql/events.h
14
sql/events.h
@@ -25,6 +25,8 @@
|
||||
A public interface of Events_Scheduler module.
|
||||
*/
|
||||
|
||||
#include "event_common.h"
|
||||
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
extern PSI_mutex_key key_event_scheduler_LOCK_scheduler_state;
|
||||
extern PSI_cond_key key_event_scheduler_COND_state;
|
||||
@@ -74,18 +76,12 @@ sortcmp_lex_string(const LEX_CSTRING *s, const LEX_CSTRING *t,
|
||||
subsystems (ACL, time zone tables, etc).
|
||||
*/
|
||||
|
||||
class Events
|
||||
class Events : public Events_common
|
||||
{
|
||||
public:
|
||||
/*
|
||||
the following block is to support --event-scheduler command line option
|
||||
and the @@global.event_scheduler SQL variable.
|
||||
See sys_var.cc
|
||||
*/
|
||||
enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED,
|
||||
EVENTS_ORIGINAL };
|
||||
|
||||
/* Protected using LOCK_global_system_variables only. */
|
||||
static ulong opt_event_scheduler, startup_state;
|
||||
static ulong opt_event_scheduler;
|
||||
static ulong inited;
|
||||
static bool check_if_system_tables_error();
|
||||
static bool start(int *err_no);
|
||||
|
||||
@@ -609,6 +609,7 @@ SYMBOL symbols[] = {
|
||||
{ "SOUNDS", SYM(SOUNDS_SYM)},
|
||||
{ "SOURCE", SYM(SOURCE_SYM)},
|
||||
{ "STAGE", SYM(STAGE_SYM)},
|
||||
{ "STARTUP", SYM(STARTUP_SYM)},
|
||||
{ "STORED", SYM(STORED_SYM)},
|
||||
{ "SPATIAL", SYM(SPATIAL_SYM)},
|
||||
{ "SPECIFIC", SYM(SPECIFIC_SYM)},
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
#include "sp_rcontext.h"
|
||||
#include "sp_cache.h"
|
||||
#include "sql_reload.h" // reload_acl_and_cache
|
||||
#include "sql_sys_or_ddl_trigger.h"
|
||||
#include "sp_head.h" // init_sp_psi_keys
|
||||
#include "log_cache.h"
|
||||
#include <mysqld_default_groups.h>
|
||||
@@ -6184,6 +6185,7 @@ int mysqld_main(int argc, char **argv)
|
||||
/* We can't close stdin until we know we're not in bootstrap mode. */
|
||||
bool please_close_stdin= fcntl(STDIN_FILENO, F_GETFD) >= 0;
|
||||
#endif
|
||||
bool bootstrap_or_noacl;
|
||||
|
||||
/*
|
||||
Perform basic thread library and malloc initialization,
|
||||
@@ -6540,6 +6542,15 @@ int mysqld_main(int argc, char **argv)
|
||||
|
||||
disable_log_notes= 0; /* Startup done, now we can give notes again */
|
||||
|
||||
/*
|
||||
Since opt_noacl can be modified between calling the functions
|
||||
run_after_startup_triggers and run_before_shutdown_triggers (on handling
|
||||
the statement FLUSH PRIVILAGES), calculate the flag based on their
|
||||
original values.
|
||||
*/
|
||||
bootstrap_or_noacl= (opt_bootstrap || opt_noacl);
|
||||
run_after_startup_triggers(bootstrap_or_noacl);
|
||||
|
||||
if (IS_SYSVAR_AUTOSIZE(&server_version_ptr))
|
||||
sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname, server_version,
|
||||
(systemd_sock_activation ? "Systemd socket activated ports" :
|
||||
@@ -6575,7 +6586,6 @@ int mysqld_main(int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Signal threads waiting for server to be started */
|
||||
mysql_mutex_lock(&LOCK_server_started);
|
||||
mysqld_server_started= 1;
|
||||
@@ -6590,6 +6600,8 @@ int mysqld_main(int argc, char **argv)
|
||||
run_main_loop();
|
||||
|
||||
/* Shutdown requested */
|
||||
run_before_shutdown_triggers(bootstrap_or_noacl);
|
||||
|
||||
{
|
||||
char *user= shutdown_user.load(std::memory_order_relaxed);
|
||||
sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
|
||||
|
||||
@@ -12423,3 +12423,13 @@ ER_RESULT_CUT_BY_LIMIT
|
||||
eng "Result was cut by %s) because of %s limit"
|
||||
ER_CUT_VALUES_WHILE_PROCESSING
|
||||
eng "Some values were cut while processing %s). Increase %s if you want to avoid the cut"
|
||||
ER_SYS_TRG_SEMANTIC_ERROR
|
||||
eng "Incorrect definition of the trigger %s.%s: action '%s' is not compatible with event '%s'"
|
||||
ER_TRG_EVENT_CONFLICTS_NAME
|
||||
eng "%s with the same name '%s' already exists"
|
||||
ER_SYSTEM_TRG_DISABLED
|
||||
eng "The table mysql.event doesn't contain all mandatory columns required for support of system triggers. Operations with system triggers are disabled."
|
||||
ER_SYSTEM_TRG_INVALID_METADATA
|
||||
eng "Wrong metadata for trigger '%s.%s': '%s'"
|
||||
ER_SYSTEM_TRG_INVALID_PREDICATE
|
||||
eng "Invalid conditional predicate %s inside a system trigger body"
|
||||
|
||||
@@ -1138,6 +1138,22 @@ protected:
|
||||
backpatch_instr_type itype);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Check whether the top level statement is CREATE TRIGGER.
|
||||
|
||||
Return true in case the top level statement is CREATE TRIGGER,
|
||||
else return false.
|
||||
*/
|
||||
|
||||
bool create_trigger_is_top_statement() const
|
||||
{
|
||||
const LEX *top_lex= m_lex.head();
|
||||
|
||||
return top_lex && top_lex->sql_command == SQLCOM_CREATE_TRIGGER;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
List of lists of Item_trigger_field objects representing all fields in
|
||||
old/new version of row in trigger. We use this list of lists for checking
|
||||
|
||||
@@ -718,7 +718,7 @@ const char *thd_where(THD *thd)
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
THD::THD(my_thread_id id, bool is_wsrep_applier)
|
||||
THD::THD(my_thread_id id, bool is_wsrep_applier, bool process_wide)
|
||||
:Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
|
||||
/* statement id */ 0),
|
||||
rli_fake(0), rgi_fake(0), rgi_slave(NULL),
|
||||
@@ -846,7 +846,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
|
||||
*/
|
||||
init_sql_alloc(key_memory_thd_main_mem_root,
|
||||
&main_mem_root, DEFAULT_ROOT_BLOCK_SIZE, 0,
|
||||
MYF(MY_THREAD_SPECIFIC));
|
||||
process_wide ? MYF(0) : MYF(MY_THREAD_SPECIFIC));
|
||||
|
||||
/*
|
||||
Use MY_ROOT_USE_VMEM to keep user vars away from the heap, so that a heap
|
||||
@@ -996,6 +996,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
|
||||
{
|
||||
m_token_array= (unsigned char*) my_malloc(PSI_INSTRUMENT_ME,
|
||||
max_digest_length,
|
||||
process_wide ? MYF(MY_WME) :
|
||||
MYF(MY_WME|MY_THREAD_SPECIFIC));
|
||||
}
|
||||
|
||||
|
||||
@@ -4692,8 +4692,11 @@ public:
|
||||
/**
|
||||
@param id thread identifier
|
||||
@param is_wsrep_applier thread type
|
||||
@param process_wide flag specifying that the instance of THD is
|
||||
per whole process, or per thread
|
||||
*/
|
||||
THD(my_thread_id id, bool is_wsrep_applier= false);
|
||||
THD(my_thread_id id, bool is_wsrep_applier= false,
|
||||
bool process_wide= false);
|
||||
|
||||
~THD();
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "sql_admin.h" // Sql_cmd_analyze/Check..._table
|
||||
#include "sql_partition.h"
|
||||
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
|
||||
#include "sql_sys_or_ddl_trigger.h"
|
||||
#include "event_parse_data.h"
|
||||
#include "opt_hints.h"
|
||||
#ifdef WITH_WSREP
|
||||
@@ -4123,6 +4124,15 @@ void LEX::cleanup_lex_after_parse_error(THD *thd)
|
||||
thd->lex= top;
|
||||
thd->lex->sphead= NULL;
|
||||
}
|
||||
else if (thd->lex->sphead->create_trigger_is_top_statement())
|
||||
{
|
||||
/*
|
||||
In case an error happens on parsing the CREATE TRIGGER statement,
|
||||
destroy an instance of sp_head to avoid memory leaks
|
||||
*/
|
||||
sp_head::destroy(thd->lex->sphead);
|
||||
thd->lex->sphead= nullptr;
|
||||
}
|
||||
else
|
||||
thd->lex->sphead->unwind_aux_lexes_and_restore_original_lex();
|
||||
}
|
||||
@@ -8911,6 +8921,14 @@ Item *LEX::create_and_link_Item_trigger_field(THD *thd,
|
||||
{
|
||||
Item_trigger_field *trg_fld;
|
||||
|
||||
if (is_sys_trg_events(trg_chistics.events))
|
||||
{
|
||||
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0),
|
||||
new_row ? "NEW" : "OLD",
|
||||
"system");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (unlikely(is_trg_event_on(trg_chistics.events, TRG_EVENT_INSERT) &&
|
||||
!new_row &&
|
||||
/*
|
||||
@@ -9551,16 +9569,31 @@ Item *LEX::create_item_ident(THD *thd,
|
||||
return create_item_ident_field(thd, schema, *b, *c);
|
||||
}
|
||||
|
||||
static inline void report_trg_invalid_pred_error(active_dml_stmt stmt_type)
|
||||
{
|
||||
static const char *dml_stmt[] = {"INSERTING", "UPDATING", "DELETING"};
|
||||
my_error(ER_SYSTEM_TRG_INVALID_PREDICATE, MYF(0),
|
||||
dml_stmt[(int)stmt_type - 1], "system");
|
||||
}
|
||||
|
||||
Item *LEX::create_item_ident_trigger_specific(THD *thd,
|
||||
active_dml_stmt stmt_type,
|
||||
bool *throw_error)
|
||||
{
|
||||
if (is_sys_trg_events(trg_chistics.events))
|
||||
{
|
||||
report_trg_invalid_pred_error(stmt_type);
|
||||
*throw_error= true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define DML_EVENT_NAME_IDX (trg_chistics.events & TRG_DML_EVENTS_MASK)
|
||||
|
||||
if (stmt_type == active_dml_stmt::INSERTING_STMT &&
|
||||
!is_trg_event_on(trg_chistics.events, TRG_EVENT_INSERT))
|
||||
{
|
||||
my_error(ER_INCOMPATIBLE_EVENT_FLAG, MYF(0), "INSERTING",
|
||||
trg_event_type_names[trg_chistics.events].str);
|
||||
trg_event_type_names[DML_EVENT_NAME_IDX].str);
|
||||
*throw_error= true;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -9569,7 +9602,7 @@ Item *LEX::create_item_ident_trigger_specific(THD *thd,
|
||||
!is_trg_event_on(trg_chistics.events, TRG_EVENT_UPDATE))
|
||||
{
|
||||
my_error(ER_INCOMPATIBLE_EVENT_FLAG, MYF(0), "UPDATING",
|
||||
trg_event_type_names[trg_chistics.events].str);
|
||||
trg_event_type_names[DML_EVENT_NAME_IDX].str);
|
||||
*throw_error= true;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -9578,7 +9611,7 @@ Item *LEX::create_item_ident_trigger_specific(THD *thd,
|
||||
!is_trg_event_on(trg_chistics.events, TRG_EVENT_DELETE))
|
||||
{
|
||||
my_error(ER_INCOMPATIBLE_EVENT_FLAG, MYF(0), "DELETING",
|
||||
trg_event_type_names[trg_chistics.events].str);
|
||||
trg_event_type_names[DML_EVENT_NAME_IDX].str);
|
||||
*throw_error= true;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -9943,6 +9976,13 @@ bool LEX::set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
|
||||
Item *val, const LEX_CSTRING &expr_str)
|
||||
{
|
||||
DBUG_ASSERT(is_trigger_new_or_old_reference(name1));
|
||||
if (is_sys_trg_events(trg_chistics.events))
|
||||
{
|
||||
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0),
|
||||
(name1->str[0]=='O' || name1->str[0]=='o') ? "OLD" : "NEW",
|
||||
"system");
|
||||
return true;
|
||||
}
|
||||
if (unlikely(name1->str[0]=='O' || name1->str[0]=='o'))
|
||||
{
|
||||
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "");
|
||||
|
||||
@@ -1821,7 +1821,7 @@ public:
|
||||
struct st_trg_chistics: public st_trg_execution_order
|
||||
{
|
||||
enum trg_action_time_type action_time;
|
||||
trg_event_set events;
|
||||
trg_all_events_set events;
|
||||
|
||||
const char *ordering_clause_begin;
|
||||
const char *ordering_clause_end;
|
||||
|
||||
@@ -687,7 +687,17 @@ inline void bubble_sort(List<T> *list_to_sort,
|
||||
struct ilink
|
||||
{
|
||||
struct ilink **prev,*next;
|
||||
/*
|
||||
Allocate a new memory for placing an instance of the class
|
||||
inherited from ilink.
|
||||
|
||||
@note In case the OOM error happened on memory allocation, the flag MY_FAE
|
||||
forces abnormal server termination via calling abort(). Therefore,
|
||||
it makes sense to add compiler hint that null value can't be returned
|
||||
for possible optimization.
|
||||
*/
|
||||
static void *operator new(size_t size) throw ()
|
||||
__attribute__((returns_nonnull))
|
||||
{
|
||||
return (void*)my_malloc(PSI_INSTRUMENT_ME,
|
||||
(uint)size, MYF(MY_WME | MY_FAE | ME_FATAL));
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
#include "sp_cache.h"
|
||||
#include "events.h"
|
||||
#include "sql_trigger.h"
|
||||
#include "sql_sys_or_ddl_trigger.h" // is_ddl_trg_events, is_sys_trg_events
|
||||
#include "transaction.h"
|
||||
#include "sql_alter.h"
|
||||
#include "sql_audit.h"
|
||||
@@ -5769,7 +5770,17 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
|
||||
case SQLCOM_CREATE_TRIGGER:
|
||||
{
|
||||
/* Conditionally writes to binlog. */
|
||||
res= mysql_create_or_drop_trigger(thd, all_tables, 1);
|
||||
if (is_dml_trg_events(thd->lex->trg_chistics.events))
|
||||
res= mysql_create_or_drop_trigger(thd, all_tables, 1);
|
||||
else if (is_sys_trg_events(thd->lex->trg_chistics.events))
|
||||
res= mysql_create_sys_trigger(thd);
|
||||
else
|
||||
{
|
||||
// TODO: Add implementation of triggers on DDL events
|
||||
DBUG_ASSERT(is_ddl_trg_events(thd->lex->trg_chistics.events));
|
||||
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "DDL triggers not supported yet");
|
||||
res= true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -5778,8 +5789,37 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
|
||||
if (thd->variables.option_bits & OPTION_IF_EXISTS)
|
||||
lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
|
||||
|
||||
/*
|
||||
The DROP TRIGGER statement doesn't contain any clauses specifying
|
||||
what kind of a trigger we are dealing with, so the kind of the trigger
|
||||
should be extracted from its metadata.
|
||||
|
||||
First try to drop a system/ddl trigger with the specified name by
|
||||
calling the function mysql_drop_sys_or_ddl_trigger(). Inside this
|
||||
function checking for presence a trigger with specified name is performed
|
||||
by querying the system table mysql.event. If the trigger exist,
|
||||
acquire lock on trigger name and handle dropping of the trigger.
|
||||
If there is no a system or ddl trigger with supplied name, assume that
|
||||
specified trigger name is for dml trigger and call
|
||||
mysql_create_or_drop_trigger() to handle dropping. The function
|
||||
mysql_create_or_drop_trigger() also take the same mdl lock so in case
|
||||
the trigger name references dml trigger, we take the same mdl lock twice,
|
||||
but overhead on taking the lock second time is good compromise in
|
||||
comparing with modification of mysql_create_or_drop_trigger that
|
||||
could be the source of bugs.
|
||||
*/
|
||||
bool no_ddl_trigger_found;
|
||||
res= mysql_drop_sys_or_ddl_trigger(thd, &no_ddl_trigger_found);
|
||||
if (res)
|
||||
break;
|
||||
|
||||
/*
|
||||
Drop a DML trigger, raise the error ER_TRG_DOES_NOT_EXIST
|
||||
in case there is no on DML trigger with the specified name
|
||||
*/
|
||||
if (no_ddl_trigger_found)
|
||||
/* Conditionally writes to binlog. */
|
||||
res= mysql_create_or_drop_trigger(thd, all_tables, 0);
|
||||
res= mysql_create_or_drop_trigger(thd, all_tables, 0);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_XA_START:
|
||||
|
||||
198
sql/sql_show.cc
198
sql/sql_show.cc
@@ -44,6 +44,7 @@
|
||||
#include "sp_pcontext.h"
|
||||
#include "set_var.h"
|
||||
#include "sql_trigger.h"
|
||||
#include "sql_sys_or_ddl_trigger.h"
|
||||
#include "sql_derived.h"
|
||||
#include "sql_statistics.h"
|
||||
#include "sql_connect.h"
|
||||
@@ -2717,7 +2718,7 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table)
|
||||
}
|
||||
|
||||
|
||||
static bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host)
|
||||
bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host)
|
||||
{
|
||||
if (!host->str || !host->str[0])
|
||||
return false;
|
||||
@@ -7440,6 +7441,25 @@ err:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Fill in the table information_schema.triggers with data about
|
||||
both DML and system triggers.
|
||||
|
||||
@param thd thread handler
|
||||
@param tables an instance of the struct TABLE_LIST for the table
|
||||
information_schema.triggers
|
||||
|
||||
@return false on success, true on error
|
||||
*/
|
||||
|
||||
static int fill_schema_triggers(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
{
|
||||
return
|
||||
get_all_tables(thd, tables, cond) ||
|
||||
fill_schema_triggers_from_mysql_events(thd, tables);
|
||||
}
|
||||
|
||||
|
||||
static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, TABLE *table,
|
||||
bool res, const LEX_CSTRING *db_name,
|
||||
const LEX_CSTRING *table_name)
|
||||
@@ -8603,7 +8623,8 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
|
||||
*/
|
||||
|
||||
int
|
||||
copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
|
||||
copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table,
|
||||
bool sys_triggers_enabled)
|
||||
{
|
||||
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
|
||||
CHARSET_INFO *scs= system_charset_info;
|
||||
@@ -8613,12 +8634,15 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
|
||||
|
||||
restore_record(sch_table, s->default_values);
|
||||
|
||||
if (et.load_from_row(thd, event_table))
|
||||
if (et.load_from_row(thd, event_table, sys_triggers_enabled))
|
||||
{
|
||||
my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (et.trigger_event)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
if (!(!wild || !wild[0] || !wild_case_compare(scs, et.name.str, wild)))
|
||||
DBUG_RETURN(0);
|
||||
|
||||
@@ -9835,7 +9859,8 @@ bool optimize_schema_tables_reads(JOIN *join)
|
||||
continue;
|
||||
|
||||
/* skip I_S optimizations specific to get_all_tables */
|
||||
if (table_list->schema_table->fill_table != get_all_tables)
|
||||
if (table_list->schema_table->fill_table != get_all_tables &&
|
||||
table_list->schema_table->fill_table != fill_schema_triggers)
|
||||
continue;
|
||||
|
||||
Item *cond= tab->select_cond;
|
||||
@@ -9930,7 +9955,8 @@ bool get_schema_tables_result(JOIN *join,
|
||||
|
||||
/* skip I_S optimizations specific to get_all_tables */
|
||||
if (lex->describe &&
|
||||
(table_list->schema_table->fill_table != get_all_tables))
|
||||
(table_list->schema_table->fill_table != get_all_tables &&
|
||||
table_list->schema_table->fill_table != fill_schema_triggers))
|
||||
continue;
|
||||
|
||||
/*
|
||||
@@ -11173,7 +11199,7 @@ ST_SCHEMA_TABLE schema_tables[]=
|
||||
Show::table_privileges_fields_info, 0,
|
||||
fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
|
||||
{"TRIGGERS"_Lex_ident_i_s_table, Show::triggers_fields_info, 0,
|
||||
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
|
||||
fill_schema_triggers, make_old_format, get_schema_triggers_record, 5, 6, 0,
|
||||
OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
|
||||
{"TRIGGERED_UPDATE_COLUMNS"_Lex_ident_i_s_table,
|
||||
Show::triggered_update_columns_info, 0, get_all_tables, 0,
|
||||
@@ -11261,6 +11287,81 @@ int finalize_schema_table(void *plugin_)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Construct and send header of SHOW CREATE TRIGGER statement to the client
|
||||
|
||||
@param thd Thread context
|
||||
@param mem_root Memory root
|
||||
@param p Client protocol
|
||||
@param trg_sql_mode_str Text representation of sql_mode value
|
||||
@param trg_sql_original_stmt Original CREATE TRIGGER statement used to
|
||||
create the trigger
|
||||
|
||||
@return false on success, true on error
|
||||
*/
|
||||
|
||||
bool send_show_create_trigger_metadata(THD *thd, MEM_ROOT *mem_root,
|
||||
Protocol *p,
|
||||
const LEX_CSTRING &trg_sql_mode_str,
|
||||
LEX_CSTRING trg_sql_original_stmt)
|
||||
{
|
||||
List<Item> fields;
|
||||
|
||||
/* Send header. */
|
||||
|
||||
fields.push_back(new (mem_root) Item_empty_string(thd, "Trigger", NAME_LEN),
|
||||
mem_root);
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "sql_mode",
|
||||
(uint)trg_sql_mode_str.length),
|
||||
mem_root);
|
||||
|
||||
{
|
||||
/*
|
||||
NOTE: SQL statement field must be not less than 1024 in order not to
|
||||
confuse old clients.
|
||||
*/
|
||||
|
||||
Item_empty_string *stmt_fld=
|
||||
new (mem_root) Item_empty_string(
|
||||
thd, "SQL Original Statement",
|
||||
(uint)MY_MAX(trg_sql_original_stmt.length, 1024));
|
||||
|
||||
stmt_fld->set_maybe_null();
|
||||
|
||||
fields.push_back(stmt_fld, mem_root);
|
||||
}
|
||||
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "character_set_client",
|
||||
MY_CS_CHARACTER_SET_NAME_SIZE),
|
||||
mem_root);
|
||||
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "collation_connection",
|
||||
MY_CS_COLLATION_NAME_SIZE),
|
||||
mem_root);
|
||||
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "Database Collation",
|
||||
MY_CS_COLLATION_NAME_SIZE),
|
||||
mem_root);
|
||||
|
||||
static const Datetime zero_datetime(Datetime::zero());
|
||||
Item_datetime_literal *tmp= (new (mem_root)
|
||||
Item_datetime_literal(thd, &zero_datetime, 2));
|
||||
tmp->set_name(thd, Lex_cstring(STRING_WITH_LEN("Created")));
|
||||
fields.push_back(tmp, mem_root);
|
||||
|
||||
if (p->send_result_set_metadata(&fields,
|
||||
Protocol::SEND_NUM_ROWS |
|
||||
Protocol::SEND_EOF))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Output trigger information (SHOW CREATE TRIGGER) to the client.
|
||||
|
||||
@@ -11276,7 +11377,6 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
|
||||
{
|
||||
int ret_code;
|
||||
Protocol *p= thd->protocol;
|
||||
List<Item> fields;
|
||||
LEX_CSTRING trg_sql_mode_str, trg_body;
|
||||
LEX_CSTRING trg_sql_original_stmt;
|
||||
LEX_STRING trg_definer;
|
||||
@@ -11306,55 +11406,10 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
|
||||
MYF(utf8_flag)))
|
||||
return TRUE;
|
||||
|
||||
/* Send header. */
|
||||
|
||||
fields.push_back(new (mem_root) Item_empty_string(thd, "Trigger", NAME_LEN),
|
||||
mem_root);
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "sql_mode", (uint)trg_sql_mode_str.length),
|
||||
mem_root);
|
||||
|
||||
{
|
||||
/*
|
||||
NOTE: SQL statement field must be not less than 1024 in order not to
|
||||
confuse old clients.
|
||||
*/
|
||||
|
||||
Item_empty_string *stmt_fld=
|
||||
new (mem_root) Item_empty_string(thd, "SQL Original Statement",
|
||||
(uint)MY_MAX(trg_sql_original_stmt.length,
|
||||
1024));
|
||||
|
||||
stmt_fld->set_maybe_null();
|
||||
|
||||
fields.push_back(stmt_fld, mem_root);
|
||||
}
|
||||
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "character_set_client",
|
||||
MY_CS_CHARACTER_SET_NAME_SIZE),
|
||||
mem_root);
|
||||
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "collation_connection",
|
||||
MY_CS_COLLATION_NAME_SIZE),
|
||||
mem_root);
|
||||
|
||||
fields.push_back(new (mem_root)
|
||||
Item_empty_string(thd, "Database Collation",
|
||||
MY_CS_COLLATION_NAME_SIZE),
|
||||
mem_root);
|
||||
|
||||
static const Datetime zero_datetime(Datetime::zero());
|
||||
Item_datetime_literal *tmp= (new (mem_root)
|
||||
Item_datetime_literal(thd, &zero_datetime, 2));
|
||||
tmp->set_name(thd, Lex_cstring(STRING_WITH_LEN("Created")));
|
||||
fields.push_back(tmp, mem_root);
|
||||
|
||||
if (p->send_result_set_metadata(&fields,
|
||||
Protocol::SEND_NUM_ROWS |
|
||||
Protocol::SEND_EOF))
|
||||
return TRUE;
|
||||
if (send_show_create_trigger_metadata(thd, mem_root, p,
|
||||
trg_sql_mode_str,
|
||||
trg_sql_original_stmt))
|
||||
return true;
|
||||
|
||||
/* Send data. */
|
||||
|
||||
@@ -11407,6 +11462,8 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
|
||||
|
||||
@param thd Thread context.
|
||||
@param trg_name Trigger name.
|
||||
@param[out] not_found On return has the true value in case there is no
|
||||
a DML trigger with supllied name, else false
|
||||
|
||||
@return TABLE_LIST object corresponding to the base table.
|
||||
|
||||
@@ -11426,7 +11483,8 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
|
||||
*/
|
||||
|
||||
static
|
||||
TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
|
||||
TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name,
|
||||
bool *not_found)
|
||||
{
|
||||
char trn_path_buff[FN_REFLEN];
|
||||
LEX_CSTRING trn_path= { trn_path_buff, 0 };
|
||||
@@ -11434,14 +11492,20 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
|
||||
LEX_CSTRING tbl_name;
|
||||
TABLE_LIST *table;
|
||||
|
||||
build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path);
|
||||
build_trn_path(trg_name, (LEX_STRING*) &trn_path);
|
||||
|
||||
if (check_trn_exists(&trn_path))
|
||||
{
|
||||
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
|
||||
/*
|
||||
Don't call my_error(ER_TRG_DOES_NOT_EXIST) to set an error in DA
|
||||
since it will be done later in the function show_create_sys_trigger()
|
||||
*/
|
||||
*not_found= true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*not_found= false;
|
||||
|
||||
if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
|
||||
return NULL;
|
||||
|
||||
@@ -11475,14 +11539,24 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
|
||||
|
||||
bool show_create_trigger(THD *thd, const sp_name *trg_name)
|
||||
{
|
||||
TABLE_LIST *lst= get_trigger_table(thd, trg_name);
|
||||
uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
|
||||
Table_triggers_list *triggers;
|
||||
Trigger *trigger;
|
||||
bool error= TRUE;
|
||||
bool error= true, not_found;
|
||||
|
||||
/* First check whether the trigger is dml trigger */
|
||||
|
||||
TABLE_LIST *lst= get_trigger_table(thd, trg_name, ¬_found);
|
||||
|
||||
if (not_found)
|
||||
/*
|
||||
There is no a trigger on DML event, so handle the trg_name as
|
||||
a name of system trigger
|
||||
*/
|
||||
return show_create_sys_trigger(thd, trg_name);
|
||||
|
||||
if (!lst)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
if (check_table_access(thd, TRIGGER_ACL, lst, FALSE, 1, TRUE))
|
||||
{
|
||||
|
||||
@@ -91,7 +91,8 @@ int show_create_table_ex(THD *thd, TABLE_LIST *table_list,
|
||||
Table_specification_st *create_info_arg,
|
||||
enum_with_db_name with_db_name);
|
||||
|
||||
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
|
||||
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table,
|
||||
bool sys_triggers_enabled);
|
||||
|
||||
bool append_identifier(THD *thd, String *packet, const char *name, size_t length);
|
||||
static inline bool append_identifier(THD *thd, String *packet, const LEX_CSTRING *name)
|
||||
@@ -124,6 +125,7 @@ bool mysqld_show_contributors(THD *thd);
|
||||
bool mysqld_show_privileges(THD *thd);
|
||||
char *make_backup_log_name(char *buff, const char *name, const char* log_ext);
|
||||
uint calc_sum_of_all_status(STATUS_VAR *to);
|
||||
bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host);
|
||||
bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
|
||||
const LEX_CSTRING *definer_host);
|
||||
int add_status_vars(SHOW_VAR *list);
|
||||
@@ -132,6 +134,10 @@ ulonglong get_status_vars_version(void);
|
||||
void init_status_vars();
|
||||
void free_status_vars();
|
||||
void reset_status_vars();
|
||||
bool send_show_create_trigger_metadata(THD *thd, MEM_ROOT *mem_root,
|
||||
Protocol *p,
|
||||
const LEX_CSTRING &trg_sql_mode_str,
|
||||
LEX_CSTRING trg_sql_original_stmt);
|
||||
bool show_create_trigger(THD *thd, const sp_name *trg_name);
|
||||
void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
|
||||
|
||||
|
||||
2361
sql/sql_sys_or_ddl_trigger.cc
Normal file
2361
sql/sql_sys_or_ddl_trigger.cc
Normal file
File diff suppressed because it is too large
Load Diff
86
sql/sql_sys_or_ddl_trigger.h
Normal file
86
sql/sql_sys_or_ddl_trigger.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef SQL_SQL_SYS_OR_DDL_TRIGGER_H
|
||||
#define SQL_SQL_SYS_OR_DDL_TRIGGER_H
|
||||
|
||||
#include "sp_head.h"
|
||||
#include "sql_trigger.h" // trg_all_events_set, TRG_EVENT_MAX
|
||||
|
||||
/**
|
||||
Type representing events for system triggers (on logon, on logoff,
|
||||
on startup, on shutdown) and ddl triggers
|
||||
*/
|
||||
enum trg_sys_event_type
|
||||
{
|
||||
TRG_SYS_EVENT_MIN= TRG_EVENT_MAX,
|
||||
TRG_EVENT_STARTUP= TRG_SYS_EVENT_MIN, // bit #3
|
||||
TRG_EVENT_SHUTDOWN, // bit #4
|
||||
TRG_EVENT_LOGON, // bit #5
|
||||
TRG_EVENT_LOGOFF, // bit #6
|
||||
TRG_EVENT_DDL, // bit #7
|
||||
TRG_SYS_EVENT_MAX
|
||||
};
|
||||
|
||||
static inline trg_all_events_set sys_trg2bit(enum trg_sys_event_type trg)
|
||||
{ return static_cast<trg_all_events_set>(1 << static_cast<int>(trg)); }
|
||||
|
||||
static inline bool is_sys_trg_events(trg_all_events_set events)
|
||||
{
|
||||
static const trg_all_events_set sys_events= trg_all_events_set(0) |
|
||||
sys_trg2bit(TRG_EVENT_LOGON) |
|
||||
sys_trg2bit(TRG_EVENT_LOGOFF) |
|
||||
sys_trg2bit(TRG_EVENT_STARTUP) |
|
||||
sys_trg2bit(TRG_EVENT_SHUTDOWN);
|
||||
/*
|
||||
Return true in case any of system events is set in the mask
|
||||
*/
|
||||
return ((events & sys_events) != 0);
|
||||
}
|
||||
|
||||
static inline bool is_ddl_trg_events(trg_all_events_set events)
|
||||
{
|
||||
static const trg_all_events_set ddl_events= trg_all_events_set(0) |
|
||||
sys_trg2bit(TRG_EVENT_DDL);
|
||||
/*
|
||||
Return true in case the only TRG_EVENT_DDL bit is set in the mask,
|
||||
that is the trigger is solely for handling DDL events
|
||||
*/
|
||||
return events != 0 && ((events & ddl_events) == events);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Class for representing a system trigger to run on events like
|
||||
AFTER STARTUP / BEFORE SHUTDOWN / etc.
|
||||
*/
|
||||
|
||||
class Sys_trigger : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
Sys_trigger(THD *thd, sp_head *sp)
|
||||
: m_thd{thd}, m_sp{sp} {}
|
||||
|
||||
~Sys_trigger()
|
||||
{
|
||||
sp_head::destroy(m_sp);
|
||||
}
|
||||
|
||||
bool execute();
|
||||
|
||||
Sys_trigger(const Sys_trigger&)= delete;
|
||||
Sys_trigger &operator=(const Sys_trigger&)= delete;
|
||||
|
||||
private:
|
||||
THD *m_thd;
|
||||
sp_head *m_sp;
|
||||
};
|
||||
|
||||
void run_after_startup_triggers(bool bootstrap_or_noacl);
|
||||
void run_before_shutdown_triggers(bool bootstrap_or_noacl);
|
||||
|
||||
bool show_create_sys_trigger(THD *thd, const sp_name *trg_name);
|
||||
bool fill_schema_triggers_from_mysql_events(THD *thd, TABLE_LIST *tables);
|
||||
void report_trg_already_exist_error(const sp_name *spname);
|
||||
bool find_sys_trigger_by_name(THD *thd, sp_name *spname, bool *found);
|
||||
|
||||
void release_resources(THD *original_thd);
|
||||
|
||||
#endif /* SQL_SQL_SYS_OR_DDL_TRIGGER_H */
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "wsrep_mysqld.h"
|
||||
#include <my_time.h>
|
||||
#include <mysql_time.h>
|
||||
#include "event_db_repository.h"
|
||||
#include "sql_sys_or_ddl_trigger.h"
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -564,6 +566,18 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (create)
|
||||
{
|
||||
bool found;
|
||||
if (find_sys_trigger_by_name(thd, thd->lex->spname, &found))
|
||||
goto end;
|
||||
|
||||
if (found)
|
||||
{
|
||||
report_trg_already_exist_error(thd->lex->spname);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables);
|
||||
|
||||
/* We should have only one table in table list. */
|
||||
@@ -2250,7 +2264,7 @@ static bool add_table_for_trigger_internal(THD *thd,
|
||||
LEX_CSTRING tbl_name= null_clex_str;
|
||||
DBUG_ENTER("add_table_for_trigger_internal");
|
||||
|
||||
build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path);
|
||||
build_trn_path(trg_name, (LEX_STRING*) &trn_path);
|
||||
|
||||
if (check_trn_exists(&trn_path))
|
||||
{
|
||||
@@ -3199,12 +3213,11 @@ process_unknown_string(const char *&unknown_key, uchar* base,
|
||||
/**
|
||||
Construct path to TRN-file.
|
||||
|
||||
@param thd[in] Thread context.
|
||||
@param trg_name[in] Trigger name.
|
||||
@param trn_path[out] Variable to store constructed path
|
||||
*/
|
||||
|
||||
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path)
|
||||
void build_trn_path(const sp_name *trg_name, LEX_STRING *trn_path)
|
||||
{
|
||||
/* Construct path to the TRN-file. */
|
||||
|
||||
|
||||
@@ -44,16 +44,31 @@ enum trg_event_type
|
||||
|
||||
typedef uint8 trg_event_set;
|
||||
|
||||
static inline uint8 trg2bit(enum trg_event_type trg)
|
||||
{ return static_cast<uint8>(1 << static_cast<int>(trg)); }
|
||||
|
||||
/**
|
||||
Check whether the specified trigger event type is set in
|
||||
Type covering a value of any event type used both in DML triggers and
|
||||
system triggers. Values of this type is produced by the parser.
|
||||
(@see LEX::st_trg_chistics.events)
|
||||
*/
|
||||
typedef uint32 trg_all_events_set;
|
||||
|
||||
constexpr static inline trg_event_set trg2bit(enum trg_event_type trg)
|
||||
{ return static_cast<trg_event_set>(1 << static_cast<int>(trg)); }
|
||||
|
||||
constexpr trg_all_events_set TRG_DML_EVENTS_MASK=trg2bit(TRG_EVENT_INSERT) |
|
||||
trg2bit(TRG_EVENT_UPDATE) |
|
||||
trg2bit(TRG_EVENT_DELETE);
|
||||
/**
|
||||
Check whether the specified DML trigger event type is set in
|
||||
the trigger's event mask
|
||||
*/
|
||||
static inline bool is_trg_event_on(uint8 trg_event_mask,
|
||||
static inline bool is_trg_event_on(trg_all_events_set trg_event_mask,
|
||||
enum trg_event_type event_type)
|
||||
{
|
||||
/*
|
||||
This function must be called only from places
|
||||
where dml triggers are expected
|
||||
*/
|
||||
DBUG_ASSERT((trg_event_mask & ~TRG_DML_EVENTS_MASK) == 0);
|
||||
return (trg_event_mask & trg2bit(event_type)) != 0;
|
||||
}
|
||||
|
||||
@@ -76,6 +91,19 @@ static inline bool is_subset_of_trg_events(trg_event_set trg_events_mask,
|
||||
return (trg_events_mask & trg_events) == trg_events;
|
||||
}
|
||||
|
||||
static inline bool is_dml_trg_events(trg_all_events_set events)
|
||||
{
|
||||
static const trg_all_events_set dml_events= TRG_DML_EVENTS_MASK;
|
||||
/*
|
||||
Return true in case any of DML relating bits are set.
|
||||
Since a trigger can't be created for firing both on DML events and
|
||||
system events, it is the correct way to check whether events is for
|
||||
a dml trigger
|
||||
*/
|
||||
return ((events & dml_events) != 0);
|
||||
}
|
||||
|
||||
|
||||
#include "table.h" /* GRANT_INFO */
|
||||
|
||||
/*
|
||||
@@ -445,7 +473,7 @@ bool add_table_for_trigger(THD *thd,
|
||||
bool continue_if_not_exist,
|
||||
TABLE_LIST **table);
|
||||
|
||||
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path);
|
||||
void build_trn_path(const sp_name *trg_name, LEX_STRING *trn_path);
|
||||
|
||||
bool check_trn_exists(const LEX_CSTRING *trn_path);
|
||||
|
||||
@@ -453,7 +481,11 @@ bool load_table_name_for_trigger(THD *thd,
|
||||
const sp_name *trg_name,
|
||||
const LEX_CSTRING *trn_path,
|
||||
LEX_CSTRING *tbl_name);
|
||||
|
||||
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
|
||||
bool mysql_create_sys_trigger(THD *thd);
|
||||
bool mysql_drop_sys_or_ddl_trigger(THD *thd, bool *no_ddl_trigger_found);
|
||||
|
||||
bool rm_trigname_file(char *path, const LEX_CSTRING *db,
|
||||
const LEX_CSTRING *trigger_name, myf MyFlags);
|
||||
|
||||
|
||||
112
sql/sql_yacc.yy
112
sql/sql_yacc.yy
@@ -73,6 +73,7 @@
|
||||
#include "json_table.h"
|
||||
#include "sql_update.h"
|
||||
#include "sql_delete.h"
|
||||
#include "sql_sys_or_ddl_trigger.h" // TRG_EVENT_STARTUP, ...
|
||||
|
||||
/* this is to get the bison compilation windows warnings out */
|
||||
#ifdef _MSC_VER
|
||||
@@ -408,9 +409,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
|
||||
*/
|
||||
|
||||
%ifdef MARIADB
|
||||
%expect 72
|
||||
%else
|
||||
%expect 73
|
||||
%else
|
||||
%expect 74
|
||||
%endif
|
||||
|
||||
/*
|
||||
@@ -1165,6 +1166,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
|
||||
%token <kwd> STAGE_SYM
|
||||
%token <kwd> STARTS_SYM
|
||||
%token <kwd> START_SYM /* SQL-2003-R */
|
||||
%token <kwd> STARTUP_SYM
|
||||
%token <kwd> STATEMENT_SYM
|
||||
%token <kwd> STATUS_SYM
|
||||
%token <kwd> STOP_SYM
|
||||
@@ -1928,7 +1930,8 @@ rule:
|
||||
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
|
||||
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
|
||||
view_list_opt view_list view_select
|
||||
trigger_tail event_tail
|
||||
trigger_tail_common trigger_tail trigger_tail_dml
|
||||
trigger_tail_sys event_tail
|
||||
install uninstall partition_entry binlog_base64_event
|
||||
normal_key_options normal_key_opts all_key_opt
|
||||
spatial_key_options fulltext_key_options normal_key_opt
|
||||
@@ -4992,6 +4995,18 @@ trg_events:
|
||||
| trg_events OR_SYM trg_event
|
||||
;
|
||||
|
||||
trg_sys_event:
|
||||
STARTUP_SYM
|
||||
{ Lex->trg_chistics.events|= sys_trg2bit(TRG_EVENT_STARTUP); }
|
||||
| SHUTDOWN
|
||||
{ Lex->trg_chistics.events|= sys_trg2bit(TRG_EVENT_SHUTDOWN); }
|
||||
;
|
||||
|
||||
trg_sys_events:
|
||||
trg_sys_event
|
||||
| trg_sys_events OR_SYM trg_sys_event
|
||||
;
|
||||
|
||||
create_body:
|
||||
create_field_list_parens
|
||||
{ Lex->create_info.option_list= NULL; }
|
||||
@@ -17360,6 +17375,7 @@ keyword_func_sp_var_and_label:
|
||||
| SQL_THREAD
|
||||
| STAGE_SYM
|
||||
| STARTS_SYM
|
||||
| STARTUP_SYM
|
||||
| STATEMENT_SYM
|
||||
| STATUS_SYM
|
||||
| STORAGE_SYM
|
||||
@@ -19305,26 +19321,80 @@ on_update_cols:
|
||||
}
|
||||
;
|
||||
|
||||
trigger_tail:
|
||||
trigger_tail_common:
|
||||
remember_name
|
||||
opt_if_not_exists
|
||||
{
|
||||
if (unlikely(Lex->add_create_options_with_check($2)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
sp_name
|
||||
sp_name /* $4 */
|
||||
trg_action_time
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
|
||||
lex->stmt_definition_begin= $1;
|
||||
lex->spname= $4;
|
||||
}
|
||||
;
|
||||
|
||||
trigger_tail:
|
||||
trigger_tail_common
|
||||
trigger_tail_dml
|
||||
{}
|
||||
| trigger_tail_common
|
||||
trigger_tail_sys
|
||||
{}
|
||||
;
|
||||
|
||||
trigger_tail_sys:
|
||||
trg_sys_events
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
Lex_input_stream *lip= YYLIP;
|
||||
|
||||
/*
|
||||
Prohibit creation of a trigger from within the another
|
||||
trigger's body, as it implemented for triggers on dml
|
||||
*/
|
||||
if (unlikely(lex->sphead))
|
||||
my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"));
|
||||
|
||||
if (unlikely(!lex->make_sp_head(thd, lex->spname,
|
||||
&sp_handler_trigger,
|
||||
DEFAULT_AGGREGATE)))
|
||||
MYSQL_YYABORT;
|
||||
|
||||
lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
|
||||
/*
|
||||
Set lex->sql_command before parsing the rule sp_proc_stmt
|
||||
in order to be able to clean up sp_head object properly in case
|
||||
there is syntax error in a trigger body.
|
||||
(@see LEX::cleanup_lex_after_parse_error)
|
||||
*/
|
||||
lex->sql_command= SQLCOM_CREATE_TRIGGER;
|
||||
}
|
||||
sp_proc_stmt force_lookahead
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
||||
if (lex->sp_body_finalize_trigger(thd))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
;
|
||||
|
||||
trigger_tail_dml:
|
||||
trg_events
|
||||
opt_on_update_cols
|
||||
ON
|
||||
remember_name /* $9 */
|
||||
{ /* $10 */
|
||||
remember_name /* $4 */
|
||||
{ /* $5 */
|
||||
Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start();
|
||||
}
|
||||
table_ident /* $11 */
|
||||
table_ident /* $6 */
|
||||
FOR_SYM
|
||||
remember_name /* $13 */
|
||||
{ /* $14 */
|
||||
remember_name /* $8 */
|
||||
{ /* $9 */
|
||||
Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start();
|
||||
}
|
||||
EACH_SYM
|
||||
@@ -19332,29 +19402,29 @@ trigger_tail:
|
||||
{
|
||||
Lex->trg_chistics.ordering_clause_begin= YYLIP->get_cpp_ptr();
|
||||
}
|
||||
trigger_follows_precedes_clause /* $18 */
|
||||
{ /* $19 */
|
||||
trigger_follows_precedes_clause /* $13 */
|
||||
{ /* $14 */
|
||||
LEX *lex= thd->lex;
|
||||
Lex_input_stream *lip= YYLIP;
|
||||
|
||||
if (unlikely(lex->sphead))
|
||||
my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"));
|
||||
|
||||
lex->stmt_definition_begin= $1;
|
||||
lex->ident.str= $9;
|
||||
lex->ident.length= $13 - $9;
|
||||
lex->spname= $4;
|
||||
(*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($18);
|
||||
lex->ident.str= $4;
|
||||
lex->ident.length= $8 - $4;
|
||||
|
||||
(*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($13);
|
||||
lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr();
|
||||
|
||||
if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger,
|
||||
if (unlikely(!lex->make_sp_head(thd, lex->spname,
|
||||
&sp_handler_trigger,
|
||||
DEFAULT_AGGREGATE)))
|
||||
MYSQL_YYABORT;
|
||||
|
||||
lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
|
||||
}
|
||||
sp_proc_stmt /* $20 */ force_lookahead /* $21 */
|
||||
{ /* $22 */
|
||||
sp_proc_stmt /* $15 */ force_lookahead /* $16 */
|
||||
{ /* $17 */
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->sql_command= SQLCOM_CREATE_TRIGGER;
|
||||
@@ -19367,7 +19437,7 @@ trigger_tail:
|
||||
lex->query_tables can be wiped out.
|
||||
*/
|
||||
if (!lex->first_select_lex()->
|
||||
add_table_to_list(thd, $11, (LEX_CSTRING*) 0,
|
||||
add_table_to_list(thd, $6, (LEX_CSTRING*) 0,
|
||||
TL_OPTION_UPDATING, TL_READ_NO_INSERT,
|
||||
MDL_SHARED_NO_WRITE))
|
||||
MYSQL_YYABORT;
|
||||
|
||||
161
sql/table.cc
161
sql/table.cc
@@ -5677,72 +5677,15 @@ bool check_column_name(const Lex_cstring &ident)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Checks whether a table is intact. Should be done *just* after the table has
|
||||
been opened.
|
||||
|
||||
@param[in] table The table to check
|
||||
@param[in] table_def Expected structure of the table (column name
|
||||
and type)
|
||||
|
||||
@retval FALSE OK
|
||||
@retval TRUE There was an error. An error message is output
|
||||
to the error log. We do not push an error
|
||||
message into the error stack because this
|
||||
function is currently only called at start up,
|
||||
and such errors never reach the user.
|
||||
*/
|
||||
|
||||
bool
|
||||
Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
bool Table_check_intact::check_columns(TABLE *table,
|
||||
const TABLE_FIELD_DEF *table_def)
|
||||
{
|
||||
uint i;
|
||||
my_bool error= FALSE;
|
||||
const TABLE_FIELD_TYPE *field_def= table_def->field;
|
||||
DBUG_ENTER("table_check_intact");
|
||||
DBUG_PRINT("info",("table: %s expected_count: %d",
|
||||
table->alias.c_ptr(), table_def->count));
|
||||
|
||||
/* Whether the table definition has already been validated. */
|
||||
if (table->s->table_field_def_cache == table_def)
|
||||
goto end;
|
||||
|
||||
if (table->s->fields != table_def->count)
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
DBUG_PRINT("info", ("Column count has changed, checking the definition"));
|
||||
|
||||
/* previous MySQL version */
|
||||
if (MYSQL_VERSION_ID > table->s->mysql_version)
|
||||
{
|
||||
report_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE,
|
||||
ER_THD(thd, ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
|
||||
table->alias.c_ptr(), table_def->count, table->s->fields,
|
||||
static_cast<int>(table->s->mysql_version),
|
||||
MYSQL_VERSION_ID);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
else if (MYSQL_VERSION_ID == table->s->mysql_version)
|
||||
{
|
||||
report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2,
|
||||
ER_THD(thd, ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2),
|
||||
table->s->db.str, table->s->table_name.str,
|
||||
table_def->count, table->s->fields);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
/*
|
||||
Something has definitely changed, but we're running an older
|
||||
version of MySQL with new system tables.
|
||||
Let's check column definitions. If a column was added at
|
||||
the end of the table, then we don't care much since such change
|
||||
is backward compatible.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuffer<1024> sql_type(system_charset_info);
|
||||
bool error= false;
|
||||
|
||||
sql_type.extra_allocation(256); // Allocate min 256 characters at once
|
||||
for (i=0 ; i < table_def->count; i++, field_def++)
|
||||
for (uint i=0 ; i < table_def->count; i++, field_def++)
|
||||
{
|
||||
sql_type.length(0);
|
||||
if (i < table->s->fields)
|
||||
@@ -5790,7 +5733,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
table->alias.c_ptr(),
|
||||
field_def->name.str, i, field_def->type.str,
|
||||
sql_type.c_ptr_safe());
|
||||
error= TRUE;
|
||||
error= true;
|
||||
}
|
||||
else if (field_def->cset.str && !field->has_charset())
|
||||
{
|
||||
@@ -5800,7 +5743,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
"character set.", table->s->db.str,
|
||||
table->alias.c_ptr(),
|
||||
field_def->name.str, i, field_def->cset.str);
|
||||
error= TRUE;
|
||||
error= true;
|
||||
}
|
||||
else if (field_def->cset.str &&
|
||||
strncmp(field->charset()->cs_name.str, field_def->cset.str,
|
||||
@@ -5813,7 +5756,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
table->alias.c_ptr(),
|
||||
field_def->name.str, i, field_def->cset.str,
|
||||
field->charset()->cs_name.str);
|
||||
error= TRUE;
|
||||
error= true;
|
||||
}
|
||||
bool col_can_be_null= field_def->type.length & CAN_BE_NULL;
|
||||
if (col_can_be_null != field->real_maybe_null())
|
||||
@@ -5833,9 +5776,95 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
" but the column is not found.",
|
||||
table->s->db.str, table->alias.c_ptr(),
|
||||
field_def->name.str, i, field_def->type.str);
|
||||
error= TRUE;
|
||||
error= true;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Checks whether a table is intact. Should be done *just* after the table has
|
||||
been opened.
|
||||
|
||||
@param[in] table The table to check
|
||||
@param[in] table_def Expected structure of the table (column name
|
||||
and type)
|
||||
|
||||
@retval FALSE OK
|
||||
@retval TRUE There was an error. An error message is output
|
||||
to the error log. We do not push an error
|
||||
message into the error stack because this
|
||||
function can be called at start up,
|
||||
and such errors never reach the user.
|
||||
*/
|
||||
|
||||
bool
|
||||
Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
||||
{
|
||||
uint i;
|
||||
my_bool error= FALSE;
|
||||
DBUG_ENTER("table_check_intact");
|
||||
DBUG_PRINT("info",("table: %s expected_count: %d",
|
||||
table->alias.c_ptr(), table_def->count));
|
||||
|
||||
/* Whether the table definition has already been validated. */
|
||||
if (table->s->table_field_def_cache == table_def)
|
||||
goto end;
|
||||
|
||||
if (table->s->fields != table_def->count)
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
DBUG_PRINT("info", ("Column count has changed, checking the definition"));
|
||||
|
||||
/* previous MySQL version */
|
||||
if (MYSQL_VERSION_ID > table->s->mysql_version)
|
||||
{
|
||||
/*
|
||||
If the table mysql.event has actually less columns than
|
||||
expected by server, it could be possibly caused by attempt
|
||||
to start server against the old data dictionary. In this case
|
||||
check that every column of the mysql.event table has correct
|
||||
structure, that is all required columns except those needed for
|
||||
support of system trigger are presented.
|
||||
*/
|
||||
if (table->s->fields < table_def->count && handle_event_table())
|
||||
{
|
||||
bool ret= check_columns(table, table_def);
|
||||
if (ret)
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE,
|
||||
ER_THD(thd, ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
|
||||
table->alias.c_ptr(), table_def->count, table->s->fields,
|
||||
static_cast<int>(table->s->mysql_version),
|
||||
MYSQL_VERSION_ID);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
else if (MYSQL_VERSION_ID == table->s->mysql_version)
|
||||
{
|
||||
report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2,
|
||||
ER_THD(thd, ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2),
|
||||
table->s->db.str, table->s->table_name.str,
|
||||
table_def->count, table->s->fields);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
/*
|
||||
Something has definitely changed, but we're running an older
|
||||
version of MySQL with new system tables.
|
||||
Let's check column definitions. If a column was added at
|
||||
the end of the table, then we don't care much since such change
|
||||
is backward compatible.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (check_columns(table, table_def))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
if (table_def->primary_key_parts)
|
||||
|
||||
@@ -616,12 +616,16 @@ protected:
|
||||
ATTRIBUTE_FORMAT(printf, 3, 4) // 1st arg is the implicit `this`
|
||||
virtual void report_error(uint code, const char *fmt, ...)= 0;
|
||||
|
||||
virtual bool handle_event_table() const { return false; }
|
||||
public:
|
||||
Table_check_intact(bool keys= false) : has_keys(keys) {}
|
||||
virtual ~Table_check_intact() = default;
|
||||
|
||||
/** Checks whether a table is intact. */
|
||||
bool check(TABLE *table, const TABLE_FIELD_DEF *table_def);
|
||||
|
||||
/** Check table columns for right types */
|
||||
bool check_columns(TABLE *table, const TABLE_FIELD_DEF *table_def);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user