diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 28ab96ce..6e811f71 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -5735,8 +5735,8 @@ sub make_row_checksum { $query = join(', ', map { my $col = $_; - if ( $col =~ m/\+ 0/ ) { - my ($real_col) = /^(\S+)/; + if ( $col =~ m/UNIX_TIMESTAMP/ ) { + my ($real_col) = /^UNIX_TIMESTAMP\((.+?)\)/; $col .= " AS $real_col"; } elsif ( $col =~ m/TRIM/ ) { @@ -5836,7 +5836,7 @@ sub get_checksum_columns { my $type = $tbl_struct->{type_for}->{$_}; my $result = $q->quote($_); if ( $type eq 'timestamp' ) { - $result .= ' + 0'; + $result = "UNIX_TIMESTAMP($result)"; } elsif ( $float_precision && $type =~ m/float|double/ ) { $result = "ROUND($result, $float_precision)"; diff --git a/lib/RowChecksum.pm b/lib/RowChecksum.pm index a8e584dc..3d6e814a 100644 --- a/lib/RowChecksum.pm +++ b/lib/RowChecksum.pm @@ -82,10 +82,10 @@ sub make_row_checksum { $query = join(', ', map { my $col = $_; - if ( $col =~ m/\+ 0/ ) { + if ( $col =~ m/UNIX_TIMESTAMP/ ) { # Alias col name back to itself else its name becomes # "col + 0" instead of just "col". - my ($real_col) = /^(\S+)/; + my ($real_col) = /^UNIX_TIMESTAMP\((.+?)\)/; $col .= " AS $real_col"; } elsif ( $col =~ m/TRIM/ ) { @@ -216,7 +216,7 @@ sub get_checksum_columns { my $type = $tbl_struct->{type_for}->{$_}; my $result = $q->quote($_); if ( $type eq 'timestamp' ) { - $result .= ' + 0'; + $result = "UNIX_TIMESTAMP($result)"; } elsif ( $float_precision && $type =~ m/float|double/ ) { $result = "ROUND($result, $float_precision)"; diff --git a/t/lib/RowChecksum.t b/t/lib/RowChecksum.t index 3ef193ea..88403d33 100644 --- a/t/lib/RowChecksum.t +++ b/t/lib/RowChecksum.t @@ -126,11 +126,11 @@ is( tbl => $tbl, func => 'SHA1', ), - q{`film_id`, `title`, `description`, `release_year`, `language_id`, `original_language_id`, `rental_duration`, `rental_rate`, `length`, `replacement_cost`, `rating`, `special_features`, `last_update` + 0 AS `last_update`, } + q{`film_id`, `title`, `description`, `release_year`, `language_id`, `original_language_id`, `rental_duration`, `rental_rate`, `length`, `replacement_cost`, `rating`, `special_features`, UNIX_TIMESTAMP(`last_update`) AS `last_update`, } . q{SHA1(CONCAT_WS('#', } . q{`film_id`, `title`, `description`, `release_year`, `language_id`, } . q{`original_language_id`, `rental_duration`, `rental_rate`, `length`, } - . q{`replacement_cost`, `rating`, `special_features`, `last_update` + 0, } + . q{`replacement_cost`, `rating`, `special_features`, UNIX_TIMESTAMP(`last_update`), } . q{CONCAT(ISNULL(`description`), ISNULL(`release_year`), } . q{ISNULL(`original_language_id`), ISNULL(`length`), } . q{ISNULL(`rating`), ISNULL(`special_features`))))}, @@ -142,11 +142,11 @@ is( tbl => $tbl, func => 'FNV_64', ), - q{`film_id`, `title`, `description`, `release_year`, `language_id`, `original_language_id`, `rental_duration`, `rental_rate`, `length`, `replacement_cost`, `rating`, `special_features`, `last_update` + 0 AS `last_update`, } + q{`film_id`, `title`, `description`, `release_year`, `language_id`, `original_language_id`, `rental_duration`, `rental_rate`, `length`, `replacement_cost`, `rating`, `special_features`, UNIX_TIMESTAMP(`last_update`) AS `last_update`, } . q{FNV_64(} . q{`film_id`, `title`, `description`, `release_year`, `language_id`, } . q{`original_language_id`, `rental_duration`, `rental_rate`, `length`, } - . q{`replacement_cost`, `rating`, `special_features`, `last_update` + 0)}, + . q{`replacement_cost`, `rating`, `special_features`, UNIX_TIMESTAMP(`last_update`))}, 'FNV_64 query for sakila.film', ); diff --git a/t/pt-table-checksum/bugs.t b/t/pt-table-checksum/bugs.t index cf67a889..2d9fb00d 100644 --- a/t/pt-table-checksum/bugs.t +++ b/t/pt-table-checksum/bugs.t @@ -294,6 +294,31 @@ like( "Bug 1210537: tool ran" ); +# ############################################################################# +# pt-table-checksum has errors when slaves have different system_time_zone +# https://bugs.launchpad.net/percona-toolkit/+bug/1388870 +# ############################################################################# + +# make slave set diferent system_time_zone by changing env var TZ. +diag(`/tmp/12346/stop >/dev/null`); +diag(`export TZ='HST';/tmp/12346/start >/dev/null`); + +$output = output( + sub { pt_table_checksum::main(@args, qw(-t sakila.payment)) }, +); + + +is( + PerconaTest::count_checksum_results($output, 'diffs'), + 0, + "Bug 1388870 - No false positive reported when system_tz differ on slave" +); + +# restore slave to original system_tz +diag(`/tmp/12346/stop >/dev/null`); +diag(`/tmp/12346/start >/dev/null`); + + # ############################################################################# # Done. # ############################################################################# diff --git a/t/pt-table-checksum/samples/chunkidx004.txt b/t/pt-table-checksum/samples/chunkidx004.txt index b3ceb684..31b89632 100644 --- a/t/pt-table-checksum/samples/chunkidx004.txt +++ b/t/pt-table-checksum/samples/chunkidx004.txt @@ -2,7 +2,7 @@ -- sakila.city -- -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('#', `city_id`, `city`, `country_id`, `last_update` + 0)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `sakila`.`city` FORCE INDEX(`idx_fk_country_id`) WHERE ((`country_id` >= ?)) AND ((`country_id` <= ?)) AND (country_id > 100) /*checksum chunk*/ +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('#', `city_id`, `city`, `country_id`, UNIX_TIMESTAMP(`last_update`))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `sakila`.`city` FORCE INDEX(`idx_fk_country_id`) WHERE ((`country_id` >= ?)) AND ((`country_id` <= ?)) AND (country_id > 100) /*checksum chunk*/ REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*), '0' FROM `sakila`.`city` FORCE INDEX(`idx_fk_country_id`) WHERE ((`country_id` < ?)) AND (country_id > 100) ORDER BY `country_id` /*past lower chunk*/ diff --git a/t/pt-table-checksum/samples/chunkidx005.txt b/t/pt-table-checksum/samples/chunkidx005.txt index ae55d193..a6177f19 100644 --- a/t/pt-table-checksum/samples/chunkidx005.txt +++ b/t/pt-table-checksum/samples/chunkidx005.txt @@ -2,7 +2,7 @@ -- sakila.city -- -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('#', `city_id`, `city`, `country_id`, `last_update` + 0)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `sakila`.`city` FORCE INDEX(`PRIMARY`) WHERE ((`city_id` >= ?)) AND ((`city_id` <= ?)) AND (country_id > 100) /*checksum chunk*/ +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('#', `city_id`, `city`, `country_id`, UNIX_TIMESTAMP(`last_update`))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `sakila`.`city` FORCE INDEX(`PRIMARY`) WHERE ((`city_id` >= ?)) AND ((`city_id` <= ?)) AND (country_id > 100) /*checksum chunk*/ REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*), '0' FROM `sakila`.`city` FORCE INDEX(`PRIMARY`) WHERE ((`city_id` < ?)) AND (country_id > 100) ORDER BY `city_id` /*past lower chunk*/ diff --git a/t/pt-table-checksum/samples/n-chunk-index-cols.txt b/t/pt-table-checksum/samples/n-chunk-index-cols.txt index f08f80ea..b8d9e17e 100644 --- a/t/pt-table-checksum/samples/n-chunk-index-cols.txt +++ b/t/pt-table-checksum/samples/n-chunk-index-cols.txt @@ -2,7 +2,7 @@ -- sakila.rental -- -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('#', `rental_id`, `rental_date`, `inventory_id`, `customer_id`, `return_date`, `staff_id`, `last_update` + 0, CONCAT(ISNULL(`return_date`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `sakila`.`rental` FORCE INDEX(`rental_date`) WHERE ((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` >= ?)) AND ((`rental_date` < ?) OR (`rental_date` = ? AND `inventory_id` <= ?)) /*checksum chunk*/ +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('#', `rental_id`, `rental_date`, `inventory_id`, `customer_id`, `return_date`, `staff_id`, UNIX_TIMESTAMP(`last_update`), CONCAT(ISNULL(`return_date`)))) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `sakila`.`rental` FORCE INDEX(`rental_date`) WHERE ((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` >= ?)) AND ((`rental_date` < ?) OR (`rental_date` = ? AND `inventory_id` <= ?)) /*checksum chunk*/ REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*), '0' FROM `sakila`.`rental` FORCE INDEX(`rental_date`) WHERE ((`rental_date` < ?) OR (`rental_date` = ? AND `inventory_id` < ?)) ORDER BY `rental_date`, `inventory_id`, `customer_id` /*past lower chunk*/