Merge lp:~percona-toolkit-dev/percona-toolkit/pk-del-bug-1103672-encore into lp:percona-toolkit/2.1

Proposed by Daniel Nichter
Status: Merged
Merged at revision: 523
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/pk-del-bug-1103672-encore
Merge into: lp:percona-toolkit/2.1
Diff against target: 322 lines (+218/-16)
4 files modified
bin/pt-online-schema-change (+104/-15)
t/pt-online-schema-change/bugs.t (+29/-1)
t/pt-online-schema-change/check_alter.t (+65/-0)
t/pt-online-schema-change/samples/del-trg-bug-1103672.sql (+20/-0)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/pk-del-bug-1103672-encore
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+145527@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve
523. By Daniel Nichter

Soften the warning about 'drop primary key' a little.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/pt-online-schema-change'
--- bin/pt-online-schema-change 2013-01-03 00:54:18 +0000
+++ bin/pt-online-schema-change 2013-01-30 15:06:22 +0000
@@ -8370,19 +8370,74 @@
8370 # Find a pk or unique index to use for the delete trigger. can_nibble()8370 # Find a pk or unique index to use for the delete trigger. can_nibble()
8371 # above returns an index, but NibbleIterator will use non-unique indexes,8371 # above returns an index, but NibbleIterator will use non-unique indexes,
8372 # so we have to do this again here.8372 # so we have to do this again here.
8373 my $indexes = $new_tbl->{tbl_struct}->{keys}; # brevity8373 {
8374 foreach my $index ( $tp->sort_indexes($new_tbl->{tbl_struct}) ) {8374 my $indexes = $new_tbl->{tbl_struct}->{keys}; # brevity
8375 if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {8375 foreach my $index ( $tp->sort_indexes($new_tbl->{tbl_struct}) ) {
8376 PTDEBUG && _d('Delete trigger index:', Dumper($index));8376 if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
8377 $new_tbl->{del_index} = $index;8377 PTDEBUG && _d('Delete trigger new index:', Dumper($index));
8378 last;8378 $new_tbl->{del_index} = $index;
8379 }8379 last;
8380 }8380 }
8381 }
8382 PTDEBUG && _d('New table delete index:', $new_tbl->{del_index});
8383 }
8384
8385 {
8386 my $indexes = $orig_tbl->{tbl_struct}->{keys}; # brevity
8387 foreach my $index ( $tp->sort_indexes($orig_tbl->{tbl_struct}) ) {
8388 if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
8389 PTDEBUG && _d('Delete trigger orig index:', Dumper($index));
8390 $orig_tbl->{del_index} = $index;
8391 last;
8392 }
8393 }
8394 PTDEBUG && _d('Orig table delete index:', $orig_tbl->{del_index});
8395 }
8396
8381 if ( !$new_tbl->{del_index} ) {8397 if ( !$new_tbl->{del_index} ) {
8382 die "The new table $new_tbl->{name} does not have a PRIMARY KEY "8398 die "The new table $new_tbl->{name} does not have a PRIMARY KEY "
8383 . "or a unique index which is required for the DELETE trigger.\n";8399 . "or a unique index which is required for the DELETE trigger.\n";
8384 }8400 }
83858401
8402 # Determine whether to use the new or orig table delete index.
8403 # The new table del index is preferred due to
8404 # https://bugs.launchpad.net/percona-toolkit/+bug/1062324
8405 # In short, if the chosen del index is re-created with new columns,
8406 # its original columns may be dropped, so just use its new columns.
8407 # But, due to https://bugs.launchpad.net/percona-toolkit/+bug/1103672,
8408 # the chosen del index on the new table may reference columns which
8409 # do not/no longer exist in the orig table, so we check for this
8410 # and, if it's the case, we fall back to using the del index from
8411 # the orig table.
8412 my $del_tbl = $new_tbl; # preferred
8413 my $new_del_index_cols # brevity
8414 = $new_tbl->{tbl_struct}->{keys}->{ $new_tbl->{del_index} }->{cols};
8415 foreach my $new_del_index_col ( @$new_del_index_cols ) {
8416 if ( !exists $orig_cols->{$new_del_index_col} ) {
8417 if ( !$orig_tbl->{del_index} ) {
8418 die "The new table index $new_tbl->{del_index} would be used "
8419 . "for the DELETE trigger, but it uses column "
8420 . "$new_del_index_col which does not exist in the original "
8421 . "table and the original table does not have a PRIMARY KEY "
8422 . "or a unique index to use for the DELETE trigger.\n";
8423 }
8424 print "Using original table index $orig_tbl->{del_index} for the "
8425 . "DELETE trigger instead of new table index $new_tbl->{del_index} "
8426 . "because the new table index uses column $new_del_index_col "
8427 . "which does not exist in the original table.\n";
8428 $del_tbl = $orig_tbl;
8429 last;
8430 }
8431 }
8432
8433 {
8434 my $del_cols
8435 = $del_tbl->{tbl_struct}->{keys}->{ $del_tbl->{del_index} }->{cols};
8436 PTDEBUG && _d('Index for delete trigger: table', $del_tbl->{name},
8437 'index', $del_tbl->{del_index},
8438 'columns', @$del_cols);
8439 }
8440
8386 # ########################################################################8441 # ########################################################################
8387 # Step 3: Create the triggers to capture changes on the original table and8442 # Step 3: Create the triggers to capture changes on the original table and
8388 # apply them to the new table.8443 # apply them to the new table.
@@ -8412,6 +8467,7 @@
8412 create_triggers(8467 create_triggers(
8413 orig_tbl => $orig_tbl,8468 orig_tbl => $orig_tbl,
8414 new_tbl => $new_tbl,8469 new_tbl => $new_tbl,
8470 del_tbl => $del_tbl,
8415 columns => \@common_cols,8471 columns => \@common_cols,
8416 Cxn => $cxn,8472 Cxn => $cxn,
8417 Quoter => $q,8473 Quoter => $q,
@@ -8931,6 +8987,28 @@
8931 my $ok = 1;8987 my $ok = 1;
89328988
8933 # ########################################################################8989 # ########################################################################
8990 # Check for DROP PRIMARY KEY.
8991 # ########################################################################
8992 if ( $alter =~ m/DROP\s+PRIMARY\s+KEY/i ) {
8993 my $msg = "--alter contains 'DROP PRIMARY KEY'. Dropping and "
8994 . "altering the primary key can be dangerous, "
8995 . "especially if the original table does not have other "
8996 . "unique indexes.\n";
8997 if ( $dry_run ) {
8998 print $msg;
8999 }
9000 else {
9001 $ok = 0;
9002 warn $msg
9003 . "The tool should handle this correctly, but you should "
9004 . "test it first and carefully examine the triggers which "
9005 . "rely on the PRIMARY KEY or a unique index. Specify "
9006 . "--no-check-alter to disable this check and perform the "
9007 . "--alter.\n";
9008 }
9009 }
9010
9011 # ########################################################################
8934 # Check for renamed columns.9012 # Check for renamed columns.
8935 # https://bugs.launchpad.net/percona-toolkit/+bug/10685629013 # https://bugs.launchpad.net/percona-toolkit/+bug/1068562
8936 # ########################################################################9014 # ########################################################################
@@ -8976,6 +9054,7 @@
8976 }9054 }
89779055
8978 if ( !$ok ) {9056 if ( !$ok ) {
9057 # check_alter.t relies on this output.
8979 die "--check-alter failed.\n";9058 die "--check-alter failed.\n";
8980 }9059 }
89819060
@@ -9536,11 +9615,11 @@
95369615
9537sub create_triggers {9616sub create_triggers {
9538 my ( %args ) = @_;9617 my ( %args ) = @_;
9539 my @required_args = qw(orig_tbl new_tbl columns Cxn Quoter OptionParser);9618 my @required_args = qw(orig_tbl new_tbl del_tbl columns Cxn Quoter OptionParser);
9540 foreach my $arg ( @required_args ) {9619 foreach my $arg ( @required_args ) {
9541 die "I need a $arg argument" unless $args{$arg};9620 die "I need a $arg argument" unless $args{$arg};
9542 }9621 }
9543 my ($orig_tbl, $new_tbl, $cols, $cxn, $q, $o) = @args{@required_args};9622 my ($orig_tbl, $new_tbl, $del_tbl, $cols, $cxn, $q, $o) = @args{@required_args};
95449623
9545 # This sub works for --dry-run and --execute. With --dry-run it's9624 # This sub works for --dry-run and --execute. With --dry-run it's
9546 # only interesting if --print is specified, too; then the user can9625 # only interesting if --print is specified, too; then the user can
@@ -9569,8 +9648,8 @@
9569 # unique indexes can be nullable. Cols are from the new table and9648 # unique indexes can be nullable. Cols are from the new table and
9570 # they may have been renamed9649 # they may have been renamed
9571 my %old_col_for = map { $_->{new} => $_->{old} } @$cols;9650 my %old_col_for = map { $_->{new} => $_->{old} } @$cols;
9572 my $tbl_struct = $new_tbl->{tbl_struct};9651 my $tbl_struct = $del_tbl->{tbl_struct};
9573 my $del_index = $new_tbl->{del_index};9652 my $del_index = $del_tbl->{del_index};
9574 my $del_index_cols = join(" AND ", map {9653 my $del_index_cols = join(" AND ", map {
9575 my $new_col = $_;9654 my $new_col = $_;
9576 my $old_col = $old_col_for{$new_col} || $new_col;9655 my $old_col = $old_col_for{$new_col} || $new_col;
@@ -10226,9 +10305,19 @@
10226C<CHANGE COLUMN name new_name> would lead to that column's data being lost.10305C<CHANGE COLUMN name new_name> would lead to that column's data being lost.
10227The tool now parses the alter statement and tries to catch these cases, so10306The tool now parses the alter statement and tries to catch these cases, so
10228the renamed columns should have the same data as the originals. However, the10307the renamed columns should have the same data as the originals. However, the
10229code that does this is not a full-blown SQL parser, so we recommend that users10308code that does this is not a full-blown SQL parser, so you should first
10230run the tool with L<"--dry-run"> and check if it's detecting the renames10309run the tool with L<"--dry-run"> and L<"--print"> and verify that it detects
10231correctly.10310the renamed columns correctly.
10311
10312=item DROP PRIMARY KEY
10313
10314If L<"--alter"> contain C<DROP PRIMARY KEY> (case- and space-insensitive),
10315a warning is printed and the tool exits unless L<"--dry-run"> is specified.
10316Altering the primary key can be dangerous, but the tool can handle it.
10317The tool's triggers, particularly the DELETE trigger, are most affected by
10318altering the primary key because the tool prefers to use the primary key
10319for its triggers. You should first run the tool with L<"--dry-run"> and
10320L<"--print"> and verify that the triggers are correct.
1023210321
10233=back10322=back
1023410323
1023510324
=== modified file 't/pt-online-schema-change/bugs.t'
--- t/pt-online-schema-change/bugs.t 2012-12-11 16:54:24 +0000
+++ t/pt-online-schema-change/bugs.t 2013-01-30 15:06:22 +0000
@@ -210,7 +210,7 @@
210 sub { pt_online_schema_change::main(@args,210 sub { pt_online_schema_change::main(@args,
211 "$master_dsn,D=test,t=t1",211 "$master_dsn,D=test,t=t1",
212 "--alter", "drop key 2bpk, drop key c3, drop primary key, drop c1, add primary key (c2, c3(4)), add key (c3(4))",212 "--alter", "drop key 2bpk, drop key c3, drop primary key, drop c1, add primary key (c2, c3(4)), add key (c3(4))",
213 qw(--execute --no-drop-new-table --no-swap-tables)) },213 qw(--no-check-alter --execute --no-drop-new-table --no-swap-tables)) },
214 );214 );
215215
216 # Since _t1_new no longer has the c1 column, the bug caused this216 # Since _t1_new no longer has the c1 column, the bug caused this
@@ -233,6 +233,34 @@
233 undef,233 undef,
234 "Delete trigger works after altering PK (bug 1062324)"234 "Delete trigger works after altering PK (bug 1062324)"
235 );235 );
236
237 # Another instance of this bug:
238 # https://bugs.launchpad.net/percona-toolkit/+bug/1103672
239 $sb->load_file('master', "$sample/del-trg-bug-1103672.sql");
240
241 ($output, $exit_status) = full_output(
242 sub { pt_online_schema_change::main(@args,
243 "$master_dsn,D=test,t=t1",
244 "--alter", "drop primary key, add column _id int unsigned not null primary key auto_increment FIRST",
245 qw(--no-check-alter --execute --no-drop-new-table --no-swap-tables)) },
246 );
247
248 eval {
249 $master_dbh->do("DELETE FROM test.t1 WHERE id=1");
250 };
251 is(
252 $EVAL_ERROR,
253 "",
254 "No delete trigger error after altering PK (bug 1103672)"
255 ) or diag($output);
256
257 $row = $master_dbh->selectrow_arrayref("SELECT * FROM test._t1_new WHERE id=1");
258 is(
259 $row,
260 undef,
261 "Delete trigger works after altering PK (bug 1103672)"
262 );
263
236}264}
237265
238# #############################################################################266# #############################################################################
239267
=== added file 't/pt-online-schema-change/check_alter.t'
--- t/pt-online-schema-change/check_alter.t 1970-01-01 00:00:00 +0000
+++ t/pt-online-schema-change/check_alter.t 2013-01-30 15:06:22 +0000
@@ -0,0 +1,65 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13
14use Data::Dumper;
15use PerconaTest;
16use Sandbox;
17
18require "$trunk/bin/pt-online-schema-change";
19
20my $dp = new DSNParser(opts=>$dsn_opts);
21my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
22my $master_dbh = $sb->get_dbh_for('master');
23my $slave_dbh = $sb->get_dbh_for('slave1');
24
25if ( !$master_dbh ) {
26 plan skip_all => 'Cannot connect to sandbox master';
27}
28elsif ( !$slave_dbh ) {
29 plan skip_all => 'Cannot connect to sandbox slave1';
30}
31
32# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
33# so we need to specify --lock-wait-timeout=3 else the tool will die.
34my $master_dsn = 'h=127.1,P=12345,u=msandbox,p=msandbox';
35my @args = (qw(--lock-wait-timeout 3));
36my $output;
37my $exit_status;
38my $sample = "t/pt-online-schema-change/samples/";
39
40# #############################################################################
41# DROP PRIMARY KEY
42# #############################################################################
43
44$sb->load_file('master', "$sample/del-trg-bug-1103672.sql");
45
46($output, $exit_status) = full_output(
47 sub { pt_online_schema_change::main(@args,
48 "$master_dsn,D=test,t=t1",
49 "--alter", "drop primary key, add column _id int unsigned not null primary key auto_increment FIRST",
50 qw(--execute)),
51 },
52);
53
54like(
55 $output,
56 qr/--check-alter failed/,
57 "DROP PRIMARY KEY"
58);
59
60# #############################################################################
61# Done.
62# #############################################################################
63$sb->wipe_clean($master_dbh);
64ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
65done_testing;
066
=== added file 't/pt-online-schema-change/samples/del-trg-bug-1103672.sql'
--- t/pt-online-schema-change/samples/del-trg-bug-1103672.sql 1970-01-01 00:00:00 +0000
+++ t/pt-online-schema-change/samples/del-trg-bug-1103672.sql 2013-01-30 15:06:22 +0000
@@ -0,0 +1,20 @@
1drop database if exists test;
2create database test;
3use test;
4
5CREATE TABLE `t1` (
6 `id` int(10) unsigned NOT NULL,
7 `x` char(3) DEFAULT NULL,
8 PRIMARY KEY (`id`)
9) ENGINE=InnoDB;
10
11INSERT INTO t1 VALUES
12 (1, 'a'),
13 (2, 'b'),
14 (3, 'c'),
15 (4, 'd'),
16 (5, 'f'),
17 (6, 'g'),
18 (7, 'h'),
19 (8, 'i'),
20 (9, 'j');

Subscribers

People subscribed via source and target branches