diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index bd7b279d..9f821425 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -5127,6 +5127,158 @@ sub _d { # End CleanupTask package # ########################################################################### +# ########################################################################### +# IndexLength package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/IndexLength.pm +# t/lib/IndexLength.t +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### +{ + +package IndexLength; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use constant PTDEBUG => $ENV{PTDEBUG} || 0; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +sub new { + my ( $class, %args ) = @_; + my @required_args = qw(Quoter); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + + my $self = { + Quoter => $args{Quoter}, + }; + + return bless $self, $class; +} + +sub index_length { + my ($self, %args) = @_; + my @required_args = qw(Cxn tbl index); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($cxn) = @args{@required_args}; + + die "The tbl argument does not have a tbl_struct" + unless exists $args{tbl}->{tbl_struct}; + die "Index $args{index} does not exist in table $args{tbl}->{name}" + unless $args{tbl}->{tbl_struct}->{keys}->{$args{index}}; + + my $index_struct = $args{tbl}->{tbl_struct}->{keys}->{$args{index}}; + my $index_cols = $index_struct->{cols}; + my $n_index_cols = $args{n_index_cols}; + if ( !$n_index_cols || $n_index_cols > @$index_cols ) { + $n_index_cols = scalar @$index_cols; + } + + my $vals = $self->_get_first_values( + %args, + n_index_cols => $n_index_cols, + ); + + my $sql = $self->_make_range_query( + %args, + n_index_cols => $n_index_cols, + vals => $vals, + ); + my $sth = $cxn->dbh()->prepare($sql); + PTDEBUG && _d($sth->{Statement}, 'params:', @$vals); + $sth->execute(@$vals); + my $row = $sth->fetchrow_hashref(); + $sth->finish(); + PTDEBUG && _d('Range scan:', Dumper($row)); + return $row->{key_len}, $row->{key}; +} + +sub _get_first_values { + my ($self, %args) = @_; + my @required_args = qw(Cxn tbl index n_index_cols); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args}; + + my $q = $self->{Quoter}; + + my $index_struct = $tbl->{tbl_struct}->{keys}->{$index}; + my $index_cols = $index_struct->{cols}; + my $index_columns = join (', ', + map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]); + + my @where; + foreach my $col ( @{$index_cols}[0..($n_index_cols - 1)] ) { + push @where, $q->quote($col) . " IS NOT NULL" + } + + my $sql = "SELECT /*!40001 SQL_NO_CACHE */ $index_columns " + . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") " + . "WHERE " . join(' AND ', @where) + . " ORDER BY $index_columns " + . "LIMIT 1 /*key_len*/"; # only need 1 row + PTDEBUG && _d($sql); + my $vals = $cxn->dbh()->selectrow_arrayref($sql); + return $vals; +} + +sub _make_range_query { + my ($self, %args) = @_; + my @required_args = qw(tbl index n_index_cols vals); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($tbl, $index, $n_index_cols, $vals) = @args{@required_args}; + + my $q = $self->{Quoter}; + + my $index_struct = $tbl->{tbl_struct}->{keys}->{$index}; + my $index_cols = $index_struct->{cols}; + + my @where; + if ( $n_index_cols > 1 ) { + foreach my $n ( 0..($n_index_cols - 2) ) { + my $col = $index_cols->[$n]; + my $val = $vals->[$n]; + push @where, $q->quote($col) . " = ?"; + } + } + + my $col = $index_cols->[$n_index_cols - 1]; + my $val = $vals->[-1]; # should only be as many vals as cols + push @where, $q->quote($col) . " >= ?"; + + my $sql = "EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * " + . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") " + . "WHERE " . join(' AND ', @where) + . " /*key_len*/"; + return $sql; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; +} +# ########################################################################### +# End IndexLength package +# ########################################################################### + # ########################################################################### # This is a combination of modules and programs in one -- a runnable module. # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last @@ -5908,30 +6060,30 @@ sub main { } else { # chunking the table if ( $o->get('check-plan') ) { - my $expl = explain_statement( - sth => $statements->{explain_first_lower_boundary}, - tbl => $tbl, - vals => [], + my $idx_len = new IndexLength(Quoter => $q); + my ($key_len, $key) = $idx_len->index_length( + Cxn => $args{Cxn}, + tbl => $tbl, + index => $nibble_iter->nibble_index(), + n_index_cols => $o->get('chunk-index-columns'), ); - if ( !$expl->{key} - || lc($expl->{key}) ne lc($nibble_iter->nibble_index()) ) - { + if ( !$key || lc($key) ne lc($nibble_iter->nibble_index()) ) { die "Cannot determine the key_len of the chunk index " . "because MySQL chose " - . ($expl->{key} ? "the $expl->{key}" : "no") . " index " + . ($key ? "the $key" : "no") . " index " . "instead of the " . $nibble_iter->nibble_index() . " index for the first lower boundary statement. " . "See --[no]check-plan in the documentation for more " . "information."; } - elsif ( !$expl->{key_len} ) { - die "The key_len of the $expl->{key} index is " - . (defined $expl->{key_len} ? "zero" : "NULL") + elsif ( !$key_len ) { + die "The key_len of the $key index is " + . (defined $key_len ? "zero" : "NULL") . ", but this should not be possible. " . "See --[no]check-plan in the documentation for more " . "information."; } - $tbl->{key_len} = $expl->{key_len}; + $tbl->{key_len} = $key_len; } } diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 17f964b8..6f052fd4 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -6080,6 +6080,158 @@ sub _d { # End WeightedAvgRate package # ########################################################################### +# ########################################################################### +# IndexLength package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/IndexLength.pm +# t/lib/IndexLength.t +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### +{ + +package IndexLength; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use constant PTDEBUG => $ENV{PTDEBUG} || 0; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +sub new { + my ( $class, %args ) = @_; + my @required_args = qw(Quoter); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + + my $self = { + Quoter => $args{Quoter}, + }; + + return bless $self, $class; +} + +sub index_length { + my ($self, %args) = @_; + my @required_args = qw(Cxn tbl index); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($cxn) = @args{@required_args}; + + die "The tbl argument does not have a tbl_struct" + unless exists $args{tbl}->{tbl_struct}; + die "Index $args{index} does not exist in table $args{tbl}->{name}" + unless $args{tbl}->{tbl_struct}->{keys}->{$args{index}}; + + my $index_struct = $args{tbl}->{tbl_struct}->{keys}->{$args{index}}; + my $index_cols = $index_struct->{cols}; + my $n_index_cols = $args{n_index_cols}; + if ( !$n_index_cols || $n_index_cols > @$index_cols ) { + $n_index_cols = scalar @$index_cols; + } + + my $vals = $self->_get_first_values( + %args, + n_index_cols => $n_index_cols, + ); + + my $sql = $self->_make_range_query( + %args, + n_index_cols => $n_index_cols, + vals => $vals, + ); + my $sth = $cxn->dbh()->prepare($sql); + PTDEBUG && _d($sth->{Statement}, 'params:', @$vals); + $sth->execute(@$vals); + my $row = $sth->fetchrow_hashref(); + $sth->finish(); + PTDEBUG && _d('Range scan:', Dumper($row)); + return $row->{key_len}, $row->{key}; +} + +sub _get_first_values { + my ($self, %args) = @_; + my @required_args = qw(Cxn tbl index n_index_cols); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args}; + + my $q = $self->{Quoter}; + + my $index_struct = $tbl->{tbl_struct}->{keys}->{$index}; + my $index_cols = $index_struct->{cols}; + my $index_columns = join (', ', + map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]); + + my @where; + foreach my $col ( @{$index_cols}[0..($n_index_cols - 1)] ) { + push @where, $q->quote($col) . " IS NOT NULL" + } + + my $sql = "SELECT /*!40001 SQL_NO_CACHE */ $index_columns " + . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") " + . "WHERE " . join(' AND ', @where) + . " ORDER BY $index_columns " + . "LIMIT 1 /*key_len*/"; # only need 1 row + PTDEBUG && _d($sql); + my $vals = $cxn->dbh()->selectrow_arrayref($sql); + return $vals; +} + +sub _make_range_query { + my ($self, %args) = @_; + my @required_args = qw(tbl index n_index_cols vals); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($tbl, $index, $n_index_cols, $vals) = @args{@required_args}; + + my $q = $self->{Quoter}; + + my $index_struct = $tbl->{tbl_struct}->{keys}->{$index}; + my $index_cols = $index_struct->{cols}; + + my @where; + if ( $n_index_cols > 1 ) { + foreach my $n ( 0..($n_index_cols - 2) ) { + my $col = $index_cols->[$n]; + my $val = $vals->[$n]; + push @where, $q->quote($col) . " = ?"; + } + } + + my $col = $index_cols->[$n_index_cols - 1]; + my $val = $vals->[-1]; # should only be as many vals as cols + push @where, $q->quote($col) . " >= ?"; + + my $sql = "EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * " + . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") " + . "WHERE " . join(' AND ', @where) + . " /*key_len*/"; + return $sql; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; +} +# ########################################################################### +# End IndexLength package +# ########################################################################### + # ########################################################################### # This is a combination of modules and programs in one -- a runnable module. # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last @@ -6748,30 +6900,30 @@ sub main { } else { # chunking the table if ( $o->get('check-plan') ) { - my $expl = explain_statement( - sth => $statements->{explain_first_lower_boundary}, - tbl => $tbl, - vals => [], + my $idx_len = new IndexLength(Quoter => $q); + my ($key_len, $key) = $idx_len->index_length( + Cxn => $args{Cxn}, + tbl => $tbl, + index => $nibble_iter->nibble_index(), + n_index_cols => $o->get('chunk-index-columns'), ); - if ( !$expl->{key} - || lc($expl->{key}) ne lc($nibble_iter->nibble_index()) ) - { + if ( !$key || lc($key) ne lc($nibble_iter->nibble_index()) ) { die "Cannot determine the key_len of the chunk index " . "because MySQL chose " - . ($expl->{key} ? "the $expl->{key}" : "no") . " index " + . ($key ? "the $key" : "no") . " index " . "instead of the " . $nibble_iter->nibble_index() . " index for the first lower boundary statement. " . "See --[no]check-plan in the documentation for more " . "information."; } - elsif ( !$expl->{key_len} ) { - die "The key_len of the $expl->{key} index is " - . (defined $expl->{key_len} ? "zero" : "NULL") + elsif ( !$key_len ) { + die "The key_len of the $key index is " + . (defined $key_len ? "zero" : "NULL") . ", but this should not be possible. " . "See --[no]check-plan in the documentation for more " . "information."; } - $tbl->{key_len} = $expl->{key_len}; + $tbl->{key_len} = $key_len; } } diff --git a/lib/IndexLength.pm b/lib/IndexLength.pm new file mode 100644 index 00000000..15e65164 --- /dev/null +++ b/lib/IndexLength.pm @@ -0,0 +1,175 @@ +# This program is copyright 2012 Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# ########################################################################### +# IndexLength package +# ########################################################################### +{ +# Package: IndexLength +# IndexLength get the key_len of a index. + +package IndexLength; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use constant PTDEBUG => $ENV{PTDEBUG} || 0; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +sub new { + my ( $class, %args ) = @_; + my @required_args = qw(Quoter); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + + my $self = { + Quoter => $args{Quoter}, + }; + + return bless $self, $class; +} + +# Returns the length of the index in bytes using only +# the first N left-most columns of the index. +sub index_length { + my ($self, %args) = @_; + my @required_args = qw(Cxn tbl index); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($cxn) = @args{@required_args}; + + die "The tbl argument does not have a tbl_struct" + unless exists $args{tbl}->{tbl_struct}; + die "Index $args{index} does not exist in table $args{tbl}->{name}" + unless $args{tbl}->{tbl_struct}->{keys}->{$args{index}}; + + my $index_struct = $args{tbl}->{tbl_struct}->{keys}->{$args{index}}; + my $index_cols = $index_struct->{cols}; + my $n_index_cols = $args{n_index_cols}; + if ( !$n_index_cols || $n_index_cols > @$index_cols ) { + $n_index_cols = scalar @$index_cols; + } + + # Get the first row with non-NULL values. + my $vals = $self->_get_first_values( + %args, + n_index_cols => $n_index_cols, + ); + + # Make an EXPLAIN query to scan the range and execute it. + my $sql = $self->_make_range_query( + %args, + n_index_cols => $n_index_cols, + vals => $vals, + ); + my $sth = $cxn->dbh()->prepare($sql); + PTDEBUG && _d($sth->{Statement}, 'params:', @$vals); + $sth->execute(@$vals); + my $row = $sth->fetchrow_hashref(); + $sth->finish(); + PTDEBUG && _d('Range scan:', Dumper($row)); + return $row->{key_len}, $row->{key}; +} + +sub _get_first_values { + my ($self, %args) = @_; + my @required_args = qw(Cxn tbl index n_index_cols); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args}; + + my $q = $self->{Quoter}; + + # Select just the index columns. + my $index_struct = $tbl->{tbl_struct}->{keys}->{$index}; + my $index_cols = $index_struct->{cols}; + my $index_columns = join (', ', + map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]); + + # Where no index column is null, because we can't > NULL. + my @where; + foreach my $col ( @{$index_cols}[0..($n_index_cols - 1)] ) { + push @where, $q->quote($col) . " IS NOT NULL" + } + + my $sql = "SELECT /*!40001 SQL_NO_CACHE */ $index_columns " + . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") " + . "WHERE " . join(' AND ', @where) + . " ORDER BY $index_columns " + . "LIMIT 1 /*key_len*/"; # only need 1 row + PTDEBUG && _d($sql); + my $vals = $cxn->dbh()->selectrow_arrayref($sql); + return $vals; +} + +sub _make_range_query { + my ($self, %args) = @_; + my @required_args = qw(tbl index n_index_cols vals); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + my ($tbl, $index, $n_index_cols, $vals) = @args{@required_args}; + + my $q = $self->{Quoter}; + + my $index_struct = $tbl->{tbl_struct}->{keys}->{$index}; + my $index_cols = $index_struct->{cols}; + + # All but the last index col = val. + my @where; + if ( $n_index_cols > 1 ) { + # -1 for zero-index array as usual, then -1 again because + # we don't want the last column; that's added below. + foreach my $n ( 0..($n_index_cols - 2) ) { + my $col = $index_cols->[$n]; + my $val = $vals->[$n]; + push @where, $q->quote($col) . " = ?"; + } + } + + # The last index col > val. This causes the range scan using just + # the N left-most index columns. + my $col = $index_cols->[$n_index_cols - 1]; + my $val = $vals->[-1]; # should only be as many vals as cols + push @where, $q->quote($col) . " >= ?"; + + my $sql = "EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * " + . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") " + . "WHERE " . join(' AND ', @where) + . " /*key_len*/"; + return $sql; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; +} +# ########################################################################### +# End IndexLength package +# ########################################################################### diff --git a/t/lib/IndexLength.pm b/t/lib/IndexLength.pm new file mode 100644 index 00000000..a61644ba --- /dev/null +++ b/t/lib/IndexLength.pm @@ -0,0 +1,135 @@ +#!/usr/bin/perl + +BEGIN { + die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" + unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; + unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; +}; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Test::More; + +use PerconaTest; +use DSNParser; +use Sandbox; + +use Cxn; +use Quoter; +use TableParser; +use OptionParser; +use IndexLength; + +use constant PTDEBUG => $ENV{PTDEBUG} || 0; +use constant PTDEVDEBUG => $ENV{PTDEBUG} || 0; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +my $dp = new DSNParser(opts=>$dsn_opts); +my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); +my $dbh = $sb->get_dbh_for('master'); + +if ( !$dbh ) { + plan skip_all => 'Cannot connect to sandbox master'; +} +else { + plan tests => 7; +} + +my $output; +my $q = new Quoter(); +my $tp = new TableParser(Quoter => $q); +my $il = new IndexLength(Quoter => $q); +my $o = new OptionParser(description => 'IndexLength'); +$o->get_specs("$trunk/bin/pt-table-checksum"); +my $cxn = new Cxn( + dbh => $dbh, + dsn => { h=>'127.1', P=>'12345', n=>'h=127.1,P=12345' }, + DSNParser => $dp, + OptionParser => $o, +); + +sub test_index_len { + my (%args) = @_; + my @required_args = qw(name tbl index len); + foreach my $arg ( @required_args ) { + die "I need a $arg argument" unless $args{$arg}; + } + + my ($len, $key) = $il->index_length( + Cxn => $cxn, + tbl => $args{tbl}, + index => $args{index}, + n_index_cols => $args{n_index_cols}, + ); + + is( + $len, + $args{len}, + "$args{name}" + ); +} + +# ############################################################################# +# bad_plan, PK with 4 cols +# ############################################################################# +$sb->load_file('master', "t/pt-table-checksum/samples/bad-plan-bug-1010232.sql"); +my $tbl_struct = $tp->parse( + $tp->get_create_table($dbh, 'bad_plan', 't')); +my $tbl = { + name => $q->quote('bad_plan', 't'), + tbl_struct => $tbl_struct, +}; + +for my $n ( 1..4 ) { + my $len = $n * 2 + ($n >= 2 ? 1 : 0); + test_index_len( + name => "bad_plan.t $n cols = $len bytes", + tbl => $tbl, + index => "PRIMARY", + n_index_cols => $n, + len => $len, + ); +} + +# ############################################################################# +# Some sakila tables +# ############################################################################# +$tbl_struct = $tp->parse( + $tp->get_create_table($dbh, 'sakila', 'film_actor')); +$tbl = { + name => $q->quote('sakila', 'film_actor'), + tbl_struct => $tbl_struct, +}; + +test_index_len( + name => "sakila.film_actor 1 col = 2 bytes", + tbl => $tbl, + index => "PRIMARY", + n_index_cols => 1, + len => 2, +); + +# ############################################################################# +# Use full index if no n_index_cols +# ############################################################################# + +# Use sakila.film_actor stuff from previous tests. + +test_index_len( + name => "sakila.film_actor all cols = 4 bytes", + tbl => $tbl, + index => "PRIMARY", + len => 4, +); + +# ############################################################################# +# Done. +# ############################################################################# +$sb->wipe_clean($dbh); +ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); +exit; diff --git a/t/pt-table-checksum/chunk_index.t b/t/pt-table-checksum/chunk_index.t index 85a2575d..fbc46f28 100644 --- a/t/pt-table-checksum/chunk_index.t +++ b/t/pt-table-checksum/chunk_index.t @@ -25,7 +25,7 @@ if ( !$dbh ) { plan skip_all => 'Cannot connect to sandbox master'; } else { - plan tests => 16; + plan tests => 17; } # The sandbox servers run with lock_wait_timeout=3 and it's not dynamic @@ -175,7 +175,7 @@ is( $exit_status, 0, "Bad key_len chunks are not errors" -); +) or diag($output); cmp_ok( PerconaTest::count_checksum_results($output, 'skipped'), @@ -205,19 +205,40 @@ $output = output( sub { $exit_status = pt_table_checksum::main( $master_dsn, '--max-load', '', - qw(--lock-wait-timeout 3 --chunk-size 5000 -t sakila.rental), - qw(--chunk-index rental_date --chunk-index-columns 5), - qw(--explain --explain)); + qw(--lock-wait-timeout 3 --chunk-size 1000 -t sakila.film_actor), + qw(--chunk-index PRIMARY --chunk-index-columns 9), + ); }, stderr => 1, ); is( - $exit_status, - 0, + PerconaTest::count_checksum_results($output, 'rows'), + 5462, "--chunk-index-columns > number of index columns" +) or diag($output); + +$output = output( + sub { + $exit_status = pt_table_checksum::main( + $master_dsn, '--max-load', '', + qw(--lock-wait-timeout 3 --chunk-size 1000 -t sakila.film_actor), + qw(--chunk-index-columns 1 --chunk-size-limit 3), + ); + }, + stderr => 1, ); +# Since we're not using the full index, it's basically a non-unique index, +# so there are dupes. The table really has 5462 rows, so we must get +# at least that many, and probably a few more. +cmp_ok( + PerconaTest::count_checksum_results($output, 'rows'), + '>=', + 5462, + "Initial key_len reflects --chunk-index-columns" +) or diag($output); + # ############################################################################# # Done. # #############################################################################