diff --git a/mysql-test/main/trigger.result b/mysql-test/main/trigger.result index 08c1abd378d32..b708bf10f793e 100644 --- a/mysql-test/main/trigger.result +++ b/mysql-test/main/trigger.result @@ -3065,3 +3065,116 @@ TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_OBJECT_CATALOG EVENT_OBJECT_SC DROP VIEW IF EXISTS vt; DROP TABLE t, t1; # End of 12.2 tests +# +# MDEV-40570: INSERT ... ON DUPLICATE KEY UPDATE reports the wrong +# INSERTING/UPDATING/DELETING flag in multi-event triggers +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +CREATE TABLE t2 (msg VARCHAR(20)); +CREATE TRIGGER t1_b_all BEFORE INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN +IF INSERTING THEN +INSERT INTO t2 VALUES ('BEFORE INSERTING'); +ELSEIF UPDATING THEN +INSERT INTO t2 VALUES ('BEFORE UPDATING'); +ELSEIF DELETING THEN +INSERT INTO t2 VALUES ('BEFORE DELETING'); +END IF; +END +$ +CREATE TRIGGER t1_a_all AFTER INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN +IF INSERTING THEN +INSERT INTO t2 VALUES ('AFTER INSERTING'); +ELSEIF UPDATING THEN +INSERT INTO t2 VALUES ('AFTER UPDATING'); +ELSEIF DELETING THEN +INSERT INTO t2 VALUES ('AFTER DELETING'); +END IF; +END +$ +# Ordinary INSERT fires INSERT triggers with the INSERTING flag set +INSERT INTO t1 VALUES (1, 1); +SELECT * FROM t2; +msg +BEFORE INSERTING +AFTER INSERTING +TRUNCATE TABLE t2; +# INSERT ... ON DUPLICATE KEY UPDATE that hits a duplicate key first +# fires INSERT triggers (INSERTING), then performs an update and so +# must fire UPDATE triggers with the UPDATING flag set +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE b = 2; +SELECT * FROM t2; +msg +BEFORE INSERTING +BEFORE UPDATING +AFTER UPDATING +TRUNCATE TABLE t2; +# ON DUPLICATE KEY UPDATE that doesn't change the row still fires +# UPDATE triggers with the UPDATING flag set +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE b = 2; +SELECT * FROM t2; +msg +BEFORE INSERTING +BEFORE UPDATING +AFTER UPDATING +TRUNCATE TABLE t2; +# REPLACE on a duplicate key fires INSERT triggers (INSERTING) and +# DELETE triggers with the DELETING flag set +REPLACE INTO t1 VALUES (1, 3); +SELECT * FROM t2; +msg +BEFORE INSERTING +BEFORE DELETING +AFTER DELETING +AFTER INSERTING +TRUNCATE TABLE t2; +DROP TABLE t1, t2; +# +# Check the same for INSERT ... SELECT ... ON DUPLICATE KEY UPDATE +# and REPLACE ... SELECT +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +CREATE TABLE t2 (msg VARCHAR(20)); +CREATE TABLE t3 (a INT, b INT); +INSERT INTO t3 VALUES (1, 2); +CREATE TRIGGER t1_b_all BEFORE INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN +IF INSERTING THEN +INSERT INTO t2 VALUES ('BEFORE INSERTING'); +ELSEIF UPDATING THEN +INSERT INTO t2 VALUES ('BEFORE UPDATING'); +ELSEIF DELETING THEN +INSERT INTO t2 VALUES ('BEFORE DELETING'); +END IF; +END +$ +CREATE TRIGGER t1_a_all AFTER INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN +IF INSERTING THEN +INSERT INTO t2 VALUES ('AFTER INSERTING'); +ELSEIF UPDATING THEN +INSERT INTO t2 VALUES ('AFTER UPDATING'); +ELSEIF DELETING THEN +INSERT INTO t2 VALUES ('AFTER DELETING'); +END IF; +END +$ +INSERT INTO t1 VALUES (1, 1); +TRUNCATE TABLE t2; +INSERT INTO t1 SELECT * FROM t3 ON DUPLICATE KEY UPDATE b = 2; +SELECT * FROM t2; +msg +BEFORE INSERTING +BEFORE UPDATING +AFTER UPDATING +TRUNCATE TABLE t2; +REPLACE INTO t1 SELECT a, b + 1 FROM t3; +SELECT * FROM t2; +msg +BEFORE INSERTING +BEFORE DELETING +AFTER DELETING +AFTER INSERTING +DROP TABLE t1, t2, t3; +# End of 12.3 tests diff --git a/mysql-test/main/trigger.test b/mysql-test/main/trigger.test index 16a99c905c68f..44b78e88a371b 100644 --- a/mysql-test/main/trigger.test +++ b/mysql-test/main/trigger.test @@ -3404,3 +3404,110 @@ DROP VIEW IF EXISTS vt; DROP TABLE t, t1; --echo # End of 12.2 tests + +--echo # +--echo # MDEV-40570: INSERT ... ON DUPLICATE KEY UPDATE reports the wrong +--echo # INSERTING/UPDATING/DELETING flag in multi-event triggers +--echo # + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +CREATE TABLE t2 (msg VARCHAR(20)); + +--delimiter $ +CREATE TRIGGER t1_b_all BEFORE INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN + IF INSERTING THEN + INSERT INTO t2 VALUES ('BEFORE INSERTING'); + ELSEIF UPDATING THEN + INSERT INTO t2 VALUES ('BEFORE UPDATING'); + ELSEIF DELETING THEN + INSERT INTO t2 VALUES ('BEFORE DELETING'); + END IF; +END +$ +CREATE TRIGGER t1_a_all AFTER INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN + IF INSERTING THEN + INSERT INTO t2 VALUES ('AFTER INSERTING'); + ELSEIF UPDATING THEN + INSERT INTO t2 VALUES ('AFTER UPDATING'); + ELSEIF DELETING THEN + INSERT INTO t2 VALUES ('AFTER DELETING'); + END IF; +END +$ +--delimiter ; + +--echo # Ordinary INSERT fires INSERT triggers with the INSERTING flag set +INSERT INTO t1 VALUES (1, 1); +SELECT * FROM t2; +TRUNCATE TABLE t2; + +--echo # INSERT ... ON DUPLICATE KEY UPDATE that hits a duplicate key first +--echo # fires INSERT triggers (INSERTING), then performs an update and so +--echo # must fire UPDATE triggers with the UPDATING flag set +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE b = 2; +SELECT * FROM t2; +TRUNCATE TABLE t2; + +--echo # ON DUPLICATE KEY UPDATE that doesn't change the row still fires +--echo # UPDATE triggers with the UPDATING flag set +INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE b = 2; +SELECT * FROM t2; +TRUNCATE TABLE t2; + +--echo # REPLACE on a duplicate key fires INSERT triggers (INSERTING) and +--echo # DELETE triggers with the DELETING flag set +REPLACE INTO t1 VALUES (1, 3); +SELECT * FROM t2; +TRUNCATE TABLE t2; + +DROP TABLE t1, t2; + +--echo # +--echo # Check the same for INSERT ... SELECT ... ON DUPLICATE KEY UPDATE +--echo # and REPLACE ... SELECT +--echo # +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +CREATE TABLE t2 (msg VARCHAR(20)); +CREATE TABLE t3 (a INT, b INT); +INSERT INTO t3 VALUES (1, 2); + +--delimiter $ +CREATE TRIGGER t1_b_all BEFORE INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN + IF INSERTING THEN + INSERT INTO t2 VALUES ('BEFORE INSERTING'); + ELSEIF UPDATING THEN + INSERT INTO t2 VALUES ('BEFORE UPDATING'); + ELSEIF DELETING THEN + INSERT INTO t2 VALUES ('BEFORE DELETING'); + END IF; +END +$ +CREATE TRIGGER t1_a_all AFTER INSERT OR UPDATE OR DELETE ON t1 FOR EACH ROW +BEGIN + IF INSERTING THEN + INSERT INTO t2 VALUES ('AFTER INSERTING'); + ELSEIF UPDATING THEN + INSERT INTO t2 VALUES ('AFTER UPDATING'); + ELSEIF DELETING THEN + INSERT INTO t2 VALUES ('AFTER DELETING'); + END IF; +END +$ +--delimiter ; + +INSERT INTO t1 VALUES (1, 1); +TRUNCATE TABLE t2; + +INSERT INTO t1 SELECT * FROM t3 ON DUPLICATE KEY UPDATE b = 2; +SELECT * FROM t2; +TRUNCATE TABLE t2; + +REPLACE INTO t1 SELECT a, b + 1 FROM t3; +SELECT * FROM t2; + +DROP TABLE t1, t2, t3; + +--echo # End of 12.3 tests diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 60668384b3e2c..1ed567a088079 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2194,6 +2194,12 @@ int Write_record::replace_row(ha_rows *inserted, ha_rows *deleted) DBUG_PRINT("info", ("Deleting offending row and trying to write" " new one again")); + /* + Make the DELETE triggers fired below see the DELETING clause + set to true + */ + Running_stmt_guard stmt_guard(thd, active_dml_stmt::DELETING_STMT); + auto *trg = table->triggers; bool trg_skip_row= false; if (use_triggers && trg->process_triggers(table->in_use, TRG_EVENT_DELETE, @@ -2262,6 +2268,12 @@ int Write_record::insert_on_duplicate_update(ha_rows *inserted, DBUG_ASSERT(info->update_fields->elements == info->update_values->elements); + /* + Make the UPDATE triggers fired below see the UPDATING clause + set to true + */ + Running_stmt_guard stmt_guard(thd, active_dml_stmt::UPDATING_STMT); + bool trg_skip_row= false; if (fill_record_n_invoke_before_triggers(thd, table, *info->update_fields, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 496426cf0447c..16a58871bb8f3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4545,6 +4545,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) case SQLCOM_INSERT_SELECT: { WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE); + Running_stmt_guard guard(thd, active_dml_stmt::INSERTING_STMT); select_insert *sel_result; select_result *result= NULL; bool explain= MY_TEST(lex->describe);