Merge lp:~percona-toolkit-dev/percona-toolkit/fix-953141-recursion-method-array into lp:percona-toolkit/2.1

Proposed by Brian Fraser
Status: Merged
Merged at revision: 345
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/fix-953141-recursion-method-array
Merge into: lp:percona-toolkit/2.1
Diff against target: 2941 lines (+985/-554)
12 files modified
bin/pt-archiver (+76/-41)
bin/pt-heartbeat (+99/-54)
bin/pt-kill (+79/-44)
bin/pt-online-schema-change (+100/-54)
bin/pt-query-digest (+76/-41)
bin/pt-slave-find (+97/-56)
bin/pt-slave-restart (+97/-56)
bin/pt-table-checksum (+102/-57)
bin/pt-table-sync (+94/-56)
lib/MasterSlave.pm (+80/-44)
t/lib/MasterSlave.t (+71/-49)
t/pt-table-checksum/option_sanity.t (+14/-2)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/fix-953141-recursion-method-array
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+117792@code.launchpad.net

This proposal supersedes a proposal from 2012-07-31.

To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal

The --recursion-method values are checked in MasterSlave::get_slaves(). I think we should fail earlier by implementing a class method like MasterSlalve::check_recursion_method() that we can call when checking other options (like Progress:validate_spec()). So something like:

eval {
   MasterSlave::check_recursion_method($o->get('recursion-method'));
};
if ( $EVAL_ERROR ) {
   $o->save_error("Invalid --recursion method: $EVAL_ERROR");
}

That method can also sanity check against things like --recursion-method none,hosts.

review: Needs Resubmitting
Revision history for this message
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal

61 + die("get_slaves() shoudn't get here; An unexpected "
62 + . "recursion method was specified: ", $methods);

Since $methods is an arrayref, it will print as ARRAY(0x100804ed0). That die can just be die "Unexpected recursion methods: @$methods";

review: Needs Fixing
Revision history for this message
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal

Also: $o->save_error("Invalid --recursion method: $EVAL_ERROR") Should be "--recursion-method" (missing hyphen).

Revision history for this message
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal

Also:

"""
=item --recursion-method

type: array; default: hosts,processlist

...

The processlist method is the default, because SHOW SLAVE HOSTS is not
reliable. ...
"""

So I made the wrong call: I told you (Brian) to default to "hosts,processlist", but I was wrong. Baron wrote that help info, so if he says processlist is default because hosts is not reliable, then he's correct.

But see original MasterSlave::find_slave_hosts():

   my @methods = qw(processlist hosts);
   if ( $method ) {
      @methods = grep { $_ ne $method } @methods;
      unshift @methods, $method;
   }
   else {
      if ( ($dsn->{P} || 3306) != 3306 ) {
         PTDEBUG && _d('Port number is non-standard; using only hosts method');
         @methods = qw(hosts);
      }
   }

So proc is default unless non-standard port, then only hosts is used. But we also need to respect the user, so altogether:

