DBIx::Exceptions
Arthur Axel "fROOH" Schmidt
Purpose
To educate audience of exceptions in Perl and briefly introduce DBIx::Exceptions
- Note: links in slides are so you can find docs for what I'm talking about later
What is an Exception?
According to wikipedia, an exception is a special condition that
changes the normal flow of program execution
- An exception is basically a form of interrupt
NOT an error code
my $fh;
if (!open $fh, '>', 'filename') {
warn "couldn't open file!"
}
- The above is not the typical idiom
- The typical idiom throws an exception instead!
Why should I use exceptions?
- Error codes can erroneously be ignored
When should I use exceptions?
- When an "exceptional" condition occurs
- File missing read access: exceptional
- Reaching EOF: not exceptional
Problems with exceptions
- Tight coupling between levels
- Worse performance
- Contrains Users
- Do not put exceptions in core
- Make them optional
Super Basic Exceptions in Perl
my $x = rand 10;
die "half-broken: $x" if $x < 5
- die kills program
- message is given to user
Vanilla Perl Exceptions
eval {
my $x = rand 10;
die "half-broken: $x" if $x < 5
};
say 'coin flip fail'
if defined $@ && $@ =~ /half-broken: (.+)/;
- eval tells perl that die should set $@, instead of crashing
- die (above) obviously sets $@ to value of string
- final conditional is a typical form of exception handling in perl
:-/
- Super simple to do
- Makes reasonable error message
- On the other hand...
- Not very reusable
- Not easy to read
- Clobbers previous exceptions (overwrites $@)
Modern Exceptions
- We can throw objects (since 5.005)
- We have better and safer syntax
Object Exceptions
eval {
my $x = rand 10;
die Foo->new({ num => $x })
if $x < 5
};
say 'coin flip fail'
if ref $@ && $@->isa('Foo');
- Note that exception does not need to be parsed. Nice.
- Does not require Moose
- Simple to use
- Adds some catch semantics
Exception::Class Exception
package MyApp::Exceptions;
use Exception::Class (
'MyApp::Exception::Foo' => {
fields => ['num'],
},
);
Exception::Class in action
eval {
my $x = rand 10;
MyApp::Exception::Foo->throw( num => $x )
if $x < 5
};
say 'coin flip fail'
if Exception::Class->caught('MyApp::Exception::Foo');
- throw dies and instantiates at the same time
- also sets previous_exception attribute to whatever was in $@
- Lightweight Moose Role for Exceptions
- Very basic; great if you use Moose
- See also: Throwable::Error
Throwable Exception
package Foo;
use Moose;
with 'Throwable';
has num => (
is => 'ro',
isa => 'Num',
);
1;
- with adds throw method and
- previous_exception attribute
- has is vanilla moose for add num attribute
Throwable Exception in action
eval {
my $x = rand 10;
Foo->throw({ num => $x })
if $x < 5
};
say 'coin flip fail'
if defined $@ && ref $@ && $@->isa('Foo');
- throw dies and instantiates at the same time
- also sets previous_exception attribute to whatever was in $@
- Minimizes common mistakes with eval (such as clobbering $@)
- Small stuff when startup time matters
- Module writers who do not want too many deps
Try::Tiny in action
try {
my $x = rand 10;
Foo->throw({ num => $x })
if $x < 5
} catch {
say 'coin flip fail'
if ref $_ && $_->isa('Foo')
};
- $@ becomes $_
- No real need to check definedness
- Also note: finally
- My favorite
- Beautiful Syntax
- Abundantly clear
- Heavy
- Broken in 5.12
TryCatch in action
try {
my $x = rand 10;
Foo->throw({ num => $x })
if $x < 5
} catch (Foo $e) {
say 'coin flip fail with num of ' . $e->num
}
- ensures that $e is of class Foo
- returns from catch work as expected (not in Try::Tiny)
Advanced TryCatch in action
try { ... }
catch (Foo $e) { ... }
catch (Bar $e) { ... }
catch (Baz $e where { $e->num > 5 }) { ... }
- multiple catch clauses
- catch with where clauses
- Super awesome: forces stacktrace
Old School Perl Database Exceptions
eval { $sth->execute };
if ($@ =~ /unique \s+ constraint \s+ violated/ix) {
...
}
:-/
- Only works on (probably) one rdbms
- Again, not reusable at all
- And again, not easy to read
Introducing DBIx::Exceptions
DBIx::Exceptions - Database exceptions for all RDBMS' Ever
- Currently uses Exception::Class, but don't depend on that
What it do's?
- Databases Initially Supported:
- SQLite
- PostgreSQL
SQLServer
MySQL
- Exceptions Initially Supported:
- Unique
- Invalid Table
- Invalid Column
- Syntax
Foreign Key
Planned Support for DBIx::Exceptions
- All databases
- All exceptions people want
- Databases in English mode
- Patches Welcome
Setup
my $dbh = DBI->connect($dsn, $username, $password);
$dbh->{HandleError} =
DBIx::ParseException->handler({ dbh => $dbh });
Example Usage (1)
try {
my $sth = $dbh->prepare(
'INSERT INTO Emails (email) VALUES (?)'
);
$sth->execute('frew@gmail.com')
for (1..2);
} catch (
DBIx::Exception::NotUnique $e
where { $e->column eq 'email'}
) {
...
};
- Not all db's support ->column
- Looking into ways to mitigate/document that
Example Usage (2)
try {
my $sth = $dbh->prepare(
'INSERT INTO Rmails (email) VALUES (?)'
);
$sth->execute('frew@gmail.com');
} catch (
DBIx::Exception::NoSuchTable $e
) {
say $e->table . ' nt lolz'
};
Example Usage (3)
try {
my $sth = $dbh->prepare(
'INSERT INTO Emails (emisl) VALUES (?)'
);
$sth->execute('frew@gmail.com');
} catch (
DBIx::Exception::NoSuchColumn $e
) {
say column: ' . $e->column .
' does not exist on table ' $e->table
};
Other Gotchas
- If you are using an ORM it will probably need tweaking for this to work
Questions?
THE END