Do not ever create --new-table. Rewrite basics.t. Add more debug statements.

This commit is contained in:
Daniel Nichter
2012-03-24 12:47:08 -06:00
parent 7add482367
commit d3ad8c84c0
4 changed files with 344 additions and 208 deletions

View File

@@ -16,150 +16,227 @@ use Sandbox;
require "$trunk/bin/pt-online-schema-change";
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Quotekeys = 0;
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $dbh = $sb->get_dbh_for('master');
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $master_dbh = $sb->get_dbh_for('master');
my $slave_dbh = $sb->get_dbh_for('slave1');
if ( !$dbh ) {
if ( !$master_dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
else {
plan tests => 23;
plan tests => 38;
}
my $output = "";
my $cnf = '/tmp/12345/my.sandbox.cnf';
my @args = ('-F', $cnf, '--execute');
my $exit = 0;
my $q = new Quoter();
my $tp = new TableParser(Quoter => $q);
my @args = qw(--lock-wait-timeout 3);
my $output = "";
my $dsn = "h=127.1,P=12345,u=msandbox,p=msandbox";
my $exit = 0;
my $sample = "t/pt-online-schema-change/samples";
my $rows;
$sb->load_file('master', "t/pt-online-schema-change/samples/small_table.sql");
$dbh->do('use mkosc');
# #############################################################################
# Tool shouldn't run without --execute (bug 933232).
# #############################################################################
$sb->load_file('master', "$sample/basic_no_fks.sql");
PerconaTest::wait_for_table($slave_dbh, "pt_osc.t", "id=20");
# --new-table really ensures the tool exists before doing stuff because
# setting up the new table is the first thing the tool does and this would
# cause an error because mysql.user already exists. To prove this, add
# --dry-run and the test will fail because the code doesn't exit early.
$output = output(
sub { pt_online_schema_change::main('h=127.1,P=12345,u=msandbox,p=msandbox,D=mkosc,t=a') },
sub { $exit = pt_online_schema_change::main(@args, "$dsn,t=pt_osc.t",
qw(--new-table mysql.user)) }
);
like(
$output,
qr/you did not specify --execute/,
"Doesn't run without --execute"
);
# #############################################################################
# --check-tables-and-exit
# #############################################################################
eval {
$exit = pt_online_schema_change::main(@args,
'D=mkosc,t=a', qw(--check-tables-and-exit --quiet))
};
is(
$EVAL_ERROR,
"",
"--check-tables-and-exit"
qr/neither --dry-run nor --execute was specified/,
"Doesn't run without --execute (bug 933232)"
);
is(
$exit,
0,
"Exit status 0"
"Exit 0"
);
# #############################################################################
# --cleanup-and-exit
# #############################################################################
eval {
$exit = pt_online_schema_change::main(@args,
'D=mkosc,t=a', qw(--cleanup-and-exit --quiet))
};
is(
$EVAL_ERROR,
"",
"--cleanup-and-exit",
);
is(
$exit,
0,
"Exit status 0"
);
# #############################################################################
# The most basic: copy, alter and rename a small table that's not even active.
# A helper sub to do the heavy lifting for us.
# #############################################################################
output(
sub { $exit = pt_online_schema_change::main(@args,
'D=mkosc,t=a', qw(--alter ENGINE=InnoDB)) },
);
sub test_alter_table {
my (%args) = @_;
return if $args{skip};
$rows = $dbh->selectall_hashref('show table status from mkosc', 'name');
is(
$rows->{a}->{engine},
'InnoDB',
"New table ENGINE=InnoDB"
);
my @required_args = qw(name table test_type cmds);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
}
my ($name, $table, $test_type, $cmds) = @args{@required_args};
is(
$rows->{__old_a}->{engine},
'MyISAM',
"Kept old table, ENGINE=MyISAM"
);
my ($db, $tbl) = $q->split_unquote($table);
my $pk_col = $args{pk_col} || 'id';
my $org_rows = $dbh->selectall_arrayref('select * from mkosc.__old_a order by i');
my $new_rows = $dbh->selectall_arrayref('select * from mkosc.a order by i');
is_deeply(
$new_rows,
$org_rows,
"New tables rows identical to old table rows"
);
if ( my $file = $args{file} ) {
$sb->load_file('master', "$sample/$file");
if ( my $wait = $args{wait} ) {
PerconaTest::wait_for_table($slave_dbh, @$wait);
}
else {
PerconaTest::wait_for_table($slave_dbh, $table, "`$pk_col`=$args{max_id}");
}
$master_dbh->do("USE `$db`");
$slave_dbh->do("USE `$db`");
}
is(
$exit,
0,
"Exit status 0"
);
my $ddl = $tp->get_create_table($master_dbh, $db, $tbl);
my $tbl_struct = $tp->parse($ddl);
my $cols = '*';
if ( $test_type eq 'drop_col' && !grep { $_ eq '--dry-run' } @$cmds ) {
# Don't select the column being dropped.
my $col = $args{drop_col};
die "I need a drop_col argument" unless $col;
$cols = join(', ', grep { $_ ne $col } @{$tbl_struct->{cols}});
}
my $orig_rows = $master_dbh->selectall_arrayref(
"SELECT $cols FROM $table ORDER BY `$pk_col`");
my $orig_tbls = $master_dbh->selectall_arrayref(
"SHOW TABLES FROM `$db`");
my @orig_fks;
if ( $args{check_fks} ) {
foreach my $tbl ( @$orig_tbls ) {
my $fks = $tp->get_fks(
$tp->get_create_table($master_dbh, $db, $tbl->[0]));
push @orig_fks, $fks;
}
}
$output = output(
sub { $exit = pt_online_schema_change::main(
@args,
"$dsn,D=$db,t=$tbl",
@$cmds,
)},
);
is(
$exit,
0,
"$name exit 0"
);
# There should be no new or missing tables.
my $new_tbls = $master_dbh->selectall_arrayref("SHOW TABLES FROM `$db`");
is_deeply(
$new_tbls,
$orig_tbls,
"$name tables"
);
# Rows in the original and new table should be identical.
my $new_rows = $master_dbh->selectall_arrayref("SELECT * FROM $table ORDER BY `$pk_col`");
is_deeply(
$new_rows,
$orig_rows,
"$name rows"
);
# Check if the ALTER was actually done.
if ( $test_type eq 'drop_col' ) {
my $col = $q->quote($args{drop_col});
my $ddl = $tp->get_create_table($master_dbh, $db, $tbl);
if ( grep { $_ eq '--dry-run' } @$cmds ) {
like(
$ddl,
qr/^\s+$col\s+/m,
"$name ALTER DROP COLUMN=$args{drop_col} (dry run)"
);
}
else {
unlike(
$ddl,
qr/^\s+$col\s+/m,
"$name ALTER DROP COLUMN=$args{drop_col}"
);
}
}
elsif ( $test_type eq 'add_col' ) {
}
elsif ( $test_type eq 'new_engine' ) {
my $new_engine = lc($args{new_engine});
die "I need a new_engine argument" unless $new_engine;
my $rows = $master_dbh->selectall_hashref(
"SHOW TABLE STATUS FROM `$db`", "name");
is(
lc($rows->{$tbl}->{engine}),
$new_engine,
"$name ALTER ENGINE=$args{new_engine}"
);
}
if ( $args{check_fks} ) {
my @new_fks;
foreach my $tbl ( @$orig_tbls ) {
my $fks = $tp->get_fks(
$tp->get_create_table($master_dbh, $db, $tbl->[0]));
push @new_fks, $fks;
}
is_deeply(
\@new_fks,
\@orig_fks,
"$name FK constraints"
);
}
return;
}
# #############################################################################
# No --alter and --drop-old-table.
# The most basic: alter a small table with no fks that's not active.
# #############################################################################
$dbh->do('drop table if exists mkosc.__old_a'); # from previous run
$sb->load_file('master', "t/pt-online-schema-change/samples/small_table.sql");
output(
sub { $exit = pt_online_schema_change::main(@args,
'D=mkosc,t=a', qw(--drop-old-table)) },
test_alter_table(
name => "Basic no fks --dry-run",
table => "pt_osc.t",
file => "basic_no_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "MyISAM",
cmds => [qw(--dry-run --alter ENGINE=InnoDB)],
);
$rows = $dbh->selectall_hashref('show table status from mkosc', 'name');
is(
$rows->{a}->{engine},
'MyISAM',
"No --alter, new table still ENGINE=MyISAM"
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.sql",
# max_id => 20,
test_type => "new_engine",
new_engine => "InnoDB",
cmds => [qw(--execute --alter ENGINE=InnoDB)],
);
ok(
!exists $rows->{__old_a},
"--drop-old-table"
);
$new_rows = $dbh->selectall_arrayref('select * from mkosc.a order by i');
is_deeply(
$new_rows,
$org_rows, # from previous run since old table was dropped this run
"New tables rows identical to old table rows"
);
is(
$exit,
0,
"Exit status 0"
test_alter_table(
name => "--execute but no --alter",
table => "pt_osc.t",
file => "basic_no_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "MyISAM",
cmds => [qw(--execute)],
);
# ############################################################################
@@ -167,82 +244,93 @@ is(
# ############################################################################
# The tables we're loading have fk constraints like:
# country
# ^- city (on update cascade)
# ^- address (on update cascade)
# country <-- city <-- address
############################
# rebuild_constraints method
############################
$sb->load_file('master', "t/pt-online-schema-change/samples/fk_tables_schema.sql");
# rebuild_constraints method -- This parses the fk constraint ddls from
# the create table ddl, rewrites them, then does an alter table on the
# child tables so they point back to the original table name.
# city has a fk constraint on country, so get its original table def.
my $orig_table_def = $dbh->selectrow_hashref('show create table mkosc.city')->{'create table'};
# Alter the parent table. The error we need to avoid is:
# DBD::mysql::db do failed: Cannot delete or update a parent row:
# a foreign key constraint fails [for Statement "DROP TABLE
# `mkosc`.`__old_country`"]
output(
sub { $exit = pt_online_schema_change::main(@args,
'D=mkosc,t=country', qw(--child-tables auto_detect --drop-old-table),
qw(--update-foreign-keys-method rebuild_constraints)) },
);
is(
$exit,
0,
"Exit status 0 (rebuild_constraints method)"
test_alter_table(
name => "Basic FK rebuild --dry-run",
table => "pt_osc.country",
pk_col => "country_id",
file => "basic_with_fks.sql",
wait => ["pt_osc.address", "address_id=5"],
test_type => "drop_col",
drop_col => "last_update",
check_fks => 1,
cmds => [
qw(
--dry-run
--find-child-tables
--update-foreign-keys-method rebuild_constraints
),
'--alter', 'DROP COLUMN last_update',
],
);
$rows = $dbh->selectall_arrayref('show tables from mkosc like "\_\_%"');
is_deeply(
$rows,
[],
"Old table dropped (rebuild_constraints method)"
test_alter_table(
name => "Basic FK rebuild --execute",
table => "pt_osc.country",
pk_col => "country_id",
# The previous test should not have modified the table.
# file => "basic_with_fks.sql",
# wait => ["pt_osc.address", "address_id=5"],
test_type => "drop_col",
drop_col => "last_update",
check_fks => 1,
cmds => [
qw(
--execute
--find-child-tables
--update-foreign-keys-method rebuild_constraints
),
'--alter', 'DROP COLUMN last_update',
],
);
# Get city's table def again and verify that its fk constraint still
# references country. When country was renamed to __old_country, MySQL
# also updated city's fk constraint to __old_country. We should have
# dropped and re-added that constraint exactly, changing only __old_country
# to country, like it originally was.
my $new_table_def = $dbh->selectrow_hashref('show create table mkosc.city')->{'create table'};
is(
$new_table_def,
$orig_table_def,
"Updated child table foreign key constraint (rebuild_constraints method)"
# drop_old_table method -- This method tricks MySQL by disabling fk checks,
# then dropping the original table and renaming the new table in its place.
# Since fk checks were disabled, MySQL doesn't update the child table fk refs.
# Somewhat dangerous, but quick. Downside: table doesn't exist for a moment.
test_alter_table(
name => "Basic FK drop-swap --dry-run",
table => "pt_osc.country",
pk_col => "country_id",
file => "basic_with_fks.sql",
wait => ["pt_osc.address", "address_id=5"],
test_type => "drop_col",
drop_col => "last_update",
check_fks => 1,
cmds => [
qw(
--dry-run
--find-child-tables
--update-foreign-keys-method drop_old_table
),
'--alter', 'DROP COLUMN last_update',
],
);
#######################
# drop_old_table method
#######################
$sb->load_file('master', "t/pt-online-schema-change/samples/fk_tables_schema.sql");
$orig_table_def = $dbh->selectrow_hashref('show create table mkosc.city')->{'create table'};
output(
sub { $exit = pt_online_schema_change::main(@args,
'D=mkosc,t=country', qw(--child-tables auto_detect),
qw(--update-foreign-keys-method drop_old_table)) },
);
is(
$exit,
0,
"Exit status 0 (drop_old_table method)"
);
$rows = $dbh->selectall_arrayref('show tables from mkosc like "\_\_%"');
is_deeply(
$rows,
[],
"Old table dropped (drop_old_table method)"
) or print Dumper($rows);
$new_table_def = $dbh->selectrow_hashref('show create table mkosc.city')->{'create table'};
is(
$new_table_def,
$orig_table_def,
"Updated child table foreign key constraint (drop_old_table method)"
test_alter_table(
name => "Basic FK drop-swap --execute",
table => "pt_osc.country",
pk_col => "country_id",
# The previous test should not have modified the table.
# file => "basic_with_fks.sql",
# wait => ["pt_osc.address", "address_id=5"],
test_type => "drop_col",
drop_col => "last_update",
check_fks => 1,
cmds => [
qw(
--execute
--find-child-tables
--update-foreign-keys-method drop_old_table
),
'--alter', 'DROP COLUMN last_update',
],
);
# #############################################################################
@@ -253,19 +341,19 @@ sub test_table {
my ($file, $name) = @args{qw(file name)};
$sb->load_file('master', "t/lib/samples/osc/$file");
PerconaTest::wait_for_table($dbh, "osc.t", "id=5");
PerconaTest::wait_for_table($dbh, "osc.__new_t");
$dbh->do('use osc');
$dbh->do("DROP TABLE IF EXISTS osc.__new_t");
PerconaTest::wait_for_table($master_dbh, "osc.t", "id=5");
PerconaTest::wait_for_table($master_dbh, "osc.__new_t");
$master_dbh->do('use osc');
$master_dbh->do("DROP TABLE IF EXISTS osc.__new_t");
$org_rows = $dbh->selectall_arrayref('select * from osc.t order by id');
my $org_rows = $master_dbh->selectall_arrayref('select * from osc.t order by id');
output(
sub { $exit = pt_online_schema_change::main(@args,
'D=osc,t=t', qw(--alter ENGINE=InnoDB)) },
"$dsn,D=osc,t=t", qw(--alter ENGINE=InnoDB)) },
);
$new_rows = $dbh->selectall_arrayref('select * from osc.t order by id');
my $new_rows = $master_dbh->selectall_arrayref('select * from osc.t order by id');
is_deeply(
$new_rows,
@@ -293,5 +381,5 @@ test_table(
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($dbh);
$sb->wipe_clean($master_dbh);
exit;