Merge lp:~percona-toolkit-dev/percona-toolkit/pt-osc-metadata-lock-bug-1113301 into lp:percona-toolkit/2.2

Proposed by Daniel Nichter
Status: Merged
Approved by: Daniel Nichter
Approved revision: 544
Merged at revision: 545
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/pt-osc-metadata-lock-bug-1113301
Merge into: lp:percona-toolkit/2.2
Diff against target: 7587 lines (+3695/-1178) (has conflicts)
85 files modified
bin/pt-archiver (+111/-15)
bin/pt-config-diff (+110/-13)
bin/pt-deadlock-logger (+110/-13)
bin/pt-duplicate-key-checker (+110/-13)
bin/pt-find (+110/-13)
bin/pt-fk-error-logger (+110/-13)
bin/pt-heartbeat (+110/-13)
bin/pt-index-usage (+110/-13)
bin/pt-kill (+110/-13)
bin/pt-online-schema-change (+593/-634)
bin/pt-query-advisor (+110/-13)
bin/pt-query-digest (+110/-13)
bin/pt-show-grants (+110/-13)
bin/pt-slave-delay (+110/-13)
bin/pt-slave-find (+110/-13)
bin/pt-slave-restart (+110/-13)
bin/pt-table-checksum (+115/-65)
bin/pt-table-sync (+110/-13)
bin/pt-table-usage (+116/-22)
bin/pt-upgrade (+110/-13)
bin/pt-variable-advisor (+110/-13)
bin/pt-visual-explain (+116/-22)
lib/DSNParser.pm (+54/-8)
lib/NibbleIterator.pm (+2/-2)
lib/OptionParser.pm (+40/-39)
sandbox/set-mysql (+23/-0)
t/lib/Cxn.t (+14/-2)
t/lib/DSNParser.t (+11/-5)
t/lib/KeySize.t (+4/-4)
t/lib/NibbleIterator.t (+3/-3)
t/lib/Percona/XtraDB/Cluster-no-PXC.t (+14/-2)
t/lib/Percona/XtraDB/Cluster.t (+14/-2)
t/lib/ReplicaLagWaiter.t (+14/-2)
t/pt-archiver/check_slave_lag.t (+10/-18)
t/pt-fifo-split/pt-fifo-split.t (+85/-12)
t/pt-online-schema-change/alter_active_table.t (+2/-2)
t/pt-online-schema-change/ansi_quotes.t (+3/-2)
t/pt-online-schema-change/basics.t (+1/-1)
t/pt-online-schema-change/bugs.t (+3/-2)
t/pt-online-schema-change/check_alter.t (+3/-2)
t/pt-online-schema-change/metadata_locks.t (+102/-0)
t/pt-online-schema-change/plugin.t (+83/-0)
t/pt-online-schema-change/privs.t (+3/-2)
t/pt-online-schema-change/pxc.t (+3/-5)
t/pt-online-schema-change/rename_columns.t (+3/-2)
t/pt-online-schema-change/samples/plugins/all_hooks.pm (+99/-0)
t/pt-online-schema-change/samples/plugins/block_create_triggers.pm (+46/-0)
t/pt-online-schema-change/samples/plugins/block_swap_tables.pm (+46/-0)
t/pt-online-schema-change/samples/stats-dry-run.txt (+3/-3)
t/pt-online-schema-change/samples/stats-execute-5.5.txt (+4/-4)
t/pt-online-schema-change/samples/stats-execute.txt (+3/-3)
t/pt-online-schema-change/sanity_checks.t (+1/-1)
t/pt-online-schema-change/skip_innodb.t (+1/-1)
t/pt-table-checksum/basics.t (+3/-3)
t/pt-table-checksum/bugs.t (+2/-2)
t/pt-table-checksum/char_chunking.t (+2/-2)
t/pt-table-checksum/chunk_index.t (+7/-7)
t/pt-table-checksum/chunk_size.t (+2/-2)
t/pt-table-checksum/create_replicate_table.t (+2/-2)
t/pt-table-checksum/error_handling.t (+3/-3)
t/pt-table-checksum/filters.t (+2/-2)
t/pt-table-checksum/float_precision.t (+2/-2)
t/pt-table-checksum/fnv_64.t (+2/-2)
t/pt-table-checksum/ignore_columns.t (+2/-2)
t/pt-table-checksum/issue_388.t (+2/-2)
t/pt-table-checksum/issue_47.t (+2/-2)
t/pt-table-checksum/issue_602.t (+2/-2)
t/pt-table-checksum/option_sanity.t (+5/-5)
t/pt-table-checksum/privs.t (+2/-2)
t/pt-table-checksum/progress.t (+2/-2)
t/pt-table-checksum/pxc.t (+2/-2)
t/pt-table-checksum/replication_filters.t (+6/-2)
t/pt-table-checksum/resume.t (+2/-2)
t/pt-table-checksum/run_time.t (+2/-2)
t/pt-table-checksum/skip_innodb.t (+2/-2)
t/pt-table-checksum/standard_options.t (+2/-2)
t/pt-table-checksum/throttle.t (+2/-2)
t/pt-table-sync/basics.t (+3/-3)
t/pt-table-sync/bugs.t (+2/-2)
t/pt-table-sync/filters.t (+2/-2)
t/pt-table-sync/issue_560.t (+1/-1)
t/pt-table-sync/issue_627.t (+1/-1)
t/pt-table-sync/issue_996.t (+1/-1)
t/pt-table-sync/triggers.t (+2/-2)
util/aspell.en.pws (+3/-0)
Text conflict in t/pt-fifo-split/pt-fifo-split.t
Text conflict in t/pt-table-checksum/replication_filters.t
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/pt-osc-metadata-lock-bug-1113301
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+151130@code.launchpad.net
To post a comment you must log in.
536. By Daniel Nichter

Retry rebuilding fk constraints. Update --retries docs.

537. By Daniel Nichter

Fix typo.

538. By Daniel Nichter

Fix --stats sample for < MySQL 5.5.

539. By Daniel Nichter

Fix bug 968596: rename NibbleIterator::chunk_size() to limit() and update ptc and pt-osc.

540. By Daniel Nichter

Fix t/pt-table-checksum/replication_filters.t again, better.

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

Fix t/pt-table-checksum/replication_filters.t again because my Mac is the odd ball.

542. By Daniel Nichter

Merge enhanced-set-vars.

543. By Daniel Nichter

Update DSNParser in all tools.

544. By Daniel Nichter

New, simpler RISKS section. Fix spelling errors.

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

