The DBI module lets you handle errors yourself if you don’t like its built-in behavior. DBI lets you handle the errors at either the database or the statement handle level by specifying attributes:
my $dbh = DBI->connect( ..., ..., \%attr ); my $sth = $dbh->prepare( ..., \%attr );
There are several attributes that affect error handling, each of which you can use with either a connection or a statement handle:
Attribute | Type | Default |
---|---|---|
PrintWarn | Boolean | On |
PrintError | Boolean | On |
RaiseError | Boolean | Off |
HandleError | Code Ref | Off |
ShowErrorStatement | Boolean | Off |
These attributes are inherited by anything derived from the handle where you set them.
The PrintWarn
and PrintError
attributes do just what they say. They are on by default, and they don’t stop your program. In this example, you prepare a statement that expects one bind parameter, but when you execute it, you give two parameters instead:
use DBI; my $dbh = DBI->connect( 'dbi:SQLite:dbname=test.db', '', '', {} ); my $sth = $dbh->prepare( 'SELECT * FROM Cats WHERE id = ?' ); $sth->execute( 1, 2 ); while( my @row = $sth->fetchrow_array ) { print "row: @row\n"; } print "Got to the end\n";
Since PrintError
is true by default, DBI prints the error, but it allows the program to continue even though there was an error:
DBD::SQLite::st execute failed: called with 2 bind variables when 1 are needed at dbi-test.pl line 12. Got to the end
If you set the ShowErrorStatement
attribute, you get a better error message because DBI appends the SQL statement that you tried to execute. You can set this either database handle or the statement handle, but if you don’t know which statement is causing the problem, it’s easier to set it as part of the database handle:
# The rest of the program is the same my $dbh = DBI->connect( 'dbi:SQLite:dbname=test.db', '', '', { ShowErrorStatement => 1, } );
The error message shows the SQL statement, but the program still continues:
DBD::SQLite::st execute failed: called with 2 bind variables when 1 are needed [for Statement "SELECT * FROM Cats WHERE id = ?"] at dbi-test.pl line 12. Got to the end
The RaiseError
attribute turns errors into fatal errors that you can trap with eval { ... }
or Try::Tiny (Item 103: Handle Exceptions Properly) (or not trap if you want your program to die):
# The rest of the program is the same my $dbh = DBI->connect( 'dbi:SQLite:dbname=test.db', '', '', { RaiseError => 1, ShowErrorStatement => 1, } ); use Try::Tiny; try { $sth->prepare( ... ); $sth->execute( ... ); } catch { ... };
The output shows that the program stops (there’s no “Got to the end”), but you see duplicated error messages; the one from PrintError
that is just a warning, and the one from RaiseError
that kills the program:
DBD::SQLite::st execute failed: called with 2 bind variables when 1 are needed [for Statement "SELECT * FROM Cats WHERE id = ?"] at dbi-test.pl line 14. DBD::SQLite::st execute failed: called with 2 bind variables when 1 are needed [for Statement "SELECT * FROM Cats WHERE id = ?"] at dbi-test.pl line 14.
Turning off PrintError
can fix the duplication:
# The rest of the program is the same my $dbh = DBI->connect( 'dbi:SQLite:dbname=test.db', '', '', { PrintError => 0, RaiseError => 1, ShowErrorStatement => 1, } );
Simply raising the exception might be good enough for some applications, but sometimes you want more control of the errors. In those cases, you can handle the errors yourself by providing a code reference to HandleError
. In this case, you can just catch the error and print it:
my $dbh = DBI->connect( 'dbi:SQLite:dbname=test.db', '', '', { ShowErrorStatement => 1, HandleError => \&dbi_error_handler, } ); sub dbi_error_handler { my( $message, $handle, $first_value ) = @_; print "Caught: $message\n"; return 1; }
DBI passes your HandleError
three arguments: the error string it would have used with PrintError
, the handle that generated the error, and first return value from the failing method (which is typically nothing useful since there’s an error of some sort).
The error message shows the you caught the error:
Caught: DBD::SQLite::st execute failed: called with 2 bind variables when 1 are needed [for Statement "SELECT * FROM Cats WHERE id = ?"] Got to the end
If you want a stack trace, you can use Carp (and curiously, the argument alignment works out!).
use Carp; my $dbh = DBI->connect( 'dbi:SQLite:dbname=test.db', '', '', { ShowErrorStatement => 1, HandleError => \&Carp::confess, } );
HandleError
is how Exception::Class::DBI inserts its error handler:
my $dbh = DBI->connect( $dsn, $user, $pass, { PrintError => 0, RaiseError => 0, HandleError => Exception::Class::DBI->handler, });
The DBIx-Log4perl uses HandleError
, although it hides the details from you:
my $dbh = DBIx::Log4perl->connect('dbi:Oracle:XE', 'user', 'password');
There are some things that you might want to do when handling the error yourself, depending on what you want to accomplish:
- rollback if you are in the middle of a transaction
- disconnect from the database if you are going to quit
- reconnect to the database if you lost the connection
- print a stack trace
No matter what you want to do, however, it’s HandleError
that lets you do it.
Things to remember
- The
RaiseError
attribute turns DBI handle warning into fatal errors - You can handle errors yourself by giving
HandleError
a code reference - Setting the
ShowErrorStatement
attribute adds the offending SQL statement to the error message