mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-22 20:19:00 +00:00
Add and use basic_no_fks_innodb.sql with PXC to avoid MyISAM bugs. Make query_table.pl handle deadlock on START TRANSACTION. Give alter_active_table.t tests unique names.
This commit is contained in:
@@ -14,6 +14,7 @@ use Test::More;
|
||||
use PerconaTest;
|
||||
use Sandbox;
|
||||
require "$trunk/bin/pt-online-schema-change";
|
||||
require VersionParser;
|
||||
|
||||
use Time::HiRes qw(sleep);
|
||||
use Data::Dumper;
|
||||
@@ -72,23 +73,27 @@ sub get_ids {
|
||||
my @lines = <$fh>;
|
||||
close $fh;
|
||||
|
||||
my %ids;
|
||||
my %ids = (
|
||||
updated => '',
|
||||
deleted => '',
|
||||
inserted => '',
|
||||
);
|
||||
foreach my $line ( @lines ) {
|
||||
my ($stmt, $ids) = split(':', $line);
|
||||
chomp $ids;
|
||||
$ids{$stmt} = $ids;
|
||||
$ids{$stmt} = $ids || '';
|
||||
}
|
||||
|
||||
return \%ids;
|
||||
}
|
||||
|
||||
sub check_ids {
|
||||
my ( $db, $tbl, $pkcol, $ids ) = @_;
|
||||
my ( $db, $tbl, $pkcol, $ids, $test ) = @_;
|
||||
my $rows;
|
||||
|
||||
my $n_updated = $ids->{updated} ? ($ids->{updated} =~ tr/,//) : 0;
|
||||
my $n_deleted = $ids->{deleted} ? ($ids->{deleted} =~ tr/,//) : 0;
|
||||
my $n_inserted = ($ids->{inserted} =~ tr/,//);
|
||||
my $n_inserted = $ids->{inserted} ?($ids->{inserted} =~ tr/,//) : 0;
|
||||
|
||||
# "1,1"=~tr/,// returns 1 but is 2 values
|
||||
$n_updated++ if $n_updated;
|
||||
@@ -100,16 +105,16 @@ sub check_ids {
|
||||
is(
|
||||
$rows->[0],
|
||||
500 + $n_inserted - $n_deleted,
|
||||
"New table row count: 500 original + $n_inserted inserted - $n_deleted deleted"
|
||||
) or print Dumper($rows);
|
||||
"$test: new table rows: 500 original + $n_inserted inserted - $n_deleted deleted"
|
||||
) or diag(Dumper($rows));
|
||||
|
||||
$rows = $master_dbh->selectall_arrayref(
|
||||
"SELECT $pkcol FROM $db.$tbl WHERE $pkcol > 500 AND $pkcol NOT IN ($ids->{inserted})");
|
||||
is_deeply(
|
||||
$rows,
|
||||
[],
|
||||
"No extra rows inserted in new table"
|
||||
) or print Dumper($rows);
|
||||
"$test: no extra rows inserted in new table"
|
||||
) or diag(Dumper($rows));
|
||||
|
||||
if ( $n_deleted ) {
|
||||
$rows = $master_dbh->selectall_arrayref(
|
||||
@@ -117,13 +122,13 @@ sub check_ids {
|
||||
is_deeply(
|
||||
$rows,
|
||||
[],
|
||||
"No deleted rows present in new table"
|
||||
) or print Dumper($rows);
|
||||
"$test: no deleted rows present in new table"
|
||||
) or diag(Dumper($rows));
|
||||
}
|
||||
else {
|
||||
ok(
|
||||
1,
|
||||
"No rows deleted"
|
||||
"$test: no rows deleted"
|
||||
);
|
||||
};
|
||||
|
||||
@@ -134,13 +139,13 @@ sub check_ids {
|
||||
is_deeply(
|
||||
$rows,
|
||||
[],
|
||||
"Updated rows correct in new table"
|
||||
) or print Dumper($rows);
|
||||
"$test: updated rows correct in new table"
|
||||
) or diag(Dumper($rows));
|
||||
}
|
||||
else {
|
||||
ok(
|
||||
1,
|
||||
"No rows updated"
|
||||
"$test: no rows updated"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,19 +155,20 @@ sub check_ids {
|
||||
# #############################################################################
|
||||
# Attempt to alter a table while another process is changing it.
|
||||
# #############################################################################
|
||||
|
||||
# Load 500 rows.
|
||||
diag('Loading sample dataset...');
|
||||
sleep 2;
|
||||
my $db_flavor = VersionParser->new($master_dbh)->flavor();
|
||||
if ( $db_flavor =~ m/XtraDB Cluster/ ) {
|
||||
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
|
||||
}
|
||||
else {
|
||||
$sb->load_file('master', "$sample/basic_no_fks.sql");
|
||||
}
|
||||
$master_dbh->do("USE pt_osc");
|
||||
$master_dbh->do("TRUNCATE TABLE t");
|
||||
$master_dbh->do("LOAD DATA INFILE '$trunk/t/pt-online-schema-change/samples/basic_no_fks.data' INTO TABLE t");
|
||||
$master_dbh->do("ANALYZE TABLE t");
|
||||
$sb->wait_for_slaves();
|
||||
|
||||
$rows = $master_dbh->selectrow_hashref('show master status');
|
||||
diag('Binlog position before altering table: ', $rows->{file}, '/', $rows->{position});
|
||||
|
||||
# Start inserting, updating, and deleting rows at random.
|
||||
start_query_table(qw(pt_osc t id));
|
||||
|
||||
@@ -178,29 +184,31 @@ start_query_table(qw(pt_osc t id));
|
||||
# Stop changing the table's data.
|
||||
stop_query_table();
|
||||
|
||||
like($output, qr/Successfully altered `pt_osc`.`t`/, 'Altered OK');
|
||||
like(
|
||||
$output,
|
||||
qr/Successfully altered `pt_osc`.`t`/,
|
||||
'Change engine: altered OK'
|
||||
);
|
||||
|
||||
$rows = $master_dbh->selectall_hashref('SHOW TABLE STATUS FROM pt_osc', 'name');
|
||||
is(
|
||||
$rows->{t}->{engine},
|
||||
'InnoDB',
|
||||
"New table ENGINE=InnoDB"
|
||||
"Change engine: new table ENGINE=InnoDB"
|
||||
) or warn Dumper($rows);
|
||||
|
||||
is(
|
||||
scalar keys %$rows,
|
||||
1,
|
||||
"Dropped old table"
|
||||
"Change engine: dropped old table"
|
||||
);
|
||||
|
||||
is(
|
||||
$exit,
|
||||
0,
|
||||
"Exit status 0"
|
||||
"Change engine: exit status 0"
|
||||
);
|
||||
|
||||
check_ids(qw(pt_osc t id), get_ids());
|
||||
|
||||
# #############################################################################
|
||||
# Check that triggers work when renaming a column
|
||||
# #############################################################################
|
||||
@@ -211,8 +219,6 @@ $master_dbh->do("LOAD DATA INFILE '$trunk/t/pt-online-schema-change/samples/basi
|
||||
$master_dbh->do("ANALYZE TABLE t");
|
||||
$sb->wait_for_slaves();
|
||||
|
||||
my $orig_rows = $master_dbh->selectall_arrayref(qq{SELECT id,d FROM pt_osc.t});
|
||||
|
||||
# Start inserting, updating, and deleting rows at random.
|
||||
start_query_table(qw(pt_osc t id));
|
||||
|
||||
@@ -230,33 +236,19 @@ start_query_table(qw(pt_osc t id));
|
||||
# Stop changing the table's data.
|
||||
stop_query_table();
|
||||
|
||||
like($output, qr/Successfully altered `pt_osc`.`t`/, 'Altered OK');
|
||||
like(
|
||||
$output,
|
||||
qr/Successfully altered `pt_osc`.`t`/,
|
||||
'Rename column: altered OK'
|
||||
);
|
||||
|
||||
is(
|
||||
$exit,
|
||||
0,
|
||||
"Exit status 0"
|
||||
"Rename columnn: exit status 0"
|
||||
);
|
||||
|
||||
my $ids = get_ids();
|
||||
my %deleted_ids = map { $_ => 1 } split /,/, $ids->{deleted};
|
||||
my %updated_ids = map { $_ => 1 } split /,/, $ids->{updated};
|
||||
|
||||
$rows = $master_dbh->selectall_arrayref(
|
||||
qq{SELECT id,q FROM pt_osc.t WHERE id}
|
||||
. ($ids->{updated} ? qq{ AND id NOT IN ($ids->{updated})} : '')
|
||||
. ($ids->{inserted} ? qq{ AND id NOT IN ($ids->{inserted})} : '')
|
||||
);
|
||||
|
||||
my @filtered_orig_rows = grep {
|
||||
!$deleted_ids{$_->[0]} && !$updated_ids{$_->[0]}
|
||||
} @$orig_rows;
|
||||
|
||||
is_deeply(
|
||||
$rows,
|
||||
\@filtered_orig_rows,
|
||||
"Triggers work if renaming a column"
|
||||
);
|
||||
check_ids(qw(pt_osc t id), get_ids(), "Rename column");
|
||||
|
||||
# #############################################################################
|
||||
# Done.
|
||||
|
@@ -15,6 +15,7 @@ use Time::HiRes qw(sleep);
|
||||
use PerconaTest;
|
||||
use Sandbox;
|
||||
require "$trunk/bin/pt-online-schema-change";
|
||||
require VersionParser;
|
||||
|
||||
use Data::Dumper;
|
||||
$Data::Dumper::Indent = 1;
|
||||
@@ -46,7 +47,7 @@ my $rows;
|
||||
# Tool shouldn't run without --execute (bug 933232).
|
||||
# #############################################################################
|
||||
|
||||
$sb->load_file('master', "$sample/basic_no_fks.sql");
|
||||
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
|
||||
|
||||
($output, $exit) = full_output(
|
||||
sub { pt_online_schema_change::main(@args, "$dsn,D=pt_osc,t=t",
|
||||
@@ -57,7 +58,7 @@ like(
|
||||
$output,
|
||||
qr/neither --dry-run nor --execute was specified/,
|
||||
"Doesn't run without --execute (bug 933232)"
|
||||
) or warn $output;
|
||||
) or diag($output);
|
||||
|
||||
my $ddl = $master_dbh->selectrow_arrayref("show create table pt_osc.t");
|
||||
like(
|
||||
@@ -305,6 +306,40 @@ sub test_alter_table {
|
||||
# The most basic: alter a small table with no fks that's not active.
|
||||
# #############################################################################
|
||||
|
||||
my $db_flavor = VersionParser->new($master_dbh)->flavor();
|
||||
if ( $db_flavor =~ m/XtraDB Cluster/ ) {
|
||||
test_alter_table(
|
||||
name => "Basic no fks --dry-run",
|
||||
table => "pt_osc.t",
|
||||
file => "basic_no_fks_innodb.sql",
|
||||
max_id => 20,
|
||||
test_type => "drop_col",
|
||||
drop_col => "d",
|
||||
cmds => [qw(--dry-run --alter), 'DROP COLUMN d'],
|
||||
);
|
||||
|
||||
test_alter_table(
|
||||
name => "Basic no fks --execute",
|
||||
table => "pt_osc.t",
|
||||
# The previous test should not have modified the table.
|
||||
# file => "basic_no_fks_innodb.sql",
|
||||
# max_id => 20,
|
||||
test_type => "drop_col",
|
||||
drop_col => "d",
|
||||
cmds => [qw(--execute --alter), 'DROP COLUMN d'],
|
||||
);
|
||||
|
||||
test_alter_table(
|
||||
name => "--execute but no --alter",
|
||||
table => "pt_osc.t",
|
||||
file => "basic_no_fks_innodb.sql",
|
||||
max_id => 20,
|
||||
test_type => "new_engine", # When there's no change, we just check
|
||||
new_engine => "InnoDB", # the engine as a NOP. Any other
|
||||
cmds => [qw(--execute)], # unintended changes are still detected.
|
||||
);
|
||||
}
|
||||
else {
|
||||
test_alter_table(
|
||||
name => "Basic no fks --dry-run",
|
||||
table => "pt_osc.t",
|
||||
@@ -335,6 +370,7 @@ test_alter_table(
|
||||
new_engine => "MyISAM",
|
||||
cmds => [qw(--execute)],
|
||||
);
|
||||
}
|
||||
|
||||
# ############################################################################
|
||||
# Alter a table with foreign keys.
|
||||
@@ -520,7 +556,7 @@ SKIP: {
|
||||
);
|
||||
|
||||
# Restore the original fks.
|
||||
diag('Restoring original Sakila foreign keys...');
|
||||
diag('Restoring sakila...');
|
||||
diag(`$trunk/sandbox/load-sakila-db 12345`);
|
||||
}
|
||||
|
||||
@@ -528,23 +564,18 @@ SKIP: {
|
||||
# --alther-foreign-keys-method=none. This intentionally breaks fks because
|
||||
# they're not updated so they'll point to the old table that is dropped.
|
||||
# #############################################################################
|
||||
diag('Loading file and waiting for replication...');
|
||||
$sb->load_file('master', "$sample/basic_with_fks.sql");
|
||||
|
||||
# Specify --alter-foreign-keys-method for a table with no child tables.
|
||||
test_alter_table(
|
||||
name => "Update fk method none",
|
||||
file => "basic_with_fks.sql",
|
||||
table => "pt_osc.country",
|
||||
pk_col => "country_id",
|
||||
file => "basic_with_fks.sql",
|
||||
max_id => 20,
|
||||
test_type => "new_engine",
|
||||
new_engine => "innodb",
|
||||
cmds => [
|
||||
qw(
|
||||
--execute
|
||||
--alter-foreign-keys-method none
|
||||
),
|
||||
'--alter', 'ENGINE=INNODB',
|
||||
qw(--execute --alter-foreign-keys-method none --alter ENGINE=INNODB)
|
||||
],
|
||||
);
|
||||
|
||||
@@ -654,7 +685,7 @@ ok(
|
||||
'--execute', '--statistics',
|
||||
'--alter', "modify column val ENUM('M','E','H') NOT NULL")
|
||||
},
|
||||
($sandbox_version ge '5.5'
|
||||
($sandbox_version ge '5.5' && $db_flavor !~ m/XtraDB Cluster/
|
||||
? "$sample/stats-execute-5.5.txt"
|
||||
: "$sample/stats-execute.txt"),
|
||||
),
|
||||
@@ -689,7 +720,6 @@ SKIP: {
|
||||
# #############################################################################
|
||||
# Done.
|
||||
# #############################################################################
|
||||
$master_dbh->do("UPDATE mysql.proc SET created='2012-06-05 00:00:00', modified='2012-06-05 00:00:00'");
|
||||
$sb->wipe_clean($master_dbh);
|
||||
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
|
||||
done_testing;
|
||||
|
@@ -38,9 +38,6 @@ elsif ( !$slave2_dbh ) {
|
||||
elsif ( !@{$master_dbh->selectall_arrayref("show databases like 'sakila'")} ) {
|
||||
plan skip_all => 'sakila database is not loaded';
|
||||
}
|
||||
else {
|
||||
plan tests => 3;
|
||||
}
|
||||
|
||||
# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
|
||||
# so we need to specify --lock-wait-timeout=3 else the tool will die.
|
||||
@@ -58,7 +55,7 @@ my $sample = "t/pt-online-schema-change/samples/";
|
||||
diag(`/tmp/12345/use -u root < $trunk/$sample/osc-user.sql`);
|
||||
PerconaTest::wait_for_table($slave1_dbh, "mysql.tables_priv", "user='osc_user'");
|
||||
|
||||
$sb->load_file('master', "$sample/basic_no_fks.sql");
|
||||
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
|
||||
|
||||
($output, $exit_status) = full_output(
|
||||
sub { $exit_status = pt_online_schema_change::main(@args,
|
||||
@@ -97,4 +94,4 @@ wait_until(
|
||||
# #############################################################################
|
||||
$sb->wipe_clean($master_dbh);
|
||||
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
|
||||
exit;
|
||||
done_testing;
|
||||
|
30
t/pt-online-schema-change/samples/basic_no_fks_innodb.sql
Normal file
30
t/pt-online-schema-change/samples/basic_no_fks_innodb.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
DROP DATABASE IF EXISTS pt_osc;
|
||||
CREATE DATABASE pt_osc;
|
||||
USE pt_osc;
|
||||
CREATE TABLE t (
|
||||
id int auto_increment primary key,
|
||||
c char(32),
|
||||
d date,
|
||||
unique index (c(32))
|
||||
) ENGINE=InnoDB;
|
||||
INSERT INTO pt_osc.t VALUES
|
||||
(1, 'a', now()),
|
||||
(2, 'b', now()),
|
||||
(3, 'c', now()),
|
||||
(4, 'd', now()),
|
||||
(5, 'e', now()),
|
||||
(6, 'f', now()),
|
||||
(7, 'g', now()),
|
||||
(8, 'h', now()),
|
||||
(9, 'i', now()),
|
||||
(10, 'j', now()), -- 10
|
||||
(11, 'k', now()),
|
||||
(12, 'l', now()),
|
||||
(13, 'm', now()),
|
||||
(14, 'n', now()),
|
||||
(15, 'o', now()),
|
||||
(16, 'p', now()),
|
||||
(17, 'q', now()),
|
||||
(18, 'r', now()),
|
||||
(19, 's', now()),
|
||||
(20, 't', now()); -- 20
|
@@ -40,17 +40,17 @@ INSERT INTO pt_osc.country VALUES
|
||||
(5, 'Spain', null);
|
||||
|
||||
INSERT INTO pt_osc.city VALUES
|
||||
(null, 'Montréal', 1, null),
|
||||
(null, 'New York', 2, null),
|
||||
(null, 'Durango', 3, null),
|
||||
(null, 'Paris', 4, null),
|
||||
(null, 'Madrid', 5, null);
|
||||
(1, 'Montréal', 1, null),
|
||||
(2, 'New York', 2, null),
|
||||
(3, 'Durango', 3, null),
|
||||
(4, 'Paris', 4, null),
|
||||
(5, 'Madrid', 5, null);
|
||||
|
||||
INSERT INTO pt_osc.address VALUES
|
||||
(null, 'addy 1', 1, '10000', null),
|
||||
(null, 'addy 2', 2, '20000', null),
|
||||
(null, 'addy 3', 3, '30000', null),
|
||||
(null, 'addy 4', 4, '40000', null),
|
||||
(null, 'addy 5', 5, '50000', null);
|
||||
(1, 'addy 1', 1, '10000', null),
|
||||
(2, 'addy 2', 2, '20000', null),
|
||||
(3, 'addy 3', 3, '30000', null),
|
||||
(4, 'addy 4', 4, '40000', null),
|
||||
(5, 'addy 5', 5, '50000', null);
|
||||
|
||||
SET foreign_key_checks=1;
|
||||
|
@@ -108,7 +108,12 @@ for my $i ( 1..5_000 ) {
|
||||
commit();
|
||||
reset_counters();
|
||||
sleep $sleep;
|
||||
$dbh->do("START TRANSACTION");
|
||||
# TODO: somehow this can fail if called very near when
|
||||
# the old table is dropped.
|
||||
eval { $dbh->do("START TRANSACTION"); };
|
||||
if ( $EVAL_ERROR ) {
|
||||
#Test::More::diag($EVAL_ERROR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sleep 0.001;
|
||||
|
@@ -28,9 +28,6 @@ my $slave_dbh = $sb->get_dbh_for('slave1');
|
||||
if ( !$master_dbh ) {
|
||||
plan skip_all => 'Cannot connect to sandbox master';
|
||||
}
|
||||
else {
|
||||
plan tests => 6;
|
||||
}
|
||||
|
||||
my $q = new Quoter();
|
||||
my $tp = new TableParser(Quoter => $q);
|
||||
@@ -66,7 +63,7 @@ like( $output,
|
||||
"Original table must exist"
|
||||
);
|
||||
|
||||
$sb->load_file('master', "$sample/basic_no_fks.sql");
|
||||
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
|
||||
$master_dbh->do("USE pt_osc");
|
||||
$slave_dbh->do("USE pt_osc");
|
||||
|
||||
@@ -100,7 +97,7 @@ like( $output,
|
||||
# Checks for the new table.
|
||||
# #############################################################################
|
||||
|
||||
$sb->load_file('master', "$sample/basic_no_fks.sql");
|
||||
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
|
||||
$master_dbh->do("USE pt_osc");
|
||||
$slave_dbh->do("USE pt_osc");
|
||||
|
||||
@@ -126,4 +123,4 @@ like(
|
||||
# #############################################################################
|
||||
$sb->wipe_clean($master_dbh);
|
||||
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
|
||||
exit;
|
||||
done_testing;
|
||||
|
Reference in New Issue
Block a user