1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-30645: CREATE TRIGGER FOR { STARTUP | SHUTDOWN }

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,
  on handling the DROP TRIGGER statement the table mysql.event now is opened
- 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:
Dmitry Shulga
2026-10-18 20:57:00 +07:00
parent 17b8b2f240
commit 33bd19d2db
43 changed files with 5124 additions and 720 deletions

View File

@@ -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,29 @@ 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;
/*
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 (opt_dump_sys_triggers &&
mysql_get_server_version(mysql) >= FIRST_SYS_TRIGGER_VERSION &&
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]) ||
/*
Skip the `mysql` database since it is dumped before entering into
the while loop
*/
!cmp_database(row[0], mysql_db))
continue;
if (include_database(row[0]))
@@ -7480,6 +7503,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 +7776,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;

View File

@@ -168,6 +168,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}
)

View File

@@ -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

View 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;

View 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;

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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...

View File

@@ -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();

View File

@@ -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.

View File

@@ -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
@@ -206,6 +206,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
View 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
View 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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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)
*/

View File

@@ -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)
*/

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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)},

View File

@@ -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>
@@ -6166,6 +6167,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,
@@ -6522,6 +6524,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" :
@@ -6557,7 +6568,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;
@@ -6572,6 +6582,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,

View File

@@ -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"

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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();

View File

@@ -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", "");

View File

@@ -1796,7 +1796,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;

View File

@@ -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));

View File

@@ -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:

View File

@@ -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, &not_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))
{

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View 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 */

View File

@@ -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. */

View 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);

View File

@@ -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
@@ -414,9 +415,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
*/
%ifdef MARIADB
%expect 72
%else
%expect 73
%else
%expect 74
%endif
/*
@@ -1171,6 +1172,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
@@ -1934,7 +1936,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
@@ -4940,6 +4943,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; }
@@ -17308,6 +17323,7 @@ keyword_func_sp_var_and_label:
| SQL_THREAD
| STAGE_SYM
| STARTS_SYM
| STARTUP_SYM
| STATEMENT_SYM
| STATUS_SYM
| STORAGE_SYM
@@ -19253,26 +19269,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
@@ -19280,29 +19350,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;
@@ -19315,7 +19385,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;

View File

@@ -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)

View File

@@ -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);
};