diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index a39867eb..427de6e0 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -13262,6 +13262,10 @@ the values are converted to strings by the CONCAT() function, and MySQL chooses the string representation. If you specify a value of 2, for example, then the values 1.008 and 1.009 will be rounded to 1.01, and will checksum as equal. +=item --force-concat-enums + +bla bla bla bla bla + =item --function type: string diff --git a/lib/NibbleIterator.pm b/lib/NibbleIterator.pm index 18f6f849..1f70235e 100644 --- a/lib/NibbleIterator.pm +++ b/lib/NibbleIterator.pm @@ -134,14 +134,37 @@ sub new { ); PTDEBUG && _d('Ascend params:', Dumper($asc)); + # Check if enum fields items are sorted or not. + # If they are sorted we can skip adding CONCAT to improve the queries eficiency. + my $force_concat_enums = $o->has('force-concat-enums') && $o->get('force-concat-enums'); + for my $index (@{$index_cols}) { + if ($tbl->{tbl_struct}->{type_for}->{$index} eq 'enum') { + if ($tbl->{tbl_struct}->{defs}->{$index} =~ m/enum\s*\((.*?)\)/) { + my @items = split(/,\s*/, $1); + my $sorted = 1; # Asume the items list is sorted to later check if this is true + for (my $i=1; $i < scalar(@items); $i++) { + if ($items[$i-1] gt $items[$i]) { + $sorted = 0; + last; + } + } + if (!$force_concat_enums && !$sorted) { + die "The index " . $index . " in table " . $tbl->{name} . + " has unsorted enum items.\nPlease read the documentation for the --force-concat-enums parameter\n"; + } + } + } + } + # Make SQL statements, prepared on first call to next(). FROM and # ORDER BY are the same for all statements. FORCE IDNEX and ORDER BY # are needed to ensure deterministic nibbling. + my $from = "$tbl->{name} FORCE INDEX(`$index`)"; - my $order_by = join(', ', map { $tbl->{tbl_struct}->{type_for}->{$_} eq 'enum' + my $order_by = join(', ', map { $tbl->{tbl_struct}->{type_for}->{$_} eq 'enum' && $force_concat_enums ? "CONCAT(".$q->quote($_).")" : $q->quote($_)} @{$index_cols}); - my $order_by_dec = join(' DESC,', map { $tbl->{tbl_struct}->{type_for}->{$_} eq 'enum' + my $order_by_dec = join(' DESC,', map { $tbl->{tbl_struct}->{type_for}->{$_} eq 'enum' && $force_concat_enums ? "CONCAT(".$q->quote($_).")" : $q->quote($_)} @{$index_cols}); # The real first row in the table. Usually we start nibbling from diff --git a/t/lib/NibbleIterator.t b/t/lib/NibbleIterator.t index 6c0f68c2..53379828 100644 --- a/t/lib/NibbleIterator.t +++ b/t/lib/NibbleIterator.t @@ -38,6 +38,8 @@ my $dbh = $sb->get_dbh_for('master'); if ( !$dbh ) { plan skip_all => 'Cannot connect to sandbox master'; +} else { + plan tests => 60; } my $q = new Quoter(); @@ -879,7 +881,7 @@ is_deeply( # ############################################################################# diag(`/tmp/12345/use < $trunk/t/lib/samples/cardinality.sql >/dev/null`); - +$dbh->do('analyze table bad_tables.inv'); $ni = make_nibble_iter( db => 'cardb', tbl => 't', @@ -892,6 +894,56 @@ is( "Use non-unique index with highest cardinality (bug 1199591)" ); +$sb->load_file('master', "t/lib/samples/NibbleIterator/enum_keys.sql"); +$ni = undef; +eval { + $ni = make_nibble_iter( + db => 'test', + tbl => 't1', + argv => [qw(--databases test --chunk-size 3)], + ); +}; + +like( + $EVAL_ERROR, + qr/The index f3 in table `test`.`t1` has unsorted enum items/, + "PT-1572 Die on unsorted enum items in index", +); + +eval { + $ni = make_nibble_iter( + db => 'test', + tbl => 't1', + argv => [qw(--databases test --force-concat-enums --chunk-size 3)], + ); +}; + +like( + $ni->{explain_first_lb_sql}, + qr/ORDER BY `f1`, `f2`, CONCAT\(`f3`\)/, + "PT-1572 Use of CONCAT for unsorted ENUM field items without --", +); + +eval { + $ni = make_nibble_iter( + db => 'test', + tbl => 't2', + argv => [qw(--databases test --chunk-size 3)], + ); +}; + +is( + $EVAL_ERROR, + '', + "PT-1572 No errors on sorted enum items in index", +); + +like( + $ni->{explain_first_lb_sql}, + qr/ORDER BY `f1`, `f2`, `f3`/, + "PT-1572 Don't use CONCAT for sorted ENUM field items without --force-concat-enums", +); + # ############################################################################# # Done. # ############################################################################# @@ -905,6 +957,7 @@ like( qr/Complete test coverage/, '_d() works' ); + $sb->wipe_clean($dbh); ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); done_testing; diff --git a/t/lib/samples/NibbleIterator/enum_keys.sql b/t/lib/samples/NibbleIterator/enum_keys.sql new file mode 100644 index 00000000..8bc40e25 --- /dev/null +++ b/t/lib/samples/NibbleIterator/enum_keys.sql @@ -0,0 +1,47 @@ +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +USE test; + +-- Don't change the comments. The enum word inside the comment is there to test the table parser + +CREATE TABLE `test`.`t1` ( + f1 DATE NOT NULL, + f2 INT(10) UNSIGNED NOT NULL, + f3 ENUM('c','a','b','d') NOT NULL DEFAULT 'c' COMMENT "unsorted enum items", + f4 INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`f1`,`f2`,`f3`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +INSERT INTO `t1` VALUES +('2018-07-27',165910161,'c',1375471152), +('2018-07-27',393713658,'d',1382302491), +('2018-07-27',472875023,'c',525456967), +('2018-07-27',543582931,'c',1657080267), +('2018-07-27',583532949,'d',280366509), +('2018-07-27',1396416465,'d',1252007743), +('2018-07-27',1705409249,'c',1714682759), +('2018-07-27',1801160058,'a',1022430181), +('2018-07-27',1898674299,'c',1310715836), +('2018-07-27',2011751560,'a',109015753); + +CREATE TABLE `test`.`t2` ( + f1 DATE NOT NULL, + f2 INT(10) UNSIGNED NOT NULL, + f3 ENUM('a','b','c','d') NOT NULL DEFAULT 'c' COMMENT "sorted enum items", + f4 INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`f1`,`f2`,`f3`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +INSERT INTO `t2` VALUES +('2018-07-27',165910161,'c',1375471152), +('2018-07-27',393713658,'d',1382302491), +('2018-07-27',472875023,'c',525456967), +('2018-07-27',543582931,'c',1657080267), +('2018-07-27',583532949,'d',280366509), +('2018-07-27',1396416465,'d',1252007743), +('2018-07-27',1705409249,'c',1714682759), +('2018-07-27',1801160058,'a',1022430181), +('2018-07-27',1898674299,'c',1310715836), +('2018-07-27',2011751560,'a',109015753);