Merge lp:~percona-toolkit-dev/percona-toolkit/fix-table-status-bug-960513 into lp:percona-toolkit/2.1

Proposed by Daniel Nichter
Status: Merged
Approved by: Daniel Nichter
Approved revision: 228
Merged at revision: 227
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/fix-table-status-bug-960513
Merge into: lp:percona-toolkit/2.1
Diff against target: 3105 lines (+1575/-763)
11 files modified
bin/pt-duplicate-key-checker (+171/-92)
bin/pt-index-usage (+170/-92)
bin/pt-table-checksum (+42/-67)
bin/pt-table-sync (+553/-404)
lib/NibbleIterator.pm (+8/-15)
lib/SchemaIterator.pm (+27/-45)
t/lib/SchemaIterator.t (+2/-47)
t/lib/samples/SchemaIterator/all-dbs-tbls.txt (+486/-0)
t/lib/samples/SchemaIterator/resume-from-ignored-sakila-payment.txt (+49/-0)
t/lib/samples/SchemaIterator/resume-from-sakila-payment.txt (+66/-0)
t/pt-table-sync/issue_408.t (+1/-1)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/fix-table-status-bug-960513
Reviewer Review Type Date Requested Status
Baron Schwartz (community) Approve
Daniel Nichter Approve
Review via email: mp+100295@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve
Revision history for this message
Baron Schwartz (baron-xaprb) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/pt-duplicate-key-checker'
2--- bin/pt-duplicate-key-checker 2012-03-07 23:41:54 +0000
3+++ bin/pt-duplicate-key-checker 2012-03-31 16:06:20 +0000
4@@ -199,19 +199,58 @@
5 return bless $self, $class;
6 }
7
8+sub get_create_table {
9+ my ( $self, $dbh, $db, $tbl ) = @_;
10+ die "I need a dbh parameter" unless $dbh;
11+ die "I need a db parameter" unless $db;
12+ die "I need a tbl parameter" unless $tbl;
13+ my $q = $self->{Quoter};
14+
15+ my $new_sql_mode
16+ = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
17+ . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
18+ . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
19+ . '@@SQL_QUOTE_SHOW_CREATE := 1 */';
20+
21+ my $old_sql_mode = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
22+ . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
23+
24+ PTDEBUG && _d($new_sql_mode);
25+ eval { $dbh->do($new_sql_mode); };
26+ PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
27+
28+ my $use_sql = 'USE ' . $q->quote($db);
29+ PTDEBUG && _d($dbh, $use_sql);
30+ $dbh->do($use_sql);
31+
32+ my $show_sql = "SHOW CREATE TABLE " . $q->quote($db, $tbl);
33+ PTDEBUG && _d($show_sql);
34+ my $href;
35+ eval { $href = $dbh->selectrow_hashref($show_sql); };
36+ if ( $EVAL_ERROR ) {
37+ PTDEBUG && _d($EVAL_ERROR);
38+
39+ PTDEBUG && _d($old_sql_mode);
40+ $dbh->do($old_sql_mode);
41+
42+ return;
43+ }
44+
45+ PTDEBUG && _d($old_sql_mode);
46+ $dbh->do($old_sql_mode);
47+
48+ my ($key) = grep { m/create (?:table|view)/i } keys %$href;
49+ if ( !$key ) {
50+ die "Error: no 'Create Table' or 'Create View' in result set from "
51+ . "$show_sql: " . Dumper($href);
52+ }
53+
54+ return $href->{$key};
55+}
56+
57 sub parse {
58 my ( $self, $ddl, $opts ) = @_;
59 return unless $ddl;
60- if ( ref $ddl eq 'ARRAY' ) {
61- if ( lc $ddl->[0] eq 'table' ) {
62- $ddl = $ddl->[1];
63- }
64- else {
65- return {
66- engine => 'VIEW',
67- };
68- }
69- }
70
71 if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
72 die "Cannot parse table definition; is ANSI quoting "
73@@ -518,41 +557,31 @@
74 return $ddl;
75 }
76
77-sub remove_secondary_indexes {
78- my ( $self, $ddl ) = @_;
79- my $sec_indexes_ddl;
80- my $tbl_struct = $self->parse($ddl);
81-
82- if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
83- my $clustered_key = $tbl_struct->{clustered_key};
84- $clustered_key ||= '';
85-
86- my @sec_indexes = map {
87- my $key_def = $_->{ddl};
88- $key_def =~ s/([\(\)])/\\$1/g;
89- $ddl =~ s/\s+$key_def//i;
90-
91- my $key_ddl = "ADD $_->{ddl}";
92- $key_ddl .= ',' unless $key_ddl =~ m/,$/;
93- $key_ddl;
94- }
95- grep { $_->{name} ne $clustered_key }
96- values %{$tbl_struct->{keys}};
97- PTDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));
98-
99- if ( @sec_indexes ) {
100- $sec_indexes_ddl = join(' ', @sec_indexes);
101- $sec_indexes_ddl =~ s/,$//;
102- }
103-
104- $ddl =~ s/,(\n\) )/$1/s;
105- }
106- else {
107- PTDEBUG && _d('Not removing secondary indexes from',
108- $tbl_struct->{engine}, 'table');
109- }
110-
111- return $ddl, $sec_indexes_ddl, $tbl_struct;
112+sub get_table_status {
113+ my ( $self, $dbh, $db, $like ) = @_;
114+ my $q = $self->{Quoter};
115+ my $sql = "SHOW TABLE STATUS FROM " . $q->quote($db);
116+ my @params;
117+ if ( $like ) {
118+ $sql .= ' LIKE ?';
119+ push @params, $like;
120+ }
121+ PTDEBUG && _d($sql, @params);
122+ my $sth = $dbh->prepare($sql);
123+ eval { $sth->execute(@params); };
124+ if ($EVAL_ERROR) {
125+ PTDEBUG && _d($EVAL_ERROR);
126+ return;
127+ }
128+ my @tables = @{$sth->fetchall_arrayref({})};
129+ @tables = map {
130+ my %tbl; # Make a copy with lowercased keys
131+ @tbl{ map { lc $_ } keys %$_ } = values %$_;
132+ $tbl{engine} ||= $tbl{type} || $tbl{comment};
133+ delete $tbl{type};
134+ \%tbl;
135+ } @tables;
136+ return @tables;
137 }
138
139 sub _d {
140@@ -3195,7 +3224,7 @@
141
142 sub new {
143 my ( $class, %args ) = @_;
144- my @required_args = qw(OptionParser Quoter);
145+ my @required_args = qw(OptionParser TableParser Quoter);
146 foreach my $arg ( @required_args ) {
147 die "I need a $arg argument" unless $args{$arg};
148 }
149@@ -3204,8 +3233,19 @@
150 die "I need either a dbh or file_itr argument"
151 if (!$dbh && !$file_itr) || ($dbh && $file_itr);
152
153+ my %resume;
154+ if ( my $table = $args{resume} ) {
155+ PTDEBUG && _d('Will resume from or after', $table);
156+ my ($db, $tbl) = $args{Quoter}->split_unquote($table);
157+ die "Resume table must be database-qualified: $table"
158+ unless $db && $tbl;
159+ $resume{db} = $db;
160+ $resume{tbl} = $tbl;
161+ }
162+
163 my $self = {
164 %args,
165+ resume => \%resume,
166 filters => _make_filters(%args),
167 };
168
169@@ -3266,9 +3306,19 @@
170 return \%filters;
171 }
172
173-sub next_schema_object {
174+sub next {
175 my ( $self ) = @_;
176
177+ if ( !$self->{initialized} ) {
178+ $self->{initialized} = 1;
179+ if ( $self->{resume}->{tbl}
180+ && !$self->table_is_allowed(@{$self->{resume}}{qw(db tbl)}) ) {
181+ PTDEBUG && _d('Will resume after',
182+ join('.', @{$self->{resume}}{qw(db tbl)}));
183+ $self->{resume}->{after} = 1;
184+ }
185+ }
186+
187 my $schema_obj;
188 if ( $self->{file_itr} ) {
189 $schema_obj= $self->_iterate_files();
190@@ -3278,24 +3328,18 @@
191 }
192
193 if ( $schema_obj ) {
194- if ( $schema_obj->{ddl} && $self->{TableParser} ) {
195- $schema_obj->{tbl_struct}
196- = $self->{TableParser}->parse($schema_obj->{ddl});
197- }
198-
199- delete $schema_obj->{ddl} unless $self->{keep_ddl};
200-
201 if ( my $schema = $self->{Schema} ) {
202 $schema->add_schema_object($schema_obj);
203 }
204+ PTDEBUG && _d('Next schema object:',
205+ $schema_obj->{db}, $schema_obj->{tbl});
206 }
207
208- PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
209 return $schema_obj;
210 }
211
212 sub _iterate_files {
213- my ( $self ) = @_;
214+ my ( $self ) = @_;
215
216 if ( !$self->{fh} ) {
217 my ($fh, $file) = $self->{file_itr}->();
218@@ -3316,7 +3360,8 @@
219 my $db = $1; # XXX
220 $db =~ s/^`//; # strip leading `
221 $db =~ s/`$//; # and trailing `
222- if ( $self->database_is_allowed($db) ) {
223+ if ( $self->database_is_allowed($db)
224+ && $self->_resume_from_database($db) ) {
225 $self->{db} = $db;
226 }
227 }
228@@ -3329,21 +3374,22 @@
229 my ($tbl) = $chunk =~ m/$tbl_name/;
230 $tbl =~ s/^\s*`//;
231 $tbl =~ s/`\s*$//;
232- if ( $self->table_is_allowed($self->{db}, $tbl) ) {
233+ if ( $self->_resume_from_table($tbl)
234+ && $self->table_is_allowed($self->{db}, $tbl) ) {
235 my ($ddl) = $chunk =~ m/^(?:$open_comment)?(CREATE TABLE.+?;)$/ms;
236 if ( !$ddl ) {
237 warn "Failed to parse CREATE TABLE from\n" . $chunk;
238 next CHUNK;
239 }
240 $ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
241-
242- my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
243-
244- if ( !$engine || $self->engine_is_allowed($engine) ) {
245+ my $tbl_struct = $self->{TableParser}->parse($ddl);
246+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
247 return {
248- db => $self->{db},
249- tbl => $tbl,
250- ddl => $ddl,
251+ db => $self->{db},
252+ tbl => $tbl,
253+ name => $self->{Quoter}->quote($self->{db}, $tbl),
254+ ddl => $ddl,
255+ tbl_struct => $tbl_struct,
256 };
257 }
258 }
259@@ -3360,6 +3406,7 @@
260 sub _iterate_dbh {
261 my ( $self ) = @_;
262 my $q = $self->{Quoter};
263+ my $tp = $self->{TableParser};
264 my $dbh = $self->{dbh};
265 PTDEBUG && _d('Getting next schema object from dbh', $dbh);
266
267@@ -3373,7 +3420,9 @@
268 }
269
270 if ( !$self->{db} ) {
271- $self->{db} = shift @{$self->{dbs}};
272+ do {
273+ $self->{db} = shift @{$self->{dbs}};
274+ } until $self->_resume_from_database($self->{db});
275 PTDEBUG && _d('Next database:', $self->{db});
276 return unless $self->{db};
277 }
278@@ -3386,8 +3435,9 @@
279 }
280 grep {
281 my ($tbl, $type) = @$_;
282- $self->table_is_allowed($self->{db}, $tbl)
283- && (!$type || ($type ne 'VIEW'));
284+ (!$type || ($type ne 'VIEW'))
285+ && $self->_resume_from_table($tbl)
286+ && $self->table_is_allowed($self->{db}, $tbl);
287 }
288 @{$dbh->selectall_arrayref($sql)};
289 PTDEBUG && _d('Found', scalar @tbls, 'tables in database', $self->{db});
290@@ -3395,27 +3445,15 @@
291 }
292
293 while ( my $tbl = shift @{$self->{tbls}} ) {
294- my $engine;
295- if ( $self->{filters}->{'engines'}
296- || $self->{filters}->{'ignore-engines'} ) {
297- my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
298- . " LIKE \'$tbl\'";
299- PTDEBUG && _d($sql);
300- $engine = $dbh->selectrow_hashref($sql)->{engine};
301- PTDEBUG && _d($tbl, 'uses', $engine, 'engine');
302- }
303-
304-
305- if ( !$engine || $self->engine_is_allowed($engine) ) {
306- my $ddl;
307- if ( my $du = $self->{MySQLDump} ) {
308- $ddl = $du->get_create_table($dbh, $q, $self->{db}, $tbl)->[1];
309- }
310-
311+ my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
312+ my $tbl_struct = $tp->parse($ddl);
313+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
314 return {
315- db => $self->{db},
316- tbl => $tbl,
317- ddl => $ddl,
318+ db => $self->{db},
319+ tbl => $tbl,
320+ name => $q->quote($self->{db}, $tbl),
321+ ddl => $ddl,
322+ tbl_struct => $tbl_struct,
323 };
324 }
325 }
326@@ -3476,6 +3514,10 @@
327
328 my $filter = $self->{filters};
329
330+ if ( $db eq 'mysql' && ($tbl eq 'general_log' || $tbl eq 'slow_log') ) {
331+ return 0;
332+ }
333+
334 if ( $filter->{'ignore-tables'}->{$tbl}
335 && ($filter->{'ignore-tables'}->{$tbl} eq '*'
336 || $filter->{'ignore-tables'}->{$tbl} eq $db) ) {
337@@ -3515,7 +3557,11 @@
338
339 sub engine_is_allowed {
340 my ( $self, $engine ) = @_;
341- die "I need an engine argument" unless $engine;
342+
343+ if ( !$engine ) {
344+ PTDEBUG && _d('No engine specified; allowing the table');
345+ return 1;
346+ }
347
348 $engine = lc $engine;
349
350@@ -3535,6 +3581,40 @@
351 return 1;
352 }
353
354+sub _resume_from_database {
355+ my ($self, $db) = @_;
356+
357+ return 1 unless $self->{resume}->{db};
358+
359+ if ( $db eq $self->{resume}->{db} ) {
360+ PTDEBUG && _d('At resume db', $db);
361+ delete $self->{resume}->{db};
362+ return 1;
363+ }
364+
365+ return 0;
366+}
367+
368+sub _resume_from_table {
369+ my ($self, $tbl) = @_;
370+
371+ return 1 unless $self->{resume}->{tbl};
372+
373+ if ( $tbl eq $self->{resume}->{tbl} ) {
374+ if ( !$self->{resume}->{after} ) {
375+ PTDEBUG && _d('Resuming from table', $tbl);
376+ delete $self->{resume}->{tbl};
377+ return 1;
378+ }
379+ else {
380+ PTDEBUG && _d('Resuming after table', $tbl);
381+ delete $self->{resume}->{tbl};
382+ }
383+ }
384+
385+ return 0;
386+}
387+
388 sub _d {
389 my ($package, undef, $line) = caller 0;
390 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
391@@ -3644,11 +3724,10 @@
392 MySQLDump => $du,
393 TableParser => $tp,
394 Schema => $schema,
395- keep_ddl => 1,
396 );
397 TABLE:
398- while ( my $tbl = $schema_itr->next_schema_object() ) {
399- $tbl->{engine} = $tp->get_engine($tbl->{ddl});
400+ while ( my $tbl = $schema_itr->next() ) {
401+ $tbl->{engine} = $tbl->{tbl_struct}->{engine};
402
403 my ($keys, $clustered_key, $fks);
404 if ( $get_keys ) {
405
406=== modified file 'bin/pt-index-usage'
407--- bin/pt-index-usage 2012-03-07 23:41:54 +0000
408+++ bin/pt-index-usage 2012-03-31 16:06:20 +0000
409@@ -2669,19 +2669,58 @@
410 return bless $self, $class;
411 }
412
413+sub get_create_table {
414+ my ( $self, $dbh, $db, $tbl ) = @_;
415+ die "I need a dbh parameter" unless $dbh;
416+ die "I need a db parameter" unless $db;
417+ die "I need a tbl parameter" unless $tbl;
418+ my $q = $self->{Quoter};
419+
420+ my $new_sql_mode
421+ = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
422+ . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
423+ . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
424+ . '@@SQL_QUOTE_SHOW_CREATE := 1 */';
425+
426+ my $old_sql_mode = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
427+ . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
428+
429+ PTDEBUG && _d($new_sql_mode);
430+ eval { $dbh->do($new_sql_mode); };
431+ PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
432+
433+ my $use_sql = 'USE ' . $q->quote($db);
434+ PTDEBUG && _d($dbh, $use_sql);
435+ $dbh->do($use_sql);
436+
437+ my $show_sql = "SHOW CREATE TABLE " . $q->quote($db, $tbl);
438+ PTDEBUG && _d($show_sql);
439+ my $href;
440+ eval { $href = $dbh->selectrow_hashref($show_sql); };
441+ if ( $EVAL_ERROR ) {
442+ PTDEBUG && _d($EVAL_ERROR);
443+
444+ PTDEBUG && _d($old_sql_mode);
445+ $dbh->do($old_sql_mode);
446+
447+ return;
448+ }
449+
450+ PTDEBUG && _d($old_sql_mode);
451+ $dbh->do($old_sql_mode);
452+
453+ my ($key) = grep { m/create (?:table|view)/i } keys %$href;
454+ if ( !$key ) {
455+ die "Error: no 'Create Table' or 'Create View' in result set from "
456+ . "$show_sql: " . Dumper($href);
457+ }
458+
459+ return $href->{$key};
460+}
461+
462 sub parse {
463 my ( $self, $ddl, $opts ) = @_;
464 return unless $ddl;
465- if ( ref $ddl eq 'ARRAY' ) {
466- if ( lc $ddl->[0] eq 'table' ) {
467- $ddl = $ddl->[1];
468- }
469- else {
470- return {
471- engine => 'VIEW',
472- };
473- }
474- }
475
476 if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
477 die "Cannot parse table definition; is ANSI quoting "
478@@ -2988,41 +3027,31 @@
479 return $ddl;
480 }
481
482-sub remove_secondary_indexes {
483- my ( $self, $ddl ) = @_;
484- my $sec_indexes_ddl;
485- my $tbl_struct = $self->parse($ddl);
486-
487- if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
488- my $clustered_key = $tbl_struct->{clustered_key};
489- $clustered_key ||= '';
490-
491- my @sec_indexes = map {
492- my $key_def = $_->{ddl};
493- $key_def =~ s/([\(\)])/\\$1/g;
494- $ddl =~ s/\s+$key_def//i;
495-
496- my $key_ddl = "ADD $_->{ddl}";
497- $key_ddl .= ',' unless $key_ddl =~ m/,$/;
498- $key_ddl;
499- }
500- grep { $_->{name} ne $clustered_key }
501- values %{$tbl_struct->{keys}};
502- PTDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));
503-
504- if ( @sec_indexes ) {
505- $sec_indexes_ddl = join(' ', @sec_indexes);
506- $sec_indexes_ddl =~ s/,$//;
507- }
508-
509- $ddl =~ s/,(\n\) )/$1/s;
510- }
511- else {
512- PTDEBUG && _d('Not removing secondary indexes from',
513- $tbl_struct->{engine}, 'table');
514- }
515-
516- return $ddl, $sec_indexes_ddl, $tbl_struct;
517+sub get_table_status {
518+ my ( $self, $dbh, $db, $like ) = @_;
519+ my $q = $self->{Quoter};
520+ my $sql = "SHOW TABLE STATUS FROM " . $q->quote($db);
521+ my @params;
522+ if ( $like ) {
523+ $sql .= ' LIKE ?';
524+ push @params, $like;
525+ }
526+ PTDEBUG && _d($sql, @params);
527+ my $sth = $dbh->prepare($sql);
528+ eval { $sth->execute(@params); };
529+ if ($EVAL_ERROR) {
530+ PTDEBUG && _d($EVAL_ERROR);
531+ return;
532+ }
533+ my @tables = @{$sth->fetchall_arrayref({})};
534+ @tables = map {
535+ my %tbl; # Make a copy with lowercased keys
536+ @tbl{ map { lc $_ } keys %$_ } = values %$_;
537+ $tbl{engine} ||= $tbl{type} || $tbl{comment};
538+ delete $tbl{type};
539+ \%tbl;
540+ } @tables;
541+ return @tables;
542 }
543
544 sub _d {
545@@ -3912,7 +3941,7 @@
546
547 sub new {
548 my ( $class, %args ) = @_;
549- my @required_args = qw(OptionParser Quoter);
550+ my @required_args = qw(OptionParser TableParser Quoter);
551 foreach my $arg ( @required_args ) {
552 die "I need a $arg argument" unless $args{$arg};
553 }
554@@ -3921,8 +3950,19 @@
555 die "I need either a dbh or file_itr argument"
556 if (!$dbh && !$file_itr) || ($dbh && $file_itr);
557
558+ my %resume;
559+ if ( my $table = $args{resume} ) {
560+ PTDEBUG && _d('Will resume from or after', $table);
561+ my ($db, $tbl) = $args{Quoter}->split_unquote($table);
562+ die "Resume table must be database-qualified: $table"
563+ unless $db && $tbl;
564+ $resume{db} = $db;
565+ $resume{tbl} = $tbl;
566+ }
567+
568 my $self = {
569 %args,
570+ resume => \%resume,
571 filters => _make_filters(%args),
572 };
573
574@@ -3983,9 +4023,19 @@
575 return \%filters;
576 }
577
578-sub next_schema_object {
579+sub next {
580 my ( $self ) = @_;
581
582+ if ( !$self->{initialized} ) {
583+ $self->{initialized} = 1;
584+ if ( $self->{resume}->{tbl}
585+ && !$self->table_is_allowed(@{$self->{resume}}{qw(db tbl)}) ) {
586+ PTDEBUG && _d('Will resume after',
587+ join('.', @{$self->{resume}}{qw(db tbl)}));
588+ $self->{resume}->{after} = 1;
589+ }
590+ }
591+
592 my $schema_obj;
593 if ( $self->{file_itr} ) {
594 $schema_obj= $self->_iterate_files();
595@@ -3995,24 +4045,18 @@
596 }
597
598 if ( $schema_obj ) {
599- if ( $schema_obj->{ddl} && $self->{TableParser} ) {
600- $schema_obj->{tbl_struct}
601- = $self->{TableParser}->parse($schema_obj->{ddl});
602- }
603-
604- delete $schema_obj->{ddl} unless $self->{keep_ddl};
605-
606 if ( my $schema = $self->{Schema} ) {
607 $schema->add_schema_object($schema_obj);
608 }
609+ PTDEBUG && _d('Next schema object:',
610+ $schema_obj->{db}, $schema_obj->{tbl});
611 }
612
613- PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
614 return $schema_obj;
615 }
616
617 sub _iterate_files {
618- my ( $self ) = @_;
619+ my ( $self ) = @_;
620
621 if ( !$self->{fh} ) {
622 my ($fh, $file) = $self->{file_itr}->();
623@@ -4033,7 +4077,8 @@
624 my $db = $1; # XXX
625 $db =~ s/^`//; # strip leading `
626 $db =~ s/`$//; # and trailing `
627- if ( $self->database_is_allowed($db) ) {
628+ if ( $self->database_is_allowed($db)
629+ && $self->_resume_from_database($db) ) {
630 $self->{db} = $db;
631 }
632 }
633@@ -4046,21 +4091,22 @@
634 my ($tbl) = $chunk =~ m/$tbl_name/;
635 $tbl =~ s/^\s*`//;
636 $tbl =~ s/`\s*$//;
637- if ( $self->table_is_allowed($self->{db}, $tbl) ) {
638+ if ( $self->_resume_from_table($tbl)
639+ && $self->table_is_allowed($self->{db}, $tbl) ) {
640 my ($ddl) = $chunk =~ m/^(?:$open_comment)?(CREATE TABLE.+?;)$/ms;
641 if ( !$ddl ) {
642 warn "Failed to parse CREATE TABLE from\n" . $chunk;
643 next CHUNK;
644 }
645 $ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
646-
647- my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
648-
649- if ( !$engine || $self->engine_is_allowed($engine) ) {
650+ my $tbl_struct = $self->{TableParser}->parse($ddl);
651+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
652 return {
653- db => $self->{db},
654- tbl => $tbl,
655- ddl => $ddl,
656+ db => $self->{db},
657+ tbl => $tbl,
658+ name => $self->{Quoter}->quote($self->{db}, $tbl),
659+ ddl => $ddl,
660+ tbl_struct => $tbl_struct,
661 };
662 }
663 }
664@@ -4077,6 +4123,7 @@
665 sub _iterate_dbh {
666 my ( $self ) = @_;
667 my $q = $self->{Quoter};
668+ my $tp = $self->{TableParser};
669 my $dbh = $self->{dbh};
670 PTDEBUG && _d('Getting next schema object from dbh', $dbh);
671
672@@ -4090,7 +4137,9 @@
673 }
674
675 if ( !$self->{db} ) {
676- $self->{db} = shift @{$self->{dbs}};
677+ do {
678+ $self->{db} = shift @{$self->{dbs}};
679+ } until $self->_resume_from_database($self->{db});
680 PTDEBUG && _d('Next database:', $self->{db});
681 return unless $self->{db};
682 }
683@@ -4103,8 +4152,9 @@
684 }
685 grep {
686 my ($tbl, $type) = @$_;
687- $self->table_is_allowed($self->{db}, $tbl)
688- && (!$type || ($type ne 'VIEW'));
689+ (!$type || ($type ne 'VIEW'))
690+ && $self->_resume_from_table($tbl)
691+ && $self->table_is_allowed($self->{db}, $tbl);
692 }
693 @{$dbh->selectall_arrayref($sql)};
694 PTDEBUG && _d('Found', scalar @tbls, 'tables in database', $self->{db});
695@@ -4112,27 +4162,15 @@
696 }
697
698 while ( my $tbl = shift @{$self->{tbls}} ) {
699- my $engine;
700- if ( $self->{filters}->{'engines'}
701- || $self->{filters}->{'ignore-engines'} ) {
702- my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
703- . " LIKE \'$tbl\'";
704- PTDEBUG && _d($sql);
705- $engine = $dbh->selectrow_hashref($sql)->{engine};
706- PTDEBUG && _d($tbl, 'uses', $engine, 'engine');
707- }
708-
709-
710- if ( !$engine || $self->engine_is_allowed($engine) ) {
711- my $ddl;
712- if ( my $du = $self->{MySQLDump} ) {
713- $ddl = $du->get_create_table($dbh, $q, $self->{db}, $tbl)->[1];
714- }
715-
716+ my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
717+ my $tbl_struct = $tp->parse($ddl);
718+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
719 return {
720- db => $self->{db},
721- tbl => $tbl,
722- ddl => $ddl,
723+ db => $self->{db},
724+ tbl => $tbl,
725+ name => $q->quote($self->{db}, $tbl),
726+ ddl => $ddl,
727+ tbl_struct => $tbl_struct,
728 };
729 }
730 }
731@@ -4193,6 +4231,10 @@
732
733 my $filter = $self->{filters};
734
735+ if ( $db eq 'mysql' && ($tbl eq 'general_log' || $tbl eq 'slow_log') ) {
736+ return 0;
737+ }
738+
739 if ( $filter->{'ignore-tables'}->{$tbl}
740 && ($filter->{'ignore-tables'}->{$tbl} eq '*'
741 || $filter->{'ignore-tables'}->{$tbl} eq $db) ) {
742@@ -4232,7 +4274,11 @@
743
744 sub engine_is_allowed {
745 my ( $self, $engine ) = @_;
746- die "I need an engine argument" unless $engine;
747+
748+ if ( !$engine ) {
749+ PTDEBUG && _d('No engine specified; allowing the table');
750+ return 1;
751+ }
752
753 $engine = lc $engine;
754
755@@ -4252,6 +4298,40 @@
756 return 1;
757 }
758
759+sub _resume_from_database {
760+ my ($self, $db) = @_;
761+
762+ return 1 unless $self->{resume}->{db};
763+
764+ if ( $db eq $self->{resume}->{db} ) {
765+ PTDEBUG && _d('At resume db', $db);
766+ delete $self->{resume}->{db};
767+ return 1;
768+ }
769+
770+ return 0;
771+}
772+
773+sub _resume_from_table {
774+ my ($self, $tbl) = @_;
775+
776+ return 1 unless $self->{resume}->{tbl};
777+
778+ if ( $tbl eq $self->{resume}->{tbl} ) {
779+ if ( !$self->{resume}->{after} ) {
780+ PTDEBUG && _d('Resuming from table', $tbl);
781+ delete $self->{resume}->{tbl};
782+ return 1;
783+ }
784+ else {
785+ PTDEBUG && _d('Resuming after table', $tbl);
786+ delete $self->{resume}->{tbl};
787+ }
788+ }
789+
790+ return 0;
791+}
792+
793 sub _d {
794 my ($package, undef, $line) = caller 0;
795 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
796@@ -5144,13 +5224,11 @@
797 dbh => $dbh,
798 OptionParser => $o,
799 Quoter => $q,
800- MySQLDump => $du,
801 TableParser => $tp,
802 Schema => $schema,
803- keep_ddl => 1,
804 );
805 TALBE:
806- while ( my $tbl = $schema_itr->next_schema_object() ) {
807+ while ( my $tbl = $schema_itr->next() ) {
808 eval {
809 my ($indexes) = $tp->get_keys($tbl->{ddl}, {version => $version});
810 $iu->add_indexes(%$tbl, indexes=>$indexes);
811
812=== modified file 'bin/pt-table-checksum'
813--- bin/pt-table-checksum 2012-03-30 16:02:37 +0000
814+++ bin/pt-table-checksum 2012-03-31 16:06:20 +0000
815@@ -3941,22 +3941,15 @@
816
817 sub get_row_estimate {
818 my (%args) = @_;
819- my @required_args = qw(Cxn tbl OptionParser TableParser Quoter);
820- my ($cxn, $tbl, $o, $tp, $q) = @args{@required_args};
821+ my @required_args = qw(Cxn tbl);
822+ my ($cxn, $tbl) = @args{@required_args};
823
824- if ( $args{where} ) {
825- PTDEBUG && _d('WHERE clause, using explain plan for row estimate');
826- my $table = $q->quote(@{$tbl}{qw(db tbl)});
827- my $sql = "EXPLAIN SELECT * FROM $table WHERE $args{where}";
828- PTDEBUG && _d($sql);
829- my $expl = $cxn->dbh()->selectrow_hashref($sql);
830- PTDEBUG && _d(Dumper($expl));
831- return ($expl->{rows} || 0), $expl->{key};
832- }
833- else {
834- PTDEBUG && _d('No WHERE clause, using table status for row estimate');
835- return $tbl->{tbl_status}->{rows} || 0;
836- }
837+ my $sql = "EXPLAIN SELECT * FROM $tbl->{name} "
838+ . "WHERE " . ($args{where} || '1=1');
839+ PTDEBUG && _d($sql);
840+ my $expl = $cxn->dbh()->selectrow_hashref($sql);
841+ PTDEBUG && _d(Dumper($expl));
842+ return ($expl->{rows} || 0), $expl->{key};
843 }
844
845 sub _prepare_sths {
846@@ -4543,7 +4536,7 @@
847
848 sub new {
849 my ( $class, %args ) = @_;
850- my @required_args = qw(OptionParser Quoter);
851+ my @required_args = qw(OptionParser TableParser Quoter);
852 foreach my $arg ( @required_args ) {
853 die "I need a $arg argument" unless $args{$arg};
854 }
855@@ -4647,25 +4640,18 @@
856 }
857
858 if ( $schema_obj ) {
859- if ( $schema_obj->{ddl} && $self->{TableParser} ) {
860- $schema_obj->{tbl_struct}
861- = $self->{TableParser}->parse($schema_obj->{ddl});
862- }
863-
864- delete $schema_obj->{ddl} unless $self->{keep_ddl};
865- delete $schema_obj->{tbl_status} unless $self->{keep_tbl_status};
866-
867 if ( my $schema = $self->{Schema} ) {
868 $schema->add_schema_object($schema_obj);
869 }
870- PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
871+ PTDEBUG && _d('Next schema object:',
872+ $schema_obj->{db}, $schema_obj->{tbl});
873 }
874
875 return $schema_obj;
876 }
877
878 sub _iterate_files {
879- my ( $self ) = @_;
880+ my ( $self ) = @_;
881
882 if ( !$self->{fh} ) {
883 my ($fh, $file) = $self->{file_itr}->();
884@@ -4708,14 +4694,14 @@
885 next CHUNK;
886 }
887 $ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
888-
889- my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
890-
891- if ( !$engine || $self->engine_is_allowed($engine) ) {
892+ my $tbl_struct = $self->{TableParser}->parse($ddl);
893+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
894 return {
895- db => $self->{db},
896- tbl => $tbl,
897- ddl => $ddl,
898+ db => $self->{db},
899+ tbl => $tbl,
900+ name => $self->{Quoter}->quote($self->{db}, $tbl),
901+ ddl => $ddl,
902+ tbl_struct => $tbl_struct,
903 };
904 }
905 }
906@@ -4732,6 +4718,7 @@
907 sub _iterate_dbh {
908 my ( $self ) = @_;
909 my $q = $self->{Quoter};
910+ my $tp = $self->{TableParser};
911 my $dbh = $self->{dbh};
912 PTDEBUG && _d('Getting next schema object from dbh', $dbh);
913
914@@ -4770,30 +4757,15 @@
915 }
916
917 while ( my $tbl = shift @{$self->{tbls}} ) {
918- my $tbl_status;
919- if ( $self->{filters}->{'engines'}
920- || $self->{filters}->{'ignore-engines'}
921- || $self->{keep_tbl_status} )
922- {
923- my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
924- . " LIKE \'$tbl\'";
925- PTDEBUG && _d($sql);
926- $tbl_status = $dbh->selectrow_hashref($sql);
927- PTDEBUG && _d(Dumper($tbl_status));
928- }
929-
930- if ( !$tbl_status
931- || $self->engine_is_allowed($tbl_status->{engine}) ) {
932- my $ddl;
933- if ( my $tp = $self->{TableParser} ) {
934- $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
935- }
936-
937+ my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
938+ my $tbl_struct = $tp->parse($ddl);
939+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
940 return {
941 db => $self->{db},
942 tbl => $tbl,
943+ name => $q->quote($self->{db}, $tbl),
944 ddl => $ddl,
945- tbl_status => $tbl_status,
946+ tbl_struct => $tbl_struct,
947 };
948 }
949 }
950@@ -4897,7 +4869,11 @@
951
952 sub engine_is_allowed {
953 my ( $self, $engine ) = @_;
954- die "I need an engine argument" unless $engine;
955+
956+ if ( !$engine ) {
957+ PTDEBUG && _d('No engine specified; allowing the table');
958+ return 1;
959+ }
960
961 $engine = lc $engine;
962
963@@ -6253,13 +6229,12 @@
964 }
965
966 my $schema_iter = new SchemaIterator(
967- dbh => $master_dbh,
968- resume => $last_chunk ? $q->quote(@{$last_chunk}{qw(db tbl)})
969- : "",
970- keep_tbl_status => 1,
971- OptionParser => $o,
972- TableParser => $tp,
973- Quoter => $q,
974+ dbh => $master_dbh,
975+ resume => $last_chunk ? $q->quote(@{$last_chunk}{qw(db tbl)})
976+ : "",
977+ OptionParser => $o,
978+ TableParser => $tp,
979+ Quoter => $q,
980 );
981
982 if ( $last_chunk &&
983@@ -6334,13 +6309,13 @@
984 my $chunk_size_limit = $o->get('chunk-size-limit');
985 my @too_large;
986 foreach my $slave ( @$slaves ) {
987+ # get_row_estimate() returns (row_est, index), but
988+ # we only need the row_est. Maybe in the future we'll
989+ # care what index MySQL will use on a slave.
990 my ($n_rows) = NibbleIterator::get_row_estimate(
991- Cxn => $slave,
992- tbl => $tbl,
993- where => $o->get('where') || "1=1",
994- OptionParser => $o,
995- TableParser => $tp,
996- Quoter => $q,
997+ Cxn => $slave,
998+ tbl => $tbl,
999+ where => $o->get('where'),
1000 );
1001 PTDEBUG && _d('Table on', $slave->name(),
1002 'has', $n_rows, 'rows');
1003
1004=== modified file 'bin/pt-table-sync'
1005--- bin/pt-table-sync 2012-03-30 16:37:16 +0000
1006+++ bin/pt-table-sync 2012-03-31 16:06:20 +0000
1007@@ -959,7 +959,7 @@
1008 $opt->{value} = ($pre || '') . $num;
1009 }
1010 else {
1011- $self->save_error("Invalid size for --$opt->{long}");
1012+ $self->save_error("Invalid size for --$opt->{long}: $val");
1013 }
1014 return;
1015 }
1016@@ -1285,12 +1285,14 @@
1017 sub as_string {
1018 my ( $self, $dsn, $props ) = @_;
1019 return $dsn unless ref $dsn;
1020- my %allowed = $props ? map { $_=>1 } @$props : ();
1021+ my @keys = $props ? @$props : sort keys %$dsn;
1022 return join(',',
1023- map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
1024- grep { defined $dsn->{$_} && $self->{opts}->{$_} }
1025- grep { !$props || $allowed{$_} }
1026- sort keys %$dsn );
1027+ map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
1028+ grep {
1029+ exists $self->{opts}->{$_}
1030+ && exists $dsn->{$_}
1031+ && defined $dsn->{$_}
1032+ } @keys);
1033 }
1034
1035 sub usage {
1036@@ -1741,19 +1743,58 @@
1037 return bless $self, $class;
1038 }
1039
1040+sub get_create_table {
1041+ my ( $self, $dbh, $db, $tbl ) = @_;
1042+ die "I need a dbh parameter" unless $dbh;
1043+ die "I need a db parameter" unless $db;
1044+ die "I need a tbl parameter" unless $tbl;
1045+ my $q = $self->{Quoter};
1046+
1047+ my $new_sql_mode
1048+ = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
1049+ . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
1050+ . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
1051+ . '@@SQL_QUOTE_SHOW_CREATE := 1 */';
1052+
1053+ my $old_sql_mode = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
1054+ . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
1055+
1056+ PTDEBUG && _d($new_sql_mode);
1057+ eval { $dbh->do($new_sql_mode); };
1058+ PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
1059+
1060+ my $use_sql = 'USE ' . $q->quote($db);
1061+ PTDEBUG && _d($dbh, $use_sql);
1062+ $dbh->do($use_sql);
1063+
1064+ my $show_sql = "SHOW CREATE TABLE " . $q->quote($db, $tbl);
1065+ PTDEBUG && _d($show_sql);
1066+ my $href;
1067+ eval { $href = $dbh->selectrow_hashref($show_sql); };
1068+ if ( $EVAL_ERROR ) {
1069+ PTDEBUG && _d($EVAL_ERROR);
1070+
1071+ PTDEBUG && _d($old_sql_mode);
1072+ $dbh->do($old_sql_mode);
1073+
1074+ return;
1075+ }
1076+
1077+ PTDEBUG && _d($old_sql_mode);
1078+ $dbh->do($old_sql_mode);
1079+
1080+ my ($key) = grep { m/create (?:table|view)/i } keys %$href;
1081+ if ( !$key ) {
1082+ die "Error: no 'Create Table' or 'Create View' in result set from "
1083+ . "$show_sql: " . Dumper($href);
1084+ }
1085+
1086+ return $href->{$key};
1087+}
1088+
1089 sub parse {
1090 my ( $self, $ddl, $opts ) = @_;
1091 return unless $ddl;
1092- if ( ref $ddl eq 'ARRAY' ) {
1093- if ( lc $ddl->[0] eq 'table' ) {
1094- $ddl = $ddl->[1];
1095- }
1096- else {
1097- return {
1098- engine => 'VIEW',
1099- };
1100- }
1101- }
1102
1103 if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
1104 die "Cannot parse table definition; is ANSI quoting "
1105@@ -2060,41 +2101,31 @@
1106 return $ddl;
1107 }
1108
1109-sub remove_secondary_indexes {
1110- my ( $self, $ddl ) = @_;
1111- my $sec_indexes_ddl;
1112- my $tbl_struct = $self->parse($ddl);
1113-
1114- if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
1115- my $clustered_key = $tbl_struct->{clustered_key};
1116- $clustered_key ||= '';
1117-
1118- my @sec_indexes = map {
1119- my $key_def = $_->{ddl};
1120- $key_def =~ s/([\(\)])/\\$1/g;
1121- $ddl =~ s/\s+$key_def//i;
1122-
1123- my $key_ddl = "ADD $_->{ddl}";
1124- $key_ddl .= ',' unless $key_ddl =~ m/,$/;
1125- $key_ddl;
1126- }
1127- grep { $_->{name} ne $clustered_key }
1128- values %{$tbl_struct->{keys}};
1129- PTDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));
1130-
1131- if ( @sec_indexes ) {
1132- $sec_indexes_ddl = join(' ', @sec_indexes);
1133- $sec_indexes_ddl =~ s/,$//;
1134- }
1135-
1136- $ddl =~ s/,(\n\) )/$1/s;
1137- }
1138- else {
1139- PTDEBUG && _d('Not removing secondary indexes from',
1140- $tbl_struct->{engine}, 'table');
1141- }
1142-
1143- return $ddl, $sec_indexes_ddl, $tbl_struct;
1144+sub get_table_status {
1145+ my ( $self, $dbh, $db, $like ) = @_;
1146+ my $q = $self->{Quoter};
1147+ my $sql = "SHOW TABLE STATUS FROM " . $q->quote($db);
1148+ my @params;
1149+ if ( $like ) {
1150+ $sql .= ' LIKE ?';
1151+ push @params, $like;
1152+ }
1153+ PTDEBUG && _d($sql, @params);
1154+ my $sth = $dbh->prepare($sql);
1155+ eval { $sth->execute(@params); };
1156+ if ($EVAL_ERROR) {
1157+ PTDEBUG && _d($EVAL_ERROR);
1158+ return;
1159+ }
1160+ my @tables = @{$sth->fetchall_arrayref({})};
1161+ @tables = map {
1162+ my %tbl; # Make a copy with lowercased keys
1163+ @tbl{ map { lc $_ } keys %$_ } = values %$_;
1164+ $tbl{engine} ||= $tbl{type} || $tbl{comment};
1165+ delete $tbl{type};
1166+ \%tbl;
1167+ } @tables;
1168+ return @tables;
1169 }
1170
1171 sub _d {
1172@@ -5445,11 +5476,12 @@
1173
1174 eval {
1175 if ( my $timeout = $args{wait} ) {
1176- my $wait = $args{wait_retry_args}->{wait} || 10;
1177+ my $ms = $self->{MasterSlave};
1178 my $tries = $args{wait_retry_args}->{tries} || 3;
1179+ my $wait;
1180 $self->{Retry}->retry(
1181- wait => sub { sleep $wait; },
1182 tries => $tries,
1183+ wait => sub { sleep $args{wait_retry_args}->{wait} || 10 },
1184 try => sub {
1185 my ( %args ) = @_;
1186
1187@@ -5457,12 +5489,18 @@
1188 warn "Retrying MASTER_POS_WAIT() for --wait $timeout...";
1189 }
1190
1191- my $ms = $self->{MasterSlave};
1192- my $wait = $ms->wait_for_master(
1193+ $wait = $ms->wait_for_master(
1194 master_status => $ms->get_master_status($src->{misc_dbh}),
1195 slave_dbh => $dst->{dbh},
1196 timeout => $timeout,
1197 );
1198+ if ( defined $wait->{result} && $wait->{result} != -1 ) {
1199+ return; # slave caught up
1200+ }
1201+ die; # call fail
1202+ },
1203+ fail => sub {
1204+ my (%args) = @_;
1205 if ( !defined $wait->{result} ) {
1206 my $msg;
1207 if ( $wait->{waited} ) {
1208@@ -5477,20 +5515,14 @@
1209 $msg .= " Sleeping $wait seconds then retrying "
1210 . ($tries - $args{tryno}) . " more times.";
1211 }
1212- warn $msg;
1213- return;
1214+ warn "$msg\n";
1215+ return 1; # call wait, call try
1216 }
1217 elsif ( $wait->{result} == -1 ) {
1218- die "Slave did not catch up to its master after waiting "
1219- . "$timeout seconds with MASTER_POS_WAIT. Try inceasing "
1220- . "the --wait time, or disable this feature by specifying "
1221- . "--wait 0.";
1222- }
1223- else {
1224- return $result; # slave caught up
1225+ return 0; # call final_fail
1226 }
1227 },
1228- on_failure => sub {
1229+ final_fail => sub {
1230 die "Slave did not catch up to its master after $tries attempts "
1231 . "of waiting $timeout seconds with MASTER_POS_WAIT. "
1232 . "Check that the slave is running, increase the --wait "
1233@@ -5603,23 +5635,21 @@
1234 die "I need a $arg argument" unless defined $args{$arg};
1235 }
1236 my ($tbl_struct, $index) = @args{@required_args};
1237- my @cols = $args{cols} ? @{$args{cols}} : @{$tbl_struct->{cols}};
1238+ my @cols = $args{cols} ? @{$args{cols}} : @{$tbl_struct->{cols}};
1239 my $q = $self->{Quoter};
1240
1241 die "Index '$index' does not exist in table"
1242 unless exists $tbl_struct->{keys}->{$index};
1243+ PTDEBUG && _d('Will ascend index', $index);
1244
1245 my @asc_cols = @{$tbl_struct->{keys}->{$index}->{cols}};
1246- my @asc_slice;
1247-
1248- @asc_cols = @{$tbl_struct->{keys}->{$index}->{cols}};
1249- PTDEBUG && _d('Will ascend index', $index);
1250- PTDEBUG && _d('Will ascend columns', join(', ', @asc_cols));
1251 if ( $args{asc_first} ) {
1252 @asc_cols = $asc_cols[0];
1253 PTDEBUG && _d('Ascending only first column');
1254 }
1255+ PTDEBUG && _d('Will ascend columns', join(', ', @asc_cols));
1256
1257+ my @asc_slice;
1258 my %col_posn = do { my $i = 0; map { $_ => $i++ } @cols };
1259 foreach my $col ( @asc_cols ) {
1260 if ( !exists $col_posn{$col} ) {
1261@@ -6720,292 +6750,427 @@
1262 # ###########################################################################
1263
1264 # ###########################################################################
1265-# SchemaIterator r7141
1266-# Don't update this package!
1267+# SchemaIterator package
1268+# This package is a copy without comments from the original. The original
1269+# with comments and its test file can be found in the Bazaar repository at,
1270+# lib/SchemaIterator.pm
1271+# t/lib/SchemaIterator.t
1272+# See https://launchpad.net/percona-toolkit for more information.
1273 # ###########################################################################
1274+{
1275 package SchemaIterator;
1276
1277 use strict;
1278 use warnings FATAL => 'all';
1279-
1280 use English qw(-no_match_vars);
1281+use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1282+
1283 use Data::Dumper;
1284 $Data::Dumper::Indent = 1;
1285 $Data::Dumper::Sortkeys = 1;
1286 $Data::Dumper::Quotekeys = 0;
1287
1288-use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1289+my $open_comment = qr{/\*!\d{5} };
1290+my $tbl_name = qr{
1291+ CREATE\s+
1292+ (?:TEMPORARY\s+)?
1293+ TABLE\s+
1294+ (?:IF NOT EXISTS\s+)?
1295+ ([^\(]+)
1296+}x;
1297+
1298
1299 sub new {
1300 my ( $class, %args ) = @_;
1301- foreach my $arg ( qw(Quoter) ) {
1302+ my @required_args = qw(OptionParser TableParser Quoter);
1303+ foreach my $arg ( @required_args ) {
1304 die "I need a $arg argument" unless $args{$arg};
1305 }
1306+
1307+ my ($file_itr, $dbh) = @args{qw(file_itr dbh)};
1308+ die "I need either a dbh or file_itr argument"
1309+ if (!$dbh && !$file_itr) || ($dbh && $file_itr);
1310+
1311+ my %resume;
1312+ if ( my $table = $args{resume} ) {
1313+ PTDEBUG && _d('Will resume from or after', $table);
1314+ my ($db, $tbl) = $args{Quoter}->split_unquote($table);
1315+ die "Resume table must be database-qualified: $table"
1316+ unless $db && $tbl;
1317+ $resume{db} = $db;
1318+ $resume{tbl} = $tbl;
1319+ }
1320+
1321 my $self = {
1322 %args,
1323- filter => undef,
1324- dbs => [],
1325+ resume => \%resume,
1326+ filters => _make_filters(%args),
1327 };
1328+
1329 return bless $self, $class;
1330 }
1331
1332-sub make_filter {
1333- my ( $self, $o ) = @_;
1334- my @lines = (
1335- 'sub {',
1336- ' my ( $dbh, $db, $tbl ) = @_;',
1337- ' my $engine = undef;',
1338- );
1339-
1340-
1341- my @permit_dbs = _make_filter('unless', '$db', $o->get('databases'))
1342- if $o->has('databases');
1343- my @reject_dbs = _make_filter('if', '$db', $o->get('ignore-databases'))
1344- if $o->has('ignore-databases');
1345- my @dbs_regex;
1346- if ( $o->has('databases-regex') && (my $p = $o->get('databases-regex')) ) {
1347- push @dbs_regex, " return 0 unless \$db && (\$db =~ m/$p/o);";
1348- }
1349- my @reject_dbs_regex;
1350- if ( $o->has('ignore-databases-regex')
1351- && (my $p = $o->get('ignore-databases-regex')) ) {
1352- push @reject_dbs_regex, " return 0 if \$db && (\$db =~ m/$p/o);";
1353- }
1354- if ( @permit_dbs || @reject_dbs || @dbs_regex || @reject_dbs_regex ) {
1355- push @lines,
1356- ' if ( $db ) {',
1357- (@permit_dbs ? @permit_dbs : ()),
1358- (@reject_dbs ? @reject_dbs : ()),
1359- (@dbs_regex ? @dbs_regex : ()),
1360- (@reject_dbs_regex ? @reject_dbs_regex : ()),
1361- ' }';
1362- }
1363-
1364- if ( $o->has('tables') || $o->has('ignore-tables')
1365- || $o->has('ignore-tables-regex') ) {
1366-
1367- my $have_qtbl = 0;
1368- my $have_only_qtbls = 0;
1369- my %qtbls;
1370-
1371- my @permit_tbls;
1372- my @permit_qtbls;
1373- my %permit_qtbls;
1374- if ( $o->get('tables') ) {
1375- my %tbls;
1376- map {
1377- if ( $_ =~ m/\./ ) {
1378- $permit_qtbls{$_} = 1;
1379- }
1380- else {
1381- $tbls{$_} = 1;
1382- }
1383- } keys %{ $o->get('tables') };
1384- @permit_tbls = _make_filter('unless', '$tbl', \%tbls);
1385- @permit_qtbls = _make_filter('unless', '$qtbl', \%permit_qtbls);
1386-
1387- if ( @permit_qtbls ) {
1388- push @lines,
1389- ' my $qtbl = ($db ? "$db." : "") . ($tbl ? $tbl : "");';
1390- $have_qtbl = 1;
1391- }
1392- }
1393-
1394- my @reject_tbls;
1395- my @reject_qtbls;
1396- my %reject_qtbls;
1397- if ( $o->get('ignore-tables') ) {
1398- my %tbls;
1399- map {
1400- if ( $_ =~ m/\./ ) {
1401- $reject_qtbls{$_} = 1;
1402- }
1403- else {
1404- $tbls{$_} = 1;
1405- }
1406- } keys %{ $o->get('ignore-tables') };
1407- @reject_tbls= _make_filter('if', '$tbl', \%tbls);
1408- @reject_qtbls = _make_filter('if', '$qtbl', \%reject_qtbls);
1409-
1410- if ( @reject_qtbls && !$have_qtbl ) {
1411- push @lines,
1412- ' my $qtbl = ($db ? "$db." : "") . ($tbl ? $tbl : "");';
1413- }
1414- }
1415-
1416- if ( keys %permit_qtbls && !@permit_dbs ) {
1417- my $dbs = {};
1418- map {
1419- my ($db, undef) = split(/\./, $_);
1420- $dbs->{$db} = 1;
1421- } keys %permit_qtbls;
1422- PTDEBUG && _d('Adding restriction "--databases',
1423- (join(',', keys %$dbs) . '"'));
1424- if ( keys %$dbs ) {
1425- $o->set('databases', $dbs);
1426- return $self->make_filter($o);
1427- }
1428- }
1429-
1430- my @tbls_regex;
1431- if ( $o->has('tables-regex') && (my $p = $o->get('tables-regex')) ) {
1432- push @tbls_regex, " return 0 unless \$tbl && (\$tbl =~ m/$p/o);";
1433- }
1434- my @reject_tbls_regex;
1435- if ( $o->has('ignore-tables-regex')
1436- && (my $p = $o->get('ignore-tables-regex')) ) {
1437- push @reject_tbls_regex,
1438- " return 0 if \$tbl && (\$tbl =~ m/$p/o);";
1439- }
1440-
1441- my @get_eng;
1442- my @permit_engs;
1443- my @reject_engs;
1444- if ( ($o->has('engines') && $o->get('engines'))
1445- || ($o->has('ignore-engines') && $o->get('ignore-engines')) ) {
1446- push @get_eng,
1447- ' my $sql = "SHOW TABLE STATUS "',
1448- ' . ($db ? "FROM `$db`" : "")',
1449- ' . " LIKE \'$tbl\'";',
1450- ' PTDEBUG && _d($sql);',
1451- ' eval {',
1452- ' $engine = $dbh->selectrow_hashref($sql)->{engine};',
1453- ' };',
1454- ' PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);',
1455- ' PTDEBUG && _d($tbl, "uses engine", $engine);',
1456- ' $engine = lc $engine if $engine;',
1457- @permit_engs
1458- = _make_filter('unless', '$engine', $o->get('engines'), 1);
1459- @reject_engs
1460- = _make_filter('if', '$engine', $o->get('ignore-engines'), 1)
1461- }
1462-
1463- if ( @permit_tbls || @permit_qtbls || @reject_tbls || @tbls_regex
1464- || @reject_tbls_regex || @permit_engs || @reject_engs ) {
1465- push @lines,
1466- ' if ( $tbl ) {',
1467- (@permit_tbls ? @permit_tbls : ()),
1468- (@reject_tbls ? @reject_tbls : ()),
1469- (@tbls_regex ? @tbls_regex : ()),
1470- (@reject_tbls_regex ? @reject_tbls_regex : ()),
1471- (@permit_qtbls ? @permit_qtbls : ()),
1472- (@reject_qtbls ? @reject_qtbls : ()),
1473- (@get_eng ? @get_eng : ()),
1474- (@permit_engs ? @permit_engs : ()),
1475- (@reject_engs ? @reject_engs : ()),
1476- ' }';
1477- }
1478- }
1479-
1480- push @lines,
1481- ' PTDEBUG && _d(\'Passes filters:\', $db, $tbl, $engine, $dbh);',
1482- ' return 1;', '}';
1483-
1484- my $code = join("\n", @lines);
1485- PTDEBUG && _d('filter sub:', $code);
1486- my $filter_sub= eval $code
1487- or die "Error compiling subroutine code:\n$code\n$EVAL_ERROR";
1488-
1489- return $filter_sub;
1490-}
1491-
1492-sub set_filter {
1493- my ( $self, $filter_sub ) = @_;
1494- $self->{filter} = $filter_sub;
1495- PTDEBUG && _d('Set filter sub');
1496- return;
1497-}
1498-
1499-sub get_db_itr {
1500- my ( $self, %args ) = @_;
1501- my @required_args = qw(dbh);
1502+sub _make_filters {
1503+ my ( %args ) = @_;
1504+ my @required_args = qw(OptionParser Quoter);
1505 foreach my $arg ( @required_args ) {
1506 die "I need a $arg argument" unless $args{$arg};
1507 }
1508- my ($dbh) = @args{@required_args};
1509-
1510- my $filter = $self->{filter};
1511- my @dbs;
1512- eval {
1513+ my ($o, $q) = @args{@required_args};
1514+
1515+ my %filters;
1516+
1517+
1518+ my @simple_filters = qw(
1519+ databases tables engines
1520+ ignore-databases ignore-tables ignore-engines);
1521+ FILTER:
1522+ foreach my $filter ( @simple_filters ) {
1523+ if ( $o->has($filter) ) {
1524+ my $objs = $o->get($filter);
1525+ next FILTER unless $objs && scalar keys %$objs;
1526+ my $is_table = $filter =~ m/table/ ? 1 : 0;
1527+ foreach my $obj ( keys %$objs ) {
1528+ die "Undefined value for --$filter" unless $obj;
1529+ $obj = lc $obj;
1530+ if ( $is_table ) {
1531+ my ($db, $tbl) = $q->split_unquote($obj);
1532+ $db ||= '*';
1533+ PTDEBUG && _d('Filter', $filter, 'value:', $db, $tbl);
1534+ $filters{$filter}->{$tbl} = $db;
1535+ }
1536+ else { # database
1537+ PTDEBUG && _d('Filter', $filter, 'value:', $obj);
1538+ $filters{$filter}->{$obj} = 1;
1539+ }
1540+ }
1541+ }
1542+ }
1543+
1544+ my @regex_filters = qw(
1545+ databases-regex tables-regex
1546+ ignore-databases-regex ignore-tables-regex);
1547+ REGEX_FILTER:
1548+ foreach my $filter ( @regex_filters ) {
1549+ if ( $o->has($filter) ) {
1550+ my $pat = $o->get($filter);
1551+ next REGEX_FILTER unless $pat;
1552+ $filters{$filter} = qr/$pat/;
1553+ PTDEBUG && _d('Filter', $filter, 'value:', $filters{$filter});
1554+ }
1555+ }
1556+
1557+ PTDEBUG && _d('Schema object filters:', Dumper(\%filters));
1558+ return \%filters;
1559+}
1560+
1561+sub next {
1562+ my ( $self ) = @_;
1563+
1564+ if ( !$self->{initialized} ) {
1565+ $self->{initialized} = 1;
1566+ if ( $self->{resume}->{tbl}
1567+ && !$self->table_is_allowed(@{$self->{resume}}{qw(db tbl)}) ) {
1568+ PTDEBUG && _d('Will resume after',
1569+ join('.', @{$self->{resume}}{qw(db tbl)}));
1570+ $self->{resume}->{after} = 1;
1571+ }
1572+ }
1573+
1574+ my $schema_obj;
1575+ if ( $self->{file_itr} ) {
1576+ $schema_obj= $self->_iterate_files();
1577+ }
1578+ else { # dbh
1579+ $schema_obj= $self->_iterate_dbh();
1580+ }
1581+
1582+ if ( $schema_obj ) {
1583+ if ( my $schema = $self->{Schema} ) {
1584+ $schema->add_schema_object($schema_obj);
1585+ }
1586+ PTDEBUG && _d('Next schema object:',
1587+ $schema_obj->{db}, $schema_obj->{tbl});
1588+ }
1589+
1590+ return $schema_obj;
1591+}
1592+
1593+sub _iterate_files {
1594+ my ( $self ) = @_;
1595+
1596+ if ( !$self->{fh} ) {
1597+ my ($fh, $file) = $self->{file_itr}->();
1598+ if ( !$fh ) {
1599+ PTDEBUG && _d('No more files to iterate');
1600+ return;
1601+ }
1602+ $self->{fh} = $fh;
1603+ $self->{file} = $file;
1604+ }
1605+ my $fh = $self->{fh};
1606+ PTDEBUG && _d('Getting next schema object from', $self->{file});
1607+
1608+ local $INPUT_RECORD_SEPARATOR = '';
1609+ CHUNK:
1610+ while (defined(my $chunk = <$fh>)) {
1611+ if ($chunk =~ m/Database: (\S+)/) {
1612+ my $db = $1; # XXX
1613+ $db =~ s/^`//; # strip leading `
1614+ $db =~ s/`$//; # and trailing `
1615+ if ( $self->database_is_allowed($db)
1616+ && $self->_resume_from_database($db) ) {
1617+ $self->{db} = $db;
1618+ }
1619+ }
1620+ elsif ($self->{db} && $chunk =~ m/CREATE TABLE/) {
1621+ if ($chunk =~ m/DROP VIEW IF EXISTS/) {
1622+ PTDEBUG && _d('Table is a VIEW, skipping');
1623+ next CHUNK;
1624+ }
1625+
1626+ my ($tbl) = $chunk =~ m/$tbl_name/;
1627+ $tbl =~ s/^\s*`//;
1628+ $tbl =~ s/`\s*$//;
1629+ if ( $self->_resume_from_table($tbl)
1630+ && $self->table_is_allowed($self->{db}, $tbl) ) {
1631+ my ($ddl) = $chunk =~ m/^(?:$open_comment)?(CREATE TABLE.+?;)$/ms;
1632+ if ( !$ddl ) {
1633+ warn "Failed to parse CREATE TABLE from\n" . $chunk;
1634+ next CHUNK;
1635+ }
1636+ $ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
1637+ my $tbl_struct = $self->{TableParser}->parse($ddl);
1638+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
1639+ return {
1640+ db => $self->{db},
1641+ tbl => $tbl,
1642+ name => $self->{Quoter}->quote($self->{db}, $tbl),
1643+ ddl => $ddl,
1644+ tbl_struct => $tbl_struct,
1645+ };
1646+ }
1647+ }
1648+ }
1649+ } # CHUNK
1650+
1651+ PTDEBUG && _d('No more schema objects in', $self->{file});
1652+ close $self->{fh};
1653+ $self->{fh} = undef;
1654+
1655+ return $self->_iterate_files();
1656+}
1657+
1658+sub _iterate_dbh {
1659+ my ( $self ) = @_;
1660+ my $q = $self->{Quoter};
1661+ my $tp = $self->{TableParser};
1662+ my $dbh = $self->{dbh};
1663+ PTDEBUG && _d('Getting next schema object from dbh', $dbh);
1664+
1665+ if ( !defined $self->{dbs} ) {
1666 my $sql = 'SHOW DATABASES';
1667 PTDEBUG && _d($sql);
1668- @dbs = grep {
1669- my $ok = $filter ? $filter->($dbh, $_, undef) : 1;
1670- $ok = 0 if $_ =~ m/information_schema|performance_schema|lost\+found/;
1671- $ok;
1672- } @{ $dbh->selectcol_arrayref($sql) };
1673+ my @dbs = grep { $self->database_is_allowed($_) }
1674+ @{$dbh->selectcol_arrayref($sql)};
1675 PTDEBUG && _d('Found', scalar @dbs, 'databases');
1676- };
1677-
1678- PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
1679- my $iterator = sub {
1680- return shift @dbs;
1681- };
1682-
1683- if (wantarray) {
1684- return ($iterator, scalar @dbs);
1685- }
1686- else {
1687- return $iterator;
1688- }
1689-}
1690-
1691-sub get_tbl_itr {
1692- my ( $self, %args ) = @_;
1693- my @required_args = qw(dbh db);
1694- foreach my $arg ( @required_args ) {
1695- die "I need a $arg argument" unless $args{$arg};
1696- }
1697- my ($dbh, $db, $views) = @args{@required_args, 'views'};
1698-
1699- my $filter = $self->{filter};
1700- my @tbls;
1701- if ( $db ) {
1702- eval {
1703- my $sql = 'SHOW /*!50002 FULL*/ TABLES FROM '
1704- . $self->{Quoter}->quote($db);
1705- PTDEBUG && _d($sql);
1706- @tbls = map {
1707- $_->[0]
1708- }
1709- grep {
1710- my ($tbl, $type) = @$_;
1711- my $ok = $filter ? $filter->($dbh, $db, $tbl) : 1;
1712- if ( !$views ) {
1713- $ok = 0 if ($type || '') eq 'VIEW';
1714- }
1715- $ok;
1716- }
1717- @{ $dbh->selectall_arrayref($sql) };
1718- PTDEBUG && _d('Found', scalar @tbls, 'tables in', $db);
1719- };
1720- PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
1721- }
1722- else {
1723- PTDEBUG && _d('No db given so no tables');
1724- }
1725-
1726- my $iterator = sub {
1727- return shift @tbls;
1728- };
1729-
1730- if ( wantarray ) {
1731- return ($iterator, scalar @tbls);
1732- }
1733- else {
1734- return $iterator;
1735- }
1736-}
1737-
1738-sub _make_filter {
1739- my ( $cond, $var_name, $objs, $lc ) = @_;
1740- my @lines;
1741- if ( scalar keys %$objs ) {
1742- my $test = join(' || ',
1743- map { "$var_name eq '" . ($lc ? lc $_ : $_) ."'" } keys %$objs);
1744- push @lines, " return 0 $cond $var_name && ($test);",
1745- }
1746- return @lines;
1747+ $self->{dbs} = \@dbs;
1748+ }
1749+
1750+ if ( !$self->{db} ) {
1751+ do {
1752+ $self->{db} = shift @{$self->{dbs}};
1753+ } until $self->_resume_from_database($self->{db});
1754+ PTDEBUG && _d('Next database:', $self->{db});
1755+ return unless $self->{db};
1756+ }
1757+
1758+ if ( !defined $self->{tbls} ) {
1759+ my $sql = 'SHOW /*!50002 FULL*/ TABLES FROM ' . $q->quote($self->{db});
1760+ PTDEBUG && _d($sql);
1761+ my @tbls = map {
1762+ $_->[0]; # (tbl, type)
1763+ }
1764+ grep {
1765+ my ($tbl, $type) = @$_;
1766+ (!$type || ($type ne 'VIEW'))
1767+ && $self->_resume_from_table($tbl)
1768+ && $self->table_is_allowed($self->{db}, $tbl);
1769+ }
1770+ @{$dbh->selectall_arrayref($sql)};
1771+ PTDEBUG && _d('Found', scalar @tbls, 'tables in database', $self->{db});
1772+ $self->{tbls} = \@tbls;
1773+ }
1774+
1775+ while ( my $tbl = shift @{$self->{tbls}} ) {
1776+ my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
1777+ my $tbl_struct = $tp->parse($ddl);
1778+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
1779+ return {
1780+ db => $self->{db},
1781+ tbl => $tbl,
1782+ name => $q->quote($self->{db}, $tbl),
1783+ ddl => $ddl,
1784+ tbl_struct => $tbl_struct,
1785+ };
1786+ }
1787+ }
1788+
1789+ PTDEBUG && _d('No more tables in database', $self->{db});
1790+ $self->{db} = undef;
1791+ $self->{tbls} = undef;
1792+
1793+ return $self->_iterate_dbh();
1794+}
1795+
1796+sub database_is_allowed {
1797+ my ( $self, $db ) = @_;
1798+ die "I need a db argument" unless $db;
1799+
1800+ $db = lc $db;
1801+
1802+ my $filter = $self->{filters};
1803+
1804+ if ( $db =~ m/information_schema|performance_schema|lost\+found/ ) {
1805+ PTDEBUG && _d('Database', $db, 'is a system database, ignoring');
1806+ return 0;
1807+ }
1808+
1809+ if ( $self->{filters}->{'ignore-databases'}->{$db} ) {
1810+ PTDEBUG && _d('Database', $db, 'is in --ignore-databases list');
1811+ return 0;
1812+ }
1813+
1814+ if ( $filter->{'ignore-databases-regex'}
1815+ && $db =~ $filter->{'ignore-databases-regex'} ) {
1816+ PTDEBUG && _d('Database', $db, 'matches --ignore-databases-regex');
1817+ return 0;
1818+ }
1819+
1820+ if ( $filter->{'databases'}
1821+ && !$filter->{'databases'}->{$db} ) {
1822+ PTDEBUG && _d('Database', $db, 'is not in --databases list, ignoring');
1823+ return 0;
1824+ }
1825+
1826+ if ( $filter->{'databases-regex'}
1827+ && $db !~ $filter->{'databases-regex'} ) {
1828+ PTDEBUG && _d('Database', $db, 'does not match --databases-regex, ignoring');
1829+ return 0;
1830+ }
1831+
1832+ return 1;
1833+}
1834+
1835+sub table_is_allowed {
1836+ my ( $self, $db, $tbl ) = @_;
1837+ die "I need a db argument" unless $db;
1838+ die "I need a tbl argument" unless $tbl;
1839+
1840+ $db = lc $db;
1841+ $tbl = lc $tbl;
1842+
1843+ my $filter = $self->{filters};
1844+
1845+ if ( $db eq 'mysql' && ($tbl eq 'general_log' || $tbl eq 'slow_log') ) {
1846+ return 0;
1847+ }
1848+
1849+ if ( $filter->{'ignore-tables'}->{$tbl}
1850+ && ($filter->{'ignore-tables'}->{$tbl} eq '*'
1851+ || $filter->{'ignore-tables'}->{$tbl} eq $db) ) {
1852+ PTDEBUG && _d('Table', $tbl, 'is in --ignore-tables list');
1853+ return 0;
1854+ }
1855+
1856+ if ( $filter->{'ignore-tables-regex'}
1857+ && $tbl =~ $filter->{'ignore-tables-regex'} ) {
1858+ PTDEBUG && _d('Table', $tbl, 'matches --ignore-tables-regex');
1859+ return 0;
1860+ }
1861+
1862+ if ( $filter->{'tables'}
1863+ && !$filter->{'tables'}->{$tbl} ) {
1864+ PTDEBUG && _d('Table', $tbl, 'is not in --tables list, ignoring');
1865+ return 0;
1866+ }
1867+
1868+ if ( $filter->{'tables-regex'}
1869+ && $tbl !~ $filter->{'tables-regex'} ) {
1870+ PTDEBUG && _d('Table', $tbl, 'does not match --tables-regex, ignoring');
1871+ return 0;
1872+ }
1873+
1874+ if ( $filter->{'tables'}
1875+ && $filter->{'tables'}->{$tbl}
1876+ && $filter->{'tables'}->{$tbl} ne '*'
1877+ && $filter->{'tables'}->{$tbl} ne $db ) {
1878+ PTDEBUG && _d('Table', $tbl, 'is only allowed in database',
1879+ $filter->{'tables'}->{$tbl});
1880+ return 0;
1881+ }
1882+
1883+ return 1;
1884+}
1885+
1886+sub engine_is_allowed {
1887+ my ( $self, $engine ) = @_;
1888+
1889+ if ( !$engine ) {
1890+ PTDEBUG && _d('No engine specified; allowing the table');
1891+ return 1;
1892+ }
1893+
1894+ $engine = lc $engine;
1895+
1896+ my $filter = $self->{filters};
1897+
1898+ if ( $filter->{'ignore-engines'}->{$engine} ) {
1899+ PTDEBUG && _d('Engine', $engine, 'is in --ignore-databases list');
1900+ return 0;
1901+ }
1902+
1903+ if ( $filter->{'engines'}
1904+ && !$filter->{'engines'}->{$engine} ) {
1905+ PTDEBUG && _d('Engine', $engine, 'is not in --engines list, ignoring');
1906+ return 0;
1907+ }
1908+
1909+ return 1;
1910+}
1911+
1912+sub _resume_from_database {
1913+ my ($self, $db) = @_;
1914+
1915+ return 1 unless $self->{resume}->{db};
1916+
1917+ if ( $db eq $self->{resume}->{db} ) {
1918+ PTDEBUG && _d('At resume db', $db);
1919+ delete $self->{resume}->{db};
1920+ return 1;
1921+ }
1922+
1923+ return 0;
1924+}
1925+
1926+sub _resume_from_table {
1927+ my ($self, $tbl) = @_;
1928+
1929+ return 1 unless $self->{resume}->{tbl};
1930+
1931+ if ( $tbl eq $self->{resume}->{tbl} ) {
1932+ if ( !$self->{resume}->{after} ) {
1933+ PTDEBUG && _d('Resuming from table', $tbl);
1934+ delete $self->{resume}->{tbl};
1935+ return 1;
1936+ }
1937+ else {
1938+ PTDEBUG && _d('Resuming after table', $tbl);
1939+ delete $self->{resume}->{tbl};
1940+ }
1941+ }
1942+
1943+ return 0;
1944 }
1945
1946 sub _d {
1947@@ -7017,7 +7182,7 @@
1948 }
1949
1950 1;
1951-
1952+}
1953 # ###########################################################################
1954 # End SchemaIterator package
1955 # ###########################################################################
1956@@ -7304,48 +7469,42 @@
1957
1958 sub retry {
1959 my ( $self, %args ) = @_;
1960- my @required_args = qw(try wait);
1961+ my @required_args = qw(try fail final_fail);
1962 foreach my $arg ( @required_args ) {
1963 die "I need a $arg argument" unless $args{$arg};
1964 };
1965- my ($try, $wait) = @args{@required_args};
1966+ my ($try, $fail, $final_fail) = @args{@required_args};
1967+ my $wait = $args{wait} || sub { sleep 1; };
1968 my $tries = $args{tries} || 3;
1969
1970+ my $last_error;
1971 my $tryno = 0;
1972+ TRY:
1973 while ( ++$tryno <= $tries ) {
1974- PTDEBUG && _d("Retry", $tryno, "of", $tries);
1975+ PTDEBUG && _d("Try", $tryno, "of", $tries);
1976 my $result;
1977 eval {
1978 $result = $try->(tryno=>$tryno);
1979 };
1980+ if ( $EVAL_ERROR ) {
1981+ PTDEBUG && _d("Try code failed:", $EVAL_ERROR);
1982+ $last_error = $EVAL_ERROR;
1983
1984- if ( defined $result ) {
1985+ if ( $tryno < $tries ) { # more retries
1986+ my $retry = $fail->(tryno=>$tryno, error=>$last_error);
1987+ last TRY unless $retry;
1988+ PTDEBUG && _d("Calling wait code");
1989+ $wait->(tryno=>$tryno);
1990+ }
1991+ }
1992+ else {
1993 PTDEBUG && _d("Try code succeeded");
1994- if ( my $on_success = $args{on_success} ) {
1995- PTDEBUG && _d("Calling on_success code");
1996- $on_success->(tryno=>$tryno, result=>$result);
1997- }
1998 return $result;
1999 }
2000-
2001- if ( $EVAL_ERROR ) {
2002- PTDEBUG && _d("Try code died:", $EVAL_ERROR);
2003- die $EVAL_ERROR unless $args{retry_on_die};
2004- }
2005-
2006- if ( $tryno < $tries ) {
2007- PTDEBUG && _d("Try code failed, calling wait code");
2008- $wait->(tryno=>$tryno);
2009- }
2010- }
2011-
2012- PTDEBUG && _d("Try code did not succeed");
2013- if ( my $on_failure = $args{on_failure} ) {
2014- PTDEBUG && _d("Calling on_failure code");
2015- $on_failure->();
2016- }
2017-
2018- return;
2019+ }
2020+
2021+ PTDEBUG && _d('Try code did not succeed');
2022+ return $final_fail->(error=>$last_error);
2023 }
2024
2025 sub _d {
2026@@ -8006,27 +8165,20 @@
2027 tbl => undef, # set later
2028 };
2029
2030- my $si = new SchemaIterator(
2031- Quoter => $args{Quoter},
2032+ my $schema_iter = new SchemaIterator(
2033+ dbh => $src->{dbh},
2034+ OptionParser => $o,
2035+ TableParser => $args{TableParser},
2036+ Quoter => $args{Quoter},
2037 );
2038- $si->set_filter($si->make_filter($o));
2039
2040 # Make a list of all dbs.tbls on the source. It's more efficient this
2041 # way because it avoids open/closing a dbh for each tbl and dsn, unless
2042 # we pre-opened the dsn. It would also cause confusing verbose output.
2043 my @dbs_tbls;
2044- my $next_db = $si->get_db_itr(dbh => $src->{dbh});
2045- while ( my $db = $next_db->() ) {
2046- PTDEBUG && _d('Getting tables from', $db);
2047- my $next_tbl = $si->get_tbl_itr(
2048- dbh => $src->{dbh},
2049- db => $db,
2050- views => 0,
2051- );
2052- while ( my $tbl = $next_tbl->() ) {
2053- PTDEBUG && _d('Got table', $tbl);
2054- push @dbs_tbls, { db => $db, tbl => $tbl };
2055- }
2056+ while ( my $tbl = $schema_iter->next() ) {
2057+ PTDEBUG && _d('Got table', $tbl->{db}, $tbl->{tbl});
2058+ push @dbs_tbls, $tbl;
2059 }
2060
2061 my $exit_status = 0;
2062@@ -8048,6 +8200,7 @@
2063 lock_server(src => $src, dst => $dst, %args);
2064
2065 foreach my $db_tbl ( @dbs_tbls ) {
2066+ $src->{tbl_struct} = $db_tbl->{tbl_struct};
2067 $src->{db} = $dst->{db} = $db_tbl->{db};
2068 $src->{tbl} = $dst->{tbl} = $db_tbl->{tbl};
2069
2070@@ -8194,8 +8347,9 @@
2071 $start_ts = get_server_time($src->{dbh}) if $o->get('verbose');
2072
2073 # This will either die if there's a problem or return the tbl struct.
2074- my $tbl_struct = ok_to_sync($src, $dst, %args);
2075-
2076+ ok_to_sync($src, $dst, %args);
2077+ my $tbl_struct = $src->{tbl_struct};
2078+
2079 if ( my $diff = $args{diff} ) {
2080 PTDEBUG && _d('Converting checksum diff to WHERE:', Dumper($diff));
2081 $args{where} = diff_where(
2082@@ -8568,35 +8722,30 @@
2083 }
2084 my ($src, $dst, $dp, $q, $vp, $tp, $du, $syncer, $o) = @args{@required_args};
2085
2086- # First things first: check that the src and dst dbs and tbls exist.
2087- # This can fail in cases like h=host,D=bad,t=also_bad (i.e. simple
2088- # user error). It can also fail when syncing all dbs/tbls with sync_all()
2089- # because the dst db/tbl is assumed to be the same as the src but
2090- # this isn't always the case.
2091- my $src_tbl_ddl;
2092- eval {
2093- # FYI: get_create_table() does USE db but doesn't eval it.
2094- $src->{dbh}->do("USE `$src->{db}`");
2095- $src_tbl_ddl = $du->get_create_table($src->{dbh}, $q,
2096- $src->{db}, $src->{tbl});
2097- };
2098- die $EVAL_ERROR if $EVAL_ERROR;
2099-
2100- my $dst_tbl_ddl;
2101- eval {
2102- # FYI: get_create_table() does USE db but doesn't eval it.
2103- $dst->{dbh}->do("USE `$dst->{db}`");
2104- $dst_tbl_ddl = $du->get_create_table($dst->{dbh}, $q,
2105- $dst->{db}, $dst->{tbl});
2106- };
2107- die $EVAL_ERROR if $EVAL_ERROR;
2108-
2109- # This doesn't work at the moment when syncing different table names.
2110- # Check that src.db.tbl has the exact same schema as dst.db.tbl.
2111- # if ( $o->get('check-schema') && ($src_tbl_ddl ne $dst_tbl_ddl) ) {
2112- # die "Source and destination tables have different schemas";
2113- # }
2114- my $tbl_struct = $tp->parse($src_tbl_ddl);
2115+ if ( !$src->{tbl_struct} ) {
2116+ eval {
2117+ $src->{ddl} = $tp->get_create_table(
2118+ $src->{dbh}, $src->{db}, $src->{tbl});
2119+ $src->{tbl_struct} = $tp->parse($src->{ddl});
2120+
2121+ };
2122+ if ( $EVAL_ERROR ) {
2123+ die "Error getting table structure for $src->{db}.$src->{tbl} on "
2124+ . $dp->as_string($src->{dsn}) . "$EVAL_ERROR\nEnsure that "
2125+ . "the table exists and is accessible.\n";
2126+ }
2127+ }
2128+
2129+ # Check that the dst has the table.
2130+ my $dst_has_table = $tp->check_table(
2131+ dbh => $dst->{dbh},
2132+ db => $dst->{db},
2133+ tbl => $dst->{tbl},
2134+ );
2135+ if ( !$dst_has_table ) {
2136+ die "Table $dst->{db}.$dst->{tbl} does not exist on "
2137+ . $dp->as_string($dst->{dsn}) . "\n";
2138+ }
2139
2140 # Check that the user has all the necessary privs on the tbls.
2141 if ( $o->get('check-privileges') ) {
2142@@ -8629,7 +8778,7 @@
2143 }
2144 }
2145
2146- return $tbl_struct;
2147+ return;
2148 }
2149
2150 # Sub: filter_diffs
2151
2152=== modified file 'lib/NibbleIterator.pm'
2153--- lib/NibbleIterator.pm 2012-02-06 20:29:08 +0000
2154+++ lib/NibbleIterator.pm 2012-03-31 16:06:20 +0000
2155@@ -511,22 +511,15 @@
2156
2157 sub get_row_estimate {
2158 my (%args) = @_;
2159- my @required_args = qw(Cxn tbl OptionParser TableParser Quoter);
2160- my ($cxn, $tbl, $o, $tp, $q) = @args{@required_args};
2161+ my @required_args = qw(Cxn tbl);
2162+ my ($cxn, $tbl) = @args{@required_args};
2163
2164- if ( $args{where} ) {
2165- PTDEBUG && _d('WHERE clause, using explain plan for row estimate');
2166- my $table = $q->quote(@{$tbl}{qw(db tbl)});
2167- my $sql = "EXPLAIN SELECT * FROM $table WHERE $args{where}";
2168- PTDEBUG && _d($sql);
2169- my $expl = $cxn->dbh()->selectrow_hashref($sql);
2170- PTDEBUG && _d(Dumper($expl));
2171- return ($expl->{rows} || 0), $expl->{key};
2172- }
2173- else {
2174- PTDEBUG && _d('No WHERE clause, using table status for row estimate');
2175- return $tbl->{tbl_status}->{rows} || 0;
2176- }
2177+ my $sql = "EXPLAIN SELECT * FROM $tbl->{name} "
2178+ . "WHERE " . ($args{where} || '1=1');
2179+ PTDEBUG && _d($sql);
2180+ my $expl = $cxn->dbh()->selectrow_hashref($sql);
2181+ PTDEBUG && _d(Dumper($expl));
2182+ return ($expl->{rows} || 0), $expl->{key};
2183 }
2184
2185 sub _prepare_sths {
2186
2187=== modified file 'lib/SchemaIterator.pm'
2188--- lib/SchemaIterator.pm 2012-01-19 19:46:56 +0000
2189+++ lib/SchemaIterator.pm 2012-03-31 16:06:20 +0000
2190@@ -55,12 +55,10 @@
2191 # OptionParser - <OptionParser> object. All filters are gotten from this
2192 # obj: --databases, --tables, etc.
2193 # Quoter - <Quoter> object.
2194+# TableParser - <TableParser> object get tbl_struct.
2195 #
2196 # Optional Arguments:
2197 # Schema - <Schema> object to initialize while iterating.
2198-# TableParser - <TableParser> object get tbl_struct.
2199-# keep_ddl - Keep SHOW CREATE TABLE (default false).
2200-# keep_tbl_status - Keep SHOW TABLE STATUS (default false).
2201 # resume - Skip tables so first call to <next()> returns
2202 # this "db.table".
2203 #
2204@@ -68,7 +66,7 @@
2205 # SchemaIterator object
2206 sub new {
2207 my ( $class, %args ) = @_;
2208- my @required_args = qw(OptionParser Quoter);
2209+ my @required_args = qw(OptionParser TableParser Quoter);
2210 foreach my $arg ( @required_args ) {
2211 die "I need a $arg argument" unless $args{$arg};
2212 }
2213@@ -222,25 +220,18 @@
2214 }
2215
2216 if ( $schema_obj ) {
2217- if ( $schema_obj->{ddl} && $self->{TableParser} ) {
2218- $schema_obj->{tbl_struct}
2219- = $self->{TableParser}->parse($schema_obj->{ddl});
2220- }
2221-
2222- delete $schema_obj->{ddl} unless $self->{keep_ddl};
2223- delete $schema_obj->{tbl_status} unless $self->{keep_tbl_status};
2224-
2225 if ( my $schema = $self->{Schema} ) {
2226 $schema->add_schema_object($schema_obj);
2227 }
2228- PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
2229+ PTDEBUG && _d('Next schema object:',
2230+ $schema_obj->{db}, $schema_obj->{tbl});
2231 }
2232
2233 return $schema_obj;
2234 }
2235
2236 sub _iterate_files {
2237- my ( $self ) = @_;
2238+ my ( $self ) = @_;
2239
2240 if ( !$self->{fh} ) {
2241 my ($fh, $file) = $self->{file_itr}->();
2242@@ -295,14 +286,14 @@
2243 next CHUNK;
2244 }
2245 $ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
2246-
2247- my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
2248-
2249- if ( !$engine || $self->engine_is_allowed($engine) ) {
2250+ my $tbl_struct = $self->{TableParser}->parse($ddl);
2251+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
2252 return {
2253- db => $self->{db},
2254- tbl => $tbl,
2255- ddl => $ddl,
2256+ db => $self->{db},
2257+ tbl => $tbl,
2258+ name => $self->{Quoter}->quote($self->{db}, $tbl),
2259+ ddl => $ddl,
2260+ tbl_struct => $tbl_struct,
2261 };
2262 }
2263 }
2264@@ -321,6 +312,7 @@
2265 sub _iterate_dbh {
2266 my ( $self ) = @_;
2267 my $q = $self->{Quoter};
2268+ my $tp = $self->{TableParser};
2269 my $dbh = $self->{dbh};
2270 PTDEBUG && _d('Getting next schema object from dbh', $dbh);
2271
2272@@ -360,33 +352,15 @@
2273 }
2274
2275 while ( my $tbl = shift @{$self->{tbls}} ) {
2276- # If there are engine filters, we have to get the table status.
2277- # Else, get it if the user wants to keep it since they'll expect
2278- # it to be available.
2279- my $tbl_status;
2280- if ( $self->{filters}->{'engines'}
2281- || $self->{filters}->{'ignore-engines'}
2282- || $self->{keep_tbl_status} )
2283- {
2284- my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
2285- . " LIKE \'$tbl\'";
2286- PTDEBUG && _d($sql);
2287- $tbl_status = $dbh->selectrow_hashref($sql);
2288- PTDEBUG && _d(Dumper($tbl_status));
2289- }
2290-
2291- if ( !$tbl_status
2292- || $self->engine_is_allowed($tbl_status->{engine}) ) {
2293- my $ddl;
2294- if ( my $tp = $self->{TableParser} ) {
2295- $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
2296- }
2297-
2298+ my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
2299+ my $tbl_struct = $tp->parse($ddl);
2300+ if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
2301 return {
2302 db => $self->{db},
2303 tbl => $tbl,
2304+ name => $q->quote($self->{db}, $tbl),
2305 ddl => $ddl,
2306- tbl_status => $tbl_status,
2307+ tbl_struct => $tbl_struct,
2308 };
2309 }
2310 }
2311@@ -502,7 +476,15 @@
2312
2313 sub engine_is_allowed {
2314 my ( $self, $engine ) = @_;
2315- die "I need an engine argument" unless $engine;
2316+
2317+ if ( !$engine ) {
2318+ # This normally doesn't happen, but it can if the user
2319+ # is iterating a file of their own table dumps, i.e. that
2320+ # weren't created by mysqldump, so there's no ENGINE=
2321+ # on the CREATE TABLE.
2322+ PTDEBUG && _d('No engine specified; allowing the table');
2323+ return 1;
2324+ }
2325
2326 $engine = lc $engine;
2327
2328
2329=== modified file 't/lib/SchemaIterator.t'
2330--- t/lib/SchemaIterator.t 2012-03-07 16:35:50 +0000
2331+++ t/lib/SchemaIterator.t 2012-03-31 16:06:20 +0000
2332@@ -9,7 +9,7 @@
2333 use strict;
2334 use warnings FATAL => 'all';
2335 use English qw(-no_match_vars);
2336-use Test::More tests => 31;
2337+use Test::More tests => 29;
2338
2339 use SchemaIterator;
2340 use FileIterator;
2341@@ -32,7 +32,7 @@
2342 my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
2343 my $dbh = $sb->get_dbh_for('master');
2344
2345-my $tp;
2346+my $tp = new TableParser(Quoter => $q);
2347 my $fi = new FileIterator();
2348 my $o = new OptionParser(description => 'SchemaIterator');
2349 $o->get_specs("$trunk/bin/pt-table-checksum");
2350@@ -55,7 +55,6 @@
2351 my $file_itr = $fi->get_file_itr(@{$args{files}});
2352 $si = new SchemaIterator(
2353 file_itr => $file_itr,
2354- keep_ddl => defined $args{keep_ddl} ? $args{keep_ddl} : 1,
2355 resume => $args{resume},
2356 OptionParser => $o,
2357 Quoter => $q,
2358@@ -65,7 +64,6 @@
2359 else {
2360 $si = new SchemaIterator(
2361 dbh => $dbh,
2362- keep_ddl => defined $args{keep_ddl} ? $args{keep_ddl} : 1,
2363 resume => $args{resume},
2364 OptionParser => $o,
2365 Quoter => $q,
2366@@ -311,7 +309,6 @@
2367 # ########################################################################
2368 # Getting CREATE TALBE (ddl).
2369 # ########################################################################
2370- $tp = new TableParser(Quoter => $q);
2371 test_so(
2372 filters => [qw(-t mysql.user)],
2373 result => $sandbox_version ge '5.1' ? "$out/mysql-user-ddl.txt"
2374@@ -319,9 +316,6 @@
2375 test_name => "Get CREATE TABLE with dbh",
2376 );
2377
2378- # Kill the TableParser obj in case the next tests don't want to use it.
2379- $tp = undef;
2380-
2381 $sb->wipe_clean($dbh);
2382 };
2383
2384@@ -370,49 +364,10 @@
2385
2386 is(
2387 $n_tbl_structs,
2388- 0,
2389- 'No tbl_struct without TableParser'
2390-);
2391-
2392-$tp = new TableParser(Quoter => $q);
2393-
2394-$objs = test_so(
2395- files => ["$in/dump001.txt"],
2396- result => "", # hack to let return_objs work
2397- test_name => "", # hack to let return_objs work
2398- return_objs => 1,
2399-);
2400-
2401-$n_tbl_structs = grep { exists $_->{tbl_struct} } @$objs;
2402-
2403-is(
2404- $n_tbl_structs,
2405 scalar @$objs,
2406 'Got tbl_struct for each schema object'
2407 );
2408
2409-# Kill the TableParser obj in case the next tests don't want to use it.
2410-$tp = undef;
2411-
2412-# ############################################################################
2413-# keep_ddl
2414-# ############################################################################
2415-$objs = test_so(
2416- files => ["$in/dump001.txt"],
2417- result => "", # hack to let return_objs work
2418- test_name => "", # hack to let return_objs work
2419- return_objs => 1,
2420- keep_ddl => 0,
2421-);
2422-
2423-my $n_ddls = grep { exists $_->{ddl} } @$objs;
2424-
2425-is(
2426- $n_ddls,
2427- 0,
2428- 'DDL deleted unless keep_ddl'
2429-);
2430-
2431 # ############################################################################
2432 # Resume
2433 # ############################################################################
2434
2435=== modified file 't/lib/samples/SchemaIterator/all-dbs-tbls.txt'
2436--- t/lib/samples/SchemaIterator/all-dbs-tbls.txt 2011-09-23 14:07:03 +0000
2437+++ t/lib/samples/SchemaIterator/all-dbs-tbls.txt 2012-03-31 16:06:20 +0000
2438@@ -1,37 +1,523 @@
2439 mysql.columns_priv
2440+CREATE TABLE `columns_priv` (
2441+ `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
2442+ `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2443+ `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '',
2444+ `Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2445+ `Column_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2446+ `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2447+ `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 NOT NULL DEFAULT '',
2448+ PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`)
2449+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges'
2450+
2451 mysql.db
2452+CREATE TABLE `db` (
2453+ `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
2454+ `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2455+ `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '',
2456+ `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2457+ `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2458+ `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2459+ `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2460+ `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2461+ `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2462+ `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2463+ `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2464+ `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2465+ `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2466+ `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2467+ `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2468+ `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2469+ `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2470+ `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2471+ `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2472+ `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2473+ `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2474+ `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2475+ PRIMARY KEY (`Host`,`Db`,`User`),
2476+ KEY `User` (`User`)
2477+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges'
2478+
2479 mysql.event
2480+CREATE TABLE `event` (
2481+ `db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
2482+ `name` char(64) NOT NULL DEFAULT '',
2483+ `body` longblob NOT NULL,
2484+ `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
2485+ `execute_at` datetime DEFAULT NULL,
2486+ `interval_value` int(11) DEFAULT NULL,
2487+ `interval_field` enum('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND') DEFAULT NULL,
2488+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2489+ `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
2490+ `last_executed` datetime DEFAULT NULL,
2491+ `starts` datetime DEFAULT NULL,
2492+ `ends` datetime DEFAULT NULL,
2493+ `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED',
2494+ `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP',
2495+ `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH') NOT NULL DEFAULT '',
2496+ `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
2497+ `originator` int(10) unsigned NOT NULL,
2498+ `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM',
2499+ `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2500+ `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2501+ `db_collation` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2502+ `body_utf8` longblob,
2503+ PRIMARY KEY (`db`,`name`)
2504+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events'
2505+
2506 mysql.func
2507+CREATE TABLE `func` (
2508+ `name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2509+ `ret` tinyint(1) NOT NULL DEFAULT '0',
2510+ `dl` char(128) COLLATE utf8_bin NOT NULL DEFAULT '',
2511+ `type` enum('function','aggregate') CHARACTER SET utf8 NOT NULL,
2512+ PRIMARY KEY (`name`)
2513+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='User defined functions'
2514+
2515 mysql.help_category
2516+CREATE TABLE `help_category` (
2517+ `help_category_id` smallint(5) unsigned NOT NULL,
2518+ `name` char(64) NOT NULL,
2519+ `parent_category_id` smallint(5) unsigned DEFAULT NULL,
2520+ `url` char(128) NOT NULL,
2521+ PRIMARY KEY (`help_category_id`),
2522+ UNIQUE KEY `name` (`name`)
2523+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='help categories'
2524+
2525 mysql.help_keyword
2526+CREATE TABLE `help_keyword` (
2527+ `help_keyword_id` int(10) unsigned NOT NULL,
2528+ `name` char(64) NOT NULL,
2529+ PRIMARY KEY (`help_keyword_id`),
2530+ UNIQUE KEY `name` (`name`)
2531+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='help keywords'
2532+
2533 mysql.help_relation
2534+CREATE TABLE `help_relation` (
2535+ `help_topic_id` int(10) unsigned NOT NULL,
2536+ `help_keyword_id` int(10) unsigned NOT NULL,
2537+ PRIMARY KEY (`help_keyword_id`,`help_topic_id`)
2538+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='keyword-topic relation'
2539+
2540 mysql.help_topic
2541+CREATE TABLE `help_topic` (
2542+ `help_topic_id` int(10) unsigned NOT NULL,
2543+ `name` char(64) NOT NULL,
2544+ `help_category_id` smallint(5) unsigned NOT NULL,
2545+ `description` text NOT NULL,
2546+ `example` text NOT NULL,
2547+ `url` char(128) NOT NULL,
2548+ PRIMARY KEY (`help_topic_id`),
2549+ UNIQUE KEY `name` (`name`)
2550+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='help topics'
2551+
2552 mysql.host
2553+CREATE TABLE `host` (
2554+ `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
2555+ `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2556+ `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2557+ `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2558+ `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2559+ `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2560+ `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2561+ `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2562+ `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2563+ `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2564+ `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2565+ `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2566+ `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2567+ `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2568+ `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2569+ `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2570+ `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2571+ `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2572+ `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2573+ `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2574+ PRIMARY KEY (`Host`,`Db`)
2575+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Host privileges; Merged with database privileges'
2576+
2577 mysql.ndb_binlog_index
2578+CREATE TABLE `ndb_binlog_index` (
2579+ `Position` bigint(20) unsigned NOT NULL,
2580+ `File` varchar(255) NOT NULL,
2581+ `epoch` bigint(20) unsigned NOT NULL,
2582+ `inserts` bigint(20) unsigned NOT NULL,
2583+ `updates` bigint(20) unsigned NOT NULL,
2584+ `deletes` bigint(20) unsigned NOT NULL,
2585+ `schemaops` bigint(20) unsigned NOT NULL,
2586+ PRIMARY KEY (`epoch`)
2587+) ENGINE=MyISAM DEFAULT CHARSET=latin1
2588+
2589 mysql.plugin
2590+CREATE TABLE `plugin` (
2591+ `name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2592+ `dl` char(128) COLLATE utf8_bin NOT NULL DEFAULT '',
2593+ PRIMARY KEY (`name`)
2594+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='MySQL plugins'
2595+
2596 mysql.proc
2597+CREATE TABLE `proc` (
2598+ `db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
2599+ `name` char(64) NOT NULL DEFAULT '',
2600+ `type` enum('FUNCTION','PROCEDURE') NOT NULL,
2601+ `specific_name` char(64) NOT NULL DEFAULT '',
2602+ `language` enum('SQL') NOT NULL DEFAULT 'SQL',
2603+ `sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL',
2604+ `is_deterministic` enum('YES','NO') NOT NULL DEFAULT 'NO',
2605+ `security_type` enum('INVOKER','DEFINER') NOT NULL DEFAULT 'DEFINER',
2606+ `param_list` blob NOT NULL,
2607+ `returns` longblob NOT NULL,
2608+ `body` longblob NOT NULL,
2609+ `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
2610+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2611+ `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
2612+ `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH') NOT NULL DEFAULT '',
2613+ `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
2614+ `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2615+ `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2616+ `db_collation` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2617+ `body_utf8` longblob,
2618+ PRIMARY KEY (`db`,`name`,`type`)
2619+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stored Procedures'
2620+
2621 mysql.procs_priv
2622+CREATE TABLE `procs_priv` (
2623+ `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
2624+ `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2625+ `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '',
2626+ `Routine_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2627+ `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL,
2628+ `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '',
2629+ `Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '',
2630+ `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2631+ PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`,`Routine_type`),
2632+ KEY `Grantor` (`Grantor`)
2633+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges'
2634+
2635 mysql.servers
2636+CREATE TABLE `servers` (
2637+ `Server_name` char(64) NOT NULL DEFAULT '',
2638+ `Host` char(64) NOT NULL DEFAULT '',
2639+ `Db` char(64) NOT NULL DEFAULT '',
2640+ `Username` char(64) NOT NULL DEFAULT '',
2641+ `Password` char(64) NOT NULL DEFAULT '',
2642+ `Port` int(4) NOT NULL DEFAULT '0',
2643+ `Socket` char(64) NOT NULL DEFAULT '',
2644+ `Wrapper` char(64) NOT NULL DEFAULT '',
2645+ `Owner` char(64) NOT NULL DEFAULT '',
2646+ PRIMARY KEY (`Server_name`)
2647+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='MySQL Foreign Servers table'
2648+
2649 mysql.tables_priv
2650+CREATE TABLE `tables_priv` (
2651+ `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
2652+ `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2653+ `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '',
2654+ `Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
2655+ `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '',
2656+ `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2657+ `Table_priv` set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') CHARACTER SET utf8 NOT NULL DEFAULT '',
2658+ `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 NOT NULL DEFAULT '',
2659+ PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`),
2660+ KEY `Grantor` (`Grantor`)
2661+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Table privileges'
2662+
2663 mysql.time_zone
2664+CREATE TABLE `time_zone` (
2665+ `Time_zone_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
2666+ `Use_leap_seconds` enum('Y','N') NOT NULL DEFAULT 'N',
2667+ PRIMARY KEY (`Time_zone_id`)
2668+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zones'
2669+
2670 mysql.time_zone_leap_second
2671+CREATE TABLE `time_zone_leap_second` (
2672+ `Transition_time` bigint(20) NOT NULL,
2673+ `Correction` int(11) NOT NULL,
2674+ PRIMARY KEY (`Transition_time`)
2675+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Leap seconds information for time zones'
2676+
2677 mysql.time_zone_name
2678+CREATE TABLE `time_zone_name` (
2679+ `Name` char(64) NOT NULL,
2680+ `Time_zone_id` int(10) unsigned NOT NULL,
2681+ PRIMARY KEY (`Name`)
2682+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone names'
2683+
2684 mysql.time_zone_transition
2685+CREATE TABLE `time_zone_transition` (
2686+ `Time_zone_id` int(10) unsigned NOT NULL,
2687+ `Transition_time` bigint(20) NOT NULL,
2688+ `Transition_type_id` int(10) unsigned NOT NULL,
2689+ PRIMARY KEY (`Time_zone_id`,`Transition_time`)
2690+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone transitions'
2691+
2692 mysql.time_zone_transition_type
2693+CREATE TABLE `time_zone_transition_type` (
2694+ `Time_zone_id` int(10) unsigned NOT NULL,
2695+ `Transition_type_id` int(10) unsigned NOT NULL,
2696+ `Offset` int(11) NOT NULL DEFAULT '0',
2697+ `Is_DST` tinyint(3) unsigned NOT NULL DEFAULT '0',
2698+ `Abbreviation` char(8) NOT NULL DEFAULT '',
2699+ PRIMARY KEY (`Time_zone_id`,`Transition_type_id`)
2700+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone transition types'
2701+
2702 mysql.user
2703+CREATE TABLE `user` (
2704+ `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
2705+ `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '',
2706+ `Password` char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
2707+ `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2708+ `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2709+ `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2710+ `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2711+ `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2712+ `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2713+ `Reload_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2714+ `Shutdown_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2715+ `Process_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2716+ `File_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2717+ `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2718+ `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2719+ `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2720+ `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2721+ `Show_db_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2722+ `Super_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2723+ `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2724+ `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2725+ `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2726+ `Repl_slave_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2727+ `Repl_client_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2728+ `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2729+ `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2730+ `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2731+ `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2732+ `Create_user_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2733+ `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2734+ `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
2735+ `ssl_type` enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '',
2736+ `ssl_cipher` blob NOT NULL,
2737+ `x509_issuer` blob NOT NULL,
2738+ `x509_subject` blob NOT NULL,
2739+ `max_questions` int(11) unsigned NOT NULL DEFAULT '0',
2740+ `max_updates` int(11) unsigned NOT NULL DEFAULT '0',
2741+ `max_connections` int(11) unsigned NOT NULL DEFAULT '0',
2742+ `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0',
2743+ PRIMARY KEY (`Host`,`User`)
2744+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
2745+
2746 sakila.actor
2747+CREATE TABLE `actor` (
2748+ `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2749+ `first_name` varchar(45) NOT NULL,
2750+ `last_name` varchar(45) NOT NULL,
2751+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2752+ PRIMARY KEY (`actor_id`),
2753+ KEY `idx_actor_last_name` (`last_name`)
2754+) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8
2755+
2756 sakila.address
2757+CREATE TABLE `address` (
2758+ `address_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2759+ `address` varchar(50) NOT NULL,
2760+ `address2` varchar(50) DEFAULT NULL,
2761+ `district` varchar(20) NOT NULL,
2762+ `city_id` smallint(5) unsigned NOT NULL,
2763+ `postal_code` varchar(10) DEFAULT NULL,
2764+ `phone` varchar(20) NOT NULL,
2765+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2766+ PRIMARY KEY (`address_id`),
2767+ KEY `idx_fk_city_id` (`city_id`),
2768+ CONSTRAINT `fk_address_city` FOREIGN KEY (`city_id`) REFERENCES `city` (`city_id`) ON UPDATE CASCADE
2769+) ENGINE=InnoDB AUTO_INCREMENT=606 DEFAULT CHARSET=utf8
2770+
2771 sakila.category
2772+CREATE TABLE `category` (
2773+ `category_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
2774+ `name` varchar(25) NOT NULL,
2775+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2776+ PRIMARY KEY (`category_id`)
2777+) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
2778+
2779 sakila.city
2780+CREATE TABLE `city` (
2781+ `city_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2782+ `city` varchar(50) NOT NULL,
2783+ `country_id` smallint(5) unsigned NOT NULL,
2784+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2785+ PRIMARY KEY (`city_id`),
2786+ KEY `idx_fk_country_id` (`country_id`),
2787+ CONSTRAINT `fk_city_country` FOREIGN KEY (`country_id`) REFERENCES `country` (`country_id`) ON UPDATE CASCADE
2788+) ENGINE=InnoDB AUTO_INCREMENT=601 DEFAULT CHARSET=utf8
2789+
2790 sakila.country
2791+CREATE TABLE `country` (
2792+ `country_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2793+ `country` varchar(50) NOT NULL,
2794+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2795+ PRIMARY KEY (`country_id`)
2796+) ENGINE=InnoDB AUTO_INCREMENT=110 DEFAULT CHARSET=utf8
2797+
2798 sakila.customer
2799+CREATE TABLE `customer` (
2800+ `customer_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2801+ `store_id` tinyint(3) unsigned NOT NULL,
2802+ `first_name` varchar(45) NOT NULL,
2803+ `last_name` varchar(45) NOT NULL,
2804+ `email` varchar(50) DEFAULT NULL,
2805+ `address_id` smallint(5) unsigned NOT NULL,
2806+ `active` tinyint(1) NOT NULL DEFAULT '1',
2807+ `create_date` datetime NOT NULL,
2808+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2809+ PRIMARY KEY (`customer_id`),
2810+ KEY `idx_fk_store_id` (`store_id`),
2811+ KEY `idx_fk_address_id` (`address_id`),
2812+ KEY `idx_last_name` (`last_name`),
2813+ CONSTRAINT `fk_customer_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE,
2814+ CONSTRAINT `fk_customer_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE
2815+) ENGINE=InnoDB AUTO_INCREMENT=600 DEFAULT CHARSET=utf8
2816+
2817 sakila.film
2818+CREATE TABLE `film` (
2819+ `film_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2820+ `title` varchar(255) NOT NULL,
2821+ `description` text,
2822+ `release_year` year(4) DEFAULT NULL,
2823+ `language_id` tinyint(3) unsigned NOT NULL,
2824+ `original_language_id` tinyint(3) unsigned DEFAULT NULL,
2825+ `rental_duration` tinyint(3) unsigned NOT NULL DEFAULT '3',
2826+ `rental_rate` decimal(4,2) NOT NULL DEFAULT '4.99',
2827+ `length` smallint(5) unsigned DEFAULT NULL,
2828+ `replacement_cost` decimal(5,2) NOT NULL DEFAULT '19.99',
2829+ `rating` enum('G','PG','PG-13','R','NC-17') DEFAULT 'G',
2830+ `special_features` set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL,
2831+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2832+ PRIMARY KEY (`film_id`),
2833+ KEY `idx_title` (`title`),
2834+ KEY `idx_fk_language_id` (`language_id`),
2835+ KEY `idx_fk_original_language_id` (`original_language_id`),
2836+ CONSTRAINT `fk_film_language` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON UPDATE CASCADE,
2837+ CONSTRAINT `fk_film_language_original` FOREIGN KEY (`original_language_id`) REFERENCES `language` (`language_id`) ON UPDATE CASCADE
2838+) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8
2839+
2840 sakila.film_actor
2841+CREATE TABLE `film_actor` (
2842+ `actor_id` smallint(5) unsigned NOT NULL,
2843+ `film_id` smallint(5) unsigned NOT NULL,
2844+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2845+ PRIMARY KEY (`actor_id`,`film_id`),
2846+ KEY `idx_fk_film_id` (`film_id`),
2847+ CONSTRAINT `fk_film_actor_actor` FOREIGN KEY (`actor_id`) REFERENCES `actor` (`actor_id`) ON UPDATE CASCADE,
2848+ CONSTRAINT `fk_film_actor_film` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON UPDATE CASCADE
2849+) ENGINE=InnoDB DEFAULT CHARSET=utf8
2850+
2851 sakila.film_category
2852+CREATE TABLE `film_category` (
2853+ `film_id` smallint(5) unsigned NOT NULL,
2854+ `category_id` tinyint(3) unsigned NOT NULL,
2855+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2856+ PRIMARY KEY (`film_id`,`category_id`),
2857+ KEY `fk_film_category_category` (`category_id`),
2858+ CONSTRAINT `fk_film_category_film` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON UPDATE CASCADE,
2859+ CONSTRAINT `fk_film_category_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`) ON UPDATE CASCADE
2860+) ENGINE=InnoDB DEFAULT CHARSET=utf8
2861+
2862 sakila.film_text
2863+CREATE TABLE `film_text` (
2864+ `film_id` smallint(6) NOT NULL,
2865+ `title` varchar(255) NOT NULL,
2866+ `description` text,
2867+ PRIMARY KEY (`film_id`),
2868+ FULLTEXT KEY `idx_title_description` (`title`,`description`)
2869+) ENGINE=MyISAM DEFAULT CHARSET=utf8
2870+
2871 sakila.inventory
2872+CREATE TABLE `inventory` (
2873+ `inventory_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
2874+ `film_id` smallint(5) unsigned NOT NULL,
2875+ `store_id` tinyint(3) unsigned NOT NULL,
2876+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2877+ PRIMARY KEY (`inventory_id`),
2878+ KEY `idx_fk_film_id` (`film_id`),
2879+ KEY `idx_store_id_film_id` (`store_id`,`film_id`),
2880+ CONSTRAINT `fk_inventory_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE,
2881+ CONSTRAINT `fk_inventory_film` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON UPDATE CASCADE
2882+) ENGINE=InnoDB AUTO_INCREMENT=4582 DEFAULT CHARSET=utf8
2883+
2884 sakila.language
2885+CREATE TABLE `language` (
2886+ `language_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
2887+ `name` char(20) NOT NULL,
2888+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2889+ PRIMARY KEY (`language_id`)
2890+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
2891+
2892 sakila.payment
2893+CREATE TABLE `payment` (
2894+ `payment_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
2895+ `customer_id` smallint(5) unsigned NOT NULL,
2896+ `staff_id` tinyint(3) unsigned NOT NULL,
2897+ `rental_id` int(11) DEFAULT NULL,
2898+ `amount` decimal(5,2) NOT NULL,
2899+ `payment_date` datetime NOT NULL,
2900+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2901+ PRIMARY KEY (`payment_id`),
2902+ KEY `idx_fk_staff_id` (`staff_id`),
2903+ KEY `idx_fk_customer_id` (`customer_id`),
2904+ KEY `fk_payment_rental` (`rental_id`),
2905+ CONSTRAINT `fk_payment_rental` FOREIGN KEY (`rental_id`) REFERENCES `rental` (`rental_id`) ON DELETE SET NULL ON UPDATE CASCADE,
2906+ CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE,
2907+ CONSTRAINT `fk_payment_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE
2908+) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
2909+
2910 sakila.rental
2911+CREATE TABLE `rental` (
2912+ `rental_id` int(11) NOT NULL AUTO_INCREMENT,
2913+ `rental_date` datetime NOT NULL,
2914+ `inventory_id` mediumint(8) unsigned NOT NULL,
2915+ `customer_id` smallint(5) unsigned NOT NULL,
2916+ `return_date` datetime DEFAULT NULL,
2917+ `staff_id` tinyint(3) unsigned NOT NULL,
2918+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2919+ PRIMARY KEY (`rental_id`),
2920+ UNIQUE KEY `rental_date` (`rental_date`,`inventory_id`,`customer_id`),
2921+ KEY `idx_fk_inventory_id` (`inventory_id`),
2922+ KEY `idx_fk_customer_id` (`customer_id`),
2923+ KEY `idx_fk_staff_id` (`staff_id`),
2924+ CONSTRAINT `fk_rental_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE,
2925+ CONSTRAINT `fk_rental_inventory` FOREIGN KEY (`inventory_id`) REFERENCES `inventory` (`inventory_id`) ON UPDATE CASCADE,
2926+ CONSTRAINT `fk_rental_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE
2927+) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
2928+
2929 sakila.staff
2930+CREATE TABLE `staff` (
2931+ `staff_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
2932+ `first_name` varchar(45) NOT NULL,
2933+ `last_name` varchar(45) NOT NULL,
2934+ `address_id` smallint(5) unsigned NOT NULL,
2935+ `picture` blob,
2936+ `email` varchar(50) DEFAULT NULL,
2937+ `store_id` tinyint(3) unsigned NOT NULL,
2938+ `active` tinyint(1) NOT NULL DEFAULT '1',
2939+ `username` varchar(16) NOT NULL,
2940+ `password` varchar(40) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2941+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2942+ PRIMARY KEY (`staff_id`),
2943+ KEY `idx_fk_store_id` (`store_id`),
2944+ KEY `idx_fk_address_id` (`address_id`),
2945+ CONSTRAINT `fk_staff_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE,
2946+ CONSTRAINT `fk_staff_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE
2947+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
2948+
2949 sakila.store
2950+CREATE TABLE `store` (
2951+ `store_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
2952+ `manager_staff_id` tinyint(3) unsigned NOT NULL,
2953+ `address_id` smallint(5) unsigned NOT NULL,
2954+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2955+ PRIMARY KEY (`store_id`),
2956+ UNIQUE KEY `idx_unique_manager` (`manager_staff_id`),
2957+ KEY `idx_fk_address_id` (`address_id`),
2958+ CONSTRAINT `fk_store_staff` FOREIGN KEY (`manager_staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE,
2959+ CONSTRAINT `fk_store_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE
2960+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
2961+
2962
2963=== modified file 't/lib/samples/SchemaIterator/resume-from-ignored-sakila-payment.txt'
2964--- t/lib/samples/SchemaIterator/resume-from-ignored-sakila-payment.txt 2011-11-08 17:20:59 +0000
2965+++ t/lib/samples/SchemaIterator/resume-from-ignored-sakila-payment.txt 2012-03-31 16:06:20 +0000
2966@@ -1,3 +1,52 @@
2967 sakila.rental
2968+CREATE TABLE `rental` (
2969+ `rental_id` int(11) NOT NULL AUTO_INCREMENT,
2970+ `rental_date` datetime NOT NULL,
2971+ `inventory_id` mediumint(8) unsigned NOT NULL,
2972+ `customer_id` smallint(5) unsigned NOT NULL,
2973+ `return_date` datetime DEFAULT NULL,
2974+ `staff_id` tinyint(3) unsigned NOT NULL,
2975+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2976+ PRIMARY KEY (`rental_id`),
2977+ UNIQUE KEY `rental_date` (`rental_date`,`inventory_id`,`customer_id`),
2978+ KEY `idx_fk_inventory_id` (`inventory_id`),
2979+ KEY `idx_fk_customer_id` (`customer_id`),
2980+ KEY `idx_fk_staff_id` (`staff_id`),
2981+ CONSTRAINT `fk_rental_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE,
2982+ CONSTRAINT `fk_rental_inventory` FOREIGN KEY (`inventory_id`) REFERENCES `inventory` (`inventory_id`) ON UPDATE CASCADE,
2983+ CONSTRAINT `fk_rental_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE
2984+) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
2985+
2986 sakila.staff
2987+CREATE TABLE `staff` (
2988+ `staff_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
2989+ `first_name` varchar(45) NOT NULL,
2990+ `last_name` varchar(45) NOT NULL,
2991+ `address_id` smallint(5) unsigned NOT NULL,
2992+ `picture` blob,
2993+ `email` varchar(50) DEFAULT NULL,
2994+ `store_id` tinyint(3) unsigned NOT NULL,
2995+ `active` tinyint(1) NOT NULL DEFAULT '1',
2996+ `username` varchar(16) NOT NULL,
2997+ `password` varchar(40) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
2998+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
2999+ PRIMARY KEY (`staff_id`),
3000+ KEY `idx_fk_store_id` (`store_id`),
3001+ KEY `idx_fk_address_id` (`address_id`),
3002+ CONSTRAINT `fk_staff_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE,
3003+ CONSTRAINT `fk_staff_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE
3004+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
3005+
3006 sakila.store
3007+CREATE TABLE `store` (
3008+ `store_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
3009+ `manager_staff_id` tinyint(3) unsigned NOT NULL,
3010+ `address_id` smallint(5) unsigned NOT NULL,
3011+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
3012+ PRIMARY KEY (`store_id`),
3013+ UNIQUE KEY `idx_unique_manager` (`manager_staff_id`),
3014+ KEY `idx_fk_address_id` (`address_id`),
3015+ CONSTRAINT `fk_store_staff` FOREIGN KEY (`manager_staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE,
3016+ CONSTRAINT `fk_store_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE
3017+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
3018+
3019
3020=== modified file 't/lib/samples/SchemaIterator/resume-from-sakila-payment.txt'
3021--- t/lib/samples/SchemaIterator/resume-from-sakila-payment.txt 2011-09-27 19:06:24 +0000
3022+++ t/lib/samples/SchemaIterator/resume-from-sakila-payment.txt 2012-03-31 16:06:20 +0000
3023@@ -1,4 +1,70 @@
3024 sakila.payment
3025+CREATE TABLE `payment` (
3026+ `payment_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
3027+ `customer_id` smallint(5) unsigned NOT NULL,
3028+ `staff_id` tinyint(3) unsigned NOT NULL,
3029+ `rental_id` int(11) DEFAULT NULL,
3030+ `amount` decimal(5,2) NOT NULL,
3031+ `payment_date` datetime NOT NULL,
3032+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
3033+ PRIMARY KEY (`payment_id`),
3034+ KEY `idx_fk_staff_id` (`staff_id`),
3035+ KEY `idx_fk_customer_id` (`customer_id`),
3036+ KEY `fk_payment_rental` (`rental_id`),
3037+ CONSTRAINT `fk_payment_rental` FOREIGN KEY (`rental_id`) REFERENCES `rental` (`rental_id`) ON DELETE SET NULL ON UPDATE CASCADE,
3038+ CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE,
3039+ CONSTRAINT `fk_payment_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE
3040+) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
3041+
3042 sakila.rental
3043+CREATE TABLE `rental` (
3044+ `rental_id` int(11) NOT NULL AUTO_INCREMENT,
3045+ `rental_date` datetime NOT NULL,
3046+ `inventory_id` mediumint(8) unsigned NOT NULL,
3047+ `customer_id` smallint(5) unsigned NOT NULL,
3048+ `return_date` datetime DEFAULT NULL,
3049+ `staff_id` tinyint(3) unsigned NOT NULL,
3050+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
3051+ PRIMARY KEY (`rental_id`),
3052+ UNIQUE KEY `rental_date` (`rental_date`,`inventory_id`,`customer_id`),
3053+ KEY `idx_fk_inventory_id` (`inventory_id`),
3054+ KEY `idx_fk_customer_id` (`customer_id`),
3055+ KEY `idx_fk_staff_id` (`staff_id`),
3056+ CONSTRAINT `fk_rental_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE,
3057+ CONSTRAINT `fk_rental_inventory` FOREIGN KEY (`inventory_id`) REFERENCES `inventory` (`inventory_id`) ON UPDATE CASCADE,
3058+ CONSTRAINT `fk_rental_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE
3059+) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
3060+
3061 sakila.staff
3062+CREATE TABLE `staff` (
3063+ `staff_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
3064+ `first_name` varchar(45) NOT NULL,
3065+ `last_name` varchar(45) NOT NULL,
3066+ `address_id` smallint(5) unsigned NOT NULL,
3067+ `picture` blob,
3068+ `email` varchar(50) DEFAULT NULL,
3069+ `store_id` tinyint(3) unsigned NOT NULL,
3070+ `active` tinyint(1) NOT NULL DEFAULT '1',
3071+ `username` varchar(16) NOT NULL,
3072+ `password` varchar(40) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
3073+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
3074+ PRIMARY KEY (`staff_id`),
3075+ KEY `idx_fk_store_id` (`store_id`),
3076+ KEY `idx_fk_address_id` (`address_id`),
3077+ CONSTRAINT `fk_staff_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON UPDATE CASCADE,
3078+ CONSTRAINT `fk_staff_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE
3079+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
3080+
3081 sakila.store
3082+CREATE TABLE `store` (
3083+ `store_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
3084+ `manager_staff_id` tinyint(3) unsigned NOT NULL,
3085+ `address_id` smallint(5) unsigned NOT NULL,
3086+ `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
3087+ PRIMARY KEY (`store_id`),
3088+ UNIQUE KEY `idx_unique_manager` (`manager_staff_id`),
3089+ KEY `idx_fk_address_id` (`address_id`),
3090+ CONSTRAINT `fk_store_staff` FOREIGN KEY (`manager_staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE,
3091+ CONSTRAINT `fk_store_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON UPDATE CASCADE
3092+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
3093+
3094
3095=== modified file 't/pt-table-sync/issue_408.t'
3096--- t/pt-table-sync/issue_408.t 2011-12-22 22:50:52 +0000
3097+++ t/pt-table-sync/issue_408.t 2012-03-31 16:06:20 +0000
3098@@ -47,7 +47,7 @@
3099 $output = `$trunk/bin/pt-table-sync --databases test --execute h=127.1,P=12345,u=msandbox,p=msandbox h=127.1,P=12348 2>&1`;
3100 like(
3101 $output,
3102- qr/Unknown database 'test'/,
3103+ qr/Table test.foo does not exist on P=12348/,
3104 'Warn about --databases missing on dest host'
3105 );
3106

Subscribers

People subscribed via source and target branches