- If $o->got('recursion-method') (i.e. user gave the option explicitly) then use that no matter what (presuming it's valid, e.g. hosts,none is not valid)
- Else, default=processlist,hosts
-- If standard port (3306), use default
-- Else use only hosts

review: Needs Resubmitting
Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

Running t/lib/MasterSlave.t:

ok 5 - No privs needed for --recursion-method=none (bug 987694)
Can't use string ("none") as an ARRAY ref while "strict refs" in use at /Users/daniel/p/fix-953141-recursion-method-array/lib/MasterSlave.pm line 138.

That line is in recurse_to_slaves():

   if ( $args->{method} && lc($args->{method}->[0] || '') eq 'none' ) {
      # https://bugs.launchpad.net/percona-toolkit/+bug/987694
      PTDEBUG && _d('Not recursing to slaves');
      return;
   }

review: Needs Fixing
334. By Brian Fraser

t/lib/MasterSlave.t: A call to ->recurse_to_slaves() still used a string instead of an arrayref

335. By Daniel Nichter

Rewrite how MasterSlave handles --recursion-methods.

Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

I decided to take this a new direction. The code wasn't clean or logical imho. It's still not, but that's because we probably need to overhaul the whole module. In any case, the major sticking point for me was the ambiguity of:

203 - method => $o->get('recursion-method'),
204 + method => $o->got('recursion-method')
205 + ? $o->get('recursion-method')
206 + : [],

That effectively threw away the "default: ..." from the option's spec and created a magical, hard-coded default in _resolve_recursion_methods() which 1) defeats the original purpose of having a hard-coded default and 2) makes it unclear how "default: ..." and a user-explicit option (i.e. --recursion-method given on the command line) interact. So I rewrote stuff in r335.

Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

Correction: "1) defeats the original purpose of _not_ having a hard-coded default"

336. By Daniel Nichter

Create MasterSlave with required args.

337. By Daniel Nichter

Pass a placeholder for Quoter since it's not needed.

Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/pt-archiver'
2--- bin/pt-archiver 2012-07-31 21:37:02 +0000
3+++ bin/pt-archiver 2012-08-02 17:46:19 +0000
4@@ -3078,8 +3078,31 @@
5 use English qw(-no_match_vars);
6 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
7
8+sub check_recursion_method {
9+ my ($methods) = @_;
10+
11+ if ( @$methods != 1 ) {
12+ if ( grep({ !m/processlist|hosts/i } @$methods)
13+ && $methods->[0] !~ /^dsn=/i )
14+ {
15+ die "Invalid combination of recursion methods: "
16+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
17+ . "Only hosts and processlist may be combined.\n"
18+ }
19+ }
20+ else {
21+ my ($method) = @$methods;
22+ die "Invalid recursion method: " . ( $method || 'undef' )
23+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
24+ }
25+}
26+
27 sub new {
28 my ( $class, %args ) = @_;
29+ my @required_args = qw(OptionParser DSNParser Quoter);
30+ foreach my $arg ( @required_args ) {
31+ die "I need a $arg argument" unless $args{$arg};
32+ }
33 my $self = {
34 %args,
35 replication_thread => {},
36@@ -3089,28 +3112,27 @@
37
38 sub get_slaves {
39 my ($self, %args) = @_;
40- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
41+ my @required_args = qw(make_cxn);
42 foreach my $arg ( @required_args ) {
43 die "I need a $arg argument" unless $args{$arg};
44 }
45- my ($make_cxn, $o, $dp) = @args{@required_args};
46-
47- my $slaves = [];
48- my $method = $o->get('recursion-method');
49- PTDEBUG && _d('Slave recursion method:', $method);
50- if ( !$method || $method =~ m/processlist|hosts/i ) {
51+ my ($make_cxn) = @args{@required_args};
52+
53+ my $slaves = [];
54+ my $dp = $self->{DSNParser};
55+ my $methods = $self->_resolve_recursion_methods($args{dsn});
56+
57+ if ( grep { m/processlist|hosts/i } @$methods ) {
58 my @required_args = qw(dbh dsn);
59 foreach my $arg ( @required_args ) {
60 die "I need a $arg argument" unless $args{$arg};
61 }
62 my ($dbh, $dsn) = @args{@required_args};
63+
64 $self->recurse_to_slaves(
65- { dbh => $dbh,
66- dsn => $dsn,
67- dsn_parser => $dp,
68- recurse => $o->get('recurse'),
69- method => $o->get('recursion-method'),
70- callback => sub {
71+ { dbh => $dbh,
72+ dsn => $dsn,
73+ callback => sub {
74 my ( $dsn, $dbh, $level, $parent ) = @_;
75 return unless $level;
76 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
77@@ -3120,31 +3142,48 @@
78 }
79 );
80 }
81- elsif ( $method =~ m/^dsn=/i ) {
82- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
83+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
84+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
85 $slaves = $self->get_cxn_from_dsn_table(
86 %args,
87 dsn_table_dsn => $dsn_table_dsn,
88 );
89 }
90- elsif ( $method =~ m/none/i ) {
91+ elsif ( $methods->[0] =~ m/none/i ) {
92 PTDEBUG && _d('Not getting to slaves');
93 }
94 else {
95- die "Invalid --recursion-method: $method. Valid values are: "
96- . "dsn=DSN, hosts, or processlist.\n";
97+ die "Unexpected recursion methods: @$methods";
98 }
99-
100+
101 return $slaves;
102 }
103
104+sub _resolve_recursion_methods {
105+ my ($self, $dsn) = @_;
106+ my $o = $self->{OptionParser};
107+ if ( $o->got('recursion-method') ) {
108+ return $o->get('recursion-method');
109+ }
110+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
111+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
112+ return [qw(hosts)];
113+ }
114+ else {
115+ return $o->get('recursion-method');
116+ }
117+}
118+
119 sub recurse_to_slaves {
120 my ( $self, $args, $level ) = @_;
121 $level ||= 0;
122- my $dp = $args->{dsn_parser};
123- my $dsn = $args->{dsn};
124+ my $dp = $self->{DSNParser};
125+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
126+ my $dsn = $args->{dsn};
127
128- if ( lc($args->{method} || '') eq 'none' ) {
129+ my $methods = $self->_resolve_recursion_methods($dsn);
130+ PTDEBUG && _d('Recursion methods:', @$methods);
131+ if ( lc($methods->[0]) eq 'none' ) {
132 PTDEBUG && _d('Not recursing to slaves');
133 return;
134 }
135@@ -3179,11 +3218,11 @@
136
137 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
138
139- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
140+ if ( !defined $recurse || $level < $recurse ) {
141
142 my @slaves =
143 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
144- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
145+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
146
147 foreach my $slave ( @slaves ) {
148 PTDEBUG && _d('Recursing from',
149@@ -3195,25 +3234,14 @@
150 }
151
152 sub find_slave_hosts {
153- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
154+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
155
156- my @methods = qw(processlist hosts);
157- if ( $method ) {
158- @methods = grep { $_ ne $method } @methods;
159- unshift @methods, $method;
160- }
161- else {
162- if ( ($dsn->{P} || 3306) != 3306 ) {
163- PTDEBUG && _d('Port number is non-standard; using only hosts method');
164- @methods = qw(hosts);
165- }
166- }
167 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
168- 'using methods', @methods);
169+ 'using methods', @$methods);
170
171 my @slaves;
172 METHOD:
173- foreach my $method ( @methods ) {
174+ foreach my $method ( @$methods ) {
175 my $find_slaves = "_find_slaves_by_$method";
176 PTDEBUG && _d('Finding slaves with', $find_slaves);
177 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
178@@ -3716,13 +3744,16 @@
179
180 sub get_cxn_from_dsn_table {
181 my ($self, %args) = @_;
182- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
183+ my @required_args = qw(dsn_table_dsn make_cxn);
184 foreach my $arg ( @required_args ) {
185 die "I need a $arg argument" unless $args{$arg};
186 }
187- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
188+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
189 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
190
191+ my $dp = $self->{DSNParser};
192+ my $q = $self->{Quoter};
193+
194 my $dsn = $dp->parse($dsn_table_dsn);
195 my $dsn_table;
196 if ( $dsn->{D} && $dsn->{t} ) {
197@@ -4054,7 +4085,11 @@
198 my $dsn_defaults = $dp->parse_options($o);
199 my $dsn = $dp->parse($o->get('check-slave-lag'), $dsn_defaults);
200 $lag_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
201- $ms = new MasterSlave();
202+ $ms = new MasterSlave(
203+ OptionParser => $o,
204+ DSNParser => $dp,
205+ Quoter => $q,
206+ );
207 }
208
209 # ########################################################################
210
211=== modified file 'bin/pt-heartbeat'
212--- bin/pt-heartbeat 2012-07-30 14:30:05 +0000
213+++ bin/pt-heartbeat 2012-08-02 17:46:19 +0000
214@@ -24,8 +24,31 @@
215 use English qw(-no_match_vars);
216 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
217
218+sub check_recursion_method {
219+ my ($methods) = @_;
220+
221+ if ( @$methods != 1 ) {
222+ if ( grep({ !m/processlist|hosts/i } @$methods)
223+ && $methods->[0] !~ /^dsn=/i )
224+ {
225+ die "Invalid combination of recursion methods: "
226+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
227+ . "Only hosts and processlist may be combined.\n"
228+ }
229+ }
230+ else {
231+ my ($method) = @$methods;
232+ die "Invalid recursion method: " . ( $method || 'undef' )
233+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
234+ }
235+}
236+
237 sub new {
238 my ( $class, %args ) = @_;
239+ my @required_args = qw(OptionParser DSNParser Quoter);
240+ foreach my $arg ( @required_args ) {
241+ die "I need a $arg argument" unless $args{$arg};
242+ }
243 my $self = {
244 %args,
245 replication_thread => {},
246@@ -35,28 +58,27 @@
247
248 sub get_slaves {
249 my ($self, %args) = @_;
250- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
251+ my @required_args = qw(make_cxn);
252 foreach my $arg ( @required_args ) {
253 die "I need a $arg argument" unless $args{$arg};
254 }
255- my ($make_cxn, $o, $dp) = @args{@required_args};
256-
257- my $slaves = [];
258- my $method = $o->get('recursion-method');
259- PTDEBUG && _d('Slave recursion method:', $method);
260- if ( !$method || $method =~ m/processlist|hosts/i ) {
261+ my ($make_cxn) = @args{@required_args};
262+
263+ my $slaves = [];
264+ my $dp = $self->{DSNParser};
265+ my $methods = $self->_resolve_recursion_methods($args{dsn});
266+
267+ if ( grep { m/processlist|hosts/i } @$methods ) {
268 my @required_args = qw(dbh dsn);
269 foreach my $arg ( @required_args ) {
270 die "I need a $arg argument" unless $args{$arg};
271 }
272 my ($dbh, $dsn) = @args{@required_args};
273+
274 $self->recurse_to_slaves(
275- { dbh => $dbh,
276- dsn => $dsn,
277- dsn_parser => $dp,
278- recurse => $o->get('recurse'),
279- method => $o->get('recursion-method'),
280- callback => sub {
281+ { dbh => $dbh,
282+ dsn => $dsn,
283+ callback => sub {
284 my ( $dsn, $dbh, $level, $parent ) = @_;
285 return unless $level;
286 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
287@@ -66,31 +88,48 @@
288 }
289 );
290 }
291- elsif ( $method =~ m/^dsn=/i ) {
292- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
293+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
294+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
295 $slaves = $self->get_cxn_from_dsn_table(
296 %args,
297 dsn_table_dsn => $dsn_table_dsn,
298 );
299 }
300- elsif ( $method =~ m/none/i ) {
301+ elsif ( $methods->[0] =~ m/none/i ) {
302 PTDEBUG && _d('Not getting to slaves');
303 }
304 else {
305- die "Invalid --recursion-method: $method. Valid values are: "
306- . "dsn=DSN, hosts, or processlist.\n";
307+ die "Unexpected recursion methods: @$methods";
308 }
309-
310+
311 return $slaves;
312 }
313
314+sub _resolve_recursion_methods {
315+ my ($self, $dsn) = @_;
316+ my $o = $self->{OptionParser};
317+ if ( $o->got('recursion-method') ) {
318+ return $o->get('recursion-method');
319+ }
320+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
321+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
322+ return [qw(hosts)];
323+ }
324+ else {
325+ return $o->get('recursion-method');
326+ }
327+}
328+
329 sub recurse_to_slaves {
330 my ( $self, $args, $level ) = @_;
331 $level ||= 0;
332- my $dp = $args->{dsn_parser};
333- my $dsn = $args->{dsn};
334+ my $dp = $self->{DSNParser};
335+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
336+ my $dsn = $args->{dsn};
337
338- if ( lc($args->{method} || '') eq 'none' ) {
339+ my $methods = $self->_resolve_recursion_methods($dsn);
340+ PTDEBUG && _d('Recursion methods:', @$methods);
341+ if ( lc($methods->[0]) eq 'none' ) {
342 PTDEBUG && _d('Not recursing to slaves');
343 return;
344 }
345@@ -125,11 +164,11 @@
346
347 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
348
349- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
350+ if ( !defined $recurse || $level < $recurse ) {
351
352 my @slaves =
353 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
354- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
355+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
356
357 foreach my $slave ( @slaves ) {
358 PTDEBUG && _d('Recursing from',
359@@ -141,25 +180,14 @@
360 }
361
362 sub find_slave_hosts {
363- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
364+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
365
366- my @methods = qw(processlist hosts);
367- if ( $method ) {
368- @methods = grep { $_ ne $method } @methods;
369- unshift @methods, $method;
370- }
371- else {
372- if ( ($dsn->{P} || 3306) != 3306 ) {
373- PTDEBUG && _d('Port number is non-standard; using only hosts method');
374- @methods = qw(hosts);
375- }
376- }
377 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
378- 'using methods', @methods);
379+ 'using methods', @$methods);
380
381 my @slaves;
382 METHOD:
383- foreach my $method ( @methods ) {
384+ foreach my $method ( @$methods ) {
385 my $find_slaves = "_find_slaves_by_$method";
386 PTDEBUG && _d('Finding slaves with', $find_slaves);
387 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
388@@ -662,13 +690,16 @@
389
390 sub get_cxn_from_dsn_table {
391 my ($self, %args) = @_;
392- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
393+ my @required_args = qw(dsn_table_dsn make_cxn);
394 foreach my $arg ( @required_args ) {
395 die "I need a $arg argument" unless $args{$arg};
396 }
397- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
398+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
399 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
400
401+ my $dp = $self->{DSNParser};
402+ my $q = $self->{Quoter};
403+
404 my $dsn = $dp->parse($dsn_table_dsn);
405 my $dsn_table;
406 if ( $dsn->{D} && $dsn->{t} ) {
407@@ -1994,7 +2025,7 @@
408 PTDEBUG && _d($dbh, $sql);
409 my ($sql_mode) = eval { $dbh->selectrow_array($sql) };
410 if ( $EVAL_ERROR ) {
411- die $EVAL_ERROR;
412+ die "Error getting the current SQL_MODE: $EVAL_ERROR";
413 }
414
415 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
416@@ -2004,15 +2035,17 @@
417 PTDEBUG && _d($dbh, $sql);
418 eval { $dbh->do($sql) };
419 if ( $EVAL_ERROR ) {
420- die $EVAL_ERROR;
421+ die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE"
422+ . ($sql_mode ? " and $sql_mode" : '')
423+ . ": $EVAL_ERROR";
424 }
425
426- if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
427- $sql = "/*!40101 SET NAMES $charset*/";
428+ if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
429+ $sql = qq{/*!40101 SET NAMES "$charset"*/};
430 PTDEBUG && _d($dbh, ':', $sql);
431 eval { $dbh->do($sql) };
432 if ( $EVAL_ERROR ) {
433- die $EVAL_ERROR;
434+ die "Error setting NAMES to $charset: $EVAL_ERROR";
435 }
436 PTDEBUG && _d('Enabling charset for STDOUT');
437 if ( $charset eq 'utf8' ) {
438@@ -2024,12 +2057,12 @@
439 }
440 }
441
442- if ( $self->prop('set-vars') ) {
443- $sql = "SET " . $self->prop('set-vars');
444+ if ( my $var = $self->prop('set-vars') ) {
445+ $sql = "SET $var";
446 PTDEBUG && _d($dbh, ':', $sql);
447 eval { $dbh->do($sql) };
448 if ( $EVAL_ERROR ) {
449- die $EVAL_ERROR;
450+ die "Error setting $var: $EVAL_ERROR";
451 }
452 }
453 }
454@@ -3282,6 +3315,13 @@
455 }
456 }
457
458+ eval {
459+ MasterSlave::check_recursion_method($o->get('recursion-method'));
460+ };
461+ if ( $EVAL_ERROR ) {
462+ $o->save_error("Invalid --recursion-method: $EVAL_ERROR")
463+ }
464+
465 $o->usage_or_errors();
466
467 # ########################################################################
468@@ -3407,7 +3447,11 @@
469 my $master_server_id = $o->get('master-server-id');
470 if ( !$master_server_id ) {
471 eval {
472- my $ms = new MasterSlave();
473+ my $ms = new MasterSlave(
474+ OptionParser => $o,
475+ DSNParser => $dp,
476+ Quoter => $q,
477+ );
478 my $master_dsn = $ms->get_master_dsn($dbh, $dsn, $dp)
479 or die "This server is not a slave";
480 my $master_dbh = $dp->get_dbh($dp->get_cxn_params($master_dsn),
481@@ -3734,19 +3778,20 @@
482 # Collect a list of connections to the slaves.
483 if ( $o->get('recurse') ) {
484 PTDEBUG && _d('Recursing to slaves');
485- my $ms = new MasterSlave();
486+ my $ms = new MasterSlave(
487+ OptionParser => $o,
488+ DSNParser => $dp,
489+ Quoter => "Quoter",
490+ );
491 $ms->recurse_to_slaves(
492 { dbh => $dbh,
493 dsn => $dsn,
494- dsn_parser => $dp,
495- recurse => $o->get('recurse'),
496 callback => sub {
497 my ( $dsn, $dbh, $level ) = @_;
498 push @dbhs, $dbh;
499 PTDEBUG && _d("Found slave", $dp->as_string($dsn));
500 push @sths, [ $dsn, $dbh->prepare($sql) ];
501 },
502- method => $o->get('recursion-method'),
503 },
504 );
505 }
506@@ -4235,7 +4280,7 @@
507
508 =item --recursion-method
509
510-type: string
511+type: array; default: processlist,hosts
512
513 Preferred recursion method used to find slaves.
514
515
516=== modified file 'bin/pt-kill'
517--- bin/pt-kill 2012-07-30 19:48:15 +0000
518+++ bin/pt-kill 2012-08-02 17:46:19 +0000
519@@ -3345,8 +3345,31 @@
520 use English qw(-no_match_vars);
521 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
522
523+sub check_recursion_method {
524+ my ($methods) = @_;
525+
526+ if ( @$methods != 1 ) {
527+ if ( grep({ !m/processlist|hosts/i } @$methods)
528+ && $methods->[0] !~ /^dsn=/i )
529+ {
530+ die "Invalid combination of recursion methods: "
531+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
532+ . "Only hosts and processlist may be combined.\n"
533+ }
534+ }
535+ else {
536+ my ($method) = @$methods;
537+ die "Invalid recursion method: " . ( $method || 'undef' )
538+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
539+ }
540+}
541+
542 sub new {
543 my ( $class, %args ) = @_;
544+ my @required_args = qw(OptionParser DSNParser Quoter);
545+ foreach my $arg ( @required_args ) {
546+ die "I need a $arg argument" unless $args{$arg};
547+ }
548 my $self = {
549 %args,
550 replication_thread => {},
551@@ -3356,28 +3379,27 @@
552
553 sub get_slaves {
554 my ($self, %args) = @_;
555- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
556+ my @required_args = qw(make_cxn);
557 foreach my $arg ( @required_args ) {
558 die "I need a $arg argument" unless $args{$arg};
559 }
560- my ($make_cxn, $o, $dp) = @args{@required_args};
561-
562- my $slaves = [];
563- my $method = $o->get('recursion-method');
564- PTDEBUG && _d('Slave recursion method:', $method);
565- if ( !$method || $method =~ m/processlist|hosts/i ) {
566+ my ($make_cxn) = @args{@required_args};
567+
568+ my $slaves = [];
569+ my $dp = $self->{DSNParser};
570+ my $methods = $self->_resolve_recursion_methods($args{dsn});
571+
572+ if ( grep { m/processlist|hosts/i } @$methods ) {
573 my @required_args = qw(dbh dsn);
574 foreach my $arg ( @required_args ) {
575 die "I need a $arg argument" unless $args{$arg};
576 }
577 my ($dbh, $dsn) = @args{@required_args};
578+
579 $self->recurse_to_slaves(
580- { dbh => $dbh,
581- dsn => $dsn,
582- dsn_parser => $dp,
583- recurse => $o->get('recurse'),
584- method => $o->get('recursion-method'),
585- callback => sub {
586+ { dbh => $dbh,
587+ dsn => $dsn,
588+ callback => sub {
589 my ( $dsn, $dbh, $level, $parent ) = @_;
590 return unless $level;
591 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
592@@ -3387,31 +3409,48 @@
593 }
594 );
595 }
596- elsif ( $method =~ m/^dsn=/i ) {
597- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
598+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
599+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
600 $slaves = $self->get_cxn_from_dsn_table(
601 %args,
602 dsn_table_dsn => $dsn_table_dsn,
603 );
604 }
605- elsif ( $method =~ m/none/i ) {
606+ elsif ( $methods->[0] =~ m/none/i ) {
607 PTDEBUG && _d('Not getting to slaves');
608 }
609 else {
610- die "Invalid --recursion-method: $method. Valid values are: "
611- . "dsn=DSN, hosts, or processlist.\n";
612+ die "Unexpected recursion methods: @$methods";
613 }
614-
615+
616 return $slaves;
617 }
618
619+sub _resolve_recursion_methods {
620+ my ($self, $dsn) = @_;
621+ my $o = $self->{OptionParser};
622+ if ( $o->got('recursion-method') ) {
623+ return $o->get('recursion-method');
624+ }
625+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
626+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
627+ return [qw(hosts)];
628+ }
629+ else {
630+ return $o->get('recursion-method');
631+ }
632+}
633+
634 sub recurse_to_slaves {
635 my ( $self, $args, $level ) = @_;
636 $level ||= 0;
637- my $dp = $args->{dsn_parser};
638- my $dsn = $args->{dsn};
639+ my $dp = $self->{DSNParser};
640+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
641+ my $dsn = $args->{dsn};
642
643- if ( lc($args->{method} || '') eq 'none' ) {
644+ my $methods = $self->_resolve_recursion_methods($dsn);
645+ PTDEBUG && _d('Recursion methods:', @$methods);
646+ if ( lc($methods->[0]) eq 'none' ) {
647 PTDEBUG && _d('Not recursing to slaves');
648 return;
649 }
650@@ -3446,11 +3485,11 @@
651
652 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
653
654- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
655+ if ( !defined $recurse || $level < $recurse ) {
656
657 my @slaves =
658 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
659- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
660+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
661
662 foreach my $slave ( @slaves ) {
663 PTDEBUG && _d('Recursing from',
664@@ -3462,25 +3501,14 @@
665 }
666
667 sub find_slave_hosts {
668- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
669+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
670
671- my @methods = qw(processlist hosts);
672- if ( $method ) {
673- @methods = grep { $_ ne $method } @methods;
674- unshift @methods, $method;
675- }
676- else {
677- if ( ($dsn->{P} || 3306) != 3306 ) {
678- PTDEBUG && _d('Port number is non-standard; using only hosts method');
679- @methods = qw(hosts);
680- }
681- }
682 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
683- 'using methods', @methods);
684+ 'using methods', @$methods);
685
686 my @slaves;
687 METHOD:
688- foreach my $method ( @methods ) {
689+ foreach my $method ( @$methods ) {
690 my $find_slaves = "_find_slaves_by_$method";
691 PTDEBUG && _d('Finding slaves with', $find_slaves);
692 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
693@@ -3983,13 +4011,16 @@
694
695 sub get_cxn_from_dsn_table {
696 my ($self, %args) = @_;
697- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
698+ my @required_args = qw(dsn_table_dsn make_cxn);
699 foreach my $arg ( @required_args ) {
700 die "I need a $arg argument" unless $args{$arg};
701 }
702- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
703+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
704 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
705
706+ my $dp = $self->{DSNParser};
707+ my $q = $self->{Quoter};
708+
709 my $dsn = $dp->parse($dsn_table_dsn);
710 my $dsn_table;
711 if ( $dsn->{D} && $dsn->{t} ) {
712@@ -4802,10 +4833,6 @@
713 sub main {
714 @ARGV = @_; # set global ARGV for this package
715
716- my $ms = new MasterSlave();
717- my $pl = new Processlist(MasterSlave => $ms);
718- my $qr = new QueryRewriter();
719-
720 # ########################################################################
721 # Get configuration information.
722 # ########################################################################
723@@ -4880,6 +4907,14 @@
724 # ########################################################################
725 # Make input sub that will either get processlist from MySQL or a file.
726 # ########################################################################
727+ my $ms = new MasterSlave(
728+ OptionParser => $o,
729+ DSNParser => $dp,
730+ Quoter => "Quoter",
731+ );
732+ my $pl = new Processlist(MasterSlave => $ms);
733+ my $qr = new QueryRewriter();
734+
735 my $cxn;
736 my $dbh; # $cxn->dbh
737 my $get_proclist; # callback to SHOW PROCESSLIST
738
739=== modified file 'bin/pt-online-schema-change'
740--- bin/pt-online-schema-change 2012-07-31 16:33:30 +0000
741+++ bin/pt-online-schema-change 2012-08-02 17:46:19 +0000
742@@ -1935,7 +1935,7 @@
743 PTDEBUG && _d($dbh, $sql);
744 my ($sql_mode) = eval { $dbh->selectrow_array($sql) };
745 if ( $EVAL_ERROR ) {
746- die $EVAL_ERROR;
747+ die "Error getting the current SQL_MODE: $EVAL_ERROR";
748 }
749
750 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
751@@ -1945,15 +1945,17 @@
752 PTDEBUG && _d($dbh, $sql);
753 eval { $dbh->do($sql) };
754 if ( $EVAL_ERROR ) {
755- die $EVAL_ERROR;
756+ die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE"
757+ . ($sql_mode ? " and $sql_mode" : '')
758+ . ": $EVAL_ERROR";
759 }
760
761- if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
762- $sql = "/*!40101 SET NAMES $charset*/";
763+ if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
764+ $sql = qq{/*!40101 SET NAMES "$charset"*/};
765 PTDEBUG && _d($dbh, ':', $sql);
766 eval { $dbh->do($sql) };
767 if ( $EVAL_ERROR ) {
768- die $EVAL_ERROR;
769+ die "Error setting NAMES to $charset: $EVAL_ERROR";
770 }
771 PTDEBUG && _d('Enabling charset for STDOUT');
772 if ( $charset eq 'utf8' ) {
773@@ -1965,12 +1967,12 @@
774 }
775 }
776
777- if ( $self->prop('set-vars') ) {
778- $sql = "SET " . $self->prop('set-vars');
779+ if ( my $var = $self->prop('set-vars') ) {
780+ $sql = "SET $var";
781 PTDEBUG && _d($dbh, ':', $sql);
782 eval { $dbh->do($sql) };
783 if ( $EVAL_ERROR ) {
784- die $EVAL_ERROR;
785+ die "Error setting $var: $EVAL_ERROR";
786 }
787 }
788 }
789@@ -3333,6 +3335,7 @@
790 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
791 hostname => '',
792 set => $args{set},
793+ NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
794 dbh_set => 0,
795 OptionParser => $o,
796 DSNParser => $dp,
797@@ -3370,7 +3373,10 @@
798
799 PTDEBUG && _d($dbh, 'Setting dbh');
800
801- $dbh->{FetchHashKeyName} = 'NAME_lc';
802+ if ( !exists $self->{NAME_lc}
803+ || (defined $self->{NAME_lc} && $self->{NAME_lc}) ) {
804+ $dbh->{FetchHashKeyName} = 'NAME_lc';
805+ }
806
807 my $sql = 'SELECT @@hostname, @@server_id';
808 PTDEBUG && _d($dbh, $sql);
809@@ -3446,8 +3452,31 @@
810 use English qw(-no_match_vars);
811 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
812
813+sub check_recursion_method {
814+ my ($methods) = @_;
815+
816+ if ( @$methods != 1 ) {
817+ if ( grep({ !m/processlist|hosts/i } @$methods)
818+ && $methods->[0] !~ /^dsn=/i )
819+ {
820+ die "Invalid combination of recursion methods: "
821+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
822+ . "Only hosts and processlist may be combined.\n"
823+ }
824+ }
825+ else {
826+ my ($method) = @$methods;
827+ die "Invalid recursion method: " . ( $method || 'undef' )
828+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
829+ }
830+}
831+
832 sub new {
833 my ( $class, %args ) = @_;
834+ my @required_args = qw(OptionParser DSNParser Quoter);
835+ foreach my $arg ( @required_args ) {
836+ die "I need a $arg argument" unless $args{$arg};
837+ }
838 my $self = {
839 %args,
840 replication_thread => {},
841@@ -3457,28 +3486,27 @@
842
843 sub get_slaves {
844 my ($self, %args) = @_;
845- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
846+ my @required_args = qw(make_cxn);
847 foreach my $arg ( @required_args ) {
848 die "I need a $arg argument" unless $args{$arg};
849 }
850- my ($make_cxn, $o, $dp) = @args{@required_args};
851-
852- my $slaves = [];
853- my $method = $o->get('recursion-method');
854- PTDEBUG && _d('Slave recursion method:', $method);
855- if ( !$method || $method =~ m/processlist|hosts/i ) {
856+ my ($make_cxn) = @args{@required_args};
857+
858+ my $slaves = [];
859+ my $dp = $self->{DSNParser};
860+ my $methods = $self->_resolve_recursion_methods($args{dsn});
861+
862+ if ( grep { m/processlist|hosts/i } @$methods ) {
863 my @required_args = qw(dbh dsn);
864 foreach my $arg ( @required_args ) {
865 die "I need a $arg argument" unless $args{$arg};
866 }
867 my ($dbh, $dsn) = @args{@required_args};
868+
869 $self->recurse_to_slaves(
870- { dbh => $dbh,
871- dsn => $dsn,
872- dsn_parser => $dp,
873- recurse => $o->get('recurse'),
874- method => $o->get('recursion-method'),
875- callback => sub {
876+ { dbh => $dbh,
877+ dsn => $dsn,
878+ callback => sub {
879 my ( $dsn, $dbh, $level, $parent ) = @_;
880 return unless $level;
881 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
882@@ -3488,31 +3516,48 @@
883 }
884 );
885 }
886- elsif ( $method =~ m/^dsn=/i ) {
887- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
888+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
889+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
890 $slaves = $self->get_cxn_from_dsn_table(
891 %args,
892 dsn_table_dsn => $dsn_table_dsn,
893 );
894 }
895- elsif ( $method =~ m/none/i ) {
896+ elsif ( $methods->[0] =~ m/none/i ) {
897 PTDEBUG && _d('Not getting to slaves');
898 }
899 else {
900- die "Invalid --recursion-method: $method. Valid values are: "
901- . "dsn=DSN, hosts, or processlist.\n";
902+ die "Unexpected recursion methods: @$methods";
903 }
904-
905+
906 return $slaves;
907 }
908
909+sub _resolve_recursion_methods {
910+ my ($self, $dsn) = @_;
911+ my $o = $self->{OptionParser};
912+ if ( $o->got('recursion-method') ) {
913+ return $o->get('recursion-method');
914+ }
915+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
916+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
917+ return [qw(hosts)];
918+ }
919+ else {
920+ return $o->get('recursion-method');
921+ }
922+}
923+
924 sub recurse_to_slaves {
925 my ( $self, $args, $level ) = @_;
926 $level ||= 0;
927- my $dp = $args->{dsn_parser};
928- my $dsn = $args->{dsn};
929+ my $dp = $self->{DSNParser};
930+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
931+ my $dsn = $args->{dsn};
932
933- if ( lc($args->{method} || '') eq 'none' ) {
934+ my $methods = $self->_resolve_recursion_methods($dsn);
935+ PTDEBUG && _d('Recursion methods:', @$methods);
936+ if ( lc($methods->[0]) eq 'none' ) {
937 PTDEBUG && _d('Not recursing to slaves');
938 return;
939 }
940@@ -3547,11 +3592,11 @@
941
942 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
943
944- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
945+ if ( !defined $recurse || $level < $recurse ) {
946
947 my @slaves =
948 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
949- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
950+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
951
952 foreach my $slave ( @slaves ) {
953 PTDEBUG && _d('Recursing from',
954@@ -3563,25 +3608,14 @@
955 }
956
957 sub find_slave_hosts {
958- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
959+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
960
961- my @methods = qw(processlist hosts);
962- if ( $method ) {
963- @methods = grep { $_ ne $method } @methods;
964- unshift @methods, $method;
965- }
966- else {
967- if ( ($dsn->{P} || 3306) != 3306 ) {
968- PTDEBUG && _d('Port number is non-standard; using only hosts method');
969- @methods = qw(hosts);
970- }
971- }
972 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
973- 'using methods', @methods);
974+ 'using methods', @$methods);
975
976 my @slaves;
977 METHOD:
978- foreach my $method ( @methods ) {
979+ foreach my $method ( @$methods ) {
980 my $find_slaves = "_find_slaves_by_$method";
981 PTDEBUG && _d('Finding slaves with', $find_slaves);
982 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
983@@ -4084,13 +4118,16 @@
984
985 sub get_cxn_from_dsn_table {
986 my ($self, %args) = @_;
987- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
988+ my @required_args = qw(dsn_table_dsn make_cxn);
989 foreach my $arg ( @required_args ) {
990 die "I need a $arg argument" unless $args{$arg};
991 }
992- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
993+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
994 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
995
996+ my $dp = $self->{DSNParser};
997+ my $q = $self->{Quoter};
998+
999 my $dsn = $dp->parse($dsn_table_dsn);
1000 my $dsn_table;
1001 if ( $dsn->{D} && $dsn->{t} ) {
1002@@ -5909,6 +5946,13 @@
1003 }
1004 }
1005
1006+ eval {
1007+ MasterSlave::check_recursion_method($o->get('recursion-method'));
1008+ };
1009+ if ( $EVAL_ERROR ) {
1010+ $o->save_error("Invalid --recursion-method: $EVAL_ERROR")
1011+ }
1012+
1013 $o->usage_or_errors();
1014
1015 if ( $o->get('quiet') ) {
1016@@ -6006,13 +6050,15 @@
1017 # #####################################################################
1018 # Find and connect to slaves.
1019 # #####################################################################
1020- my $ms = new MasterSlave();
1021+ my $ms = new MasterSlave(
1022+ OptionParser => $o,
1023+ DSNParser => $dp,
1024+ Quoter => $q,
1025+ );
1026+
1027 $slaves = $ms->get_slaves(
1028 dbh => $cxn->dbh(),
1029 dsn => $cxn->dsn(),
1030- OptionParser => $o,
1031- DSNParser => $dp,
1032- Quoter => $q,
1033 make_cxn => sub {
1034 return $make_cxn->(@_, prev_dsn => $cxn->dsn());
1035 },
1036@@ -8368,7 +8414,7 @@
1037
1038 =item --recursion-method
1039
1040-type: string
1041+type: array; default: processlist,hosts
1042
1043 Preferred recursion method for discovering replicas. Possible methods are:
1044
1045
1046=== modified file 'bin/pt-query-digest'
1047--- bin/pt-query-digest 2012-07-30 19:48:15 +0000
1048+++ bin/pt-query-digest 2012-08-02 17:46:19 +0000
1049@@ -10339,8 +10339,31 @@
1050 use English qw(-no_match_vars);
1051 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1052
1053+sub check_recursion_method {
1054+ my ($methods) = @_;
1055+
1056+ if ( @$methods != 1 ) {
1057+ if ( grep({ !m/processlist|hosts/i } @$methods)
1058+ && $methods->[0] !~ /^dsn=/i )
1059+ {
1060+ die "Invalid combination of recursion methods: "
1061+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
1062+ . "Only hosts and processlist may be combined.\n"
1063+ }
1064+ }
1065+ else {
1066+ my ($method) = @$methods;
1067+ die "Invalid recursion method: " . ( $method || 'undef' )
1068+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
1069+ }
1070+}
1071+
1072 sub new {
1073 my ( $class, %args ) = @_;
1074+ my @required_args = qw(OptionParser DSNParser Quoter);
1075+ foreach my $arg ( @required_args ) {
1076+ die "I need a $arg argument" unless $args{$arg};
1077+ }
1078 my $self = {
1079 %args,
1080 replication_thread => {},
1081@@ -10350,28 +10373,27 @@
1082
1083 sub get_slaves {
1084 my ($self, %args) = @_;
1085- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
1086+ my @required_args = qw(make_cxn);
1087 foreach my $arg ( @required_args ) {
1088 die "I need a $arg argument" unless $args{$arg};
1089 }
1090- my ($make_cxn, $o, $dp) = @args{@required_args};
1091-
1092- my $slaves = [];
1093- my $method = $o->get('recursion-method');
1094- PTDEBUG && _d('Slave recursion method:', $method);
1095- if ( !$method || $method =~ m/processlist|hosts/i ) {
1096+ my ($make_cxn) = @args{@required_args};
1097+
1098+ my $slaves = [];
1099+ my $dp = $self->{DSNParser};
1100+ my $methods = $self->_resolve_recursion_methods($args{dsn});
1101+
1102+ if ( grep { m/processlist|hosts/i } @$methods ) {
1103 my @required_args = qw(dbh dsn);
1104 foreach my $arg ( @required_args ) {
1105 die "I need a $arg argument" unless $args{$arg};
1106 }
1107 my ($dbh, $dsn) = @args{@required_args};
1108+
1109 $self->recurse_to_slaves(
1110- { dbh => $dbh,
1111- dsn => $dsn,
1112- dsn_parser => $dp,
1113- recurse => $o->get('recurse'),
1114- method => $o->get('recursion-method'),
1115- callback => sub {
1116+ { dbh => $dbh,
1117+ dsn => $dsn,
1118+ callback => sub {
1119 my ( $dsn, $dbh, $level, $parent ) = @_;
1120 return unless $level;
1121 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
1122@@ -10381,31 +10403,48 @@
1123 }
1124 );
1125 }
1126- elsif ( $method =~ m/^dsn=/i ) {
1127- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
1128+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
1129+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
1130 $slaves = $self->get_cxn_from_dsn_table(
1131 %args,
1132 dsn_table_dsn => $dsn_table_dsn,
1133 );
1134 }
1135- elsif ( $method =~ m/none/i ) {
1136+ elsif ( $methods->[0] =~ m/none/i ) {
1137 PTDEBUG && _d('Not getting to slaves');
1138 }
1139 else {
1140- die "Invalid --recursion-method: $method. Valid values are: "
1141- . "dsn=DSN, hosts, or processlist.\n";
1142+ die "Unexpected recursion methods: @$methods";
1143 }
1144-
1145+
1146 return $slaves;
1147 }
1148
1149+sub _resolve_recursion_methods {
1150+ my ($self, $dsn) = @_;
1151+ my $o = $self->{OptionParser};
1152+ if ( $o->got('recursion-method') ) {
1153+ return $o->get('recursion-method');
1154+ }
1155+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
1156+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
1157+ return [qw(hosts)];
1158+ }
1159+ else {
1160+ return $o->get('recursion-method');
1161+ }
1162+}
1163+
1164 sub recurse_to_slaves {
1165 my ( $self, $args, $level ) = @_;
1166 $level ||= 0;
1167- my $dp = $args->{dsn_parser};
1168- my $dsn = $args->{dsn};
1169+ my $dp = $self->{DSNParser};
1170+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
1171+ my $dsn = $args->{dsn};
1172
1173- if ( lc($args->{method} || '') eq 'none' ) {
1174+ my $methods = $self->_resolve_recursion_methods($dsn);
1175+ PTDEBUG && _d('Recursion methods:', @$methods);
1176+ if ( lc($methods->[0]) eq 'none' ) {
1177 PTDEBUG && _d('Not recursing to slaves');
1178 return;
1179 }
1180@@ -10440,11 +10479,11 @@
1181
1182 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
1183
1184- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
1185+ if ( !defined $recurse || $level < $recurse ) {
1186
1187 my @slaves =
1188 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
1189- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
1190+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
1191
1192 foreach my $slave ( @slaves ) {
1193 PTDEBUG && _d('Recursing from',
1194@@ -10456,25 +10495,14 @@
1195 }
1196
1197 sub find_slave_hosts {
1198- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
1199+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
1200
1201- my @methods = qw(processlist hosts);
1202- if ( $method ) {
1203- @methods = grep { $_ ne $method } @methods;
1204- unshift @methods, $method;
1205- }
1206- else {
1207- if ( ($dsn->{P} || 3306) != 3306 ) {
1208- PTDEBUG && _d('Port number is non-standard; using only hosts method');
1209- @methods = qw(hosts);
1210- }
1211- }
1212 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
1213- 'using methods', @methods);
1214+ 'using methods', @$methods);
1215
1216 my @slaves;
1217 METHOD:
1218- foreach my $method ( @methods ) {
1219+ foreach my $method ( @$methods ) {
1220 my $find_slaves = "_find_slaves_by_$method";
1221 PTDEBUG && _d('Finding slaves with', $find_slaves);
1222 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
1223@@ -10977,13 +11005,16 @@
1224
1225 sub get_cxn_from_dsn_table {
1226 my ($self, %args) = @_;
1227- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
1228+ my @required_args = qw(dsn_table_dsn make_cxn);
1229 foreach my $arg ( @required_args ) {
1230 die "I need a $arg argument" unless $args{$arg};
1231 }
1232- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
1233+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
1234 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
1235
1236+ my $dp = $self->{DSNParser};
1237+ my $q = $self->{Quoter};
1238+
1239 my $dsn = $dp->parse($dsn_table_dsn);
1240 my $dsn_table;
1241 if ( $dsn->{D} && $dsn->{t} ) {
1242@@ -12195,7 +12226,11 @@
1243 { # event
1244 my $misc;
1245 if ( my $ps_dsn = $o->get('processlist') ) {
1246- my $ms = new MasterSlave();
1247+ my $ms = new MasterSlave(
1248+ OptionParser => $o,
1249+ DSNParser => $dp,
1250+ Quoter => $q,
1251+ );
1252 my $pl = new Processlist(
1253 interval => $o->get('interval') * 1_000_000,
1254 MasterSlave => $ms
1255
1256=== modified file 'bin/pt-slave-find'
1257--- bin/pt-slave-find 2012-07-20 20:33:13 +0000
1258+++ bin/pt-slave-find 2012-08-02 17:46:19 +0000
1259@@ -1741,7 +1741,7 @@
1260 PTDEBUG && _d($dbh, $sql);
1261 my ($sql_mode) = eval { $dbh->selectrow_array($sql) };
1262 if ( $EVAL_ERROR ) {
1263- die $EVAL_ERROR;
1264+ die "Error getting the current SQL_MODE: $EVAL_ERROR";
1265 }
1266
1267 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1268@@ -1751,15 +1751,17 @@
1269 PTDEBUG && _d($dbh, $sql);
1270 eval { $dbh->do($sql) };
1271 if ( $EVAL_ERROR ) {
1272- die $EVAL_ERROR;
1273+ die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE"
1274+ . ($sql_mode ? " and $sql_mode" : '')
1275+ . ": $EVAL_ERROR";
1276 }
1277
1278- if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
1279- $sql = "/*!40101 SET NAMES $charset*/";
1280+ if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1281+ $sql = qq{/*!40101 SET NAMES "$charset"*/};
1282 PTDEBUG && _d($dbh, ':', $sql);
1283 eval { $dbh->do($sql) };
1284 if ( $EVAL_ERROR ) {
1285- die $EVAL_ERROR;
1286+ die "Error setting NAMES to $charset: $EVAL_ERROR";
1287 }
1288 PTDEBUG && _d('Enabling charset for STDOUT');
1289 if ( $charset eq 'utf8' ) {
1290@@ -1771,12 +1773,12 @@
1291 }
1292 }
1293
1294- if ( $self->prop('set-vars') ) {
1295- $sql = "SET " . $self->prop('set-vars');
1296+ if ( my $var = $self->prop('set-vars') ) {
1297+ $sql = "SET $var";
1298 PTDEBUG && _d($dbh, ':', $sql);
1299 eval { $dbh->do($sql) };
1300 if ( $EVAL_ERROR ) {
1301- die $EVAL_ERROR;
1302+ die "Error setting $var: $EVAL_ERROR";
1303 }
1304 }
1305 }
1306@@ -1870,8 +1872,31 @@
1307 use English qw(-no_match_vars);
1308 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1309
1310+sub check_recursion_method {
1311+ my ($methods) = @_;
1312+
1313+ if ( @$methods != 1 ) {
1314+ if ( grep({ !m/processlist|hosts/i } @$methods)
1315+ && $methods->[0] !~ /^dsn=/i )
1316+ {
1317+ die "Invalid combination of recursion methods: "
1318+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
1319+ . "Only hosts and processlist may be combined.\n"
1320+ }
1321+ }
1322+ else {
1323+ my ($method) = @$methods;
1324+ die "Invalid recursion method: " . ( $method || 'undef' )
1325+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
1326+ }
1327+}
1328+
1329 sub new {
1330 my ( $class, %args ) = @_;
1331+ my @required_args = qw(OptionParser DSNParser Quoter);
1332+ foreach my $arg ( @required_args ) {
1333+ die "I need a $arg argument" unless $args{$arg};
1334+ }
1335 my $self = {
1336 %args,
1337 replication_thread => {},
1338@@ -1881,28 +1906,27 @@
1339
1340 sub get_slaves {
1341 my ($self, %args) = @_;
1342- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
1343+ my @required_args = qw(make_cxn);
1344 foreach my $arg ( @required_args ) {
1345 die "I need a $arg argument" unless $args{$arg};
1346 }
1347- my ($make_cxn, $o, $dp) = @args{@required_args};
1348-
1349- my $slaves = [];
1350- my $method = $o->get('recursion-method');
1351- PTDEBUG && _d('Slave recursion method:', $method);
1352- if ( !$method || $method =~ m/processlist|hosts/i ) {
1353+ my ($make_cxn) = @args{@required_args};
1354+
1355+ my $slaves = [];
1356+ my $dp = $self->{DSNParser};
1357+ my $methods = $self->_resolve_recursion_methods($args{dsn});
1358+
1359+ if ( grep { m/processlist|hosts/i } @$methods ) {
1360 my @required_args = qw(dbh dsn);
1361 foreach my $arg ( @required_args ) {
1362 die "I need a $arg argument" unless $args{$arg};
1363 }
1364 my ($dbh, $dsn) = @args{@required_args};
1365+
1366 $self->recurse_to_slaves(
1367- { dbh => $dbh,
1368- dsn => $dsn,
1369- dsn_parser => $dp,
1370- recurse => $o->get('recurse'),
1371- method => $o->get('recursion-method'),
1372- callback => sub {
1373+ { dbh => $dbh,
1374+ dsn => $dsn,
1375+ callback => sub {
1376 my ( $dsn, $dbh, $level, $parent ) = @_;
1377 return unless $level;
1378 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
1379@@ -1912,31 +1936,48 @@
1380 }
1381 );
1382 }
1383- elsif ( $method =~ m/^dsn=/i ) {
1384- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
1385+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
1386+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
1387 $slaves = $self->get_cxn_from_dsn_table(
1388 %args,
1389 dsn_table_dsn => $dsn_table_dsn,
1390 );
1391 }
1392- elsif ( $method =~ m/none/i ) {
1393+ elsif ( $methods->[0] =~ m/none/i ) {
1394 PTDEBUG && _d('Not getting to slaves');
1395 }
1396 else {
1397- die "Invalid --recursion-method: $method. Valid values are: "
1398- . "dsn=DSN, hosts, or processlist.\n";
1399+ die "Unexpected recursion methods: @$methods";
1400 }
1401-
1402+
1403 return $slaves;
1404 }
1405
1406+sub _resolve_recursion_methods {
1407+ my ($self, $dsn) = @_;
1408+ my $o = $self->{OptionParser};
1409+ if ( $o->got('recursion-method') ) {
1410+ return $o->get('recursion-method');
1411+ }
1412+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
1413+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
1414+ return [qw(hosts)];
1415+ }
1416+ else {
1417+ return $o->get('recursion-method');
1418+ }
1419+}
1420+
1421 sub recurse_to_slaves {
1422 my ( $self, $args, $level ) = @_;
1423 $level ||= 0;
1424- my $dp = $args->{dsn_parser};
1425- my $dsn = $args->{dsn};
1426+ my $dp = $self->{DSNParser};
1427+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
1428+ my $dsn = $args->{dsn};
1429
1430- if ( lc($args->{method} || '') eq 'none' ) {
1431+ my $methods = $self->_resolve_recursion_methods($dsn);
1432+ PTDEBUG && _d('Recursion methods:', @$methods);
1433+ if ( lc($methods->[0]) eq 'none' ) {
1434 PTDEBUG && _d('Not recursing to slaves');
1435 return;
1436 }
1437@@ -1971,11 +2012,11 @@
1438
1439 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
1440
1441- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
1442+ if ( !defined $recurse || $level < $recurse ) {
1443
1444 my @slaves =
1445 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
1446- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
1447+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
1448
1449 foreach my $slave ( @slaves ) {
1450 PTDEBUG && _d('Recursing from',
1451@@ -1987,25 +2028,14 @@
1452 }
1453
1454 sub find_slave_hosts {
1455- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
1456+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
1457
1458- my @methods = qw(processlist hosts);
1459- if ( $method ) {
1460- @methods = grep { $_ ne $method } @methods;
1461- unshift @methods, $method;
1462- }
1463- else {
1464- if ( ($dsn->{P} || 3306) != 3306 ) {
1465- PTDEBUG && _d('Port number is non-standard; using only hosts method');
1466- @methods = qw(hosts);
1467- }
1468- }
1469 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
1470- 'using methods', @methods);
1471+ 'using methods', @$methods);
1472
1473 my @slaves;
1474 METHOD:
1475- foreach my $method ( @methods ) {
1476+ foreach my $method ( @$methods ) {
1477 my $find_slaves = "_find_slaves_by_$method";
1478 PTDEBUG && _d('Finding slaves with', $find_slaves);
1479 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
1480@@ -2508,13 +2538,16 @@
1481
1482 sub get_cxn_from_dsn_table {
1483 my ($self, %args) = @_;
1484- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
1485+ my @required_args = qw(dsn_table_dsn make_cxn);
1486 foreach my $arg ( @required_args ) {
1487 die "I need a $arg argument" unless $args{$arg};
1488 }
1489- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
1490+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
1491 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
1492
1493+ my $dp = $self->{DSNParser};
1494+ my $q = $self->{Quoter};
1495+
1496 my $dsn = $dp->parse($dsn_table_dsn);
1497 my $dsn_table;
1498 if ( $dsn->{D} && $dsn->{t} ) {
1499@@ -3330,6 +3363,13 @@
1500 $o->save_error("Missing or invalid master host");
1501 }
1502
1503+ eval {
1504+ MasterSlave::check_recursion_method($o->get('recursion-method'));
1505+ };
1506+ if ( $EVAL_ERROR ) {
1507+ $o->save_error("Invalid --recursion-method: $EVAL_ERROR")
1508+ }
1509+
1510 $o->usage_or_errors();
1511
1512 # ########################################################################
1513@@ -3355,14 +3395,15 @@
1514
1515 # Despite the name, recursing to slaves actually begins at the specified
1516 # server, so the named server may also be included.
1517- my $ms = new MasterSlave();
1518+ my $ms = new MasterSlave(
1519+ OptionParser => $o,
1520+ DSNParser => $dp,
1521+ Quoter => "Quoter",
1522+ );
1523 $ms->recurse_to_slaves(
1524- { dbh => $dbh,
1525- dsn => $master_dsn,
1526- dsn_parser => $dp,
1527- recurse => $o->get('recurse'),
1528- method => $o->get('recursion-method'),
1529- callback => sub {
1530+ { dbh => $dbh,
1531+ dsn => $master_dsn,
1532+ callback => sub {
1533 my ( $dsn, $dbh, $level, $parent ) = @_;
1534 if ( !$parent ) {
1535 $root = $dsn;
1536@@ -3710,7 +3751,7 @@
1537
1538 =item --recursion-method
1539
1540-type: string
1541+type: array; default: processlist,hosts
1542
1543 Preferred recursion method used to find slaves.
1544
1545
1546=== modified file 'bin/pt-slave-restart'
1547--- bin/pt-slave-restart 2012-07-20 20:33:13 +0000
1548+++ bin/pt-slave-restart 2012-08-02 17:46:19 +0000
1549@@ -2054,7 +2054,7 @@
1550 PTDEBUG && _d($dbh, $sql);
1551 my ($sql_mode) = eval { $dbh->selectrow_array($sql) };
1552 if ( $EVAL_ERROR ) {
1553- die $EVAL_ERROR;
1554+ die "Error getting the current SQL_MODE: $EVAL_ERROR";
1555 }
1556
1557 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1558@@ -2064,15 +2064,17 @@
1559 PTDEBUG && _d($dbh, $sql);
1560 eval { $dbh->do($sql) };
1561 if ( $EVAL_ERROR ) {
1562- die $EVAL_ERROR;
1563+ die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE"
1564+ . ($sql_mode ? " and $sql_mode" : '')
1565+ . ": $EVAL_ERROR";
1566 }
1567
1568- if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
1569- $sql = "/*!40101 SET NAMES $charset*/";
1570+ if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1571+ $sql = qq{/*!40101 SET NAMES "$charset"*/};
1572 PTDEBUG && _d($dbh, ':', $sql);
1573 eval { $dbh->do($sql) };
1574 if ( $EVAL_ERROR ) {
1575- die $EVAL_ERROR;
1576+ die "Error setting NAMES to $charset: $EVAL_ERROR";
1577 }
1578 PTDEBUG && _d('Enabling charset for STDOUT');
1579 if ( $charset eq 'utf8' ) {
1580@@ -2084,12 +2086,12 @@
1581 }
1582 }
1583
1584- if ( $self->prop('set-vars') ) {
1585- $sql = "SET " . $self->prop('set-vars');
1586+ if ( my $var = $self->prop('set-vars') ) {
1587+ $sql = "SET $var";
1588 PTDEBUG && _d($dbh, ':', $sql);
1589 eval { $dbh->do($sql) };
1590 if ( $EVAL_ERROR ) {
1591- die $EVAL_ERROR;
1592+ die "Error setting $var: $EVAL_ERROR";
1593 }
1594 }
1595 }
1596@@ -2183,8 +2185,31 @@
1597 use English qw(-no_match_vars);
1598 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1599
1600+sub check_recursion_method {
1601+ my ($methods) = @_;
1602+
1603+ if ( @$methods != 1 ) {
1604+ if ( grep({ !m/processlist|hosts/i } @$methods)
1605+ && $methods->[0] !~ /^dsn=/i )
1606+ {
1607+ die "Invalid combination of recursion methods: "
1608+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
1609+ . "Only hosts and processlist may be combined.\n"
1610+ }
1611+ }
1612+ else {
1613+ my ($method) = @$methods;
1614+ die "Invalid recursion method: " . ( $method || 'undef' )
1615+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
1616+ }
1617+}
1618+
1619 sub new {
1620 my ( $class, %args ) = @_;
1621+ my @required_args = qw(OptionParser DSNParser Quoter);
1622+ foreach my $arg ( @required_args ) {
1623+ die "I need a $arg argument" unless $args{$arg};
1624+ }
1625 my $self = {
1626 %args,
1627 replication_thread => {},
1628@@ -2194,28 +2219,27 @@
1629
1630 sub get_slaves {
1631 my ($self, %args) = @_;
1632- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
1633+ my @required_args = qw(make_cxn);
1634 foreach my $arg ( @required_args ) {
1635 die "I need a $arg argument" unless $args{$arg};
1636 }
1637- my ($make_cxn, $o, $dp) = @args{@required_args};
1638-
1639- my $slaves = [];
1640- my $method = $o->get('recursion-method');
1641- PTDEBUG && _d('Slave recursion method:', $method);
1642- if ( !$method || $method =~ m/processlist|hosts/i ) {
1643+ my ($make_cxn) = @args{@required_args};
1644+
1645+ my $slaves = [];
1646+ my $dp = $self->{DSNParser};
1647+ my $methods = $self->_resolve_recursion_methods($args{dsn});
1648+
1649+ if ( grep { m/processlist|hosts/i } @$methods ) {
1650 my @required_args = qw(dbh dsn);
1651 foreach my $arg ( @required_args ) {
1652 die "I need a $arg argument" unless $args{$arg};
1653 }
1654 my ($dbh, $dsn) = @args{@required_args};
1655+
1656 $self->recurse_to_slaves(
1657- { dbh => $dbh,
1658- dsn => $dsn,
1659- dsn_parser => $dp,
1660- recurse => $o->get('recurse'),
1661- method => $o->get('recursion-method'),
1662- callback => sub {
1663+ { dbh => $dbh,
1664+ dsn => $dsn,
1665+ callback => sub {
1666 my ( $dsn, $dbh, $level, $parent ) = @_;
1667 return unless $level;
1668 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
1669@@ -2225,31 +2249,48 @@
1670 }
1671 );
1672 }
1673- elsif ( $method =~ m/^dsn=/i ) {
1674- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
1675+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
1676+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
1677 $slaves = $self->get_cxn_from_dsn_table(
1678 %args,
1679 dsn_table_dsn => $dsn_table_dsn,
1680 );
1681 }
1682- elsif ( $method =~ m/none/i ) {
1683+ elsif ( $methods->[0] =~ m/none/i ) {
1684 PTDEBUG && _d('Not getting to slaves');
1685 }
1686 else {
1687- die "Invalid --recursion-method: $method. Valid values are: "
1688- . "dsn=DSN, hosts, or processlist.\n";
1689+ die "Unexpected recursion methods: @$methods";
1690 }
1691-
1692+
1693 return $slaves;
1694 }
1695
1696+sub _resolve_recursion_methods {
1697+ my ($self, $dsn) = @_;
1698+ my $o = $self->{OptionParser};
1699+ if ( $o->got('recursion-method') ) {
1700+ return $o->get('recursion-method');
1701+ }
1702+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
1703+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
1704+ return [qw(hosts)];
1705+ }
1706+ else {
1707+ return $o->get('recursion-method');
1708+ }
1709+}
1710+
1711 sub recurse_to_slaves {
1712 my ( $self, $args, $level ) = @_;
1713 $level ||= 0;
1714- my $dp = $args->{dsn_parser};
1715- my $dsn = $args->{dsn};
1716+ my $dp = $self->{DSNParser};
1717+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
1718+ my $dsn = $args->{dsn};
1719
1720- if ( lc($args->{method} || '') eq 'none' ) {
1721+ my $methods = $self->_resolve_recursion_methods($dsn);
1722+ PTDEBUG && _d('Recursion methods:', @$methods);
1723+ if ( lc($methods->[0]) eq 'none' ) {
1724 PTDEBUG && _d('Not recursing to slaves');
1725 return;
1726 }
1727@@ -2284,11 +2325,11 @@
1728
1729 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
1730
1731- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
1732+ if ( !defined $recurse || $level < $recurse ) {
1733
1734 my @slaves =
1735 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
1736- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
1737+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
1738
1739 foreach my $slave ( @slaves ) {
1740 PTDEBUG && _d('Recursing from',
1741@@ -2300,25 +2341,14 @@
1742 }
1743
1744 sub find_slave_hosts {
1745- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
1746+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
1747
1748- my @methods = qw(processlist hosts);
1749- if ( $method ) {
1750- @methods = grep { $_ ne $method } @methods;
1751- unshift @methods, $method;
1752- }
1753- else {
1754- if ( ($dsn->{P} || 3306) != 3306 ) {
1755- PTDEBUG && _d('Port number is non-standard; using only hosts method');
1756- @methods = qw(hosts);
1757- }
1758- }
1759 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
1760- 'using methods', @methods);
1761+ 'using methods', @$methods);
1762
1763 my @slaves;
1764 METHOD:
1765- foreach my $method ( @methods ) {
1766+ foreach my $method ( @$methods ) {
1767 my $find_slaves = "_find_slaves_by_$method";
1768 PTDEBUG && _d('Finding slaves with', $find_slaves);
1769 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
1770@@ -2821,13 +2851,16 @@
1771
1772 sub get_cxn_from_dsn_table {
1773 my ($self, %args) = @_;
1774- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
1775+ my @required_args = qw(dsn_table_dsn make_cxn);
1776 foreach my $arg ( @required_args ) {
1777 die "I need a $arg argument" unless $args{$arg};
1778 }
1779- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
1780+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
1781 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
1782
1783+ my $dp = $self->{DSNParser};
1784+ my $q = $self->{Quoter};
1785+
1786 my $dsn = $dp->parse($dsn_table_dsn);
1787 my $dsn_table;
1788 if ( $dsn->{D} && $dsn->{t} ) {
1789@@ -3113,6 +3146,13 @@
1790 }
1791 }
1792
1793+ eval {
1794+ MasterSlave::check_recursion_method($o->get('recursion-method'));
1795+ };
1796+ if ( $EVAL_ERROR ) {
1797+ $o->save_error("Invalid --recursion-method: $EVAL_ERROR")
1798+ }
1799+
1800 $o->usage_or_errors();
1801
1802 # ########################################################################
1803@@ -3178,13 +3218,15 @@
1804
1805 # Despite the name, recursing to slaves actually begins at the specified
1806 # server, so the named server may also be watched, if it's a slave.
1807- my $ms = new MasterSlave();
1808+ my $ms = new MasterSlave(
1809+ OptionParser => $o,
1810+ DSNParser => $dp,
1811+ Quoter => $q,
1812+ );
1813 $ms->recurse_to_slaves(
1814- { dbh => $dbh,
1815- dsn => $dsn,
1816- dsn_parser => $dp,
1817- recurse => $o->get('recurse') || 0,
1818- callback => sub {
1819+ { dbh => $dbh,
1820+ dsn => $dsn,
1821+ callback => sub {
1822 my ( $dsn, $dbh, $level ) = @_;
1823 # Test whether we want to watch this server.
1824 eval {
1825@@ -3206,7 +3248,6 @@
1826 my ( $dsn, $dbh, $level ) = @_;
1827 print STDERR "Skipping ", $dp->as_string($dsn), "\n";
1828 },
1829- method => $o->get('recursion-method'),
1830 }
1831 );
1832
1833@@ -3792,7 +3833,7 @@
1834
1835 =item --recursion-method
1836
1837-type: string
1838+type: array; default: processlist,hosts
1839
1840 Preferred recursion method used to find slaves.
1841
1842
1843=== modified file 'bin/pt-table-checksum'
1844--- bin/pt-table-checksum 2012-08-01 20:58:52 +0000
1845+++ bin/pt-table-checksum 2012-08-02 17:46:19 +0000
1846@@ -266,7 +266,7 @@
1847 PTDEBUG && _d($dbh, $sql);
1848 my ($sql_mode) = eval { $dbh->selectrow_array($sql) };
1849 if ( $EVAL_ERROR ) {
1850- die $EVAL_ERROR;
1851+ die "Error getting the current SQL_MODE: $EVAL_ERROR";
1852 }
1853
1854 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1855@@ -276,15 +276,17 @@
1856 PTDEBUG && _d($dbh, $sql);
1857 eval { $dbh->do($sql) };
1858 if ( $EVAL_ERROR ) {
1859- die $EVAL_ERROR;
1860+ die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE"
1861+ . ($sql_mode ? " and $sql_mode" : '')
1862+ . ": $EVAL_ERROR";
1863 }
1864
1865- if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
1866- $sql = "/*!40101 SET NAMES $charset*/";
1867+ if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1868+ $sql = qq{/*!40101 SET NAMES "$charset"*/};
1869 PTDEBUG && _d($dbh, ':', $sql);
1870 eval { $dbh->do($sql) };
1871 if ( $EVAL_ERROR ) {
1872- die $EVAL_ERROR;
1873+ die "Error setting NAMES to $charset: $EVAL_ERROR";
1874 }
1875 PTDEBUG && _d('Enabling charset for STDOUT');
1876 if ( $charset eq 'utf8' ) {
1877@@ -296,12 +298,12 @@
1878 }
1879 }
1880
1881- if ( $self->prop('set-vars') ) {
1882- $sql = "SET " . $self->prop('set-vars');
1883+ if ( my $var = $self->prop('set-vars') ) {
1884+ $sql = "SET $var";
1885 PTDEBUG && _d($dbh, ':', $sql);
1886 eval { $dbh->do($sql) };
1887 if ( $EVAL_ERROR ) {
1888- die $EVAL_ERROR;
1889+ die "Error setting $var: $EVAL_ERROR";
1890 }
1891 }
1892 }
1893@@ -1901,6 +1903,7 @@
1894 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
1895 hostname => '',
1896 set => $args{set},
1897+ NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
1898 dbh_set => 0,
1899 OptionParser => $o,
1900 DSNParser => $dp,
1901@@ -1938,7 +1941,10 @@
1902
1903 PTDEBUG && _d($dbh, 'Setting dbh');
1904
1905- $dbh->{FetchHashKeyName} = 'NAME_lc';
1906+ if ( !exists $self->{NAME_lc}
1907+ || (defined $self->{NAME_lc} && $self->{NAME_lc}) ) {
1908+ $dbh->{FetchHashKeyName} = 'NAME_lc';
1909+ }
1910
1911 my $sql = 'SELECT @@hostname, @@server_id';
1912 PTDEBUG && _d($dbh, $sql);
1913@@ -3030,8 +3036,31 @@
1914 use English qw(-no_match_vars);
1915 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1916
1917+sub check_recursion_method {
1918+ my ($methods) = @_;
1919+
1920+ if ( @$methods != 1 ) {
1921+ if ( grep({ !m/processlist|hosts/i } @$methods)
1922+ && $methods->[0] !~ /^dsn=/i )
1923+ {
1924+ die "Invalid combination of recursion methods: "
1925+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
1926+ . "Only hosts and processlist may be combined.\n"
1927+ }
1928+ }
1929+ else {
1930+ my ($method) = @$methods;
1931+ die "Invalid recursion method: " . ( $method || 'undef' )
1932+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
1933+ }
1934+}
1935+
1936 sub new {
1937 my ( $class, %args ) = @_;
1938+ my @required_args = qw(OptionParser DSNParser Quoter);
1939+ foreach my $arg ( @required_args ) {
1940+ die "I need a $arg argument" unless $args{$arg};
1941+ }
1942 my $self = {
1943 %args,
1944 replication_thread => {},
1945@@ -3041,28 +3070,27 @@
1946
1947 sub get_slaves {
1948 my ($self, %args) = @_;
1949- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
1950+ my @required_args = qw(make_cxn);
1951 foreach my $arg ( @required_args ) {
1952 die "I need a $arg argument" unless $args{$arg};
1953 }
1954- my ($make_cxn, $o, $dp) = @args{@required_args};
1955-
1956- my $slaves = [];
1957- my $method = $o->get('recursion-method');
1958- PTDEBUG && _d('Slave recursion method:', $method);
1959- if ( !$method || $method =~ m/processlist|hosts/i ) {
1960+ my ($make_cxn) = @args{@required_args};
1961+
1962+ my $slaves = [];
1963+ my $dp = $self->{DSNParser};
1964+ my $methods = $self->_resolve_recursion_methods($args{dsn});
1965+
1966+ if ( grep { m/processlist|hosts/i } @$methods ) {
1967 my @required_args = qw(dbh dsn);
1968 foreach my $arg ( @required_args ) {
1969 die "I need a $arg argument" unless $args{$arg};
1970 }
1971 my ($dbh, $dsn) = @args{@required_args};
1972+
1973 $self->recurse_to_slaves(
1974- { dbh => $dbh,
1975- dsn => $dsn,
1976- dsn_parser => $dp,
1977- recurse => $o->get('recurse'),
1978- method => $o->get('recursion-method'),
1979- callback => sub {
1980+ { dbh => $dbh,
1981+ dsn => $dsn,
1982+ callback => sub {
1983 my ( $dsn, $dbh, $level, $parent ) = @_;
1984 return unless $level;
1985 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
1986@@ -3072,31 +3100,48 @@
1987 }
1988 );
1989 }
1990- elsif ( $method =~ m/^dsn=/i ) {
1991- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
1992+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
1993+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
1994 $slaves = $self->get_cxn_from_dsn_table(
1995 %args,
1996 dsn_table_dsn => $dsn_table_dsn,
1997 );
1998 }
1999- elsif ( $method =~ m/none/i ) {
2000+ elsif ( $methods->[0] =~ m/none/i ) {
2001 PTDEBUG && _d('Not getting to slaves');
2002 }
2003 else {
2004- die "Invalid --recursion-method: $method. Valid values are: "
2005- . "dsn=DSN, hosts, or processlist.\n";
2006+ die "Unexpected recursion methods: @$methods";
2007 }
2008-
2009+
2010 return $slaves;
2011 }
2012
2013+sub _resolve_recursion_methods {
2014+ my ($self, $dsn) = @_;
2015+ my $o = $self->{OptionParser};
2016+ if ( $o->got('recursion-method') ) {
2017+ return $o->get('recursion-method');
2018+ }
2019+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
2020+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
2021+ return [qw(hosts)];
2022+ }
2023+ else {
2024+ return $o->get('recursion-method');
2025+ }
2026+}
2027+
2028 sub recurse_to_slaves {
2029 my ( $self, $args, $level ) = @_;
2030 $level ||= 0;
2031- my $dp = $args->{dsn_parser};
2032- my $dsn = $args->{dsn};
2033+ my $dp = $self->{DSNParser};
2034+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
2035+ my $dsn = $args->{dsn};
2036
2037- if ( lc($args->{method} || '') eq 'none' ) {
2038+ my $methods = $self->_resolve_recursion_methods($dsn);
2039+ PTDEBUG && _d('Recursion methods:', @$methods);
2040+ if ( lc($methods->[0]) eq 'none' ) {
2041 PTDEBUG && _d('Not recursing to slaves');
2042 return;
2043 }
2044@@ -3131,11 +3176,11 @@
2045
2046 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
2047
2048- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
2049+ if ( !defined $recurse || $level < $recurse ) {
2050
2051 my @slaves =
2052 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
2053- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
2054+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
2055
2056 foreach my $slave ( @slaves ) {
2057 PTDEBUG && _d('Recursing from',
2058@@ -3147,25 +3192,14 @@
2059 }
2060
2061 sub find_slave_hosts {
2062- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
2063+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
2064
2065- my @methods = qw(processlist hosts);
2066- if ( $method ) {
2067- @methods = grep { $_ ne $method } @methods;
2068- unshift @methods, $method;
2069- }
2070- else {
2071- if ( ($dsn->{P} || 3306) != 3306 ) {
2072- PTDEBUG && _d('Port number is non-standard; using only hosts method');
2073- @methods = qw(hosts);
2074- }
2075- }
2076 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
2077- 'using methods', @methods);
2078+ 'using methods', @$methods);
2079
2080 my @slaves;
2081 METHOD:
2082- foreach my $method ( @methods ) {
2083+ foreach my $method ( @$methods ) {
2084 my $find_slaves = "_find_slaves_by_$method";
2085 PTDEBUG && _d('Finding slaves with', $find_slaves);
2086 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
2087@@ -3668,13 +3702,16 @@
2088
2089 sub get_cxn_from_dsn_table {
2090 my ($self, %args) = @_;
2091- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
2092+ my @required_args = qw(dsn_table_dsn make_cxn);
2093 foreach my $arg ( @required_args ) {
2094 die "I need a $arg argument" unless $args{$arg};
2095 }
2096- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
2097+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
2098 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
2099
2100+ my $dp = $self->{DSNParser};
2101+ my $q = $self->{Quoter};
2102+
2103 my $dsn = $dp->parse($dsn_table_dsn);
2104 my $dsn_table;
2105 if ( $dsn->{D} && $dsn->{t} ) {
2106@@ -6856,6 +6893,13 @@
2107 }
2108 }
2109
2110+ eval {
2111+ MasterSlave::check_recursion_method($o->get('recursion-method'));
2112+ };
2113+ if ( $EVAL_ERROR ) {
2114+ $o->save_error("Invalid --recursion-method: $EVAL_ERROR")
2115+ }
2116+
2117 $o->usage_or_errors();
2118
2119 # ########################################################################
2120@@ -7006,7 +7050,11 @@
2121 my $q = new Quoter();
2122 my $tp = new TableParser(Quoter => $q);
2123 my $rc = new RowChecksum(Quoter=> $q, OptionParser => $o);
2124- my $ms = new MasterSlave();
2125+ my $ms = new MasterSlave(
2126+ OptionParser => $o,
2127+ DSNParser => $dp,
2128+ Quoter => $q,
2129+ );
2130
2131 my $slaves; # all slaves (that we can find)
2132 my $slave_lag_cxns; # slaves whose lag we'll check
2133@@ -7026,12 +7074,9 @@
2134 # Find and connect to slaves.
2135 # #####################################################################
2136 $slaves = $ms->get_slaves(
2137- dbh => $master_dbh,
2138- dsn => $master_dsn,
2139- OptionParser => $o,
2140- DSNParser => $dp,
2141- Quoter => $q,
2142- make_cxn => sub {
2143+ dbh => $master_dbh,
2144+ dsn => $master_dsn,
2145+ make_cxn => sub {
2146 return $make_cxn->(@_, prev_dsn => $master_cxn->dsn());
2147 },
2148 );
2149@@ -9347,7 +9392,7 @@
2150
2151 =item --recursion-method
2152
2153-type: string
2154+type: array; default: processlist,hosts
2155
2156 Preferred recursion method for discovering replicas. Possible methods are:
2157
2158
2159=== modified file 'bin/pt-table-sync'
2160--- bin/pt-table-sync 2012-07-31 09:46:13 +0000
2161+++ bin/pt-table-sync 2012-08-02 17:46:19 +0000
2162@@ -1860,7 +1860,7 @@
2163 PTDEBUG && _d($dbh, $sql);
2164 my ($sql_mode) = eval { $dbh->selectrow_array($sql) };
2165 if ( $EVAL_ERROR ) {
2166- die $EVAL_ERROR;
2167+ die "Error getting the current SQL_MODE: $EVAL_ERROR";
2168 }
2169
2170 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
2171@@ -1870,15 +1870,17 @@
2172 PTDEBUG && _d($dbh, $sql);
2173 eval { $dbh->do($sql) };
2174 if ( $EVAL_ERROR ) {
2175- die $EVAL_ERROR;
2176+ die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE"
2177+ . ($sql_mode ? " and $sql_mode" : '')
2178+ . ": $EVAL_ERROR";
2179 }
2180
2181- if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
2182- $sql = "/*!40101 SET NAMES $charset*/";
2183+ if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
2184+ $sql = qq{/*!40101 SET NAMES "$charset"*/};
2185 PTDEBUG && _d($dbh, ':', $sql);
2186 eval { $dbh->do($sql) };
2187 if ( $EVAL_ERROR ) {
2188- die $EVAL_ERROR;
2189+ die "Error setting NAMES to $charset: $EVAL_ERROR";
2190 }
2191 PTDEBUG && _d('Enabling charset for STDOUT');
2192 if ( $charset eq 'utf8' ) {
2193@@ -1890,12 +1892,12 @@
2194 }
2195 }
2196
2197- if ( $self->prop('set-vars') ) {
2198- $sql = "SET " . $self->prop('set-vars');
2199+ if ( my $var = $self->prop('set-vars') ) {
2200+ $sql = "SET $var";
2201 PTDEBUG && _d($dbh, ':', $sql);
2202 eval { $dbh->do($sql) };
2203 if ( $EVAL_ERROR ) {
2204- die $EVAL_ERROR;
2205+ die "Error setting $var: $EVAL_ERROR";
2206 }
2207 }
2208 }
2209@@ -6454,8 +6456,31 @@
2210 use English qw(-no_match_vars);
2211 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
2212
2213+sub check_recursion_method {
2214+ my ($methods) = @_;
2215+
2216+ if ( @$methods != 1 ) {
2217+ if ( grep({ !m/processlist|hosts/i } @$methods)
2218+ && $methods->[0] !~ /^dsn=/i )
2219+ {
2220+ die "Invalid combination of recursion methods: "
2221+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
2222+ . "Only hosts and processlist may be combined.\n"
2223+ }
2224+ }
2225+ else {
2226+ my ($method) = @$methods;
2227+ die "Invalid recursion method: " . ( $method || 'undef' )
2228+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
2229+ }
2230+}
2231+
2232 sub new {
2233 my ( $class, %args ) = @_;
2234+ my @required_args = qw(OptionParser DSNParser Quoter);
2235+ foreach my $arg ( @required_args ) {
2236+ die "I need a $arg argument" unless $args{$arg};
2237+ }
2238 my $self = {
2239 %args,
2240 replication_thread => {},
2241@@ -6465,28 +6490,27 @@
2242
2243 sub get_slaves {
2244 my ($self, %args) = @_;
2245- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
2246+ my @required_args = qw(make_cxn);
2247 foreach my $arg ( @required_args ) {
2248 die "I need a $arg argument" unless $args{$arg};
2249 }
2250- my ($make_cxn, $o, $dp) = @args{@required_args};
2251-
2252- my $slaves = [];
2253- my $method = $o->get('recursion-method');
2254- PTDEBUG && _d('Slave recursion method:', $method);
2255- if ( !$method || $method =~ m/processlist|hosts/i ) {
2256+ my ($make_cxn) = @args{@required_args};
2257+
2258+ my $slaves = [];
2259+ my $dp = $self->{DSNParser};
2260+ my $methods = $self->_resolve_recursion_methods($args{dsn});
2261+
2262+ if ( grep { m/processlist|hosts/i } @$methods ) {
2263 my @required_args = qw(dbh dsn);
2264 foreach my $arg ( @required_args ) {
2265 die "I need a $arg argument" unless $args{$arg};
2266 }
2267 my ($dbh, $dsn) = @args{@required_args};
2268+
2269 $self->recurse_to_slaves(
2270- { dbh => $dbh,
2271- dsn => $dsn,
2272- dsn_parser => $dp,
2273- recurse => $o->get('recurse'),
2274- method => $o->get('recursion-method'),
2275- callback => sub {
2276+ { dbh => $dbh,
2277+ dsn => $dsn,
2278+ callback => sub {
2279 my ( $dsn, $dbh, $level, $parent ) = @_;
2280 return unless $level;
2281 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
2282@@ -6496,31 +6520,48 @@
2283 }
2284 );
2285 }
2286- elsif ( $method =~ m/^dsn=/i ) {
2287- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
2288+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
2289+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
2290 $slaves = $self->get_cxn_from_dsn_table(
2291 %args,
2292 dsn_table_dsn => $dsn_table_dsn,
2293 );
2294 }
2295- elsif ( $method =~ m/none/i ) {
2296+ elsif ( $methods->[0] =~ m/none/i ) {
2297 PTDEBUG && _d('Not getting to slaves');
2298 }
2299 else {
2300- die "Invalid --recursion-method: $method. Valid values are: "
2301- . "dsn=DSN, hosts, or processlist.\n";
2302+ die "Unexpected recursion methods: @$methods";
2303 }
2304-
2305+
2306 return $slaves;
2307 }
2308
2309+sub _resolve_recursion_methods {
2310+ my ($self, $dsn) = @_;
2311+ my $o = $self->{OptionParser};
2312+ if ( $o->got('recursion-method') ) {
2313+ return $o->get('recursion-method');
2314+ }
2315+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
2316+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
2317+ return [qw(hosts)];
2318+ }
2319+ else {
2320+ return $o->get('recursion-method');
2321+ }
2322+}
2323+
2324 sub recurse_to_slaves {
2325 my ( $self, $args, $level ) = @_;
2326 $level ||= 0;
2327- my $dp = $args->{dsn_parser};
2328- my $dsn = $args->{dsn};
2329+ my $dp = $self->{DSNParser};
2330+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
2331+ my $dsn = $args->{dsn};
2332
2333- if ( lc($args->{method} || '') eq 'none' ) {
2334+ my $methods = $self->_resolve_recursion_methods($dsn);
2335+ PTDEBUG && _d('Recursion methods:', @$methods);
2336+ if ( lc($methods->[0]) eq 'none' ) {
2337 PTDEBUG && _d('Not recursing to slaves');
2338 return;
2339 }
2340@@ -6555,11 +6596,11 @@
2341
2342 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
2343
2344- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
2345+ if ( !defined $recurse || $level < $recurse ) {
2346
2347 my @slaves =
2348 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
2349- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
2350+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
2351
2352 foreach my $slave ( @slaves ) {
2353 PTDEBUG && _d('Recursing from',
2354@@ -6571,25 +6612,14 @@
2355 }
2356
2357 sub find_slave_hosts {
2358- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
2359+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
2360
2361- my @methods = qw(processlist hosts);
2362- if ( $method ) {
2363- @methods = grep { $_ ne $method } @methods;
2364- unshift @methods, $method;
2365- }
2366- else {
2367- if ( ($dsn->{P} || 3306) != 3306 ) {
2368- PTDEBUG && _d('Port number is non-standard; using only hosts method');
2369- @methods = qw(hosts);
2370- }
2371- }
2372 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
2373- 'using methods', @methods);
2374+ 'using methods', @$methods);
2375
2376 my @slaves;
2377 METHOD:
2378- foreach my $method ( @methods ) {
2379+ foreach my $method ( @$methods ) {
2380 my $find_slaves = "_find_slaves_by_$method";
2381 PTDEBUG && _d('Finding slaves with', $find_slaves);
2382 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
2383@@ -7092,13 +7122,16 @@
2384
2385 sub get_cxn_from_dsn_table {
2386 my ($self, %args) = @_;
2387- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
2388+ my @required_args = qw(dsn_table_dsn make_cxn);
2389 foreach my $arg ( @required_args ) {
2390 die "I need a $arg argument" unless $args{$arg};
2391 }
2392- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
2393+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
2394 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
2395
2396+ my $dp = $self->{DSNParser};
2397+ my $q = $self->{Quoter};
2398+
2399 my $dsn = $dp->parse($dsn_table_dsn);
2400 my $dsn_table;
2401 if ( $dsn->{D} && $dsn->{t} ) {
2402@@ -8320,6 +8353,13 @@
2403 return 0;
2404 }
2405
2406+ eval {
2407+ MasterSlave::check_recursion_method($o->get('recursion-method'));
2408+ };
2409+ if ( $EVAL_ERROR ) {
2410+ $o->save_error("Invalid --recursion-method: $EVAL_ERROR")
2411+ }
2412+
2413 $o->usage_or_errors();
2414
2415 # ########################################################################
2416@@ -8338,7 +8378,7 @@
2417 # Do the work.
2418 # ########################################################################
2419 my $tp = new TableParser( Quoter => $q );
2420- my $ms = new MasterSlave();
2421+ my $ms = new MasterSlave(OptionParser=>$o,DSNParser=>$dp,Quoter=>$q);
2422 my $du = new MySQLDump( cache => 0 );
2423 my $rt = new Retry();
2424 my $chunker = new TableChunker( Quoter => $q, TableParser => $tp );
2425@@ -8736,11 +8776,10 @@
2426 # then sync them.
2427 else {
2428 $ms->recurse_to_slaves(
2429- { dbh => $src->{dbh},
2430- dsn => $src->{dsn},
2431- dsn_parser => $dp,
2432- recurse => 1,
2433- callback => sub {
2434+ { dbh => $src->{dbh},
2435+ dsn => $src->{dsn},
2436+ recurse => 1,
2437+ callback => sub {
2438 my ( $dsn, $dbh, $level, $parent ) = @_;
2439 my $all_diffs = $checksum->find_replication_differences(
2440 $dbh, $o->get('replicate'));
2441@@ -8809,7 +8848,6 @@
2442
2443 return;
2444 }, # recurse_to_slaves() callback
2445- method => $o->get('recursion-method'),
2446 },
2447 );
2448 } # DSN is master
2449@@ -10767,7 +10805,7 @@
2450
2451 =item --recursion-method
2452
2453-type: string
2454+type: array; default: processlist,hosts
2455
2456 Preferred recursion method used to find slaves.
2457
2458
2459=== modified file 'lib/MasterSlave.pm'
2460--- lib/MasterSlave.pm 2012-07-13 04:16:04 +0000
2461+++ lib/MasterSlave.pm 2012-08-02 17:46:19 +0000
2462@@ -1,4 +1,4 @@
2463-# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc.
2464+# This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc.
2465 # Feedback and improvements are welcome.
2466 #
2467 # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
2468@@ -27,8 +27,33 @@
2469 use English qw(-no_match_vars);
2470 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
2471
2472+# Sub: check_recursion_method
2473+# Check that the arrayref of recursion methods passed in is valid
2474+sub check_recursion_method {
2475+ my ($methods) = @_;
2476+
2477+ if ( @$methods != 1 ) {
2478+ if ( grep({ !m/processlist|hosts/i } @$methods)
2479+ && $methods->[0] !~ /^dsn=/i )
2480+ {
2481+ die "Invalid combination of recursion methods: "
2482+ . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". "
2483+ . "Only hosts and processlist may be combined.\n"
2484+ }
2485+ }
2486+ else {
2487+ my ($method) = @$methods;
2488+ die "Invalid recursion method: " . ( $method || 'undef' )
2489+ unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i;
2490+ }
2491+}
2492+
2493 sub new {
2494 my ( $class, %args ) = @_;
2495+ my @required_args = qw(OptionParser DSNParser Quoter);
2496+ foreach my $arg ( @required_args ) {
2497+ die "I need a $arg argument" unless $args{$arg};
2498+ }
2499 my $self = {
2500 %args,
2501 replication_thread => {},
2502@@ -38,28 +63,27 @@
2503
2504 sub get_slaves {
2505 my ($self, %args) = @_;
2506- my @required_args = qw(make_cxn OptionParser DSNParser Quoter);
2507+ my @required_args = qw(make_cxn);
2508 foreach my $arg ( @required_args ) {
2509 die "I need a $arg argument" unless $args{$arg};
2510 }
2511- my ($make_cxn, $o, $dp) = @args{@required_args};
2512-
2513- my $slaves = [];
2514- my $method = $o->get('recursion-method');
2515- PTDEBUG && _d('Slave recursion method:', $method);
2516- if ( !$method || $method =~ m/processlist|hosts/i ) {
2517+ my ($make_cxn) = @args{@required_args};
2518+
2519+ my $slaves = [];
2520+ my $dp = $self->{DSNParser};
2521+ my $methods = $self->_resolve_recursion_methods($args{dsn});
2522+
2523+ if ( grep { m/processlist|hosts/i } @$methods ) {
2524 my @required_args = qw(dbh dsn);
2525 foreach my $arg ( @required_args ) {
2526 die "I need a $arg argument" unless $args{$arg};
2527 }
2528 my ($dbh, $dsn) = @args{@required_args};
2529+
2530 $self->recurse_to_slaves(
2531- { dbh => $dbh,
2532- dsn => $dsn,
2533- dsn_parser => $dp,
2534- recurse => $o->get('recurse'),
2535- method => $o->get('recursion-method'),
2536- callback => sub {
2537+ { dbh => $dbh,
2538+ dsn => $dsn,
2539+ callback => sub {
2540 my ( $dsn, $dbh, $level, $parent ) = @_;
2541 return unless $level;
2542 PTDEBUG && _d('Found slave:', $dp->as_string($dsn));
2543@@ -69,25 +93,41 @@
2544 }
2545 );
2546 }
2547- elsif ( $method =~ m/^dsn=/i ) {
2548- my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
2549+ elsif ( $methods->[0] =~ m/^dsn=/i ) {
2550+ (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i;
2551 $slaves = $self->get_cxn_from_dsn_table(
2552 %args,
2553 dsn_table_dsn => $dsn_table_dsn,
2554 );
2555 }
2556- elsif ( $method =~ m/none/i ) {
2557- # https://bugs.launchpad.net/percona-toolkit/+bug/987694
2558+ elsif ( $methods->[0] =~ m/none/i ) {
2559 PTDEBUG && _d('Not getting to slaves');
2560 }
2561 else {
2562- die "Invalid --recursion-method: $method. Valid values are: "
2563- . "dsn=DSN, hosts, or processlist.\n";
2564+ die "Unexpected recursion methods: @$methods";
2565 }
2566-
2567+
2568 return $slaves;
2569 }
2570
2571+sub _resolve_recursion_methods {
2572+ my ($self, $dsn) = @_;
2573+ my $o = $self->{OptionParser};
2574+ if ( $o->got('recursion-method') ) {
2575+ # Use whatever the user explicitly gave on the command line.
2576+ return $o->get('recursion-method');
2577+ }
2578+ elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) {
2579+ # Special case: hosts is best when port is non-standard.
2580+ PTDEBUG && _d('Port number is non-standard; using only hosts method');
2581+ return [qw(hosts)];
2582+ }
2583+ else {
2584+ # Use the option's default.
2585+ return $o->get('recursion-method');
2586+ }
2587+}
2588+
2589 # Sub: recurse_to_slaves
2590 # Descend to slaves by examining SHOW SLAVE HOSTS.
2591 # The callback gets the slave's DSN, dbh, parent, and the recursion level
2592@@ -111,10 +151,16 @@
2593 sub recurse_to_slaves {
2594 my ( $self, $args, $level ) = @_;
2595 $level ||= 0;
2596- my $dp = $args->{dsn_parser};
2597- my $dsn = $args->{dsn};
2598+ my $dp = $self->{DSNParser};
2599+ my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse');
2600+ my $dsn = $args->{dsn};
2601
2602- if ( lc($args->{method} || '') eq 'none' ) {
2603+ # Re-resolve the recursion methods for each slave. In most cases
2604+ # it won't change, but it could if one slave uses standard port (3306)
2605+ # and another does not.
2606+ my $methods = $self->_resolve_recursion_methods($dsn);
2607+ PTDEBUG && _d('Recursion methods:', @$methods);
2608+ if ( lc($methods->[0]) eq 'none' ) {
2609 # https://bugs.launchpad.net/percona-toolkit/+bug/987694
2610 PTDEBUG && _d('Not recursing to slaves');
2611 return;
2612@@ -154,13 +200,13 @@
2613 # Call the callback!
2614 $args->{callback}->($dsn, $dbh, $level, $args->{parent});
2615
2616- if ( !defined $args->{recurse} || $level < $args->{recurse} ) {
2617+ if ( !defined $recurse || $level < $recurse ) {
2618
2619 # Find the slave hosts. Eliminate hosts that aren't slaves of me (as
2620 # revealed by server_id and master_id).
2621 my @slaves =
2622 grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves.
2623- $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method});
2624+ $self->find_slave_hosts($dp, $dbh, $dsn, $methods);
2625
2626 foreach my $slave ( @slaves ) {
2627 PTDEBUG && _d('Recursing from',
2628@@ -184,27 +230,14 @@
2629 # If a method is given, it becomes the preferred (first tried) method.
2630 # Searching stops as soon as a method finds slaves.
2631 sub find_slave_hosts {
2632- my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_;
2633+ my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_;
2634
2635- my @methods = qw(processlist hosts);
2636- if ( $method ) {
2637- # Remove all but the given method.
2638- @methods = grep { $_ ne $method } @methods;
2639- # Add given method to the head of the list.
2640- unshift @methods, $method;
2641- }
2642- else {
2643- if ( ($dsn->{P} || 3306) != 3306 ) {
2644- PTDEBUG && _d('Port number is non-standard; using only hosts method');
2645- @methods = qw(hosts);
2646- }
2647- }
2648 PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn),
2649- 'using methods', @methods);
2650+ 'using methods', @$methods);
2651
2652 my @slaves;
2653 METHOD:
2654- foreach my $method ( @methods ) {
2655+ foreach my $method ( @$methods ) {
2656 my $find_slaves = "_find_slaves_by_$method";
2657 PTDEBUG && _d('Finding slaves with', $find_slaves);
2658 @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn);
2659@@ -849,13 +882,16 @@
2660
2661 sub get_cxn_from_dsn_table {
2662 my ($self, %args) = @_;
2663- my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter);
2664+ my @required_args = qw(dsn_table_dsn make_cxn);
2665 foreach my $arg ( @required_args ) {
2666 die "I need a $arg argument" unless $args{$arg};
2667 }
2668- my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args};
2669+ my ($dsn_table_dsn, $make_cxn) = @args{@required_args};
2670 PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
2671
2672+ my $dp = $self->{DSNParser};
2673+ my $q = $self->{Quoter};
2674+
2675 my $dsn = $dp->parse($dsn_table_dsn);
2676 my $dsn_table;
2677 if ( $dsn->{D} && $dsn->{t} ) {
2678
2679=== modified file 't/lib/MasterSlave.t'
2680--- t/lib/MasterSlave.t 2012-07-23 15:28:25 +0000
2681+++ t/lib/MasterSlave.t 2012-08-02 17:46:19 +0000
2682@@ -9,7 +9,7 @@
2683 use strict;
2684 use warnings FATAL => 'all';
2685 use English qw(-no_match_vars);
2686-use Test::More tests => 52;
2687+use Test::More;
2688
2689 use MasterSlave;
2690 use DSNParser;
2691@@ -20,13 +20,13 @@
2692 use Sandbox;
2693 use PerconaTest;
2694
2695-my $ms = new MasterSlave();
2696+use Data::Dumper;
2697+
2698 my $dp = new DSNParser(opts=>$dsn_opts);
2699 my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
2700
2701 my $master_dbh = $sb->get_dbh_for('master');
2702 my $slave_dbh = $sb->get_dbh_for('slave1');
2703-
2704 my $master_dsn = {
2705 h => '127.1',
2706 P => '12345',
2707@@ -34,25 +34,29 @@
2708 p => 'msandbox',
2709 };
2710
2711-# ############################################################################
2712-# get_slaves() wrapper around recurse_to_slaves()
2713-# ############################################################################
2714 my $q = new Quoter;
2715 my $o = new OptionParser(description => 'MasterSlave');
2716 $o->get_specs("$trunk/bin/pt-table-checksum");
2717
2718+my $ms = new MasterSlave(
2719+ OptionParser => $o,
2720+ DSNParser => $dp,
2721+ Quoter => $q,
2722+);
2723+
2724+# ############################################################################
2725+# get_slaves() wrapper around recurse_to_slaves()
2726+# ############################################################################
2727+
2728 SKIP: {
2729 skip "Cannot connect to sandbox master", 2 unless $master_dbh;
2730- @ARGV = ();
2731+ local @ARGV = ();
2732 $o->get_opts();
2733-
2734+
2735 my $slaves = $ms->get_slaves(
2736- dbh => $master_dbh,
2737- dsn => $master_dsn,
2738- OptionParser => $o,
2739- DSNParser => $dp,
2740- Quoter => $q,
2741- make_cxn => sub {
2742+ dbh => $master_dbh,
2743+ dsn => $master_dsn,
2744+ make_cxn => sub {
2745 my $cxn = new Cxn(
2746 @_,
2747 DSNParser => $dp,
2748@@ -78,7 +82,7 @@
2749 master_id => 12345,
2750 source => 'hosts',
2751 },
2752- 'get_slaves() from recurse_to_slaves()'
2753+ 'get_slaves() from recurse_to_slaves() with a default --recursion-method'
2754 );
2755
2756 my ($id) = $slaves->[0]->dbh()->selectrow_array('SELECT @@SERVER_ID');
2757@@ -93,16 +97,13 @@
2758 # and ignore it. This tests nonetheless that "processlist" isn't
2759 # misspelled, which would cause the sub to die.
2760 # https://bugs.launchpad.net/percona-toolkit/+bug/921802
2761- @ARGV = ('--recursion-method', 'processlist');
2762+ local @ARGV = ('--recursion-method', 'processlist');
2763 $o->get_opts();
2764
2765 $slaves = $ms->get_slaves(
2766- OptionParser => $o,
2767- DSNParser => $dp,
2768- Quoter => $q,
2769- dbh => $master_dbh,
2770- dsn => $master_dsn,
2771- make_cxn => sub {
2772+ dbh => $master_dbh,
2773+ dsn => $master_dsn,
2774+ make_cxn => sub {
2775 my $cxn = new Cxn(
2776 @_,
2777 DSNParser => $dp,
2778@@ -146,12 +147,9 @@
2779 throws_ok(
2780 sub {
2781 $slaves = $ms->get_slaves(
2782- OptionParser => $o,
2783- DSNParser => $dp,
2784- Quoter => $q,
2785- dbh => $ro_dbh,
2786- dsn => $ro_dsn,
2787- make_cxn => sub {
2788+ dbh => $ro_dbh,
2789+ dsn => $ro_dsn,
2790+ make_cxn => sub {
2791 my $cxn = new Cxn(
2792 @_,
2793 DSNParser => $dp,
2794@@ -169,12 +167,9 @@
2795 @ARGV = ('--recursion-method', 'none');
2796 $o->get_opts();
2797 $slaves = $ms->get_slaves(
2798- OptionParser => $o,
2799- DSNParser => $dp,
2800- Quoter => $q,
2801- dbh => $ro_dbh,
2802- dsn => $ro_dsn,
2803- make_cxn => sub {
2804+ dbh => $ro_dbh,
2805+ dsn => $ro_dsn,
2806+ make_cxn => sub {
2807 my $cxn = new Cxn(
2808 @_,
2809 DSNParser => $dp,
2810@@ -190,14 +185,13 @@
2811 "No privs needed for --recursion-method=none (bug 987694)"
2812 );
2813
2814+ @ARGV = ('--recursion-method', 'none', '--recurse', '2');
2815+ $o->get_opts();
2816 my $recursed = 0;
2817 $ms->recurse_to_slaves(
2818- { dsn_parser => $dp,
2819- dbh => $ro_dbh,
2820- dsn => $ro_dsn,
2821- recurse => 2,
2822- callback => sub { $recursed++ },
2823- method => 'none',
2824+ { dbh => $ro_dbh,
2825+ dsn => $ro_dsn,
2826+ callback => sub { $recursed++ },
2827 });
2828 is(
2829 $recursed,
2830@@ -231,10 +225,10 @@
2831 diag(`$trunk/sandbox/stop-sandbox $port >/dev/null 2>&1`);
2832 }
2833 }
2834-diag(`$trunk/sandbox/start-sandbox master 2900 >/dev/null 2>&1`);
2835-diag(`$trunk/sandbox/start-sandbox slave 2903 2900 >/dev/null 2>&1`);
2836-diag(`$trunk/sandbox/start-sandbox slave 2901 2900 >/dev/null 2>&1`);
2837-diag(`$trunk/sandbox/start-sandbox slave 2902 2901 >/dev/null 2>&1`);
2838+diag(`$trunk/sandbox/start-sandbox master 2900`);
2839+diag(`$trunk/sandbox/start-sandbox slave 2903 2900`);
2840+diag(`$trunk/sandbox/start-sandbox slave 2901 2900`);
2841+diag(`$trunk/sandbox/start-sandbox slave 2902 2901`);
2842
2843 # I discovered something weird while updating this test. Above, you see that
2844 # slave2 is started first, then the others. Before, slave2 was started last,
2845@@ -279,11 +273,12 @@
2846 . " from $dsn->{source}");
2847 };
2848
2849+@ARGV = ('--recurse', '2');
2850+$o->get_opts();
2851+
2852 $ms->recurse_to_slaves(
2853- { dsn_parser => $dp,
2854- dbh => $dbh,
2855+ { dbh => $dbh,
2856 dsn => $dsn,
2857- recurse => 2,
2858 callback => $callback,
2859 skip_callback => $skip_callback,
2860 });
2861@@ -707,13 +702,40 @@
2862 'dbh created from DSN table works'
2863 );
2864
2865+# ############################################################################
2866+# Invalid recursion methods are caught
2867+# ############################################################################
2868+local $EVAL_ERROR;
2869+eval {
2870+ MasterSlave::check_recursion_method([qw(stuff)])
2871+};
2872+
2873+like(
2874+ $EVAL_ERROR,
2875+ qr/Invalid recursion method: stuff/,
2876+ "--recursion-method stuff causes error"
2877+);
2878+
2879+local $EVAL_ERROR;
2880+eval {
2881+ MasterSlave::check_recursion_method([qw(processlist stuff)])
2882+};
2883+
2884+like(
2885+ $EVAL_ERROR,
2886+ qr/Invalid combination of recursion methods: processlist, stuff/,
2887+ "--recursion-method processlist,stuff causes error",
2888+);
2889+
2890 # #############################################################################
2891 # Done.
2892 # #############################################################################
2893 $sb->wipe_clean($master_dbh);
2894-diag(`$trunk/sandbox/stop-sandbox 2903 2902 2901 2900 >/dev/null 2>&1`);
2895+diag(`$trunk/sandbox/stop-sandbox 2903 2902 2901 2900`);
2896 diag(`/tmp/12346/use -e "set global read_only=1"`);
2897 diag(`/tmp/12347/use -e "set global read_only=1"`);
2898-diag(`$trunk/sandbox/test-env reset >/dev/null 2>&1`);
2899+$sb->wait_for_slaves();
2900+diag(`$trunk/sandbox/test-env reset`);
2901 ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
2902+done_testing;
2903 exit;
2904
2905=== modified file 't/pt-table-checksum/option_sanity.t'
2906--- t/pt-table-checksum/option_sanity.t 2012-05-25 21:34:58 +0000
2907+++ t/pt-table-checksum/option_sanity.t 2012-08-02 17:46:19 +0000
2908@@ -9,7 +9,7 @@
2909 use strict;
2910 use warnings FATAL => 'all';
2911 use English qw(-no_match_vars);
2912-use Test::More tests => 19;
2913+use Test::More;
2914
2915 use PerconaTest;
2916 shift @INC; # our unshift (above)
2917@@ -95,6 +95,18 @@
2918 "Default --replicate-check=TRUE"
2919 );
2920
2921+like(
2922+ $output,
2923+ qr/^\s+--recursion-method=a/m,
2924+ "--recursion-method is an array"
2925+);
2926+
2927+like(
2928+ $output,
2929+ qr/^\s+--recursion-method\s+processlist,hosts/m,
2930+ "Default --recursion-method is processlist,hosts"
2931+);
2932+
2933 # ############################################################################
2934 # Check opts that disable other opts.
2935 # ############################################################################
2936@@ -170,4 +182,4 @@
2937 # #############################################################################
2938 # Done.
2939 # #############################################################################
2940-exit;
2941+done_testing;

Subscribers

People subscribed via source and target branches