Update SchemaIterator, TableParser, and NibbleIterator in tools that use them. All tools' tests still pass.

This commit is contained in:
Daniel Nichter
2012-03-31 09:59:15 -06:00
parent 2bae569429
commit c04b85a066
5 changed files with 415 additions and 314 deletions

View File

@@ -199,19 +199,58 @@ sub new {
return bless $self, $class;
}
sub get_create_table {
my ( $self, $dbh, $db, $tbl ) = @_;
die "I need a dbh parameter" unless $dbh;
die "I need a db parameter" unless $db;
die "I need a tbl parameter" unless $tbl;
my $q = $self->{Quoter};
my $new_sql_mode
= '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
. q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
. '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
. '@@SQL_QUOTE_SHOW_CREATE := 1 */';
my $old_sql_mode = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
. '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
PTDEBUG && _d($new_sql_mode);
eval { $dbh->do($new_sql_mode); };
PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
my $use_sql = 'USE ' . $q->quote($db);
PTDEBUG && _d($dbh, $use_sql);
$dbh->do($use_sql);
my $show_sql = "SHOW CREATE TABLE " . $q->quote($db, $tbl);
PTDEBUG && _d($show_sql);
my $href;
eval { $href = $dbh->selectrow_hashref($show_sql); };
if ( $EVAL_ERROR ) {
PTDEBUG && _d($EVAL_ERROR);
PTDEBUG && _d($old_sql_mode);
$dbh->do($old_sql_mode);
return;
}
PTDEBUG && _d($old_sql_mode);
$dbh->do($old_sql_mode);
my ($key) = grep { m/create (?:table|view)/i } keys %$href;
if ( !$key ) {
die "Error: no 'Create Table' or 'Create View' in result set from "
. "$show_sql: " . Dumper($href);
}
return $href->{$key};
}
sub parse {
my ( $self, $ddl, $opts ) = @_;
return unless $ddl;
if ( ref $ddl eq 'ARRAY' ) {
if ( lc $ddl->[0] eq 'table' ) {
$ddl = $ddl->[1];
}
else {
return {
engine => 'VIEW',
};
}
}
if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
die "Cannot parse table definition; is ANSI quoting "
@@ -518,41 +557,31 @@ sub remove_auto_increment {
return $ddl;
}
sub remove_secondary_indexes {
my ( $self, $ddl ) = @_;
my $sec_indexes_ddl;
my $tbl_struct = $self->parse($ddl);
if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
my $clustered_key = $tbl_struct->{clustered_key};
$clustered_key ||= '';
my @sec_indexes = map {
my $key_def = $_->{ddl};
$key_def =~ s/([\(\)])/\\$1/g;
$ddl =~ s/\s+$key_def//i;
my $key_ddl = "ADD $_->{ddl}";
$key_ddl .= ',' unless $key_ddl =~ m/,$/;
$key_ddl;
}
grep { $_->{name} ne $clustered_key }
values %{$tbl_struct->{keys}};
PTDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));
if ( @sec_indexes ) {
$sec_indexes_ddl = join(' ', @sec_indexes);
$sec_indexes_ddl =~ s/,$//;
}
$ddl =~ s/,(\n\) )/$1/s;
sub get_table_status {
my ( $self, $dbh, $db, $like ) = @_;
my $q = $self->{Quoter};
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($db);
my @params;
if ( $like ) {
$sql .= ' LIKE ?';
push @params, $like;
}
else {
PTDEBUG && _d('Not removing secondary indexes from',
$tbl_struct->{engine}, 'table');
PTDEBUG && _d($sql, @params);
my $sth = $dbh->prepare($sql);
eval { $sth->execute(@params); };
if ($EVAL_ERROR) {
PTDEBUG && _d($EVAL_ERROR);
return;
}
return $ddl, $sec_indexes_ddl, $tbl_struct;
my @tables = @{$sth->fetchall_arrayref({})};
@tables = map {
my %tbl; # Make a copy with lowercased keys
@tbl{ map { lc $_ } keys %$_ } = values %$_;
$tbl{engine} ||= $tbl{type} || $tbl{comment};
delete $tbl{type};
\%tbl;
} @tables;
return @tables;
}
sub _d {
@@ -3195,7 +3224,7 @@ my $tbl_name = qr{
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(OptionParser Quoter);
my @required_args = qw(OptionParser TableParser Quoter);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
}
@@ -3204,8 +3233,19 @@ sub new {
die "I need either a dbh or file_itr argument"
if (!$dbh && !$file_itr) || ($dbh && $file_itr);
my %resume;
if ( my $table = $args{resume} ) {
PTDEBUG && _d('Will resume from or after', $table);
my ($db, $tbl) = $args{Quoter}->split_unquote($table);
die "Resume table must be database-qualified: $table"
unless $db && $tbl;
$resume{db} = $db;
$resume{tbl} = $tbl;
}
my $self = {
%args,
resume => \%resume,
filters => _make_filters(%args),
};
@@ -3266,9 +3306,19 @@ sub _make_filters {
return \%filters;
}
sub next_schema_object {
sub next {
my ( $self ) = @_;
if ( !$self->{initialized} ) {
$self->{initialized} = 1;
if ( $self->{resume}->{tbl}
&& !$self->table_is_allowed(@{$self->{resume}}{qw(db tbl)}) ) {
PTDEBUG && _d('Will resume after',
join('.', @{$self->{resume}}{qw(db tbl)}));
$self->{resume}->{after} = 1;
}
}
my $schema_obj;
if ( $self->{file_itr} ) {
$schema_obj= $self->_iterate_files();
@@ -3278,24 +3328,18 @@ sub next_schema_object {
}
if ( $schema_obj ) {
if ( $schema_obj->{ddl} && $self->{TableParser} ) {
$schema_obj->{tbl_struct}
= $self->{TableParser}->parse($schema_obj->{ddl});
}
delete $schema_obj->{ddl} unless $self->{keep_ddl};
if ( my $schema = $self->{Schema} ) {
$schema->add_schema_object($schema_obj);
}
PTDEBUG && _d('Next schema object:',
$schema_obj->{db}, $schema_obj->{tbl});
}
PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
return $schema_obj;
}
sub _iterate_files {
my ( $self ) = @_;
my ( $self ) = @_;
if ( !$self->{fh} ) {
my ($fh, $file) = $self->{file_itr}->();
@@ -3316,7 +3360,8 @@ sub _iterate_files {
my $db = $1; # XXX
$db =~ s/^`//; # strip leading `
$db =~ s/`$//; # and trailing `
if ( $self->database_is_allowed($db) ) {
if ( $self->database_is_allowed($db)
&& $self->_resume_from_database($db) ) {
$self->{db} = $db;
}
}
@@ -3329,21 +3374,22 @@ sub _iterate_files {
my ($tbl) = $chunk =~ m/$tbl_name/;
$tbl =~ s/^\s*`//;
$tbl =~ s/`\s*$//;
if ( $self->table_is_allowed($self->{db}, $tbl) ) {
if ( $self->_resume_from_table($tbl)
&& $self->table_is_allowed($self->{db}, $tbl) ) {
my ($ddl) = $chunk =~ m/^(?:$open_comment)?(CREATE TABLE.+?;)$/ms;
if ( !$ddl ) {
warn "Failed to parse CREATE TABLE from\n" . $chunk;
next CHUNK;
}
$ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
if ( !$engine || $self->engine_is_allowed($engine) ) {
my $tbl_struct = $self->{TableParser}->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
ddl => $ddl,
db => $self->{db},
tbl => $tbl,
name => $self->{Quoter}->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_struct => $tbl_struct,
};
}
}
@@ -3360,6 +3406,7 @@ sub _iterate_files {
sub _iterate_dbh {
my ( $self ) = @_;
my $q = $self->{Quoter};
my $tp = $self->{TableParser};
my $dbh = $self->{dbh};
PTDEBUG && _d('Getting next schema object from dbh', $dbh);
@@ -3373,7 +3420,9 @@ sub _iterate_dbh {
}
if ( !$self->{db} ) {
$self->{db} = shift @{$self->{dbs}};
do {
$self->{db} = shift @{$self->{dbs}};
} until $self->_resume_from_database($self->{db});
PTDEBUG && _d('Next database:', $self->{db});
return unless $self->{db};
}
@@ -3386,8 +3435,9 @@ sub _iterate_dbh {
}
grep {
my ($tbl, $type) = @$_;
$self->table_is_allowed($self->{db}, $tbl)
&& (!$type || ($type ne 'VIEW'));
(!$type || ($type ne 'VIEW'))
&& $self->_resume_from_table($tbl)
&& $self->table_is_allowed($self->{db}, $tbl);
}
@{$dbh->selectall_arrayref($sql)};
PTDEBUG && _d('Found', scalar @tbls, 'tables in database', $self->{db});
@@ -3395,27 +3445,15 @@ sub _iterate_dbh {
}
while ( my $tbl = shift @{$self->{tbls}} ) {
my $engine;
if ( $self->{filters}->{'engines'}
|| $self->{filters}->{'ignore-engines'} ) {
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
. " LIKE \'$tbl\'";
PTDEBUG && _d($sql);
$engine = $dbh->selectrow_hashref($sql)->{engine};
PTDEBUG && _d($tbl, 'uses', $engine, 'engine');
}
if ( !$engine || $self->engine_is_allowed($engine) ) {
my $ddl;
if ( my $du = $self->{MySQLDump} ) {
$ddl = $du->get_create_table($dbh, $q, $self->{db}, $tbl)->[1];
}
my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
my $tbl_struct = $tp->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
ddl => $ddl,
db => $self->{db},
tbl => $tbl,
name => $q->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_struct => $tbl_struct,
};
}
}
@@ -3476,6 +3514,10 @@ sub table_is_allowed {
my $filter = $self->{filters};
if ( $db eq 'mysql' && ($tbl eq 'general_log' || $tbl eq 'slow_log') ) {
return 0;
}
if ( $filter->{'ignore-tables'}->{$tbl}
&& ($filter->{'ignore-tables'}->{$tbl} eq '*'
|| $filter->{'ignore-tables'}->{$tbl} eq $db) ) {
@@ -3515,7 +3557,11 @@ sub table_is_allowed {
sub engine_is_allowed {
my ( $self, $engine ) = @_;
die "I need an engine argument" unless $engine;
if ( !$engine ) {
PTDEBUG && _d('No engine specified; allowing the table');
return 1;
}
$engine = lc $engine;
@@ -3535,6 +3581,40 @@ sub engine_is_allowed {
return 1;
}
sub _resume_from_database {
my ($self, $db) = @_;
return 1 unless $self->{resume}->{db};
if ( $db eq $self->{resume}->{db} ) {
PTDEBUG && _d('At resume db', $db);
delete $self->{resume}->{db};
return 1;
}
return 0;
}
sub _resume_from_table {
my ($self, $tbl) = @_;
return 1 unless $self->{resume}->{tbl};
if ( $tbl eq $self->{resume}->{tbl} ) {
if ( !$self->{resume}->{after} ) {
PTDEBUG && _d('Resuming from table', $tbl);
delete $self->{resume}->{tbl};
return 1;
}
else {
PTDEBUG && _d('Resuming after table', $tbl);
delete $self->{resume}->{tbl};
}
}
return 0;
}
sub _d {
my ($package, undef, $line) = caller 0;
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
@@ -3644,11 +3724,10 @@ sub main {
MySQLDump => $du,
TableParser => $tp,
Schema => $schema,
keep_ddl => 1,
);
TABLE:
while ( my $tbl = $schema_itr->next_schema_object() ) {
$tbl->{engine} = $tp->get_engine($tbl->{ddl});
while ( my $tbl = $schema_itr->next() ) {
$tbl->{engine} = $tbl->{tbl_struct}->{engine};
my ($keys, $clustered_key, $fks);
if ( $get_keys ) {

View File

@@ -2669,19 +2669,58 @@ sub new {
return bless $self, $class;
}
sub get_create_table {
my ( $self, $dbh, $db, $tbl ) = @_;
die "I need a dbh parameter" unless $dbh;
die "I need a db parameter" unless $db;
die "I need a tbl parameter" unless $tbl;
my $q = $self->{Quoter};
my $new_sql_mode
= '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
. q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
. '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
. '@@SQL_QUOTE_SHOW_CREATE := 1 */';
my $old_sql_mode = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
. '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
PTDEBUG && _d($new_sql_mode);
eval { $dbh->do($new_sql_mode); };
PTDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
my $use_sql = 'USE ' . $q->quote($db);
PTDEBUG && _d($dbh, $use_sql);
$dbh->do($use_sql);
my $show_sql = "SHOW CREATE TABLE " . $q->quote($db, $tbl);
PTDEBUG && _d($show_sql);
my $href;
eval { $href = $dbh->selectrow_hashref($show_sql); };
if ( $EVAL_ERROR ) {
PTDEBUG && _d($EVAL_ERROR);
PTDEBUG && _d($old_sql_mode);
$dbh->do($old_sql_mode);
return;
}
PTDEBUG && _d($old_sql_mode);
$dbh->do($old_sql_mode);
my ($key) = grep { m/create (?:table|view)/i } keys %$href;
if ( !$key ) {
die "Error: no 'Create Table' or 'Create View' in result set from "
. "$show_sql: " . Dumper($href);
}
return $href->{$key};
}
sub parse {
my ( $self, $ddl, $opts ) = @_;
return unless $ddl;
if ( ref $ddl eq 'ARRAY' ) {
if ( lc $ddl->[0] eq 'table' ) {
$ddl = $ddl->[1];
}
else {
return {
engine => 'VIEW',
};
}
}
if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
die "Cannot parse table definition; is ANSI quoting "
@@ -2988,41 +3027,31 @@ sub remove_auto_increment {
return $ddl;
}
sub remove_secondary_indexes {
my ( $self, $ddl ) = @_;
my $sec_indexes_ddl;
my $tbl_struct = $self->parse($ddl);
if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
my $clustered_key = $tbl_struct->{clustered_key};
$clustered_key ||= '';
my @sec_indexes = map {
my $key_def = $_->{ddl};
$key_def =~ s/([\(\)])/\\$1/g;
$ddl =~ s/\s+$key_def//i;
my $key_ddl = "ADD $_->{ddl}";
$key_ddl .= ',' unless $key_ddl =~ m/,$/;
$key_ddl;
}
grep { $_->{name} ne $clustered_key }
values %{$tbl_struct->{keys}};
PTDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));
if ( @sec_indexes ) {
$sec_indexes_ddl = join(' ', @sec_indexes);
$sec_indexes_ddl =~ s/,$//;
}
$ddl =~ s/,(\n\) )/$1/s;
sub get_table_status {
my ( $self, $dbh, $db, $like ) = @_;
my $q = $self->{Quoter};
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($db);
my @params;
if ( $like ) {
$sql .= ' LIKE ?';
push @params, $like;
}
else {
PTDEBUG && _d('Not removing secondary indexes from',
$tbl_struct->{engine}, 'table');
PTDEBUG && _d($sql, @params);
my $sth = $dbh->prepare($sql);
eval { $sth->execute(@params); };
if ($EVAL_ERROR) {
PTDEBUG && _d($EVAL_ERROR);
return;
}
return $ddl, $sec_indexes_ddl, $tbl_struct;
my @tables = @{$sth->fetchall_arrayref({})};
@tables = map {
my %tbl; # Make a copy with lowercased keys
@tbl{ map { lc $_ } keys %$_ } = values %$_;
$tbl{engine} ||= $tbl{type} || $tbl{comment};
delete $tbl{type};
\%tbl;
} @tables;
return @tables;
}
sub _d {
@@ -3912,7 +3941,7 @@ my $tbl_name = qr{
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(OptionParser Quoter);
my @required_args = qw(OptionParser TableParser Quoter);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
}
@@ -3921,8 +3950,19 @@ sub new {
die "I need either a dbh or file_itr argument"
if (!$dbh && !$file_itr) || ($dbh && $file_itr);
my %resume;
if ( my $table = $args{resume} ) {
PTDEBUG && _d('Will resume from or after', $table);
my ($db, $tbl) = $args{Quoter}->split_unquote($table);
die "Resume table must be database-qualified: $table"
unless $db && $tbl;
$resume{db} = $db;
$resume{tbl} = $tbl;
}
my $self = {
%args,
resume => \%resume,
filters => _make_filters(%args),
};
@@ -3983,9 +4023,19 @@ sub _make_filters {
return \%filters;
}
sub next_schema_object {
sub next {
my ( $self ) = @_;
if ( !$self->{initialized} ) {
$self->{initialized} = 1;
if ( $self->{resume}->{tbl}
&& !$self->table_is_allowed(@{$self->{resume}}{qw(db tbl)}) ) {
PTDEBUG && _d('Will resume after',
join('.', @{$self->{resume}}{qw(db tbl)}));
$self->{resume}->{after} = 1;
}
}
my $schema_obj;
if ( $self->{file_itr} ) {
$schema_obj= $self->_iterate_files();
@@ -3995,24 +4045,18 @@ sub next_schema_object {
}
if ( $schema_obj ) {
if ( $schema_obj->{ddl} && $self->{TableParser} ) {
$schema_obj->{tbl_struct}
= $self->{TableParser}->parse($schema_obj->{ddl});
}
delete $schema_obj->{ddl} unless $self->{keep_ddl};
if ( my $schema = $self->{Schema} ) {
$schema->add_schema_object($schema_obj);
}
PTDEBUG && _d('Next schema object:',
$schema_obj->{db}, $schema_obj->{tbl});
}
PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
return $schema_obj;
}
sub _iterate_files {
my ( $self ) = @_;
my ( $self ) = @_;
if ( !$self->{fh} ) {
my ($fh, $file) = $self->{file_itr}->();
@@ -4033,7 +4077,8 @@ sub _iterate_files {
my $db = $1; # XXX
$db =~ s/^`//; # strip leading `
$db =~ s/`$//; # and trailing `
if ( $self->database_is_allowed($db) ) {
if ( $self->database_is_allowed($db)
&& $self->_resume_from_database($db) ) {
$self->{db} = $db;
}
}
@@ -4046,21 +4091,22 @@ sub _iterate_files {
my ($tbl) = $chunk =~ m/$tbl_name/;
$tbl =~ s/^\s*`//;
$tbl =~ s/`\s*$//;
if ( $self->table_is_allowed($self->{db}, $tbl) ) {
if ( $self->_resume_from_table($tbl)
&& $self->table_is_allowed($self->{db}, $tbl) ) {
my ($ddl) = $chunk =~ m/^(?:$open_comment)?(CREATE TABLE.+?;)$/ms;
if ( !$ddl ) {
warn "Failed to parse CREATE TABLE from\n" . $chunk;
next CHUNK;
}
$ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
if ( !$engine || $self->engine_is_allowed($engine) ) {
my $tbl_struct = $self->{TableParser}->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
ddl => $ddl,
db => $self->{db},
tbl => $tbl,
name => $self->{Quoter}->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_struct => $tbl_struct,
};
}
}
@@ -4077,6 +4123,7 @@ sub _iterate_files {
sub _iterate_dbh {
my ( $self ) = @_;
my $q = $self->{Quoter};
my $tp = $self->{TableParser};
my $dbh = $self->{dbh};
PTDEBUG && _d('Getting next schema object from dbh', $dbh);
@@ -4090,7 +4137,9 @@ sub _iterate_dbh {
}
if ( !$self->{db} ) {
$self->{db} = shift @{$self->{dbs}};
do {
$self->{db} = shift @{$self->{dbs}};
} until $self->_resume_from_database($self->{db});
PTDEBUG && _d('Next database:', $self->{db});
return unless $self->{db};
}
@@ -4103,8 +4152,9 @@ sub _iterate_dbh {
}
grep {
my ($tbl, $type) = @$_;
$self->table_is_allowed($self->{db}, $tbl)
&& (!$type || ($type ne 'VIEW'));
(!$type || ($type ne 'VIEW'))
&& $self->_resume_from_table($tbl)
&& $self->table_is_allowed($self->{db}, $tbl);
}
@{$dbh->selectall_arrayref($sql)};
PTDEBUG && _d('Found', scalar @tbls, 'tables in database', $self->{db});
@@ -4112,27 +4162,15 @@ sub _iterate_dbh {
}
while ( my $tbl = shift @{$self->{tbls}} ) {
my $engine;
if ( $self->{filters}->{'engines'}
|| $self->{filters}->{'ignore-engines'} ) {
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
. " LIKE \'$tbl\'";
PTDEBUG && _d($sql);
$engine = $dbh->selectrow_hashref($sql)->{engine};
PTDEBUG && _d($tbl, 'uses', $engine, 'engine');
}
if ( !$engine || $self->engine_is_allowed($engine) ) {
my $ddl;
if ( my $du = $self->{MySQLDump} ) {
$ddl = $du->get_create_table($dbh, $q, $self->{db}, $tbl)->[1];
}
my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
my $tbl_struct = $tp->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
ddl => $ddl,
db => $self->{db},
tbl => $tbl,
name => $q->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_struct => $tbl_struct,
};
}
}
@@ -4193,6 +4231,10 @@ sub table_is_allowed {
my $filter = $self->{filters};
if ( $db eq 'mysql' && ($tbl eq 'general_log' || $tbl eq 'slow_log') ) {
return 0;
}
if ( $filter->{'ignore-tables'}->{$tbl}
&& ($filter->{'ignore-tables'}->{$tbl} eq '*'
|| $filter->{'ignore-tables'}->{$tbl} eq $db) ) {
@@ -4232,7 +4274,11 @@ sub table_is_allowed {
sub engine_is_allowed {
my ( $self, $engine ) = @_;
die "I need an engine argument" unless $engine;
if ( !$engine ) {
PTDEBUG && _d('No engine specified; allowing the table');
return 1;
}
$engine = lc $engine;
@@ -4252,6 +4298,40 @@ sub engine_is_allowed {
return 1;
}
sub _resume_from_database {
my ($self, $db) = @_;
return 1 unless $self->{resume}->{db};
if ( $db eq $self->{resume}->{db} ) {
PTDEBUG && _d('At resume db', $db);
delete $self->{resume}->{db};
return 1;
}
return 0;
}
sub _resume_from_table {
my ($self, $tbl) = @_;
return 1 unless $self->{resume}->{tbl};
if ( $tbl eq $self->{resume}->{tbl} ) {
if ( !$self->{resume}->{after} ) {
PTDEBUG && _d('Resuming from table', $tbl);
delete $self->{resume}->{tbl};
return 1;
}
else {
PTDEBUG && _d('Resuming after table', $tbl);
delete $self->{resume}->{tbl};
}
}
return 0;
}
sub _d {
my ($package, undef, $line) = caller 0;
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
@@ -5144,13 +5224,11 @@ sub main {
dbh => $dbh,
OptionParser => $o,
Quoter => $q,
MySQLDump => $du,
TableParser => $tp,
Schema => $schema,
keep_ddl => 1,
);
TALBE:
while ( my $tbl = $schema_itr->next_schema_object() ) {
while ( my $tbl = $schema_itr->next() ) {
eval {
my ($indexes) = $tp->get_keys($tbl->{ddl}, {version => $version});
$iu->add_indexes(%$tbl, indexes=>$indexes);

View File

@@ -3941,22 +3941,15 @@ sub _get_index_cardinality {
sub get_row_estimate {
my (%args) = @_;
my @required_args = qw(Cxn tbl OptionParser TableParser Quoter);
my ($cxn, $tbl, $o, $tp, $q) = @args{@required_args};
my @required_args = qw(Cxn tbl);
my ($cxn, $tbl) = @args{@required_args};
if ( $args{where} ) {
PTDEBUG && _d('WHERE clause, using explain plan for row estimate');
my $table = $q->quote(@{$tbl}{qw(db tbl)});
my $sql = "EXPLAIN SELECT * FROM $table WHERE $args{where}";
PTDEBUG && _d($sql);
my $expl = $cxn->dbh()->selectrow_hashref($sql);
PTDEBUG && _d(Dumper($expl));
return ($expl->{rows} || 0), $expl->{key};
}
else {
PTDEBUG && _d('No WHERE clause, using table status for row estimate');
return $tbl->{tbl_status}->{rows} || 0;
}
my $sql = "EXPLAIN SELECT * FROM $tbl->{name} "
. "WHERE " . ($args{where} || '1=1');
PTDEBUG && _d($sql);
my $expl = $cxn->dbh()->selectrow_hashref($sql);
PTDEBUG && _d(Dumper($expl));
return ($expl->{rows} || 0), $expl->{key};
}
sub _prepare_sths {
@@ -4543,7 +4536,7 @@ my $tbl_name = qr{
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(OptionParser Quoter);
my @required_args = qw(OptionParser TableParser Quoter);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
}
@@ -4647,25 +4640,18 @@ sub next {
}
if ( $schema_obj ) {
if ( $schema_obj->{ddl} && $self->{TableParser} ) {
$schema_obj->{tbl_struct}
= $self->{TableParser}->parse($schema_obj->{ddl});
}
delete $schema_obj->{ddl} unless $self->{keep_ddl};
delete $schema_obj->{tbl_status} unless $self->{keep_tbl_status};
if ( my $schema = $self->{Schema} ) {
$schema->add_schema_object($schema_obj);
}
PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
PTDEBUG && _d('Next schema object:',
$schema_obj->{db}, $schema_obj->{tbl});
}
return $schema_obj;
}
sub _iterate_files {
my ( $self ) = @_;
my ( $self ) = @_;
if ( !$self->{fh} ) {
my ($fh, $file) = $self->{file_itr}->();
@@ -4708,14 +4694,14 @@ sub _iterate_files {
next CHUNK;
}
$ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
if ( !$engine || $self->engine_is_allowed($engine) ) {
my $tbl_struct = $self->{TableParser}->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
ddl => $ddl,
db => $self->{db},
tbl => $tbl,
name => $self->{Quoter}->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_struct => $tbl_struct,
};
}
}
@@ -4732,6 +4718,7 @@ sub _iterate_files {
sub _iterate_dbh {
my ( $self ) = @_;
my $q = $self->{Quoter};
my $tp = $self->{TableParser};
my $dbh = $self->{dbh};
PTDEBUG && _d('Getting next schema object from dbh', $dbh);
@@ -4770,30 +4757,15 @@ sub _iterate_dbh {
}
while ( my $tbl = shift @{$self->{tbls}} ) {
my $tbl_status;
if ( $self->{filters}->{'engines'}
|| $self->{filters}->{'ignore-engines'}
|| $self->{keep_tbl_status} )
{
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
. " LIKE \'$tbl\'";
PTDEBUG && _d($sql);
$tbl_status = $dbh->selectrow_hashref($sql);
PTDEBUG && _d(Dumper($tbl_status));
}
if ( !$tbl_status
|| $self->engine_is_allowed($tbl_status->{engine}) ) {
my $ddl;
if ( my $tp = $self->{TableParser} ) {
$ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
}
my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
my $tbl_struct = $tp->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
name => $q->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_status => $tbl_status,
tbl_struct => $tbl_struct,
};
}
}
@@ -4897,7 +4869,11 @@ sub table_is_allowed {
sub engine_is_allowed {
my ( $self, $engine ) = @_;
die "I need an engine argument" unless $engine;
if ( !$engine ) {
PTDEBUG && _d('No engine specified; allowing the table');
return 1;
}
$engine = lc $engine;
@@ -6253,13 +6229,12 @@ sub main {
}
my $schema_iter = new SchemaIterator(
dbh => $master_dbh,
resume => $last_chunk ? $q->quote(@{$last_chunk}{qw(db tbl)})
: "",
keep_tbl_status => 1,
OptionParser => $o,
TableParser => $tp,
Quoter => $q,
dbh => $master_dbh,
resume => $last_chunk ? $q->quote(@{$last_chunk}{qw(db tbl)})
: "",
OptionParser => $o,
TableParser => $tp,
Quoter => $q,
);
if ( $last_chunk &&
@@ -6334,13 +6309,13 @@ sub main {
my $chunk_size_limit = $o->get('chunk-size-limit');
my @too_large;
foreach my $slave ( @$slaves ) {
# get_row_estimate() returns (row_est, index), but
# we only need the row_est. Maybe in the future we'll
# care what index MySQL will use on a slave.
my ($n_rows) = NibbleIterator::get_row_estimate(
Cxn => $slave,
tbl => $tbl,
where => $o->get('where') || "1=1",
OptionParser => $o,
TableParser => $tp,
Quoter => $q,
Cxn => $slave,
tbl => $tbl,
where => $o->get('where'),
);
PTDEBUG && _d('Table on', $slave->name(),
'has', $n_rows, 'rows');

View File

@@ -6782,7 +6782,7 @@ my $tbl_name = qr{
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(OptionParser Quoter);
my @required_args = qw(OptionParser TableParser Quoter);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
}
@@ -6801,15 +6801,10 @@ sub new {
$resume{tbl} = $tbl;
}
my $engine_key
= $dbh && ($dbh->{FetchHashKeyName} || '') eq 'NAME_lc' ? 'engine'
: 'Engine';
my $self = {
%args,
resume => \%resume,
filters => _make_filters(%args),
engine_key => $engine_key,
resume => \%resume,
filters => _make_filters(%args),
};
return bless $self, $class;
@@ -6891,25 +6886,18 @@ sub next {
}
if ( $schema_obj ) {
if ( $schema_obj->{ddl} && $self->{TableParser} ) {
$schema_obj->{tbl_struct}
= $self->{TableParser}->parse($schema_obj->{ddl});
}
delete $schema_obj->{ddl} unless $self->{keep_ddl};
delete $schema_obj->{tbl_status} unless $self->{keep_tbl_status};
if ( my $schema = $self->{Schema} ) {
$schema->add_schema_object($schema_obj);
}
PTDEBUG && _d('Next schema object:', $schema_obj->{db}, $schema_obj->{tbl});
PTDEBUG && _d('Next schema object:',
$schema_obj->{db}, $schema_obj->{tbl});
}
return $schema_obj;
}
sub _iterate_files {
my ( $self ) = @_;
my ( $self ) = @_;
if ( !$self->{fh} ) {
my ($fh, $file) = $self->{file_itr}->();
@@ -6952,14 +6940,14 @@ sub _iterate_files {
next CHUNK;
}
$ddl =~ s/ \*\/;\Z/;/; # remove end of version comment
my ($engine) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
if ( !$engine || $self->engine_is_allowed($engine) ) {
my $tbl_struct = $self->{TableParser}->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
ddl => $ddl,
db => $self->{db},
tbl => $tbl,
name => $self->{Quoter}->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_struct => $tbl_struct,
};
}
}
@@ -6976,6 +6964,7 @@ sub _iterate_files {
sub _iterate_dbh {
my ( $self ) = @_;
my $q = $self->{Quoter};
my $tp = $self->{TableParser};
my $dbh = $self->{dbh};
PTDEBUG && _d('Getting next schema object from dbh', $dbh);
@@ -7014,30 +7003,15 @@ sub _iterate_dbh {
}
while ( my $tbl = shift @{$self->{tbls}} ) {
my $tbl_status;
if ( $self->{filters}->{'engines'}
|| $self->{filters}->{'ignore-engines'}
|| $self->{keep_tbl_status} )
{
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
. " LIKE \'$tbl\'";
PTDEBUG && _d($sql);
$tbl_status = $dbh->selectrow_hashref($sql);
PTDEBUG && _d(Dumper($tbl_status));
}
if ( !$tbl_status
|| $self->engine_is_allowed($tbl_status->{$self->{engine_key}}) ) {
my $ddl;
if ( my $tp = $self->{TableParser} ) {
$ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
}
my $ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
my $tbl_struct = $tp->parse($ddl);
if ( $self->engine_is_allowed($tbl_struct->{engine}) ) {
return {
db => $self->{db},
tbl => $tbl,
name => $q->quote($self->{db}, $tbl),
ddl => $ddl,
tbl_status => $tbl_status,
tbl_struct => $tbl_struct,
};
}
}
@@ -7141,7 +7115,11 @@ sub table_is_allowed {
sub engine_is_allowed {
my ( $self, $engine ) = @_;
die "I need an engine argument" unless $engine;
if ( !$engine ) {
PTDEBUG && _d('No engine specified; allowing the table');
return 1;
}
$engine = lc $engine;
@@ -8188,12 +8166,10 @@ sub sync_all {
};
my $schema_iter = new SchemaIterator(
dbh => $src->{dbh},
keep_tbl_status => 1,
keep_ddl => 1,
OptionParser => $o,
TableParser => $args{TableParser},
Quoter => $args{Quoter},
dbh => $src->{dbh},
OptionParser => $o,
TableParser => $args{TableParser},
Quoter => $args{Quoter},
);
# Make a list of all dbs.tbls on the source. It's more efficient this

View File

@@ -511,22 +511,15 @@ sub _get_index_cardinality {
sub get_row_estimate {
my (%args) = @_;
my @required_args = qw(Cxn tbl OptionParser TableParser Quoter);
my ($cxn, $tbl, $o, $tp, $q) = @args{@required_args};
my @required_args = qw(Cxn tbl);
my ($cxn, $tbl) = @args{@required_args};
if ( $args{where} ) {
PTDEBUG && _d('WHERE clause, using explain plan for row estimate');
my $table = $q->quote(@{$tbl}{qw(db tbl)});
my $sql = "EXPLAIN SELECT * FROM $table WHERE $args{where}";
PTDEBUG && _d($sql);
my $expl = $cxn->dbh()->selectrow_hashref($sql);
PTDEBUG && _d(Dumper($expl));
return ($expl->{rows} || 0), $expl->{key};
}
else {
PTDEBUG && _d('No WHERE clause, using table status for row estimate');
return $tbl->{tbl_status}->{rows} || 0;
}
my $sql = "EXPLAIN SELECT * FROM $tbl->{name} "
. "WHERE " . ($args{where} || '1=1');
PTDEBUG && _d($sql);
my $expl = $cxn->dbh()->selectrow_hashref($sql);
PTDEBUG && _d(Dumper($expl));
return ($expl->{rows} || 0), $expl->{key};
}
sub _prepare_sths {