Merge lp:~percona-toolkit-dev/percona-toolkit/detect-key-len-with-range-scan into lp:percona-toolkit/2.1

Proposed by Daniel Nichter
Status: Merged
Merged at revision: 284
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/detect-key-len-with-range-scan
Merge into: lp:percona-toolkit/2.1
Diff against target: 799 lines (+666/-31)
5 files modified
bin/pt-online-schema-change (+164/-12)
bin/pt-table-checksum (+164/-12)
lib/IndexLength.pm (+175/-0)
t/lib/IndexLength.pm (+135/-0)
t/pt-table-checksum/chunk_index.t (+28/-7)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/detect-key-len-with-range-scan
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+109679@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/pt-online-schema-change'
2--- bin/pt-online-schema-change 2012-06-11 12:15:48 +0000
3+++ bin/pt-online-schema-change 2012-06-11 16:59:23 +0000
4@@ -5128,6 +5128,158 @@
5 # ###########################################################################
6
7 # ###########################################################################
8+# IndexLength package
9+# This package is a copy without comments from the original. The original
10+# with comments and its test file can be found in the Bazaar repository at,
11+# lib/IndexLength.pm
12+# t/lib/IndexLength.t
13+# See https://launchpad.net/percona-toolkit for more information.
14+# ###########################################################################
15+{
16+
17+package IndexLength;
18+
19+use strict;
20+use warnings FATAL => 'all';
21+use English qw(-no_match_vars);
22+use constant PTDEBUG => $ENV{PTDEBUG} || 0;
23+
24+use Data::Dumper;
25+$Data::Dumper::Indent = 1;
26+$Data::Dumper::Sortkeys = 1;
27+$Data::Dumper::Quotekeys = 0;
28+
29+sub new {
30+ my ( $class, %args ) = @_;
31+ my @required_args = qw(Quoter);
32+ foreach my $arg ( @required_args ) {
33+ die "I need a $arg argument" unless $args{$arg};
34+ }
35+
36+ my $self = {
37+ Quoter => $args{Quoter},
38+ };
39+
40+ return bless $self, $class;
41+}
42+
43+sub index_length {
44+ my ($self, %args) = @_;
45+ my @required_args = qw(Cxn tbl index);
46+ foreach my $arg ( @required_args ) {
47+ die "I need a $arg argument" unless $args{$arg};
48+ }
49+ my ($cxn) = @args{@required_args};
50+
51+ die "The tbl argument does not have a tbl_struct"
52+ unless exists $args{tbl}->{tbl_struct};
53+ die "Index $args{index} does not exist in table $args{tbl}->{name}"
54+ unless $args{tbl}->{tbl_struct}->{keys}->{$args{index}};
55+
56+ my $index_struct = $args{tbl}->{tbl_struct}->{keys}->{$args{index}};
57+ my $index_cols = $index_struct->{cols};
58+ my $n_index_cols = $args{n_index_cols};
59+ if ( !$n_index_cols || $n_index_cols > @$index_cols ) {
60+ $n_index_cols = scalar @$index_cols;
61+ }
62+
63+ my $vals = $self->_get_first_values(
64+ %args,
65+ n_index_cols => $n_index_cols,
66+ );
67+
68+ my $sql = $self->_make_range_query(
69+ %args,
70+ n_index_cols => $n_index_cols,
71+ vals => $vals,
72+ );
73+ my $sth = $cxn->dbh()->prepare($sql);
74+ PTDEBUG && _d($sth->{Statement}, 'params:', @$vals);
75+ $sth->execute(@$vals);
76+ my $row = $sth->fetchrow_hashref();
77+ $sth->finish();
78+ PTDEBUG && _d('Range scan:', Dumper($row));
79+ return $row->{key_len}, $row->{key};
80+}
81+
82+sub _get_first_values {
83+ my ($self, %args) = @_;
84+ my @required_args = qw(Cxn tbl index n_index_cols);
85+ foreach my $arg ( @required_args ) {
86+ die "I need a $arg argument" unless $args{$arg};
87+ }
88+ my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args};
89+
90+ my $q = $self->{Quoter};
91+
92+ my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
93+ my $index_cols = $index_struct->{cols};
94+ my $index_columns = join (', ',
95+ map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]);
96+
97+ my @where;
98+ foreach my $col ( @{$index_cols}[0..($n_index_cols - 1)] ) {
99+ push @where, $q->quote($col) . " IS NOT NULL"
100+ }
101+
102+ my $sql = "SELECT /*!40001 SQL_NO_CACHE */ $index_columns "
103+ . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") "
104+ . "WHERE " . join(' AND ', @where)
105+ . " ORDER BY $index_columns "
106+ . "LIMIT 1 /*key_len*/"; # only need 1 row
107+ PTDEBUG && _d($sql);
108+ my $vals = $cxn->dbh()->selectrow_arrayref($sql);
109+ return $vals;
110+}
111+
112+sub _make_range_query {
113+ my ($self, %args) = @_;
114+ my @required_args = qw(tbl index n_index_cols vals);
115+ foreach my $arg ( @required_args ) {
116+ die "I need a $arg argument" unless $args{$arg};
117+ }
118+ my ($tbl, $index, $n_index_cols, $vals) = @args{@required_args};
119+
120+ my $q = $self->{Quoter};
121+
122+ my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
123+ my $index_cols = $index_struct->{cols};
124+
125+ my @where;
126+ if ( $n_index_cols > 1 ) {
127+ foreach my $n ( 0..($n_index_cols - 2) ) {
128+ my $col = $index_cols->[$n];
129+ my $val = $vals->[$n];
130+ push @where, $q->quote($col) . " = ?";
131+ }
132+ }
133+
134+ my $col = $index_cols->[$n_index_cols - 1];
135+ my $val = $vals->[-1]; # should only be as many vals as cols
136+ push @where, $q->quote($col) . " >= ?";
137+
138+ my $sql = "EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * "
139+ . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") "
140+ . "WHERE " . join(' AND ', @where)
141+ . " /*key_len*/";
142+ return $sql;
143+}
144+
145+sub _d {
146+ my ($package, undef, $line) = caller 0;
147+ @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
148+ map { defined $_ ? $_ : 'undef' }
149+ @_;
150+ print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
151+}
152+
153+1;
154+}
155+# ###########################################################################
156+# End IndexLength package
157+# ###########################################################################
158+
159+# ###########################################################################
160 # This is a combination of modules and programs in one -- a runnable module.
161 # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
162 # Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition.
163@@ -5908,30 +6060,30 @@
164 }
165 else { # chunking the table
166 if ( $o->get('check-plan') ) {
167- my $expl = explain_statement(
168- sth => $statements->{explain_first_lower_boundary},
169- tbl => $tbl,
170- vals => [],
171+ my $idx_len = new IndexLength(Quoter => $q);
172+ my ($key_len, $key) = $idx_len->index_length(
173+ Cxn => $args{Cxn},
174+ tbl => $tbl,
175+ index => $nibble_iter->nibble_index(),
176+ n_index_cols => $o->get('chunk-index-columns'),
177 );
178- if ( !$expl->{key}
179- || lc($expl->{key}) ne lc($nibble_iter->nibble_index()) )
180- {
181+ if ( !$key || lc($key) ne lc($nibble_iter->nibble_index()) ) {
182 die "Cannot determine the key_len of the chunk index "
183 . "because MySQL chose "
184- . ($expl->{key} ? "the $expl->{key}" : "no") . " index "
185+ . ($key ? "the $key" : "no") . " index "
186 . "instead of the " . $nibble_iter->nibble_index()
187 . " index for the first lower boundary statement. "
188 . "See --[no]check-plan in the documentation for more "
189 . "information.";
190 }
191- elsif ( !$expl->{key_len} ) {
192- die "The key_len of the $expl->{key} index is "
193- . (defined $expl->{key_len} ? "zero" : "NULL")
194+ elsif ( !$key_len ) {
195+ die "The key_len of the $key index is "
196+ . (defined $key_len ? "zero" : "NULL")
197 . ", but this should not be possible. "
198 . "See --[no]check-plan in the documentation for more "
199 . "information.";
200 }
201- $tbl->{key_len} = $expl->{key_len};
202+ $tbl->{key_len} = $key_len;
203 }
204 }
205
206
207=== modified file 'bin/pt-table-checksum'
208--- bin/pt-table-checksum 2012-06-11 12:15:48 +0000
209+++ bin/pt-table-checksum 2012-06-11 16:59:23 +0000
210@@ -6081,6 +6081,158 @@
211 # ###########################################################################
212
213 # ###########################################################################
214+# IndexLength package
215+# This package is a copy without comments from the original. The original
216+# with comments and its test file can be found in the Bazaar repository at,
217+# lib/IndexLength.pm
218+# t/lib/IndexLength.t
219+# See https://launchpad.net/percona-toolkit for more information.
220+# ###########################################################################
221+{
222+
223+package IndexLength;
224+
225+use strict;
226+use warnings FATAL => 'all';
227+use English qw(-no_match_vars);
228+use constant PTDEBUG => $ENV{PTDEBUG} || 0;
229+
230+use Data::Dumper;
231+$Data::Dumper::Indent = 1;
232+$Data::Dumper::Sortkeys = 1;
233+$Data::Dumper::Quotekeys = 0;
234+
235+sub new {
236+ my ( $class, %args ) = @_;
237+ my @required_args = qw(Quoter);
238+ foreach my $arg ( @required_args ) {
239+ die "I need a $arg argument" unless $args{$arg};
240+ }
241+
242+ my $self = {
243+ Quoter => $args{Quoter},
244+ };
245+
246+ return bless $self, $class;
247+}
248+
249+sub index_length {
250+ my ($self, %args) = @_;
251+ my @required_args = qw(Cxn tbl index);
252+ foreach my $arg ( @required_args ) {
253+ die "I need a $arg argument" unless $args{$arg};
254+ }
255+ my ($cxn) = @args{@required_args};
256+
257+ die "The tbl argument does not have a tbl_struct"
258+ unless exists $args{tbl}->{tbl_struct};
259+ die "Index $args{index} does not exist in table $args{tbl}->{name}"
260+ unless $args{tbl}->{tbl_struct}->{keys}->{$args{index}};
261+
262+ my $index_struct = $args{tbl}->{tbl_struct}->{keys}->{$args{index}};
263+ my $index_cols = $index_struct->{cols};
264+ my $n_index_cols = $args{n_index_cols};
265+ if ( !$n_index_cols || $n_index_cols > @$index_cols ) {
266+ $n_index_cols = scalar @$index_cols;
267+ }
268+
269+ my $vals = $self->_get_first_values(
270+ %args,
271+ n_index_cols => $n_index_cols,
272+ );
273+
274+ my $sql = $self->_make_range_query(
275+ %args,
276+ n_index_cols => $n_index_cols,
277+ vals => $vals,
278+ );
279+ my $sth = $cxn->dbh()->prepare($sql);
280+ PTDEBUG && _d($sth->{Statement}, 'params:', @$vals);
281+ $sth->execute(@$vals);
282+ my $row = $sth->fetchrow_hashref();
283+ $sth->finish();
284+ PTDEBUG && _d('Range scan:', Dumper($row));
285+ return $row->{key_len}, $row->{key};
286+}
287+
288+sub _get_first_values {
289+ my ($self, %args) = @_;
290+ my @required_args = qw(Cxn tbl index n_index_cols);
291+ foreach my $arg ( @required_args ) {
292+ die "I need a $arg argument" unless $args{$arg};
293+ }
294+ my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args};
295+
296+ my $q = $self->{Quoter};
297+
298+ my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
299+ my $index_cols = $index_struct->{cols};
300+ my $index_columns = join (', ',
301+ map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]);
302+
303+ my @where;
304+ foreach my $col ( @{$index_cols}[0..($n_index_cols - 1)] ) {
305+ push @where, $q->quote($col) . " IS NOT NULL"
306+ }
307+
308+ my $sql = "SELECT /*!40001 SQL_NO_CACHE */ $index_columns "
309+ . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") "
310+ . "WHERE " . join(' AND ', @where)
311+ . " ORDER BY $index_columns "
312+ . "LIMIT 1 /*key_len*/"; # only need 1 row
313+ PTDEBUG && _d($sql);
314+ my $vals = $cxn->dbh()->selectrow_arrayref($sql);
315+ return $vals;
316+}
317+
318+sub _make_range_query {
319+ my ($self, %args) = @_;
320+ my @required_args = qw(tbl index n_index_cols vals);
321+ foreach my $arg ( @required_args ) {
322+ die "I need a $arg argument" unless $args{$arg};
323+ }
324+ my ($tbl, $index, $n_index_cols, $vals) = @args{@required_args};
325+
326+ my $q = $self->{Quoter};
327+
328+ my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
329+ my $index_cols = $index_struct->{cols};
330+
331+ my @where;
332+ if ( $n_index_cols > 1 ) {
333+ foreach my $n ( 0..($n_index_cols - 2) ) {
334+ my $col = $index_cols->[$n];
335+ my $val = $vals->[$n];
336+ push @where, $q->quote($col) . " = ?";
337+ }
338+ }
339+
340+ my $col = $index_cols->[$n_index_cols - 1];
341+ my $val = $vals->[-1]; # should only be as many vals as cols
342+ push @where, $q->quote($col) . " >= ?";
343+
344+ my $sql = "EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * "
345+ . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") "
346+ . "WHERE " . join(' AND ', @where)
347+ . " /*key_len*/";
348+ return $sql;
349+}
350+
351+sub _d {
352+ my ($package, undef, $line) = caller 0;
353+ @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
354+ map { defined $_ ? $_ : 'undef' }
355+ @_;
356+ print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
357+}
358+
359+1;
360+}
361+# ###########################################################################
362+# End IndexLength package
363+# ###########################################################################
364+
365+# ###########################################################################
366 # This is a combination of modules and programs in one -- a runnable module.
367 # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
368 # Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition.
369@@ -6748,30 +6900,30 @@
370 }
371 else { # chunking the table
372 if ( $o->get('check-plan') ) {
373- my $expl = explain_statement(
374- sth => $statements->{explain_first_lower_boundary},
375- tbl => $tbl,
376- vals => [],
377+ my $idx_len = new IndexLength(Quoter => $q);
378+ my ($key_len, $key) = $idx_len->index_length(
379+ Cxn => $args{Cxn},
380+ tbl => $tbl,
381+ index => $nibble_iter->nibble_index(),
382+ n_index_cols => $o->get('chunk-index-columns'),
383 );
384- if ( !$expl->{key}
385- || lc($expl->{key}) ne lc($nibble_iter->nibble_index()) )
386- {
387+ if ( !$key || lc($key) ne lc($nibble_iter->nibble_index()) ) {
388 die "Cannot determine the key_len of the chunk index "
389 . "because MySQL chose "
390- . ($expl->{key} ? "the $expl->{key}" : "no") . " index "
391+ . ($key ? "the $key" : "no") . " index "
392 . "instead of the " . $nibble_iter->nibble_index()
393 . " index for the first lower boundary statement. "
394 . "See --[no]check-plan in the documentation for more "
395 . "information.";
396 }
397- elsif ( !$expl->{key_len} ) {
398- die "The key_len of the $expl->{key} index is "
399- . (defined $expl->{key_len} ? "zero" : "NULL")
400+ elsif ( !$key_len ) {
401+ die "The key_len of the $key index is "
402+ . (defined $key_len ? "zero" : "NULL")
403 . ", but this should not be possible. "
404 . "See --[no]check-plan in the documentation for more "
405 . "information.";
406 }
407- $tbl->{key_len} = $expl->{key_len};
408+ $tbl->{key_len} = $key_len;
409 }
410 }
411
412
413=== added file 'lib/IndexLength.pm'
414--- lib/IndexLength.pm 1970-01-01 00:00:00 +0000
415+++ lib/IndexLength.pm 2012-06-11 16:59:23 +0000
416@@ -0,0 +1,175 @@
417+# This program is copyright 2012 Percona Inc.
418+# Feedback and improvements are welcome.
419+#
420+# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
421+# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
422+# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
423+#
424+# This program is free software; you can redistribute it and/or modify it under
425+# the terms of the GNU General Public License as published by the Free Software
426+# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
427+# systems, you can issue `man perlgpl' or `man perlartistic' to read these
428+# licenses.
429+#
430+# You should have received a copy of the GNU General Public License along with
431+# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
432+# Place, Suite 330, Boston, MA 02111-1307 USA.
433+# ###########################################################################
434+# IndexLength package
435+# ###########################################################################
436+{
437+# Package: IndexLength
438+# IndexLength get the key_len of a index.
439+
440+package IndexLength;
441+
442+use strict;
443+use warnings FATAL => 'all';
444+use English qw(-no_match_vars);
445+use constant PTDEBUG => $ENV{PTDEBUG} || 0;
446+
447+use Data::Dumper;
448+$Data::Dumper::Indent = 1;
449+$Data::Dumper::Sortkeys = 1;
450+$Data::Dumper::Quotekeys = 0;
451+
452+sub new {
453+ my ( $class, %args ) = @_;
454+ my @required_args = qw(Quoter);
455+ foreach my $arg ( @required_args ) {
456+ die "I need a $arg argument" unless $args{$arg};
457+ }
458+
459+ my $self = {
460+ Quoter => $args{Quoter},
461+ };
462+
463+ return bless $self, $class;
464+}
465+
466+# Returns the length of the index in bytes using only
467+# the first N left-most columns of the index.
468+sub index_length {
469+ my ($self, %args) = @_;
470+ my @required_args = qw(Cxn tbl index);
471+ foreach my $arg ( @required_args ) {
472+ die "I need a $arg argument" unless $args{$arg};
473+ }
474+ my ($cxn) = @args{@required_args};
475+
476+ die "The tbl argument does not have a tbl_struct"
477+ unless exists $args{tbl}->{tbl_struct};
478+ die "Index $args{index} does not exist in table $args{tbl}->{name}"
479+ unless $args{tbl}->{tbl_struct}->{keys}->{$args{index}};
480+
481+ my $index_struct = $args{tbl}->{tbl_struct}->{keys}->{$args{index}};
482+ my $index_cols = $index_struct->{cols};
483+ my $n_index_cols = $args{n_index_cols};
484+ if ( !$n_index_cols || $n_index_cols > @$index_cols ) {
485+ $n_index_cols = scalar @$index_cols;
486+ }
487+
488+ # Get the first row with non-NULL values.
489+ my $vals = $self->_get_first_values(
490+ %args,
491+ n_index_cols => $n_index_cols,
492+ );
493+
494+ # Make an EXPLAIN query to scan the range and execute it.
495+ my $sql = $self->_make_range_query(
496+ %args,
497+ n_index_cols => $n_index_cols,
498+ vals => $vals,
499+ );
500+ my $sth = $cxn->dbh()->prepare($sql);
501+ PTDEBUG && _d($sth->{Statement}, 'params:', @$vals);
502+ $sth->execute(@$vals);
503+ my $row = $sth->fetchrow_hashref();
504+ $sth->finish();
505+ PTDEBUG && _d('Range scan:', Dumper($row));
506+ return $row->{key_len}, $row->{key};
507+}
508+
509+sub _get_first_values {
510+ my ($self, %args) = @_;
511+ my @required_args = qw(Cxn tbl index n_index_cols);
512+ foreach my $arg ( @required_args ) {
513+ die "I need a $arg argument" unless $args{$arg};
514+ }
515+ my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args};
516+
517+ my $q = $self->{Quoter};
518+
519+ # Select just the index columns.
520+ my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
521+ my $index_cols = $index_struct->{cols};
522+ my $index_columns = join (', ',
523+ map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]);
524+
525+ # Where no index column is null, because we can't > NULL.
526+ my @where;
527+ foreach my $col ( @{$index_cols}[0..($n_index_cols - 1)] ) {
528+ push @where, $q->quote($col) . " IS NOT NULL"
529+ }
530+
531+ my $sql = "SELECT /*!40001 SQL_NO_CACHE */ $index_columns "
532+ . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") "
533+ . "WHERE " . join(' AND ', @where)
534+ . " ORDER BY $index_columns "
535+ . "LIMIT 1 /*key_len*/"; # only need 1 row
536+ PTDEBUG && _d($sql);
537+ my $vals = $cxn->dbh()->selectrow_arrayref($sql);
538+ return $vals;
539+}
540+
541+sub _make_range_query {
542+ my ($self, %args) = @_;
543+ my @required_args = qw(tbl index n_index_cols vals);
544+ foreach my $arg ( @required_args ) {
545+ die "I need a $arg argument" unless $args{$arg};
546+ }
547+ my ($tbl, $index, $n_index_cols, $vals) = @args{@required_args};
548+
549+ my $q = $self->{Quoter};
550+
551+ my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
552+ my $index_cols = $index_struct->{cols};
553+
554+ # All but the last index col = val.
555+ my @where;
556+ if ( $n_index_cols > 1 ) {
557+ # -1 for zero-index array as usual, then -1 again because
558+ # we don't want the last column; that's added below.
559+ foreach my $n ( 0..($n_index_cols - 2) ) {
560+ my $col = $index_cols->[$n];
561+ my $val = $vals->[$n];
562+ push @where, $q->quote($col) . " = ?";
563+ }
564+ }
565+
566+ # The last index col > val. This causes the range scan using just
567+ # the N left-most index columns.
568+ my $col = $index_cols->[$n_index_cols - 1];
569+ my $val = $vals->[-1]; # should only be as many vals as cols
570+ push @where, $q->quote($col) . " >= ?";
571+
572+ my $sql = "EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * "
573+ . "FROM $tbl->{name} FORCE INDEX (" . $q->quote($index) . ") "
574+ . "WHERE " . join(' AND ', @where)
575+ . " /*key_len*/";
576+ return $sql;
577+}
578+
579+sub _d {
580+ my ($package, undef, $line) = caller 0;
581+ @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
582+ map { defined $_ ? $_ : 'undef' }
583+ @_;
584+ print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
585+}
586+
587+1;
588+}
589+# ###########################################################################
590+# End IndexLength package
591+# ###########################################################################
592
593=== added file 't/lib/IndexLength.pm'
594--- t/lib/IndexLength.pm 1970-01-01 00:00:00 +0000
595+++ t/lib/IndexLength.pm 2012-06-11 16:59:23 +0000
596@@ -0,0 +1,135 @@
597+#!/usr/bin/perl
598+
599+BEGIN {
600+ die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
601+ unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
602+ unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
603+};
604+
605+use strict;
606+use warnings FATAL => 'all';
607+use English qw(-no_match_vars);
608+use Test::More;
609+
610+use PerconaTest;
611+use DSNParser;
612+use Sandbox;
613+
614+use Cxn;
615+use Quoter;
616+use TableParser;
617+use OptionParser;
618+use IndexLength;
619+
620+use constant PTDEBUG => $ENV{PTDEBUG} || 0;
621+use constant PTDEVDEBUG => $ENV{PTDEBUG} || 0;
622+
623+use Data::Dumper;
624+$Data::Dumper::Indent = 1;
625+$Data::Dumper::Sortkeys = 1;
626+$Data::Dumper::Quotekeys = 0;
627+
628+my $dp = new DSNParser(opts=>$dsn_opts);
629+my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
630+my $dbh = $sb->get_dbh_for('master');
631+
632+if ( !$dbh ) {
633+ plan skip_all => 'Cannot connect to sandbox master';
634+}
635+else {
636+ plan tests => 7;
637+}
638+
639+my $output;
640+my $q = new Quoter();
641+my $tp = new TableParser(Quoter => $q);
642+my $il = new IndexLength(Quoter => $q);
643+my $o = new OptionParser(description => 'IndexLength');
644+$o->get_specs("$trunk/bin/pt-table-checksum");
645+my $cxn = new Cxn(
646+ dbh => $dbh,
647+ dsn => { h=>'127.1', P=>'12345', n=>'h=127.1,P=12345' },
648+ DSNParser => $dp,
649+ OptionParser => $o,
650+);
651+
652+sub test_index_len {
653+ my (%args) = @_;
654+ my @required_args = qw(name tbl index len);
655+ foreach my $arg ( @required_args ) {
656+ die "I need a $arg argument" unless $args{$arg};
657+ }
658+
659+ my ($len, $key) = $il->index_length(
660+ Cxn => $cxn,
661+ tbl => $args{tbl},
662+ index => $args{index},
663+ n_index_cols => $args{n_index_cols},
664+ );
665+
666+ is(
667+ $len,
668+ $args{len},
669+ "$args{name}"
670+ );
671+}
672+
673+# #############################################################################
674+# bad_plan, PK with 4 cols
675+# #############################################################################
676+$sb->load_file('master', "t/pt-table-checksum/samples/bad-plan-bug-1010232.sql");
677+my $tbl_struct = $tp->parse(
678+ $tp->get_create_table($dbh, 'bad_plan', 't'));
679+my $tbl = {
680+ name => $q->quote('bad_plan', 't'),
681+ tbl_struct => $tbl_struct,
682+};
683+
684+for my $n ( 1..4 ) {
685+ my $len = $n * 2 + ($n >= 2 ? 1 : 0);
686+ test_index_len(
687+ name => "bad_plan.t $n cols = $len bytes",
688+ tbl => $tbl,
689+ index => "PRIMARY",
690+ n_index_cols => $n,
691+ len => $len,
692+ );
693+}
694+
695+# #############################################################################
696+# Some sakila tables
697+# #############################################################################
698+$tbl_struct = $tp->parse(
699+ $tp->get_create_table($dbh, 'sakila', 'film_actor'));
700+$tbl = {
701+ name => $q->quote('sakila', 'film_actor'),
702+ tbl_struct => $tbl_struct,
703+};
704+
705+test_index_len(
706+ name => "sakila.film_actor 1 col = 2 bytes",
707+ tbl => $tbl,
708+ index => "PRIMARY",
709+ n_index_cols => 1,
710+ len => 2,
711+);
712+
713+# #############################################################################
714+# Use full index if no n_index_cols
715+# #############################################################################
716+
717+# Use sakila.film_actor stuff from previous tests.
718+
719+test_index_len(
720+ name => "sakila.film_actor all cols = 4 bytes",
721+ tbl => $tbl,
722+ index => "PRIMARY",
723+ len => 4,
724+);
725+
726+# #############################################################################
727+# Done.
728+# #############################################################################
729+$sb->wipe_clean($dbh);
730+ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
731+exit;
732
733=== modified file 't/pt-table-checksum/chunk_index.t'
734--- t/pt-table-checksum/chunk_index.t 2012-06-11 12:07:18 +0000
735+++ t/pt-table-checksum/chunk_index.t 2012-06-11 16:59:23 +0000
736@@ -25,7 +25,7 @@
737 plan skip_all => 'Cannot connect to sandbox master';
738 }
739 else {
740- plan tests => 16;
741+ plan tests => 17;
742 }
743
744 # The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
745@@ -175,7 +175,7 @@
746 $exit_status,
747 0,
748 "Bad key_len chunks are not errors"
749-);
750+) or diag($output);
751
752 cmp_ok(
753 PerconaTest::count_checksum_results($output, 'skipped'),
754@@ -205,19 +205,40 @@
755 sub {
756 $exit_status = pt_table_checksum::main(
757 $master_dsn, '--max-load', '',
758- qw(--lock-wait-timeout 3 --chunk-size 5000 -t sakila.rental),
759- qw(--chunk-index rental_date --chunk-index-columns 5),
760- qw(--explain --explain));
761+ qw(--lock-wait-timeout 3 --chunk-size 1000 -t sakila.film_actor),
762+ qw(--chunk-index PRIMARY --chunk-index-columns 9),
763+ );
764 },
765 stderr => 1,
766 );
767
768 is(
769- $exit_status,
770- 0,
771+ PerconaTest::count_checksum_results($output, 'rows'),
772+ 5462,
773 "--chunk-index-columns > number of index columns"
774+) or diag($output);
775+
776+$output = output(
777+ sub {
778+ $exit_status = pt_table_checksum::main(
779+ $master_dsn, '--max-load', '',
780+ qw(--lock-wait-timeout 3 --chunk-size 1000 -t sakila.film_actor),
781+ qw(--chunk-index-columns 1 --chunk-size-limit 3),
782+ );
783+ },
784+ stderr => 1,
785 );
786
787+# Since we're not using the full index, it's basically a non-unique index,
788+# so there are dupes. The table really has 5462 rows, so we must get
789+# at least that many, and probably a few more.
790+cmp_ok(
791+ PerconaTest::count_checksum_results($output, 'rows'),
792+ '>=',
793+ 5462,
794+ "Initial key_len reflects --chunk-index-columns"
795+) or diag($output);
796+
797 # #############################################################################
798 # Done.
799 # #############################################################################

Subscribers

People subscribed via source and target branches