~percona-toolkit-dev/percona-toolkit/release-2.2.2

« back to all changes in this revision

Viewing changes to t/lib/ExplainAnalyzer.t

  • Committer: Daniel Nichter
  • Date: 2011-06-24 17:22:06 UTC
  • Revision ID: daniel@percona.com-20110624172206-c7q4s4ad6r260zz6
Add lib/, t/lib/, and sandbox/.  All modules are updated and passing on MySQL 5.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl
 
2
 
 
3
BEGIN {
 
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";
 
7
};
 
8
 
 
9
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
 
10
use strict;
 
11
use warnings FATAL => 'all';
 
12
use English qw(-no_match_vars);
 
13
use Data::Dumper;
 
14
$Data::Dumper::Indent    = 1;
 
15
$Data::Dumper::Sortkeys  = 1;
 
16
$Data::Dumper::Quotekeys = 0;
 
17
 
 
18
use Test::More;
 
19
 
 
20
use ExplainAnalyzer;
 
21
use QueryRewriter;
 
22
use QueryParser;
 
23
use DSNParser;
 
24
use Sandbox;
 
25
use MaatkitTest;
 
26
 
 
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});
 
30
 
 
31
if ( !$dbh ) {
 
32
   plan skip_all => "Cannot connect to sandbox master";
 
33
}
 
34
else {
 
35
   plan tests => 16;
 
36
}
 
37
 
 
38
$dbh->do('use sakila');
 
39
 
 
40
my $qr  = new QueryRewriter();
 
41
my $qp  = new QueryParser();
 
42
my $exa = new ExplainAnalyzer(QueryRewriter => $qr, QueryParser => $qp);
 
43
 
 
44
# #############################################################################
 
45
# Tests for getting an EXPLAIN from a database.
 
46
# #############################################################################
 
47
 
 
48
is_deeply(
 
49
   $exa->explain_query(
 
50
      dbh   => $dbh,
 
51
      query => 'select * from actor where actor_id = 5',
 
52
   ),
 
53
   [
 
54
      { id            => 1,
 
55
        select_type   => 'SIMPLE',
 
56
        table         => 'actor',
 
57
        type          => 'const',
 
58
        possible_keys => 'PRIMARY',
 
59
        key           => 'PRIMARY',
 
60
        key_len       => 2,
 
61
        ref           => 'const',
 
62
        rows          => 1,
 
63
        Extra         => '',
 
64
      },
 
65
   ],
 
66
   'Got a simple EXPLAIN result',
 
67
);
 
68
 
 
69
is_deeply(
 
70
   $exa->explain_query(
 
71
      dbh   => $dbh,
 
72
      query => 'delete from actor where actor_id = 5',
 
73
   ),
 
74
   [
 
75
      { id            => 1,
 
76
        select_type   => 'SIMPLE',
 
77
        table         => 'actor',
 
78
        type          => 'const',
 
79
        possible_keys => 'PRIMARY',
 
80
        key           => 'PRIMARY',
 
81
        key_len       => 2,
 
82
        ref           => 'const',
 
83
        rows          => 1,
 
84
        Extra         => '',
 
85
      },
 
86
   ],
 
87
   'Got EXPLAIN result for a DELETE',
 
88
);
 
89
 
 
90
is(
 
91
   $exa->explain_query(
 
92
      dbh   => $dbh,
 
93
      query => 'insert into t values (1)',
 
94
   ),
 
95
   undef,
 
96
   "Doesn't EXPLAIN non-convertable non-SELECT"
 
97
);
 
98
 
 
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;
 
106
 
 
107
# #############################################################################
 
108
# Tests for normalizing raw EXPLAIN into a format that's easier to work with.
 
109
# #############################################################################
 
110
is_deeply(
 
111
   $exa->normalize(
 
112
      [
 
113
         { id            => 1,
 
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',
 
119
           key_len       => '2,2',
 
120
           ref           => undef,
 
121
           rows          => 34,
 
122
           Extra         => 'Using union(PRIMARY,idx_fk_film_id); Using where',
 
123
         },
 
124
      ],
 
125
   ),
 
126
   [
 
127
      { id            => 1,
 
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)],
 
133
        key_len       => [2,2],
 
134
        ref           => [qw()],
 
135
        rows          => 34,
 
136
        Extra         => {
 
137
           'Using union' => [qw(PRIMARY idx_fk_film_id)],
 
138
           'Using where' => 1,
 
139
        },
 
140
      },
 
141
   ],
 
142
   'Normalizes an EXPLAIN',
 
143
);
 
