mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-09 18:30:16 +00:00
2461 lines
85 KiB
Perl
2461 lines
85 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 tests => 60;
|
|
|
|
use PerconaTest;
|
|
require "$trunk/bin/pt-visual-explain";
|
|
|
|
my $e = new ExplainTree;
|
|
my $t;
|
|
|
|
$t = $e->parse('');
|
|
is_deeply( $t, undef, 'No valid input' );
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/fulltext.sql") );
|
|
## Please see file perltidy.ERR
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Fulltext scan',
|
|
key_len => undef,
|
|
possible_keys => 'a',
|
|
ref => undef,
|
|
rows => '1',
|
|
partitions => undef,
|
|
key => 'foo->a'
|
|
},
|
|
{ type => 'Table',
|
|
table => 'foo',
|
|
partitions => undef,
|
|
possible_keys => 'a',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Fulltext query',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/impossible_where.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'IMPOSSIBLE',
|
|
id => 1,
|
|
rowid => 0,
|
|
warning => 'Impossible WHERE noticed after reading const tables',
|
|
},
|
|
'Impossible WHERE',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/impossible_having.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'IMPOSSIBLE',
|
|
id => 1,
|
|
rowid => 0,
|
|
warning => 'Impossible HAVING',
|
|
},
|
|
'Impossible HAVING',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/const_row_not_found.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'UNION RESULT',
|
|
children => [
|
|
{ type => 'Constant table access',
|
|
id => 1,
|
|
rowid => 0,
|
|
rows => undef,
|
|
warning => 'const row not found',
|
|
children => [
|
|
{ type => 'Table',
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
table => 't1',
|
|
},
|
|
],
|
|
},
|
|
{ type => 'SUBQUERY',
|
|
id => 2,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
id => 2,
|
|
rowid => 4,
|
|
rows => undef,
|
|
children => [
|
|
{ type => 'UNION',
|
|
table => 'union(<none>,t12)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'SUBQUERY',
|
|
children => [
|
|
{ type => 'SUBQUERY',
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
{ type => 'IMPOSSIBLE',
|
|
id => 3,
|
|
rowid => 2,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Constant table access',
|
|
warning => 'const row not found',
|
|
id => 4,
|
|
rowid => 3,
|
|
rows => undef,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 't12',
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Const row not found',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/dual_union_in_subquery.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'UNION RESULT',
|
|
children => [
|
|
{ id => 1,
|
|
type => 'DUAL',
|
|
rowid => 0,
|
|
},
|
|
{ id => 2,
|
|
type => 'DEPENDENT SUBQUERY',
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => undef,
|
|
rowid => 3,
|
|
id => 2,
|
|
children => [
|
|
{ type => 'UNION',
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
table => 'union(<none>,<none>)',
|
|
children => [
|
|
{ id => 2,
|
|
type => 'DEPENDENT SUBQUERY',
|
|
rowid => 1
|
|
},
|
|
{ id => 3,
|
|
type => 'DEPENDENT UNION',
|
|
rowid => 2
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
'UNION of DUAL in a subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/no_const_row.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Constant table access',
|
|
id => 1,
|
|
rowid => 0,
|
|
rows => undef,
|
|
warning => 'const row not found',
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 't1',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
'No constant row found',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/unique_row_not_found.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
rowid => 0,
|
|
id => 1,
|
|
children => [
|
|
{ key_len => '4',
|
|
possible_keys => 'PRIMARY',
|
|
ref => 'const',
|
|
type => 'Constant index lookup',
|
|
rows => '1',
|
|
partitions => undef,
|
|
key => 'user->PRIMARY'
|
|
},
|
|
{ possible_keys => 'PRIMARY',
|
|
table => 'user',
|
|
type => 'Table',
|
|
partitions => undef
|
|
}
|
|
],
|
|
},
|
|
{ type => 'Bookmark lookup',
|
|
rowid => 1,
|
|
warning => 'unique row not found',
|
|
id => 1,
|
|
children => [
|
|
{ key_len => '2',
|
|
possible_keys => 'PRIMARY',
|
|
ref => 'const',
|
|
type => 'Constant index lookup',
|
|
rows => undef,
|
|
partitions => undef,
|
|
key => 'avatar->PRIMARY'
|
|
},
|
|
{ possible_keys => 'PRIMARY',
|
|
table => 'avatar',
|
|
type => 'Table',
|
|
partitions => undef
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
{ type => 'Bookmark lookup',
|
|
rowid => 2,
|
|
warning => 'unique row not found',
|
|
id => 1,
|
|
children => [
|
|
{ key_len => '4',
|
|
possible_keys => 'PRIMARY',
|
|
ref => 'const',
|
|
type => 'Constant index lookup',
|
|
rows => undef,
|
|
partitions => undef,
|
|
key => 'customavatar->PRIMARY'
|
|
},
|
|
{ possible_keys => 'PRIMARY',
|
|
table => 'customavatar',
|
|
type => 'Table',
|
|
partitions => undef
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
'Unique row not found',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/no_min_max_row.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'IMPOSSIBLE',
|
|
warning => 'No matching min/max row',
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
'No min/max row',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/simple_partition.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filesort',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 10,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'trb1',
|
|
possible_keys => undef,
|
|
partitions => 'p0,p1,p2,p3',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Partition',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/full_scan_sakila_film.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
id => 1,
|
|
rowid => 0,
|
|
rows => 935,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
}
|
|
]
|
|
},
|
|
'Simple scan',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/actor_join_film_ref.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 952,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
}
|
|
],
|
|
},
|
|
{ type => 'Bookmark lookup',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
key_len => 2,
|
|
'ref' => 'sakila.film.film_id',
|
|
rows => 2,
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film_actor',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Simple join',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/join_buffer.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 10,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 't1',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
}
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 't2->key1',
|
|
key_len => 5,
|
|
'ref' => 'test.t1.col1',
|
|
rows => 2,
|
|
possible_keys => 'key1',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 't2',
|
|
possible_keys => 'key1',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Join buffer',
|
|
id => 1,
|
|
rowid => 2,
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 't3->key1',
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 40,
|
|
possible_keys => 'key1',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 't3',
|
|
possible_keys => 'key1',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Three-way join with buffer',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/range_check.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
rows => 5,
|
|
key => 'v->OXROOTID',
|
|
key_len => 32,
|
|
'ref' => 'const',
|
|
possible_keys => 'OXLEFT,OXRIGHT,OXROOTID',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'v',
|
|
possible_keys => 'OXLEFT,OXRIGHT,OXROOTID',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type => 'Re-evaluate indexes each row',
|
|
id => 1,
|
|
rowid => 1,
|
|
possible_keys => '3',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 5,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 's',
|
|
possible_keys => 'OXLEFT',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join that uses a range check',
|
|
);
|
|
|
|
is_deeply(
|
|
$e->parse( load_file("t/pt-visual-explain/samples/range_check_3.sql") ),
|
|
$t,
|
|
'Key map same when decimal as when hex',
|
|
);
|
|
|
|
is_deeply(
|
|
$e->parse( load_file("t/pt-visual-explain/samples/range_check_2.sql") ),
|
|
$t,
|
|
'Key map same as index map',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/not_exists.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 951,
|
|
id => 1,
|
|
rowid => 0,
|
|
key => 'film->idx_fk_language_id',
|
|
key_len => 1,
|
|
'ref' => undef,
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Distinct/Not-Exists',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
key_len => 2,
|
|
'ref' => 'sakila.film.film_id',
|
|
rows => 2,
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join that uses Not exists',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/join_temporary_with_where_distinct.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => undef,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'TEMPORARY',
|
|
table => 'temporary(film)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 951,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Distinct/Not-Exists',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'sakila.film.film_id',
|
|
rows => 2,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join that uses a temp table, WHERE, and Distinct',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/simple_join_three_tables.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_1->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
{ type => 'Unique index lookup',
|
|
key => 'actor_2->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'sakila.actor_1.actor_id',
|
|
rows => 1,
|
|
id => 1,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Unique index lookup',
|
|
key => 'actor_3->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'sakila.actor_1.actor_id',
|
|
rows => 1,
|
|
id => 1,
|
|
rowid => 2,
|
|
},
|
|
],
|
|
},
|
|
'Simple join over three tables',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/film_join_actor_eq_ref.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 5143,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'film_actor',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
]
|
|
},
|
|
{ type => 'Bookmark lookup',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Unique index lookup',
|
|
key => 'film->PRIMARY',
|
|
key_len => 2,
|
|
'ref' => 'sakila.film_actor.film_id',
|
|
rows => 1,
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Straight join',
|
|
);
|
|
|
|
$t = $e->parse(
|
|
load_file("t/pt-visual-explain/samples/film_join_actor_eq_ref.sql"),
|
|
{ clustered => 1 },
|
|
);
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 5143,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'film_actor',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
]
|
|
},
|
|
{ type => 'Unique index lookup',
|
|
id => 1,
|
|
rowid => 1,
|
|
key => 'film->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'sakila.film_actor.film_id',
|
|
rows => 1,
|
|
},
|
|
],
|
|
},
|
|
'Straight join assuming clustered PK',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/full_row_pk_lookup_sakila_film.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Bookmark lookup',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Constant index lookup',
|
|
key => 'film->PRIMARY',
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 1,
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
'Constant lookup',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/index_scan_sakila_film.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Bookmark lookup',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'film->idx_title',
|
|
key_len => 767,
|
|
'ref' => undef,
|
|
rows => 952,
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
'Index scan',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/index_scan_sakila_film_using_where.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'film->idx_title',
|
|
key_len => 767,
|
|
'ref' => undef,
|
|
rows => 952,
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index scan with WHERE clause',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/pk_lookup_sakila_film.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Constant index lookup',
|
|
key => 'film->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 1,
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
'PK lookup with covering index',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/film_join_actor_const.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Constant index lookup',
|
|
key => 'film->PRIMARY',
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 1,
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Bookmark lookup',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 10,
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film_actor',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join from constant lookup in film to const ref in film_actor',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/film_join_actor_const_using_index.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Constant index lookup',
|
|
key => 'film->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 1,
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 10,
|
|
id => 1,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
'Join from const film to const ref film_actor with covering index',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/film_range_on_pk.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 'film->PRIMARY',
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 20,
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index range scan with WHERE clause',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/loose_index_scan.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Loose index scan',
|
|
key => 'film->idx_fk_language_id',
|
|
key_len => 1,
|
|
'ref' => undef,
|
|
rows => 2,
|
|
id => 1,
|
|
rowid => 0,
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
'Loose index scan',
|
|
);
|
|
|
|
$t = $e->parse(
|
|
load_file("t/pt-visual-explain/samples/film_ref_or_null_on_original_language_id.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index lookup with extra null lookup',
|
|
key => 'film->idx_fk_original_language_id',
|
|
key_len => 2,
|
|
'ref' => 'const',
|
|
rows => 512,
|
|
possible_keys => 'idx_fk_original_language_id',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => 'idx_fk_original_language_id',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index ref_or_null scan',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/rental_index_merge_intersect.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index merge',
|
|
method => 'intersect',
|
|
rows => 1,
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 'rental->idx_fk_inventory_id',
|
|
possible_keys =>
|
|
'idx_fk_inventory_id,idx_fk_customer_id',
|
|
partitions => undef,
|
|
key_len => 3,
|
|
'ref' => undef,
|
|
rows => 1,
|
|
},
|
|
{ type => 'Index range scan',
|
|
key => 'rental->idx_fk_customer_id',
|
|
possible_keys =>
|
|
'idx_fk_inventory_id,idx_fk_customer_id',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 1,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Table',
|
|
table => 'rental',
|
|
possible_keys => 'idx_fk_inventory_id,idx_fk_customer_id',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index intersection merge',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/index_merge_three_keys.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Index merge',
|
|
method => 'intersect',
|
|
rows => 2,
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 't1->key1',
|
|
possible_keys => 'key1,key2,key3',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 2,
|
|
},
|
|
{ type => 'Index range scan',
|
|
key => 't1->key2',
|
|
possible_keys => 'key1,key2,key3',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 2,
|
|
},
|
|
{ type => 'Index range scan',
|
|
key => 't1->key3',
|
|
possible_keys => 'key1,key2,key3',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 2,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index intersection merge with three keys and covering index',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/index_merge_union_intersect.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index merge',
|
|
method => 'union',
|
|
rows => 154,
|
|
children => [
|
|
{ type => 'Index merge',
|
|
method => 'intersect',
|
|
rows => 154,
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 't1->key1',
|
|
possible_keys => 'key1,key2,key3,key4',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 154,
|
|
},
|
|
{ type => 'Index range scan',
|
|
key => 't1->key2',
|
|
possible_keys => 'key1,key2,key3,key4',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 154,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Index merge',
|
|
method => 'intersect',
|
|
rows => 154,
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 't1->key3',
|
|
possible_keys => 'key1,key2,key3,key4',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 154,
|
|
},
|
|
{ type => 'Index range scan',
|
|
key => 't1->key4',
|
|
possible_keys => 'key1,key2,key3,key4',
|
|
partitions => undef,
|
|
key_len => 5,
|
|
'ref' => undef,
|
|
rows => 154,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Table',
|
|
table => 't1',
|
|
possible_keys => 'key1,key2,key3,key4',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index merge union-intersection',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/index_merge_sort_union.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index merge',
|
|
method => 'sort_union',
|
|
rows => 45,
|
|
children => [
|
|
{ type => 'Index range scan',
|
|
key => 't0->i1',
|
|
possible_keys => 'i1,i2',
|
|
partitions => undef,
|
|
key_len => 4,
|
|
'ref' => undef,
|
|
rows => 45,
|
|
},
|
|
{ type => 'Index range scan',
|
|
key => 't0->i2',
|
|
possible_keys => 'i1,i2',
|
|
partitions => undef,
|
|
key_len => 4,
|
|
'ref' => undef,
|
|
rows => 45,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Table',
|
|
table => 't0',
|
|
possible_keys => 'i1,i2',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Index merge sort_union',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/optimized_away.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'CONSTANT',
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
'No tables used - constant',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/no_from.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DUAL',
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
'No tables used - no FROM',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/filesort.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filesort',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 951,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'film',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Filesort',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/temporary_filesort.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filesort',
|
|
children => [
|
|
{ type => 'TEMPORARY',
|
|
table => 'temporary(film)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'film->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 951,
|
|
rowid => 0,
|
|
id => 1,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Filesort with temporary',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/filesort_on_subsequent_tbl.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Constant table access',
|
|
rows => '1',
|
|
rowid => 0,
|
|
id => 1,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'const_tbl',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filesort',
|
|
rowid => 1,
|
|
id => 1,
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => '10',
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 't1',
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
rowid => 2,
|
|
id => 1,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 't2->a',
|
|
key_len => '5',
|
|
possible_keys => 'a',
|
|
ref => 'test4.t1.a',
|
|
rows => '11',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 't2',
|
|
possible_keys => 'a',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Filesort on first non-constant table',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/three_table_join_with_temp_filesort.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Filesort',
|
|
children => [
|
|
{ type => 'TEMPORARY',
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
table => 'temporary(actor,film_actor,film)',
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
key_len => '2',
|
|
ref => undef,
|
|
rows => '200',
|
|
partitions => undef,
|
|
rowid => 0,
|
|
id => 1,
|
|
},
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->PRIMARY',
|
|
key_len => '2',
|
|
ref => 'sakila.actor.actor_id',
|
|
rows => '13',
|
|
partitions => undef,
|
|
possible_keys => 'PRIMARY,idx_fk_film_id',
|
|
rowid => 1,
|
|
id => 1
|
|
}
|
|
],
|
|
},
|
|
{ type => 'Unique index lookup',
|
|
key => 'film->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
key_len => '2',
|
|
ref => 'sakila.film_actor.film_id',
|
|
rows => '1',
|
|
partitions => undef,
|
|
rowid => 2,
|
|
id => 1,
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
'Filesort with temporary',
|
|
);
|
|
|
|
eval {
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/too_many_unions.sql") );
|
|
};
|
|
like($EVAL_ERROR, qr/UNION has too many tables/, 'Too many unions');
|
|
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/simple_union.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
rows => undef,
|
|
id => 1,
|
|
rowid => 2,
|
|
children => [
|
|
{ type => 'UNION',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
table => 'union(actor_1,actor_2)',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_1->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_2->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Simple union',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/derived_over_bookmark_lookup.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
rows => 10,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(film_actor)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
id => 2,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 10,
|
|
},
|
|
{ type => 'Table',
|
|
table => 'film_actor',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Derived table over a bookmark lookup',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/simple_derived.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(actor)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Simple derived table',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/derived_over_join.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
rows => 40000,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(actor_1,actor_2)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_1->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_2->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 2,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Simple derived table over a simple join',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/join_two_derived_tables_of_joins.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 40000,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(actor_1,actor_2)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_1->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 4,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_2->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 5,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 40000,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(actor_3,actor_4)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_3->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 3,
|
|
rowid => 2,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_4->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 3,
|
|
rowid => 3,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join two derived tables which each contain a join',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/union_of_derived_tables.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
rows => undef,
|
|
id => 1,
|
|
rowid => 4,
|
|
children => [
|
|
{ type => 'UNION',
|
|
table => 'union(derived(actor),derived(film))',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
id => 1,
|
|
rowid => 0,
|
|
rows => 200,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(actor)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Table scan',
|
|
id => 3,
|
|
rowid => 2,
|
|
rows => 1000,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(film)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'film->idx_fk_language_id',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 1,
|
|
'ref' => undef,
|
|
rows => 951,
|
|
id => 4,
|
|
rowid => 3,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Union over two derived tables',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/join_two_derived_tables_of_unions.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Constant table access',
|
|
id => 1,
|
|
rowid => 0,
|
|
rows => 1,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(union(actor_1,actor_2))',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
id => 2,
|
|
rowid => 7,
|
|
rows => undef,
|
|
children => [
|
|
{ type => 'UNION',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
table => 'union(actor_1,actor_2)',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_1->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 5,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_2->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 3,
|
|
rowid => 6,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Constant table access',
|
|
id => 1,
|
|
rowid => 1,
|
|
rows => 1,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(union(actor_3,actor_4))',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
id => 4,
|
|
rowid => 4,
|
|
rows => undef,
|
|
children => [
|
|
{ type => 'UNION',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
table => 'union(actor_3,actor_4)',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_3->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 4,
|
|
rowid => 2,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_4->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 5,
|
|
rowid => 3,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join over two derived tables of unions',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/union_of_derived_unions.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'Table scan',
|
|
rows => undef,
|
|
id => 1,
|
|
rowid => 8,
|
|
children => [
|
|
{ type => 'UNION',
|
|
table =>
|
|
'union(derived(union(actor_1,actor_2)),derived(union(actor_3,actor_4)))',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Constant table access',
|
|
id => 1,
|
|
rowid => 0,
|
|
rows => 1,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(union(actor_1,actor_2))',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
id => 2,
|
|
rowid => 3,
|
|
rows => undef,
|
|
children => [
|
|
{ type => 'UNION',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
table => 'union(actor_1,actor_2)',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_1->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_2->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 3,
|
|
rowid => 2,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Table scan',
|
|
id => 4,
|
|
rowid => 4,
|
|
rows => 400,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(union(actor_3,actor_4))',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => undef,
|
|
id => 5,
|
|
rowid => 7,
|
|
children => [
|
|
{ type => 'UNION',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
table => 'union(actor_3,actor_4)',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor_3->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 5,
|
|
rowid => 5,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor_4->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 6,
|
|
rowid => 6,
|
|
},
|
|
],
|
|
},
|
|
]
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Union over two derived tables of unions',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/simple_subquery.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'SUBQUERY',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
key_len => 2,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'film->idx_fk_language_id',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 1,
|
|
'ref' => undef,
|
|
rows => 951,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
]
|
|
},
|
|
'Simple subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/dependent_subquery.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
key_len => 2,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 2,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'actor.actor_id',
|
|
rows => 13,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Dependent subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/uncacheable_subquery.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'UNCACHEABLE SUBQUERY',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
key_len => 2,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => undef,
|
|
rows => 200,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
'Dependent uncacheable subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/join_in_subquery.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'SUBQUERY',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 200,
|
|
id => 1,
|
|
rowid => 0,
|
|
key_len => 2,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
},
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'film->idx_fk_language_id',
|
|
key_len => 1,
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
rows => 951,
|
|
id => 2,
|
|
rowid => 1,
|
|
},
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->idx_fk_film_id',
|
|
possible_keys => 'idx_fk_film_id',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'sakila.film.film_id',
|
|
rows => 2,
|
|
id => 2,
|
|
rowid => 2,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Join inside a subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/unique_subquery_in_where_clause.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 5143,
|
|
key_len => 2,
|
|
key => 'film_actor->idx_fk_film_id',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Unique subquery',
|
|
rows => 1,
|
|
id => 2,
|
|
rowid => 1,
|
|
key_len => 2,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => 'func',
|
|
},
|
|
],
|
|
},
|
|
'Unique subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/index_subquery_in_where_clause.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Index scan',
|
|
rows => 200,
|
|
key_len => 2,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
'ref' => undef,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Index subquery',
|
|
rows => 13,
|
|
id => 2,
|
|
rowid => 1,
|
|
key_len => 2,
|
|
key => 'film_actor->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => 'func',
|
|
},
|
|
],
|
|
},
|
|
'Index subquery',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/full_scan_on_null_key.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 4,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 't1',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'JOIN',
|
|
children => [
|
|
{ type => 'Filter with WHERE',
|
|
id => 2,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'Unique index lookup',
|
|
rows => 1,
|
|
key_len => 4,
|
|
key => 't2->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => 'func',
|
|
warning => 'Full scan on NULL key',
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 2,
|
|
rowid => 2,
|
|
children => [
|
|
{ type => 'Bookmark lookup',
|
|
children => [
|
|
{ type => 'Unique index lookup',
|
|
rows => 1,
|
|
key_len => 4,
|
|
key => 't3->PRIMARY',
|
|
'ref' => 'func',
|
|
warning => 'Full scan on NULL key',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
{ type => 'Table',
|
|
table => 't3',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Subqueries that do a full scan on a NULL key',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/nested_derived_tables.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 1000,
|
|
id => 1,
|
|
rowid => 0,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(derived(inner_der,inner_sub),mid_sub)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 1000,
|
|
id => 3,
|
|
rowid => 1,
|
|
children => [
|
|
{ type => 'DERIVED',
|
|
table => 'derived(inner_der,inner_sub)',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'Table scan',
|
|
rows => 951,
|
|
id => 5,
|
|
rowid => 2,
|
|
children => [
|
|
{ type => 'Table',
|
|
table => 'inner_der',
|
|
possible_keys => undef,
|
|
partitions => undef,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 6,
|
|
rowid => 3,
|
|
children => [
|
|
{ type => 'Unique index lookup',
|
|
rows => 1,
|
|
key_len => 2,
|
|
key => 'inner_sub->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => 'inner_der.film_id',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 4,
|
|
rowid => 4,
|
|
children => [
|
|
{ type => 'Unique index lookup',
|
|
rows => 1,
|
|
key_len => 2,
|
|
key => 'mid_sub->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => 'mid_der.film_id',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 2,
|
|
rowid => 5,
|
|
children => [
|
|
{ type => 'Unique index lookup',
|
|
rows => 1,
|
|
key_len => 2,
|
|
key => 'outer_sub->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
'ref' => 'outer_der.film_id',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Nested derived tables and subqueries',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/adjacent_subqueries.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ type => 'SUBQUERY',
|
|
children => [
|
|
{ type => 'Index scan',
|
|
key => 'actor->PRIMARY',
|
|
key_len => 2,
|
|
rows => 200,
|
|
'ref' => undef,
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
id => 1,
|
|
rowid => 0,
|
|
},
|
|
{ type => 'Index scan',
|
|
key => 'f->idx_fk_language_id',
|
|
key_len => 1,
|
|
rows => 951,
|
|
'ref' => undef,
|
|
partitions => undef,
|
|
possible_keys => undef,
|
|
id => 3,
|
|
rowid => 1,
|
|
},
|
|
],
|
|
},
|
|
{ type => 'Filter with WHERE',
|
|
id => 2,
|
|
rowid => 2,
|
|
children => [
|
|
{ type => 'Index lookup',
|
|
key => 'film_actor->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
partitions => undef,
|
|
key_len => 2,
|
|
'ref' => 'actor.actor_id',
|
|
rows => 13,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
'Adjacent subqueries',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/complex_select_types.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ id => '1',
|
|
type => 'Table scan',
|
|
rows => undef,
|
|
rowid => 7,
|
|
children => [
|
|
{ possible_keys => undef,
|
|
table => 'union(derived(actor),film_actor,derived(film,store),rental)',
|
|
type => 'UNION',
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'DEPENDENT SUBQUERY',
|
|
children => [
|
|
{ id => 1,
|
|
type => 'Table scan',
|
|
rows => '5',
|
|
rowid => 0,
|
|
children => [
|
|
{ possible_keys => undef,
|
|
table => 'derived(actor)',
|
|
type => 'DERIVED',
|
|
partitions => undef,
|
|
children => [
|
|
{ key_len => '2',
|
|
ref => undef,
|
|
rows => '200',
|
|
partitions => undef,
|
|
rowid => 1,
|
|
key => 'actor->PRIMARY',
|
|
possible_keys => undef,
|
|
type => 'Index scan',
|
|
id => 3
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
{ key_len => '2',
|
|
ref => 'der_1.actor_id',
|
|
rows => '13',
|
|
partitions => undef,
|
|
rowid => 2,
|
|
key => 'film_actor->PRIMARY',
|
|
possible_keys => 'PRIMARY',
|
|
type => 'Index lookup',
|
|
id => 2
|
|
}
|
|
],
|
|
},
|
|
{ type => 'UNCACHEABLE SUBQUERY',
|
|
children => [
|
|
{ id => 4,
|
|
type => 'Table scan',
|
|
rows => '5',
|
|
rowid => 3,
|
|
children => [
|
|
{ possible_keys => undef,
|
|
table => 'derived(film,store)',
|
|
type => 'DERIVED',
|
|
partitions => undef,
|
|
children => [
|
|
{ type => 'SUBQUERY',
|
|
children => [
|
|
{ key_len => '1',
|
|
ref => undef,
|
|
rows => '1022',
|
|
partitions => undef,
|
|
rowid => 4,
|
|
key => 'film->idx_fk_language_id',
|
|
possible_keys => undef,
|
|
type => 'Index scan',
|
|
id => 6
|
|
},
|
|
{ key_len => '1',
|
|
ref => undef,
|
|
rows => '2',
|
|
partitions => undef,
|
|
rowid => 5,
|
|
key => 'store->PRIMARY',
|
|
possible_keys => undef,
|
|
type => 'Index scan',
|
|
id => 7
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
{ key_len => '1',
|
|
ref => undef,
|
|
rows => '16305',
|
|
partitions => undef,
|
|
rowid => 6,
|
|
key => 'rental->idx_fk_staff_id',
|
|
possible_keys => undef,
|
|
type => 'Index scan',
|
|
id => 5
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
'Complex SELECT types combined',
|
|
);
|
|
|
|
$t = $e->parse( load_file("t/pt-visual-explain/samples/derived_without_table.sql") );
|
|
is_deeply(
|
|
$t,
|
|
{ id => 1,
|
|
type => 'Constant table access',
|
|
rows => '1',
|
|
rowid => 0,
|
|
children => [
|
|
{ possible_keys => undef,
|
|
table => 'derived(<none>)',
|
|
type => 'DERIVED',
|
|
partitions => undef,
|
|
children => [
|
|
{ id => 2,
|
|
type => 'DERIVED',
|
|
rowid => 1
|
|
}
|
|
],
|
|
}
|
|
],
|
|
},
|
|
'Recursive table name with anonymous derived table',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Done.
|
|
# #############################################################################
|
|
exit;
|