Files
percona-toolkit/t/lib/RowDiff.t
Sveta Smirnova 5c999ca3e0 PT-2340 - Support MySQL 8.4
- Removed runtime.txt after discussion with Anastasia Alexandrova
- Added "use VersionParser" into tests in t/lib when needed
- Removed word master from tests for pt-archiver, pt-config-diff, pt-deadlock-logger, pt-duplicate-key-checker, pt-find, pt-fk-error-logger, pt-heartbeat, pt-index-usage, pt-ioprofile, pt-kill, pt-mysql-summary
- Removed word slave from tests for pt-archiver, pt-config-diff, pt-deadlock-logger, pt-duplicate-key-checker, pt-find, pt-fk-error-logger, pt-heartbeat, pt-index-usage, pt-ioprofile, pt-kill, pt-mysql-summary
- Updated modules for pt-archiver, pt-config-diff, pt-deadlock-logger, pt-duplicate-key-checker, pt-find, pt-fk-error-logger, pt-heartbeat, pt-index-usage, pt-ioprofile, pt-kill, pt-mysql-summary
- Changed mysql_ssl patch, so it is now short option s
- Added a check for existing zombies in t/pt-kill/execute_command.t
- Added bin/pt-galera-log-explainer to .gitignore
2024-07-27 01:59:52 +03:00

557 lines
12 KiB
Perl