144
 
 
145
is_deeply(
 
146
   $exa->normalize(
 
147
      [
 
148
         { id            => 1,
 
149
           select_type   => 'PRIMARY',
 
150
           table         => undef,
 
151
           type          => undef,
 
152
           possible_keys => undef,
 
153
           key           => undef,
 
154
           key_len       => undef,
 
155
           ref           => undef,
 
156
           rows          => undef,
 
157
           Extra         => 'No tables used',
 
158
         },
 
159
         { id            => 1,
 
160
           select_type   => 'UNION',
 
161
           table         => 'a',
 
162
           type          => 'index',
 
163
           possible_keys => undef,
 
164
           key           => 'PRIMARY',
 
165
           key_len       => '2',
 
166
           ref           => undef,
 
167
           rows          => 200,
 
168
           Extra         => 'Using index',
 
169
         },
 
170
         { id            => undef,
 
171
           select_type   => 'UNION RESULT',
 
172
           table         => '<union1,2>',
 
173
           type          => 'ALL',
 
174
           possible_keys => undef,
 
175
           key           => undef,
 
176
           key_len       => undef,
 
177
           ref           => undef,
 
178
           rows          => undef,
 
179
           Extra         => '',
 
180
         },
 
181
      ],
 
182
   ),
 
183
   [
 
184
      { id            => 1,
 
185
        select_type   => 'PRIMARY',
 
186
        table         => undef,
 
187
        type          => undef,
 
188
        possible_keys => [],
 
189
        key           => [],
 
190
        key_len       => [],
 
191
        ref           => [],
 
192
        rows          => undef,
 
193
        Extra         => {
 
194
           'No tables used' => 1,
 
195
        },
 
196
      },
 
197
      { id            => 1,
 
198
        select_type   => 'UNION',
 
199
        table         => 'a',
 
200
        type          => 'index',
 
201
        possible_keys => [],
 
202
        key           => ['PRIMARY'],
 
203
        key_len       => [2],
 
204
        ref           => [],
 
205
        rows          => 200,
 
206
        Extra         => {
 
207
         'Using index' => 1,
 
208
        },
 
209
      },
 
210
      { id            => undef,
 
211
        select_type   => 'UNION RESULT',
 
212
        table         => '<union1,2>',
 
213
        type          => 'ALL',
 
214
        possible_keys => [],
 
215
        key           => [],
 
216
        key_len       => [],
 
217
        ref           => [],
 
218
        rows          => undef,
 
219
        Extra         => {},
 
220
      },
 
221
   ],
 
222
   'Normalizes a more complex EXPLAIN',
 
223
);
 
224
 
 
225
is_deeply(
 
226
   $exa->normalize(
 
227
      [
 
228
         { id            => 1,
 
229
           select_type   => 'SIMPLE',
 
230
           table         => 't1',
 
231
           type          => 'ALL',
 
232
           possible_keys => 'PRIMARY',
 
233
           key           => undef,
 
234
           key_len       => undef,
 
235
           ref           => undef,
 
236
           rows          => '4',
 
237
           # Extra         => 'Using where; Using temporary; Using filesort',
 
238
         },
 
239
      ],
 
240
   ),
 
241
   [
 
242
      {
 
243
         Extra          => {},  # is auto-vivified
 
244
         id             => 1,
 
245
         select_type    => 'SIMPLE',
 
246
         table          => 't1',
 
247
         type           => 'ALL',
 
248
         possible_keys  => ['PRIMARY'],
 
249
         key            => [],
 
250
         key_len        => [],
 
251
         ref            => [],
 
252
         rows           => '4',
 
253
      }
 
254
   ],
 
255
   "normalize() doesn't crash if EXPLAIN Extra is missing"
 
256
);
 
257
 
 
258
# #############################################################################
 
259
# Tests for trimming indexes out of possible_keys.
 
260
# #############################################################################
 
261
is_deeply(
 
262
   $exa->get_alternate_indexes(
 
263
      [qw(index1 index2)],
 
264
      [qw(index1 index2 index3 index4)],
 
265
   ),
 
266
   [qw(index3 index4)],
 
267
   'Normalizes alternate indexes',
 
268
);
 
269
 
 
270
# #############################################################################
 
271
# Tests for translating aliased names back to their real names.
 
272
# #############################################################################
 
273
 
 
274
# Putting it all together: given a query and an EXPLAIN, determine which indexes
 
275
# the query used.
 
276
is_deeply(
 
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",
 
281
      db    => 'sakila',
 
282
      explain => $exa->normalize(
 
283
         [
 
284
            { id            => 1,
 
285
              select_type   => 'SIMPLE',
 
286
              table         => 'fa',
 
287
              type          => 'index_merge',
 
288
              possible_keys => 'PRIMARY,idx_fk_film_id',
 
289
              key           => 'PRIMARY,idx_fk_film_id',
 
290
              key_len       => '2,2',
 
291
              ref           => undef,
 
292
              rows          => 34,
 
293
              Extra         => 'Using union(PRIMARY,idx_fk_film_id); Using where',
 
294
            },
 
295
            { id            => 1,
 
296
              select_type   => 'SIMPLE',
 
297
              table         => 'a',
 
298
              type          => 'eq_ref',
 
299
              possible_keys => 'PRIMARY,idx_actor_last_name',
 
300
              key           => 'PRIMARY',
 
301
              key_len       => '2',
 
302
              ref           => 'sakila.fa.actor_id',
 
303
              rows          => 1,
 
304
              Extra         => 'Using where',
 
305
            },
 
306
         ],
 
307
      ),
 
308
   ),
 
