1#!/usr/bin/env perl 2 3 4use strict; 5use warnings; 6 7my $usage= <<EOF; 8This program tests that the options 9--aria-force-start-after-recovery-failures --aria-recover work as 10expected. 11It has to be run from directory mysql-test, and works with non-debug 12and debug binaries. 13Pass it option -d or -i (to test corruption of data or index file). 14EOF 15 16# -d currently exhibits BUG#36578 17# "Maria: maria-recover may fail to autorepair a table" 18 19die($usage) if (@ARGV == 0); 20 21my $corrupt_index; 22 23if ($ARGV[0] eq '-d') 24 { 25 $corrupt_index= 0; 26 } 27elsif ($ARGV[0] eq '-i') 28 { 29 $corrupt_index= 1; 30 } 31else 32 { 33 die($usage); 34 } 35 36my $force_after= 3; 37my $corrupt_file= $corrupt_index ? "MAI" : "MAD"; 38my $corrupt_message= 39 "\\[ERROR\\] mysqld(.exe)*: Table '..test.t1' is marked as crashed and should be repaired"; 40 41my $sql_name= "./var/tmp/create_table.sql"; 42my $error_log_name= "./var/log/master.err"; 43my @cmd_output; 44my $whatever; # garbage data 45$ENV{MTR_VERSION} = 1; # MTR2 does not have --start-and-exit 46my $base_server_cmd= "perl mysql-test-run.pl --mysqld=--aria-force-start-after-recovery-failures=$force_after --suite=maria maria.maria-recover "; 47if ($^O =~ /^mswin/i) 48 { 49 print <<EOF; 50WARNING: with Activestate Perl, mysql-test-run.pl --start-and-exit has a bug: 51it does not exit; cygwin perl recommended 52EOF 53 } 54my $iswindows= ( $^O =~ /win/i && $^O !~ /darwin/i ); 55$base_server_cmd.= ($iswindows ? "--mysqld=--console" : "--mem"); 56my $server_cmd; 57my $server_pid_name="./var/run/master.pid"; 58my $server_pid; 59my $i; # count of server restarts 60sub kill_server; 61 62my $suffix= ($iswindows ? ".exe" : ""); 63my $client_exe_path= "../client/release"; 64# we use -f, sometimes -x is unexpectedly false in Cygwin 65if ( ! -f "$client_exe_path/mysql$suffix" ) 66 { 67 $client_exe_path= "../client/relwithdebinfo"; 68 if ( ! -f "$client_exe_path/mysql$suffix" ) 69 { 70 $client_exe_path= "../client/debug"; 71 if ( ! -f "$client_exe_path/mysql$suffix" ) 72 { 73 $client_exe_path= "../client"; 74 if ( ! -f "$client_exe_path/mysql$suffix" ) 75 { 76 die("Cannot find 'mysql' executable\n"); 77 } 78 } 79 } 80 } 81 82print "starting mysqld\n"; 83$server_cmd= $base_server_cmd . " --start-and-exit 2>&1"; 84@cmd_output=`$server_cmd`; 85die if $?; 86my $master_port= (grep (/Using MASTER_MYPORT .*= (\d+)$/, @cmd_output))[0]; 87$master_port =~ s/.*= //; 88chomp $master_port; 89die unless $master_port > 0; 90 91my $client_cmd= "$client_exe_path/mysql -u root -h 127.0.0.1 -P $master_port test < $sql_name"; 92 93open(FILE, ">", $sql_name) or die; 94 95# To exhibit BUG#36578 with -d, we don't create an index if -d. This is 96# because the presence of an index will cause repair-by-sort to be used, 97# where sort_get_next_record() is only called inside 98#_ma_create_index_by_sort(), so the latter function fails and in this 99# case retry_repair is set, so bug does not happen. Whereas without 100# an index, repair-with-key-cache is called, which calls 101# sort_get_next_record() whose failure itself does not cause a retry. 102 103print FILE "create table t1 (a varchar(1000)". 104 ($corrupt_index ? ", index(a)" : "") .") engine=aria;\n"; 105print FILE <<EOF; 106insert into t1 values("ThursdayMorningsMarket"); 107# If Recovery executes REDO_INDEX_NEW_PAGE it will overwrite our 108# intentional corruption; we make Recovery skip this record by bumping 109# create_rename_lsn using OPTIMIZE TABLE. This also makes sure to put 110# the pages on disk, so that we can corrupt them. 111optimize table t1; 112# mark table open, so that --aria-recover repairs it 113insert into t1 select concat(a,'b') from t1 limit 1; 114EOF 115close FILE; 116 117print "creating table\n"; 118`$client_cmd`; 119die if $?; 120 121print "killing mysqld hard\n"; 122kill_server(9); 123 124print "ruining " . 125 ($corrupt_index ? "first page of keys" : "bitmap page") . 126 " in table to test aria-recover\n"; 127open(FILE, "+<", "./var/master-data/test/t1.$corrupt_file") or die; 128$whatever= ("\xAB" x 100); 129sysseek (FILE, $corrupt_index ? 8192 : (8192-100-100), 0) or die; 130syswrite (FILE, $whatever) or die; 131close FILE; 132 133print "ruining log to make recovery fail; mysqld should fail the $force_after first restarts\n"; 134open(FILE, "+<", "./var/tmp/aria_log.00000001") or die; 135$whatever= ("\xAB" x 8192); 136sysseek (FILE, 99, 0) or die; 137syswrite (FILE, $whatever) or die; 138close FILE; 139 140$server_cmd= $base_server_cmd . " --start-dirty 2>&1"; 141for($i= 1; $i <= $force_after; $i= $i + 1) 142 { 143 print "mysqld restart number $i... "; 144 unlink($error_log_name) or die; 145 `$server_cmd`; 146 # mysqld should return 1 when can't read log 147 die unless (($? >> 8) == 1); 148 open(FILE, "<", $error_log_name) or die; 149 @cmd_output= <FILE>; 150 close FILE; 151 die unless grep(/\[ERROR\] mysqld(.exe)*: Aria engine: log initialization failed/, @cmd_output); 152 die unless grep(/\[ERROR\] Plugin 'Aria' init function returned error./, @cmd_output); 153 print "failed - ok\n"; 154 } 155 156print "mysqld restart number $i... "; 157unlink($error_log_name) or die; 158@cmd_output=`$server_cmd`; 159die if $?; 160open(FILE, "<", $error_log_name) or die; 161@cmd_output= <FILE>; 162close FILE; 163die unless grep(/\[Warning\] mysqld(.exe)*: Aria engine: removed all logs after [\d]+ consecutive failures of recovery from logs/, @cmd_output); 164die unless grep(/\[ERROR\] mysqld(.exe)*: File '.*tmp.aria_log.00000001' not found \(Errcode: 2\)/, @cmd_output); 165print "success - ok\n"; 166 167open(FILE, ">", $sql_name) or die; 168print FILE <<EOF; 169set global aria_recover=normal; 170insert into t1 values('aaa'); 171EOF 172close FILE; 173 174# verify corruption has not yet been noticed 175open(FILE, "<", $error_log_name) or die; 176@cmd_output= <FILE>; 177close FILE; 178die if grep(/$corrupt_message/, @cmd_output); 179 180print "inserting in table\n"; 181`$client_cmd`; 182die if $?; 183print "table is usable - ok\n"; 184 185open(FILE, "<", $error_log_name) or die; 186@cmd_output= <FILE>; 187close FILE; 188die unless grep(/$corrupt_message/, @cmd_output); 189die unless grep(/\[Warning\] Recovering table: '..test.t1'/, @cmd_output); 190print "was corrupted and automatically repaired - ok\n"; 191 192# remove our traces 193kill_server(15); 194 195print "TEST ALL OK\n"; 196 197# kills mysqld with signal given in parameter 198sub kill_server 199 { 200 my ($sig)= @_; 201 my $wait_count= 0; 202 my $kill_cmd; 203 my @kill_output; 204 open(FILE, "<", $server_pid_name) or die; 205 @cmd_output= <FILE>; 206 close FILE; 207 $server_pid= $cmd_output[0]; 208 chomp $server_pid; 209 die unless $server_pid > 0; 210 if ($iswindows) 211 { 212 # On Windows, server_pid_name is not the "main" process id 213 # so perl's kill() does not see this process id. 214 # But taskkill works, though only with /F ("-9"-style kill). 215 $kill_cmd= "taskkill /F /PID $server_pid 2>&1"; 216 @kill_output= `$kill_cmd`; 217 die unless grep(/has been terminated/, @kill_output); 218 } 219 else 220 { 221 kill($sig, $server_pid) or die; 222 } 223 while (1) # wait until mysqld process gone 224 { 225 if ($iswindows) 226 { 227 @kill_output= `$kill_cmd`; 228 last if grep(/not found/, @kill_output); 229 } 230 else 231 { 232 kill (0, $server_pid) or last; 233 } 234 print "waiting for mysqld to die\n" if ($wait_count > 30); 235 $wait_count= $wait_count + 1; 236 select(undef, undef, undef, 0.1); 237 } 238 } 239