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

MDEV-23844 Atomic DROP TABLE (single table)

Logging logic:
- Log tables, just before they are dropped, to the ddl log
- After the last table for the statement is dropped, log an xid for the
  whole ddl log event

In case of crash:
- Remove first any active DROP TABLE events from the ddl log that matches
  xids found in binary log (this mean the drop was successful and was
  propery logged).
- Loop over all active DROP TABLE events
  - Ensure that the table is completely dropped
- Write a DROP TABLE entry to the binary log with the dropped tables.

Other things:
- Added code to ha_drop_table() to be able to tell the difference if
  a get_new_handler() failed because of out-of-memory or because the
  handler refused/was not able to create a a handler. This was needed
  to get sequences to work as sequences needs a share object to be passed
  to get_new_handler()
- TC_LOG_BINLOG::recover() was changed to always collect Xid's from the
  binary log and always call ddl_log_close_binlogged_events(). This was
  needed to be able to collect DROP TABLE events with embedded Xid's
  (used by ddl log).
- Added a new variable "$grep_script" to binlog filter to be able to find
  only rows that matches a regexp.
- Had to adjust some test that changed because drop statements are a bit
  larger in the binary log than before (as we have to store the xid)

Other things:
- MDEV-25588 Atomic DDL: Binlog query event written upon recovery is corrupt
  fixed (in the original commit).