#!/usr/bin/env 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 MockSync;
use RowDiff;
use MockSth;
use Sandbox;
use DSNParser;
use VersionParser;
use TableParser;
use Quoter;
use PerconaTest;
my ($d, $s);
my $q = new Quoter();
my $tp = new TableParser(Quoter => $q);
my $dp = new DSNParser(opts=>$dsn_opts);
# Connect to sandbox now to make sure it's running.
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $master_dbh = $sb->get_dbh_for('source');
my $slave_dbh = $sb->get_dbh_for('replica1');
throws_ok( sub { new RowDiff() }, qr/I need a dbh/, 'DBH required' );
$d = new RowDiff(dbh => 1);
# #############################################################################
# Test key_cmp().
# #############################################################################
my %args = (
key_cols => [qw(a)],
tbl_struct => {},
);
is(
$d->key_cmp(
lr => { a => 1 },
rr => { a => 1 },
%args,
),
0,
'Equal keys',
);
is(
$d->key_cmp(
lr => { a => undef },
rr => { a => undef },
%args,
),
0,
'Equal null keys',
);
is(
$d->key_cmp(
lr => undef,
rr => { a => 1 },
%args,
),
-1,
'Left key missing',
);
is(
$d->key_cmp(
lr => { a => 1 },
rr => undef,
%args,
),
1,
'Right key missing',
);
is(
$d->key_cmp(
lr => { a => 2 },
rr => { a => 1 },
%args,
),
1,
'Right key smaller',
);
is(
$d->key_cmp(
lr => { a => 2 },
rr => { a => 3 },
%args,
),
-1,
'Right key larger',
);
$args{key_cols} = [qw(a b)];
is(
$d->key_cmp(
lr => { a => 1, b => 2, },
rr => { a => 1, b => 1 },
%args,
),
1,
'Right two-part key smaller',
);
is(
$d->key_cmp(
lr => { a => 1, b => 0, },
rr => { a => 1, b => 1 },
%args,
),
-1,
'Right two-part key larger',
);
is(
$d->key_cmp(
lr => { a => 1, b => undef, },
rr => { a => 1, b => 1 },
%args,
),
-1,
'Right two-part key larger because of null',
);
is(
$d->key_cmp(
lr => { a => 1, b => 0, },
rr => { a => 1, b => undef },
%args,
),
1,
'Left two-part key larger because of null',
);
is(
$d->key_cmp(
lr => { a => 1, b => 0, },
rr => { a => undef, b => 1 },
%args,
),
1,
'Left two-part key larger because of null in first key part',
);
# #############################################################################
# Test compare_sets() using a mock syncer.
# #############################################################################
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(),
right_sth => new MockSth(),
syncer => $s,
tbl_struct => {},
);
is_deeply(
$s,
[
'done',
],
'no rows',
);
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(
),
right_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
),
syncer => $s,
tbl_struct => {},
);
is_deeply(
$s,
[
[ 'not in left', { a => 1, b => 2, c => 3 },],
'done',
],
'right only',
);
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
),
right_sth => new MockSth(
),
syncer => $s,
tbl_struct => {},
);
is_deeply(
$s,
[
[ 'not in right', { a => 1, b => 2, c => 3 },],
'done',
],
'left only',
);
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
),
right_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
),
syncer => $s,
tbl_struct => {},
);
is_deeply(
$s,
[
'same',
'done',
],
'one identical row',
);
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
{ a => 2, b => 2, c => 3 },
{ a => 3, b => 2, c => 3 },
# { a => 4, b => 2, c => 3 },
),
right_sth => new MockSth(
# { a => 1, b => 2, c => 3 },
{ a => 2, b => 2, c => 3 },
{ a => 3, b => 2, c => 3 },
{ a => 4, b => 2, c => 3 },
),
syncer => $s,
tbl_struct => {},
);
is_deeply(
$s,
[
[ 'not in right', { a => 1, b => 2, c => 3 }, ],
'same',
'same',
[ 'not in left', { a => 4, b => 2, c => 3 }, ],
'done',
],
'differences in basic set of rows',
);
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
),
right_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
),
syncer => $s,
tbl_struct => { is_numeric => { a => 1 } },
);
is_deeply(
$s,
[
'same',
'done',
],
'Identical with numeric columns',
);
SKIP: {
skip 'Cannot connect to sandbox master', 1 unless $master_dbh;
$d = new RowDiff(dbh => $master_dbh);
$s = new MockSync();
$d->compare_sets(
left_sth => new MockSth(
{ a => 'A', b => 2, c => 3 },
),
right_sth => new MockSth(
# The difference is the lowercase 'a', which in a _ci collation will
# sort the same. So the rows are really identical, from MySQL's point
# of view.
{ a => 'a', b => 2, c => 3 },
),
syncer => $s,
tbl_struct => { collation_for => { a => 'utf8mb4_general_ci' } },
);
is_deeply(
$s,
[
'same',
'done',
],
'Identical with utf8 columns',
);
}
# #############################################################################
# Test that the callbacks work.
# #############################################################################
my @rows;
my $same_row = sub {
push @rows, 'same row';
};
my $not_in_left = sub {
push @rows, 'not in left';
};
my $not_in_right = sub {
push @rows, 'not in right';
};
my $key_cmp = sub {
my ( $col, $lr, $rr ) = @_;
push @rows, "col $col differs";
};
$s = new MockSync();
$d = new RowDiff(
dbh => 1,
key_cmp => $key_cmp,
same_row => $same_row,
not_in_left => $not_in_left,
not_in_right => $not_in_right,
);
@rows = ();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
{ a => 2, b => 2, c => 3 },
{ a => 3, b => 2, c => 3 },
# { a => 4, b => 2, c => 3 },
),
right_sth => new MockSth(
# { a => 1, b => 2, c => 3 },
{ a => 2, b => 2, c => 3 },
{ a => 3, b => 2, c => 3 },
{ a => 4, b => 2, c => 3 },
),
syncer => $s,
tbl_struct => {},
);
is_deeply(
\@rows,
[
'col a differs',
'not in right',
'same row',
'same row',
'not in left',
],
'callbacks'
);
my $i = 0;
$d = new RowDiff(
dbh => 1,
key_cmp => $key_cmp,
same_row => $same_row,
not_in_left => $not_in_left,
not_in_right => $not_in_right,
done => sub { return ++$i > 2 ? 1 : 0; },
);
@rows = ();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
{ a => 2, b => 2, c => 3 },
{ a => 3, b => 2, c => 3 },
# { a => 4, b => 2, c => 3 },
),
right_sth => new MockSth(
# { a => 1, b => 2, c => 3 },
{ a => 2, b => 2, c => 3 },
{ a => 3, b => 2, c => 3 },
{ a => 4, b => 2, c => 3 },
),
syncer => $s,
tbl_struct => {},
);
is_deeply(
\@rows,
[
'col a differs',
'not in right',
'same row',
'same row',
],
'done callback'
);
$d = new RowDiff(
dbh => 1,
key_cmp => $key_cmp,
same_row => $same_row,
not_in_left => $not_in_left,
not_in_right => $not_in_right,
trf => sub {
my ( $l, $r, $tbl, $col ) = @_;
return 1, 1; # causes all rows to look like they're identical
},
);
@rows = ();
$d->compare_sets(
left_sth => new MockSth(
{ a => 1, b => 2, c => 3 },
{ a => 4, b => 5, c => 6 },
),
right_sth => new MockSth(
{ a => 7, b => 8, c => 9 },
{ a => 10, b => 11, c => 12 },
),
syncer => $s,
tbl_struct => { is_numeric => { a => 1, b => 1, c => 1 } },
);
is_deeply(
\@rows,
[
'same row',
'same row',
],
'trf callback'
);
# #############################################################################
# The following tests use "real" (sandbox) servers and real statement handles.
# #############################################################################
SKIP: {
skip 'Cannot connect to sandbox master', 4 unless $master_dbh;
skip 'Cannot connect to sandbox slave', 4 unless $slave_dbh;
PXC_SKIP: {
skip 'Not for PXC' if ( $sb->is_cluster_mode );
$d = new RowDiff(dbh => $master_dbh);
$sb->create_dbs($master_dbh, [qw(test)]);
$sb->load_file('source', 't/lib/samples/issue_11.sql');
PerconaTest::wait_until(
sub {
my $r;
eval {
$r = $slave_dbh->selectrow_arrayref('SHOW TABLES FROM test LIKE "issue_11"');
};
return 1 if ($r->[0] || '') eq 'issue_11';
return 0;
},
0.25,
30,
);
my $tbl = $tp->parse(
$tp->get_create_table($master_dbh, 'test', 'issue_11'));
my $left_sth = $master_dbh->prepare('SELECT * FROM test.issue_11');
my $right_sth = $slave_dbh->prepare('SELECT * FROM test.issue_11');
$left_sth->execute();
$right_sth->execute();
$s = new MockSync();
$d->compare_sets(
left_sth => $left_sth,
right_sth => $right_sth,
syncer => $s,
tbl_struct => $tbl,
);
is_deeply(
$s,
['done',],
'no rows (real DBI sth)',
);
$slave_dbh->do('INSERT INTO test.issue_11 VALUES (1,2,3)');
$left_sth = $master_dbh->prepare('SELECT * FROM test.issue_11');
$right_sth = $slave_dbh->prepare('SELECT * FROM test.issue_11');
$left_sth->execute();
$right_sth->execute();
$s = new MockSync();
$d->compare_sets(
left_sth => $left_sth,
right_sth => $right_sth,
syncer => $s,
tbl_struct => $tbl,
);
is_deeply(
$s,
[
['not in left', { a => 1, b => 2, c => 3 },],
'done',
],
'right only (real DBI sth)',
);
$slave_dbh->do('TRUNCATE TABLE test.issue_11');
$master_dbh->do('SET SQL_LOG_BIN=0;');
$master_dbh->do('INSERT INTO test.issue_11 VALUES (1,2,3)');
$left_sth = $master_dbh->prepare('SELECT * FROM test.issue_11');
$right_sth = $slave_dbh->prepare('SELECT * FROM test.issue_11');
$left_sth->execute();
$right_sth->execute();
$s = new MockSync();
$d->compare_sets(
left_sth => $left_sth,
right_sth => $right_sth,
syncer => $s,
tbl_struct => $tbl,
);
is_deeply(
$s,
[
[ 'not in right', { a => 1, b => 2, c => 3 },],
'done',
],
'left only (real DBI sth)',
);
$slave_dbh->do('INSERT INTO test.issue_11 VALUES (1,2,3)');
$left_sth = $master_dbh->prepare('SELECT * FROM test.issue_11');
$right_sth = $slave_dbh->prepare('SELECT * FROM test.issue_11');
$left_sth->execute();
$right_sth->execute();
$s = new MockSync();
$d->compare_sets(
left_sth => $left_sth,
right_sth => $right_sth,
syncer => $s,
tbl_struct => $tbl,
);
is_deeply(
$s,
[
'same',
'done',
],
'one identical row (real DBI sth)',
);
}
}
$sb->wipe_clean($master_dbh);
$sb->wipe_clean($slave_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;