4
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
9
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
11
use warnings FATAL => 'all';
12
use English qw(-no_match_vars);
14
$Data::Dumper::Indent = 1;
15
$Data::Dumper::Sortkeys = 1;
16
$Data::Dumper::Quotekeys = 0;
27
my $dp = new DSNParser(opts=>$dsn_opts);
28
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
29
my $dbh = $sb->get_dbh_for('master', {no_lc=>1});
32
plan skip_all => "Cannot connect to sandbox master";
38
$dbh->do('use sakila');
40
my $qr = new QueryRewriter();
41
my $qp = new QueryParser();
42
my $exa = new ExplainAnalyzer(QueryRewriter => $qr, QueryParser => $qp);
44
# #############################################################################
45
# Tests for getting an EXPLAIN from a database.
46
# #############################################################################
51
query => 'select * from actor where actor_id = 5',
55
select_type => 'SIMPLE',
58
possible_keys => 'PRIMARY',
66
'Got a simple EXPLAIN result',
72
query => 'delete from actor where actor_id = 5',
76
select_type => 'SIMPLE',
79
possible_keys => 'PRIMARY',
87
'Got EXPLAIN result for a DELETE',
93
query => 'insert into t values (1)',
96
"Doesn't EXPLAIN non-convertable non-SELECT"
99
# #############################################################################
100
# NOTE: EXPLAIN will vary between versions, so rely on the database as little as
101
# possible for tests. Most things that need an EXPLAIN in the tests below
102
# should be using a hard-coded data structure. Thus the following, intended to
103
# help prevent $dbh being used too much.
104
# #############################################################################
105
# XXX $dbh->disconnect;
107
# #############################################################################
108
# Tests for normalizing raw EXPLAIN into a format that's easier to work with.
109
# #############################################################################
114
select_type => 'SIMPLE',
115
table => 'film_actor',
116
type => 'index_merge',
117
possible_keys => 'PRIMARY,idx_fk_film_id',
118
key => 'PRIMARY,idx_fk_film_id',
122
Extra => 'Using union(PRIMARY,idx_fk_film_id); Using where',
128
select_type => 'SIMPLE',
129
table => 'film_actor',
130
type => 'index_merge',
131
possible_keys => [qw(PRIMARY idx_fk_film_id)],
132
key => [qw(PRIMARY idx_fk_film_id)],
137
'Using union' => [qw(PRIMARY idx_fk_film_id)],
142
'Normalizes an EXPLAIN',
149
select_type => 'PRIMARY',
152
possible_keys => undef,
157
Extra => 'No tables used',
160
select_type => 'UNION',
163
possible_keys => undef,
168
Extra => 'Using index',
171
select_type => 'UNION RESULT',
172
table => '<union1,2>',
174
possible_keys => undef,
185
select_type => 'PRIMARY',
194
'No tables used' => 1,
198
select_type => 'UNION',
211
select_type => 'UNION RESULT',
212
table => '<union1,2>',
222
'Normalizes a more complex EXPLAIN',
229
select_type => 'SIMPLE',
232
possible_keys => 'PRIMARY',
237
# Extra => 'Using where; Using temporary; Using filesort',
243
Extra => {}, # is auto-vivified
245
select_type => 'SIMPLE',
248
possible_keys => ['PRIMARY'],
255
"normalize() doesn't crash if EXPLAIN Extra is missing"
258
# #############################################################################
259
# Tests for trimming indexes out of possible_keys.
260
# #############################################################################
262
$exa->get_alternate_indexes(
264
[qw(index1 index2 index3 index4)],
267
'Normalizes alternate indexes',
270
# #############################################################################
271
# Tests for translating aliased names back to their real names.
272
# #############################################################################
274
# Putting it all together: given a query and an EXPLAIN, determine which indexes
277
$exa->get_index_usage(
278
query => "select * from film_actor as fa inner join sakila.actor as a "
279
. "on a.actor_id = fa.actor_id and a.last_name is not null "
280
. "where a.actor_id = 5 or film_id = 5",
282
explain => $exa->normalize(
285
select_type => 'SIMPLE',
287
type => 'index_merge',
288
possible_keys => 'PRIMARY,idx_fk_film_id',
289
key => 'PRIMARY,idx_fk_film_id',
293
Extra => 'Using union(PRIMARY,idx_fk_film_id); Using where',
296
select_type => 'SIMPLE',
299
possible_keys => 'PRIMARY,idx_actor_last_name',
302
ref => 'sakila.fa.actor_id',
304
Extra => 'Using where',
311
idx => [qw(PRIMARY idx_fk_film_id)],
316
idx => [qw(PRIMARY)],
317
alt => [qw(idx_actor_last_name)],
320
'Translate an EXPLAIN and a query into simplified index usage',
323
# This is kind of a pathological case.
325
$exa->get_index_usage(
326
query => "select 1 union select count(*) from actor a",
328
explain => $exa->normalize(
331
select_type => 'PRIMARY',
334
possible_keys => undef,
339
Extra => 'No tables used',
342
select_type => 'UNION',
345
possible_keys => undef,
350
Extra => 'Using index',
353
select_type => 'UNION RESULT',
354
table => '<union1,2>',
356
possible_keys => undef,
368
idx => [qw(PRIMARY)],
372
'Translate an EXPLAIN and a query for a harder case',
375
# Here's a query that uses a table but no indexes in it.
377
$exa->get_index_usage(
378
query => "select * from film_text",
380
explain => $exa->normalize(
383
select_type => 'SIMPLE',
384
table => 'film_text',
386
possible_keys => undef,
402
'Translate an EXPLAIN for a query that uses no indexes',
405
# #############################################################################
406
# Methods to save and retrieve index usage for a specific query and database.
407
# #############################################################################
409
$exa->get_usage_for('0xdeadbeef', 'sakila'),
411
'No usage recorded for 0xdeadbeef');
413
$exa->save_usage_for('0xdeadbeef', 'sakila',
416
idx => [qw(PRIMARY)],
422
$exa->get_usage_for('0xdeadbeef','sakila'),
425
idx => [qw(PRIMARY)],
429
'Got saved usage for 0xdeadbeef');
431
# #############################################################################
432
# Issue 1141: Add "spark charts" to mk-query-digest profile
433
# #############################################################################
435
$exa->sparkline(explain =>
438
select_type => 'PRIMARY',
441
possible_keys => ['idx'],
454
"sparkline: basic 1 table eq_ref"
458
$exa->sparkline(explain =>
461
select_type => 'PRIMARY',
464
possible_keys => ['idx'],
472
'Using filesort' => 1,
476
select_type => 'PRIMARY',
479
possible_keys => ['idx'],
490
"sparkline: 2 table with filesort at start"
494
$exa->sparkline(explain =>
497
select_type => 'PRIMARY',
500
possible_keys => ['idx'],
509
select_type => 'PRIMARY',
512
possible_keys => ['idx'],
518
'Using temporary' => 1,
519
'Using filesort' => 1,
525
"sparkline: 2 table with temp and filesort at end"
529
$exa->sparkline(explain =>
532
select_type => 'PRIMARY',
541
'No tables used' => 1,
545
select_type => 'UNION',
558
select_type => 'UNION RESULT',
559
table => '<union1,2>',
571
"sparkline: 3 tables, using index"
574
# #############################################################################
576
# #############################################################################