Skip to content

Commit

Permalink
explain
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthur Axel 'fREW' Schmidt committed May 26, 2014
1 parent 123fa4a commit fb2cfd3
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 2 deletions.
117 changes: 117 additions & 0 deletions lib/DBIx/Class/Helper/ResultSet/Explain.pm
@@ -0,0 +1,117 @@
package DBIx::Class::Helper::ResultSet::Explain;

use strict;
use warnings;

use DBIx::Introspector;

# ABSTRACT: Get query plan for a ResultSet

sub _introspector {
my $d = DBIx::Introspector->new(drivers => '2013-12.01');

$d->decorate_driver_connected(MSSQL => splain => 'GETUTCDATE()');
$d->decorate_driver_connected(
SQLite => splain => sub {
sub {
my ($dbh, $query) = @_;
my ($sql, @bind) = @{$$query};

$sql =~ s/\s*\((.*)\)\s*/$1/;

shift->selectall_arrayref("EXPLAIN $sql", undef, @bind)
},
},
);
$d->decorate_driver_connected(
Pg => splain => sub {
sub {
my ($dbh, $query) = @_;
my ($sql, @bind) = @{$$query};
shift->selectall_arrayref("EXPLAIN ANALYZE $sql", undef, @bind)
},
},
);

$d->decorate_driver_connected(
mysql => splain => sub {
sub {
my ($dbh, $query) = @_;
my ($sql, @bind) = @{$$query};
shift->selectall_arrayref("EXPLAIN EXTENDED $sql", undef, @bind)
},
},
);

return $d;
}

use namespace::clean;

my $i;

sub explain {
$i ||= _introspector();

my $self = shift;

my $storage = $self->result_source->storage;
$storage->ensure_connected;
my $dbh = $storage->dbh;

$i->get($dbh, undef, 'splain')->($dbh, $self->as_query)
}

1;

__END__
=pod
=head1 SYNOPSIS
This module mostly makes sense to be used without setting as a component:
ues Devel::Dwarn;
Dwarn DBIx::Class::ResultSet::Explain::explain($rs)
But as usual, if you prefer to use it as a component here's how:
package MyApp::Schema::ResultSet::Foo;
__PACKAGE__->load_components(qw{Helper::ResultSet::Explain});
...
1;
And then in a script or something:
use Devel::Dwarn;
Dwarn $rs->explain;
=head1 DESCRIPTION
This is just a handy little tool that gives you the query plan for a given
ResultSet. The output is in no way normalized, so just treat it as a debug tool
or something. The only supported DB's are those listed below. Have fun!
See L<DBIx::Class::Helper::ResultSet/NOTE> for a nice way to apply it
to your entire schema.
=head1 EXAMPLE OUTPUT FROM SUPPORTED DB's
=head2 SQlite
=for exec
perl maint/explain-out SQLite
=head2 Pg
=for exec
perl maint/explain-out Pg
=head2 mysql
=for exec
perl maint/explain-out mysql
19 changes: 19 additions & 0 deletions maint/explain-out
@@ -0,0 +1,19 @@
#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper::Concise;

use lib 't/lib';

use A::Util;

my $engine = shift;
if ($engine eq 'SQLite') {
my $s = TestSchema->connect('dbi:SQLite::memory:');
$s->deploy;
print Dumper($s->resultset('Gnarly')->explain)
} else {
print Dumper(A::Util::connect($engine, $engine)->resultset('Gnarly')->explain)
}
2 changes: 1 addition & 1 deletion t/lib/TestSchema.pm
Expand Up @@ -56,7 +56,7 @@ sub generate_ddl {
($_ ne 'SQLite'
? (
add_drop_table => 1,
parser_args => { sources => ['HasDateOps'] })
parser_args => { sources => ['HasDateOps', 'Gnarly'] })
: ( add_drop_table => 0 )
)
},
Expand Down
2 changes: 1 addition & 1 deletion t/lib/TestSchema/ResultSet/Gnarly.pm
Expand Up @@ -5,7 +5,7 @@ use warnings;
# intentionally not using TestSchema::ResultSet
use parent 'DBIx::Class::ResultSet';

__PACKAGE__->load_components(qw{ Helper::ResultSet::Me Helper::ResultSet::ResultClassDWIM Helper::ResultSet::CorrelateRelationship Helper::ResultSet::SearchOr Helper::ResultSet::NoColumns });
__PACKAGE__->load_components(qw{ Helper::ResultSet::Me Helper::ResultSet::ResultClassDWIM Helper::ResultSet::CorrelateRelationship Helper::ResultSet::SearchOr Helper::ResultSet::NoColumns Helper::ResultSet::Explain });

sub with_id_plus_one {
my $self = shift;
Expand Down
33 changes: 33 additions & 0 deletions t/resultset/explain.t
@@ -0,0 +1,33 @@
#!perl

use Test::Roo;
use Test::Fatal;
use Data::Dumper::Concise;

use lib 't/lib';

with 'A::Role::TestConnect';

sub rs { shift->schema->resultset('Gnarly') }

test basic => sub {
my $self = shift;
my $rs = $self->rs;
SKIP: {
skip 'cannot test without a connection', 1 unless $self->connected;

my $s;
my $e = exception { $s = $rs->explain };
ok(!$e, 'valid SQL') or diag $e;
note(Dumper($s)) if $s;
}
};

run_me(SQLite => {
engine => 'SQLite',
connect_info => [ 'dbi:SQLite::memory:'],
});
run_me(Pg => { engine => 'Pg' });
run_me(mysql => { engine => 'mysql' });

done_testing;
23 changes: 23 additions & 0 deletions weaver.ini
@@ -0,0 +1,23 @@
[-Exec]
[@CorePrep]

[Name]

[Region / prelude]

[Generic / SYNOPSIS]
[Generic / DESCRIPTION]
[Generic / OVERVIEW]

[Leftovers]

[Collect / ATTRIBUTES]
command = attr

[Collect / METHODS]
command = method

[Region / postlude]

[Authors]
[Legal]

0 comments on commit fb2cfd3

Please sign in to comment.