309
   [  {  db  => 'sakila',
 
310
         tbl => 'film_actor',
 
311
         idx => [qw(PRIMARY idx_fk_film_id)],
 
312
         alt => [],
 
313
      },
 
314
      {  db  => 'sakila',
 
315
         tbl => 'actor',
 
316
         idx => [qw(PRIMARY)],
 
317
         alt => [qw(idx_actor_last_name)],
 
318
      },
 
319
   ],
 
320
   'Translate an EXPLAIN and a query into simplified index usage',
 
321
);
 
322
 
 
323
# This is kind of a pathological case.
 
324
is_deeply(
 
325
   $exa->get_index_usage(
 
326
      query   => "select 1 union select count(*) from actor a",
 
327
      db      => 'sakila',
 
328
      explain => $exa->normalize(
 
329
         [
 
330
            { id            => 1,
 
331
              select_type   => 'PRIMARY',
 
332
              table         => undef,
 
333
              type          => undef,
 
334
              possible_keys => undef,
 
335
              key           => undef,
 
336
              key_len       => undef,
 
337
              ref           => undef,
 
338
              rows          => undef,
 
339
              Extra         => 'No tables used',
 
340
            },
 
341
            { id            => 1,
 
342
              select_type   => 'UNION',
 
343
              table         => 'a',
 
344
              type          => 'index',
 
345
              possible_keys => undef,
 
346
              key           => 'PRIMARY',
 
347
              key_len       => '2',
 
348
              ref           => undef,
 
349
              rows          => 200,
 
350
              Extra         => 'Using index',
 
351
            },
 
352
            { id            => undef,
 
353
              select_type   => 'UNION RESULT',
 
354
              table         => '<union1,2>',
 
355
              type          => 'ALL',
 
356
              possible_keys => undef,
 
357
              key           => undef,
 
358
              key_len       => undef,
 
359
              ref           => undef,
 
360
              rows          => undef,
 
361
              Extra         => '',
 
362
            },
 
363
         ],
 
364
      ),
 
365
   ),
 
366
   [  {  db  => 'sakila',
 
367
         tbl => 'actor',
 
368
         idx => [qw(PRIMARY)],
 
369
         alt => [],
 
370
      },
 
371
   ],
 
372
   'Translate an EXPLAIN and a query for a harder case',
 
373
);
 
374
 
 
375
# Here's a query that uses a table but no indexes in it.
 
376
is_deeply(
 
377
   $exa->get_index_usage(
 
378
      query   => "select * from film_text",
 
379
      db      => 'sakila',
 
380
      explain => $exa->normalize(
 
381
         [
 
382
            { id            => 1,
 
383
              select_type   => 'SIMPLE',
 
384
              table         => 'film_text',
 
385
              type          => 'ALL',
 
386
              possible_keys => undef,
 
387
              key           => undef,
 
388
              key_len       => undef,
 
389
              ref           => undef,
 
390
              rows          => 1000,
 
391
              Extra         => '',
 
392
            },
 
393
         ],
 
394
      ),
 
395
   ),
 
396
   [  {  db  => 'sakila',
 
397
         tbl => 'film_text',
 
398
         idx => [],
 
399
         alt => [],
 
400
      },
 
401
   ],
 
402
   'Translate an EXPLAIN for a query that uses no indexes',
 
403
);
 
404
 
 
405
# #############################################################################
 
406
# Methods to save and retrieve index usage for a specific query and database.
 
407
# #############################################################################
 
408
is_deeply(
 
409
   $exa->get_usage_for('0xdeadbeef', 'sakila'),
 
410
   undef,
 
411
   'No usage recorded for 0xdeadbeef');
 
412
 
 
413
$exa->save_usage_for('0xdeadbeef', 'sakila',
 
414
   [  {  db  => 'sakila',
 
415
         tbl => 'actor',
 
416
         idx => [qw(PRIMARY)],
 
417
         alt => [],
 
418
      },
 
419
   ]);
 
420
 
 
421
is_deeply(
 
422
   $exa->get_usage_for('0xdeadbeef','sakila'),
 
423
   [  {  db  => 'sakila',
 
424
         tbl => 'actor',
 
425
         idx => [qw(PRIMARY)],
 
426
         alt => [],
 
427
      },
 
428
   ],
 
429
   'Got saved usage for 0xdeadbeef');
 
430
 
 
431
# #############################################################################
 
432
# Issue 1141: Add "spark charts" to mk-query-digest profile
 
433
# #############################################################################
 
434
is(
 
435
   $exa->sparkline(explain =>
 
436
      [
 
437
         { id            => 1,
 
438
           select_type   => 'PRIMARY',
 
439
           table         => 'foo',
 
440
           type          => 'eq_ref',
 
441
           possible_keys => ['idx'],
 
442
           key           => ['idx'],
 
443
           key_len       => [10],
 
444
           ref           => [],
 
445
           rows          => 100,
 
446
           Extra         => {
 
447
              'Using index' => 1,
 
448
              'Using where' => 1,
 
449
           },
 
450
         },
 
451
      ],
 
452
   ),
 
453
   "E",
 
454
   "sparkline: basic 1 table eq_ref"
 
455
);
 
456
 
 
457
is(
 
458
   $exa->sparkline(explain =>
 
459
      [
 
460
         { id            => 1,
 
461
           select_type   => 'PRIMARY',
 
462
           table         => 'foo',
 
463
           type          => 'eq_ref',
 
464
           possible_keys => ['idx'],
 
465
           key           => ['idx'],
 
466
           key_len       => [10],
 
467
           ref           => [],
 
468
           rows          => 100,
 
469
           Extra         => {
 
470
              'Using index'    => 1,
 
471
              'Using where'    => 1,
 
472
              'Using filesort' => 1,
 
473
           },
 
474
         },
 
475
         { id            => 2,
 
476
           select_type   => 'PRIMARY',
 
477
           table         => 'bar',
 
478
           type          => 'ref',
 
479
           possible_keys => ['idx'],
 
480
           key           => ['idx'],
 
481
           key_len       => [10],
 
482
           ref           => ['foo.col'],
 
483
           rows          => 100,
 
484
           Extra         => {
 
485
           },
 
486
         },
 
487
      ],
 
488
   ),
 
489
   "F>Er",
 
490
   "sparkline: 2 table with filesort at start"
 
491
);
 
492
 
 
493
is(
 
494
   $exa->sparkline(explain =>
 
495
      [
 
496
         { id            => 1,
 
497
           select_type   => 'PRIMARY',
 
498
           table         => 'foo',
 
499
           type          => 'range',
 
500
           possible_keys => ['idx'],
 
501
           key           => ['idx'],
 
502
           key_len       => [10],
 
503
           ref           => [],
 
504
           rows          => 100,
 
505
           Extra         => {
 
506
           },
 
507
         },
 
508
         { id            => 2,
 
509
           select_type   => 'PRIMARY',
 
510
           table         => 'bar',
 
511
           type          => 'ref',
 
512
           possible_keys => ['idx'],
 
513
           key           => ['idx'],
 
514
           key_len       => [10],
 
515
           ref           => ['foo.col'],
 
516
           rows          => 100,
 
517
           Extra         => {
 
518
              'Using temporary' => 1,
 
519
              'Using filesort'  => 1,
 
520
           },
 
521
         },
 
522
      ],
 
523
   ),
 
524
   "nr>TF",
 
525
   "sparkline: 2 table with temp and filesort at end"
 
526
);
 
527
 
 
528
is(
 
529
   $exa->sparkline(explain =>
 
530
      [
 
531
         { id            => 1,
 
532
           select_type   => 'PRIMARY',
 
533
           table         => undef,
 
534
           type          => undef,
 
535
           possible_keys => [],
 
536
           key           => [],
 
537
           key_len       => [],
 
538
           ref           => [],
 
539
           rows          => undef,
 
540
           Extra         => {
 
541
              'No tables used' => 1,
 
542
           },
 
543
         },
 
544
         { id            => 1,
 
545
           select_type   => 'UNION',
 
546
           table         => 'a',
 
547
           type          => 'index',
 
548
           possible_keys => [],
 
549
           key           => ['PRIMARY'],
 
550
           key_len       => [2],
 
551
           ref           => [],
 
552
           rows          => 200,
 
553
           Extra         => {
 
554
            'Using index' => 1,
 
555
           },
 
556
         },
 
557
         { id            => undef,
 
558
           select_type   => 'UNION RESULT',
 
559
           table         => '<union1,2>',
 
560
           type          => 'ALL',
 
561
           possible_keys => [],
 
562
           key           => [],
 
563
           key_len       => [],
 
564
           ref           => [],
 
565
           rows          => undef,
 
566
           Extra         => {},
 
567
         },
 
568
      ],
 
569
   ),
 
570
   "-Ia",
 
571
   "sparkline: 3 tables, using index"
 
572
);
 
573
 
 
574
# #############################################################################
 
575
# Done.
 
576
# #############################################################################
 
577
exit;