All pt-osc tests are passing, in all 20 envs.

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 2013-02-27 00:01:17 +0000
3+++ bin/pt-archiver 2013-03-04 18:10:28 +0000
4@@ -712,6 +712,7 @@
5
6 use List::Util qw(max);
7 use Getopt::Long;
8+use Data::Dumper;
9
10 my $POD_link_re = '[LC]<"?([^">]+)"?>';
11
12@@ -1695,6 +1696,45 @@
13 );
14 };
15
16+sub set_vars {
17+ my ($self, $file) = @_;
18+ $file ||= $self->{file} || __FILE__;
19+
20+ my %user_vars;
21+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
22+ if ( $user_vars ) {
23+ foreach my $var_val ( @$user_vars ) {
24+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
25+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
26+ $user_vars{$var} = {
27+ val => $val,
28+ default => 0,
29+ };
30+ }
31+ }
32+
33+ my %default_vars;
34+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
35+ if ( $default_vars ) {
36+ %default_vars = map {
37+ my $var_val = $_;
38+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
39+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
40+ $var => {
41+ val => $val,
42+ default => 1,
43+ };
44+ } split("\n", $default_vars);
45+ }
46+
47+ my %vars = (
48+ %default_vars, # first the tool's defaults
49+ %user_vars, # then the user's which overwrite the defaults
50+ );
51+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
52+ return \%vars;
53+}
54+
55 sub _d {
56 my ($package, undef, $line) = caller 0;
57 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
58@@ -2397,7 +2437,7 @@
59
60 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
61 $sql = qq{/*!40101 SET NAMES "$charset"*/};
62- PTDEBUG && _d($dbh, ':', $sql);
63+ PTDEBUG && _d($dbh, $sql);
64 eval { $dbh->do($sql) };
65 if ( $EVAL_ERROR ) {
66 die "Error setting NAMES to $charset: $EVAL_ERROR";
67@@ -2412,13 +2452,8 @@
68 }
69 }
70
71- if ( my $var = $self->prop('set-vars') ) {
72- $sql = "SET $var";
73- PTDEBUG && _d($dbh, ':', $sql);
74- eval { $dbh->do($sql) };
75- if ( $EVAL_ERROR ) {
76- die "Error setting $var: $EVAL_ERROR";
77- }
78+ if ( my $vars = $self->prop('set-vars') ) {
79+ $self->set_vars($dbh, $vars);
80 }
81
82 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
83@@ -2493,6 +2528,57 @@
84 return \%new_dsn;
85 }
86
87+sub set_vars {
88+ my ($self, $dbh, $vars) = @_;
89+
90+ return unless $vars;
91+
92+ foreach my $var ( sort keys %$vars ) {
93+ my $val = $vars->{$var}->{val};
94+
95+ (my $quoted_var = $var) =~ s/_/\\_/;
96+ my ($var_exists, $current_val);
97+ eval {
98+ ($var_exists, $current_val) = $dbh->selectrow_array(
99+ "SHOW VARIABLES LIKE '$quoted_var'");
100+ };
101+ my $e = $EVAL_ERROR;
102+ if ( $e ) {
103+ PTDEBUG && _d($e);
104+ }
105+
106+ if ( $vars->{$var}->{default} && !$var_exists ) {
107+ PTDEBUG && _d('Not setting default var', $var,
108+ 'because it does not exist');
109+ next;
110+ }
111+
112+ if ( $current_val && $current_val eq $val ) {
113+ PTDEBUG && _d('Not setting var', $var, 'because its value',
114+ 'is already', $val);
115+ next;
116+ }
117+
118+ my $sql = "SET SESSION $var=$val";
119+ PTDEBUG && _d($dbh, $sql);
120+ eval { $dbh->do($sql) };
121+ if ( my $set_error = $EVAL_ERROR ) {
122+ chomp($set_error);
123+ $set_error =~ s/ at \S+ line \d+//;
124+ my $msg = "Error setting $var: $set_error";
125+ if ( $current_val ) {
126+ $msg .= " The current value for $var is $current_val. "
127+ . "If the variable is read only (not dynamic), specify "
128+ . "--set-vars $var=$current_val to avoid this warning, "
129+ . "else manually set the variable and restart MySQL.";
130+ }
131+ warn $msg . "\n\n";
132+ }
133+ }
134+
135+ return;
136+}
137+
138 sub _d {
139 my ($package, undef, $line) = caller 0;
140 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
141@@ -5335,7 +5421,7 @@
142 $o->get_opts();
143
144 my $dp = $o->DSNParser();
145- $dp->prop('set-vars', $o->get('set-vars'));
146+ $dp->prop('set-vars', $o->set_vars());
147
148 # Frequently used options.
149 $src = $o->get('source');
150@@ -7094,12 +7180,22 @@
151
152 =item --set-vars
153
154-type: string; default: wait_timeout=10000
155-
156-Set these MySQL variables.
157-
158-Specify any variables you want to be set immediately after connecting to MySQL.
159-These will be included in a C<SET> command.
160+type: Array
161+
162+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
163+
164+By default, the tool sets:
165+
166+=for comment ignore-pt-internal-value
167+MAGIC_set_vars
168+
169+ wait_timeout=10000
170+
171+Variables specified on the command line override these defaults. For
172+example, specifying C<--set-vars wait_timeout=500> overrides the default
173+value of C<10000>.
174+
175+The tool prints a warning and continues if a variable cannot be set.
176
177 =item --share-lock
178
179
180=== modified file 'bin/pt-config-diff'
181--- bin/pt-config-diff 2013-02-26 23:57:02 +0000
182+++ bin/pt-config-diff 2013-03-04 18:10:28 +0000
183@@ -712,6 +712,7 @@
184
185 use List::Util qw(max);
186 use Getopt::Long;
187+use Data::Dumper;
188
189 my $POD_link_re = '[LC]<"?([^">]+)"?>';
190
191@@ -1695,6 +1696,45 @@
192 );
193 };
194
195+sub set_vars {
196+ my ($self, $file) = @_;
197+ $file ||= $self->{file} || __FILE__;
198+
199+ my %user_vars;
200+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
201+ if ( $user_vars ) {
202+ foreach my $var_val ( @$user_vars ) {
203+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
204+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
205+ $user_vars{$var} = {
206+ val => $val,
207+ default => 0,
208+ };
209+ }
210+ }
211+
212+ my %default_vars;
213+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
214+ if ( $default_vars ) {
215+ %default_vars = map {
216+ my $var_val = $_;
217+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
218+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
219+ $var => {
220+ val => $val,
221+ default => 1,
222+ };
223+ } split("\n", $default_vars);
224+ }
225+
226+ my %vars = (
227+ %default_vars, # first the tool's defaults
228+ %user_vars, # then the user's which overwrite the defaults
229+ );
230+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
231+ return \%vars;
232+}
233+
234 sub _d {
235 my ($package, undef, $line) = caller 0;
236 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
237@@ -1986,7 +2026,7 @@
238
239 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
240 $sql = qq{/*!40101 SET NAMES "$charset"*/};
241- PTDEBUG && _d($dbh, ':', $sql);
242+ PTDEBUG && _d($dbh, $sql);
243 eval { $dbh->do($sql) };
244 if ( $EVAL_ERROR ) {
245 die "Error setting NAMES to $charset: $EVAL_ERROR";
246@@ -2001,13 +2041,8 @@
247 }
248 }
249
250- if ( my $var = $self->prop('set-vars') ) {
251- $sql = "SET $var";
252- PTDEBUG && _d($dbh, ':', $sql);
253- eval { $dbh->do($sql) };
254- if ( $EVAL_ERROR ) {
255- die "Error setting $var: $EVAL_ERROR";
256- }
257+ if ( my $vars = $self->prop('set-vars') ) {
258+ $self->set_vars($dbh, $vars);
259 }
260
261 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
262@@ -2082,6 +2117,57 @@
263 return \%new_dsn;
264 }
265
266+sub set_vars {
267+ my ($self, $dbh, $vars) = @_;
268+
269+ return unless $vars;
270+
271+ foreach my $var ( sort keys %$vars ) {
272+ my $val = $vars->{$var}->{val};
273+
274+ (my $quoted_var = $var) =~ s/_/\\_/;
275+ my ($var_exists, $current_val);
276+ eval {
277+ ($var_exists, $current_val) = $dbh->selectrow_array(
278+ "SHOW VARIABLES LIKE '$quoted_var'");
279+ };
280+ my $e = $EVAL_ERROR;
281+ if ( $e ) {
282+ PTDEBUG && _d($e);
283+ }
284+
285+ if ( $vars->{$var}->{default} && !$var_exists ) {
286+ PTDEBUG && _d('Not setting default var', $var,
287+ 'because it does not exist');
288+ next;
289+ }
290+
291+ if ( $current_val && $current_val eq $val ) {
292+ PTDEBUG && _d('Not setting var', $var, 'because its value',
293+ 'is already', $val);
294+ next;
295+ }
296+
297+ my $sql = "SET SESSION $var=$val";
298+ PTDEBUG && _d($dbh, $sql);
299+ eval { $dbh->do($sql) };
300+ if ( my $set_error = $EVAL_ERROR ) {
301+ chomp($set_error);
302+ $set_error =~ s/ at \S+ line \d+//;
303+ my $msg = "Error setting $var: $set_error";
304+ if ( $current_val ) {
305+ $msg .= " The current value for $var is $current_val. "
306+ . "If the variable is read only (not dynamic), specify "
307+ . "--set-vars $var=$current_val to avoid this warning, "
308+ . "else manually set the variable and restart MySQL.";
309+ }
310+ warn $msg . "\n\n";
311+ }
312+ }
313+
314+ return;
315+}
316+
317 sub _d {
318 my ($package, undef, $line) = caller 0;
319 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
320@@ -4984,7 +5070,7 @@
321 $o->get_opts();
322
323 my $dp = $o->DSNParser();
324- $dp->prop('set-vars', $o->get('set-vars'));
325+ $dp->prop('set-vars', $o->set_vars());
326
327 if ( !$o->get('help') ) {
328 if ( @ARGV < 1 ) {
329@@ -5325,10 +5411,21 @@
330
331 =item --set-vars
332
333-type: string; default: wait_timeout=10000
334-
335-Set these MySQL variables. Immediately after connecting to MySQL, this string
336-will be appended to SET and executed.
337+type: Array
338+
339+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
340+
341+By default, the tool sets:
342+
343+=for comment ignore-pt-internal-value
344+MAGIC_set_vars
345+
346+ wait_timeout=10000
347+
348+Variables specified on the command line override these defaults. For
349+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
350+
351+The tool prints a warning and continues if a variable cannot be set.
352
353 =item --socket
354
355
356=== modified file 'bin/pt-deadlock-logger'
357--- bin/pt-deadlock-logger 2013-02-28 02:23:08 +0000
358+++ bin/pt-deadlock-logger 2013-03-04 18:10:28 +0000
359@@ -67,6 +67,7 @@
360
361 use List::Util qw(max);
362 use Getopt::Long;
363+use Data::Dumper;
364
365 my $POD_link_re = '[LC]<"?([^">]+)"?>';
366
367@@ -1050,6 +1051,45 @@
368 );
369 };
370
371+sub set_vars {
372+ my ($self, $file) = @_;
373+ $file ||= $self->{file} || __FILE__;
374+
375+ my %user_vars;
376+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
377+ if ( $user_vars ) {
378+ foreach my $var_val ( @$user_vars ) {
379+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
380+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
381+ $user_vars{$var} = {
382+ val => $val,
383+ default => 0,
384+ };
385+ }
386+ }
387+
388+ my %default_vars;
389+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
390+ if ( $default_vars ) {
391+ %default_vars = map {
392+ my $var_val = $_;
393+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
394+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
395+ $var => {
396+ val => $val,
397+ default => 1,
398+ };
399+ } split("\n", $default_vars);
400+ }
401+
402+ my %vars = (
403+ %default_vars, # first the tool's defaults
404+ %user_vars, # then the user's which overwrite the defaults
405+ );
406+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
407+ return \%vars;
408+}
409+
410 sub _d {
411 my ($package, undef, $line) = caller 0;
412 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
413@@ -2330,7 +2370,7 @@
414
415 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
416 $sql = qq{/*!40101 SET NAMES "$charset"*/};
417- PTDEBUG && _d($dbh, ':', $sql);
418+ PTDEBUG && _d($dbh, $sql);
419 eval { $dbh->do($sql) };
420 if ( $EVAL_ERROR ) {
421 die "Error setting NAMES to $charset: $EVAL_ERROR";
422@@ -2345,13 +2385,8 @@
423 }
424 }
425
426- if ( my $var = $self->prop('set-vars') ) {
427- $sql = "SET $var";
428- PTDEBUG && _d($dbh, ':', $sql);
429- eval { $dbh->do($sql) };
430- if ( $EVAL_ERROR ) {
431- die "Error setting $var: $EVAL_ERROR";
432- }
433+ if ( my $vars = $self->prop('set-vars') ) {
434+ $self->set_vars($dbh, $vars);
435 }
436
437 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
438@@ -2426,6 +2461,57 @@
439 return \%new_dsn;
440 }
441
442+sub set_vars {
443+ my ($self, $dbh, $vars) = @_;
444+
445+ return unless $vars;
446+
447+ foreach my $var ( sort keys %$vars ) {
448+ my $val = $vars->{$var}->{val};
449+
450+ (my $quoted_var = $var) =~ s/_/\\_/;
451+ my ($var_exists, $current_val);
452+ eval {
453+ ($var_exists, $current_val) = $dbh->selectrow_array(
454+ "SHOW VARIABLES LIKE '$quoted_var'");
455+ };
456+ my $e = $EVAL_ERROR;
457+ if ( $e ) {
458+ PTDEBUG && _d($e);
459+ }
460+
461+ if ( $vars->{$var}->{default} && !$var_exists ) {
462+ PTDEBUG && _d('Not setting default var', $var,
463+ 'because it does not exist');
464+ next;
465+ }
466+
467+ if ( $current_val && $current_val eq $val ) {
468+ PTDEBUG && _d('Not setting var', $var, 'because its value',
469+ 'is already', $val);
470+ next;
471+ }
472+
473+ my $sql = "SET SESSION $var=$val";
474+ PTDEBUG && _d($dbh, $sql);
475+ eval { $dbh->do($sql) };
476+ if ( my $set_error = $EVAL_ERROR ) {
477+ chomp($set_error);
478+ $set_error =~ s/ at \S+ line \d+//;
479+ my $msg = "Error setting $var: $set_error";
480+ if ( $current_val ) {
481+ $msg .= " The current value for $var is $current_val. "
482+ . "If the variable is read only (not dynamic), specify "
483+ . "--set-vars $var=$current_val to avoid this warning, "
484+ . "else manually set the variable and restart MySQL.";
485+ }
486+ warn $msg . "\n\n";
487+ }
488+ }
489+
490+ return;
491+}
492+
493 sub _d {
494 my ($package, undef, $line) = caller 0;
495 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
496@@ -4254,7 +4340,7 @@
497 $o->get_opts();
498
499 my $dp = $o->DSNParser();
500- $dp->prop('set-vars', $o->get('set-vars'));
501+ $dp->prop('set-vars', $o->set_vars());
502
503 my $src;
504 if ( my $src_dsn_string = shift @ARGV ) {
505@@ -5116,10 +5202,21 @@
506
507 =item --set-vars
508
509-type: string; default: wait_timeout=10000
510-
511-Set these MySQL variables. Immediately after connecting to MySQL, this string
512-will be appended to SET and executed.
513+type: Array
514+
515+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
516+
517+By default, the tool sets:
518+
519+=for comment ignore-pt-internal-value
520+MAGIC_set_vars
521+
522+ wait_timeout=10000
523+
524+Variables specified on the command line override these defaults. For
525+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
526+
527+The tool prints a warning and continues if a variable cannot be set.
528
529 =item --socket
530
531
532=== modified file 'bin/pt-duplicate-key-checker'
533--- bin/pt-duplicate-key-checker 2013-02-27 00:01:17 +0000
534+++ bin/pt-duplicate-key-checker 2013-03-04 18:10:28 +0000
535@@ -875,7 +875,7 @@
536
537 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
538 $sql = qq{/*!40101 SET NAMES "$charset"*/};
539- PTDEBUG && _d($dbh, ':', $sql);
540+ PTDEBUG && _d($dbh, $sql);
541 eval { $dbh->do($sql) };
542 if ( $EVAL_ERROR ) {
543 die "Error setting NAMES to $charset: $EVAL_ERROR";
544@@ -890,13 +890,8 @@
545 }
546 }
547
548- if ( my $var = $self->prop('set-vars') ) {
549- $sql = "SET $var";
550- PTDEBUG && _d($dbh, ':', $sql);
551- eval { $dbh->do($sql) };
552- if ( $EVAL_ERROR ) {
553- die "Error setting $var: $EVAL_ERROR";
554- }
555+ if ( my $vars = $self->prop('set-vars') ) {
556+ $self->set_vars($dbh, $vars);
557 }
558
559 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
560@@ -971,6 +966,57 @@
561 return \%new_dsn;
562 }
563
564+sub set_vars {
565+ my ($self, $dbh, $vars) = @_;
566+
567+ return unless $vars;
568+
569+ foreach my $var ( sort keys %$vars ) {
570+ my $val = $vars->{$var}->{val};
571+
572+ (my $quoted_var = $var) =~ s/_/\\_/;
573+ my ($var_exists, $current_val);
574+ eval {
575+ ($var_exists, $current_val) = $dbh->selectrow_array(
576+ "SHOW VARIABLES LIKE '$quoted_var'");
577+ };
578+ my $e = $EVAL_ERROR;
579+ if ( $e ) {
580+ PTDEBUG && _d($e);
581+ }
582+
583+ if ( $vars->{$var}->{default} && !$var_exists ) {
584+ PTDEBUG && _d('Not setting default var', $var,
585+ 'because it does not exist');
586+ next;
587+ }
588+
589+ if ( $current_val && $current_val eq $val ) {
590+ PTDEBUG && _d('Not setting var', $var, 'because its value',
591+ 'is already', $val);
592+ next;
593+ }
594+
595+ my $sql = "SET SESSION $var=$val";
596+ PTDEBUG && _d($dbh, $sql);
597+ eval { $dbh->do($sql) };
598+ if ( my $set_error = $EVAL_ERROR ) {
599+ chomp($set_error);
600+ $set_error =~ s/ at \S+ line \d+//;
601+ my $msg = "Error setting $var: $set_error";
602+ if ( $current_val ) {
603+ $msg .= " The current value for $var is $current_val. "
604+ . "If the variable is read only (not dynamic), specify "
605+ . "--set-vars $var=$current_val to avoid this warning, "
606+ . "else manually set the variable and restart MySQL.";
607+ }
608+ warn $msg . "\n\n";
609+ }
610+ }
611+
612+ return;
613+}
614+
615 sub _d {
616 my ($package, undef, $line) = caller 0;
617 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
618@@ -1003,6 +1049,7 @@
619
620 use List::Util qw(max);
621 use Getopt::Long;
622+use Data::Dumper;
623
624 my $POD_link_re = '[LC]<"?([^">]+)"?>';
625
626@@ -1986,6 +2033,45 @@
627 );
628 };
629
630+sub set_vars {
631+ my ($self, $file) = @_;
632+ $file ||= $self->{file} || __FILE__;
633+
634+ my %user_vars;
635+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
636+ if ( $user_vars ) {
637+ foreach my $var_val ( @$user_vars ) {
638+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
639+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
640+ $user_vars{$var} = {
641+ val => $val,
642+ default => 0,
643+ };
644+ }
645+ }
646+
647+ my %default_vars;
648+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
649+ if ( $default_vars ) {
650+ %default_vars = map {
651+ my $var_val = $_;
652+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
653+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
654+ $var => {
655+ val => $val,
656+ default => 1,
657+ };
658+ } split("\n", $default_vars);
659+ }
660+
661+ my %vars = (
662+ %default_vars, # first the tool's defaults
663+ %user_vars, # then the user's which overwrite the defaults
664+ );
665+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
666+ return \%vars;
667+}
668+
669 sub _d {
670 my ($package, undef, $line) = caller 0;
671 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
672@@ -4662,7 +4748,7 @@
673 $o->get_opts();
674
675 my $dp = $o->DSNParser();
676- $dp->prop('set-vars', $o->get('set-vars'));
677+ $dp->prop('set-vars', $o->set_vars());
678
679 $o->usage_or_errors();
680
681@@ -5144,10 +5230,21 @@
682
683 =item --set-vars
684
685-type: string; default: wait_timeout=10000
686-
687-Set these MySQL variables. Immediately after connecting to MySQL, this string
688-will be appended to SET and executed.
689+type: Array
690+
691+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
692+
693+By default, the tool sets:
694+
695+=for comment ignore-pt-internal-value
696+MAGIC_set_vars
697+
698+ wait_timeout=10000
699+
700+Variables specified on the command line override these defaults. For
701+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
702+
703+The tool prints a warning and continues if a variable cannot be set.
704
705 =item --socket
706
707
708=== modified file 'bin/pt-find'
709--- bin/pt-find 2013-02-27 00:01:17 +0000
710+++ bin/pt-find 2013-03-04 18:10:28 +0000
711@@ -309,7 +309,7 @@
712
713 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
714 $sql = qq{/*!40101 SET NAMES "$charset"*/};
715- PTDEBUG && _d($dbh, ':', $sql);
716+ PTDEBUG && _d($dbh, $sql);
717 eval { $dbh->do($sql) };
718 if ( $EVAL_ERROR ) {
719 die "Error setting NAMES to $charset: $EVAL_ERROR";
720@@ -324,13 +324,8 @@
721 }
722 }
723
724- if ( my $var = $self->prop('set-vars') ) {
725- $sql = "SET $var";
726- PTDEBUG && _d($dbh, ':', $sql);
727- eval { $dbh->do($sql) };
728- if ( $EVAL_ERROR ) {
729- die "Error setting $var: $EVAL_ERROR";
730- }
731+ if ( my $vars = $self->prop('set-vars') ) {
732+ $self->set_vars($dbh, $vars);
733 }
734
735 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
736@@ -405,6 +400,57 @@
737 return \%new_dsn;
738 }
739
740+sub set_vars {
741+ my ($self, $dbh, $vars) = @_;
742+
743+ return unless $vars;
744+
745+ foreach my $var ( sort keys %$vars ) {
746+ my $val = $vars->{$var}->{val};
747+
748+ (my $quoted_var = $var) =~ s/_/\\_/;
749+ my ($var_exists, $current_val);
750+ eval {
751+ ($var_exists, $current_val) = $dbh->selectrow_array(
752+ "SHOW VARIABLES LIKE '$quoted_var'");
753+ };
754+ my $e = $EVAL_ERROR;
755+ if ( $e ) {
756+ PTDEBUG && _d($e);
757+ }
758+
759+ if ( $vars->{$var}->{default} && !$var_exists ) {
760+ PTDEBUG && _d('Not setting default var', $var,
761+ 'because it does not exist');
762+ next;
763+ }
764+
765+ if ( $current_val && $current_val eq $val ) {
766+ PTDEBUG && _d('Not setting var', $var, 'because its value',
767+ 'is already', $val);
768+ next;
769+ }
770+
771+ my $sql = "SET SESSION $var=$val";
772+ PTDEBUG && _d($dbh, $sql);
773+ eval { $dbh->do($sql) };
774+ if ( my $set_error = $EVAL_ERROR ) {
775+ chomp($set_error);
776+ $set_error =~ s/ at \S+ line \d+//;
777+ my $msg = "Error setting $var: $set_error";
778+ if ( $current_val ) {
779+ $msg .= " The current value for $var is $current_val. "
780+ . "If the variable is read only (not dynamic), specify "
781+ . "--set-vars $var=$current_val to avoid this warning, "
782+ . "else manually set the variable and restart MySQL.";
783+ }
784+ warn $msg . "\n\n";
785+ }
786+ }
787+
788+ return;
789+}
790+
791 sub _d {
792 my ($package, undef, $line) = caller 0;
793 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
794@@ -437,6 +483,7 @@
795
796 use List::Util qw(max);
797 use Getopt::Long;
798+use Data::Dumper;
799
800 my $POD_link_re = '[LC]<"?([^">]+)"?>';
801
802@@ -1420,6 +1467,45 @@
803 );
804 };
805
806+sub set_vars {
807+ my ($self, $file) = @_;
808+ $file ||= $self->{file} || __FILE__;
809+
810+ my %user_vars;
811+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
812+ if ( $user_vars ) {
813+ foreach my $var_val ( @$user_vars ) {
814+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
815+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
816+ $user_vars{$var} = {
817+ val => $val,
818+ default => 0,
819+ };
820+ }
821+ }
822+
823+ my %default_vars;
824+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
825+ if ( $default_vars ) {
826+ %default_vars = map {
827+ my $var_val = $_;
828+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
829+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
830+ $var => {
831+ val => $val,
832+ default => 1,
833+ };
834+ } split("\n", $default_vars);
835+ }
836+
837+ my %vars = (
838+ %default_vars, # first the tool's defaults
839+ %user_vars, # then the user's which overwrite the defaults
840+ );
841+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
842+ return \%vars;
843+}
844+
845 sub _d {
846 my ($package, undef, $line) = caller 0;
847 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
848@@ -3719,7 +3805,7 @@
849 $o->get_opts();
850
851 my $dp = $o->DSNParser();
852- $dp->prop('set-vars', $o->get('set-vars'));
853+ $dp->prop('set-vars', $o->set_vars());
854
855 # Make sure OptionParser understands that these options are used.
856 # cmin ctime empty kmin ktime mmin mtime exec printf
857@@ -4263,10 +4349,21 @@
858
859 =item --set-vars
860
861-type: string; default: wait_timeout=10000
862-
863-Set these MySQL variables. Immediately after connecting to MySQL, this string
864-will be appended to SET and executed.
865+type: Array
866+
867+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
868+
869+By default, the tool sets:
870+
871+=for comment ignore-pt-internal-value
872+MAGIC_set_vars
873+
874+ wait_timeout=10000
875+
876+Variables specified on the command line override these defaults. For
877+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
878+
879+The tool prints a warning and continues if a variable cannot be set.
880
881 =item --socket
882
883
884=== modified file 'bin/pt-fk-error-logger'
885--- bin/pt-fk-error-logger 2013-02-28 02:23:08 +0000
886+++ bin/pt-fk-error-logger 2013-03-04 18:10:28 +0000
887@@ -62,6 +62,7 @@
888
889 use List::Util qw(max);
890 use Getopt::Long;
891+use Data::Dumper;
892
893 my $POD_link_re = '[LC]<"?([^">]+)"?>';
894
895@@ -1045,6 +1046,45 @@
896 );
897 };
898
899+sub set_vars {
900+ my ($self, $file) = @_;
901+ $file ||= $self->{file} || __FILE__;
902+
903+ my %user_vars;
904+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
905+ if ( $user_vars ) {
906+ foreach my $var_val ( @$user_vars ) {
907+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
908+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
909+ $user_vars{$var} = {
910+ val => $val,
911+ default => 0,
912+ };
913+ }
914+ }
915+
916+ my %default_vars;
917+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
918+ if ( $default_vars ) {
919+ %default_vars = map {
920+ my $var_val = $_;
921+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
922+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
923+ $var => {
924+ val => $val,
925+ default => 1,
926+ };
927+ } split("\n", $default_vars);
928+ }
929+
930+ my %vars = (
931+ %default_vars, # first the tool's defaults
932+ %user_vars, # then the user's which overwrite the defaults
933+ );
934+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
935+ return \%vars;
936+}
937+
938 sub _d {
939 my ($package, undef, $line) = caller 0;
940 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
941@@ -1487,7 +1527,7 @@
942
943 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
944 $sql = qq{/*!40101 SET NAMES "$charset"*/};
945- PTDEBUG && _d($dbh, ':', $sql);
946+ PTDEBUG && _d($dbh, $sql);
947 eval { $dbh->do($sql) };
948 if ( $EVAL_ERROR ) {
949 die "Error setting NAMES to $charset: $EVAL_ERROR";
950@@ -1502,13 +1542,8 @@
951 }
952 }
953
954- if ( my $var = $self->prop('set-vars') ) {
955- $sql = "SET $var";
956- PTDEBUG && _d($dbh, ':', $sql);
957- eval { $dbh->do($sql) };
958- if ( $EVAL_ERROR ) {
959- die "Error setting $var: $EVAL_ERROR";
960- }
961+ if ( my $vars = $self->prop('set-vars') ) {
962+ $self->set_vars($dbh, $vars);
963 }
964
965 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
966@@ -1583,6 +1618,57 @@
967 return \%new_dsn;
968 }
969
970+sub set_vars {
971+ my ($self, $dbh, $vars) = @_;
972+
973+ return unless $vars;
974+
975+ foreach my $var ( sort keys %$vars ) {
976+ my $val = $vars->{$var}->{val};
977+
978+ (my $quoted_var = $var) =~ s/_/\\_/;
979+ my ($var_exists, $current_val);
980+ eval {
981+ ($var_exists, $current_val) = $dbh->selectrow_array(
982+ "SHOW VARIABLES LIKE '$quoted_var'");
983+ };
984+ my $e = $EVAL_ERROR;
985+ if ( $e ) {
986+ PTDEBUG && _d($e);
987+ }
988+
989+ if ( $vars->{$var}->{default} && !$var_exists ) {
990+ PTDEBUG && _d('Not setting default var', $var,
991+ 'because it does not exist');
992+ next;
993+ }
994+
995+ if ( $current_val && $current_val eq $val ) {
996+ PTDEBUG && _d('Not setting var', $var, 'because its value',
997+ 'is already', $val);
998+ next;
999+ }
1000+
1001+ my $sql = "SET SESSION $var=$val";
1002+ PTDEBUG && _d($dbh, $sql);
1003+ eval { $dbh->do($sql) };
1004+ if ( my $set_error = $EVAL_ERROR ) {
1005+ chomp($set_error);
1006+ $set_error =~ s/ at \S+ line \d+//;
1007+ my $msg = "Error setting $var: $set_error";
1008+ if ( $current_val ) {
1009+ $msg .= " The current value for $var is $current_val. "
1010+ . "If the variable is read only (not dynamic), specify "
1011+ . "--set-vars $var=$current_val to avoid this warning, "
1012+ . "else manually set the variable and restart MySQL.";
1013+ }
1014+ warn $msg . "\n\n";
1015+ }
1016+ }
1017+
1018+ return;
1019+}
1020+
1021 sub _d {
1022 my ($package, undef, $line) = caller 0;
1023 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1024@@ -3721,7 +3807,7 @@
1025 $o->get_opts();
1026
1027 my $dp = $o->DSNParser();
1028- $dp->prop('set-vars', $o->get('set-vars'));
1029+ $dp->prop('set-vars', $o->set_vars());
1030
1031 my $src;
1032 if ( my $src_dsn_string = shift @ARGV ) {
1033@@ -4127,10 +4213,21 @@
1034
1035 =item --set-vars
1036
1037-type: string; default: wait_timeout=10000
1038-
1039-Set these MySQL variables. Immediately after connecting to MySQL, this string
1040-will be appended to SET and executed.
1041+type: Array
1042+
1043+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
1044+
1045+By default, the tool sets:
1046+
1047+=for comment ignore-pt-internal-value
1048+MAGIC_set_vars
1049+
1050+ wait_timeout=10000
1051+
1052+Variables specified on the command line override these defaults. For
1053+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
1054+
1055+The tool prints a warning and continues if a variable cannot be set.
1056
1057 =item --socket
1058
1059
1060=== modified file 'bin/pt-heartbeat'
1061--- bin/pt-heartbeat 2013-02-27 00:01:17 +0000
1062+++ bin/pt-heartbeat 2013-03-04 18:10:28 +0000
1063@@ -797,6 +797,7 @@
1064
1065 use List::Util qw(max);
1066 use Getopt::Long;
1067+use Data::Dumper;
1068
1069 my $POD_link_re = '[LC]<"?([^">]+)"?>';
1070
1071@@ -1780,6 +1781,45 @@
1072 );
1073 };
1074
1075+sub set_vars {
1076+ my ($self, $file) = @_;
1077+ $file ||= $self->{file} || __FILE__;
1078+
1079+ my %user_vars;
1080+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
1081+ if ( $user_vars ) {
1082+ foreach my $var_val ( @$user_vars ) {
1083+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1084+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1085+ $user_vars{$var} = {
1086+ val => $val,
1087+ default => 0,
1088+ };
1089+ }
1090+ }
1091+
1092+ my %default_vars;
1093+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
1094+ if ( $default_vars ) {
1095+ %default_vars = map {
1096+ my $var_val = $_;
1097+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1098+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1099+ $var => {
1100+ val => $val,
1101+ default => 1,
1102+ };
1103+ } split("\n", $default_vars);
1104+ }
1105+
1106+ my %vars = (
1107+ %default_vars, # first the tool's defaults
1108+ %user_vars, # then the user's which overwrite the defaults
1109+ );
1110+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
1111+ return \%vars;
1112+}
1113+
1114 sub _d {
1115 my ($package, undef, $line) = caller 0;
1116 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1117@@ -2071,7 +2111,7 @@
1118
1119 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1120 $sql = qq{/*!40101 SET NAMES "$charset"*/};
1121- PTDEBUG && _d($dbh, ':', $sql);
1122+ PTDEBUG && _d($dbh, $sql);
1123 eval { $dbh->do($sql) };
1124 if ( $EVAL_ERROR ) {
1125 die "Error setting NAMES to $charset: $EVAL_ERROR";
1126@@ -2086,13 +2126,8 @@
1127 }
1128 }
1129
1130- if ( my $var = $self->prop('set-vars') ) {
1131- $sql = "SET $var";
1132- PTDEBUG && _d($dbh, ':', $sql);
1133- eval { $dbh->do($sql) };
1134- if ( $EVAL_ERROR ) {
1135- die "Error setting $var: $EVAL_ERROR";
1136- }
1137+ if ( my $vars = $self->prop('set-vars') ) {
1138+ $self->set_vars($dbh, $vars);
1139 }
1140
1141 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1142@@ -2167,6 +2202,57 @@
1143 return \%new_dsn;
1144 }
1145
1146+sub set_vars {
1147+ my ($self, $dbh, $vars) = @_;
1148+
1149+ return unless $vars;
1150+
1151+ foreach my $var ( sort keys %$vars ) {
1152+ my $val = $vars->{$var}->{val};
1153+
1154+ (my $quoted_var = $var) =~ s/_/\\_/;
1155+ my ($var_exists, $current_val);
1156+ eval {
1157+ ($var_exists, $current_val) = $dbh->selectrow_array(
1158+ "SHOW VARIABLES LIKE '$quoted_var'");
1159+ };
1160+ my $e = $EVAL_ERROR;
1161+ if ( $e ) {
1162+ PTDEBUG && _d($e);
1163+ }
1164+
1165+ if ( $vars->{$var}->{default} && !$var_exists ) {
1166+ PTDEBUG && _d('Not setting default var', $var,
1167+ 'because it does not exist');
1168+ next;
1169+ }
1170+
1171+ if ( $current_val && $current_val eq $val ) {
1172+ PTDEBUG && _d('Not setting var', $var, 'because its value',
1173+ 'is already', $val);
1174+ next;
1175+ }
1176+
1177+ my $sql = "SET SESSION $var=$val";
1178+ PTDEBUG && _d($dbh, $sql);
1179+ eval { $dbh->do($sql) };
1180+ if ( my $set_error = $EVAL_ERROR ) {
1181+ chomp($set_error);
1182+ $set_error =~ s/ at \S+ line \d+//;
1183+ my $msg = "Error setting $var: $set_error";
1184+ if ( $current_val ) {
1185+ $msg .= " The current value for $var is $current_val. "
1186+ . "If the variable is read only (not dynamic), specify "
1187+ . "--set-vars $var=$current_val to avoid this warning, "
1188+ . "else manually set the variable and restart MySQL.";
1189+ }
1190+ warn $msg . "\n\n";
1191+ }
1192+ }
1193+
1194+ return;
1195+}
1196+
1197 sub _d {
1198 my ($package, undef, $line) = caller 0;
1199 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1200@@ -4641,7 +4727,7 @@
1201
1202 my $dp = $o->DSNParser;
1203 $dp->prop('dbidriver', $o->get('dbi-driver'));
1204- $dp->prop('set-vars', $o->get('set-vars'));
1205+ $dp->prop('set-vars', $o->set_vars());
1206
1207 if ( !$o->get('help') ) {
1208 my @frames = $o->get('frames') =~ m/(\d+[smhd])/g;
1209@@ -5737,10 +5823,21 @@
1210
1211 =item --set-vars
1212
1213-type: string; default: wait_timeout=10000
1214-
1215-Set these MySQL variables. Immediately after connecting to MySQL, this string
1216-will be appended to SET and executed.
1217+type: Array
1218+
1219+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
1220+
1221+By default, the tool sets:
1222+
1223+=for comment ignore-pt-internal-value
1224+MAGIC_set_vars
1225+
1226+ wait_timeout=10000
1227+
1228+Variables specified on the command line override these defaults. For
1229+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
1230+
1231+The tool prints a warning and continues if a variable cannot be set.
1232
1233 =item --skew
1234
1235
1236=== modified file 'bin/pt-index-usage'
1237--- bin/pt-index-usage 2013-02-22 17:47:57 +0000
1238+++ bin/pt-index-usage 2013-03-04 18:10:28 +0000
1239@@ -319,7 +319,7 @@
1240
1241 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1242 $sql = qq{/*!40101 SET NAMES "$charset"*/};
1243- PTDEBUG && _d($dbh, ':', $sql);
1244+ PTDEBUG && _d($dbh, $sql);
1245 eval { $dbh->do($sql) };
1246 if ( $EVAL_ERROR ) {
1247 die "Error setting NAMES to $charset: $EVAL_ERROR";
1248@@ -334,13 +334,8 @@
1249 }
1250 }
1251
1252- if ( my $var = $self->prop('set-vars') ) {
1253- $sql = "SET $var";
1254- PTDEBUG && _d($dbh, ':', $sql);
1255- eval { $dbh->do($sql) };
1256- if ( $EVAL_ERROR ) {
1257- die "Error setting $var: $EVAL_ERROR";
1258- }
1259+ if ( my $vars = $self->prop('set-vars') ) {
1260+ $self->set_vars($dbh, $vars);
1261 }
1262
1263 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1264@@ -415,6 +410,57 @@
1265 return \%new_dsn;
1266 }
1267
1268+sub set_vars {
1269+ my ($self, $dbh, $vars) = @_;
1270+
1271+ return unless $vars;
1272+
1273+ foreach my $var ( sort keys %$vars ) {
1274+ my $val = $vars->{$var}->{val};
1275+
1276+ (my $quoted_var = $var) =~ s/_/\\_/;
1277+ my ($var_exists, $current_val);
1278+ eval {
1279+ ($var_exists, $current_val) = $dbh->selectrow_array(
1280+ "SHOW VARIABLES LIKE '$quoted_var'");
1281+ };
1282+ my $e = $EVAL_ERROR;
1283+ if ( $e ) {
1284+ PTDEBUG && _d($e);
1285+ }
1286+
1287+ if ( $vars->{$var}->{default} && !$var_exists ) {
1288+ PTDEBUG && _d('Not setting default var', $var,
1289+ 'because it does not exist');
1290+ next;
1291+ }
1292+
1293+ if ( $current_val && $current_val eq $val ) {
1294+ PTDEBUG && _d('Not setting var', $var, 'because its value',
1295+ 'is already', $val);
1296+ next;
1297+ }
1298+
1299+ my $sql = "SET SESSION $var=$val";
1300+ PTDEBUG && _d($dbh, $sql);
1301+ eval { $dbh->do($sql) };
1302+ if ( my $set_error = $EVAL_ERROR ) {
1303+ chomp($set_error);
1304+ $set_error =~ s/ at \S+ line \d+//;
1305+ my $msg = "Error setting $var: $set_error";
1306+ if ( $current_val ) {
1307+ $msg .= " The current value for $var is $current_val. "
1308+ . "If the variable is read only (not dynamic), specify "
1309+ . "--set-vars $var=$current_val to avoid this warning, "
1310+ . "else manually set the variable and restart MySQL.";
1311+ }
1312+ warn $msg . "\n\n";
1313+ }
1314+ }
1315+
1316+ return;
1317+}
1318+
1319 sub _d {
1320 my ($package, undef, $line) = caller 0;
1321 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1322@@ -598,6 +644,7 @@
1323
1324 use List::Util qw(max);
1325 use Getopt::Long;
1326+use Data::Dumper;
1327
1328 my $POD_link_re = '[LC]<"?([^">]+)"?>';
1329
1330@@ -1581,6 +1628,45 @@
1331 );
1332 };
1333
1334+sub set_vars {
1335+ my ($self, $file) = @_;
1336+ $file ||= $self->{file} || __FILE__;
1337+
1338+ my %user_vars;
1339+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
1340+ if ( $user_vars ) {
1341+ foreach my $var_val ( @$user_vars ) {
1342+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1343+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1344+ $user_vars{$var} = {
1345+ val => $val,
1346+ default => 0,
1347+ };
1348+ }
1349+ }
1350+
1351+ my %default_vars;
1352+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
1353+ if ( $default_vars ) {
1354+ %default_vars = map {
1355+ my $var_val = $_;
1356+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1357+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1358+ $var => {
1359+ val => $val,
1360+ default => 1,
1361+ };
1362+ } split("\n", $default_vars);
1363+ }
1364+
1365+ my %vars = (
1366+ %default_vars, # first the tool's defaults
1367+ %user_vars, # then the user's which overwrite the defaults
1368+ );
1369+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
1370+ return \%vars;
1371+}
1372+
1373 sub _d {
1374 my ($package, undef, $line) = caller 0;
1375 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1376@@ -6112,7 +6198,7 @@
1377 $o->get_specs();
1378 $o->get_opts();
1379 my $dp = $o->DSNParser();
1380- $dp->prop('set-vars', $o->get('set-vars'));
1381+ $dp->prop('set-vars', $o->set_vars());
1382 $o->set('progress', undef) if $o->get('q');
1383
1384 if ( !$o->got('help') ) {
1385@@ -7159,10 +7245,21 @@
1386
1387 =item --set-vars
1388
1389-type: string; default: wait_timeout=10000
1390-
1391-Set these MySQL variables. Immediately after connecting to MySQL, this
1392-string will be appended to SET and executed.
1393+type: Array
1394+
1395+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
1396+
1397+By default, the tool sets:
1398+
1399+=for comment ignore-pt-internal-value
1400+MAGIC_set_vars
1401+
1402+ wait_timeout=10000
1403+
1404+Variables specified on the command line override these defaults. For
1405+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
1406+
1407+The tool prints a warning and continues if a variable cannot be set.
1408
1409 =item --socket
1410
1411
1412=== modified file 'bin/pt-kill'
1413--- bin/pt-kill 2013-02-27 00:01:17 +0000
1414+++ bin/pt-kill 2013-03-04 18:10:28 +0000
1415@@ -72,6 +72,7 @@
1416
1417 use List::Util qw(max);
1418 use Getopt::Long;
1419+use Data::Dumper;
1420
1421 my $POD_link_re = '[LC]<"?([^">]+)"?>';
1422
1423@@ -1055,6 +1056,45 @@
1424 );
1425 };
1426
1427+sub set_vars {
1428+ my ($self, $file) = @_;
1429+ $file ||= $self->{file} || __FILE__;
1430+
1431+ my %user_vars;
1432+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
1433+ if ( $user_vars ) {
1434+ foreach my $var_val ( @$user_vars ) {
1435+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1436+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1437+ $user_vars{$var} = {
1438+ val => $val,
1439+ default => 0,
1440+ };
1441+ }
1442+ }
1443+
1444+ my %default_vars;
1445+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
1446+ if ( $default_vars ) {
1447+ %default_vars = map {
1448+ my $var_val = $_;
1449+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1450+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1451+ $var => {
1452+ val => $val,
1453+ default => 1,
1454+ };
1455+ } split("\n", $default_vars);
1456+ }
1457+
1458+ my %vars = (
1459+ %default_vars, # first the tool's defaults
1460+ %user_vars, # then the user's which overwrite the defaults
1461+ );
1462+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
1463+ return \%vars;
1464+}
1465+
1466 sub _d {
1467 my ($package, undef, $line) = caller 0;
1468 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1469@@ -1990,7 +2030,7 @@
1470
1471 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1472 $sql = qq{/*!40101 SET NAMES "$charset"*/};
1473- PTDEBUG && _d($dbh, ':', $sql);
1474+ PTDEBUG && _d($dbh, $sql);
1475 eval { $dbh->do($sql) };
1476 if ( $EVAL_ERROR ) {
1477 die "Error setting NAMES to $charset: $EVAL_ERROR";
1478@@ -2005,13 +2045,8 @@
1479 }
1480 }
1481
1482- if ( my $var = $self->prop('set-vars') ) {
1483- $sql = "SET $var";
1484- PTDEBUG && _d($dbh, ':', $sql);
1485- eval { $dbh->do($sql) };
1486- if ( $EVAL_ERROR ) {
1487- die "Error setting $var: $EVAL_ERROR";
1488- }
1489+ if ( my $vars = $self->prop('set-vars') ) {
1490+ $self->set_vars($dbh, $vars);
1491 }
1492
1493 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1494@@ -2086,6 +2121,57 @@
1495 return \%new_dsn;
1496 }
1497
1498+sub set_vars {
1499+ my ($self, $dbh, $vars) = @_;
1500+
1501+ return unless $vars;
1502+
1503+ foreach my $var ( sort keys %$vars ) {
1504+ my $val = $vars->{$var}->{val};
1505+
1506+ (my $quoted_var = $var) =~ s/_/\\_/;
1507+ my ($var_exists, $current_val);
1508+ eval {
1509+ ($var_exists, $current_val) = $dbh->selectrow_array(
1510+ "SHOW VARIABLES LIKE '$quoted_var'");
1511+ };
1512+ my $e = $EVAL_ERROR;
1513+ if ( $e ) {
1514+ PTDEBUG && _d($e);
1515+ }
1516+
1517+ if ( $vars->{$var}->{default} && !$var_exists ) {
1518+ PTDEBUG && _d('Not setting default var', $var,
1519+ 'because it does not exist');
1520+ next;
1521+ }
1522+
1523+ if ( $current_val && $current_val eq $val ) {
1524+ PTDEBUG && _d('Not setting var', $var, 'because its value',
1525+ 'is already', $val);
1526+ next;
1527+ }
1528+
1529+ my $sql = "SET SESSION $var=$val";
1530+ PTDEBUG && _d($dbh, $sql);
1531+ eval { $dbh->do($sql) };
1532+ if ( my $set_error = $EVAL_ERROR ) {
1533+ chomp($set_error);
1534+ $set_error =~ s/ at \S+ line \d+//;
1535+ my $msg = "Error setting $var: $set_error";
1536+ if ( $current_val ) {
1537+ $msg .= " The current value for $var is $current_val. "
1538+ . "If the variable is read only (not dynamic), specify "
1539+ . "--set-vars $var=$current_val to avoid this warning, "
1540+ . "else manually set the variable and restart MySQL.";
1541+ }
1542+ warn $msg . "\n\n";
1543+ }
1544+ }
1545+
1546+ return;
1547+}
1548+
1549 sub _d {
1550 my ($package, undef, $line) = caller 0;
1551 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1552@@ -6371,7 +6457,7 @@
1553 $o->get_opts();
1554
1555 my $dp = $o->DSNParser();
1556- $dp->prop('set-vars', $o->get('set-vars'));
1557+ $dp->prop('set-vars', $o->set_vars());
1558
1559 if ( !$o->got('busy-time') ) {
1560 $o->set('interval', 30) unless $o->got('interval');
1561@@ -7383,10 +7469,21 @@
1562
1563 =item --set-vars
1564
1565-type: string; default: wait_timeout=10000
1566-
1567-Set these MySQL variables. Immediately after connecting to MySQL, this string
1568-will be appended to SET and executed.
1569+type: Array
1570+
1571+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
1572+
1573+By default, the tool sets:
1574+
1575+=for comment ignore-pt-internal-value
1576+MAGIC_set_vars
1577+
1578+ wait_timeout=10000
1579+
1580+Variables specified on the command line override these defaults. For
1581+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
1582+
1583+The tool prints a warning and continues if a variable cannot be set.
1584
1585 =item --socket
1586
1587
1588=== modified file 'bin/pt-online-schema-change'
1589--- bin/pt-online-schema-change 2013-02-27 00:01:17 +0000
1590+++ bin/pt-online-schema-change 2013-03-04 18:10:28 +0000
1591@@ -23,7 +23,6 @@
1592 VersionParser
1593 DSNParser
1594 Daemon
1595- ReportFormatter
1596 Quoter
1597 TableNibbler
1598 TableParser
1599@@ -80,6 +79,7 @@
1600
1601 use List::Util qw(max);
1602 use Getopt::Long;
1603+use Data::Dumper;
1604
1605 my $POD_link_re = '[LC]<"?([^">]+)"?>';
1606
1607@@ -1063,6 +1063,45 @@
1608 );
1609 };
1610
1611+sub set_vars {
1612+ my ($self, $file) = @_;
1613+ $file ||= $self->{file} || __FILE__;
1614+
1615+ my %user_vars;
1616+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
1617+ if ( $user_vars ) {
1618+ foreach my $var_val ( @$user_vars ) {
1619+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1620+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1621+ $user_vars{$var} = {
1622+ val => $val,
1623+ default => 0,
1624+ };
1625+ }
1626+ }
1627+
1628+ my %default_vars;
1629+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
1630+ if ( $default_vars ) {
1631+ %default_vars = map {
1632+ my $var_val = $_;
1633+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
1634+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
1635+ $var => {
1636+ val => $val,
1637+ default => 1,
1638+ };
1639+ } split("\n", $default_vars);
1640+ }
1641+
1642+ my %vars = (
1643+ %default_vars, # first the tool's defaults
1644+ %user_vars, # then the user's which overwrite the defaults
1645+ );
1646+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
1647+ return \%vars;
1648+}
1649+
1650 sub _d {
1651 my ($package, undef, $line) = caller 0;
1652 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1653@@ -2193,7 +2232,7 @@
1654
1655 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
1656 $sql = qq{/*!40101 SET NAMES "$charset"*/};
1657- PTDEBUG && _d($dbh, ':', $sql);
1658+ PTDEBUG && _d($dbh, $sql);
1659 eval { $dbh->do($sql) };
1660 if ( $EVAL_ERROR ) {
1661 die "Error setting NAMES to $charset: $EVAL_ERROR";
1662@@ -2208,13 +2247,8 @@
1663 }
1664 }
1665
1666- if ( my $var = $self->prop('set-vars') ) {
1667- $sql = "SET $var";
1668- PTDEBUG && _d($dbh, ':', $sql);
1669- eval { $dbh->do($sql) };
1670- if ( $EVAL_ERROR ) {
1671- die "Error setting $var: $EVAL_ERROR";
1672- }
1673+ if ( my $vars = $self->prop('set-vars') ) {
1674+ $self->set_vars($dbh, $vars);
1675 }
1676
1677 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
1678@@ -2289,6 +2323,57 @@
1679 return \%new_dsn;
1680 }
1681
1682+sub set_vars {
1683+ my ($self, $dbh, $vars) = @_;
1684+
1685+ return unless $vars;
1686+
1687+ foreach my $var ( sort keys %$vars ) {
1688+ my $val = $vars->{$var}->{val};
1689+
1690+ (my $quoted_var = $var) =~ s/_/\\_/;
1691+ my ($var_exists, $current_val);
1692+ eval {
1693+ ($var_exists, $current_val) = $dbh->selectrow_array(
1694+ "SHOW VARIABLES LIKE '$quoted_var'");
1695+ };
1696+ my $e = $EVAL_ERROR;
1697+ if ( $e ) {
1698+ PTDEBUG && _d($e);
1699+ }
1700+
1701+ if ( $vars->{$var}->{default} && !$var_exists ) {
1702+ PTDEBUG && _d('Not setting default var', $var,
1703+ 'because it does not exist');
1704+ next;
1705+ }
1706+
1707+ if ( $current_val && $current_val eq $val ) {
1708+ PTDEBUG && _d('Not setting var', $var, 'because its value',
1709+ 'is already', $val);
1710+ next;
1711+ }
1712+
1713+ my $sql = "SET SESSION $var=$val";
1714+ PTDEBUG && _d($dbh, $sql);
1715+ eval { $dbh->do($sql) };
1716+ if ( my $set_error = $EVAL_ERROR ) {
1717+ chomp($set_error);
1718+ $set_error =~ s/ at \S+ line \d+//;
1719+ my $msg = "Error setting $var: $set_error";
1720+ if ( $current_val ) {
1721+ $msg .= " The current value for $var is $current_val. "
1722+ . "If the variable is read only (not dynamic), specify "
1723+ . "--set-vars $var=$current_val to avoid this warning, "
1724+ . "else manually set the variable and restart MySQL.";
1725+ }
1726+ warn $msg . "\n\n";
1727+ }
1728+ }
1729+
1730+ return;
1731+}
1732+
1733 sub _d {
1734 my ($package, undef, $line) = caller 0;
1735 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1736@@ -2506,427 +2591,6 @@
1737 # ###########################################################################
1738
1739 # ###########################################################################
1740-# ReportFormatter package
1741-# This package is a copy without comments from the original. The original
1742-# with comments and its test file can be found in the Bazaar repository at,
1743-# lib/ReportFormatter.pm
1744-# t/lib/ReportFormatter.t
1745-# See https://launchpad.net/percona-toolkit for more information.
1746-# ###########################################################################
1747-{
1748-package ReportFormatter;
1749-
1750-use Lmo;
1751-use English qw(-no_match_vars);
1752-use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1753-
1754-use List::Util qw(min max);
1755-use POSIX qw(ceil);
1756-
1757-eval { require Term::ReadKey };
1758-my $have_term = $EVAL_ERROR ? 0 : 1;
1759-
1760-
1761-has underline_header => (
1762- is => 'ro',
1763- isa => 'Bool',
1764- default => sub { 1 },
1765-);
1766-has line_prefix => (
1767- is => 'ro',
1768- isa => 'Str',
1769- default => sub { '# ' },
1770-);
1771-has line_width => (
1772- is => 'ro',
1773- isa => 'Int',
1774- default => sub { 78 },
1775-);
1776-has column_spacing => (
1777- is => 'ro',
1778- isa => 'Str',
1779- default => sub { ' ' },
1780-);
1781-has extend_right => (
1782- is => 'ro',
1783- isa => 'Bool',
1784- default => sub { '' },
1785-);
1786-has truncate_line_mark => (
1787- is => 'ro',
1788- isa => 'Str',
1789- default => sub { '...' },
1790-);
1791-has column_errors => (
1792- is => 'ro',
1793- isa => 'Str',
1794- default => sub { 'warn' },
1795-);
1796-has truncate_header_side => (
1797- is => 'ro',
1798- isa => 'Str',
1799- default => sub { 'left' },
1800-);
1801-has strip_whitespace => (
1802- is => 'ro',
1803- isa => 'Bool',
1804- default => sub { 1 },
1805-);
1806-has title => (
1807- is => 'rw',
1808- isa => 'Str',
1809- predicate => 'has_title',
1810-);
1811-
1812-
1813-has n_cols => (
1814- is => 'rw',
1815- isa => 'Int',
1816- default => sub { 0 },
1817- init_arg => undef,
1818-);
1819-
1820-has cols => (
1821- is => 'ro',
1822- isa => 'ArrayRef',
1823- init_arg => undef,
1824- default => sub { [] },
1825- clearer => 'clear_cols',
1826-);
1827-
1828-has lines => (
1829- is => 'ro',
1830- isa => 'ArrayRef',
1831- init_arg => undef,
1832- default => sub { [] },
1833- clearer => 'clear_lines',
1834-);
1835-
1836-has truncate_headers => (
1837- is => 'rw',
1838- isa => 'Bool',
1839- default => sub { undef },
1840- init_arg => undef,
1841- clearer => 'clear_truncate_headers',
1842-);
1843-
1844-sub BUILDARGS {
1845- my $class = shift;
1846- my $args = $class->SUPER::BUILDARGS(@_);
1847-
1848- if ( ($args->{line_width} || '') eq 'auto' ) {
1849- die "Cannot auto-detect line width because the Term::ReadKey module "
1850- . "is not installed" unless $have_term;
1851- ($args->{line_width}) = GetTerminalSize();
1852- PTDEBUG && _d('Line width:', $args->{line_width});
1853- }
1854-
1855- return $args;
1856-}
1857-
1858-sub set_columns {
1859- my ( $self, @cols ) = @_;
1860- my $min_hdr_wid = 0; # check that header fits on line
1861- my $used_width = 0;
1862- my @auto_width_cols;
1863-
1864- for my $i ( 0..$#cols ) {
1865- my $col = $cols[$i];
1866- my $col_name = $col->{name};
1867- my $col_len = length $col_name;
1868- die "Column does not have a name" unless defined $col_name;
1869-
1870- if ( $col->{width} ) {
1871- $col->{width_pct} = ceil(($col->{width} * 100) / $self->line_width());
1872- PTDEBUG && _d('col:', $col_name, 'width:', $col->{width}, 'chars =',
1873- $col->{width_pct}, '%');
1874- }
1875-
1876- if ( $col->{width_pct} ) {
1877- $used_width += $col->{width_pct};
1878- }
1879- else {
1880- PTDEBUG && _d('Auto width col:', $col_name);
1881- $col->{auto_width} = 1;
1882- push @auto_width_cols, $i;
1883- }
1884-
1885- $col->{truncate} = 1 unless defined $col->{truncate};
1886- $col->{truncate_mark} = '...' unless defined $col->{truncate_mark};
1887- $col->{truncate_side} ||= 'right';
1888- $col->{undef_value} = '' unless defined $col->{undef_value};
1889-
1890- $col->{min_val} = 0;
1891- $col->{max_val} = 0;
1892-
1893- $min_hdr_wid += $col_len;
1894- $col->{header_width} = $col_len;
1895-
1896- $col->{right_most} = 1 if $i == $#cols;
1897-
1898- push @{$self->cols}, $col;
1899- }
1900-
1901- $self->n_cols( scalar @cols );
1902-
1903- if ( ($used_width || 0) > 100 ) {
1904- die "Total width_pct for all columns is >100%";
1905- }
1906-
1907- if ( @auto_width_cols ) {
1908- my $wid_per_col = int((100 - $used_width) / scalar @auto_width_cols);
1909- PTDEBUG && _d('Line width left:', (100-$used_width), '%;',
1910- 'each auto width col:', $wid_per_col, '%');
1911- map { $self->cols->[$_]->{width_pct} = $wid_per_col } @auto_width_cols;
1912- }
1913-
1914- $min_hdr_wid += ($self->n_cols() - 1) * length $self->column_spacing();
1915- PTDEBUG && _d('min header width:', $min_hdr_wid);
1916- if ( $min_hdr_wid > $self->line_width() ) {
1917- PTDEBUG && _d('Will truncate headers because min header width',
1918- $min_hdr_wid, '> line width', $self->line_width());
1919- $self->truncate_headers(1);
1920- }
1921-
1922- return;
1923-}
1924-
1925-sub add_line {
1926- my ( $self, @vals ) = @_;
1927- my $n_vals = scalar @vals;
1928- if ( $n_vals != $self->n_cols() ) {
1929- $self->_column_error("Number of values $n_vals does not match "
1930- . "number of columns " . $self->n_cols());
1931- }
1932- for my $i ( 0..($n_vals-1) ) {
1933- my $col = $self->cols->[$i];
1934- my $val = defined $vals[$i] ? $vals[$i] : $col->{undef_value};
1935- if ( $self->strip_whitespace() ) {
1936- $val =~ s/^\s+//g;
1937- $val =~ s/\s+$//;
1938- $vals[$i] = $val;
1939- }
1940- my $width = length $val;
1941- $col->{min_val} = min($width, ($col->{min_val} || $width));
1942- $col->{max_val} = max($width, ($col->{max_val} || $width));
1943- }
1944- push @{$self->lines}, \@vals;
1945- return;
1946-}
1947-
1948-sub get_report {
1949- my ( $self, %args ) = @_;
1950-
1951- $self->_calculate_column_widths();
1952- if ( $self->truncate_headers() ) {
1953- $self->_truncate_headers();
1954- }
1955- $self->_truncate_line_values(%args);
1956-
1957- my @col_fmts = $self->_make_column_formats();
1958- my $fmt = $self->line_prefix()
1959- . join($self->column_spacing(), @col_fmts);
1960- PTDEBUG && _d('Format:', $fmt);
1961-
1962- (my $hdr_fmt = $fmt) =~ s/%([^-])/%-$1/g;
1963-
1964- my @lines;
1965- push @lines, $self->line_prefix() . $self->title() if $self->has_title();
1966- push @lines, $self->_truncate_line(
1967- sprintf($hdr_fmt, map { $_->{name} } @{$self->cols}),
1968- strip => 1,
1969- mark => '',
1970- );
1971-
1972- if ( $self->underline_header() ) {
1973- my @underlines = map { '=' x $_->{print_width} } @{$self->cols};
1974- push @lines, $self->_truncate_line(
1975- sprintf($fmt, map { $_ || '' } @underlines),
1976- mark => '',
1977- );
1978- }
1979-
1980- push @lines, map {
1981- my $vals = $_;
1982- my $i = 0;
1983- my @vals = map {
1984- my $val = defined $_ ? $_ : $self->cols->[$i++]->{undef_value};
1985- $val = '' if !defined $val;
1986- $val =~ s/\n/ /g;
1987- $val;
1988- } @$vals;
1989- my $line = sprintf($fmt, @vals);
1990- if ( $self->extend_right() ) {
1991- $line;
1992- }
1993- else {
1994- $self->_truncate_line($line);
1995- }
1996- } @{$self->lines};
1997-
1998- $self->clear_cols();
1999- $self->clear_lines();
2000- $self->clear_truncate_headers();
2001-
2002- return join("\n", @lines) . "\n";
2003-}
2004-
2005-sub truncate_value {
2006- my ( $self, $col, $val, $width, $side ) = @_;
2007- return $val if length $val <= $width;
2008- return $val if $col->{right_most} && $self->extend_right();
2009- $side ||= $col->{truncate_side};
2010- my $mark = $col->{truncate_mark};
2011- if ( $side eq 'right' ) {
2012- $val = substr($val, 0, $width - length $mark);
2013- $val .= $mark;
2014- }
2015- elsif ( $side eq 'left') {
2016- $val = $mark . substr($val, -1 * $width + length $mark);
2017- }
2018- else {
2019- PTDEBUG && _d("I don't know how to", $side, "truncate values");
2020- }
2021- return $val;
2022-}
2023-
2024-sub _calculate_column_widths {
2025- my ( $self ) = @_;
2026-
2027- my $extra_space = 0;
2028- foreach my $col ( @{$self->cols} ) {
2029- my $print_width = int($self->line_width() * ($col->{width_pct} / 100));
2030-
2031- PTDEBUG && _d('col:', $col->{name}, 'width pct:', $col->{width_pct},
2032- 'char width:', $print_width,
2033- 'min val:', $col->{min_val}, 'max val:', $col->{max_val});
2034-
2035- if ( $col->{auto_width} ) {
2036- if ( $col->{min_val} && $print_width < $col->{min_val} ) {
2037- PTDEBUG && _d('Increased to min val width:', $col->{min_val});
2038- $print_width = $col->{min_val};
2039- }
2040- elsif ( $col->{max_val} && $print_width > $col->{max_val} ) {
2041- PTDEBUG && _d('Reduced to max val width:', $col->{max_val});
2042- $extra_space += $print_width - $col->{max_val};
2043- $print_width = $col->{max_val};
2044- }
2045- }
2046-
2047- $col->{print_width} = $print_width;
2048- PTDEBUG && _d('print width:', $col->{print_width});
2049- }
2050-
2051- PTDEBUG && _d('Extra space:', $extra_space);
2052- while ( $extra_space-- ) {
2053- foreach my $col ( @{$self->cols} ) {
2054- if ( $col->{auto_width}
2055- && ( $col->{print_width} < $col->{max_val}
2056- || $col->{print_width} < $col->{header_width})
2057- ) {
2058- $col->{print_width}++;
2059- }
2060- }
2061- }
2062-
2063- return;
2064-}
2065-
2066-sub _truncate_headers {
2067- my ( $self, $col ) = @_;
2068- my $side = $self->truncate_header_side();
2069- foreach my $col ( @{$self->cols} ) {
2070- my $col_name = $col->{name};
2071- my $print_width = $col->{print_width};
2072- next if length $col_name <= $print_width;
2073- $col->{name} = $self->truncate_value($col, $col_name, $print_width, $side);
2074- PTDEBUG && _d('Truncated hdr', $col_name, 'to', $col->{name},
2075- 'max width:', $print_width);
2076- }
2077- return;
2078-}
2079-
2080-sub _truncate_line_values {
2081- my ( $self, %args ) = @_;
2082- my $n_vals = $self->n_cols() - 1;
2083- foreach my $vals ( @{$self->lines} ) {
2084- for my $i ( 0..$n_vals ) {
2085- my $col = $self->cols->[$i];
2086- my $val = defined $vals->[$i] ? $vals->[$i] : $col->{undef_value};
2087- my $width = length $val;
2088-
2089- if ( $col->{print_width} && $width > $col->{print_width} ) {
2090- if ( !$col->{truncate} ) {
2091- $self->_column_error("Value '$val' is too wide for column "
2092- . $col->{name});
2093- }
2094-
2095- my $callback = $args{truncate_callback};
2096- my $print_width = $col->{print_width};
2097- $val = $callback ? $callback->($col, $val, $print_width)
2098- : $self->truncate_value($col, $val, $print_width);
2099- PTDEBUG && _d('Truncated val', $vals->[$i], 'to', $val,
2100- '; max width:', $print_width);
2101- $vals->[$i] = $val;
2102- }
2103- }
2104- }
2105- return;
2106-}
2107-
2108-sub _make_column_formats {
2109- my ( $self ) = @_;
2110- my @col_fmts;
2111- my $n_cols = $self->n_cols() - 1;
2112- for my $i ( 0..$n_cols ) {
2113- my $col = $self->cols->[$i];
2114-
2115- my $width = $col->{right_most} && !$col->{right_justify} ? ''
2116- : $col->{print_width};
2117-
2118- my $col_fmt = '%' . ($col->{right_justify} ? '' : '-') . $width . 's';
2119- push @col_fmts, $col_fmt;
2120- }
2121- return @col_fmts;
2122-}
2123-
2124-sub _truncate_line {
2125- my ( $self, $line, %args ) = @_;
2126- my $mark = defined $args{mark} ? $args{mark} : $self->truncate_line_mark();
2127- if ( $line ) {
2128- $line =~ s/\s+$// if $args{strip};
2129- my $len = length($line);
2130- if ( $len > $self->line_width() ) {
2131- $line = substr($line, 0, $self->line_width() - length $mark);
2132- $line .= $mark if $mark;
2133- }
2134- }
2135- return $line;
2136-}
2137-
2138-sub _column_error {
2139- my ( $self, $err ) = @_;
2140- my $msg = "Column error: $err";
2141- $self->column_errors() eq 'die' ? die $msg : warn $msg;
2142- return;
2143-}
2144-
2145-sub _d {
2146- my ($package, undef, $line) = caller 0;
2147- @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
2148- map { defined $_ ? $_ : 'undef' }
2149- @_;
2150- print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
2151-}
2152-
2153-no Lmo;
2154-1;
2155-}
2156-# ###########################################################################
2157-# End ReportFormatter package
2158-# ###########################################################################
2159-
2160-# ###########################################################################
2161 # Quoter package
2162 # This package is a copy without comments from the original. The original
2163 # with comments and its test file can be found in the Bazaar repository at,
2164@@ -5617,9 +5281,9 @@
2165 return $self->{one_nibble};
2166 }
2167
2168-sub chunk_size {
2169+sub limit {
2170 my ($self) = @_;
2171- return $self->{limit} + 1;
2172+ return $self->{limit};
2173 }
2174
2175 sub set_chunk_size {
2176@@ -7870,6 +7534,7 @@
2177 use Percona::Toolkit;
2178 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
2179
2180+use List::Util qw(max);
2181 use Time::HiRes qw(time sleep);
2182 use Data::Dumper;
2183 $Data::Dumper::Indent = 1;
2184@@ -7905,7 +7570,7 @@
2185 $o->get_opts();
2186
2187 my $dp = $o->DSNParser();
2188- $dp->prop('set-vars', $o->get('set-vars'));
2189+ $dp->prop('set-vars', $o->set_vars());
2190
2191 # The original table, i.e. the one being altered, must be specified
2192 # on the command line via the DSN.
2193@@ -8010,37 +7675,7 @@
2194 # ########################################################################
2195 my $set_on_connect = sub {
2196 my ($dbh) = @_;
2197-
2198- # See the same code in pt-table-checksum.
2199- my $lock_wait_timeout = $o->get('lock-wait-timeout');
2200- my $set_lwt = "SET SESSION innodb_lock_wait_timeout=$lock_wait_timeout";
2201- PTDEBUG && _d($set_lwt);
2202- eval {
2203- $dbh->do($set_lwt);
2204- };
2205- if ( $EVAL_ERROR ) {
2206- PTDEBUG && _d($EVAL_ERROR);
2207- # Get the server's current value.
2208- my $sql = "SHOW SESSION VARIABLES LIKE 'innodb_lock_wait_timeout'";
2209- PTDEBUG && _d($dbh, $sql);
2210- my (undef, $curr_lwt) = $dbh->selectrow_array($sql);
2211- PTDEBUG && _d('innodb_lock_wait_timeout on server:', $curr_lwt);
2212- if ( !defined $curr_lwt ) {
2213- PTDEBUG && _d('innodb_lock_wait_timeout does not exist;',
2214- 'InnoDB is probably disabled');
2215- }
2216- elsif ( $curr_lwt > $lock_wait_timeout ) {
2217- warn "Failed to $set_lwt: $EVAL_ERROR\n"
2218- . "The current innodb_lock_wait_timeout value "
2219- . "$curr_lwt is greater than the --lock-wait-timeout "
2220- . "value $lock_wait_timeout and the variable cannot be "
2221- . "changed. innodb_lock_wait_timeout is only dynamic when "
2222- . "using the InnoDB plugin. To prevent this warning, either "
2223- . "specify --lock-wait-time=$curr_lwt, or manually set "
2224- . "innodb_lock_wait_timeout to a value less than or equal "
2225- . "to $lock_wait_timeout and restart MySQL.\n";
2226- }
2227- }
2228+ return;
2229 };
2230
2231 # Do not call "new Cxn(" directly; use this sub so that set_on_connect
2232@@ -8051,7 +7686,7 @@
2233 # and it might be good to just make a convention of it.
2234 my $make_cxn = sub {
2235 my (%args) = @_;
2236- my $cxn = new Cxn(
2237+ my $cxn = Cxn->new(
2238 %args,
2239 DSNParser => $dp,
2240 OptionParser => $o,
2241@@ -8064,7 +7699,8 @@
2242 return $cxn;
2243 };
2244
2245- my $cxn = $make_cxn->(dsn => $dsn);
2246+ my $cxn = $make_cxn->(dsn => $dsn);
2247+ my $aux_cxn = $make_cxn->(dsn => $dsn, prev_dsn => $dsn);
2248
2249 my $cluster = Percona::XtraDB::Cluster->new;
2250 if ( $cluster->is_cluster_node($cxn) ) {
2251@@ -8110,6 +7746,32 @@
2252 my $lock_in_share_mode = $server_version < '5.1' ? 0 : 1;
2253
2254 # ########################################################################
2255+ # Create --plugin.
2256+ # ########################################################################
2257+ my $plugin;
2258+ if ( my $file = $o->get('plugin') ) {
2259+ die "--plugin file $file does not exist\n" unless -f $file;
2260+ eval {
2261+ require $file;
2262+ };
2263+ die "Error loading --plugin $file: $EVAL_ERROR" if $EVAL_ERROR;
2264+ eval {
2265+ $plugin = pt_online_schema_change_plugin->new(
2266+ cxn => $cxn,
2267+ aux_cxn => $aux_cxn,
2268+ alter => $o->get('alter'),
2269+ execute => $o->get('execute'),
2270+ dry_run => $o->get('dry-run'),
2271+ print => $o->get('print'),
2272+ quiet => $o->get('quiet'),
2273+ Quoter => $q,
2274+ );
2275+ };
2276+ die "Error creating --plugin: $EVAL_ERROR" if $EVAL_ERROR;
2277+ print "Created plugin from $file.\n";
2278+ }
2279+
2280+ # ########################################################################
2281 # Setup lag and load monitors.
2282 # ########################################################################
2283 my $slaves; # all slaves that are found or specified
2284@@ -8290,7 +7952,7 @@
2285 ],
2286 );
2287 }
2288-
2289+
2290 # ########################################################################
2291 # Setup and check the original table.
2292 # ########################################################################
2293@@ -8452,6 +8114,21 @@
2294 return;
2295 };
2296
2297+ # The 2nd to last cleanup task is printing the --statistics which
2298+ # may reveal something about the failure.
2299+ if ( $o->get('statistics') ) {
2300+ push @cleanup_tasks, sub {
2301+ my $n = max( map { length $_ } keys %stats );
2302+ my $fmt = "# %-${n}s %5s\n";
2303+ printf $fmt, 'Event', 'Count';
2304+ printf $fmt, ('=' x $n),'=====';
2305+ foreach my $event ( sort keys %stats ) {
2306+ printf $fmt,
2307+ $event, (defined $stats{$event} ? $stats{$event} : '?');
2308+ }
2309+ };
2310+ }
2311+
2312 # ########################################################################
2313 # Check the --alter statement.
2314 # ########################################################################
2315@@ -8491,9 +8168,30 @@
2316 $daemon->make_PID_file();
2317 }
2318
2319+ # ########################################################################
2320+ # Init the --plugin.
2321+ # ########################################################################
2322+
2323+ # --plugin hook
2324+ if ( $plugin && $plugin->can('init') ) {
2325+ $plugin->init(
2326+ orig_tbl => $orig_tbl,
2327+ child_tables => $child_tables,
2328+ renamed_cols => $renamed_cols,
2329+ slaves => $slaves,
2330+ slave_lag_cxns => $slave_lag_cxns,
2331+ );
2332+ }
2333+
2334 # #####################################################################
2335 # Step 1: Create the new table.
2336 # #####################################################################
2337+
2338+ # --plugin hook
2339+ if ( $plugin && $plugin->can('before_create_new_table') ) {
2340+ $plugin->before_create_new_table();
2341+ }
2342+
2343 my $new_tbl;
2344 eval {
2345 $new_tbl = create_new_table(
2346@@ -8564,10 +8262,25 @@
2347 );
2348 }
2349
2350+ # --plugin hook
2351+ if ( $plugin && $plugin->can('after_create_new_table') ) {
2352+ $plugin->after_create_new_table(
2353+ new_tbl => $new_tbl,
2354+ );
2355+ }
2356+
2357 # #####################################################################
2358 # Step 2: Alter the new, empty table. This should be very quick,
2359 # or die if the user specified a bad alter statement.
2360 # #####################################################################
2361+
2362+ # --plugin hook
2363+ if ( $plugin && $plugin->can('before_alter_new_table') ) {
2364+ $plugin->before_alter_new_table(
2365+ new_tbl => $new_tbl,
2366+ );
2367+ }
2368+
2369 if ( my $alter = $o->get('alter') ) {
2370 print "Altering new table...\n";
2371 my $sql = "ALTER TABLE $new_tbl->{name} $alter";
2372@@ -8679,22 +8392,43 @@
2373 'columns', @$del_cols);
2374 }
2375
2376+ # --plugin hook
2377+ if ( $plugin && $plugin->can('after_alter_new_table') ) {
2378+ $plugin->after_alter_new_table(
2379+ new_tbl => $new_tbl,
2380+ del_tbl => $del_tbl,
2381+ );
2382+ }
2383+
2384 # ########################################################################
2385 # Step 3: Create the triggers to capture changes on the original table and
2386 # apply them to the new table.
2387 # ########################################################################
2388
2389+ my $retry = new Retry();
2390+
2391 # Drop the triggers. We can save this cleanup task before
2392 # adding the triggers because if adding them fails, this will be
2393 # called which will drop whichever triggers were created.
2394 push @cleanup_tasks, sub {
2395 PTDEBUG && _d('Clean up triggers');
2396+ # --plugin hook
2397+ if ( $plugin && $plugin->can('before_drop_triggers') ) {
2398+ $plugin->before_drop_triggers(
2399+ oktorun => $oktorun,
2400+ drop_trigger_sqls => \@drop_trigger_sqls,
2401+ );
2402+ }
2403+
2404 if ( $oktorun ) {
2405 drop_triggers(
2406 tbl => $orig_tbl,
2407 Cxn => $cxn,
2408 Quoter => $q,
2409 OptionParser => $o,
2410+ Retry => $retry,
2411+ retries => $o->get('retries'),
2412+ stats => \%stats,
2413 );
2414 }
2415 else {
2416@@ -8704,6 +8438,11 @@
2417 }
2418 };
2419
2420+ # --plugin hook
2421+ if ( $plugin && $plugin->can('before_create_triggers') ) {
2422+ $plugin->before_create_triggers();
2423+ }
2424+
2425 my @trigger_names = eval {
2426 create_triggers(
2427 orig_tbl => $orig_tbl,
2428@@ -8713,12 +8452,20 @@
2429 Cxn => $cxn,
2430 Quoter => $q,
2431 OptionParser => $o,
2432+ Retry => $retry,
2433+ retries => $o->get('retries'),
2434+ stats => \%stats,
2435 );
2436 };
2437 if ( $EVAL_ERROR ) {
2438 die "Error creating triggers: $EVAL_ERROR\n";
2439 };
2440
2441+ # --plugin hook
2442+ if ( $plugin && $plugin->can('after_create_triggers') ) {
2443+ $plugin->after_create_triggers();
2444+ }
2445+
2446 # #####################################################################
2447 # Step 4: Copy rows.
2448 # #####################################################################
2449@@ -8730,7 +8477,6 @@
2450 my $total_rows = 0;
2451 my $total_time = 0;
2452 my $avg_rate = 0; # rows/second
2453- my $retry = new Retry(); # for retrying to exec the copy statement
2454 my $limit = $o->get('chunk-size-limit'); # brevity
2455 my $chunk_time = $o->get('chunk-time'); # brevity
2456
2457@@ -8850,7 +8596,7 @@
2458 my $expl = explain_statement(
2459 tbl => $tbl,
2460 sth => $sth->{explain_upper_boundary},
2461- vals => [ @{$boundary->{lower}}, $nibble_iter->chunk_size() ],
2462+ vals => [ @{$boundary->{lower}}, $nibble_iter->limit() ],
2463 );
2464 if (lc($expl->{key} || '') ne lc($nibble_iter->nibble_index() || '')) {
2465 my $msg
2466@@ -8865,7 +8611,7 @@
2467 . $sth->{upper_boundary}->{Statement}
2468 . " with values "
2469 . join(", ", map { defined $_ ? $_ : "NULL" }
2470- (@{$boundary->{lower}}, $nibble_iter->chunk_size()))
2471+ (@{$boundary->{lower}}, $nibble_iter->limit()))
2472 . "\n";
2473 die $msg;
2474 }
2475@@ -8896,10 +8642,10 @@
2476 # Exec and time the chunk checksum query.
2477 $tbl->{nibble_time} = exec_nibble(
2478 %args,
2479- Retry => $retry,
2480- Quoter => $q,
2481- OptionParser => $o,
2482- stats => \%stats,
2483+ retries => $o->get('retries'),
2484+ Retry => $retry,
2485+ Quoter => $q,
2486+ stats => \%stats,
2487 );
2488 PTDEBUG && _d('Nibble time:', $tbl->{nibble_time});
2489
2490@@ -9031,6 +8777,11 @@
2491 );
2492 }
2493
2494+ # --plugin hook
2495+ if ( $plugin && $plugin->can('before_copy_rows') ) {
2496+ $plugin->before_copy_rows();
2497+ }
2498+
2499 # Start copying rows. This may take awhile, but --progress is on
2500 # by default so there will be progress updates to stderr.
2501 eval {
2502@@ -9071,6 +8822,11 @@
2503 }
2504 }
2505
2506+ # --plugin hook
2507+ if ( $plugin && $plugin->can('after_copy_rows') ) {
2508+ $plugin->after_copy_rows();
2509+ }
2510+
2511 # #####################################################################
2512 # XXX
2513 # Step 5: Rename tables: orig -> old, new -> orig
2514@@ -9079,6 +8835,12 @@
2515 # state the tables are left in.
2516 # XXX
2517 # #####################################################################
2518+
2519+ # --plugin hook
2520+ if ( $plugin && $plugin->can('before_swap_tables') ) {
2521+ $plugin->before_swap_tables();
2522+ }
2523+
2524 my $old_tbl;
2525 if ( $o->get('swap-tables') ) {
2526 eval {
2527@@ -9089,22 +8851,38 @@
2528 Cxn => $cxn,
2529 Quoter => $q,
2530 OptionParser => $o,
2531+ Retry => $retry,
2532+ retries => $o->get('retries'),
2533+ stats => \%stats,
2534 );
2535 };
2536 if ( $EVAL_ERROR ) {
2537- die "Error swapping the tables: $EVAL_ERROR\n"
2538- . "Verify that the original table $orig_tbl->{name} has not "
2539- . "been modified or renamed to the old table $old_tbl->{name}. "
2540- . "Then drop the new table $new_tbl->{name} if it exists.\n";
2541+ # TODO: one of these values can be undefined
2542+ die "Error swapping tables: $EVAL_ERROR\n"
2543+ . "To clean up, first verify that the original table "
2544+ . "$orig_tbl->{name} has not been modified or renamed, "
2545+ . "then drop the new table $new_tbl->{name} if it exists.\n";
2546 }
2547 }
2548 $orig_tbl->{swapped} = 1; # flag for cleanup tasks
2549 PTDEBUG && _d('Old table:', Dumper($old_tbl));
2550
2551+ # --plugin hook
2552+ if ( $plugin && $plugin->can('after_swap_tables') ) {
2553+ $plugin->after_swap_tables(
2554+ old_tbl => $old_tbl,
2555+ );
2556+ }
2557+
2558 # #####################################################################
2559 # Step 6: Update foreign key constraints if there are child tables.
2560 # #####################################################################
2561 if ( $child_tables ) {
2562+ # --plugin hook
2563+ if ( $plugin && $plugin->can('before_update_foreign_keys') ) {
2564+ $plugin->before_update_foreign_keys();
2565+ }
2566+
2567 eval {
2568 if ( $alter_fk_method eq 'none' ) {
2569 # This shouldn't happen, but in case it does we should know.
2570@@ -9121,6 +8899,8 @@
2571 Cxn => $cxn,
2572 TableParser => $tp,
2573 stats => \%stats,
2574+ Retry => $retry,
2575+ retries => $o->get('retries'),
2576 );
2577 }
2578 elsif ( $alter_fk_method eq 'drop_swap' ) {
2579@@ -9129,6 +8909,9 @@
2580 new_tbl => $new_tbl,
2581 Cxn => $cxn,
2582 OptionParser => $o,
2583+ stats => \%stats,
2584+ Retry => $retry,
2585+ retries => $o->get('retries'),
2586 );
2587 }
2588 elsif ( !$alter_fk_method
2589@@ -9147,6 +8930,11 @@
2590 # TODO: improve error message and handling.
2591 die "Error updating foreign key constraints: $EVAL_ERROR\n";
2592 }
2593+
2594+ # --plugin hook
2595+ if ( $plugin && $plugin->can('after_update_foreign_keys') ) {
2596+ $plugin->after_update_foreign_keys();
2597+ }
2598 }
2599
2600 # ########################################################################
2601@@ -9160,6 +8948,11 @@
2602 print "Not dropping old table because --no-swap-tables was specified.\n";
2603 }
2604 else {
2605+ # --plugin hook
2606+ if ( $plugin && $plugin->can('before_drop_old_table') ) {
2607+ $plugin->before_drop_old_table();
2608+ }
2609+
2610 print "Dropping old table...\n";
2611
2612 if ( $alter_fk_method eq 'none' ) {
2613@@ -9182,32 +8975,25 @@
2614 die "Error dropping the old table: $EVAL_ERROR\n";
2615 }
2616 print "Dropped old table $old_tbl->{name} OK.\n";
2617+
2618+ # --plugin hook
2619+ if ( $plugin && $plugin->can('after_drop_old_table') ) {
2620+ $plugin->after_drop_old_table();
2621+ }
2622 }
2623 }
2624-
2625+
2626 # ########################################################################
2627 # Done.
2628 # ########################################################################
2629 $orig_tbl->{success} = 1; # flag for cleanup tasks
2630 $cleanup = undef; # exec cleanup tasks
2631
2632- if ( $o->get('statistics') ) {
2633- my $report = new ReportFormatter(
2634- line_width => 74,
2635- );
2636- $report->set_columns(
2637- { name => 'Event', },
2638- { name => 'Count', right_justify => 1 },
2639- );
2640-
2641- foreach my $event ( sort keys %stats ) {
2642- $report->add_line(
2643- $event,
2644- $stats{$event},
2645- );
2646- }
2647-
2648- print $report->get_report();
2649+ # --plugin hook
2650+ if ( $plugin && $plugin->can('before_exit') ) {
2651+ $plugin->before_exit(
2652+ exit_status => $exit_status,
2653+ );
2654 }
2655
2656 return $exit_status;
2657@@ -9424,7 +9210,7 @@
2658 return 1; # safe
2659 }
2660
2661-sub create_new_table{
2662+sub create_new_table {
2663 my (%args) = @_;
2664 my @required_args = qw(orig_tbl Cxn Quoter OptionParser TableParser);
2665 foreach my $arg ( @required_args ) {
2666@@ -9514,15 +9300,16 @@
2667
2668 sub swap_tables {
2669 my (%args) = @_;
2670- my @required_args = qw(orig_tbl new_tbl Cxn Quoter OptionParser);
2671+ my @required_args = qw(orig_tbl new_tbl Cxn Quoter OptionParser Retry retries stats);
2672 foreach my $arg ( @required_args ) {
2673 die "I need a $arg argument" unless $args{$arg};
2674 }
2675- my ($orig_tbl, $new_tbl, $cxn, $q, $o) = @args{@required_args};
2676+ my ($orig_tbl, $new_tbl, $cxn, $q, $o, $retry, $retries, $stats) = @args{@required_args};
2677
2678- my $prefix = '_';
2679- my $table_name = $orig_tbl->{tbl} . ($args{suffix} || '');
2680- my $tries = 10; # don't try forever
2681+ my $prefix = '_';
2682+ my $table_name = $orig_tbl->{tbl} . ($args{suffix} || '');
2683+ my $tries = 10; # don't try forever
2684+ my $table_exists = qr/table.+?already exists/i;
2685
2686 # This sub only works for --execute. Since the options are
2687 # mutually exclusive and we return in the if case, the elsif
2688@@ -9538,10 +9325,10 @@
2689 }
2690 elsif ( $o->get('execute') ) {
2691 print "Swapping tables...\n";
2692-
2693+
2694 while ( $tries-- ) {
2695 $table_name = $prefix . $table_name;
2696-
2697+
2698 if ( length($table_name) > 64 ) {
2699 my $truncated_table_name = substr($table_name, 0, 64);
2700 PTDEBUG && _d($table_name, 'is over 64 characters long, truncating to',
2701@@ -9552,22 +9339,36 @@
2702 my $sql = "RENAME TABLE $orig_tbl->{name} "
2703 . "TO " . $q->quote($orig_tbl->{db}, $table_name)
2704 . ", $new_tbl->{name} TO $orig_tbl->{name}";
2705- PTDEBUG && _d($sql);
2706+
2707 eval {
2708- $cxn->dbh()->do($sql);
2709+ osc_retry(
2710+ Cxn => $cxn,
2711+ Retry => $retry,
2712+ retries => $retries,
2713+ stats => $stats,
2714+ code => sub {
2715+ PTDEBUG && _d($sql);
2716+ $cxn->dbh()->do($sql);
2717+ },
2718+ ignore_errors => [
2719+ # Ignore this error because if multiple instances of the tool
2720+ # are running, or previous runs failed and weren't cleaned up,
2721+ # then there will be other similarly named tables with fewer
2722+ # leading prefix chars. Or, in rare cases, the db happens
2723+ # to have a similarly named table created by the user for
2724+ # other purposes.
2725+ $table_exists,
2726+ ],
2727+ );
2728 };
2729- if ( $EVAL_ERROR ) {
2730- # Ignore this error because if multiple instances of the tool
2731- # are running, or previous runs failed and weren't cleaned up,
2732- # then there will be other similarly named tables with fewer
2733- # leading prefix chars. Or, in rarer cases, the db just happens
2734- # to have a similarly named table created by the user for other
2735- # purposes.
2736- next if $EVAL_ERROR =~ m/table.+?already exists/i;
2737-
2738- # Some other error happened. Let caller catch it.
2739- die $EVAL_ERROR;
2740+ if ( my $e = $EVAL_ERROR ) {
2741+ if ( $e =~ $table_exists ) {
2742+ PTDEBUG && _d($e);
2743+ next;
2744+ }
2745+ die $e;
2746 }
2747+
2748 print $sql, "\n" if $o->get('print');
2749 print "Swapped original and new tables OK.\n";
2750 return { # success
2751@@ -9728,11 +9529,12 @@
2752 sub rebuild_constraints {
2753 my ( %args ) = @_;
2754 my @required_args = qw(orig_tbl old_tbl child_tables stats
2755- Cxn Quoter OptionParser TableParser);
2756+ Cxn Quoter OptionParser TableParser
2757+ Retry retries);
2758 foreach my $arg ( @required_args ) {
2759 die "I need a $arg argument" unless $args{$arg};
2760 }
2761- my ($orig_tbl, $old_tbl, $child_tables, $stats, $cxn, $q, $o, $tp)
2762+ my ($orig_tbl, $old_tbl, $child_tables, $stats, $cxn, $q, $o, $tp, $retry, $retries)
2763 = @args{@required_args};
2764
2765 # MySQL has a "feature" where if the parent tbl is in the same db,
2766@@ -9807,9 +9609,17 @@
2767 . join(', ', @rebuilt_constraints);
2768 print $sql, "\n" if $o->get('print');
2769 if ( $o->get('execute') ) {
2770- PTDEBUG && _d($sql);
2771- $cxn->dbh()->do($sql);
2772- $stats->{rebuilt_constraint}++;
2773+ osc_retry(
2774+ Cxn => $cxn,
2775+ Retry => $retry,
2776+ retries => $retries,
2777+ stats => $stats,
2778+ code => sub {
2779+ PTDEBUG && _d($sql);
2780+ $cxn->dbh()->do($sql);
2781+ $stats->{rebuilt_constraint}++;
2782+ },
2783+ );
2784 }
2785 }
2786
2787@@ -9822,11 +9632,11 @@
2788
2789 sub drop_swap {
2790 my ( %args ) = @_;
2791- my @required_args = qw(orig_tbl new_tbl Cxn OptionParser);
2792+ my @required_args = qw(orig_tbl new_tbl Cxn OptionParser stats Retry retries);
2793 foreach my $arg ( @required_args ) {
2794 die "I need a $arg argument" unless $args{$arg};
2795 }
2796- my ($orig_tbl, $new_tbl, $cxn, $o) = @args{@required_args};
2797+ my ($orig_tbl, $new_tbl, $cxn, $o, $stats, $retry, $retries) = @args{@required_args};
2798
2799 if ( $o->get('dry-run') ) {
2800 print "Not drop-swapping tables because this is a dry run.\n";
2801@@ -9843,8 +9653,19 @@
2802
2803 foreach my $sql ( @sqls ) {
2804 PTDEBUG && _d($sql);
2805- print $sql, "\n" if $o->get('print');
2806- $cxn->dbh()->do($sql) if $o->get('execute');
2807+ print $sql, "\n" if $o->get('print');
2808+ if ( $o->get('execute') ) {
2809+ osc_retry(
2810+ Cxn => $cxn,
2811+ Retry => $retry,
2812+ retries => $retries,
2813+ stats => $stats,
2814+ code => sub {
2815+ PTDEBUG && _d($sql);
2816+ $cxn->dbh()->do($sql);
2817+ },
2818+ );
2819+ }
2820 }
2821
2822 if ( $o->get('execute') ) {
2823@@ -9856,11 +9677,11 @@
2824
2825 sub create_triggers {
2826 my ( %args ) = @_;
2827- my @required_args = qw(orig_tbl new_tbl del_tbl columns Cxn Quoter OptionParser);
2828+ my @required_args = qw(orig_tbl new_tbl del_tbl columns Cxn Quoter OptionParser Retry retries stats);
2829 foreach my $arg ( @required_args ) {
2830 die "I need a $arg argument" unless $args{$arg};
2831 }
2832- my ($orig_tbl, $new_tbl, $del_tbl, $cols, $cxn, $q, $o) = @args{@required_args};
2833+ my ($orig_tbl, $new_tbl, $del_tbl, $cols, $cxn, $q, $o, $retry, $retries, $stats) = @args{@required_args};
2834
2835 # This sub works for --dry-run and --execute. With --dry-run it's
2836 # only interesting if --print is specified, too; then the user can
2837@@ -9928,9 +9749,16 @@
2838 my ($name, $sql) = @$trg;
2839 print $sql, "\n" if $o->get('print');
2840 if ( $o->get('execute') ) {
2841- # Let caller catch errors.
2842- PTDEBUG && _d($sql);
2843- $cxn->dbh()->do($sql);
2844+ osc_retry(
2845+ Cxn => $cxn,
2846+ Retry => $retry,
2847+ retries => $retries,
2848+ stats => $stats,
2849+ code => sub {
2850+ PTDEBUG && _d($sql);
2851+ $cxn->dbh()->do($sql);
2852+ },
2853+ );
2854 }
2855 # Only save the trigger once it has been created
2856 # (or faked to be created) so if the 2nd trigger
2857@@ -9950,11 +9778,11 @@
2858
2859 sub drop_triggers {
2860 my ( %args ) = @_;
2861- my @required_args = qw(tbl Cxn Quoter OptionParser);
2862+ my @required_args = qw(tbl Cxn Quoter OptionParser Retry retries stats);
2863 foreach my $arg ( @required_args ) {
2864 die "I need a $arg argument" unless $args{$arg};
2865 }
2866- my ($tbl, $cxn, $q, $o) = @args{@required_args};
2867+ my ($tbl, $cxn, $q, $o, $retry, $retries, $stats) = @args{@required_args};
2868
2869 # This sub works for --dry-run and --execute, although --dry-run is
2870 # only interesting with --print so the user can see the drop trigger
2871@@ -9970,9 +9798,17 @@
2872 foreach my $sql ( @drop_trigger_sqls ) {
2873 print $sql, "\n" if $o->get('print');
2874 if ( $o->get('execute') ) {
2875- PTDEBUG && _d($sql);
2876 eval {
2877- $cxn->dbh()->do($sql);
2878+ osc_retry(
2879+ Cxn => $cxn,
2880+ Retry => $retry,
2881+ retries => $retries,
2882+ stats => $stats,
2883+ code => sub {
2884+ PTDEBUG && _d($sql);
2885+ $cxn->dbh()->do($sql);
2886+ },
2887+ );
2888 };
2889 if ( $EVAL_ERROR ) {
2890 warn "Error dropping trigger: $EVAL_ERROR\n";
2891@@ -9995,15 +9831,93 @@
2892 return;
2893 }
2894
2895+sub error_event {
2896+ my ($error) = @_;
2897+ return 'undefined_error' unless $error;
2898+ my $event
2899+ = $error =~ m/Lock wait timeout/ ? 'lock_wait_timeout'
2900+ : $error =~ m/Deadlock found/ ? 'deadlock'
2901+ : $error =~ m/execution was interrupted/ ? 'query_killed'
2902+ : $error =~ m/server has gone away/ ? 'lost_connection'
2903+ : $error =~ m/Lost connection/ ? 'connection_killed'
2904+ : 'unknown_error';
2905+ return $event;
2906+}
2907+
2908+sub osc_retry {
2909+ my (%args) = @_;
2910+ my @required_args = qw(Cxn Retry retries code stats);
2911+ foreach my $arg ( @required_args ) {
2912+ die "I need a $arg argument" unless $args{$arg};
2913+ }
2914+ my $cxn = $args{Cxn};
2915+ my $retry = $args{Retry};
2916+ my $retries = $args{retries};
2917+ my $code = $args{code};
2918+ my $stats = $args{stats};
2919+ my $ignore_errors = $args{ignore_errors};
2920+
2921+ return $retry->retry(
2922+ tries => $retries,
2923+ wait => sub { sleep 0.25; return; },
2924+ try => $code,
2925+ fail => sub {
2926+ my (%args) = @_;
2927+ my $error = $args{error};
2928+ PTDEBUG && _d('Retry fail:', $error);
2929+
2930+ if ( $ignore_errors ) {
2931+ return 0 if grep { $error =~ $_ } @$ignore_errors;
2932+ }
2933+
2934+ # The query failed/caused an error. If the error is one of these,
2935+ # then we can possibly retry.
2936+ if ( $error =~ m/Lock wait timeout exceeded/
2937+ || $error =~ m/Deadlock found/
2938+ || $error =~ m/Query execution was interrupted/
2939+ ) {
2940+ # These errors/warnings can be retried, so don't print
2941+ # a warning yet; do that in final_fail.
2942+ $stats->{ error_event($error) }++;
2943+ return 1; # try again
2944+ }
2945+ elsif ( $error =~ m/MySQL server has gone away/
2946+ || $error =~ m/Lost connection to MySQL server/
2947+ ) {
2948+ # The 1st pattern means that MySQL itself died or was stopped.
2949+ # The 2nd pattern means that our cxn was killed (KILL <id>).
2950+ $stats->{ error_event($error) }++;
2951+ $cxn->connect(); # connect or die trying
2952+ return 1; # reconnected, try again
2953+ }
2954+
2955+ $stats->{retry_fail}++;
2956+
2957+ # At this point, either the error/warning cannot be retried,
2958+ # or we failed to reconnect. Don't retry; call final_fail.
2959+ return 0;
2960+ },
2961+ final_fail => sub {
2962+ my (%args) = @_;
2963+ my $error = $args{error};
2964+ # This die should be caught by the caller. Copying rows and
2965+ # the tool will stop, which is probably good because by this
2966+ # point the error or warning indicates that something is wrong.
2967+ $stats->{ error_event($error) }++;
2968+ die $error;
2969+ }
2970+ );
2971+}
2972+
2973 sub exec_nibble {
2974 my (%args) = @_;
2975- my @required_args = qw(Cxn tbl stats NibbleIterator Retry Quoter OptionParser);
2976+ my @required_args = qw(Cxn tbl stats retries Retry NibbleIterator Quoter);
2977 foreach my $arg ( @required_args ) {
2978 die "I need a $arg argument" unless $args{$arg};
2979 }
2980- my ($cxn, $tbl, $stats, $nibble_iter, $retry, $q, $o)= @args{@required_args};
2981+ my ($cxn, $tbl, $stats, $retries, $retry, $nibble_iter, $q)
2982+ = @args{@required_args};
2983
2984- my $dbh = $cxn->dbh();
2985 my $sth = $nibble_iter->statements();
2986 my $boundary = $nibble_iter->boundaries();
2987 my $lb_quoted = $q->serialize_list(@{$boundary->{lower}});
2988@@ -10035,10 +9949,12 @@
2989 },
2990 );
2991
2992- return $retry->retry(
2993- tries => $o->get('retries'),
2994- wait => sub { sleep 0.25; return; },
2995- try => sub {
2996+ return osc_retry(
2997+ Cxn => $cxn,
2998+ Retry => $retry,
2999+ retries => $retries,
3000+ stats => $stats,
3001+ code => sub {
3002 # ###################################################################
3003 # Start timing the query.
3004 # ###################################################################
3005@@ -10067,7 +9983,7 @@
3006 # Check if query caused any warnings.
3007 my $sql_warn = 'SHOW WARNINGS';
3008 PTDEBUG && _d($sql_warn);
3009- my $warnings = $dbh->selectall_arrayref($sql_warn, { Slice => {} } );
3010+ my $warnings = $cxn->dbh->selectall_arrayref($sql_warn, {Slice => {}});
3011 foreach my $warning ( @$warnings ) {
3012 my $code = ($warning->{code} || 0);
3013 my $message = $warning->{message};
3014@@ -10087,7 +10003,7 @@
3015 ? $warn_code{$code}->{message}
3016 : $message)
3017 . "\nThis MySQL error is being ignored ";
3018- if ( $o->get('statistics') ) {
3019+ if ( get('statistics') ) {
3020 $err .= "but further occurrences will be reported "
3021 . "by --statistics when the tool finishes.\n";
3022 }
3023@@ -10114,54 +10030,6 @@
3024 # Success: no warnings, no errors. Return nibble time.
3025 return $t_end - $t_start;
3026 },
3027- fail => sub {
3028- my (%args) = @_;
3029- my $error = $args{error};
3030- PTDEBUG && _d('Retry fail:', $error);
3031-
3032- # The query failed/caused an error. If the error is one of these,
3033- # then we can possibly retry.
3034- if ( $error =~ m/Lock wait timeout exceeded/
3035- || $error =~ m/Deadlock found/
3036- || $error =~ m/Query execution was interrupted/
3037- ) {
3038- # These errors/warnings can be retried, so don't print
3039- # a warning yet; do that in final_fail.
3040- my $event
3041- = $error =~ m/Lock wait timeout/ ? 'lock_wait_timeout'
3042- : $error =~ m/Deadlock found/ ? 'deadlock'
3043- : $error =~ m/execution was interrupted/ ? 'query_killed'
3044- : 'unknown1';
3045- $stats->{$event}++;
3046- return 1; # try again
3047- }
3048- elsif ( $error =~ m/MySQL server has gone away/
3049- || $error =~ m/Lost connection to MySQL server/
3050- ) {
3051- # The 1st pattern means that MySQL itself died or was stopped.
3052- # The 2nd pattern means that our cxn was killed (KILL <id>).
3053- my $event
3054- = $error =~ m/server has gone away/ ? 'lost_connection'
3055- : $error =~ m/Lost connection/ ? 'connection_killed'
3056- : 'unknown2';
3057- $stats->{$event}++;
3058- $dbh = $cxn->connect(); # connect or die trying
3059- return 1; # reconnected, try again
3060- }
3061-
3062- $stats->{retry_fail}++;
3063-
3064- # At this point, either the error/warning cannot be retried,
3065- # or we failed to reconnect. Don't retry; call final_fail.
3066- return 0;
3067- },
3068- final_fail => sub {
3069- my (%args) = @_;
3070- # This die should be caught by the caller. Copying rows and
3071- # the tool will stop, which is probably good because by this
3072- # point the error or warning indicates that something is wrong.
3073- die $args{error};
3074- }
3075 );
3076 }
3077
3078@@ -10249,23 +10117,21 @@
3079
3080 =head1 RISKS
3081
3082-The following section is included to inform users about the potential risks,
3083-whether known or unknown, of using this tool. The two main categories of risks
3084-are those created by the nature of the tool (e.g. read-only tools vs. read-write
3085-tools) and those created by bugs.
3086-
3087-pt-online-schema-change modifies data and structures. You should be careful with
3088-it, and test it before using it in production. You should also ensure that you
3089-have recoverable backups before using this tool.
3090-
3091-At the time of this release, we know of no bugs that could cause harm to users.
3092-
3093-The authoritative source for updated information is always the online issue
3094-tracking system. Issues that affect this tool will be marked as such. You can
3095-see a list of such issues at the following URL:
3096-L<http://www.percona.com/bugs/pt-online-schema-change>.
3097-
3098-See also L<"BUGS"> for more information on filing bugs and getting help.
3099+Percona Toolkit is mature, proven in the real world, and well tested,
3100+but all database tools can pose a risk to the system and the database
3101+server. Before using this tool, please:
3102+
3103+=over
3104+
3105+=item * Read the tool's documentation
3106+
3107+=item * Review the tool's known L<"BUGS">
3108+
3109+=item * Test the tool on a non-production server
3110+
3111+=item * Backup your production server and verify the backups
3112+
3113+=back
3114
3115 =head1 DESCRIPTION
3116
3117@@ -10329,9 +10195,10 @@
3118
3119 =item *
3120
3121-The tool sets its lock wait timeout to 1 second so that it is more likely to be
3122-the victim of any lock contention, and less likely to disrupt other
3123-transactions. See L<"--lock-wait-timeout"> for details.
3124+The tool sets C<innodb_lock_wait_timeout=1> and (for MySQL 5.5 and newer)
3125+C<lock_wait_timeout=60> so that it is more likely to be the victim of any
3126+lock contention, and less likely to disrupt other transactions. These
3127+values can be changed by specifying L<"--set-vars">.
3128
3129 =item *
3130
3131@@ -10409,7 +10276,7 @@
3132 C<DROP FOREIGN KEY constraint_name> requires specifying C<_constraint_name>
3133 rather than the real C<constraint_name>. Due to a limitation in MySQL,
3134 pt-online-schema-change adds a leading underscore to foreign key constraint
3135-names when creating the new table. For example, to drop this contraint:
3136+names when creating the new table. For example, to drop this constraint:
3137
3138 CONSTRAINT `fk_foo` FOREIGN KEY (`foo_id`) REFERENCES `bar` (`foo_id`)
3139
3140@@ -10798,18 +10665,6 @@
3141
3142 Connect to host.
3143
3144-=item --lock-wait-timeout
3145-
3146-type: int; default: 1
3147-
3148-Set the session value of C<innodb_lock_wait_timeout>. This option helps guard
3149-against long lock waits if the data-copy queries become slow for some reason.
3150-Setting this option dynamically requires the InnoDB plugin, so this works only
3151-on newer InnoDB and MySQL versions. If the setting's current value is greater
3152-than the specified value, and the tool cannot set the value as desired, then it
3153-prints a warning. If the tool cannot set the value but the current value is less
3154-than or equal to the desired value, there is no error.
3155-
3156 =item --max-lag
3157
3158 type: time; default: 1s
3159@@ -10874,6 +10729,18 @@
3160 tool will overwrite the PID file with the current PID. The PID file is
3161 removed automatically when the tool exits.
3162
3163+=item --plugin
3164+
3165+type: string
3166+
3167+Perl module file that defines a C<pt_online_schema_change_plugin> class.
3168+A plugin allows you to write a Perl module that can hook into many parts
3169+of pt-online-schema-change. This requires a good knowledge of Perl and
3170+Percona Toolkit conventions, which are beyond this scope of this
3171+documentation. Please contact Percona if you have questions or need help.
3172+
3173+See L<"PLUGIN"> for more information.
3174+
3175 =item --port
3176
3177 short form: -P; type: int
3178@@ -10949,18 +10816,64 @@
3179
3180 =item --retries
3181
3182-type: int; default: 3
3183-
3184-Retry a chunk this many times when there is a nonfatal error. Nonfatal errors
3185-are problems such as a lock wait timeout or the query being killed. This option
3186-applies to the data copy operation.
3187+type: int; default: 10
3188+
3189+Retry critical operations and recover from non-fatal errors. The tool
3190+retries these operations:
3191+
3192+ Creating triggers
3193+ Dropping triggers
3194+ Copying chunks
3195+ Swapping tables
3196+ Rebuilding foreign key constraints
3197+
3198+For creating and dropping triggers, the number of retries applies to each
3199+C<CREATE TRIGGER> and C<DROP TRIGGER> statement for each trigger.
3200+For copying chunks, the number of retries applies to each chunk, not the
3201+entire table. For swapping tables, the number of retries usually applies
3202+once because there is usually only one C<RENAME TABLE> statement.
3203+For rebuilding foreign key constraints, the number of retries applies to
3204+each statement (C<ALTER> statements for the C<rebuild_constraints>
3205+L<"--alter-foreign-keys-method">; other statements for the C<drop_swap>
3206+method).
3207+
3208+The tool retries each operation if these errors occur:
3209+
3210+ Lock wait timeout (innodb_lock_wait_timeout and lock_wait_timeout)
3211+ Deadlock found
3212+ Query is killed (KILL QUERY <thread_id>)
3213+ Connection is killed (KILL CONNECTION <thread_id>)
3214+ Lost connection to MySQL
3215+
3216+In the case of lost and killed connections, the tool will automatically
3217+reconnect.
3218+
3219+To alter extremely busy tables, it may be necessary to increase L<"--retries">,
3220+and also C<innodb_lock_wait_timeout> and (for MySQL 5.5 and newer)
3221+C<lock_wait_timeout> by specifying higher values with L<"--set-vars">.
3222+
3223+Failures and retries are recorded in the L<"--statistics">.
3224
3225 =item --set-vars
3226
3227-type: string; default: wait_timeout=10000
3228-
3229-Set these MySQL variables. Immediately after connecting to MySQL, this string
3230-will be appended to SET and executed.
3231+type: Array
3232+
3233+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
3234+
3235+By default, the tool sets:
3236+
3237+=for comment ignore-pt-internal-value
3238+MAGIC_set_vars
3239+
3240+ wait_timeout=10000
3241+ innodb_lock_wait_timeout=1
3242+ lock_wait_timeout=60
3243+
3244+Variables specified on the command line override these defaults. For
3245+example, specifying C<--set-vars wait_timeout=500> overrides the default
3246+value of C<10000>.
3247+
3248+The tool prints a warning and continues if a variable cannot be set.
3249
3250 =item --socket
3251
3252@@ -11014,6 +10927,52 @@
3253
3254 =back
3255
3256+=head1 PLUGIN
3257+
3258+The file specified by L<"--plugin"> must define a class (i.e. a package)
3259+called C<pt_online_schema_change_plugin> with a C<new()> subroutine.
3260+The tool will create an instance of this class and call any hooks that
3261+it defines. No hooks are required, but a plugin isn't very useful without
3262+them.
3263+
3264+These hooks, in this order, are called if defined:
3265+
3266+ init
3267+ before_create_new_table
3268+ after_create_new_table
3269+ before_alter_new_table
3270+ after_alter_new_table
3271+ before_create_triggers
3272+ after_create_triggers
3273+ before_copy_rows
3274+ after_copy_rows
3275+ before_swap_tables
3276+ after_swap_tables
3277+ before_update_foreign_keys
3278+ after_update_foreign_keys
3279+ before_drop_old_table
3280+ after_drop_old_table
3281+ before_drop_triggers
3282+ before_exit
3283+
3284+Each hook is passed different arguments. To see which arguments are passed
3285+to a hook, search for the hook's name in the tool's source code, like:
3286+
3287+ # --plugin hook
3288+ if ( $plugin && $plugin->can('init') ) {
3289+ $plugin->init(
3290+ orig_tbl => $orig_tbl,
3291+ child_tables => $child_tables,
3292+ renamed_cols => $renamed_cols,
3293+ slaves => $slaves,
3294+ slave_lag_cxns => $slave_lag_cxns,
3295+ );
3296+ }
3297+
3298+The comment C<# --plugin hook> precedes every hook call.
3299+
3300+Please contact Percona if you have questions or need help.
3301+
3302 =head1 DSN OPTIONS
3303
3304 These DSN options are used to create a DSN. Each option is given like
3305
3306=== modified file 'bin/pt-query-advisor'
3307--- bin/pt-query-advisor 2013-02-27 00:01:17 +0000
3308+++ bin/pt-query-advisor 2013-03-04 18:10:28 +0000
3309@@ -969,7 +969,7 @@
3310
3311 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
3312 $sql = qq{/*!40101 SET NAMES "$charset"*/};
3313- PTDEBUG && _d($dbh, ':', $sql);
3314+ PTDEBUG && _d($dbh, $sql);
3315 eval { $dbh->do($sql) };
3316 if ( $EVAL_ERROR ) {
3317 die "Error setting NAMES to $charset: $EVAL_ERROR";
3318@@ -984,13 +984,8 @@
3319 }
3320 }
3321
3322- if ( my $var = $self->prop('set-vars') ) {
3323- $sql = "SET $var";
3324- PTDEBUG && _d($dbh, ':', $sql);
3325- eval { $dbh->do($sql) };
3326- if ( $EVAL_ERROR ) {
3327- die "Error setting $var: $EVAL_ERROR";
3328- }
3329+ if ( my $vars = $self->prop('set-vars') ) {
3330+ $self->set_vars($dbh, $vars);
3331 }
3332
3333 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
3334@@ -1065,6 +1060,57 @@
3335 return \%new_dsn;
3336 }
3337
3338+sub set_vars {
3339+ my ($self, $dbh, $vars) = @_;
3340+
3341+ return unless $vars;
3342+
3343+ foreach my $var ( sort keys %$vars ) {
3344+ my $val = $vars->{$var}->{val};
3345+
3346+ (my $quoted_var = $var) =~ s/_/\\_/;
3347+ my ($var_exists, $current_val);
3348+ eval {
3349+ ($var_exists, $current_val) = $dbh->selectrow_array(
3350+ "SHOW VARIABLES LIKE '$quoted_var'");
3351+ };
3352+ my $e = $EVAL_ERROR;
3353+ if ( $e ) {
3354+ PTDEBUG && _d($e);
3355+ }
3356+
3357+ if ( $vars->{$var}->{default} && !$var_exists ) {
3358+ PTDEBUG && _d('Not setting default var', $var,
3359+ 'because it does not exist');
3360+ next;
3361+ }
3362+
3363+ if ( $current_val && $current_val eq $val ) {
3364+ PTDEBUG && _d('Not setting var', $var, 'because its value',
3365+ 'is already', $val);
3366+ next;
3367+ }
3368+
3369+ my $sql = "SET SESSION $var=$val";
3370+ PTDEBUG && _d($dbh, $sql);
3371+ eval { $dbh->do($sql) };
3372+ if ( my $set_error = $EVAL_ERROR ) {
3373+ chomp($set_error);
3374+ $set_error =~ s/ at \S+ line \d+//;
3375+ my $msg = "Error setting $var: $set_error";
3376+ if ( $current_val ) {
3377+ $msg .= " The current value for $var is $current_val. "
3378+ . "If the variable is read only (not dynamic), specify "
3379+ . "--set-vars $var=$current_val to avoid this warning, "
3380+ . "else manually set the variable and restart MySQL.";
3381+ }
3382+ warn $msg . "\n\n";
3383+ }
3384+ }
3385+
3386+ return;
3387+}
3388+
3389 sub _d {
3390 my ($package, undef, $line) = caller 0;
3391 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3392@@ -1097,6 +1143,7 @@
3393
3394 use List::Util qw(max);
3395 use Getopt::Long;
3396+use Data::Dumper;
3397
3398 my $POD_link_re = '[LC]<"?([^">]+)"?>';
3399
3400@@ -2080,6 +2127,45 @@
3401 );
3402 };
3403
3404+sub set_vars {
3405+ my ($self, $file) = @_;
3406+ $file ||= $self->{file} || __FILE__;
3407+
3408+ my %user_vars;
3409+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
3410+ if ( $user_vars ) {
3411+ foreach my $var_val ( @$user_vars ) {
3412+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3413+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3414+ $user_vars{$var} = {
3415+ val => $val,
3416+ default => 0,
3417+ };
3418+ }
3419+ }
3420+
3421+ my %default_vars;
3422+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
3423+ if ( $default_vars ) {
3424+ %default_vars = map {
3425+ my $var_val = $_;
3426+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3427+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3428+ $var => {
3429+ val => $val,
3430+ default => 1,
3431+ };
3432+ } split("\n", $default_vars);
3433+ }
3434+
3435+ my %vars = (
3436+ %default_vars, # first the tool's defaults
3437+ %user_vars, # then the user's which overwrite the defaults
3438+ );
3439+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
3440+ return \%vars;
3441+}
3442+
3443 sub _d {
3444 my ($package, undef, $line) = caller 0;
3445 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3446@@ -8078,7 +8164,7 @@
3447 $o->get_opts();
3448
3449 my $dp = $o->DSNParser();
3450- $dp->prop('set-vars', $o->get('set-vars'));
3451+ $dp->prop('set-vars', $o->set_vars());
3452
3453 my $review_dsn = $o->get('review');
3454 my $groupby = lc $o->get('group-by');
3455@@ -9150,10 +9236,21 @@
3456
3457 =item --set-vars
3458
3459-type: string; default: wait_timeout=10000
3460-
3461-Set these MySQL variables. Immediately after connecting to MySQL, this string
3462-will be appended to SET and executed.
3463+type: Array
3464+
3465+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
3466+
3467+By default, the tool sets:
3468+
3469+=for comment ignore-pt-internal-value
3470+MAGIC_set_vars
3471+
3472+ wait_timeout=10000
3473+
3474+Variables specified on the command line override these defaults. For
3475+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
3476+
3477+The tool prints a warning and continues if a variable cannot be set.
3478
3479 =item --[no]show-create-table
3480
3481
3482=== modified file 'bin/pt-query-digest'
3483--- bin/pt-query-digest 2013-02-28 02:23:08 +0000
3484+++ bin/pt-query-digest 2013-03-04 18:10:28 +0000
3485@@ -981,7 +981,7 @@
3486
3487 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
3488 $sql = qq{/*!40101 SET NAMES "$charset"*/};
3489- PTDEBUG && _d($dbh, ':', $sql);
3490+ PTDEBUG && _d($dbh, $sql);
3491 eval { $dbh->do($sql) };
3492 if ( $EVAL_ERROR ) {
3493 die "Error setting NAMES to $charset: $EVAL_ERROR";
3494@@ -996,13 +996,8 @@
3495 }
3496 }
3497
3498- if ( my $var = $self->prop('set-vars') ) {
3499- $sql = "SET $var";
3500- PTDEBUG && _d($dbh, ':', $sql);
3501- eval { $dbh->do($sql) };
3502- if ( $EVAL_ERROR ) {
3503- die "Error setting $var: $EVAL_ERROR";
3504- }
3505+ if ( my $vars = $self->prop('set-vars') ) {
3506+ $self->set_vars($dbh, $vars);
3507 }
3508
3509 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
3510@@ -1077,6 +1072,57 @@
3511 return \%new_dsn;
3512 }
3513
3514+sub set_vars {
3515+ my ($self, $dbh, $vars) = @_;
3516+
3517+ return unless $vars;
3518+
3519+ foreach my $var ( sort keys %$vars ) {
3520+ my $val = $vars->{$var}->{val};
3521+
3522+ (my $quoted_var = $var) =~ s/_/\\_/;
3523+ my ($var_exists, $current_val);
3524+ eval {
3525+ ($var_exists, $current_val) = $dbh->selectrow_array(
3526+ "SHOW VARIABLES LIKE '$quoted_var'");
3527+ };
3528+ my $e = $EVAL_ERROR;
3529+ if ( $e ) {
3530+ PTDEBUG && _d($e);
3531+ }
3532+
3533+ if ( $vars->{$var}->{default} && !$var_exists ) {
3534+ PTDEBUG && _d('Not setting default var', $var,
3535+ 'because it does not exist');
3536+ next;
3537+ }
3538+
3539+ if ( $current_val && $current_val eq $val ) {
3540+ PTDEBUG && _d('Not setting var', $var, 'because its value',
3541+ 'is already', $val);
3542+ next;
3543+ }
3544+
3545+ my $sql = "SET SESSION $var=$val";
3546+ PTDEBUG && _d($dbh, $sql);
3547+ eval { $dbh->do($sql) };
3548+ if ( my $set_error = $EVAL_ERROR ) {
3549+ chomp($set_error);
3550+ $set_error =~ s/ at \S+ line \d+//;
3551+ my $msg = "Error setting $var: $set_error";
3552+ if ( $current_val ) {
3553+ $msg .= " The current value for $var is $current_val. "
3554+ . "If the variable is read only (not dynamic), specify "
3555+ . "--set-vars $var=$current_val to avoid this warning, "
3556+ . "else manually set the variable and restart MySQL.";
3557+ }
3558+ warn $msg . "\n\n";
3559+ }
3560+ }
3561+
3562+ return;
3563+}
3564+
3565 sub _d {
3566 my ($package, undef, $line) = caller 0;
3567 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3568@@ -1260,6 +1306,7 @@
3569
3570 use List::Util qw(max);
3571 use Getopt::Long;
3572+use Data::Dumper;
3573
3574 my $POD_link_re = '[LC]<"?([^">]+)"?>';
3575
3576@@ -2243,6 +2290,45 @@
3577 );
3578 };
3579
3580+sub set_vars {
3581+ my ($self, $file) = @_;
3582+ $file ||= $self->{file} || __FILE__;
3583+
3584+ my %user_vars;
3585+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
3586+ if ( $user_vars ) {
3587+ foreach my $var_val ( @$user_vars ) {
3588+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3589+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3590+ $user_vars{$var} = {
3591+ val => $val,
3592+ default => 0,
3593+ };
3594+ }
3595+ }
3596+
3597+ my %default_vars;
3598+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
3599+ if ( $default_vars ) {
3600+ %default_vars = map {
3601+ my $var_val = $_;
3602+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3603+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3604+ $var => {
3605+ val => $val,
3606+ default => 1,
3607+ };
3608+ } split("\n", $default_vars);
3609+ }
3610+
3611+ my %vars = (
3612+ %default_vars, # first the tool's defaults
3613+ %user_vars, # then the user's which overwrite the defaults
3614+ );
3615+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
3616+ return \%vars;
3617+}
3618+
3619 sub _d {
3620 my ($package, undef, $line) = caller 0;
3621 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3622@@ -12394,7 +12480,7 @@
3623 $o->get_opts();
3624
3625 my $dp = $o->DSNParser();
3626- $dp->prop('set-vars', $o->get('set-vars'));
3627+ $dp->prop('set-vars', $o->set_vars());
3628
3629 # Frequently used options.
3630 my $review_dsn = $o->get('review');
3631@@ -15408,10 +15494,21 @@
3632
3633 =item --set-vars
3634
3635-type: string; default: wait_timeout=10000
3636-
3637-Set these MySQL variables. Immediately after connecting to MySQL, this
3638-string will be appended to SET and executed.
3639+type: Array
3640+
3641+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
3642+
3643+By default, the tool sets:
3644+
3645+=for comment ignore-pt-internal-value
3646+MAGIC_set_vars
3647+
3648+ wait_timeout=10000
3649+
3650+Variables specified on the command line override these defaults. For
3651+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
3652+
3653+The tool prints a warning and continues if a variable cannot be set.
3654
3655 =item --shorten
3656
3657
3658=== modified file 'bin/pt-show-grants'
3659--- bin/pt-show-grants 2013-02-27 00:01:17 +0000
3660+++ bin/pt-show-grants 2013-03-04 18:10:28 +0000
3661@@ -37,6 +37,7 @@
3662
3663 use List::Util qw(max);
3664 use Getopt::Long;
3665+use Data::Dumper;
3666
3667 my $POD_link_re = '[LC]<"?([^">]+)"?>';
3668
3669@@ -1020,6 +1021,45 @@
3670 );
3671 };
3672
3673+sub set_vars {
3674+ my ($self, $file) = @_;
3675+ $file ||= $self->{file} || __FILE__;
3676+
3677+ my %user_vars;
3678+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
3679+ if ( $user_vars ) {
3680+ foreach my $var_val ( @$user_vars ) {
3681+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3682+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3683+ $user_vars{$var} = {
3684+ val => $val,
3685+ default => 0,
3686+ };
3687+ }
3688+ }
3689+
3690+ my %default_vars;
3691+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
3692+ if ( $default_vars ) {
3693+ %default_vars = map {
3694+ my $var_val = $_;
3695+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3696+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3697+ $var => {
3698+ val => $val,
3699+ default => 1,
3700+ };
3701+ } split("\n", $default_vars);
3702+ }
3703+
3704+ my %vars = (
3705+ %default_vars, # first the tool's defaults
3706+ %user_vars, # then the user's which overwrite the defaults
3707+ );
3708+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
3709+ return \%vars;
3710+}
3711+
3712 sub _d {
3713 my ($package, undef, $line) = caller 0;
3714 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3715@@ -1311,7 +1351,7 @@
3716
3717 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
3718 $sql = qq{/*!40101 SET NAMES "$charset"*/};
3719- PTDEBUG && _d($dbh, ':', $sql);
3720+ PTDEBUG && _d($dbh, $sql);
3721 eval { $dbh->do($sql) };
3722 if ( $EVAL_ERROR ) {
3723 die "Error setting NAMES to $charset: $EVAL_ERROR";
3724@@ -1326,13 +1366,8 @@
3725 }
3726 }
3727
3728- if ( my $var = $self->prop('set-vars') ) {
3729- $sql = "SET $var";
3730- PTDEBUG && _d($dbh, ':', $sql);
3731- eval { $dbh->do($sql) };
3732- if ( $EVAL_ERROR ) {
3733- die "Error setting $var: $EVAL_ERROR";
3734- }
3735+ if ( my $vars = $self->prop('set-vars') ) {
3736+ $self->set_vars($dbh, $vars);
3737 }
3738
3739 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
3740@@ -1407,6 +1442,57 @@
3741 return \%new_dsn;
3742 }
3743
3744+sub set_vars {
3745+ my ($self, $dbh, $vars) = @_;
3746+
3747+ return unless $vars;
3748+
3749+ foreach my $var ( sort keys %$vars ) {
3750+ my $val = $vars->{$var}->{val};
3751+
3752+ (my $quoted_var = $var) =~ s/_/\\_/;
3753+ my ($var_exists, $current_val);
3754+ eval {
3755+ ($var_exists, $current_val) = $dbh->selectrow_array(
3756+ "SHOW VARIABLES LIKE '$quoted_var'");
3757+ };
3758+ my $e = $EVAL_ERROR;
3759+ if ( $e ) {
3760+ PTDEBUG && _d($e);
3761+ }
3762+
3763+ if ( $vars->{$var}->{default} && !$var_exists ) {
3764+ PTDEBUG && _d('Not setting default var', $var,
3765+ 'because it does not exist');
3766+ next;
3767+ }
3768+
3769+ if ( $current_val && $current_val eq $val ) {
3770+ PTDEBUG && _d('Not setting var', $var, 'because its value',
3771+ 'is already', $val);
3772+ next;
3773+ }
3774+
3775+ my $sql = "SET SESSION $var=$val";
3776+ PTDEBUG && _d($dbh, $sql);
3777+ eval { $dbh->do($sql) };
3778+ if ( my $set_error = $EVAL_ERROR ) {
3779+ chomp($set_error);
3780+ $set_error =~ s/ at \S+ line \d+//;
3781+ my $msg = "Error setting $var: $set_error";
3782+ if ( $current_val ) {
3783+ $msg .= " The current value for $var is $current_val. "
3784+ . "If the variable is read only (not dynamic), specify "
3785+ . "--set-vars $var=$current_val to avoid this warning, "
3786+ . "else manually set the variable and restart MySQL.";
3787+ }
3788+ warn $msg . "\n\n";
3789+ }
3790+ }
3791+
3792+ return;
3793+}
3794+
3795 sub _d {
3796 my ($package, undef, $line) = caller 0;
3797 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3798@@ -1654,7 +1740,7 @@
3799 $o->get_opts();
3800
3801 my $dp = $o->DSNParser();
3802- $dp->prop('set-vars', $o->get('set-vars'));
3803+ $dp->prop('set-vars', $o->set_vars());
3804
3805 $o->usage_or_errors();
3806
3807@@ -2119,10 +2205,21 @@
3808
3809 =item --set-vars
3810
3811-type: string; default: wait_timeout=10000
3812-
3813-Set these MySQL variables. Immediately after connecting to MySQL, this
3814-string will be appended to SET and executed.
3815+type: Array
3816+
3817+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
3818+
3819+By default, the tool sets:
3820+
3821+=for comment ignore-pt-internal-value
3822+MAGIC_set_vars
3823+
3824+ wait_timeout=10000
3825+
3826+Variables specified on the command line override these defaults. For
3827+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
3828+
3829+The tool prints a warning and continues if a variable cannot be set.
3830
3831 =item --socket
3832
3833
3834=== modified file 'bin/pt-slave-delay'
3835--- bin/pt-slave-delay 2013-02-27 00:01:17 +0000
3836+++ bin/pt-slave-delay 2013-03-04 18:10:28 +0000
3837@@ -65,6 +65,7 @@
3838
3839 use List::Util qw(max);
3840 use Getopt::Long;
3841+use Data::Dumper;
3842
3843 my $POD_link_re = '[LC]<"?([^">]+)"?>';
3844
3845@@ -1048,6 +1049,45 @@
3846 );
3847 };
3848
3849+sub set_vars {
3850+ my ($self, $file) = @_;
3851+ $file ||= $self->{file} || __FILE__;
3852+
3853+ my %user_vars;
3854+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
3855+ if ( $user_vars ) {
3856+ foreach my $var_val ( @$user_vars ) {
3857+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3858+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3859+ $user_vars{$var} = {
3860+ val => $val,
3861+ default => 0,
3862+ };
3863+ }
3864+ }
3865+
3866+ my %default_vars;
3867+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
3868+ if ( $default_vars ) {
3869+ %default_vars = map {
3870+ my $var_val = $_;
3871+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
3872+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
3873+ $var => {
3874+ val => $val,
3875+ default => 1,
3876+ };
3877+ } split("\n", $default_vars);
3878+ }
3879+
3880+ my %vars = (
3881+ %default_vars, # first the tool's defaults
3882+ %user_vars, # then the user's which overwrite the defaults
3883+ );
3884+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
3885+ return \%vars;
3886+}
3887+
3888 sub _d {
3889 my ($package, undef, $line) = caller 0;
3890 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3891@@ -1983,7 +2023,7 @@
3892
3893 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
3894 $sql = qq{/*!40101 SET NAMES "$charset"*/};
3895- PTDEBUG && _d($dbh, ':', $sql);
3896+ PTDEBUG && _d($dbh, $sql);
3897 eval { $dbh->do($sql) };
3898 if ( $EVAL_ERROR ) {
3899 die "Error setting NAMES to $charset: $EVAL_ERROR";
3900@@ -1998,13 +2038,8 @@
3901 }
3902 }
3903
3904- if ( my $var = $self->prop('set-vars') ) {
3905- $sql = "SET $var";
3906- PTDEBUG && _d($dbh, ':', $sql);
3907- eval { $dbh->do($sql) };
3908- if ( $EVAL_ERROR ) {
3909- die "Error setting $var: $EVAL_ERROR";
3910- }
3911+ if ( my $vars = $self->prop('set-vars') ) {
3912+ $self->set_vars($dbh, $vars);
3913 }
3914
3915 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
3916@@ -2079,6 +2114,57 @@
3917 return \%new_dsn;
3918 }
3919
3920+sub set_vars {
3921+ my ($self, $dbh, $vars) = @_;
3922+
3923+ return unless $vars;
3924+
3925+ foreach my $var ( sort keys %$vars ) {
3926+ my $val = $vars->{$var}->{val};
3927+
3928+ (my $quoted_var = $var) =~ s/_/\\_/;
3929+ my ($var_exists, $current_val);
3930+ eval {
3931+ ($var_exists, $current_val) = $dbh->selectrow_array(
3932+ "SHOW VARIABLES LIKE '$quoted_var'");
3933+ };
3934+ my $e = $EVAL_ERROR;
3935+ if ( $e ) {
3936+ PTDEBUG && _d($e);
3937+ }
3938+
3939+ if ( $vars->{$var}->{default} && !$var_exists ) {
3940+ PTDEBUG && _d('Not setting default var', $var,
3941+ 'because it does not exist');
3942+ next;
3943+ }
3944+
3945+ if ( $current_val && $current_val eq $val ) {
3946+ PTDEBUG && _d('Not setting var', $var, 'because its value',
3947+ 'is already', $val);
3948+ next;
3949+ }
3950+
3951+ my $sql = "SET SESSION $var=$val";
3952+ PTDEBUG && _d($dbh, $sql);
3953+ eval { $dbh->do($sql) };
3954+ if ( my $set_error = $EVAL_ERROR ) {
3955+ chomp($set_error);
3956+ $set_error =~ s/ at \S+ line \d+//;
3957+ my $msg = "Error setting $var: $set_error";
3958+ if ( $current_val ) {
3959+ $msg .= " The current value for $var is $current_val. "
3960+ . "If the variable is read only (not dynamic), specify "
3961+ . "--set-vars $var=$current_val to avoid this warning, "
3962+ . "else manually set the variable and restart MySQL.";
3963+ }
3964+ warn $msg . "\n\n";
3965+ }
3966+ }
3967+
3968+ return;
3969+}
3970+
3971 sub _d {
3972 my ($package, undef, $line) = caller 0;
3973 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3974@@ -3981,7 +4067,7 @@
3975 $o->get_opts();
3976
3977 my $dp = $o->DSNParser();
3978- $dp->prop('set-vars', $o->get('set-vars'));
3979+ $dp->prop('set-vars', $o->set_vars());
3980
3981 my $dsn_defaults = $dp->parse_options($o);
3982 my $slave_dsn = @ARGV ? $dp->parse(shift @ARGV, $dsn_defaults)
3983@@ -4505,10 +4591,21 @@
3984
3985 =item --set-vars
3986
3987-type: string; default: wait_timeout=10000
3988-
3989-Set these MySQL variables. Immediately after connecting to MySQL, this string
3990-will be appended to SET and executed.
3991+type: Array
3992+
3993+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
3994+
3995+By default, the tool sets:
3996+
3997+=for comment ignore-pt-internal-value
3998+MAGIC_set_vars
3999+
4000+ wait_timeout=10000
4001+
4002+Variables specified on the command line override these defaults. For
4003+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
4004+
4005+The tool prints a warning and continues if a variable cannot be set.
4006
4007 =item --socket
4008
4009
4010=== modified file 'bin/pt-slave-find'
4011--- bin/pt-slave-find 2013-02-27 00:01:17 +0000
4012+++ bin/pt-slave-find 2013-03-04 18:10:28 +0000
4013@@ -45,6 +45,7 @@
4014
4015 use List::Util qw(max);
4016 use Getopt::Long;
4017+use Data::Dumper;
4018
4019 my $POD_link_re = '[LC]<"?([^">]+)"?>';
4020
4021@@ -1028,6 +1029,45 @@
4022 );
4023 };
4024
4025+sub set_vars {
4026+ my ($self, $file) = @_;
4027+ $file ||= $self->{file} || __FILE__;
4028+
4029+ my %user_vars;
4030+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
4031+ if ( $user_vars ) {
4032+ foreach my $var_val ( @$user_vars ) {
4033+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4034+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4035+ $user_vars{$var} = {
4036+ val => $val,
4037+ default => 0,
4038+ };
4039+ }
4040+ }
4041+
4042+ my %default_vars;
4043+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
4044+ if ( $default_vars ) {
4045+ %default_vars = map {
4046+ my $var_val = $_;
4047+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4048+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4049+ $var => {
4050+ val => $val,
4051+ default => 1,
4052+ };
4053+ } split("\n", $default_vars);
4054+ }
4055+
4056+ my %vars = (
4057+ %default_vars, # first the tool's defaults
4058+ %user_vars, # then the user's which overwrite the defaults
4059+ );
4060+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
4061+ return \%vars;
4062+}
4063+
4064 sub _d {
4065 my ($package, undef, $line) = caller 0;
4066 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4067@@ -1963,7 +2003,7 @@
4068
4069 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
4070 $sql = qq{/*!40101 SET NAMES "$charset"*/};
4071- PTDEBUG && _d($dbh, ':', $sql);
4072+ PTDEBUG && _d($dbh, $sql);
4073 eval { $dbh->do($sql) };
4074 if ( $EVAL_ERROR ) {
4075 die "Error setting NAMES to $charset: $EVAL_ERROR";
4076@@ -1978,13 +2018,8 @@
4077 }
4078 }
4079
4080- if ( my $var = $self->prop('set-vars') ) {
4081- $sql = "SET $var";
4082- PTDEBUG && _d($dbh, ':', $sql);
4083- eval { $dbh->do($sql) };
4084- if ( $EVAL_ERROR ) {
4085- die "Error setting $var: $EVAL_ERROR";
4086- }
4087+ if ( my $vars = $self->prop('set-vars') ) {
4088+ $self->set_vars($dbh, $vars);
4089 }
4090
4091 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
4092@@ -2059,6 +2094,57 @@
4093 return \%new_dsn;
4094 }
4095
4096+sub set_vars {
4097+ my ($self, $dbh, $vars) = @_;
4098+
4099+ return unless $vars;
4100+
4101+ foreach my $var ( sort keys %$vars ) {
4102+ my $val = $vars->{$var}->{val};
4103+
4104+ (my $quoted_var = $var) =~ s/_/\\_/;
4105+ my ($var_exists, $current_val);
4106+ eval {
4107+ ($var_exists, $current_val) = $dbh->selectrow_array(
4108+ "SHOW VARIABLES LIKE '$quoted_var'");
4109+ };
4110+ my $e = $EVAL_ERROR;
4111+ if ( $e ) {
4112+ PTDEBUG && _d($e);
4113+ }
4114+
4115+ if ( $vars->{$var}->{default} && !$var_exists ) {
4116+ PTDEBUG && _d('Not setting default var', $var,
4117+ 'because it does not exist');
4118+ next;
4119+ }
4120+
4121+ if ( $current_val && $current_val eq $val ) {
4122+ PTDEBUG && _d('Not setting var', $var, 'because its value',
4123+ 'is already', $val);
4124+ next;
4125+ }
4126+
4127+ my $sql = "SET SESSION $var=$val";
4128+ PTDEBUG && _d($dbh, $sql);
4129+ eval { $dbh->do($sql) };
4130+ if ( my $set_error = $EVAL_ERROR ) {
4131+ chomp($set_error);
4132+ $set_error =~ s/ at \S+ line \d+//;
4133+ my $msg = "Error setting $var: $set_error";
4134+ if ( $current_val ) {
4135+ $msg .= " The current value for $var is $current_val. "
4136+ . "If the variable is read only (not dynamic), specify "
4137+ . "--set-vars $var=$current_val to avoid this warning, "
4138+ . "else manually set the variable and restart MySQL.";
4139+ }
4140+ warn $msg . "\n\n";
4141+ }
4142+ }
4143+
4144+ return;
4145+}
4146+
4147 sub _d {
4148 my ($package, undef, $line) = caller 0;
4149 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4150@@ -3584,7 +3670,7 @@
4151 $o->get_opts();
4152
4153 my $dp = $o->DSNParser();
4154- $dp->prop('set-vars', $o->get('set-vars'));
4155+ $dp->prop('set-vars', $o->set_vars());
4156
4157 if ( $o->get('ask-pass') ) {
4158 $o->set('password', OptionParser::prompt_noecho("Enter password: "));
4159@@ -4047,10 +4133,21 @@
4160
4161 =item --set-vars
4162
4163-type: string; default: wait_timeout=10000
4164-
4165-Set these MySQL variables. Immediately after connecting to MySQL, this
4166-string will be appended to SET and executed.
4167+type: Array
4168+
4169+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
4170+
4171+By default, the tool sets:
4172+
4173+=for comment ignore-pt-internal-value
4174+MAGIC_set_vars
4175+
4176+ wait_timeout=10000
4177+
4178+Variables specified on the command line override these defaults. For
4179+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
4180+
4181+The tool prints a warning and continues if a variable cannot be set.
4182
4183 =item --socket
4184
4185
4186=== modified file 'bin/pt-slave-restart'
4187--- bin/pt-slave-restart 2013-02-27 00:01:17 +0000
4188+++ bin/pt-slave-restart 2013-03-04 18:10:28 +0000
4189@@ -217,6 +217,7 @@
4190
4191 use List::Util qw(max);
4192 use Getopt::Long;
4193+use Data::Dumper;
4194
4195 my $POD_link_re = '[LC]<"?([^">]+)"?>';
4196
4197@@ -1200,6 +1201,45 @@
4198 );
4199 };
4200
4201+sub set_vars {
4202+ my ($self, $file) = @_;
4203+ $file ||= $self->{file} || __FILE__;
4204+
4205+ my %user_vars;
4206+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
4207+ if ( $user_vars ) {
4208+ foreach my $var_val ( @$user_vars ) {
4209+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4210+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4211+ $user_vars{$var} = {
4212+ val => $val,
4213+ default => 0,
4214+ };
4215+ }
4216+ }
4217+
4218+ my %default_vars;
4219+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
4220+ if ( $default_vars ) {
4221+ %default_vars = map {
4222+ my $var_val = $_;
4223+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4224+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4225+ $var => {
4226+ val => $val,
4227+ default => 1,
4228+ };
4229+ } split("\n", $default_vars);
4230+ }
4231+
4232+ my %vars = (
4233+ %default_vars, # first the tool's defaults
4234+ %user_vars, # then the user's which overwrite the defaults
4235+ );
4236+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
4237+ return \%vars;
4238+}
4239+
4240 sub _d {
4241 my ($package, undef, $line) = caller 0;
4242 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4243@@ -2329,7 +2369,7 @@
4244
4245 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
4246 $sql = qq{/*!40101 SET NAMES "$charset"*/};
4247- PTDEBUG && _d($dbh, ':', $sql);
4248+ PTDEBUG && _d($dbh, $sql);
4249 eval { $dbh->do($sql) };
4250 if ( $EVAL_ERROR ) {
4251 die "Error setting NAMES to $charset: $EVAL_ERROR";
4252@@ -2344,13 +2384,8 @@
4253 }
4254 }
4255
4256- if ( my $var = $self->prop('set-vars') ) {
4257- $sql = "SET $var";
4258- PTDEBUG && _d($dbh, ':', $sql);
4259- eval { $dbh->do($sql) };
4260- if ( $EVAL_ERROR ) {
4261- die "Error setting $var: $EVAL_ERROR";
4262- }
4263+ if ( my $vars = $self->prop('set-vars') ) {
4264+ $self->set_vars($dbh, $vars);
4265 }
4266
4267 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
4268@@ -2425,6 +2460,57 @@
4269 return \%new_dsn;
4270 }
4271
4272+sub set_vars {
4273+ my ($self, $dbh, $vars) = @_;
4274+
4275+ return unless $vars;
4276+
4277+ foreach my $var ( sort keys %$vars ) {
4278+ my $val = $vars->{$var}->{val};
4279+
4280+ (my $quoted_var = $var) =~ s/_/\\_/;
4281+ my ($var_exists, $current_val);
4282+ eval {
4283+ ($var_exists, $current_val) = $dbh->selectrow_array(
4284+ "SHOW VARIABLES LIKE '$quoted_var'");
4285+ };
4286+ my $e = $EVAL_ERROR;
4287+ if ( $e ) {
4288+ PTDEBUG && _d($e);
4289+ }
4290+
4291+ if ( $vars->{$var}->{default} && !$var_exists ) {
4292+ PTDEBUG && _d('Not setting default var', $var,
4293+ 'because it does not exist');
4294+ next;
4295+ }
4296+
4297+ if ( $current_val && $current_val eq $val ) {
4298+ PTDEBUG && _d('Not setting var', $var, 'because its value',
4299+ 'is already', $val);
4300+ next;
4301+ }
4302+
4303+ my $sql = "SET SESSION $var=$val";
4304+ PTDEBUG && _d($dbh, $sql);
4305+ eval { $dbh->do($sql) };
4306+ if ( my $set_error = $EVAL_ERROR ) {
4307+ chomp($set_error);
4308+ $set_error =~ s/ at \S+ line \d+//;
4309+ my $msg = "Error setting $var: $set_error";
4310+ if ( $current_val ) {
4311+ $msg .= " The current value for $var is $current_val. "
4312+ . "If the variable is read only (not dynamic), specify "
4313+ . "--set-vars $var=$current_val to avoid this warning, "
4314+ . "else manually set the variable and restart MySQL.";
4315+ }
4316+ warn $msg . "\n\n";
4317+ }
4318+ }
4319+
4320+ return;
4321+}
4322+
4323 sub _d {
4324 my ($package, undef, $line) = caller 0;
4325 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4326@@ -4636,7 +4722,7 @@
4327 $o->get_opts();
4328
4329 $dp = $o->DSNParser();
4330- $dp->prop('set-vars', $o->get('set-vars'));
4331+ $dp->prop('set-vars', $o->set_vars());
4332
4333 $o->set('verbose', 0) if $o->get('quiet');
4334
4335@@ -5386,10 +5472,21 @@
4336
4337 =item --set-vars
4338
4339-type: string; default: wait_timeout=10000
4340-
4341-Set these MySQL variables. Immediately after connecting to MySQL, this string
4342-will be appended to SET and executed.
4343+type: Array
4344+
4345+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
4346+
4347+By default, the tool sets:
4348+
4349+=for comment ignore-pt-internal-value
4350+MAGIC_set_vars
4351+
4352+ wait_timeout=10000
4353+
4354+Variables specified on the command line override these defaults. For
4355+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
4356+
4357+The tool prints a warning and continues if a variable cannot be set.
4358
4359 =item --skip-count
4360
4361
4362=== modified file 'bin/pt-table-checksum'
4363--- bin/pt-table-checksum 2013-02-28 02:23:08 +0000
4364+++ bin/pt-table-checksum 2013-03-04 18:10:28 +0000
4365@@ -1555,7 +1555,7 @@
4366
4367 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
4368 $sql = qq{/*!40101 SET NAMES "$charset"*/};
4369- PTDEBUG && _d($dbh, ':', $sql);
4370+ PTDEBUG && _d($dbh, $sql);
4371 eval { $dbh->do($sql) };
4372 if ( $EVAL_ERROR ) {
4373 die "Error setting NAMES to $charset: $EVAL_ERROR";
4374@@ -1570,13 +1570,8 @@
4375 }
4376 }
4377
4378- if ( my $var = $self->prop('set-vars') ) {
4379- $sql = "SET $var";
4380- PTDEBUG && _d($dbh, ':', $sql);
4381- eval { $dbh->do($sql) };
4382- if ( $EVAL_ERROR ) {
4383- die "Error setting $var: $EVAL_ERROR";
4384- }
4385+ if ( my $vars = $self->prop('set-vars') ) {
4386+ $self->set_vars($dbh, $vars);
4387 }
4388
4389 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
4390@@ -1651,6 +1646,57 @@
4391 return \%new_dsn;
4392 }
4393
4394+sub set_vars {
4395+ my ($self, $dbh, $vars) = @_;
4396+
4397+ return unless $vars;
4398+
4399+ foreach my $var ( sort keys %$vars ) {
4400+ my $val = $vars->{$var}->{val};
4401+
4402+ (my $quoted_var = $var) =~ s/_/\\_/;
4403+ my ($var_exists, $current_val);
4404+ eval {
4405+ ($var_exists, $current_val) = $dbh->selectrow_array(
4406+ "SHOW VARIABLES LIKE '$quoted_var'");
4407+ };
4408+ my $e = $EVAL_ERROR;
4409+ if ( $e ) {
4410+ PTDEBUG && _d($e);
4411+ }
4412+
4413+ if ( $vars->{$var}->{default} && !$var_exists ) {
4414+ PTDEBUG && _d('Not setting default var', $var,
4415+ 'because it does not exist');
4416+ next;
4417+ }
4418+
4419+ if ( $current_val && $current_val eq $val ) {
4420+ PTDEBUG && _d('Not setting var', $var, 'because its value',
4421+ 'is already', $val);
4422+ next;
4423+ }
4424+
4425+ my $sql = "SET SESSION $var=$val";
4426+ PTDEBUG && _d($dbh, $sql);
4427+ eval { $dbh->do($sql) };
4428+ if ( my $set_error = $EVAL_ERROR ) {
4429+ chomp($set_error);
4430+ $set_error =~ s/ at \S+ line \d+//;
4431+ my $msg = "Error setting $var: $set_error";
4432+ if ( $current_val ) {
4433+ $msg .= " The current value for $var is $current_val. "
4434+ . "If the variable is read only (not dynamic), specify "
4435+ . "--set-vars $var=$current_val to avoid this warning, "
4436+ . "else manually set the variable and restart MySQL.";
4437+ }
4438+ warn $msg . "\n\n";
4439+ }
4440+ }
4441+
4442+ return;
4443+}
4444+
4445 sub _d {
4446 my ($package, undef, $line) = caller 0;
4447 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4448@@ -1683,6 +1729,7 @@
4449
4450 use List::Util qw(max);
4451 use Getopt::Long;
4452+use Data::Dumper;
4453
4454 my $POD_link_re = '[LC]<"?([^">]+)"?>';
4455
4456@@ -2666,6 +2713,45 @@
4457 );
4458 };
4459
4460+sub set_vars {
4461+ my ($self, $file) = @_;
4462+ $file ||= $self->{file} || __FILE__;
4463+
4464+ my %user_vars;
4465+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
4466+ if ( $user_vars ) {
4467+ foreach my $var_val ( @$user_vars ) {
4468+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4469+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4470+ $user_vars{$var} = {
4471+ val => $val,
4472+ default => 0,
4473+ };
4474+ }
4475+ }
4476+
4477+ my %default_vars;
4478+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
4479+ if ( $default_vars ) {
4480+ %default_vars = map {
4481+ my $var_val = $_;
4482+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4483+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4484+ $var => {
4485+ val => $val,
4486+ default => 1,
4487+ };
4488+ } split("\n", $default_vars);
4489+ }
4490+
4491+ my %vars = (
4492+ %default_vars, # first the tool's defaults
4493+ %user_vars, # then the user's which overwrite the defaults
4494+ );
4495+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
4496+ return \%vars;
4497+}
4498+
4499 sub _d {
4500 my ($package, undef, $line) = caller 0;
4501 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4502@@ -6055,9 +6141,9 @@
4503 return $self->{one_nibble};
4504 }
4505
4506-sub chunk_size {
4507+sub limit {
4508 my ($self) = @_;
4509- return $self->{limit} + 1;
4510+ return $self->{limit};
4511 }
4512
4513 sub set_chunk_size {
4514@@ -8613,7 +8699,7 @@
4515 $o->get_opts();
4516
4517 my $dp = $o->DSNParser();
4518- $dp->prop('set-vars', $o->get('set-vars'));
4519+ $dp->prop('set-vars', $o->set_vars());
4520
4521 # Add the --replicate table to --ignore-tables.
4522 my %ignore_tables = (
4523@@ -8754,43 +8840,7 @@
4524 . "level to REPEATABLE-READ.\n";
4525 }
4526
4527- # We set innodb_lock_wait_timeout=1 (the option's default value)
4528- # so that if this tool happens to cause some locking, it will more
4529- # likely be the victim than other connections and thus avoid disrupting
4530- # the server. The var is only dynamic with the InnoDB plugin, so
4531- # if setting it fails we only warn if the server's value is greater
4532- # than the desired value. E.g. if user does --lock-wait-timeout 5
4533- # and the set fails but the server's value is 1, then that's ok, but
4534- # if the server's value is 10, then that's not ok.
4535- my $lock_wait_timeout = $o->get('lock-wait-timeout');
4536- my $set_lwt = "SET SESSION innodb_lock_wait_timeout=$lock_wait_timeout";
4537- PTDEBUG && _d($dbh, $set_lwt);
4538- eval {
4539- $dbh->do($set_lwt);
4540- };
4541- if ( $EVAL_ERROR ) {
4542- PTDEBUG && _d($EVAL_ERROR);
4543- # Get the server's current value.
4544- $sql = "SHOW SESSION VARIABLES LIKE 'innodb_lock_wait_timeout'";
4545- PTDEBUG && _d($dbh, $sql);
4546- my (undef, $curr_lwt) = $dbh->selectrow_array($sql);
4547- PTDEBUG && _d('innodb_lock_wait_timeout on server:', $curr_lwt);
4548- if ( !defined $curr_lwt ) {
4549- PTDEBUG && _d('innodb_lock_wait_timeout does not exist;',
4550- 'InnoDB is probably disabled');
4551- }
4552- elsif ( $curr_lwt > $lock_wait_timeout ) {
4553- warn "Failed to $set_lwt: $EVAL_ERROR\n"
4554- . "The current innodb_lock_wait_timeout value "
4555- . "$curr_lwt is greater than the --lock-wait-timeout "
4556- . "value $lock_wait_timeout and the variable cannot be "
4557- . "changed. innodb_lock_wait_timeout is only dynamic when "
4558- . "using the InnoDB plugin. To prevent this warning, either "
4559- . "specify --lock-wait-time=$curr_lwt, or manually set "
4560- . "innodb_lock_wait_timeout to a value less than or equal "
4561- . "to $lock_wait_timeout and restart MySQL.\n";
4562- }
4563- }
4564+ return;
4565 };
4566
4567 # Do not call "new Cxn(" directly; use this sub so that set_on_connect
4568@@ -9533,7 +9583,7 @@
4569 my $expl = explain_statement(
4570 tbl => $tbl,
4571 sth => $sth->{explain_upper_boundary},
4572- vals => [ @{$boundary->{lower}}, $nibble_iter->chunk_size() ],
4573+ vals => [ @{$boundary->{lower}}, $nibble_iter->limit() ],
4574 );
4575 if ( lc($expl->{key} || '')
4576 ne lc($nibble_iter->nibble_index() || '') ) {
4577@@ -11553,18 +11603,6 @@
4578
4579 Ignore tables whose names match the Perl regex.
4580
4581-=item --lock-wait-timeout
4582-
4583-type: int; default: 1
4584-
4585-Set the session value of C<innodb_lock_wait_timeout> on the master host.
4586-This option helps guard against long lock waits if the checksum queries
4587-become slow for some reason. Setting this option dynamically requires the
4588-InnoDB plugin, so this works only on newer InnoDB and MySQL versions. If
4589-setting the value fails and the current server value is greater than the
4590-specified value, then a warning is printed; else, if the current server
4591-value is less than or equal to the specified value, no warning is printed.
4592-
4593 =item --max-lag
4594
4595 type: time; default: 1s; group: Throttle
4596@@ -11824,10 +11862,22 @@
4597
4598 =item --set-vars
4599
4600-type: string; default: wait_timeout=10000; group: Connection
4601-
4602-Set these MySQL variables. Immediately after connecting to MySQL, this
4603-string will be appended to SET and executed.
4604+type: Array; group: Connection
4605+
4606+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
4607+
4608+By default, the tool sets:
4609+
4610+=for comment ignore-pt-internal-value
4611+MAGIC_set_vars
4612+
4613+ wait_timeout=10000
4614+ innodb_lock_wait_timeout=1
4615+
4616+Variables specified on the command line override these defaults. For
4617+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
4618+
4619+The tool prints a warning and continues if a variable cannot be set.
4620
4621 =item --socket
4622
4623
4624=== modified file 'bin/pt-table-sync'
4625--- bin/pt-table-sync 2013-02-27 00:01:17 +0000
4626+++ bin/pt-table-sync 2013-03-04 18:10:28 +0000
4627@@ -80,6 +80,7 @@
4628
4629 use List::Util qw(max);
4630 use Getopt::Long;
4631+use Data::Dumper;
4632
4633 my $POD_link_re = '[LC]<"?([^">]+)"?>';
4634
4635@@ -1063,6 +1064,45 @@
4636 );
4637 };
4638
4639+sub set_vars {
4640+ my ($self, $file) = @_;
4641+ $file ||= $self->{file} || __FILE__;
4642+
4643+ my %user_vars;
4644+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
4645+ if ( $user_vars ) {
4646+ foreach my $var_val ( @$user_vars ) {
4647+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4648+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4649+ $user_vars{$var} = {
4650+ val => $val,
4651+ default => 0,
4652+ };
4653+ }
4654+ }
4655+
4656+ my %default_vars;
4657+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
4658+ if ( $default_vars ) {
4659+ %default_vars = map {
4660+ my $var_val = $_;
4661+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4662+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4663+ $var => {
4664+ val => $val,
4665+ default => 1,
4666+ };
4667+ } split("\n", $default_vars);
4668+ }
4669+
4670+ my %vars = (
4671+ %default_vars, # first the tool's defaults
4672+ %user_vars, # then the user's which overwrite the defaults
4673+ );
4674+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
4675+ return \%vars;
4676+}
4677+
4678 sub _d {
4679 my ($package, undef, $line) = caller 0;
4680 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4681@@ -2149,7 +2189,7 @@
4682
4683 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
4684 $sql = qq{/*!40101 SET NAMES "$charset"*/};
4685- PTDEBUG && _d($dbh, ':', $sql);
4686+ PTDEBUG && _d($dbh, $sql);
4687 eval { $dbh->do($sql) };
4688 if ( $EVAL_ERROR ) {
4689 die "Error setting NAMES to $charset: $EVAL_ERROR";
4690@@ -2164,13 +2204,8 @@
4691 }
4692 }
4693
4694- if ( my $var = $self->prop('set-vars') ) {
4695- $sql = "SET $var";
4696- PTDEBUG && _d($dbh, ':', $sql);
4697- eval { $dbh->do($sql) };
4698- if ( $EVAL_ERROR ) {
4699- die "Error setting $var: $EVAL_ERROR";
4700- }
4701+ if ( my $vars = $self->prop('set-vars') ) {
4702+ $self->set_vars($dbh, $vars);
4703 }
4704
4705 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
4706@@ -2245,6 +2280,57 @@
4707 return \%new_dsn;
4708 }
4709
4710+sub set_vars {
4711+ my ($self, $dbh, $vars) = @_;
4712+
4713+ return unless $vars;
4714+
4715+ foreach my $var ( sort keys %$vars ) {
4716+ my $val = $vars->{$var}->{val};
4717+
4718+ (my $quoted_var = $var) =~ s/_/\\_/;
4719+ my ($var_exists, $current_val);
4720+ eval {
4721+ ($var_exists, $current_val) = $dbh->selectrow_array(
4722+ "SHOW VARIABLES LIKE '$quoted_var'");
4723+ };
4724+ my $e = $EVAL_ERROR;
4725+ if ( $e ) {
4726+ PTDEBUG && _d($e);
4727+ }
4728+
4729+ if ( $vars->{$var}->{default} && !$var_exists ) {
4730+ PTDEBUG && _d('Not setting default var', $var,
4731+ 'because it does not exist');
4732+ next;
4733+ }
4734+
4735+ if ( $current_val && $current_val eq $val ) {
4736+ PTDEBUG && _d('Not setting var', $var, 'because its value',
4737+ 'is already', $val);
4738+ next;
4739+ }
4740+
4741+ my $sql = "SET SESSION $var=$val";
4742+ PTDEBUG && _d($dbh, $sql);
4743+ eval { $dbh->do($sql) };
4744+ if ( my $set_error = $EVAL_ERROR ) {
4745+ chomp($set_error);
4746+ $set_error =~ s/ at \S+ line \d+//;
4747+ my $msg = "Error setting $var: $set_error";
4748+ if ( $current_val ) {
4749+ $msg .= " The current value for $var is $current_val. "
4750+ . "If the variable is read only (not dynamic), specify "
4751+ . "--set-vars $var=$current_val to avoid this warning, "
4752+ . "else manually set the variable and restart MySQL.";
4753+ }
4754+ warn $msg . "\n\n";
4755+ }
4756+ }
4757+
4758+ return;
4759+}
4760+
4761 sub _d {
4762 my ($package, undef, $line) = caller 0;
4763 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4764@@ -9476,7 +9562,7 @@
4765 $o->get_opts();
4766
4767 my $dp = $o->DSNParser();
4768- $dp->prop('set-vars', $o->get('set-vars'));
4769+ $dp->prop('set-vars', $o->set_vars());
4770
4771 if ( $o->get('replicate') || $o->get('sync-to-master') ) {
4772 $o->set('wait', 60) unless $o->got('wait');
4773@@ -12137,10 +12223,21 @@
4774
4775 =item --set-vars
4776
4777-type: string; default: wait_timeout=10000
4778-
4779-Set these MySQL variables. Immediately after connecting to MySQL, this
4780-string will be appended to SET and executed.
4781+type: Array
4782+
4783+Set the MySQL variables in this comma-separated list of C<variable=value> pairs.
4784+
4785+By default, the tool sets:
4786+
4787+=for comment ignore-pt-internal-value
4788+MAGIC_set_vars
4789+
4790+ wait_timeout=10000
4791+
4792+Variables specified on the command line override these defaults. For
4793+example, specifying C<--set-vars wait_timeout=500> overrides the defaultvalue of C<10000>.
4794+
4795+The tool prints a warning and continues if a variable cannot be set.
4796
4797 =item --socket
4798
4799
4800=== modified file 'bin/pt-table-usage'
4801--- bin/pt-table-usage 2013-02-28 02:23:08 +0000
4802+++ bin/pt-table-usage 2013-03-04 18:10:28 +0000
4803@@ -300,7 +300,7 @@
4804
4805 if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) {
4806 $sql = qq{/*!40101 SET NAMES "$charset"*/};
4807- PTDEBUG && _d($dbh, ':', $sql);
4808+ PTDEBUG && _d($dbh, $sql);
4809 eval { $dbh->do($sql) };
4810 if ( $EVAL_ERROR ) {
4811 die "Error setting NAMES to $charset: $EVAL_ERROR";
4812@@ -315,13 +315,8 @@
4813 }
4814 }
4815
4816- if ( my $var = $self->prop('set-vars') ) {
4817- $sql = "SET $var";
4818- PTDEBUG && _d($dbh, ':', $sql);
4819- eval { $dbh->do($sql) };
4820- if ( $EVAL_ERROR ) {
4821- die "Error setting $var: $EVAL_ERROR";
4822- }
4823+ if ( my $vars = $self->prop('set-vars') ) {
4824+ $self->set_vars($dbh, $vars);
4825 }
4826
4827 $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
4828@@ -396,6 +391,57 @@
4829 return \%new_dsn;
4830 }
4831
4832+sub set_vars {
4833+ my ($self, $dbh, $vars) = @_;
4834+
4835+ return unless $vars;
4836+
4837+ foreach my $var ( sort keys %$vars ) {
4838+ my $val = $vars->{$var}->{val};
4839+
4840+ (my $quoted_var = $var) =~ s/_/\\_/;
4841+ my ($var_exists, $current_val);
4842+ eval {
4843+ ($var_exists, $current_val) = $dbh->selectrow_array(
4844+ "SHOW VARIABLES LIKE '$quoted_var'");
4845+ };
4846+ my $e = $EVAL_ERROR;
4847+ if ( $e ) {
4848+ PTDEBUG && _d($e);
4849+ }
4850+
4851+ if ( $vars->{$var}->{default} && !$var_exists ) {
4852+ PTDEBUG && _d('Not setting default var', $var,
4853+ 'because it does not exist');
4854+ next;
4855+ }
4856+
4857+ if ( $current_val && $current_val eq $val ) {
4858+ PTDEBUG && _d('Not setting var', $var, 'because its value',
4859+ 'is already', $val);
4860+ next;
4861+ }
4862+
4863+ my $sql = "SET SESSION $var=$val";
4864+ PTDEBUG && _d($dbh, $sql);
4865+ eval { $dbh->do($sql) };
4866+ if ( my $set_error = $EVAL_ERROR ) {
4867+ chomp($set_error);
4868+ $set_error =~ s/ at \S+ line \d+//;
4869+ my $msg = "Error setting $var: $set_error";
4870+ if ( $current_val ) {
4871+ $msg .= " The current value for $var is $current_val. "
4872+ . "If the variable is read only (not dynamic), specify "
4873+ . "--set-vars $var=$current_val to avoid this warning, "
4874+ . "else manually set the variable and restart MySQL.";
4875+ }
4876+ warn $msg . "\n\n";
4877+ }
4878+ }
4879+
4880+ return;
4881+}
4882+
4883 sub _d {
4884 my ($package, undef, $line) = caller 0;
4885 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4886@@ -428,6 +474,7 @@
4887
4888 use List::Util qw(max);
4889 use Getopt::Long;
4890+use Data::Dumper;
4891
4892 my $POD_link_re = '[LC]<"?([^">]+)"?>';
4893
4894@@ -449,7 +496,6 @@
4895 'default' => 1,
4896 'cumulative' => 1,
4897 'negatable' => 1,
4898- 'value_is_optional' => 1,
4899 );
4900
4901 my $self = {
4902@@ -691,10 +737,9 @@
4903 $opt->{short} = undef;
4904 }
4905
4906- $opt->{is_negatable} = $opt->{spec} =~ m/!/ ? 1 : 0;
4907- $opt->{is_cumulative} = $opt->{spec} =~ m/\+/ ? 1 : 0;
4908- $opt->{optional_value} = $opt->{spec} =~ m/:/ ? 1 : 0;
4909- $opt->{is_required} = $opt->{desc} =~ m/required/ ? 1 : 0;
4910+ $opt->{is_negatable} = $opt->{spec} =~ m/!/ ? 1 : 0;
4911+ $opt->{is_cumulative} = $opt->{spec} =~ m/\+/ ? 1 : 0;
4912+ $opt->{is_required} = $opt->{desc} =~ m/required/ ? 1 : 0;
4913
4914 $opt->{group} ||= 'default';
4915 $self->{groups}->{ $opt->{group} }->{$long} = 1;
4916@@ -830,7 +875,7 @@
4917 if ( $opt->{is_cumulative} ) {
4918 $opt->{value}++;
4919 }
4920- elsif ( !($opt->{optional_value} && !$val) ) {
4921+ else {
4922 $opt->{value} = $val;
4923 }
4924 $opt->{got} = 1;
4925@@ -1210,7 +1255,7 @@
4926 $desc .= ". Optional suffix s=seconds, m=minutes, h=hours, "
4927 . "d=days; if no suffix, $s is used.";
4928 }
4929- $desc = join("\n$rpad", grep { $_ } $desc =~ m/(.{0,$rcol})(?:\s+|$)/g);
4930+ $desc = join("\n$rpad", grep { $_ } $desc =~ m/(.{0,$rcol}(?!\W))(?:\s+|(?<=\W)|$)/g);
4931 $desc =~ s/ +$//mg;
4932 if ( $short ) {
4933 $usage .= sprintf(" --%-${maxs}s -%s %s\n", $long, $short, $desc);
4934@@ -1371,12 +1416,11 @@
4935 sub _parse_attribs {
4936 my ( $self, $option, $attribs ) = @_;
4937 my $types = $self->{types};
4938- my $eq = $attribs->{'value_is_optional'} ? ':' : '=';
4939 return $option
4940 . ($attribs->{'short form'} ? '|' . $attribs->{'short form'} : '' )
4941 . ($attribs->{'negatable'} ? '!' : '' )
4942 . ($attribs->{'cumulative'} ? '+' : '' )
4943- . ($attribs->{'type'} ? $eq . $types->{$attribs->{type}} : '' );
4944+ . ($attribs->{'type'} ? '=' . $types->{$attribs->{type}} : '' );
4945 }
4946
4947 sub _parse_synopsis {
4948@@ -1414,6 +1458,45 @@
4949 );
4950 };
4951
4952+sub set_vars {
4953+ my ($self, $file) = @_;
4954+ $file ||= $self->{file} || __FILE__;
4955+
4956+ my %user_vars;
4957+ my $user_vars = $self->has('set-vars') ? $self->get('set-vars') : undef;
4958+ if ( $user_vars ) {
4959+ foreach my $var_val ( @$user_vars ) {
4960+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4961+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4962+ $user_vars{$var} = {
4963+ val => $val,
4964+ default => 0,
4965+ };
4966+ }
4967+ }
4968+
4969+ my %default_vars;
4970+ my $default_vars = $self->read_para_after($file, qr/MAGIC_set_vars/);
4971+ if ( $default_vars ) {
4972+ %default_vars = map {
4973+ my $var_val = $_;
4974+ my ($var, $val) = $var_val =~ m/([^\s=]+)=(\S+)/;
4975+ die "Invalid --set-vars value: $var_val\n" unless $var && $val;
4976+ $var => {
4977+ val => $val,
4978+ default => 1,
4979+ };
4980+ } split("\n", $default_vars);
4981+ }
4982+
4983+ my %vars = (
4984+ %default_vars, # first the tool's defaults
4985+ %user_vars, # then the user's which overwrite the defaults
4986+ );
4987+ PTDEBUG && _d('--set-vars:', Dumper(\%vars));
4988+ return \%vars;
4989+}
4990+
4991 sub _d {
4992 my ($package, undef, $line) = caller 0;
4993 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4994@@ -6398,7 +6481,7 @@
4995 $o->get_opts();
4996
4997 my $dp = $o->DSNParser();
4998- $dp->prop('set-vars', $o->get('set-vars'));
4999+ $dp->prop('set-vars', $o->set_vars());
5000
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches