Commit ffe7f19f authored by Monty's avatar Monty Committed by Sergei Golubchik

MDEV-24746 Atomic CREATE TRIGGER

The purpose of this task is to ensure that CREATE TRIGGER is atomic

When a trigger is created, we first create a trigger_name.TRN file and then
create or update the table_name.TRG files.
This is done by creating .TRN~ and .TRG~ files and replacing (or creating)
the result files.

The new logic is

- Log CREATE TRIGGER to DDL log, with a marker if old trigger existsted
- If old .TRN or .TRG files exists, make backup copies of these
- Create the new .TRN and .TRG files as before
- Remove the backups

Crash recovery
- If query has been logged to binary log:
  - delete any left over backup files
- else
   - Delete any old .TRN~ or .TRG~ files
   - If there was orignally some triggers (old .TRG file existed)
      - If we crashed before creating all backup files
         - Delete existing backup files
      - else
         - Restore backup files
      - end
   - Delete .TRN and .TRG file (as there was no triggers before

One benefit of the new code is that CREATE OR REPLACE TRIGGER is now
totally atomic even if there existed an old trigger: Either the old
trigger will be replaced or the old one will be left untouched.

Other things:
- If sql_create_definition_file() would fail, there could be memory leaks
  in CREATE TRIGGER, DROP TRIGGER or CREATE OR REPLACE TRIGGER.  This
  is now fixed.
parent d494abd1
To debug a the ddl_recovery code in a failing ddl_recovery test one could do
the following:
- Add # before --exec echo "restart" ...
- Force $e (engine), $c (crash point) and $r (crash position) to the values
where things goes wrong. See comments in alter_table.test for how to do this.
- start mariadbd in a debugger
run the following in the debugger
(Replace 'atomic.create_trigger' with the failing test case)
#break ha_recover
#break MYSQL_BIN_LOG::recover
#break MYSQL_BIN_LOG::open
break ddl_log_close_binlogged_events
break ddl_log_execute_action
break ddl_log_execute_recovery
run --datadir=/my/maria-10.6/mysql-test/var/log/atomic.create_trigger/mysqld.1/data --log-basename=master --log-bin-index=mysqld-bin.index --debug --log-bin
"engine: aria crash point: ddl_log_create_before_create_trigger position: 1"
"engine: aria crash point: ddl_log_create_before_create_trigger position: 2"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
"engine: aria crash point: ddl_log_create_before_create_trigger position: 3"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
"engine: aria crash point: ddl_log_create_after_create_trigger position: 1"
"engine: aria crash point: ddl_log_create_after_create_trigger position: 2"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
"engine: aria crash point: ddl_log_create_after_create_trigger position: 3"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
"engine: aria crash point: definition_file_after_create position: 1"
"engine: aria crash point: definition_file_after_create position: 2"
"engine: aria crash point: definition_file_after_create position: 3"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
"engine: aria crash point: ddl_log_drop_before_binlog position: 1"
"engine: aria crash point: ddl_log_drop_before_binlog position: 2"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
"engine: aria crash point: ddl_log_drop_before_binlog position: 3"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
"engine: aria crash point: ddl_log_drop_after_binlog position: 1"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
"engine: aria crash point: ddl_log_drop_after_binlog position: 2"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
"engine: aria crash point: ddl_log_drop_after_binlog position: 3"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end
"engine: aria crash point: ddl_log_drop_before_delete_tmp position: 1"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
"engine: aria crash point: ddl_log_drop_before_delete_tmp position: 2"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
"engine: aria crash point: ddl_log_drop_before_delete_tmp position: 3"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end
master-bin.000001 # Query # # use `test`; CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end
--source include/have_debug.inc
--source include/have_log_bin.inc
--source include/not_valgrind.inc
#
# Testing of atomic CREATE TRIGGER with crashes in a lot of different places
#
let $MYSQLD_DATADIR= `SELECT @@datadir`;
let $engine_count=1;
let $engines='aria';
let $crash_count=6;
let $crash_points='ddl_log_create_before_create_trigger', 'ddl_log_create_after_create_trigger', 'definition_file_after_create', 'ddl_log_drop_before_binlog', 'ddl_log_drop_after_binlog','ddl_log_drop_before_delete_tmp';
let $old_debug=`select @@debug_dbug`;
let $e=0;
let $keep_include_silent=1;
let $grep_script=CREATE.*TRIGGER;
let $drops=3;
--disable_query_log
while ($e < $engine_count)
{
inc $e;
let $engine=`select ELT($e, $engines)`;
let $default_engine=$engine;
let $extra_option=;
if ($engine == "aria")
{
let $extra_option=transactional=1;
}
if ($engine == "aria_notrans")
{
let $default_engine="aria";
let $extra_option=transactional=0;
}
--eval set @@default_storage_engine=$default_engine
--eval create table t1 (a int not null, b int not null) $extra_option;
insert into t1 values(1,1);
flush tables;
let $c=0;
while ($c < $crash_count)
{
inc $c;
let $crash=`select ELT($c, $crash_points)`;
let $r=0;
while ($r < $drops)
{
inc $r;
RESET MASTER;
echo "engine: $engine crash point: $crash position: $r";
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--disable_reconnect
--eval set @@debug_dbug="+d,$crash",@debug_crash_counter=$r
let $errno=0;
delimiter |;
--error 0,2013
CREATE TRIGGER t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end|
delimiter ;|
let $error=$errno;
if ($error == 0)
{
delimiter |;
--error 0,2013
CREATE OR REPLACE TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end|
delimiter ;|
let $error=$errno;
}
if ($error == 0)
{
delimiter |;
--error 0,2013
CREATE OR REPLACE TRIGGER t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end|
delimiter ;|
let $error=$errno;
}
--enable_reconnect
--source include/wait_until_connected_again.inc
--disable_query_log
--eval set @@debug_dbug="$old_debug"
if ($error == 0)
{
echo "No crash!";
}
# Check which tables still exists
--list_files $MYSQLD_DATADIR/test *TR*
--list_files $MYSQLD_DATADIR/test *sql*
--replace_column 7 #
--error 0,ER_TRG_DOES_NOT_EXIST
SHOW CREATE TRIGGER t1_trg;
--replace_column 7 #
--error 0,ER_TRG_DOES_NOT_EXIST
SHOW CREATE TRIGGER t2_trg;
--let $binlog_file=master-bin.000001
--source include/show_binlog_events.inc
--disable_warnings
drop trigger if exists t1_trg;
drop trigger if exists t2_trg;
--enable_warnings
}
}
}
drop table t1;
--enable_query_log
"position: 1"
"position: 2"
"position: 3"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
"position: 4"
t1.TRG
t1_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
"position: 5"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
"position: 6"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
"position: 7"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
"position: 8"
t1.TRG
t1_trg.TRN
t2_trg.TRN
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t1_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
t2_trg STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` trigger t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end latin1 latin1_swedish_ci latin1_swedish_ci #
--source include/have_debug.inc
#
# Testing of atomic CREATE TRIGGER when write fails in create_definition_file
#
let $MYSQLD_DATADIR= `SELECT @@datadir`;
let $engine_count=1;
let $engines='aria';
let $old_debug=`select @@debug_dbug`;
let $e=0;
--disable_query_log
create table t1 (a int not null, b int not null);
insert into t1 values(1,1);
flush tables;
# sql_create_definition_file is called twice per CREATE TRIGGER and 1 more
# in case we drop an existing trigger, so we need to test 3*2 +1 failures
# and also when there is no failures (= 8)
let $try_count=8;
let $r=0;
while ($r < $try_count)
{
inc $r;
echo "position: $r";
--eval set @@debug_dbug="+d,definition_file_simulate_write_error",@debug_error_counter=$r;
let $errno=0;
delimiter |;
--error 0,3
create trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end|
delimiter ;|
let $error=$errno;
if ($error == 0)
{
delimiter |;
--error 0,3
create or replace trigger t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end|
delimiter ;|
let $error=$errno;
}
if ($error == 0)
{
delimiter |;
--error 0,3
create or replace trigger t2_trg before insert on t1 for each row
begin
if isnull(new.b) then
set new.b:= 3000;
end if;
end|
delimiter ;|
let $error=$errno;
}
--eval set @@debug_dbug="$old_debug"
# Check which tables still exists
--list_files $MYSQLD_DATADIR/test *TR*
--list_files $MYSQLD_DATADIR/test *sql*
--replace_column 7 #
--error 0,ER_TRG_DOES_NOT_EXIST
SHOW CREATE TRIGGER t1_trg;
--replace_column 7 #
--error 0,ER_TRG_DOES_NOT_EXIST
SHOW CREATE TRIGGER t2_trg;
--disable_warnings
drop trigger if exists t1_trg;
drop trigger if exists t2_trg;
--enable_warnings
}
drop table t1;
--enable_query_log
...@@ -90,7 +90,7 @@ const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]= ...@@ -90,7 +90,7 @@ const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
"rename table", "rename view", "rename table", "rename view",
"initialize drop table", "drop table", "initialize drop table", "drop table",
"drop view", "drop trigger", "drop db", "create table", "create view", "drop view", "drop trigger", "drop db", "create table", "create view",
"delete tmp file", "delete tmp file", "create trigger",
}; };
/* Number of phases per entry */ /* Number of phases per entry */
...@@ -100,7 +100,7 @@ const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]= ...@@ -100,7 +100,7 @@ const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]=
(uchar) EXCH_PHASE_END, (uchar) DDL_RENAME_PHASE_END, 1, 1, (uchar) EXCH_PHASE_END, (uchar) DDL_RENAME_PHASE_END, 1, 1,
(uchar) DDL_DROP_PHASE_END, 1, 1, (uchar) DDL_DROP_PHASE_END, 1, 1,
(uchar) DDL_DROP_DB_PHASE_END, (uchar) DDL_CREATE_TABLE_PHASE_END, (uchar) DDL_DROP_DB_PHASE_END, (uchar) DDL_CREATE_TABLE_PHASE_END,
(uchar) DDL_CREATE_VIEW_PHASE_END, 0, (uchar) DDL_CREATE_VIEW_PHASE_END, 0, (uchar) DDL_CREATE_TRIGGER_PHASE_END,
}; };
...@@ -1637,7 +1637,96 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, ...@@ -1637,7 +1637,96 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root,
(void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE); (void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
break; break;
} }
break; case DDL_LOG_CREATE_TRIGGER_ACTION:
{
LEX_CSTRING db, table, trigger;
db= ddl_log_entry->db;
table= ddl_log_entry->name;
trigger= ddl_log_entry->tmp_name;
/* Delete backup .TRG (trigger file) if it exists */
(void) build_filename_and_delete_tmp_file(to_path, sizeof(to_path) - 1,
&db, &table,
TRG_EXT,
key_file_fileparser);
(void) build_filename_and_delete_tmp_file(to_path, sizeof(to_path) - 1,
&db, &trigger,
TRN_EXT,
key_file_fileparser);
switch (ddl_log_entry->phase) {
case DDL_CREATE_TRIGGER_PHASE_DELETE_COPY:
{
size_t length;
/* Delete copy of .TRN and .TRG files */
length= build_table_filename(to_path, sizeof(to_path) - 1,
db.str, table.str, TRG_EXT, 0);
to_path[length]= '-';
to_path[length+1]= 0;
mysql_file_delete(key_file_fileparser, to_path,
MYF(MY_WME|MY_IGNORE_ENOENT));
length= build_table_filename(to_path, sizeof(to_path) - 1,
db.str, trigger.str, TRN_EXT, 0);
to_path[length]= '-';
to_path[length+1]= 0;
mysql_file_delete(key_file_fileparser, to_path,
MYF(MY_WME|MY_IGNORE_ENOENT));
}
/* Nothing else to do */
(void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
break;
case DDL_CREATE_TRIGGER_PHASE_OLD_COPIED:
{
LEX_CSTRING path= {to_path, 0};
size_t length;
/* Restore old version if the .TRN and .TRG files */
length= build_table_filename(to_path, sizeof(to_path) - 1,
db.str, table.str, TRG_EXT, 0);
to_path[length]='-';
to_path[length+1]= 0;
path.length= length+1;
/* an old TRN file only exist in the case if REPLACE was used */
if (!access(to_path, F_OK))
sql_restore_definition_file(&path);
length= build_table_filename(to_path, sizeof(to_path) - 1,
db.str, trigger.str, TRN_EXT, 0);
to_path[length]='-';
to_path[length+1]= 0;
path.length= length+1;
if (!access(to_path, F_OK))
sql_restore_definition_file(&path);
else
{
/*
There was originally no .TRN for this trigger.
Delete the newly created one.
*/
to_path[length]= 0;
mysql_file_delete(key_file_fileparser, to_path,
MYF(MY_WME|MY_IGNORE_ENOENT));
}
(void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
break;
}
case DDL_CREATE_TRIGGER_PHASE_NO_OLD_TRIGGER:
{
/* No old trigger existed. We can just delete the .TRN and .TRG files */
build_table_filename(to_path, sizeof(to_path) - 1,
db.str, table.str, TRG_EXT, 0);
mysql_file_delete(key_file_fileparser, to_path,
MYF(MY_WME|MY_IGNORE_ENOENT));
build_table_filename(to_path, sizeof(to_path) - 1,
db.str, trigger.str, TRN_EXT, 0);
mysql_file_delete(key_file_fileparser, to_path,
MYF(MY_WME|MY_IGNORE_ENOENT));
(void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
break;
}
}
break;
}
}
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
break; break;
...@@ -2633,3 +2722,25 @@ bool ddl_log_delete_tmp_file(THD *thd, DDL_LOG_STATE *ddl_state, ...@@ -2633,3 +2722,25 @@ bool ddl_log_delete_tmp_file(THD *thd, DDL_LOG_STATE *ddl_state,
ddl_log_entry.unique_id= depending_state->execute_entry->entry_pos; ddl_log_entry.unique_id= depending_state->execute_entry->entry_pos;
DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry)); DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
} }
/**
Log CREATE TRIGGER
*/
bool ddl_log_create_trigger(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *db, const LEX_CSTRING *table,
const LEX_CSTRING *trigger_name,
enum_ddl_log_create_trigger_phase phase)
{
DDL_LOG_ENTRY ddl_log_entry;
DBUG_ENTER("ddl_log_create_view");
bzero(&ddl_log_entry, sizeof(ddl_log_entry));
ddl_log_entry.action_type= DDL_LOG_CREATE_TRIGGER_ACTION;
ddl_log_entry.db= *const_cast<LEX_CSTRING*>(db);
ddl_log_entry.name= *const_cast<LEX_CSTRING*>(table);
ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(trigger_name);
ddl_log_entry.phase= (uchar) phase;
DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
}
...@@ -85,6 +85,7 @@ enum ddl_log_action_code ...@@ -85,6 +85,7 @@ enum ddl_log_action_code
DDL_LOG_CREATE_TABLE_ACTION=12, DDL_LOG_CREATE_TABLE_ACTION=12,
DDL_LOG_CREATE_VIEW_ACTION=13, DDL_LOG_CREATE_VIEW_ACTION=13,
DDL_LOG_DELETE_TMP_FILE_ACTION=14, DDL_LOG_DELETE_TMP_FILE_ACTION=14,
DDL_LOG_CREATE_TRIGGER_ACTION=15,
DDL_LOG_LAST_ACTION /* End marker */ DDL_LOG_LAST_ACTION /* End marker */
}; };
...@@ -134,6 +135,13 @@ enum enum_ddl_log_create_view_phase { ...@@ -134,6 +135,13 @@ enum enum_ddl_log_create_view_phase {
DDL_CREATE_VIEW_PHASE_END DDL_CREATE_VIEW_PHASE_END
}; };
enum enum_ddl_log_create_trigger_phase {
DDL_CREATE_TRIGGER_PHASE_NO_OLD_TRIGGER,
DDL_CREATE_TRIGGER_PHASE_DELETE_COPY,
DDL_CREATE_TRIGGER_PHASE_OLD_COPIED,
DDL_CREATE_TRIGGER_PHASE_END
};
/* /*
Setting ddl_log_entry.phase to this has the same effect as setting Setting ddl_log_entry.phase to this has the same effect as setting
...@@ -282,5 +290,9 @@ bool ddl_log_create_view(THD *thd, DDL_LOG_STATE *ddl_state, ...@@ -282,5 +290,9 @@ bool ddl_log_create_view(THD *thd, DDL_LOG_STATE *ddl_state,
bool ddl_log_delete_tmp_file(THD *thd, DDL_LOG_STATE *ddl_state, bool ddl_log_delete_tmp_file(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *path, const LEX_CSTRING *path,
DDL_LOG_STATE *depending_state); DDL_LOG_STATE *depending_state);
bool ddl_log_create_trigger(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *db, const LEX_CSTRING *table,
const LEX_CSTRING *trigger_name,
enum_ddl_log_create_trigger_phase phase);
extern mysql_mutex_t LOCK_gdl; extern mysql_mutex_t LOCK_gdl;
#endif /* DDL_LOG_INCLUDED */ #endif /* DDL_LOG_INCLUDED */
...@@ -347,6 +347,48 @@ sql_create_definition_file(const LEX_CSTRING *dir, ...@@ -347,6 +347,48 @@ sql_create_definition_file(const LEX_CSTRING *dir,
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/*
Make a copy of a definition file with '-' added to the name
@param org_name Original file name
@param new_name Pointer to a buff of FN_REFLEN. Will be updated to name of
backup file
@return 0 ok
@return 1 error
*/
int sql_backup_definition_file(const LEX_CSTRING *org_name,
LEX_CSTRING *new_name)
{
char *new_name_buff= (char*) new_name->str;
new_name->length= org_name->length+1;
memcpy(new_name_buff, org_name->str, org_name->length+1);
new_name_buff[org_name->length]= '-';
new_name_buff[org_name->length+1]= 0;
return my_copy(org_name->str, new_name->str, MYF(MY_WME));
}
/*
Restore copy of a definition file
@param org_name Name of backup file (ending with '-' or '~')
@return 0 ok
@return 1 error
*/
int sql_restore_definition_file(const LEX_CSTRING *name)
{
char new_name[FN_REFLEN+1];
memcpy(new_name, name->str, name->length-1);
new_name[name->length-1]= 0;
return mysql_file_rename(key_file_fileparser, name->str, new_name,
MYF(MY_WME));
}
/** /**
Renames a frm file (including backups) in same schema. Renames a frm file (including backups) in same schema.
......
...@@ -96,6 +96,10 @@ my_bool rename_in_schema_file(THD *thd, ...@@ -96,6 +96,10 @@ my_bool rename_in_schema_file(THD *thd,
const char *schema, const char *old_name, const char *schema, const char *old_name,
const char *new_db, const char *new_name); const char *new_db, const char *new_name);
int sql_backup_definition_file(const LEX_CSTRING *org_name,
LEX_CSTRING *new_name);
int sql_restore_definition_file(const LEX_CSTRING *name);
class File_parser: public Sql_alloc class File_parser: public Sql_alloc
{ {
char *start, *end; char *start, *end;
......
...@@ -402,12 +402,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) ...@@ -402,12 +402,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
MDL_ticket *mdl_ticket= NULL; MDL_ticket *mdl_ticket= NULL;
MDL_request mdl_request_for_trn; MDL_request mdl_request_for_trn;
Query_tables_list backup; Query_tables_list backup;
DDL_LOG_STATE ddl_log_state; DDL_LOG_STATE ddl_log_state, ddl_log_state_tmp_file;
DBUG_ENTER("mysql_create_or_drop_trigger"); DBUG_ENTER("mysql_create_or_drop_trigger");
/* Charset of the buffer for statement must be system one. */ /* Charset of the buffer for statement must be system one. */
stmt_query.set_charset(system_charset_info); stmt_query.set_charset(system_charset_info);
bzero(&ddl_log_state, sizeof(ddl_log_state)); bzero(&ddl_log_state, sizeof(ddl_log_state));
bzero(&ddl_log_state_tmp_file, sizeof(ddl_log_state_tmp_file));
/* /*
QQ: This function could be merged in mysql_alter_table() function QQ: This function could be merged in mysql_alter_table() function
...@@ -608,7 +609,9 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) ...@@ -608,7 +609,9 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
if (create) if (create)
result= table->triggers->create_trigger(thd, tables, &stmt_query); result= table->triggers->create_trigger(thd, tables, &stmt_query,
&ddl_log_state,
&ddl_log_state_tmp_file);
else else
{ {
result= table->triggers->drop_trigger(thd, tables, result= table->triggers->drop_trigger(thd, tables,
...@@ -644,7 +647,9 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) ...@@ -644,7 +647,9 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
debug_crash_here("ddl_log_drop_after_binlog"); debug_crash_here("ddl_log_drop_after_binlog");
} }
ddl_log_complete(&ddl_log_state); ddl_log_complete(&ddl_log_state);
debug_crash_here("ddl_log_drop_before_delete_tmp");
/* delete any created log files */
ddl_log_revert(thd, &ddl_log_state_tmp_file);
/* /*
If we are under LOCK TABLES we should restore original state of If we are under LOCK TABLES we should restore original state of
meta-data locks. Otherwise all locks will be released along meta-data locks. Otherwise all locks will be released along
...@@ -797,18 +802,23 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, ...@@ -797,18 +802,23 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables,
*/ */
bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
String *stmt_query) String *stmt_query,
DDL_LOG_STATE *ddl_log_state,
DDL_LOG_STATE *ddl_log_state_tmp_file)
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
TABLE *table= tables->table; TABLE *table= tables->table;
char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN]; char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
LEX_CSTRING file, trigname_file; char backup_file_buff[FN_REFLEN];
char trg_definer_holder[USER_HOST_BUFF_SIZE]; char trg_definer_holder[USER_HOST_BUFF_SIZE];
LEX_CSTRING backup_name= { backup_file_buff, 0 };
LEX_CSTRING file, trigname_file;
Item_trigger_field *trg_field; Item_trigger_field *trg_field;
struct st_trigname trigname; struct st_trigname trigname;
String trigger_definition; String trigger_definition;
Trigger *trigger= 0; Trigger *trigger= 0;
bool trigger_dropped= 0; int error;
bool trigger_exists;
DBUG_ENTER("create_trigger"); DBUG_ENTER("create_trigger");
if (check_for_broken_triggers()) if (check_for_broken_triggers())
...@@ -882,11 +892,34 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -882,11 +892,34 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
trigname_file.str= trigname_buff; trigname_file.str= trigname_buff;
/* Use the filesystem to enforce trigger namespace constraints. */ /* Use the filesystem to enforce trigger namespace constraints. */
if (!access(trigname_buff, F_OK)) trigger_exists= !access(trigname_file.str, F_OK);
ddl_log_create_trigger(thd, ddl_log_state, &tables->db, &tables->table_name,
&lex->spname->m_name,
trigger_exists || table->triggers->count ?
DDL_CREATE_TRIGGER_PHASE_DELETE_COPY :
DDL_CREATE_TRIGGER_PHASE_NO_OLD_TRIGGER);
/* Make a backup of the .TRG file that we can restore in case of crash */
if (table->triggers->count &&
(sql_backup_definition_file(&file, &backup_name) ||
ddl_log_delete_tmp_file(thd, ddl_log_state_tmp_file, &backup_name,
ddl_log_state)))
DBUG_RETURN(true);
if (trigger_exists)
{ {
if (lex->create_info.or_replace()) if (lex->create_info.or_replace())
{ {
LEX_CSTRING *sp_name= &thd->lex->spname->m_name; // alias LEX_CSTRING *sp_name= &thd->lex->spname->m_name; // alias
/* Make a backup of the .TRN file that we can restore in case of crash */
if (sql_backup_definition_file(&trigname_file, &backup_name) ||
ddl_log_delete_tmp_file(thd, ddl_log_state_tmp_file, &backup_name,
ddl_log_state))
DBUG_RETURN(true);
ddl_log_update_phase(ddl_log_state, DDL_CREATE_TRIGGER_PHASE_OLD_COPIED);
/* /*
The following can fail if the trigger is for another table or The following can fail if the trigger is for another table or
there exists a .TRN file but there was no trigger for it in there exists a .TRN file but there was no trigger for it in
...@@ -922,6 +955,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -922,6 +955,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
DBUG_RETURN(true); DBUG_RETURN(true);
} }
} }
else
{
if (table->triggers->count)
ddl_log_update_phase(ddl_log_state, DDL_CREATE_TRIGGER_PHASE_OLD_COPIED);
}
trigname.trigger_table= tables->table_name; trigname.trigger_table= tables->table_name;
...@@ -930,12 +968,16 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -930,12 +968,16 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
going to access lex->sphead later in build_trig_stmt_query() going to access lex->sphead later in build_trig_stmt_query()
*/ */
if (!(trigger= new (&table->mem_root) Trigger(this, 0))) if (!(trigger= new (&table->mem_root) Trigger(this, 0)))
goto err_without_cleanup; goto err;
/* Create trigger_name.TRN file to ensure trigger name is unique */ /* Create trigger_name.TRN file to ensure trigger name is unique */
if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type, if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
(uchar*)&trigname, trigname_file_parameters)) (uchar*)&trigname, trigname_file_parameters))
goto err_without_cleanup; {
delete trigger;
trigger= 0;
goto err;
}
/* Populate the trigger object */ /* Populate the trigger object */
...@@ -957,6 +999,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -957,6 +999,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
trigger->client_cs_name= thd->charset()->cs_name; trigger->client_cs_name= thd->charset()->cs_name;
trigger->connection_cl_name= thd->variables.collation_connection->coll_name; trigger->connection_cl_name= thd->variables.collation_connection->coll_name;
trigger->db_cl_name= get_default_db_collation(thd, tables->db.str)->coll_name; trigger->db_cl_name= get_default_db_collation(thd, tables->db.str)->coll_name;
trigger->name= lex->spname->m_name;
/* Add trigger in it's correct place */ /* Add trigger in it's correct place */
add_trigger(lex->trg_chistics.event, add_trigger(lex->trg_chistics.event,
...@@ -967,30 +1010,31 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, ...@@ -967,30 +1010,31 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
/* Create trigger definition file .TRG */ /* Create trigger definition file .TRG */
if (unlikely(create_lists_needed_for_files(thd->mem_root))) if (unlikely(create_lists_needed_for_files(thd->mem_root)))
goto err_with_cleanup; goto err;
if (!sql_create_definition_file(NULL, &file, &triggers_file_type,
(uchar*)this, triggers_file_parameters))
DBUG_RETURN(false);
err_with_cleanup: debug_crash_here("ddl_log_create_before_create_trigger");
/* Delete .TRN file */ error= sql_create_definition_file(NULL, &file, &triggers_file_type,
mysql_file_delete(key_file_trn, trigname_buff, MYF(MY_WME)); (uchar*)this, triggers_file_parameters);
debug_crash_here("ddl_log_create_after_create_trigger");
err_without_cleanup: if (!error)
delete trigger; // Safety, not critical DBUG_RETURN(false);
if (trigger_dropped) err:
DBUG_PRINT("error",("create trigger failed"));
if (trigger)
{ {
String drop_trg_query; my_debug_put_break_here();
drop_trg_query.append(STRING_WITH_LEN("DROP TRIGGER /* generated by failed CREATE TRIGGER */ ")); /* Delete trigger from trigger list if it exists */
drop_trg_query.append(&lex->spname->m_name); find_trigger(&trigger->name, 1);
/* /* Free trigger memory */
We dropped an existing trigger and was not able to recreate it because delete trigger;
of an internal error. Ensure it's also dropped on the slave.
*/
write_bin_log(thd, FALSE, drop_trg_query.ptr(), drop_trg_query.length());
} }
/* Recover the old .TRN and .TRG files & delete backup files */
ddl_log_revert(thd, ddl_log_state);
/* All backup files are now deleted */
ddl_log_complete(ddl_log_state_tmp_file);
DBUG_RETURN(true); DBUG_RETURN(true);
} }
...@@ -1113,15 +1157,17 @@ bool Table_triggers_list::save_trigger_file(THD *thd, const LEX_CSTRING *db, ...@@ -1113,15 +1157,17 @@ bool Table_triggers_list::save_trigger_file(THD *thd, const LEX_CSTRING *db,
{ {
char file_buff[FN_REFLEN]; char file_buff[FN_REFLEN];
LEX_CSTRING file; LEX_CSTRING file;
DBUG_ENTER("Table_triggers_list::save_trigger_file");
if (create_lists_needed_for_files(thd->mem_root)) if (create_lists_needed_for_files(thd->mem_root))
return true; DBUG_RETURN(true);
file.length= build_table_filename(file_buff, FN_REFLEN - 1, db->str, table_name->str, file.length= build_table_filename(file_buff, FN_REFLEN - 1, db->str, table_name->str,
TRG_EXT, 0); TRG_EXT, 0);
file.str= file_buff; file.str= file_buff;
return sql_create_definition_file(NULL, &file, &triggers_file_type, DBUG_RETURN(sql_create_definition_file(NULL, &file, &triggers_file_type,
(uchar*) this, triggers_file_parameters); (uchar*) this,
triggers_file_parameters));
} }
...@@ -1191,6 +1237,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, ...@@ -1191,6 +1237,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
{ {
char path[FN_REFLEN]; char path[FN_REFLEN];
Trigger *trigger; Trigger *trigger;
DBUG_ENTER("Table_triggers_list::drop_trigger");
if (stmt_query) if (stmt_query)
stmt_query->set(thd->query(), thd->query_length(), stmt_query->charset()); stmt_query->set(thd->query(), thd->query_length(), stmt_query->charset());
...@@ -1200,9 +1247,11 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, ...@@ -1200,9 +1247,11 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
{ {
my_message(ER_TRG_DOES_NOT_EXIST, ER_THD(thd, ER_TRG_DOES_NOT_EXIST), my_message(ER_TRG_DOES_NOT_EXIST, ER_THD(thd, ER_TRG_DOES_NOT_EXIST),
MYF(0)); MYF(0));
return 1; DBUG_RETURN(1);
} }
delete trigger;
if (ddl_log_state)
{ {
LEX_CSTRING query= {0,0}; LEX_CSTRING query= {0,0};
if (stmt_query) if (stmt_query)
...@@ -1210,41 +1259,40 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, ...@@ -1210,41 +1259,40 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
/* This code is executed in case of DROP TRIGGER */ /* This code is executed in case of DROP TRIGGER */
lex_string_set3(&query, thd->query(), thd->query_length()); lex_string_set3(&query, thd->query(), thd->query_length());
} }
if (ddl_log_drop_trigger(thd, ddl_log_state,
if (ddl_log_state) &tables->db, &tables->table_name,
if (ddl_log_drop_trigger(thd, ddl_log_state, sp_name, &query))
&tables->db, &tables->table_name, goto err;
sp_name, &query))
return 1;
} }
debug_crash_here("ddl_log_drop_before_drop_trigger"); debug_crash_here("ddl_log_drop_before_drop_trigger");
if (!count) // If no more triggers if (!count) // If no more triggers
{ {
/* /*
TODO: Probably instead of removing .TRG file we should move It is safe to remove the trigger file. If something goes wrong during
to archive directory but this should be done as part of drop or create ddl_log recovery will ensure that all related
parse_file.cc functionality (because we will need it trigger files are deleted or the original ones are restored.
elsewhere).
*/ */
if (rm_trigger_file(path, &tables->db, &tables->table_name, MYF(MY_WME))) if (rm_trigger_file(path, &tables->db, &tables->table_name, MYF(MY_WME)))
return 1; goto err;
} }
else else
{ {
if (save_trigger_file(thd, &tables->db, &tables->table_name)) if (save_trigger_file(thd, &tables->db, &tables->table_name))
return 1; goto err;
} }
debug_crash_here("ddl_log_drop_before_drop_trn"); debug_crash_here("ddl_log_drop_before_drop_trn");
if (rm_trigname_file(path, &tables->db, sp_name, MYF(MY_WME))) if (rm_trigname_file(path, &tables->db, sp_name, MYF(MY_WME)))
return 1; goto err;
debug_crash_here("ddl_log_drop_after_drop_trigger"); debug_crash_here("ddl_log_drop_after_drop_trigger");
delete trigger; DBUG_RETURN(0);
return 0;
err:
DBUG_RETURN(1);
} }
......
...@@ -220,7 +220,9 @@ class Table_triggers_list: public Sql_alloc ...@@ -220,7 +220,9 @@ class Table_triggers_list: public Sql_alloc
} }
~Table_triggers_list(); ~Table_triggers_list();
bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query); bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query,
DDL_LOG_STATE *ddl_log_state,
DDL_LOG_STATE *ddl_log_state_tmp_file);
bool drop_trigger(THD *thd, TABLE_LIST *table, bool drop_trigger(THD *thd, TABLE_LIST *table,
LEX_CSTRING *sp_name, LEX_CSTRING *sp_name,
String *stmt_query, DDL_LOG_STATE *ddl_log_state); String *stmt_query, DDL_LOG_STATE *ddl_log_state);
......
...@@ -1178,11 +1178,8 @@ static int mysql_register_view(THD *thd, DDL_LOG_STATE *ddl_log_state, ...@@ -1178,11 +1178,8 @@ static int mysql_register_view(THD *thd, DDL_LOG_STATE *ddl_log_state,
if (old_view_exists) if (old_view_exists)
{ {
/* Make a backup that we can restore in case of crash */ LEX_CSTRING backup_name= { backup_file_name, 0 };
memcpy(backup_file_name, path.str, path.length); if (sql_backup_definition_file(&path, &backup_name))
backup_file_name[path.length]='-';
backup_file_name[path.length+1]= 0;
if (my_copy(path.str, backup_file_name, MYF(MY_WME)))
{ {
error= 1; error= 1;
goto err; goto err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment