PT-153 Added --[no]fail-on-unique-key-change param

This commit is contained in:
Carlos Salguero
2017-06-27 13:53:08 -03:00
parent a01f4650de
commit 3d0deabbdf
3 changed files with 174 additions and 4 deletions

View File

@@ -8941,6 +8941,7 @@ sub main {
renamed_cols => $renamed_cols,
Cxn => $cxn,
TableParser => $tp,
OptionParser => $o,
);
}
}
@@ -9918,15 +9919,31 @@ sub validate_tries {
sub check_alter {
my (%args) = @_;
my @required_args = qw(alter tbl dry_run Cxn TableParser);
my @required_args = qw(alter tbl dry_run Cxn TableParser OptionParser);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless exists $args{$arg};
}
my ($alter, $tbl, $dry_run, $cxn, $tp) = @args{@required_args};
my ($alter, $tbl, $dry_run, $cxn, $tp, $o) = @args{@required_args};
my $ok = 1;
$alter = clean_alter($alter);
my $unique_fields = get_unique_index_fields($alter);
if (scalar @$unique_fields && $o->get('fail-on-unique-key-change')) {
my $msg = "You are trying to add an unique key. This is highly discouraged.\n"
. "Please read the documentation for the --fail-on-unique-key-change parameter.\n"
. "You can check if there are already rows that will produce duplicated indexes "
. "by running this/these query/queries:\n\n";
foreach my $fields (@$unique_fields) {
my $sql = "SELECT IF(COUNT(DISTINCT " . join(", ", @$fields) . ") = COUNT(*),\n"
. " 'Yes, the desired unique index currently contains only unique values', \n"
. " 'No, the desired unique index contains duplicated values. There will be data loss'\n"
. ") AS IsThereUniqueness FROM `$tbl->{db}`.`$tbl->{tbl}`;\n\n";
$msg .= $sql;
}
$msg .= "Keep in mind that these queries could take a long time and consume a lot of resources\n\n";
die ($msg);
}
# ########################################################################
# Check for DROP PRIMARY KEY.
@@ -10035,7 +10052,7 @@ sub get_unique_index_fields {
$clean .= $suffix;
my $fields = [];
my $fields_re = qr/.*?(?:ADD|CREATE)\s*UNIQUE\s*INDEX\s*(?:\w+\s+on\s+\w+|\w+)(?:\s\w+)* \((.*?)\)/i;
my $fields_re = qr/UNIQUE\s*INDEX\s*(?:.*?)* \((.*?)\)/i;
while($clean =~ /$fields_re/g) {
push @$fields, [ split /\s*,\s*/, $1 ];
@@ -11797,6 +11814,42 @@ only perform some safety checks and exit. This helps ensure that you have read
documentation and understand how to use this tool. If you have not read the
documentation, then do not specify this option.
=item --[no]fail-on-unique-key-change
default: yes
Force pt-online-schema-change to run even if the specified statement for --alter is
trying to add an unique index.
Since pt-online-schema-change uses INSERT IGNORE to copy rows to the new table, if the
row being written produce a duplicate key, it will fail silently.
Example:
CREATE DATABASE test;
USE test;
CREATE TABLE `a` (
`id` int(11) NOT NULL,
`unique_id` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into a values (1, "a");
insert into a values (2, "b");
insert into a values (3, "");
insert into a values (4, "");
insert into a values (5, NULL);
insert into a values (6, NULL);
Using pt-osc to add an unique index on the `unique_id` field, will cause some rows to
be lost due to the use os INSERT IGNORE to copy rows from the source table.
For this reason, pt-osc will fail if it detects that the --alter parameter is trying
to add an unique key and it will show an example query to run to detect if there are
rows that will produce duplicated indexes.
Even if you run the query and there are no rows that will produce duplicated indexes,
take into consideration that running INSERTs while pt-osc is running, could produce
duplicated indexes and those rows are going to be lost without any notification.
=item --force
This options bypasses confirmation in case of using alter-foreign-keys-method = none , which might break foreign key constraints.

View File

@@ -0,0 +1,98 @@
#!/usr/bin/env perl
BEGIN {
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
};
use strict;
use warnings FATAL => 'all';
use threads;
use English qw(-no_match_vars);
use Test::More;
use Data::Dumper;
use PerconaTest;
use Sandbox;
use SqlModes;
use File::Temp qw/ tempdir /;
plan tests => 6;
require "$trunk/bin/pt-online-schema-change";
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $master_dbh = $sb->get_dbh_for('master');
my $master_dsn = 'h=127.1,P=12345,u=msandbox,p=msandbox';
if ( !$master_dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
# so we need to specify --set-vars innodb_lock_wait_timeout=3 else the
# tool will die.
my @args = (qw(--set-vars innodb_lock_wait_timeout=3));
my $output;
my $exit_status;
my $sample = "t/pt-online-schema-change/samples/";
$sb->load_file('master', "$sample/pt-153.sql");
($output, $exit_status) = full_output(
sub { pt_online_schema_change::main(@args, "$master_dsn,D=test,t=t1",
'--execute',
'--alter', "ADD UNIQUE INDEX c1 (f2, f3)",
),
},
);
isnt(
$exit_status,
0,
"PT-153 Adding unique index exit status != 0.",
);
like(
$output,
qr/You are trying to add an unique key. This is highly discouraged./s,
"PT-153 Adding unique index warning message.",
);
($output, $exit_status) = full_output(
sub { pt_online_schema_change::main(@args, "$master_dsn,D=test,t=t1",
'--execute',
'--alter', "ADD UNIQUE INDEX c1 (f2, f3), CREATE UNIQUE INDEX idx2 ON test.t1 (f3)",
),
},
);
isnt(
$exit_status,
0,
"PT-153 Adding multiple unique indexes exit status != 0.",
);
like(
$output,
qr/You are trying to add an unique key. This is highly discouraged./s,
"PT-153 Adding multiple unique indexes warning message.",
);
like(
$output,
qr/SELECT IF\(COUNT\(DISTINCT f2, f3\).*?SELECT IF\(COUNT\(DISTINCT f3\)/s,
"PT-153 Adding multiple unique indexes -> multime example queries.",
);
$master_dbh->do("DROP DATABASE IF EXISTS test");
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($master_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;

View File

@@ -0,0 +1,19 @@
DROP DATABASE IF EXISTS test;
CREATE DATABASE test;
USE test;
CREATE TABLE test.t1 (
id int,
f2 int,
f3 int NULL,
f4 int,
PRIMARY KEY (id)
);
INSERT INTO test.t1 VALUES
(1,1,1,1),
(2,1,1,1),
(3,1,2,1),
(4,2,NULL,2),
(5,3,NULL,2),
(6,4,4,4),
(7,4,4,4);