Files
percona-toolkit/t/lib/ExplainAnalyzer.t
Sveta Smirnova e2207ea232 PT-2340 - Support MySQL 8.4
- Removed offensive terminology from library files and their tests
- Removed unused sandbox/prove2junit.pl
- Added option mysql_ssl to DSN and possibility to have DSN of multiple letters
2024-07-25 19:03:33 +03:00

447 lines
13 KiB
Perl

#!/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 constant PTDEBUG => $ENV{PTDEBUG} || 0;
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Quotekeys = 0;
use Test::More;
use ExplainAnalyzer;
use QueryRewriter;
use QueryParser;
use DSNParser;
use Sandbox;
use PerconaTest;
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $dbh = $sb->get_dbh_for('source', {no_lc=>1});
if ( !$dbh ) {
plan skip_all => "Cannot connect to sandbox master";
}
$dbh->do('use sakila');
my $qr = new QueryRewriter();
my $qp = new QueryParser();
my $exa = new ExplainAnalyzer(QueryRewriter => $qr, QueryParser => $qp);
# #############################################################################
# Tests for getting an EXPLAIN from a database.
# #############################################################################
my $want = [
{ id => 1,
select_type => 'SIMPLE',
table => 'actor',
type => 'const',
possible_keys => 'PRIMARY',
key => 'PRIMARY',
key_len => 2,
ref => 'const',
rows => 1,
Extra => $sandbox_version eq '5.6' ? undef : '',
},
];
if ( $sandbox_version gt '5.6' ) {
$want = [
{ id => 1,
select_type => 'SIMPLE',
table => 'actor',
type => 'const',
possible_keys => 'PRIMARY',
key => 'PRIMARY',
key_len => 2,
filtered => 100,
partitions => undef,
ref => 'const',
rows => 1,
Extra => $sandbox_version gt '5.6' ? undef : '',
},
];
}
my $got = $exa->explain_query(
dbh => $dbh,
query => 'select * from actor where actor_id = 5',
);
$got->[0]->{filtered} = int($got->[0]->{filtered}) if (defined($got->[0]->{filtered}));
is_deeply(
$got,
$want,
'Got a simple EXPLAIN result',
);
$got = $exa->explain_query(
dbh => $dbh,
query => 'delete from actor where actor_id = 5',
);
$got->[0]->{filtered} = int($got->[0]->{filtered}) if (defined($got->[0]->{filtered}));
is_deeply(
$got,
$want,
'Got EXPLAIN result for a DELETE',
);
is(
$exa->explain_query(
dbh => $dbh,
query => 'insert into t values (1)',
),
undef,
"Doesn't EXPLAIN non-convertable non-SELECT"
);
# #############################################################################
# NOTE: EXPLAIN will vary between versions, so rely on the database as little as
# possible for tests. Most things that need an EXPLAIN in the tests below
# should be using a hard-coded data structure. Thus the following, intended to
# help prevent $dbh being used too much.
# #############################################################################
# XXX $dbh->disconnect;
# #############################################################################
# Tests for normalizing raw EXPLAIN into a format that's easier to work with.
# #############################################################################
is_deeply(
$exa->normalize(
[
{ id => 1,
select_type => 'SIMPLE',
table => 'film_actor',
type => 'index_merge',
possible_keys => 'PRIMARY,idx_fk_film_id',
key => 'PRIMARY,idx_fk_film_id',
key_len => '2,2',
ref => undef,
rows => 34,
Extra => 'Using union(PRIMARY,idx_fk_film_id); Using where',
},
],
),
[
{ id => 1,
select_type => 'SIMPLE',
table => 'film_actor',
type => 'index_merge',
possible_keys => [qw(PRIMARY idx_fk_film_id)],
key => [qw(PRIMARY idx_fk_film_id)],
key_len => [2,2],
ref => [qw()],
rows => 34,
Extra => {
'Using union' => [qw(PRIMARY idx_fk_film_id)],
'Using where' => 1,
},
},
],
'Normalizes an EXPLAIN',
);
is_deeply(
$exa->normalize(
[
{ id => 1,
select_type => 'PRIMARY',
table => undef,
type => undef,
possible_keys => undef,
key => undef,
key_len => undef,
ref => undef,
rows => undef,
Extra => 'No tables used',
},
{ id => 1,
select_type => 'UNION',
table => 'a',
type => 'index',
possible_keys => undef,
key => 'PRIMARY',
key_len => '2',
ref => undef,
rows => 200,
Extra => 'Using index',
},
{ id => undef,
select_type => 'UNION RESULT',
table => '<union1,2>',
type => 'ALL',
possible_keys => undef,
key => undef,
key_len => undef,
ref => undef,
rows => undef,
Extra => '',
},
],
),
[
{ id => 1,
select_type => 'PRIMARY',
table => undef,
type => undef,
possible_keys => [],
key => [],
key_len => [],
ref => [],
rows => undef,
Extra => {
'No tables used' => 1,
},
},
{ id => 1,
select_type => 'UNION',
table => 'a',
type => 'index',
possible_keys => [],
key => ['PRIMARY'],
key_len => [2],
ref => [],
rows => 200,
Extra => {
'Using index' => 1,
},
},
{ id => undef,
select_type => 'UNION RESULT',
table => '<union1,2>',
type => 'ALL',
possible_keys => [],
key => [],
key_len => [],
ref => [],
rows => undef,
Extra => {},
},
],
'Normalizes a more complex EXPLAIN',
);
is_deeply(
$exa->normalize(
[
{ id => 1,
select_type => 'SIMPLE',
table => 't1',
type => 'ALL',
possible_keys => 'PRIMARY',
key => undef,
key_len => undef,
ref => undef,
rows => '4',
# Extra => 'Using where; Using temporary; Using filesort',
},
],
),
[
{
Extra => {}, # is auto-vivified
id => 1,
select_type => 'SIMPLE',
table => 't1',
type => 'ALL',
possible_keys => ['PRIMARY'],
key => [],
key_len => [],
ref => [],
rows => '4',
}
],
"normalize() doesn't crash if EXPLAIN Extra is missing"
);
# #############################################################################
# Tests for trimming indexes out of possible_keys.
# #############################################################################
is_deeply(
$exa->get_alternate_indexes(
[qw(index1 index2)],
[qw(index1 index2 index3 index4)],
),
[qw(index3 index4)],
'Normalizes alternate indexes',
);
# #############################################################################
# Tests for translating aliased names back to their real names.
# #############################################################################
# Putting it all together: given a query and an EXPLAIN, determine which indexes
# the query used.
is_deeply(
$exa->get_index_usage(
query => "select * from film_actor as fa inner join sakila.actor as a "
. "on a.actor_id = fa.actor_id and a.last_name is not null "
. "where a.actor_id = 5 or film_id = 5",
db => 'sakila',
explain => $exa->normalize(
[
{ id => 1,
select_type => 'SIMPLE',
table => 'fa',
type => 'index_merge',
possible_keys => 'PRIMARY,idx_fk_film_id',
key => 'PRIMARY,idx_fk_film_id',
key_len => '2,2',
ref => undef,
rows => 34,
Extra => 'Using union(PRIMARY,idx_fk_film_id); Using where',
},
{ id => 1,
select_type => 'SIMPLE',
table => 'a',
type => 'eq_ref',
possible_keys => 'PRIMARY,idx_actor_last_name',
key => 'PRIMARY',
key_len => '2',
ref => 'sakila.fa.actor_id',
rows => 1,
Extra => 'Using where',
},
],
),
),
[ { db => 'sakila',
tbl => 'film_actor',
idx => [qw(PRIMARY idx_fk_film_id)],
alt => [],
},
{ db => 'sakila',
tbl => 'actor',
idx => [qw(PRIMARY)],
alt => [qw(idx_actor_last_name)],
},
],
'Translate an EXPLAIN and a query into simplified index usage',
);
# This is kind of a pathological case.
is_deeply(
$exa->get_index_usage(
query => "select 1 union select count(*) from actor a",
db => 'sakila',
explain => $exa->normalize(
[
{ id => 1,
select_type => 'PRIMARY',
table => undef,
type => undef,
possible_keys => undef,
key => undef,
key_len => undef,
ref => undef,
rows => undef,
Extra => 'No tables used',
},
{ id => 1,
select_type => 'UNION',
table => 'a',
type => 'index',
possible_keys => undef,
key => 'PRIMARY',
key_len => '2',
ref => undef,
rows => 200,
Extra => 'Using index',
},
{ id => undef,
select_type => 'UNION RESULT',
table => '<union1,2>',
type => 'ALL',
possible_keys => undef,
key => undef,
key_len => undef,
ref => undef,
rows => undef,
Extra => '',
},
],
),
),
[ { db => 'sakila',
tbl => 'actor',
idx => [qw(PRIMARY)],
alt => [],
},
],
'Translate an EXPLAIN and a query for a harder case',
);
# Here's a query that uses a table but no indexes in it.
is_deeply(
$exa->get_index_usage(
query => "select * from film_text",
db => 'sakila',
explain => $exa->normalize(
[
{ id => 1,
select_type => 'SIMPLE',
table => 'film_text',
type => 'ALL',
possible_keys => undef,
key => undef,
key_len => undef,
ref => undef,
rows => 1000,
Extra => '',
},
],
),
),
[ { db => 'sakila',
tbl => 'film_text',
idx => [],
alt => [],
},
],
'Translate an EXPLAIN for a query that uses no indexes',
);
# #############################################################################
# Methods to save and retrieve index usage for a specific query and database.
# #############################################################################
is_deeply(
$exa->get_usage_for('0xdeadbeef', 'sakila'),
undef,
'No usage recorded for 0xdeadbeef');
$exa->save_usage_for('0xdeadbeef', 'sakila',
[ { db => 'sakila',
tbl => 'actor',
idx => [qw(PRIMARY)],
alt => [],
},
]);
is_deeply(
$exa->get_usage_for('0xdeadbeef','sakila'),
[ { db => 'sakila',
tbl => 'actor',
idx => [qw(PRIMARY)],
alt => [],
},
],
'Got saved usage for 0xdeadbeef');
# #############################################################################
# Done.
# #############################################################################
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;