parent 47010ccf
......@@ -56,6 +56,9 @@
#
# $filter_script
# If set, rows matching this regexp will be filtered out
#
# $grep_script
# If set, only include rows matching this regexp
--let $include_filename= filter_file.inc
--source include/begin_include_file.inc
......@@ -71,6 +74,7 @@ if ($rpl_debug)
--let _FF_PRE_SCRIPT= $pre_script
--let _FF_SCRIPT= $script
--let _FF_FILTER_SCRIPT= $filter_script
--let _FF_GREP_SCRIPT= $grep_script
--let _FF_INPUT_FILE= $input_file
--let _FF_OUTPUT_FILE= $output_file
--let _FF_SELECT_COLUMNS= $select_columns
......@@ -85,6 +89,7 @@ perl;
$pre_script =~ s/DOLLAR/\$/g;
my $script = $ENV{'_FF_SCRIPT'};
my $filter_script = $ENV{'_FF_FILTER_SCRIPT'};
my $grep_script = $ENV{'_FF_GREP_SCRIPT'};
$script =~ s/DOLLAR/\$/g;
my $input_file = $ENV{'_FF_INPUT_FILE'};
my $output_file = $ENV{'_FF_OUTPUT_FILE'};
......@@ -129,7 +134,8 @@ perl;
{
' . $script . '
}
if (!$filter_script || ! m/$filter_script/)
if ((!$filter_script || ! m/$filter_script/) &&
(!$grep_script || m/$grep_script/))
{
$filtered_contents .= $_."\n";
}
......
call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal");
"engine: aria crash point: ddl_log_drop_before_delete_table position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_delete_table position: 2"
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_delete_table position: 3"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_table position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_table position: 2"
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_table position: 3"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger position: 2"
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger position: 3"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 2"
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 3"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_drop_trigger position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_drop_trigger position: 2"
ts.MAD
ts.MAI
ts.frm
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_drop_trigger position: 3"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2`,`test`.`ts` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */
"engine: aria crash point: ddl_log_drop_before_binlog position: 3"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */
"engine: aria crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */
"engine: aria crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */
"engine: aria crash point: ddl_log_drop_after_binlog position: 3"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2`,`ts` /* generated by server */
Warnings:
Note 1051 Unknown table 'test.t1,test.t2,test.ts'
--source include/have_debug.inc
--source include/have_log_bin.inc
--source include/not_valgrind.inc
#
# Testing of atomic drop with crashes in a lot of different places
#
call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal");
let $MYSQLD_DATADIR= `SELECT @@datadir`;
let $engine_count=1;
let $engines='aria';
let $crash_count=7;
let $crash_points='ddl_log_drop_before_delete_table', 'ddl_log_drop_after_delete_table', 'ddl_log_drop_before_drop_trigger', 'ddl_log_drop_before_drop_trigger2', 'ddl_log_drop_after_drop_trigger', 'ddl_log_drop_before_binlog', 'ddl_log_drop_after_binlog';
# Number of drops in the tested statement
let $drops=3;
let $old_debug=`select @@debug_dbug`;
let $e=0;
let $keep_include_silent=1;
let $grep_script=DROP TABLE;
--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;
}
let $c=0;
while ($c < $crash_count)
{
inc $c;
let $crash=`select ELT($c, $crash_points)`;
let $r=0;
while ($r < $drops)
{
inc $r;
--eval set @@default_storage_engine=$default_engine
--eval create table t1 (a int not null) $extra_option;
--eval create table t2 (b int not null) $extra_option;
create sequence ts;
insert into t1 values(1);
insert into t2 values(2);
flush tables;
delimiter |;
create trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end|
create trigger t2_trg before insert on t2 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end|
delimiter ;|
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;
--error 0,2013
drop table t1,t2,ts;
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 t*
--let $binlog_file=master-bin.000001
--source include/show_binlog_events.inc
if ($error)
{
--let $binlog_file=master-bin.000002
--source include/show_binlog_events.inc
}
# Really drop the tables. The warnings will show what was dropped
--disable_warnings
drop table if exists t1,t2,ts;
--enable_warnings
}
}
}
drop table if exists t1,t2,ts;
--enable_query_log
call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal");
"engine: myisam crash point: ddl_log_drop_before_delete_table position: 1"
t2.MYD
t2.MYI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_after_delete_table position: 1"
t2.MYD
t2.MYI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_after_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_drop_trigger position: 1"
t2.MYD
t2.MYI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_drop_trigger2 position: 1"
t2.MYD
t2.MYI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_drop_trigger2 position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_after_drop_trigger position: 1"
t2.MYD
t2.MYI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_after_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: myisam crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: myisam crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: myisam crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: aria crash point: ddl_log_drop_before_delete_table position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_table position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_drop_trigger2 position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_drop_trigger position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: aria crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: aria crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: aria_notrans crash point: ddl_log_drop_before_delete_table position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_after_delete_table position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_after_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger2 position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_drop_trigger2 position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_after_drop_trigger position: 1"
t2.MAD
t2.MAI
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_after_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: aria_notrans crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: aria_notrans crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: aria_notrans crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: innodb crash point: ddl_log_drop_before_delete_table position: 1"
t2.TRG
t2.frm
t2.ibd
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_after_delete_table position: 1"
t2.TRG
t2.frm
t2.ibd
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_after_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_drop_trigger position: 1"
t2.TRG
t2.frm
t2.ibd
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_drop_trigger2 position: 1"
t2.TRG
t2.frm
t2.ibd
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_drop_trigger2 position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_after_drop_trigger position: 1"
t2.TRG
t2.frm
t2.ibd
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_after_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: innodb crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: innodb crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: innodb crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: csv crash point: ddl_log_drop_before_delete_table position: 1"
t2.CSM
t2.CSV
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_after_delete_table position: 1"
t2.CSM
t2.CSV
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_after_delete_table position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_drop_trigger position: 1"
t2.CSM
t2.CSV
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_drop_trigger2 position: 1"
t2.CSM
t2.CSV
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_drop_trigger2 position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_after_drop_trigger position: 1"
t2.CSM
t2.CSV
t2.TRG
t2.frm
t2_trg.TRN
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_after_drop_trigger position: 2"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP TABLE IF EXISTS `test`.`t1`,`test`.`t2` /* generated by ddl recovery */
"engine: csv crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: csv crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
"engine: csv crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */
Warnings:
Note 1051 Unknown table 'test.t1,test.t2'
--source include/have_debug.inc
--source include/have_innodb.inc
--source include/have_csv.inc
--source include/have_log_bin.inc
--source include/not_valgrind.inc
#
# Testing of atomic drop with crashes in a lot of different places
#
call mtr.add_suppression("InnoDB: .* does not exist in the InnoDB internal");
let $MYSQLD_DATADIR= `SELECT @@datadir`;
let $engine_count=5;
let $engines='myisam','aria','aria_notrans','innodb','csv';
let $crash_count=7;
let $crash_points='ddl_log_drop_before_delete_table', 'ddl_log_drop_after_delete_table', 'ddl_log_drop_before_drop_trigger', 'ddl_log_drop_before_drop_trigger2', 'ddl_log_drop_after_drop_trigger', 'ddl_log_drop_before_binlog', 'ddl_log_drop_after_binlog';
# Number of drops in the tested statement
let $drops=2;
let $old_debug=`select @@debug_dbug`;
let $e=0;
let $keep_include_silent=1;
let $grep_script=DROP TABLE;
--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;
}
let $c=0;
while ($c < $crash_count)
{
inc $c;
let $crash=`select ELT($c, $crash_points)`;
let $r=0;
while ($r < $drops)
{
inc $r;
--eval set @@default_storage_engine=$default_engine
--eval create table t1 (a int not null) $extra_option;
--eval create table t2 (b int not null) $extra_option;
insert into t1 values(1);
insert into t2 values(2);
flush tables;
delimiter |;
create trigger t1_trg before insert on t1 for each row
begin
if isnull(new.a) then
set new.a:= 1000;
end if;
end|
create trigger t2_trg before insert on t2 for each row
begin
if isnull(new.b) then
set new.b:= 2000;
end if;
end|
delimiter ;|
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;
--error 0,2013
drop table t1,t2;
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 t*
--let $binlog_file=master-bin.000001
--source include/show_binlog_events.inc
if ($error)
{
--let $binlog_file=master-bin.000002
--source include/show_binlog_events.inc
}
# Really drop the tables. The warnings will show what was dropped
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
}
}
}
drop table if exists t1,t2;
--enable_query_log
"engine: aria crash point: ddl_log_drop_before_delete_view position: 1"
v2.frm
master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_delete_view position: 2"
master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1`,`test`.`v2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_view position: 1"
v2.frm
master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_after_delete_view position: 2"
master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1`,`test`.`v2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_binlog position: 1"
master-bin.000002 # Query # # DROP VIEW IF EXISTS `test`.`v1`,`test`.`v2` /* generated by ddl recovery */
"engine: aria crash point: ddl_log_drop_before_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP VIEW v1,v2
"engine: aria crash point: ddl_log_drop_after_binlog position: 1"
master-bin.000001 # Query # # use `test`; DROP VIEW v1,v2
"engine: aria crash point: ddl_log_drop_after_binlog position: 2"
"No crash!"
master-bin.000001 # Query # # use `test`; DROP VIEW v1,v2
--source include/have_debug.inc
--source include/have_log_bin.inc
--source include/not_valgrind.inc
#
# Testing of atomic drop of view with crashes in a lot of different places
#
let $MYSQLD_DATADIR= `SELECT @@datadir`;
let $engine_count=1;
let $engines='aria';
let $crash_count=4;
let $crash_points='ddl_log_drop_before_delete_view', 'ddl_log_drop_after_delete_view', 'ddl_log_drop_before_binlog', 'ddl_log_drop_after_binlog';
# Number of drops in the tested statement
let $drops=2;
let $old_debug=`select @@debug_dbug`;
let $e=0;
let $keep_include_silent=1;
let $grep_script=DROP ;
--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) $extra_option;
--eval create table t2 (b int not null) $extra_option;
insert into t1 values(1);
insert into t2 values(2);
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;
create view v1 as select * from t1;
create view v2 as select * from t1;
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;
--error 0,2013
DROP VIEW v1,v2;
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 v*
--let $binlog_file=master-bin.000001
--source include/show_binlog_events.inc
if ($error)
{
--let $binlog_file=master-bin.000002
--source include/show_binlog_events.inc
}
# Really drop the views
--disable_warnings
drop view if exists v1,v2;
--enable_warnings
}
}
drop table t1,t2;
}
--enable_query_log
......@@ -506,6 +506,7 @@ master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `mysql`; DELETE FROM db WHERE host='localhost' AND user='@#@'
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Rotate # # master-bin.000002;pos=POS
drop table t1,t2,t3,tt1;
reset master;
create table t1 (a int not null auto_increment, primary key (a)) engine=myisam;
......
......@@ -39,9 +39,9 @@ stop slave 'master1';
--let $datadir = `SELECT @@datadir`
let read_master_log_pos=`select $binlog_start_pos + 590`;
let relay_log_pos=`select 2*$binlog_start_pos + 634`;
let relay_log_space=`select 3*$binlog_start_pos + 696`;
let read_master_log_pos=`select $binlog_start_pos + 599`;
let relay_log_pos=`select 2*$binlog_start_pos + 643`;
let relay_log_space=`select 3*$binlog_start_pos + 705`;
--replace_result $SERVER_MYPORT_1 MYPORT_1 $read_master_log_pos <read_master_log_pos> $relay_log_pos <relay_log_pos> $relay_log_space <relay_log_space>
show slave 'master1' status;
--list_files $datadir mysqld*
......
......@@ -26,6 +26,7 @@
#include "sql_statistics.h" // rename_table_in_stats_tables
#include "sql_view.h" // mysql_rename_view()
#include "strfunc.h" // strconvert
#include "sql_show.h" // append_identifier()
#include <mysys_err.h> // EE_LINK
......@@ -81,17 +82,19 @@ uchar ddl_log_file_magic[]=
/* Action names for ddl_log_action_code */
const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
const char *ddl_log_action_name[]=
{
"Unknown", "partitioning delete", "partitioning rename",
"partitioning replace", "partitioning exchange",
"rename table", "rename view"
"rename table", "rename view",
"initialize drop table", "drop table",
"initialize drop view", "drop view"
};
/* Number of phases per entry */
const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]=
{
1, 1, 2, 3, 4, 1
0, 1, 1, 2, 3, 4, 1, 1, 3, 1, 1
};
......@@ -109,6 +112,7 @@ struct st_global_ddl_log
};
st_global_ddl_log global_ddl_log;
String ddl_drop_query; // Used during startup recovery
mysql_mutex_t LOCK_gdl;
......@@ -285,6 +289,19 @@ static bool update_phase(uint entry_pos, uchar phase)
}
static bool update_next_entry_pos(uint entry_pos, uint next_entry)
{
uchar buff[4];
DBUG_ENTER("update_next_entry_pos");
int4store(buff, next_entry);
DBUG_RETURN(mysql_file_pwrite(global_ddl_log.file_id, buff, sizeof(buff),
global_ddl_log.io_size * entry_pos +
DDL_LOG_NEXT_ENTRY_POS,
MYF(MY_WME | MY_NABP)));
}
static bool update_xid(uint entry_pos, ulonglong xid)
{
uchar buff[8];
......@@ -1130,6 +1147,122 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root,
(void) update_phase(entry_pos, DDL_LOG_FINAL_PHASE);
}
break;
case DDL_LOG_DROP_TABLE_INIT_ACTION:
{
LEX_CSTRING *comment= &ddl_log_entry->tmp_name;
ddl_drop_query.length(0);
ddl_drop_query.set_charset(system_charset_info);
ddl_drop_query.append(STRING_WITH_LEN("DROP TABLE IF EXISTS "));
if (comment->length)
{
ddl_drop_query.append(comment);
ddl_drop_query.append(' ');
}
/* We don't increment phase as we want to retry this in case of crash */
break;
}
case DDL_LOG_DROP_TABLE_ACTION:
{
LEX_CSTRING db, table, path;
db= ddl_log_entry->db;
table= ddl_log_entry->name;
/* Note that path is without .frm extension */
path= ddl_log_entry->tmp_name;
switch (ddl_log_entry->phase) {
case DDL_DROP_PHASE_TABLE:
if (hton)
{
if ((error= hton->drop_table(hton, path.str)))
{
if (!non_existing_table_error(error))
break;
error= -1;
}
}
else
error= ha_delete_table_force(thd, path.str, &db, &table);
if (error <= 0)
{
/* Not found or already deleted. Delete .frm if it exists */
strxnmov(to_path, sizeof(to_path)-1, path.str, reg_ext, NullS);
mysql_file_delete(key_file_frm, to_path, MYF(MY_WME|MY_IGNORE_ENOENT));
}
if (ddl_log_increment_phase_no_lock(entry_pos))
break;
(void) ddl_log_sync_no_lock();
/* Fall through */
case DDL_DROP_PHASE_TRIGGER:
Table_triggers_list::drop_all_triggers(thd, &db, &table,
MYF(MY_WME | MY_IGNORE_ENOENT));
if (ddl_log_increment_phase_no_lock(entry_pos))
break;
(void) ddl_log_sync_no_lock();
/* Fall through */
case DDL_DROP_PHASE_BINLOG:
append_identifier(thd, &ddl_drop_query, &db);
ddl_drop_query.append('.');
append_identifier(thd, &ddl_drop_query, &table);
ddl_drop_query.append(',');
/* We don't increment phase as we want to retry this in case of crash */
if (!ddl_log_entry->next_entry && mysql_bin_log.is_open())
{
/* Last drop table. Write query to binlog */
LEX_CSTRING end_comment=
{ STRING_WITH_LEN(" /* generated by ddl recovery */")};
ddl_drop_query.length(ddl_drop_query.length()-1);
ddl_drop_query.append(&end_comment);
mysql_mutex_unlock(&LOCK_gdl);
(void) thd->binlog_query(THD::STMT_QUERY_TYPE, ddl_drop_query.ptr(),
ddl_drop_query.length(), TRUE, FALSE,
FALSE, 0);
mysql_mutex_lock(&LOCK_gdl);
}
break;
}
break;
}
case DDL_LOG_DROP_VIEW_INIT_ACTION:
{
ddl_drop_query.length(0);
ddl_drop_query.set_charset(system_charset_info);
ddl_drop_query.append(STRING_WITH_LEN("DROP VIEW IF EXISTS "));
/* We don't increment phase as we want to retry this in case of crash */
break;
}
case DDL_LOG_DROP_VIEW_ACTION:
{
LEX_CSTRING db, table, path;
db= ddl_log_entry->db;
table= ddl_log_entry->name;
/* Note that for views path is WITH .frm extension */
path= ddl_log_entry->tmp_name;
mysql_file_delete(key_file_frm, path.str, MYF(MY_WME|MY_IGNORE_ENOENT));
append_identifier(thd, &ddl_drop_query, &db);
ddl_drop_query.append('.');
append_identifier(thd, &ddl_drop_query, &table);
ddl_drop_query.append(',');
if (!ddl_log_entry->next_entry)
{
/* Last drop view. Write query to binlog */
LEX_CSTRING end_comment=
{ STRING_WITH_LEN(" /* generated by ddl recovery */")};
ddl_drop_query.length(ddl_drop_query.length()-1);
ddl_drop_query.append(&end_comment);
mysql_mutex_unlock(&LOCK_gdl);
(void) thd->binlog_query(THD::STMT_QUERY_TYPE, ddl_drop_query.ptr(),
ddl_drop_query.length(), TRUE, FALSE,
FALSE, 0);
mysql_mutex_lock(&LOCK_gdl);
}
break;
}
default:
DBUG_ASSERT(0);
break;
......@@ -1298,9 +1431,14 @@ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry,
uchar *pos, *end;
DBUG_ENTER("ddl_log_write_entry");
*active_entry= 0;
mysql_mutex_assert_owner(&LOCK_gdl);
if (!global_ddl_log.open)
DBUG_ASSERT(global_ddl_log.open);
if (unlikely(!global_ddl_log.open))
{
my_error(ER_INTERNAL_ERROR, MYF(0), "ddl log not initialized");
DBUG_RETURN(TRUE);
}
ddl_log_entry->entry_type= DDL_LOG_ENTRY_CODE;
set_global_from_ddl_log_entry(ddl_log_entry);
......@@ -1509,6 +1647,7 @@ bool ddl_log_close_binlogged_events(HASH *xids)
{
if (read_ddl_log_entry(i, &ddl_log_entry))
break; // Read error. Stop reading
DBUG_PRINT("xid",("xid: %llu", ddl_log_entry.xid));
if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE &&
ddl_log_entry.xid != 0 &&
my_hash_search(xids, (uchar*) &ddl_log_entry.xid,
......@@ -1562,6 +1701,7 @@ int ddl_log_execute_recovery()
thd->store_globals();
thd->init(); // Needed for error messages
thd->log_all_errors= (global_system_variables.log_warnings >= 3);
ddl_drop_query.free();
thd->set_query(recover_query_string, strlen(recover_query_string));
......@@ -1599,6 +1739,7 @@ int ddl_log_execute_recovery()
count++;
}
}
ddl_drop_query.free();
close_ddl_log();
mysql_mutex_unlock(&LOCK_gdl);
thd->reset_query();
......@@ -1682,6 +1823,7 @@ void ddl_log_release_entries(DDL_LOG_STATE *ddl_log_state)
next= log_entry->next_active_log_entry;
ddl_log_release_memory_entry(log_entry);
}
ddl_log_state->list= 0;
if (ddl_log_state->execute_entry)
{
......@@ -1779,6 +1921,33 @@ bool ddl_log_update_xid(DDL_LOG_STATE *state, ulonglong xid)
}
/*
Write ddl_log_entry and write or update ddl_execute_entry
*/
static bool ddl_log_write(DDL_LOG_STATE *ddl_state,
DDL_LOG_ENTRY *ddl_log_entry)
{
int error;
DDL_LOG_MEMORY_ENTRY *log_entry;
DBUG_ENTER("ddl_log_write");
mysql_mutex_lock(&LOCK_gdl);
error= ((ddl_log_write_entry(ddl_log_entry, &log_entry)) ||
ddl_log_write_execute_entry(log_entry->entry_pos,
&ddl_state->execute_entry));
mysql_mutex_unlock(&LOCK_gdl);
if (error)
{
if (log_entry)
ddl_log_release_memory_entry(log_entry);
DBUG_RETURN(1);
}
add_log_entry(ddl_state, log_entry);
DBUG_RETURN(0);
}
/**
Logging of rename table
*/
......@@ -1791,13 +1960,10 @@ bool ddl_log_rename_table(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *new_alias)
{
DDL_LOG_ENTRY ddl_log_entry;
DDL_LOG_MEMORY_ENTRY *log_entry;
DBUG_ENTER("ddl_log_rename_file");
bzero(&ddl_log_entry, sizeof(ddl_log_entry));
mysql_mutex_lock(&LOCK_gdl);
ddl_log_entry.action_type= DDL_LOG_RENAME_TABLE_ACTION;
ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0;
lex_string_set(&ddl_log_entry.handler_name,
......@@ -1808,20 +1974,7 @@ bool ddl_log_rename_table(THD *thd, DDL_LOG_STATE *ddl_state,
ddl_log_entry.from_name= *const_cast<LEX_CSTRING*>(org_alias);
ddl_log_entry.phase= DDL_RENAME_PHASE_TABLE;
if (ddl_log_write_entry(&ddl_log_entry, &log_entry))
goto error;
if (ddl_log_write_execute_entry(log_entry->entry_pos,
&ddl_state->execute_entry))
goto error;
add_log_entry(ddl_state, log_entry);
mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(0);
error:
mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(1);
DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
}
/*
......@@ -1835,13 +1988,10 @@ bool ddl_log_rename_view(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *new_alias)
{
DDL_LOG_ENTRY ddl_log_entry;
DDL_LOG_MEMORY_ENTRY *log_entry;
DBUG_ENTER("ddl_log_rename_file");
bzero(&ddl_log_entry, sizeof(ddl_log_entry));
mysql_mutex_lock(&LOCK_gdl);
ddl_log_entry.action_type= DDL_LOG_RENAME_VIEW_ACTION;
ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0;
ddl_log_entry.db= *const_cast<LEX_CSTRING*>(new_db);
......@@ -1849,18 +1999,117 @@ bool ddl_log_rename_view(THD *thd, DDL_LOG_STATE *ddl_state,
ddl_log_entry.from_db= *const_cast<LEX_CSTRING*>(org_db);
ddl_log_entry.from_name= *const_cast<LEX_CSTRING*>(org_alias);
DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
}
/**
Logging of DROP TABLE and DROP VIEW
Note that in contrast to rename, which are re-done in reverse order,
deletes are stored in a linked list according to delete order. This
is to ensure that the tables, for the query generated for binlog,
is in original delete order.
*/
static bool ddl_log_drop_init(THD *thd, DDL_LOG_STATE *ddl_state,
ddl_log_action_code action_code,
const LEX_CSTRING *comment)
{
DDL_LOG_ENTRY ddl_log_entry;
DBUG_ENTER("ddl_log_drop_file");
bzero(&ddl_log_entry, sizeof(ddl_log_entry));
ddl_log_entry.action_type= action_code;
ddl_log_entry.next_entry= 0;
ddl_log_entry.tmp_name= *const_cast<LEX_CSTRING*>(comment);
ddl_log_entry.phase= 0;
DBUG_RETURN(ddl_log_write(ddl_state, &ddl_log_entry));
}
bool ddl_log_drop_table_init(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *comment)
{
return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_TABLE_INIT_ACTION,
comment);
}
bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state)
{
LEX_CSTRING comment= {0,0};
return ddl_log_drop_init(thd, ddl_state, DDL_LOG_DROP_VIEW_INIT_ACTION,
&comment);
}
static bool ddl_log_drop(THD *thd, DDL_LOG_STATE *ddl_state,
ddl_log_action_code action_code,
uint phase,
handlerton *hton,
const LEX_CSTRING *path,
const LEX_CSTRING *db,
const LEX_CSTRING *table)
{
DDL_LOG_ENTRY ddl_log_entry;
DDL_LOG_MEMORY_ENTRY *log_entry;
DBUG_ENTER("ddl_log_drop");
DBUG_ASSERT(ddl_state->list);
bzero(&ddl_log_entry, sizeof(ddl_log_entry));
ddl_log_entry.action_type= action_code;
if (hton)
lex_string_set(&ddl_log_entry.handler_name,
ha_resolve_storage_engine_name(hton));
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*>(path);
ddl_log_entry.phase= (uchar) phase;
mysql_mutex_lock(&LOCK_gdl);
if (ddl_log_write_entry(&ddl_log_entry, &log_entry))
goto error;
if (ddl_log_write_execute_entry(log_entry->entry_pos,
&ddl_state->execute_entry))
(void) ddl_log_sync_no_lock();
if (update_next_entry_pos(ddl_state->list->entry_pos,
log_entry->entry_pos))
{
ddl_log_release_memory_entry(log_entry);
goto error;
}
add_log_entry(ddl_state, log_entry);
mysql_mutex_unlock(&LOCK_gdl);
add_log_entry(ddl_state, log_entry);
DBUG_RETURN(0);
error:
mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(1);
}
bool ddl_log_drop_table(THD *thd, DDL_LOG_STATE *ddl_state,
handlerton *hton,
const LEX_CSTRING *path,
const LEX_CSTRING *db,
const LEX_CSTRING *table)
{
DBUG_ENTER("ddl_log_drop_table");
DBUG_RETURN(ddl_log_drop(thd, ddl_state,
DDL_LOG_DROP_TABLE_ACTION, DDL_DROP_PHASE_TABLE,
hton, path, db, table));
}
bool ddl_log_drop_view(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *path,
const LEX_CSTRING *db,
const LEX_CSTRING *table)
{
DBUG_ENTER("ddl_log_drop_view");
DBUG_RETURN(ddl_log_drop(thd, ddl_state,
DDL_LOG_DROP_VIEW_ACTION, 0,
(handlerton*) 0, path, db, table));
}
......@@ -46,8 +46,8 @@ enum ddl_log_entry_code
/*
When adding things below, also add an entry to ddl_log_entry_phases in
ddl_log.cc
When adding things below, also add an entry to ddl_log_action_names and
ddl_log_entry_phases in ddl_log.cc
*/
enum ddl_log_action_code
......@@ -77,6 +77,10 @@ enum ddl_log_action_code
*/
DDL_LOG_RENAME_TABLE_ACTION= 5,
DDL_LOG_RENAME_VIEW_ACTION= 6,
DDL_LOG_DROP_TABLE_INIT_ACTION= 7,
DDL_LOG_DROP_TABLE_ACTION= 8,
DDL_LOG_DROP_VIEW_INIT_ACTION= 9,
DDL_LOG_DROP_VIEW_ACTION= 10,
DDL_LOG_LAST_ACTION /* End marker */
};
......@@ -97,6 +101,14 @@ enum enum_ddl_log_rename_table_phase {
DDL_RENAME_PHASE_TABLE,
};
enum enum_ddl_log_drop_table_phase {
DDL_DROP_PHASE_TABLE=0,
DDL_DROP_PHASE_TRIGGER,
DDL_DROP_PHASE_BINLOG,
DDL_DROP_PHASE_RESET, /* Reset found list of dropped tables */
DDL_DROP_PHASE_END
};
/*
Setting ddl_log_entry.phase to this has the same effect as setting
the phase to the maximum phase (..PHASE_END) for an entry.
......@@ -204,6 +216,17 @@ bool ddl_log_rename_view(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *org_alias,
const LEX_CSTRING *new_db,
const LEX_CSTRING *new_alias);
bool ddl_log_drop_table_init(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *comment);
bool ddl_log_drop_view_init(THD *thd, DDL_LOG_STATE *ddl_state);
bool ddl_log_drop_table(THD *thd, DDL_LOG_STATE *ddl_state,
handlerton *hton,
const LEX_CSTRING *path,
const LEX_CSTRING *db,
const LEX_CSTRING *table);
bool ddl_log_drop_view(THD *thd, DDL_LOG_STATE *ddl_state,
const LEX_CSTRING *path,
const LEX_CSTRING *db,
const LEX_CSTRING *table);
extern mysql_mutex_t LOCK_gdl;
#endif /* DDL_LOG_INCLUDED */
......@@ -566,7 +566,13 @@ static int hton_drop_table(handlerton *hton, const char *path)
char tmp_path[FN_REFLEN];
handler *file= get_new_handler(nullptr, current_thd->mem_root, hton);
if (!file)
return ENOMEM;
{
/*
If file is not defined it means that the engine can't create a
handler if share is not set or we got an out of memory error
*/
return my_errno == ENOMEM ? ENOMEM : ENOENT;
}
path= get_canonical_filename(file, path, tmp_path);
int error= file->delete_table(path);
delete file;
......
......@@ -9982,6 +9982,7 @@ int TC_LOG::using_heuristic_recover()
int TC_LOG_BINLOG::open(const char *opt_name)
{
int error= 1;
DBUG_ENTER("TC_LOG_BINLOG::open");
DBUG_ASSERT(total_ha_2pc > 1);
DBUG_ASSERT(opt_name);
......@@ -9991,7 +9992,7 @@ int TC_LOG_BINLOG::open(const char *opt_name)
{
/* There was a failure to open the index file, can't open the binlog */
cleanup();
return 1;
DBUG_RETURN(1);
}
if (using_heuristic_recover())
......@@ -10001,12 +10002,12 @@ int TC_LOG_BINLOG::open(const char *opt_name)
open(opt_name, 0, 0, WRITE_CACHE, max_binlog_size, 0, TRUE);
mysql_mutex_unlock(&LOCK_log);
cleanup();
return 1;
DBUG_RETURN(1);
}
error= do_binlog_recovery(opt_name, true);
binlog_state_recover_done= true;
return error;
DBUG_RETURN(error);
}
/** This is called on shutdown, after ha_panic. */
......@@ -10536,6 +10537,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
Query_log_event *query_ev= (Query_log_event*) ev;
if (query_ev->xid)
{
DBUG_PRINT("QQ", ("xid: %llu xid"));
DBUG_ASSERT(sizeof(query_ev->xid) == sizeof(my_xid));
uchar *x= (uchar *) memdup_root(&mem_root,
(uchar*) &query_ev->xid,
......
......@@ -56,6 +56,7 @@
#include "tztime.h"
#include "sql_insert.h" // binlog_drop_table
#include "ddl_log.h"
#include "debug_sync.h" // debug_crash_here()
#include <algorithm>
#ifdef __WIN__
......@@ -1139,9 +1140,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
char path[FN_REFLEN + 1];
LEX_CSTRING alias= null_clex_str;
StringBuffer<160> unknown_tables(system_charset_info);
uint not_found_errors= 0;
DDL_LOG_STATE ddl_log_state;
const char *comment_start;
uint not_found_errors= 0, table_count= 0, non_temp_tables_count= 0;
int error= 0;
int non_temp_tables_count= 0;
uint32 comment_len;
bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
bool non_tmp_table_deleted= 0;
bool is_drop_tmp_if_exists_added= 0;
......@@ -1154,6 +1157,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
DBUG_ENTER("mysql_rm_table_no_locks");
unknown_tables.length(0);
comment_len= get_comment(thd, if_exists ? 17:9, &comment_start);
/*
Prepares the drop statements that will be written into the binary
log as follows:
......@@ -1204,6 +1209,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
built_non_trans_tmp_query.set_charset(system_charset_info);
built_non_trans_tmp_query.copy(built_trans_tmp_query);
}
bzero(&ddl_log_state, sizeof(ddl_log_state));
for (table= tables; table; table= table->next_local)
{
......@@ -1213,6 +1219,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool table_dropped= 0;
const LEX_CSTRING db= table->db;
const LEX_CSTRING table_name= table->table_name;
LEX_CSTRING cpath= {0,0};
handlerton *hton= 0;
Table_type table_type;
size_t path_length= 0;
......@@ -1347,6 +1354,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
continue;
}
lex_string_set3(&cpath, path, (size_t) (path_end - path));
{
char engine_buf[NAME_CHAR_LEN + 1];
LEX_CSTRING engine= { engine_buf, 0 };
......@@ -1363,6 +1372,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
thd->replication_flags= 0;
was_view= table_type == TABLE_TYPE_VIEW;
if (!table_count++)
{
LEX_CSTRING comment= {comment_start, (size_t) comment_len};
if (ddl_log_drop_table_init(thd, &ddl_log_state, &comment))
{
error= 1;
goto err;
}
}
if ((table_type == TABLE_TYPE_UNKNOWN) || (was_view && !drop_view) ||
(table_type != TABLE_TYPE_SEQUENCE && drop_sequence))
{
......@@ -1376,6 +1396,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
was_table|= wrong_drop_sequence;
error= table_type == TABLE_TYPE_UNKNOWN ? ENOENT : -1;
tdc_remove_table(thd, db.str, table_name.str);
if (wrong_drop_sequence)
goto report_error;
}
else
{
......@@ -1411,7 +1433,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
log_if_exists= 1;
bool enoent_warning= !dont_log_query && !(hton && hton->discover_table);
error= ha_delete_table(thd, hton, path, &db, &table_name, enoent_warning);
if (ddl_log_drop_table(thd, &ddl_log_state, hton, &cpath, &db,
&table_name))
{
error= -1;
goto err;
}
debug_crash_here("ddl_log_drop_before_delete_table");
error= ha_delete_table(thd, hton, path, &db, &table_name,
enoent_warning);
debug_crash_here("ddl_log_drop_after_delete_table");
if (!error)
table_dropped= 1;
......@@ -1473,11 +1505,18 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
scan all engines try to drop the table from there.
This is to ensure we don't have any partial table files left.
*/
if (non_existing_table_error(error) && !wrong_drop_sequence)
if (non_existing_table_error(error))
{
int ferror= 0;
DBUG_ASSERT(!was_view);
if (ddl_log_drop_table(thd, &ddl_log_state, hton, &cpath, &db,
&table_name))
{
error= -1;
goto err;
}
/* Remove extension for delete */
*path_end= '\0';
ferror= ha_delete_table_force(thd, path, &db, &table_name);
......@@ -1509,13 +1548,19 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (thd->replication_flags & OPTION_IF_EXISTS)
log_if_exists= 1;
debug_crash_here("ddl_log_drop_before_drop_trigger");
ddl_log_update_phase(&ddl_log_state, DDL_DROP_PHASE_TRIGGER);
debug_crash_here("ddl_log_drop_before_drop_trigger2");
if (likely(!error) || non_existing_table_error(error))
{
if (Table_triggers_list::drop_all_triggers(thd, &db, &table_name,
MYF(MY_WME | MY_IGNORE_ENOENT)))
error= error ? error : -1;
}
debug_crash_here("ddl_log_drop_after_drop_trigger");
report_error:
if (error)
{
StringBuffer<FN_REFLEN> tbl_name(system_charset_info);
......@@ -1565,6 +1610,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
table_name.str, (uint)table_name.length);
mysql_audit_drop_table(thd, table);
}
ddl_log_update_phase(&ddl_log_state, DDL_DROP_PHASE_BINLOG);
if (!dont_log_query &&
(!error || table_dropped || non_existing_table_error(error)))
......@@ -1621,6 +1667,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
query_cache_invalidate3(thd, tables, 0);
if (!dont_log_query && mysql_bin_log.is_open())
{
debug_crash_here("ddl_log_drop_before_binlog");
if (non_trans_tmp_table_deleted)
{
/* Chop of the last comma */
......@@ -1648,8 +1695,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (non_tmp_table_deleted)
{
String built_query;
const char *comment_start;
uint32 comment_len;
built_query.set_charset(thd->charset());
built_query.append(STRING_WITH_LEN("DROP "));
......@@ -1659,8 +1704,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
built_query.append(STRING_WITH_LEN("IF EXISTS "));
/* Preserve comment in original query */
if ((comment_len= get_comment(thd, if_exists ? 17:9,
&comment_start)))
if (comment_len)
{
built_query.append(comment_start, comment_len);
built_query.append(' ');
......@@ -1670,13 +1714,18 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
normal_tables.chop();
built_query.append(normal_tables.ptr(), normal_tables.length());
built_query.append(generated_by_server);
thd->binlog_xid= thd->query_id;
ddl_log_update_xid(&ddl_log_state, thd->binlog_xid);
error |= (thd->binlog_query(THD::STMT_QUERY_TYPE,
built_query.ptr(),
built_query.length(),
TRUE, FALSE, FALSE, 0) > 0);
thd->binlog_xid= 0;
}
debug_crash_here("ddl_log_drop_after_binlog");
}
}
ddl_log_complete(&ddl_log_state);
if (!drop_temporary)
{
......
......@@ -36,6 +36,7 @@
#include "sql_derived.h"
#include "sql_cte.h" // check_dependencies_in_with_clauses()
#include "opt_trace.h"
#include "ddl_log.h"
#include "wsrep_mysqld.h"
#include "debug_sync.h" // debug_crash_here
......@@ -1817,9 +1818,12 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
bool delete_error= FALSE, wrong_object_name= FALSE;
bool some_views_deleted= FALSE;
bool something_wrong= FALSE;
uint not_exists_count= 0;
uint not_exists_count= 0, view_count= 0;
DDL_LOG_STATE ddl_log_state;
DBUG_ENTER("mysql_drop_view");
bzero(&ddl_log_state, sizeof(ddl_log_state));
/*
We can't allow dropping of unlocked view under LOCK TABLES since this
might lead to deadlock. But since we can't really lock view with LOCK
......@@ -1838,9 +1842,12 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
for (view= views; view; view= view->next_local)
{
LEX_CSTRING cpath;
bool not_exist;
build_table_filename(path, sizeof(path) - 1,
size_t length;
length= build_table_filename(path, sizeof(path) - 1,
view->db.str, view->table_name.str, reg_ext, 0);
lex_string_set3(&cpath, path, length);
if ((not_exist= my_access(path, F_OK)) || !dd_frm_is_view(thd, path))
{
......@@ -1861,8 +1868,18 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
not_exists_count++;
continue;
}
if (!view_count++)
{
if (ddl_log_drop_view_init(thd, &ddl_log_state))
DBUG_RETURN(TRUE);
}
if (ddl_log_drop_view(thd, &ddl_log_state, &cpath, &view->db,
&view->table_name))
DBUG_RETURN(TRUE);
debug_crash_here("ddl_log_drop_before_delete_view");
if (unlikely(mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
delete_error= TRUE;
debug_crash_here("ddl_log_drop_after_delete_view");
some_views_deleted= TRUE;
......@@ -1890,10 +1907,16 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
/* if something goes wrong, bin-log with possible error code,
otherwise bin-log with error code cleared.
*/
debug_crash_here("ddl_log_drop_before_binlog");
thd->binlog_xid= thd->query_id;
ddl_log_update_xid(&ddl_log_state, thd->binlog_xid);
if (unlikely(write_bin_log(thd, !something_wrong, thd->query(),
thd->query_length())))
something_wrong= 1;
thd->binlog_xid= 0;
debug_crash_here("ddl_log_drop_after_binlog");
}
ddl_log_complete(&ddl_log_state);
if (unlikely(something_wrong))
{
......
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