mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-14 15:33:49 +00:00
Get row estimate from NibbleIterator. Use user's index over MySQL's index when --where is given.
This commit is contained in:
@@ -3461,15 +3461,18 @@ sub new {
|
|||||||
}
|
}
|
||||||
my ($cxn, $tbl, $chunk_size, $o, $q) = @args{@required_args};
|
my ($cxn, $tbl, $chunk_size, $o, $q) = @args{@required_args};
|
||||||
|
|
||||||
|
my ($row_est, $mysql_index) = _get_row_estimate(%args);
|
||||||
my $one_nibble = !defined $args{one_nibble} || $args{one_nibble}
|
my $one_nibble = !defined $args{one_nibble} || $args{one_nibble}
|
||||||
? _can_nibble_once(dbh => $cxn->dbh(), %args)
|
? $row_est < $chunk_size * $o->get('chunk-size-limit')
|
||||||
: 0;
|
: 0;
|
||||||
|
MKDEBUG && _d('One nibble:', $one_nibble ? 'yes' : 'no');
|
||||||
|
|
||||||
my $index = _find_best_index(dbh => $cxn->dbh(), %args);
|
my $index = _find_best_index(%args, mysql_index => $mysql_index);
|
||||||
if ( !$index && !$one_nibble ) {
|
if ( !$index && !$one_nibble ) {
|
||||||
die "There is no good index and the table is oversized.";
|
die "There is no good index and the table is oversized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $where = $o->get('where');
|
||||||
my $self;
|
my $self;
|
||||||
if ( $one_nibble ) {
|
if ( $one_nibble ) {
|
||||||
my $tbl_struct = $tbl->{tbl_struct};
|
my $tbl_struct = $tbl->{tbl_struct};
|
||||||
@@ -3482,7 +3485,7 @@ sub new {
|
|||||||
. ($args{select} ? $args{select}
|
. ($args{select} ? $args{select}
|
||||||
: join(', ', map { $q->quote($_) } @cols))
|
: join(', ', map { $q->quote($_) } @cols))
|
||||||
. " FROM " . $q->quote(@{$tbl}{qw(db tbl)})
|
. " FROM " . $q->quote(@{$tbl}{qw(db tbl)})
|
||||||
. ($args{where} ? " AND ($args{where})" : '')
|
. ($where ? " AND ($where)" : '')
|
||||||
. " /*checksum table*/";
|
. " /*checksum table*/";
|
||||||
MKDEBUG && _d('One nibble statement:', $nibble_sql);
|
MKDEBUG && _d('One nibble statement:', $nibble_sql);
|
||||||
|
|
||||||
@@ -3491,7 +3494,7 @@ sub new {
|
|||||||
. ($args{select} ? $args{select}
|
. ($args{select} ? $args{select}
|
||||||
: join(', ', map { $q->quote($_) } @cols))
|
: join(', ', map { $q->quote($_) } @cols))
|
||||||
. " FROM " . $q->quote(@{$tbl}{qw(db tbl)})
|
. " FROM " . $q->quote(@{$tbl}{qw(db tbl)})
|
||||||
. ($args{where} ? " AND ($args{where})" : '')
|
. ($where ? " AND ($where)" : '')
|
||||||
. " /*explain checksum table*/";
|
. " /*explain checksum table*/";
|
||||||
MKDEBUG && _d('Explain one nibble statement:', $explain_nibble_sql);
|
MKDEBUG && _d('Explain one nibble statement:', $explain_nibble_sql);
|
||||||
|
|
||||||
@@ -3521,7 +3524,7 @@ sub new {
|
|||||||
= "SELECT /*!40001 SQL_NO_CACHE */ "
|
= "SELECT /*!40001 SQL_NO_CACHE */ "
|
||||||
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
|
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
|
||||||
. " FROM $from"
|
. " FROM $from"
|
||||||
. ($args{where} ? " WHERE $args{where}" : '')
|
. ($where ? " WHERE $where" : '')
|
||||||
. " ORDER BY $order_by"
|
. " ORDER BY $order_by"
|
||||||
. " LIMIT 1"
|
. " LIMIT 1"
|
||||||
. " /*first lower boundary*/";
|
. " /*first lower boundary*/";
|
||||||
@@ -3531,7 +3534,7 @@ sub new {
|
|||||||
= "SELECT /*!40001 SQL_NO_CACHE */ "
|
= "SELECT /*!40001 SQL_NO_CACHE */ "
|
||||||
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
|
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
|
||||||
. " FROM $from"
|
. " FROM $from"
|
||||||
. ($args{where} ? " WHERE $args{where}" : '')
|
. ($where ? " WHERE $where" : '')
|
||||||
. " ORDER BY "
|
. " ORDER BY "
|
||||||
. join(' DESC, ', map {$q->quote($_)} @{$index_cols}) . ' DESC'
|
. join(' DESC, ', map {$q->quote($_)} @{$index_cols}) . ' DESC'
|
||||||
. " LIMIT 1"
|
. " LIMIT 1"
|
||||||
@@ -3543,7 +3546,7 @@ sub new {
|
|||||||
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
|
. join(', ', map { $q->quote($_) } @{$asc->{scols}})
|
||||||
. " FROM $from"
|
. " FROM $from"
|
||||||
. " WHERE " . $asc->{boundaries}->{'>='}
|
. " WHERE " . $asc->{boundaries}->{'>='}
|
||||||
. ($args{where} ? " AND ($args{where})" : '')
|
. ($where ? " AND ($where)" : '')
|
||||||
. " ORDER BY $order_by"
|
. " ORDER BY $order_by"
|
||||||
. " LIMIT ?, 2"
|
. " LIMIT ?, 2"
|
||||||
. " /*next chunk boundary*/";
|
. " /*next chunk boundary*/";
|
||||||
@@ -3556,7 +3559,7 @@ sub new {
|
|||||||
. " FROM $from"
|
. " FROM $from"
|
||||||
. " WHERE " . $asc->{boundaries}->{'>='} # lower boundary
|
. " WHERE " . $asc->{boundaries}->{'>='} # lower boundary
|
||||||
. " AND " . $asc->{boundaries}->{'<='} # upper boundary
|
. " AND " . $asc->{boundaries}->{'<='} # upper boundary
|
||||||
. ($args{where} ? " AND ($args{where})" : '')
|
. ($where ? " AND ($where)" : '')
|
||||||
. " ORDER BY $order_by"
|
. " ORDER BY $order_by"
|
||||||
. " /*checksum chunk*/";
|
. " /*checksum chunk*/";
|
||||||
MKDEBUG && _d('Nibble statement:', $nibble_sql);
|
MKDEBUG && _d('Nibble statement:', $nibble_sql);
|
||||||
@@ -3568,7 +3571,7 @@ sub new {
|
|||||||
. " FROM $from"
|
. " FROM $from"
|
||||||
. " WHERE " . $asc->{boundaries}->{'>='} # lower boundary
|
. " WHERE " . $asc->{boundaries}->{'>='} # lower boundary
|
||||||
. " AND " . $asc->{boundaries}->{'<='} # upper boundary
|
. " AND " . $asc->{boundaries}->{'<='} # upper boundary
|
||||||
. ($args{where} ? " AND ($args{where})" : '')
|
. ($where ? " AND ($where)" : '')
|
||||||
. " ORDER BY $order_by"
|
. " ORDER BY $order_by"
|
||||||
. " /*explain checksum chunk*/";
|
. " /*explain checksum chunk*/";
|
||||||
MKDEBUG && _d('Explain nibble statement:', $explain_nibble_sql);
|
MKDEBUG && _d('Explain nibble statement:', $explain_nibble_sql);
|
||||||
@@ -3589,13 +3592,14 @@ sub new {
|
|||||||
sql => {
|
sql => {
|
||||||
columns => $asc->{scols},
|
columns => $asc->{scols},
|
||||||
from => $from,
|
from => $from,
|
||||||
where => $args{where},
|
where => $where,
|
||||||
boundaries => $asc->{boundaries},
|
boundaries => $asc->{boundaries},
|
||||||
order_by => $order_by,
|
order_by => $order_by,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$self->{row_est} = $row_est;
|
||||||
$self->{nibbleno} = 0;
|
$self->{nibbleno} = 0;
|
||||||
$self->{have_rows} = 0;
|
$self->{have_rows} = 0;
|
||||||
$self->{rowno} = 0;
|
$self->{rowno} = 0;
|
||||||
@@ -3746,26 +3750,46 @@ sub more_boundaries {
|
|||||||
return !$self->{no_more_boundaries};
|
return !$self->{no_more_boundaries};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub row_estimate {
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->{row_est};
|
||||||
|
}
|
||||||
|
|
||||||
sub _find_best_index {
|
sub _find_best_index {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my @required_args = qw(tbl TableParser dbh Quoter);
|
my @required_args = qw(Cxn tbl TableParser);
|
||||||
my ($tbl, $tp) = @args{@required_args};
|
my ($cxn, $tbl, $tp) = @args{@required_args};
|
||||||
|
|
||||||
my $tbl_struct = $tbl->{tbl_struct};
|
my $tbl_struct = $tbl->{tbl_struct};
|
||||||
my $indexes = $tbl_struct->{keys};
|
my $indexes = $tbl_struct->{keys};
|
||||||
|
|
||||||
|
my $want_index = $args{chunk_index};
|
||||||
|
if ( $want_index ) {
|
||||||
|
MKDEBUG && _d('User wants to use index', $want_index);
|
||||||
|
if ( !exists $indexes->{$want_index} ) {
|
||||||
|
MKDEBUG && _d('Cannot use user index because it does not exist');
|
||||||
|
$want_index = undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$want_index && $args{mysql_index} ) {
|
||||||
|
MKDEBUG && _d('MySQL wants to use index', $args{mysql_index});
|
||||||
|
$want_index = $args{mysql_index};
|
||||||
|
}
|
||||||
|
|
||||||
my $best_index;
|
my $best_index;
|
||||||
my @possible_indexes;
|
my @possible_indexes;
|
||||||
if ( my $want_index = $args{chunk_index} ) {
|
if ( $want_index ) {
|
||||||
MKDEBUG && _d('Want to use nibble index', $want_index);
|
if ( $indexes->{$want_index}->{is_unique} ) {
|
||||||
if ( $want_index eq 'PRIMARY' || $indexes->{$want_index}->{is_unique} ) {
|
MKDEBUG && _d('Will use wanted index');
|
||||||
$best_index = $want_index;
|
$best_index = $want_index;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
MKDEBUG && _d('Wanted index is a possible index');
|
||||||
push @possible_indexes, $want_index;
|
push @possible_indexes, $want_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
MKDEBUG && _d('Auto-selecting best index');
|
||||||
foreach my $index ( $tp->sort_indexes($tbl_struct) ) {
|
foreach my $index ( $tp->sort_indexes($tbl_struct) ) {
|
||||||
if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
|
if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
|
||||||
$best_index = $index;
|
$best_index = $index;
|
||||||
@@ -3804,14 +3828,14 @@ sub _find_best_index {
|
|||||||
|
|
||||||
sub _get_index_cardinality {
|
sub _get_index_cardinality {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my @required_args = qw(dbh tbl index Quoter);
|
my @required_args = qw(Cxn tbl index Quoter);
|
||||||
my ($dbh, $tbl, $index, $q) = @args{@required_args};
|
my ($cxn, $tbl, $index, $q) = @args{@required_args};
|
||||||
|
|
||||||
my $sql = "SHOW INDEXES FROM " . $q->quote(@{$tbl}{qw(db tbl)})
|
my $sql = "SHOW INDEXES FROM " . $q->quote(@{$tbl}{qw(db tbl)})
|
||||||
. " WHERE Key_name = '$index'";
|
. " WHERE Key_name = '$index'";
|
||||||
MKDEBUG && _d($sql);
|
MKDEBUG && _d($sql);
|
||||||
my $cardinality = 1;
|
my $cardinality = 1;
|
||||||
my $rows = $dbh->selectall_hashref($sql, 'key_name');
|
my $rows = $cxn->dbh()->selectall_hashref($sql, 'key_name');
|
||||||
foreach my $row ( values %$rows ) {
|
foreach my $row ( values %$rows ) {
|
||||||
$cardinality *= $row->{cardinality} if $row->{cardinality};
|
$cardinality *= $row->{cardinality} if $row->{cardinality};
|
||||||
}
|
}
|
||||||
@@ -3819,17 +3843,24 @@ sub _get_index_cardinality {
|
|||||||
return $cardinality;
|
return $cardinality;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _can_nibble_once {
|
sub _get_row_estimate {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my @required_args = qw(dbh tbl chunk_size OptionParser TableParser);
|
my @required_args = qw(Cxn tbl OptionParser TableParser Quoter);
|
||||||
my ($dbh, $tbl, $chunk_size, $o, $tp) = @args{@required_args};
|
my ($cxn, $tbl, $o, $tp, $q) = @args{@required_args};
|
||||||
my ($table_status) = $tp->get_table_status($dbh, $tbl->{db}, $tbl->{tbl});
|
|
||||||
MKDEBUG && _d('TABLE STATUS', Dumper($table_status));
|
if ( my $where = $o->get('where') ) {
|
||||||
my $n_rows = $table_status->{rows} || 0;
|
MKDEBUG && _d('WHERE clause, using explain plan for row estimate');
|
||||||
my $limit = $o->get('chunk-size-limit');
|
my $table = $q->quote(@{$tbl}{qw(db tbl)});
|
||||||
my $one_nibble = $n_rows < $chunk_size * $limit ? 1 : 0;
|
my $sql = "EXPLAIN SELECT COUNT(*) FROM $table WHERE $where";
|
||||||
MKDEBUG && _d('One nibble:', $one_nibble ? 'yes' : 'no');
|
MKDEBUG && _d($sql);
|
||||||
return $one_nibble;
|
my $expl = $cxn->dbh()->selectrow_hashref($sql);
|
||||||
|
MKDEBUG && _d(Dumper($expl));
|
||||||
|
return ($expl->{rows} || 0), $expl->{key};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MKDEBUG && _d('No WHERE clause, using table status for row estimate');
|
||||||
|
return $tbl->{tbl_status}->{rows} || 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _prepare_sths {
|
sub _prepare_sths {
|
||||||
@@ -4292,6 +4323,7 @@ sub next {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete $schema_obj->{ddl} unless $self->{keep_ddl};
|
delete $schema_obj->{ddl} unless $self->{keep_ddl};
|
||||||
|
delete $schema_obj->{tbl_status} unless $self->{keep_tbl_status};
|
||||||
|
|
||||||
if ( my $schema = $self->{Schema} ) {
|
if ( my $schema = $self->{Schema} ) {
|
||||||
$schema->add_schema_object($schema_obj);
|
$schema->add_schema_object($schema_obj);
|
||||||
@@ -4408,18 +4440,21 @@ sub _iterate_dbh {
|
|||||||
|
|
||||||
while ( my $tbl = shift @{$self->{tbls}} ) {
|
while ( my $tbl = shift @{$self->{tbls}} ) {
|
||||||
next unless $self->_resume_from_table($tbl);
|
next unless $self->_resume_from_table($tbl);
|
||||||
my $engine;
|
|
||||||
|
my $tbl_status;
|
||||||
if ( $self->{filters}->{'engines'}
|
if ( $self->{filters}->{'engines'}
|
||||||
|| $self->{filters}->{'ignore-engines'} ) {
|
|| $self->{filters}->{'ignore-engines'}
|
||||||
|
|| $self->{keep_tbl_status} )
|
||||||
|
{
|
||||||
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
|
my $sql = "SHOW TABLE STATUS FROM " . $q->quote($self->{db})
|
||||||
. " LIKE \'$tbl\'";
|
. " LIKE \'$tbl\'";
|
||||||
MKDEBUG && _d($sql);
|
MKDEBUG && _d($sql);
|
||||||
$engine = $dbh->selectrow_hashref($sql)->{engine};
|
$tbl_status = $dbh->selectrow_hashref($sql);
|
||||||
MKDEBUG && _d($tbl, 'uses', $engine, 'engine');
|
MKDEBUG && _d(Dumper($tbl_status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !$tbl_status
|
||||||
if ( !$engine || $self->engine_is_allowed($engine) ) {
|
|| $self->engine_is_allowed($tbl_status->{engine}) ) {
|
||||||
my $ddl;
|
my $ddl;
|
||||||
if ( my $tp = $self->{TableParser} ) {
|
if ( my $tp = $self->{TableParser} ) {
|
||||||
$ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
|
$ddl = $tp->get_create_table($dbh, $self->{db}, $tbl);
|
||||||
@@ -4429,6 +4464,7 @@ sub _iterate_dbh {
|
|||||||
db => $self->{db},
|
db => $self->{db},
|
||||||
tbl => $tbl,
|
tbl => $tbl,
|
||||||
ddl => $ddl,
|
ddl => $ddl,
|
||||||
|
tbl_status => $tbl_status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5995,7 +6031,9 @@ sub main {
|
|||||||
# ########################################################################
|
# ########################################################################
|
||||||
my $schema_iter = new SchemaIterator(
|
my $schema_iter = new SchemaIterator(
|
||||||
dbh => $master_dbh,
|
dbh => $master_dbh,
|
||||||
resume => $last_chunk ? $q->quote(@{$last_chunk}{qw(db tbl)}) : "",
|
resume => $last_chunk ? $q->quote(@{$last_chunk}{qw(db tbl)})
|
||||||
|
: "",
|
||||||
|
keep_tbl_status => 1,
|
||||||
OptionParser => $o,
|
OptionParser => $o,
|
||||||
TableParser => $tp,
|
TableParser => $tp,
|
||||||
Quoter => $q,
|
Quoter => $q,
|
||||||
@@ -6068,12 +6106,14 @@ sub main {
|
|||||||
# Make a Progress obj for this table. It may not be used;
|
# Make a Progress obj for this table. It may not be used;
|
||||||
# depends on how many rows, chunk size, how fast the server
|
# depends on how many rows, chunk size, how fast the server
|
||||||
# is, etc. But just in case, all tables have a Progress obj.
|
# is, etc. But just in case, all tables have a Progress obj.
|
||||||
if ( $o->get('progress') ) {
|
if ( $o->get('progress')
|
||||||
$tbl->{progress} = table_progress(
|
&& !$nibble_iter->one_nibble()
|
||||||
dbh => $master_cxn->dbh(),
|
&& $nibble_iter->row_estimate() )
|
||||||
tbl => $tbl,
|
{
|
||||||
OptionParser => $o,
|
$tbl->{progress} = new Progress(
|
||||||
Quoter => $q,
|
jobsize => $nibble_iter->row_estimate(),
|
||||||
|
spec => $o->get('progress'),
|
||||||
|
name => "Checksumming $tbl->{db}.$tbl->{tbl}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6552,31 +6592,6 @@ sub explain_statement {
|
|||||||
return $expl;
|
return $expl;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub table_progress {
|
|
||||||
my (%args) = @_;
|
|
||||||
my @required_args = qw(dbh tbl OptionParser Quoter);
|
|
||||||
foreach my $arg ( @required_args ) {
|
|
||||||
die "I need a $arg argument" unless $args{$arg};
|
|
||||||
}
|
|
||||||
my ($dbh, $tbl, $o, $q) = @args{@required_args};
|
|
||||||
|
|
||||||
my $table = $q->quote(@{$tbl}{qw(db tbl)});
|
|
||||||
my $sql = "EXPLAIN SELECT COUNT(*) FROM $table"
|
|
||||||
. ($args{where} ? " WHERE $args{where}" : '');
|
|
||||||
MKDEBUG && _d($sql);
|
|
||||||
my $expl = $dbh->selectrow_hashref($sql);
|
|
||||||
my $rows = $expl->{rows} || 0;
|
|
||||||
my $pr;
|
|
||||||
if ( $rows ) {
|
|
||||||
$pr = new Progress(
|
|
||||||
jobsize => $rows,
|
|
||||||
spec => $o->get('progress'),
|
|
||||||
name => "Checksumming $tbl->{db}.$tbl->{tbl}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return $pr;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub last_chunk {
|
sub last_chunk {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my @required_args = qw(dbh repl_table);
|
my @required_args = qw(dbh repl_table);
|
||||||
|
@@ -214,6 +214,7 @@ sub new {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$self->{row_est} = $row_est;
|
||||||
$self->{nibbleno} = 0;
|
$self->{nibbleno} = 0;
|
||||||
$self->{have_rows} = 0;
|
$self->{have_rows} = 0;
|
||||||
$self->{rowno} = 0;
|
$self->{rowno} = 0;
|
||||||
@@ -373,6 +374,11 @@ sub more_boundaries {
|
|||||||
return !$self->{no_more_boundaries};
|
return !$self->{no_more_boundaries};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub row_estimate {
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->{row_est};
|
||||||
|
}
|
||||||
|
|
||||||
sub _find_best_index {
|
sub _find_best_index {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my @required_args = qw(Cxn tbl TableParser);
|
my @required_args = qw(Cxn tbl TableParser);
|
||||||
@@ -381,12 +387,15 @@ sub _find_best_index {
|
|||||||
my $indexes = $tbl_struct->{keys};
|
my $indexes = $tbl_struct->{keys};
|
||||||
|
|
||||||
my $want_index = $args{chunk_index};
|
my $want_index = $args{chunk_index};
|
||||||
MKDEBUG && _d('Wanted index:', $want_index);
|
if ( $want_index ) {
|
||||||
if ( $want_index && !exists $indexes->{$want_index} ) {
|
MKDEBUG && _d('User wants to use index', $want_index);
|
||||||
MKDEBUG && _d('Wanted index does not exist; will auto-select best index');
|
if ( !exists $indexes->{$want_index} ) {
|
||||||
|
MKDEBUG && _d('Cannot use user index because it does not exist');
|
||||||
$want_index = undef;
|
$want_index = undef;
|
||||||
}
|
}
|
||||||
elsif ( $args{mysql_index} ) {
|
}
|
||||||
|
|
||||||
|
if ( !$want_index && $args{mysql_index} ) {
|
||||||
MKDEBUG && _d('MySQL wants to use index', $args{mysql_index});
|
MKDEBUG && _d('MySQL wants to use index', $args{mysql_index});
|
||||||
$want_index = $args{mysql_index};
|
$want_index = $args{mysql_index};
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ if ( !$dbh ) {
|
|||||||
plan skip_all => 'Cannot connect to sandbox master';
|
plan skip_all => 'Cannot connect to sandbox master';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
plan tests => 28;
|
plan tests => 29;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $q = new Quoter();
|
my $q = new Quoter();
|
||||||
@@ -581,6 +581,7 @@ $ni = make_nibble_iter(
|
|||||||
one_nibble => 0,
|
one_nibble => 0,
|
||||||
argv => [qw(--databases test --where c>'m')],
|
argv => [qw(--databases test --where c>'m')],
|
||||||
);
|
);
|
||||||
|
$dbh->do('analyze table test.t');
|
||||||
|
|
||||||
@rows = ();
|
@rows = ();
|
||||||
while (my $row = $ni->next()) {
|
while (my $row = $ni->next()) {
|
||||||
@@ -593,6 +594,14 @@ is_deeply(
|
|||||||
"Nibbles only values in --where clause range"
|
"Nibbles only values in --where clause range"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# The real number of rows is 13, but MySQL may estimate a little.
|
||||||
|
cmp_ok(
|
||||||
|
$ni->row_estimate(),
|
||||||
|
'<=',
|
||||||
|
15,
|
||||||
|
"row_estimate()"
|
||||||
|
);
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Done.
|
# Done.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
@@ -15,7 +15,6 @@ use PerconaTest;
|
|||||||
use Sandbox;
|
use Sandbox;
|
||||||
require "$trunk/bin/pt-table-checksum";
|
require "$trunk/bin/pt-table-checksum";
|
||||||
|
|
||||||
my $vp = new VersionParser();
|
|
||||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||||
my $dbh = $sb->get_dbh_for('master');
|
my $dbh = $sb->get_dbh_for('master');
|
||||||
@@ -27,18 +26,29 @@ else {
|
|||||||
plan tests => 6;
|
plan tests => 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $output;
|
|
||||||
my $cnf='/tmp/12345/my.sandbox.cnf';
|
my $cnf='/tmp/12345/my.sandbox.cnf';
|
||||||
my @args = ('-F', $cnf, 'h=127.1', qw(-d issue_519 --explain --chunk-size 3));
|
# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
|
||||||
|
# so we need to specify --lock-wait-timeout=3 else the tool will die.
|
||||||
|
my $master_dsn = 'h=127.1,P=12345,u=msandbox,p=msandbox';
|
||||||
|
my @args = ($master_dsn, qw(--lock-wait-timeout 3 -d issue_519 --explain --explain --chunk-size 3));
|
||||||
|
my $output;
|
||||||
|
|
||||||
$sb->load_file('master', "t/pt-table-checksum/samples/issue_519.sql");
|
$sb->load_file('master', "t/pt-table-checksum/samples/issue_519.sql");
|
||||||
|
|
||||||
my $default_output = "issue_519 t SELECT /*issue_519.t:1/5*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX (`PRIMARY`) WHERE (`i` = 0)
|
my $default_output = "--
|
||||||
issue_519 t `i` = 0
|
-- issue_519.t
|
||||||
issue_519 t `i` > 0 AND `i` < '4'
|
--
|
||||||
issue_519 t `i` >= '4' AND `i` < '7'
|
|
||||||
issue_519 t `i` >= '7' AND `i` < '10'
|
REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX(`PRIMARY`) WHERE ((`i` >= ?)) AND ((`i` <= ?)) ORDER BY `i` /*checksum chunk*/
|
||||||
issue_519 t `i` >= '10'
|
|
||||||
|
SELECT /*!40001 SQL_NO_CACHE */ `i` FROM `issue_519`.`t` FORCE INDEX(`PRIMARY`) WHERE ((`i` >= ?)) ORDER BY `i` LIMIT ?, 2 /*next chunk boundary*/
|
||||||
|
|
||||||
|
1 1 3
|
||||||
|
2 4 6
|
||||||
|
3 7 9
|
||||||
|
4 10 11
|
||||||
|
|
||||||
";
|
";
|
||||||
|
|
||||||
$output = output(
|
$output = output(
|
||||||
@@ -67,12 +77,19 @@ $output = output(
|
|||||||
|
|
||||||
is(
|
is(
|
||||||
$output,
|
$output,
|
||||||
"issue_519 t SELECT /*issue_519.t:1/5*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX (`myidx`) WHERE (`i` = 0)
|
"--
|
||||||
issue_519 t `i` = 0
|
-- issue_519.t
|
||||||
issue_519 t `i` > 0 AND `i` < '4'
|
--
|
||||||
issue_519 t `i` >= '4' AND `i` < '7'
|
|
||||||
issue_519 t `i` >= '7' AND `i` < '10'
|
REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX(`myidx`) WHERE ((`i` > ?) OR (`i` = ? AND `y` >= ?)) AND ((`i` < ?) OR (`i` = ? AND `y` <= ?)) ORDER BY `i`, `y` /*checksum chunk*/
|
||||||
issue_519 t `i` >= '10'
|
|
||||||
|
SELECT /*!40001 SQL_NO_CACHE */ `i`, `i`, `y` FROM `issue_519`.`t` FORCE INDEX(`myidx`) WHERE ((`i` > ?) OR (`i` = ? AND `y` >= ?)) ORDER BY `i`, `y` LIMIT ?, 2 /*next chunk boundary*/
|
||||||
|
|
||||||
|
1 1,1,2000 3,3,2002
|
||||||
|
2 4,4,2003 6,6,2005
|
||||||
|
3 7,7,2006 9,9,2008
|
||||||
|
4 10,10,2009 11,11,2010
|
||||||
|
|
||||||
",
|
",
|
||||||
"Use --chunk-index"
|
"Use --chunk-index"
|
||||||
);
|
);
|
||||||
@@ -81,37 +98,27 @@ $output = output(
|
|||||||
sub { pt_table_checksum::main(@args, qw(--chunk-index y)) },
|
sub { pt_table_checksum::main(@args, qw(--chunk-index y)) },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# XXX I'm not sure what this tests thinks it's testing because index y
|
||||||
|
# is a single column index, so there's really not "left-most".
|
||||||
is(
|
is(
|
||||||
$output,
|
$output,
|
||||||
"issue_519 t SELECT /*issue_519.t:1/5*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX (`y`) WHERE (`y` = 0)
|
"--
|
||||||
issue_519 t `y` = 0
|
-- issue_519.t
|
||||||
issue_519 t `y` > 0 AND `y` < '2003'
|
--
|
||||||
issue_519 t `y` >= '2003' AND `y` < '2006'
|
|
||||||
issue_519 t `y` >= '2006' AND `y` < '2009'
|
REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX(`y`) WHERE ((`y` >= ?)) AND ((`y` <= ?)) ORDER BY `y` /*checksum chunk*/
|
||||||
issue_519 t `y` >= '2009'
|
|
||||||
|
SELECT /*!40001 SQL_NO_CACHE */ `y` FROM `issue_519`.`t` FORCE INDEX(`y`) WHERE ((`y` >= ?)) ORDER BY `y` LIMIT ?, 2 /*next chunk boundary*/
|
||||||
|
|
||||||
|
1 2000 2002
|
||||||
|
2 2003 2005
|
||||||
|
3 2006 2008
|
||||||
|
4 2009 2010
|
||||||
|
|
||||||
",
|
",
|
||||||
"Chunks on left-most --chunk-index column"
|
"Chunks on left-most --chunk-index column"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Disabling the index hint with --no-use-index should not affect the
|
|
||||||
# chunks. It should only remove the FORCE INDEX clause from the SQL.
|
|
||||||
$output = output(
|
|
||||||
sub { pt_table_checksum::main(@args, qw(--chunk-index y --no-use-index)) },
|
|
||||||
);
|
|
||||||
|
|
||||||
is(
|
|
||||||
$output,
|
|
||||||
"issue_519 t SELECT /*issue_519.t:1/5*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` WHERE (`y` = 0)
|
|
||||||
issue_519 t `y` = 0
|
|
||||||
issue_519 t `y` > 0 AND `y` < '2003'
|
|
||||||
issue_519 t `y` >= '2003' AND `y` < '2006'
|
|
||||||
issue_519 t `y` >= '2006' AND `y` < '2009'
|
|
||||||
issue_519 t `y` >= '2009'
|
|
||||||
",
|
|
||||||
"No index hint with --no-use-index"
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Issue 378: Make mk-table-checksum try to use the index preferred by the
|
# Issue 378: Make mk-table-checksum try to use the index preferred by the
|
||||||
# optimizer
|
# optimizer
|
||||||
@@ -127,12 +134,43 @@ $output = output(
|
|||||||
|
|
||||||
is(
|
is(
|
||||||
$output,
|
$output,
|
||||||
"issue_519 t SELECT /*issue_519.t:1/1*/ 0 AS chunk_num, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX (`y`) WHERE (1=1) AND ((y > 2009))
|
"--
|
||||||
issue_519 t 1=1
|
-- issue_519.t
|
||||||
|
--
|
||||||
|
|
||||||
|
REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX(`y`) WHERE ((`y` >= ?)) AND ((`y` <= ?)) AND (y > 2009) ORDER BY `y` /*checksum chunk*/
|
||||||
|
|
||||||
|
SELECT /*!40001 SQL_NO_CACHE */ `y` FROM `issue_519`.`t` FORCE INDEX(`y`) WHERE ((`y` >= ?)) AND (y > 2009) ORDER BY `y` LIMIT ?, 2 /*next chunk boundary*/
|
||||||
|
|
||||||
|
1 2010 2010
|
||||||
|
|
||||||
",
|
",
|
||||||
"Auto-chosen --chunk-index for --where (issue 378)"
|
"Auto-chosen --chunk-index for --where (issue 378)"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# If user specifies --chunk-index, then ignore the index MySQL wants to
|
||||||
|
# use (y in this case) and use the user's index.
|
||||||
|
$output = output(
|
||||||
|
sub { pt_table_checksum::main(@args, qw(--chunk-index PRIMARY),
|
||||||
|
"--where", "y > 2009") },
|
||||||
|
);
|
||||||
|
|
||||||
|
is(
|
||||||
|
$output,
|
||||||
|
"--
|
||||||
|
-- issue_519.t
|
||||||
|
--
|
||||||
|
|
||||||
|
REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `i`, `y`, `t`, CONCAT(ISNULL(`t`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `issue_519`.`t` FORCE INDEX(`PRIMARY`) WHERE ((`i` >= ?)) AND ((`i` <= ?)) AND (y > 2009) ORDER BY `i` /*checksum chunk*/
|
||||||
|
|
||||||
|
SELECT /*!40001 SQL_NO_CACHE */ `i` FROM `issue_519`.`t` FORCE INDEX(`PRIMARY`) WHERE ((`i` >= ?)) AND (y > 2009) ORDER BY `i` LIMIT ?, 2 /*next chunk boundary*/
|
||||||
|
|
||||||
|
1 11 11
|
||||||
|
|
||||||
|
",
|
||||||
|
"Explicit --chunk-index overrides MySQL's index for --where"
|
||||||
|
);
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Done.
|
# Done.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
Reference in New Issue
Block a user