23
23
use warnings FATAL => 'all';
24
24
use English qw(-no_match_vars);
25
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
25
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
28
28
my ( $class, %args ) = @_;
225
225
$self->{DSNParser} = DSNParser->new(opts => \@dsn_opts);
228
if ( $contents =~ m/^(Percona Toolkit v.+)$/m ) {
228
if ( $contents =~ m/^=head1 VERSION\n\n^(.+)$/m ) {
229
229
$self->{version} = $1;
230
MKDEBUG && _d($self->{version});
230
PTDEBUG && _d($self->{version});
300
300
$para =~ s/$POD_link_re/$1/go;
302
302
$para =~ s/\.(?:\n.*| [A-Z].*|\Z)//s;
303
MKDEBUG && _d('Short help:', $para);
303
PTDEBUG && _d('Short help:', $para);
305
305
die "No description after option spec $option" if $para =~ m/^=item/;
339
339
foreach my $opt ( @specs ) {
340
340
if ( ref $opt ) { # It's an option spec, not a rule.
341
MKDEBUG && _d('Parsing opt spec:',
341
PTDEBUG && _d('Parsing opt spec:',
342
342
map { ($_, '=>', $opt->{$_}) } keys %$opt);
344
344
my ( $long, $short ) = $opt->{spec} =~ m/^([\w-]+)(?:\|([^!+=]*))?/;
351
351
$self->{opts}->{$long} = $opt;
353
353
if ( length $long == 1 ) {
354
MKDEBUG && _d('Long opt', $long, 'looks like short opt');
354
PTDEBUG && _d('Long opt', $long, 'looks like short opt');
355
355
$self->{short_opts}->{$long} = $long;
378
378
my ( $type ) = $opt->{spec} =~ m/=(.)/;
379
379
$opt->{type} = $type;
380
MKDEBUG && _d($long, 'type:', $type);
380
PTDEBUG && _d($long, 'type:', $type);
383
383
$opt->{spec} =~ s/=./=s/ if ( $type && $type =~ m/[HhAadzm]/ );
385
385
if ( (my ($def) = $opt->{desc} =~ m/default\b(?: ([^)]+))?/) ) {
386
386
$self->{defaults}->{$long} = defined $def ? $def : 1;
387
MKDEBUG && _d($long, 'default:', $def);
387
PTDEBUG && _d($long, 'default:', $def);
390
390
if ( $long eq 'config' ) {
394
394
if ( (my ($dis) = $opt->{desc} =~ m/(disables .*)/) ) {
395
395
$disables{$long} = $dis;
396
MKDEBUG && _d('Deferring check of disables rule for', $opt, $dis);
396
PTDEBUG && _d('Deferring check of disables rule for', $opt, $dis);
399
399
$self->{opts}->{$long} = $opt;
401
401
else { # It's an option rule, not a spec.
402
MKDEBUG && _d('Parsing rule:', $opt);
402
PTDEBUG && _d('Parsing rule:', $opt);
403
403
push @{$self->{rules}}, $opt;
404
404
my @participants = $self->_get_participants($opt);
407
407
if ( $opt =~ m/mutually exclusive|one and only one/ ) {
409
409
push @{$self->{mutex}}, \@participants;
410
MKDEBUG && _d(@participants, 'are mutually exclusive');
410
PTDEBUG && _d(@participants, 'are mutually exclusive');
412
412
if ( $opt =~ m/at least one|one and only one/ ) {
414
414
push @{$self->{atleast1}}, \@participants;
415
MKDEBUG && _d(@participants, 'require at least one');
415
PTDEBUG && _d(@participants, 'require at least one');
417
417
if ( $opt =~ m/default to/ ) {
419
419
$self->{defaults_to}->{$participants[0]} = $participants[1];
420
MKDEBUG && _d($participants[0], 'defaults to', $participants[1]);
420
PTDEBUG && _d($participants[0], 'defaults to', $participants[1]);
422
422
if ( $opt =~ m/restricted to option groups/ ) {
431
431
if( $opt =~ m/accepts additional command-line arguments/ ) {
433
433
$self->{strict} = 0;
434
MKDEBUG && _d("Strict mode disabled by rule");
434
PTDEBUG && _d("Strict mode disabled by rule");
437
437
die "Unrecognized option rule: $opt" unless $rule_ok;
441
441
foreach my $long ( keys %disables ) {
442
442
my @participants = $self->_get_participants($disables{$long});
443
443
$self->{disables}->{$long} = \@participants;
444
MKDEBUG && _d('Option', $long, 'disables', @participants);
444
PTDEBUG && _d('Option', $long, 'disables', @participants);
455
455
unless exists $self->{opts}->{$long};
456
456
push @participants, $long;
458
MKDEBUG && _d('Participants for', $str, ':', @participants);
458
PTDEBUG && _d('Participants for', $str, ':', @participants);
459
459
return @participants;
478
478
die "Cannot set default for nonexistent option $long"
479
479
unless exists $self->{opts}->{$long};
480
480
$self->{defaults}->{$long} = $defaults{$long};
481
MKDEBUG && _d('Default val for', $long, ':', $defaults{$long});
481
PTDEBUG && _d('Default val for', $long, ':', $defaults{$long});
507
507
$opt->{value} = $val;
510
MKDEBUG && _d('Got option', $long, '=', $val);
510
PTDEBUG && _d('Got option', $long, '=', $val);
605
605
if ( exists $self->{disables}->{$long} ) {
606
606
my @disable_opts = @{$self->{disables}->{$long}};
607
607
map { $self->{opts}->{$_}->{value} = undef; } @disable_opts;
608
MKDEBUG && _d('Unset options', @disable_opts,
608
PTDEBUG && _d('Unset options', @disable_opts,
609
609
'because', $long,'disables them');
678
678
my $val = $opt->{value};
680
680
if ( $val && $opt->{type} eq 'm' ) { # type time
681
MKDEBUG && _d('Parsing option', $opt->{long}, 'as a time value');
681
PTDEBUG && _d('Parsing option', $opt->{long}, 'as a time value');
682
682
my ( $prefix, $num, $suffix ) = $val =~ m/([+-]?)(\d+)([a-z])?$/;
683
683
if ( !$suffix ) {
684
684
my ( $s ) = $opt->{desc} =~ m/\(suffix (.)\)/;
685
685
$suffix = $s || 's';
686
MKDEBUG && _d('No suffix given; using', $suffix, 'for',
686
PTDEBUG && _d('No suffix given; using', $suffix, 'for',
687
687
$opt->{long}, '(value:', $val, ')');
689
689
if ( $suffix =~ m/[smhd]/ ) {
692
692
: $suffix eq 'h' ? $num * 3600 # Hours
693
693
: $num * 86400; # Days
694
694
$opt->{value} = ($prefix || '') . $val;
695
MKDEBUG && _d('Setting option', $opt->{long}, 'to', $val);
695
PTDEBUG && _d('Setting option', $opt->{long}, 'to', $val);
698
698
$self->save_error("Invalid time suffix for --$opt->{long}");
701
701
elsif ( $val && $opt->{type} eq 'd' ) { # type DSN
702
MKDEBUG && _d('Parsing option', $opt->{long}, 'as a DSN');
702
PTDEBUG && _d('Parsing option', $opt->{long}, 'as a DSN');
704
704
my $from_key = $self->{defaults_to}->{ $opt->{long} };
705
705
if ( $from_key ) {
706
MKDEBUG && _d($opt->{long}, 'DSN copies from', $from_key, 'DSN');
706
PTDEBUG && _d($opt->{long}, 'DSN copies from', $from_key, 'DSN');
707
707
if ( $self->{opts}->{$from_key}->{parsed} ) {
708
708
$prev = $self->{opts}->{$from_key}->{value};
711
MKDEBUG && _d('Cannot parse', $opt->{long}, 'until',
711
PTDEBUG && _d('Cannot parse', $opt->{long}, 'until',
712
712
$from_key, 'parsed');
717
717
$opt->{value} = $self->{DSNParser}->parse($val, $prev, $defaults);
719
719
elsif ( $val && $opt->{type} eq 'z' ) { # type size
720
MKDEBUG && _d('Parsing option', $opt->{long}, 'as a size value');
720
PTDEBUG && _d('Parsing option', $opt->{long}, 'as a size value');
721
721
$self->_parse_size($opt, $val);
723
723
elsif ( $opt->{type} eq 'H' || (defined $val && $opt->{type} eq 'h') ) {
727
727
$opt->{value} = [ split(/(?<!\\),\s*/, ($val || '')) ];
730
MKDEBUG && _d('Nothing to validate for option',
730
PTDEBUG && _d('Nothing to validate for option',
731
731
$opt->{long}, 'type', $opt->{type}, 'value', $val);
801
801
$file ||= $self->{file} || __FILE__;
803
803
if ( !$self->{description} || !$self->{usage} ) {
804
MKDEBUG && _d("Getting description and usage from SYNOPSIS in", $file);
804
PTDEBUG && _d("Getting description and usage from SYNOPSIS in", $file);
805
805
my %synop = $self->_parse_synopsis($file);
806
806
$self->{description} ||= $synop{description};
807
807
$self->{usage} ||= $synop{usage};
808
MKDEBUG && _d("Description:", $self->{description},
808
PTDEBUG && _d("Description:", $self->{description},
809
809
"\nUsage:", $self->{usage});
1020
1020
my ( $self, $opt, $val ) = @_;
1022
1022
if ( lc($val || '') eq 'null' ) {
1023
MKDEBUG && _d('NULL size for', $opt->{long});
1023
PTDEBUG && _d('NULL size for', $opt->{long});
1024
1024
$opt->{value} = 'null';
1030
1030
if ( defined $num ) {
1031
1031
if ( $factor ) {
1032
1032
$num *= $factor_for{$factor};
1033
MKDEBUG && _d('Setting option', $opt->{y},
1033
PTDEBUG && _d('Setting option', $opt->{y},
1034
1034
'to num', $num, '* factor', $factor);
1036
1036
$opt->{value} = ($pre || '') . $num;
1054
1054
sub _parse_synopsis {
1055
1055
my ( $self, $file ) = @_;
1056
1056
$file ||= $self->{file} || __FILE__;
1057
MKDEBUG && _d("Parsing SYNOPSIS in", $file);
1057
PTDEBUG && _d("Parsing SYNOPSIS in", $file);
1059
1059
local $INPUT_RECORD_SEPARATOR = ''; # read paragraphs
1060
1060
open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR";
1067
1067
push @synop, $para;
1070
MKDEBUG && _d("Raw SYNOPSIS text:", @synop);
1070
PTDEBUG && _d("Raw SYNOPSIS text:", @synop);
1071
1071
my ($usage, $desc) = @synop;
1072
1072
die "The SYNOPSIS section in $file is not formatted properly"
1073
1073
unless $usage && $desc;
1094
1094
print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
1098
1098
print '# ', $^X, ' ', $], "\n";
1099
1099
if ( my $uname = `uname -a` ) {
1100
1100
$uname =~ s/\s+/ /g;
1135
1135
my ( $self, $str ) = @_;
1136
1136
my $result = sprintf('%03d%03d%03d', $str =~ m/(\d+)/g);
1137
MKDEBUG && _d($str, 'parses to', $result);
1137
PTDEBUG && _d($str, 'parses to', $result);
1138
1138
return $result;
1145
1145
$dbh->selectrow_array('SELECT VERSION()'));
1147
1147
my $result = $self->{$dbh} ge $self->parse($target) ? 1 : 0;
1148
MKDEBUG && _d($self->{$dbh}, 'ge', $target, ':', $result);
1148
PTDEBUG && _d($self->{$dbh}, 'ge', $target, ':', $result);
1149
1149
return $result;
1164
1164
@{ $dbh->selectall_arrayref("SHOW ENGINES", {Slice=>{}}) };
1165
1165
if ( $innodb ) {
1166
MKDEBUG && _d("InnoDB support:", $innodb->{support});
1166
PTDEBUG && _d("InnoDB support:", $innodb->{support});
1167
1167
if ( $innodb->{support} =~ m/YES|DEFAULT/i ) {
1168
1168
my $vars = $dbh->selectrow_hashref(
1169
1169
"SHOW VARIABLES LIKE 'innodb_version'");
1208
1208
use warnings FATAL => 'all';
1209
1209
use English qw(-no_match_vars);
1210
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
1210
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1212
1212
use Data::Dumper;
1213
1213
$Data::Dumper::Indent = 0;
1230
1230
if ( !$opt->{key} || !$opt->{desc} ) {
1231
1231
die "Invalid DSN option: ", Dumper($opt);
1233
MKDEBUG && _d('DSN option:',
1233
PTDEBUG && _d('DSN option:',
1235
1235
map { "$_=" . (defined $opt->{$_} ? ($opt->{$_} || '') : 'undef') }
1249
1249
my ( $self, $prop, $value ) = @_;
1250
1250
if ( @_ > 2 ) {
1251
MKDEBUG && _d('Setting', $prop, 'property');
1251
PTDEBUG && _d('Setting', $prop, 'property');
1252
1252
$self->{$prop} = $value;
1254
1254
return $self->{$prop};
1258
1258
my ( $self, $dsn, $prev, $defaults ) = @_;
1260
MKDEBUG && _d('No DSN to parse');
1260
PTDEBUG && _d('No DSN to parse');
1263
MKDEBUG && _d('Parsing', $dsn);
1263
PTDEBUG && _d('Parsing', $dsn);
1265
1265
$defaults ||= {};
1266
1266
my %given_props;
1272
1272
$given_props{$prop_key} = $prop_val;
1275
MKDEBUG && _d('Interpreting', $dsn_part, 'as h=', $dsn_part);
1275
PTDEBUG && _d('Interpreting', $dsn_part, 'as h=', $dsn_part);
1276
1276
$given_props{h} = $dsn_part;
1280
1280
foreach my $key ( keys %$opts ) {
1281
MKDEBUG && _d('Finding value for', $key);
1281
PTDEBUG && _d('Finding value for', $key);
1282
1282
$final_props{$key} = $given_props{$key};
1283
1283
if ( !defined $final_props{$key}
1284
1284
&& defined $prev->{$key} && $opts->{$key}->{copy} )
1286
1286
$final_props{$key} = $prev->{$key};
1287
MKDEBUG && _d('Copying value for', $key, 'from previous DSN');
1287
PTDEBUG && _d('Copying value for', $key, 'from previous DSN');
1289
1289
if ( !defined $final_props{$key} ) {
1290
1290
$final_props{$key} = $defaults->{$key};
1291
MKDEBUG && _d('Copying value for', $key, 'from defaults');
1291
PTDEBUG && _d('Copying value for', $key, 'from defaults');
1319
1319
grep { $o->has($_) && $o->get($_) }
1320
1320
keys %{$self->{opts}}
1322
MKDEBUG && _d('DSN string made from options:', $dsn_string);
1322
PTDEBUG && _d('DSN string made from options:', $dsn_string);
1323
1323
return $self->parse($dsn_string);
1416
1416
while ( !$dbh && $tries-- ) {
1417
MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass,
1417
PTDEBUG && _d($cxn_string, ' ', $user, ' ', $pass,
1418
1418
join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ));
1426
1426
$sql = 'SELECT @@SQL_MODE';
1427
MKDEBUG && _d($dbh, $sql);
1427
PTDEBUG && _d($dbh, $sql);
1428
1428
my ($sql_mode) = $dbh->selectrow_array($sql);
1430
1430
$sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1431
1431
. '/*!40101, @@SQL_MODE=\'NO_AUTO_VALUE_ON_ZERO'
1432
1432
. ($sql_mode ? ",$sql_mode" : '')
1434
MKDEBUG && _d($dbh, $sql);
1434
PTDEBUG && _d($dbh, $sql);
1435
1435
$dbh->do($sql);
1437
1437
if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
1438
1438
$sql = "/*!40101 SET NAMES $charset*/";
1439
MKDEBUG && _d($dbh, ':', $sql);
1439
PTDEBUG && _d($dbh, ':', $sql);
1440
1440
$dbh->do($sql);
1441
MKDEBUG && _d('Enabling charset for STDOUT');
1441
PTDEBUG && _d('Enabling charset for STDOUT');
1442
1442
if ( $charset eq 'utf8' ) {
1443
1443
binmode(STDOUT, ':utf8')
1444
1444
or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
1451
1451
if ( $self->prop('set-vars') ) {
1452
1452
$sql = "SET " . $self->prop('set-vars');
1453
MKDEBUG && _d($dbh, ':', $sql);
1453
PTDEBUG && _d($dbh, ':', $sql);
1454
1454
$dbh->do($sql);
1458
1458
if ( !$dbh && $EVAL_ERROR ) {
1459
MKDEBUG && _d($EVAL_ERROR);
1459
PTDEBUG && _d($EVAL_ERROR);
1460
1460
if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) {
1461
MKDEBUG && _d('Going to try again without utf8 support');
1461
PTDEBUG && _d('Going to try again without utf8 support');
1462
1462
delete $defaults->{mysql_enable_utf8};
1464
1464
elsif ( $EVAL_ERROR =~ m/locate DBD\/mysql/i ) {
1479
MKDEBUG && _d('DBH info: ',
1479
PTDEBUG && _d('DBH info: ',
1481
1481
Dumper($dbh->selectrow_hashref(
1482
1482
'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')),
1564
1564
use warnings FATAL => 'all';
1565
1565
use English qw(-no_match_vars);
1566
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
1566
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1569
1569
my ( $class, %args ) = @_;
1574
1574
return bless $self, $class;
1578
my ($self, %args) = @_;
1579
my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
1580
foreach my $arg ( @required_args ) {
1581
die "I need a $arg argument" unless $args{$arg};
1583
my ($make_cxn, $o, $dp) = @args{@required_args};
1586
my $method = $o->get('recursion-method');
1587
PTDEBUG && _d('Slave recursion method:', $method);
1588
if ( !$method || $method =~ m/processlist|hosts/i ) {
1589
my @required_args = qw(dbh dsn);
1590
foreach my $arg ( @required_args ) {
1591
die "I need a $arg argument" unless $args{$arg};
1593
my ($dbh, $dsn) = @args{@required_args};
1594
$self->recurse_to_slaves(
1598
recurse => $o->get('recurse'),
1599
method => $o->get('recursion-method'),
1601
my ( $dsn, $dbh, $level, $parent ) = @_;
1602
return unless $level;
1603
PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
1604
push @$slaves, $make_cxn->(dsn => $dsn, dbh => $dbh);
1610
elsif ( $method =~ m/^dsn=/i ) {
1611
my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
1612
$slaves = $self->get_cxn_from_dsn_table(
1614
dsn_table_dsn => $dsn_table_dsn,
1618
die "Invalid --recursion-method: $method. Valid values are: "
1619
. "dsn=DSN, hosts, or processlist.\n";
1577
1625
sub recurse_to_slaves {
1578
1626
my ( $self, $args, $level ) = @_;
1585
1633
$dbh = $args->{dbh} || $dp->get_dbh(
1586
1634
$dp->get_cxn_params($dsn), { AutoCommit => 1 });
1587
MKDEBUG && _d('Connected to', $dp->as_string($dsn));
1635
PTDEBUG && _d('Connected to', $dp->as_string($dsn));
1589
1637
if ( $EVAL_ERROR ) {
1590
1638
print STDERR "Cannot connect to ", $dp->as_string($dsn), "\n"
1595
1643
my $sql = 'SELECT @@SERVER_ID';
1596
MKDEBUG && _d($sql);
1644
PTDEBUG && _d($sql);
1597
1645
my ($id) = $dbh->selectrow_array($sql);
1598
MKDEBUG && _d('Working on server ID', $id);
1646
PTDEBUG && _d('Working on server ID', $id);
1599
1647
my $master_thinks_i_am = $dsn->{server_id};
1600
1648
if ( !defined $id
1601
1649
|| ( defined $master_thinks_i_am && $master_thinks_i_am != $id )
1602
1650
|| $args->{server_ids_seen}->{$id}++
1604
MKDEBUG && _d('Server ID seen, or not what master said');
1652
PTDEBUG && _d('Server ID seen, or not what master said');
1605
1653
if ( $args->{skip_callback} ) {
1606
1654
$args->{skip_callback}->($dsn, $dbh, $level, $args->{parent});
1617
1665
$self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
1619
1667
foreach my $slave ( @slaves ) {
1620
MKDEBUG && _d('Recursing from',
1668
PTDEBUG && _d('Recursing from',
1621
1669
$dp->as_string($dsn), 'to', $dp->as_string($slave));
1622
1670
$self->recurse_to_slaves(
1623
1671
{ %$args, dsn => $slave, dbh => undef, parent => $dsn }, $level + 1 );
1637
1685
if ( ($dsn->{P} || 3306) != 3306 ) {
1638
MKDEBUG && _d('Port number is non-standard; using only hosts method');
1686
PTDEBUG && _d('Port number is non-standard; using only hosts method');
1639
1687
@methods = qw(hosts);
1642
MKDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
1690
PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
1643
1691
'using methods', @methods);
1647
1695
foreach my $method ( @methods ) {
1648
1696
my $find_slaves = "_find_slaves_by_$method";
1649
MKDEBUG && _d('Finding slaves with', $find_slaves);
1697
PTDEBUG && _d('Finding slaves with', $find_slaves);
1650
1698
@slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
1651
1699
last METHOD if @slaves;
1654
MKDEBUG && _d('Found', scalar(@slaves), 'slaves');
1702
PTDEBUG && _d('Found', scalar(@slaves), 'slaves');
1655
1703
return @slaves;
1682
1730
my $sql = 'SHOW SLAVE HOSTS';
1683
MKDEBUG && _d($dbh, $sql);
1731
PTDEBUG && _d($dbh, $sql);
1684
1732
@slaves = @{$dbh->selectall_arrayref($sql, { Slice => {} })};
1686
1734
if ( @slaves ) {
1687
MKDEBUG && _d('Found some SHOW SLAVE HOSTS info');
1735
PTDEBUG && _d('Found some SHOW SLAVE HOSTS info');
1688
1736
@slaves = map {
1690
1738
@hash{ map { lc $_ } keys %$_ } = values %$_;
1724
1772
if ( $EVAL_ERROR ) {
1726
1774
if ( $EVAL_ERROR =~ m/no such grant defined for user/ ) {
1727
MKDEBUG && _d('Retrying SHOW GRANTS without host; error:',
1775
PTDEBUG && _d('Retrying SHOW GRANTS without host; error:',
1729
1777
($user) = split('@', $user);
1730
1778
$sql = $show . $user;
1731
MKDEBUG && _d($sql);
1779
PTDEBUG && _d($sql);
1734
1782
m/ALL PRIVILEGES.*?\*\.\*|PROCESS/
1745
1793
$sql = 'SHOW PROCESSLIST';
1746
MKDEBUG && _d($dbh, $sql);
1794
PTDEBUG && _d($dbh, $sql);
1747
1795
grep { $_->{command} =~ m/Binlog Dump/i }
1748
1796
map { # Lowercase the column names
1803
1851
if ( !$self->{not_a_slave}->{$dbh} ) {
1804
1852
my $sth = $self->{sths}->{$dbh}->{SLAVE_STATUS}
1805
1853
||= $dbh->prepare('SHOW SLAVE STATUS');
1806
MKDEBUG && _d($dbh, 'SHOW SLAVE STATUS');
1854
PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS');
1807
1855
$sth->execute();
1808
1856
my ($ss) = @{$sth->fetchall_arrayref({})};
1821
1869
my ( $self, $dbh ) = @_;
1823
1871
if ( $self->{not_a_master}->{$dbh} ) {
1824
MKDEBUG && _d('Server on dbh', $dbh, 'is not a master');
1872
PTDEBUG && _d('Server on dbh', $dbh, 'is not a master');
1828
1876
my $sth = $self->{sths}->{$dbh}->{MASTER_STATUS}
1829
1877
||= $dbh->prepare('SHOW MASTER STATUS');
1830
MKDEBUG && _d($dbh, 'SHOW MASTER STATUS');
1878
PTDEBUG && _d($dbh, 'SHOW MASTER STATUS');
1831
1879
$sth->execute();
1832
1880
my ($ms) = @{$sth->fetchall_arrayref({})};
1834
1882
$ms ? map { "$_=" . (defined $ms->{$_} ? $ms->{$_} : '') } keys %$ms
1837
1885
if ( !$ms || scalar keys %$ms < 2 ) {
1838
MKDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master');
1886
PTDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master');
1839
1887
$self->{not_a_master}->{$dbh}++;
1856
1904
if ( $master_status ) {
1857
1905
my $sql = "SELECT MASTER_POS_WAIT('$master_status->{file}', "
1858
1906
. "$master_status->{position}, $timeout)";
1859
MKDEBUG && _d($slave_dbh, $sql);
1907
PTDEBUG && _d($slave_dbh, $sql);
1860
1908
my $start = time;
1861
1909
($result) = $slave_dbh->selectrow_array($sql);
1863
1911
$waited = time - $start;
1865
MKDEBUG && _d('Result of waiting:', $result);
1866
MKDEBUG && _d("Waited", $waited, "seconds");
1913
PTDEBUG && _d('Result of waiting:', $result);
1914
PTDEBUG && _d("Waited", $waited, "seconds");
1869
MKDEBUG && _d('Not waiting: this server is not a master');
1917
PTDEBUG && _d('Not waiting: this server is not a master');
1879
1927
my ( $self, $dbh ) = @_;
1880
1928
my $sth = $self->{sths}->{$dbh}->{STOP_SLAVE}
1881
1929
||= $dbh->prepare('STOP SLAVE');
1882
MKDEBUG && _d($dbh, $sth->{Statement});
1930
PTDEBUG && _d($dbh, $sth->{Statement});
1883
1931
$sth->execute();
1889
1937
my $sql = "START SLAVE UNTIL MASTER_LOG_FILE='$pos->{file}', "
1890
1938
. "MASTER_LOG_POS=$pos->{position}";
1891
MKDEBUG && _d($dbh, $sql);
1939
PTDEBUG && _d($dbh, $sql);
1892
1940
$dbh->do($sql);
1895
1943
my $sth = $self->{sths}->{$dbh}->{START_SLAVE}
1896
1944
||= $dbh->prepare('START SLAVE');
1897
MKDEBUG && _d($dbh, $sth->{Statement});
1945
PTDEBUG && _d($dbh, $sth->{Statement});
1898
1946
$sth->execute();
1907
1955
my $slave_pos = $self->repl_posn($slave_status);
1908
1956
my $master_status = $self->get_master_status($master);
1909
1957
my $master_pos = $self->repl_posn($master_status);
1910
MKDEBUG && _d('Master position:', $self->pos_to_string($master_pos),
1958
PTDEBUG && _d('Master position:', $self->pos_to_string($master_pos),
1911
1959
'Slave position:', $self->pos_to_string($slave_pos));
1914
1962
if ( $self->pos_cmp($slave_pos, $master_pos) < 0 ) {
1915
MKDEBUG && _d('Waiting for slave to catch up to master');
1963
PTDEBUG && _d('Waiting for slave to catch up to master');
1916
1964
$self->start_slave($slave, $master_pos);
1918
1966
$result = $self->wait_for_master(
1924
1972
if ( !defined $result->{result} ) {
1925
1973
$slave_status = $self->get_slave_status($slave);
1926
1974
if ( !$self->slave_is_running($slave_status) ) {
1927
MKDEBUG && _d('Master position:',
1975
PTDEBUG && _d('Master position:',
1928
1976
$self->pos_to_string($master_pos),
1929
1977
'Slave position:', $self->pos_to_string($slave_pos));
1930
1978
$slave_pos = $self->repl_posn($slave_status);
1932
1980
die "MASTER_POS_WAIT() returned NULL but slave has not "
1933
1981
. "caught up to master";
1935
MKDEBUG && _d('Slave is caught up to master and stopped');
1983
PTDEBUG && _d('Slave is caught up to master and stopped');
1938
1986
die "Slave has not caught up to master and it is still running";
1983
2031
sub has_slave_updates {
1984
2032
my ( $self, $dbh ) = @_;
1985
2033
my $sql = q{SHOW VARIABLES LIKE 'log_slave_updates'};
1986
MKDEBUG && _d($dbh, $sql);
2034
PTDEBUG && _d($dbh, $sql);
1987
2035
my ($name, $value) = $dbh->selectrow_array($sql);
1988
2036
return $value && $value =~ m/^(1|ON)$/;
2046
2094
if ( !$match ) {
2047
2095
if ( ($query->{User} || $query->{user} || '') eq "system user" ) {
2048
MKDEBUG && _d("Slave replication thread");
2096
PTDEBUG && _d("Slave replication thread");
2049
2097
if ( $type ne 'all' ) {
2050
2098
my $state = $query->{State} || $query->{state} || '';
2052
2100
if ( $state =~ m/^init|end$/ ) {
2053
MKDEBUG && _d("Special state:", $state);
2101
PTDEBUG && _d("Special state:", $state);
2083
2131
if ( $self->{replication_thread}->{$id} ) {
2084
MKDEBUG && _d("Thread ID is a known replication thread ID");
2132
PTDEBUG && _d("Thread ID is a known replication thread ID");
2091
MKDEBUG && _d('Matches', $type, 'replication thread:',
2139
PTDEBUG && _d('Matches', $type, 'replication thread:',
2092
2140
($match ? 'yes' : 'no'), '; match:', $match);
2131
2179
my $sql = "SHOW VARIABLES LIKE 'slave_skip_errors'";
2132
MKDEBUG && _d($dbh, $sql);
2180
PTDEBUG && _d($dbh, $sql);
2133
2181
my $row = $dbh->selectrow_arrayref($sql);
2134
2182
$filters{slave_skip_errors} = $row->[1] if $row->[1] && $row->[1] ne 'OFF';
2201
sub get_cxn_from_dsn_table {
2202
my ($self, %args) = @_;
2203
my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
2204
foreach my $arg ( @required_args ) {
2205
die "I need a $arg argument" unless $args{$arg};
2207
my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
2208
PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
2210
my $dsn = $dp->parse($dsn_table_dsn);
2212
if ( $dsn->{D} && $dsn->{t} ) {
2213
$dsn_table = $q->quote($dsn->{D}, $dsn->{t});
2215
elsif ( $dsn->{t} && $dsn->{t} =~ m/\./ ) {
2216
$dsn_table = $q->quote($q->split_unquote($dsn->{t}));
2219
die "DSN table DSN does not specify a database (D) "
2220
. "or a database-qualified table (t)";
2223
my $dsn_tbl_cxn = $make_cxn->(dsn => $dsn);
2224
my $dbh = $dsn_tbl_cxn->connect();
2225
my $sql = "SELECT dsn FROM $dsn_table ORDER BY id";
2226
PTDEBUG && _d($sql);
2227
my $dsn_strings = $dbh->selectcol_arrayref($sql);
2229
if ( $dsn_strings ) {
2230
foreach my $dsn_string ( @$dsn_strings ) {
2231
PTDEBUG && _d('DSN from DSN table:', $dsn_string);
2232
push @cxn, $make_cxn->(dsn_string => $dsn_string);
2154
2239
my ($package, undef, $line) = caller 0;
2155
2240
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
2197
2282
check_PID_file(undef, $self->{PID_file});
2199
MKDEBUG && _d('Daemonized child will log to', $self->{log_file});
2284
PTDEBUG && _d('Daemonized child will log to', $self->{log_file});
2200
2285
return bless $self, $class;
2203
2288
sub daemonize {
2204
2289
my ( $self ) = @_;
2206
MKDEBUG && _d('About to fork and daemonize');
2291
PTDEBUG && _d('About to fork and daemonize');
2207
2292
defined (my $pid = fork()) or die "Cannot fork: $OS_ERROR";
2209
MKDEBUG && _d('I am the parent and now I die');
2294
PTDEBUG && _d('I am the parent and now I die');
2251
MKDEBUG && _d('I am the child and now I live daemonized');
2336
PTDEBUG && _d('I am the child and now I live daemonized');
2255
2340
sub check_PID_file {
2256
2341
my ( $self, $file ) = @_;
2257
2342
my $PID_file = $self ? $self->{PID_file} : $file;
2258
MKDEBUG && _d('Checking PID file', $PID_file);
2343
PTDEBUG && _d('Checking PID file', $PID_file);
2259
2344
if ( $PID_file && -f $PID_file ) {
2261
2346
eval { chomp($pid = `cat $PID_file`); };
2262
2347
die "Cannot cat $PID_file: $OS_ERROR" if $EVAL_ERROR;
2263
MKDEBUG && _d('PID file exists; it contains PID', $pid);
2348
PTDEBUG && _d('PID file exists; it contains PID', $pid);
2265
2350
my $pid_is_alive = kill 0, $pid;
2266
2351
if ( $pid_is_alive ) {
2320
2405
if ( $self->{PID_file} && -f $self->{PID_file} ) {
2321
2406
unlink $self->{PID_file}
2322
2407
or warn "Cannot remove PID file $self->{PID_file}: $OS_ERROR";
2323
MKDEBUG && _d('Removed PID file');
2408
PTDEBUG && _d('Removed PID file');
2326
MKDEBUG && _d('No PID to remove');
2411
PTDEBUG && _d('No PID to remove');
2411
2496
# ########################################################################
2412
2497
my $sentinel = $o->get('sentinel');
2413
2498
if ( $o->get('stop') ) {
2414
MKDEBUG && _d('Creating sentinel file', $sentinel);
2499
PTDEBUG && _d('Creating sentinel file', $sentinel);
2415
2500
my $file = IO::File->new($sentinel, ">>")
2416
2501
or die "Cannot open $sentinel: $OS_ERROR\n";
2417
2502
print $file "Remove this file to permit pt-slave-restart to run\n"
2422
2507
unless $o->get('quiet');
2423
2508
# Exit unlesss --monitor is given.
2424
2509
if ( !$o->got('monitor') ) {
2425
MKDEBUG && _d('Nothing more to do, quitting');
2510
PTDEBUG && _d('Nothing more to do, quitting');
2429
2514
# Wait for all other running instances to quit, assuming they have the
2430
2515
# same --interval as this invocation. Then remove the file and
2432
MKDEBUG && _d('Waiting for other instances to quit');
2517
PTDEBUG && _d('Waiting for other instances to quit');
2433
2518
sleep $o->get('max-sleep');
2434
MKDEBUG && _d('Unlinking', $sentinel);
2519
PTDEBUG && _d('Unlinking', $sentinel);
2435
2520
unlink $sentinel
2436
2521
or die "Cannot unlink $sentinel: $OS_ERROR";
2456
2541
if ( $o->get('daemonize') ) {
2457
2542
$daemon = new Daemon(o=>$o);
2458
2543
$daemon->daemonize();
2459
MKDEBUG && _d('I am a daemon now');
2544
PTDEBUG && _d('I am a daemon now');
2461
2546
elsif ( $o->get('pid') ) {
2462
2547
# We're not daemoninzing, it just handles PID stuff.
2521
2606
$children{$dp->as_string($host->{dsn})} = $pid if $must_fork;
2524
MKDEBUG && _d('Child PIDs:', values %children);
2609
PTDEBUG && _d('Child PIDs:', values %children);
2525
2610
# Wait for the children to exit.
2526
2611
foreach my $host ( keys %children ) {
2527
MKDEBUG && _d('Waiting to reap', $host);
2612
PTDEBUG && _d('Waiting to reap', $host);
2528
2613
my $pid = waitpid($children{$host}, 0);
2529
2614
$exit_status ||= $CHILD_ERROR >> 8;
2542
2627
sub watch_server {
2543
2628
my ( $dsn, $dbh, $was_forked, $ms ) = @_;
2545
MKDEBUG && _d('Watching server', $dp->as_string($dsn),
2630
PTDEBUG && _d('Watching server', $dp->as_string($dsn),
2546
2631
'forked:', $was_forked);
2548
2633
my $start_sql = $vp->version_ge($dbh, '4.0.5')
2591
2676
my ( $stat, $dbh ) = @_;
2592
MKDEBUG && _d('Found non-relay-log error');
2677
PTDEBUG && _d('Found non-relay-log error');
2593
2678
$set_skip->execute();
2595
2680
repair_table => sub {
2596
2681
my ( $stat, $dbh ) = @_;
2597
MKDEBUG && _d('Found corrupt table');
2682
PTDEBUG && _d('Found corrupt table');
2598
2683
# [ qr/Incorrect key file for table './foo/bar.MYI'
2599
2684
my ( $db, $tbl ) = $stat->{last_error} =~ m!([^/]+)/(.*?)\.MYI!;
2600
2685
if ( $db && $tbl ) {
2601
2686
my $sql = "REPAIR TABLE " . $q->quote($db, $tbl);
2602
MKDEBUG && _d($sql);
2687
PTDEBUG && _d($sql);
2603
2688
$dbh->do($sql);
2626
MKDEBUG && _d('Last/current relay log file:',
2711
PTDEBUG && _d('Last/current relay log file:',
2627
2712
$last_log, $stat->{relay_log_file});
2628
MKDEBUG && _d('Last/current relay log pos:',
2713
PTDEBUG && _d('Last/current relay log pos:',
2629
2714
$last_pos, $stat->{relay_log_pos});
2630
2715
if ( !$last_log
2631
2716
|| $last_log ne $stat->{relay_log_file} # Avoid infinite loops
3343
3428
=head1 COPYRIGHT, LICENSE, AND WARRANTY
3345
This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc.
3430
This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc.
3346
3431
Feedback and improvements are welcome.
3348
3433
THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED