pt-table-sync: Bail out if --lock-and-rename and MySQL <= 5.5

This commit is contained in:
Brian Fraser fraserb@gmail.com
2012-06-05 14:41:36 -03:00
parent b96e43419f
commit 1f4d99665f
2 changed files with 174 additions and 11 deletions

View File

@@ -1536,20 +1536,81 @@ sub new {
sub parse { sub parse {
my ( $self, $str ) = @_; my ( $self, $str ) = @_;
my $result = sprintf('%03d%03d%03d', $str =~ m/(\d+)/g); my @version_parts = $str =~ m/(\d+)/g;
@version_parts = map { $_ || 0 } @version_parts[0..2];
my $result = sprintf('%03d%03d%03d', @version_parts);
PTDEBUG && _d($str, 'parses to', $result); PTDEBUG && _d($str, 'parses to', $result);
return $result; return $result;
} }
sub version_cmp {
my ($self, $dbh, $target, $cmp) = @_;
my $version = $self->version($dbh);
my $result;
if ( $cmp eq 'ge' ) {
$result = $self->{$dbh} ge $self->parse($target) ? 1 : 0;
}
elsif ( $cmp eq 'gt' ) {
$result = $self->{$dbh} gt $self->parse($target) ? 1 : 0;
}
elsif ( $cmp eq 'eq' ) {
$result = $self->{$dbh} eq $self->parse($target) ? 1 : 0;
}
elsif ( $cmp eq 'ne' ) {
$result = $self->{$dbh} ne $self->parse($target) ? 1 : 0;
}
elsif ( $cmp eq 'lt' ) {
$result = $self->{$dbh} lt $self->parse($target) ? 1 : 0;
}
elsif ( $cmp eq 'le' ) {
$result = $self->{$dbh} le $self->parse($target) ? 1 : 0;
}
else {
die "Asked for an unknown comparizon: $cmp"
}
PTDEBUG && _d($self->{$dbh}, $cmp, $target, ':', $result);
return $result;
}
sub version_ge { sub version_ge {
my ( $self, $dbh, $target ) = @_; my ( $self, $dbh, $target ) = @_;
return $self->version_cmp($dbh, $target, 'ge');
}
sub version_gt {
my ( $self, $dbh, $target ) = @_;
return $self->version_cmp($dbh, $target, 'gt');
}
sub version_eq {
my ( $self, $dbh, $target ) = @_;
return $self->version_cmp($dbh, $target, 'eq');
}
sub version_ne {
my ( $self, $dbh, $target ) = @_;
return $self->version_cmp($dbh, $target, 'ne');
}
sub version_lt {
my ( $self, $dbh, $target ) = @_;
return $self->version_cmp($dbh, $target, 'lt');
}
sub version_le {
my ( $self, $dbh, $target ) = @_;
return $self->version_cmp($dbh, $target, 'le');
}
sub version {
my ( $self, $dbh ) = @_;
if ( !$self->{$dbh} ) { if ( !$self->{$dbh} ) {
$self->{$dbh} = $self->parse( $self->{$dbh} = $self->parse(
$dbh->selectrow_array('SELECT VERSION()')); $dbh->selectrow_array('SELECT VERSION()'));
} }
my $result = $self->{$dbh} ge $self->parse($target) ? 1 : 0; return $self->{$dbh};
PTDEBUG && _d($self->{$dbh}, 'ge', $target, ':', $result);
return $result;
} }
sub innodb_version { sub innodb_version {
@@ -7215,6 +7276,7 @@ use constant PTDEBUG => $ENV{PTDEBUG} || 0;
use Time::Local qw(timegm timelocal); use Time::Local qw(timegm timelocal);
use Digest::MD5 qw(md5_hex); use Digest::MD5 qw(md5_hex);
use B qw();
require Exporter; require Exporter;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
@@ -7232,6 +7294,7 @@ our @EXPORT_OK = qw(
any_unix_timestamp any_unix_timestamp
make_checksum make_checksum
crc32 crc32
encode_json
); );
our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/;
@@ -7439,6 +7502,96 @@ sub crc32 {
return $crc ^ 0xFFFFFFFF; return $crc ^ 0xFFFFFFFF;
} }
my $got_json = eval { require JSON };
sub encode_json {
return JSON::encode_json(@_) if $got_json;
my ( $data ) = @_;
return (object_to_json($data) || '');
}
sub object_to_json {
my ($obj) = @_;
my $type = ref($obj);
if($type eq 'HASH'){
return hash_to_json($obj);
}
elsif($type eq 'ARRAY'){
return array_to_json($obj);
}
else {
return value_to_json($obj);
}
}
sub hash_to_json {
my ($obj) = @_;
my @res;
for my $k ( sort { $a cmp $b } keys %$obj ) {
push @res, string_to_json( $k )
. ":"
. ( object_to_json( $obj->{$k} ) || value_to_json( $obj->{$k} ) );
}
return '{' . ( @res ? join( ",", @res ) : '' ) . '}';
}
sub array_to_json {
my ($obj) = @_;
my @res;
for my $v (@$obj) {
push @res, object_to_json($v) || value_to_json($v);
}
return '[' . ( @res ? join( ",", @res ) : '' ) . ']';
}
sub value_to_json {
my ($value) = @_;
return 'null' if(!defined $value);
my $b_obj = B::svref_2object(\$value); # for round trip problem
my $flags = $b_obj->FLAGS;
return $value # as is
if $flags & ( B::SVp_IOK | B::SVp_NOK ) and !( $flags & B::SVp_POK ); # SvTYPE is IV or NV?
my $type = ref($value);
if( !$type ) {
return string_to_json($value);
}
else {
return 'null';
}
}
my %esc = (
"\n" => '\n',
"\r" => '\r',
"\t" => '\t',
"\f" => '\f',
"\b" => '\b',
"\"" => '\"',
"\\" => '\\\\',
"\'" => '\\\'',
);
sub string_to_json {
my ($arg) = @_;
$arg =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g;
$arg =~ s/\//\\\//g;
$arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
utf8::upgrade($arg);
utf8::encode($arg);
return '"' . $arg . '"';
}
sub _d { sub _d {
my ($package, undef, $line) = caller 0; my ($package, undef, $line) = caller 0;
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
@@ -7556,7 +7709,7 @@ my %dsn_for;
my $q = new Quoter(); my $q = new Quoter();
sub main { sub main {
@ARGV = @_; # set global ARGV for this package local @ARGV = @_; # set global ARGV for this package
# Reset global vars else tests will have weird results. # Reset global vars else tests will have weird results.
%dsn_for = (); %dsn_for = ();
@@ -7564,6 +7717,7 @@ sub main {
# ######################################################################## # ########################################################################
# Get configuration information. # Get configuration information.
# ######################################################################## # ########################################################################
my $vp = new VersionParser();
my $o = new OptionParser(); my $o = new OptionParser();
$o->get_specs(); $o->get_specs();
$o->get_opts(); $o->get_opts();
@@ -7674,7 +7828,6 @@ sub main {
# Do the work. # Do the work.
# ######################################################################## # ########################################################################
my $tp = new TableParser( Quoter => $q ); my $tp = new TableParser( Quoter => $q );
my $vp = new VersionParser();
my $ms = new MasterSlave(VersionParser => $vp); my $ms = new MasterSlave(VersionParser => $vp);
my $du = new MySQLDump( cache => 0 ); my $du = new MySQLDump( cache => 0 );
my $rt = new Retry(); my $rt = new Retry();
@@ -7825,6 +7978,13 @@ sub lock_and_rename {
tbl => $dsns->[1]->{t}, tbl => $dsns->[1]->{t},
}; };
my $vp = VersionParser->new();
my %options = ( DSNParser => $dp, OptionParser => $o );
if ( grep { $vp->version_lt($_->{dbh}, '5.5') } $src, $dst ) {
disconnect($src, $dst);
die "--lock-and-rename requires MySQL 5.5 or later";
}
if ( $o->get('verbose') ) { if ( $o->get('verbose') ) {
print_header("# Lock and rename " . $dp->as_string($src->{dsn})); print_header("# Lock and rename " . $dp->as_string($src->{dsn}));
} }
@@ -9278,9 +9438,9 @@ In general, this tool is best suited when your tables have a primary key or
unique index. Although it can synchronize data in tables lacking a primary key unique index. Although it can synchronize data in tables lacking a primary key
or unique index, it might be best to synchronize that data by another means. or unique index, it might be best to synchronize that data by another means.
At the time of this release, there is a potential bug using At the time of this release, due to bugs in earlier versions of MySQL,
L<"--lock-and-rename"> with MySQL 5.1, a bug detecting certain differences, L<"--lock-and-rename"> is disabled in versions earlier than 5.5. Consider
a bug using ROUND() across different platforms, and a bug mixing collations. using L<"pt-online-schema-change"> instead.
The authoritative source for updated information is always the online issue The authoritative source for updated information is always the online issue
tracking system. Issues that affect this tool will be marked as such. You can tracking system. Issues that affect this tool will be marked as such. You can

View File

@@ -22,6 +22,9 @@ my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $master_dbh = $sb->get_dbh_for('master'); my $master_dbh = $sb->get_dbh_for('master');
my $slave_dbh = $sb->get_dbh_for('slave1'); my $slave_dbh = $sb->get_dbh_for('slave1');
if ( $vp->version_le($master_dbh, '5.5') ) {
plan skip_all => "This functionality doesn't work correctly on MySQLs earlier than 5.5";
}
if ( !$master_dbh ) { if ( !$master_dbh ) {
plan skip_all => 'Cannot connect to sandbox master'; plan skip_all => 'Cannot connect to sandbox master';
} }
@@ -59,5 +62,5 @@ like($output, qr/COMMENT='test1'/, '--lock-and-rename worked');
# Done. # Done.
# ############################################################################# # #############################################################################
$sb->wipe_clean($master_dbh); $sb->wipe_clean($master_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); is($sb->ok(), '', "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit; exit;