NAME
DBIx::Error - Structured exceptions for DBI
SYNOPSIS
use DBIx::Error;
# Configure exception handler for DBI
my $db = DBI->connect ( $dsn, $user, $password,
{ HandleError => DBIx::Error->HandleError,
ShowErrorStatement => 1 } );
# Catch a unique constraint violation (by class name)
use TryCatch;
try {
...
} catch ( DBIx::Error::UniqueViolation $err ) {
die "Name $name is already in use";
}
# Catch a unique constraint violation (by SQLSTATE)
use TryCatch;
try {
...
} catch ( DBIx::Error $err where { $_->state eq "23505" } ) {
die "Name $name is already in use";
}
# Catch any type of database error
use TryCatch;
try {
...
} catch ( DBIx::Error $err ) {
die "Internal database error [SQL state ".$err->state."]:\n".$err;
}
DESCRIPTION
DBIx::Error
provides structured exceptions for DBI
errors. Each five-character SQLSTATE
is mapped to a Perl exception class, allowing exceptions to be caught using code such as
try {
...
} catch ( DBIx::Error::NotNullViolation $err ) {
...
} catch ( DBIx::Error::UniqueViolation $err ) {
...
}
The exception stringifies to produce the full DBI
error message (including a stack trace). The original DBI error attributes (err
, errstr
and state
) are also provided for inspection.
See "EXCEPTION CLASS HIERARCHY" below for a list of supported exception classes.
ATTRIBUTES
message
-
The error message, as produced by
DBI
.If
ShowErrorStatement
was set to a true value when connecting to the database, this message will include the SQL statement that caused the error. stack_trace
-
A stack trace, as produced using
StackTrace::Auto
. err
-
The native database engine error code, as obtained from
$DBI::err
. errstr
-
The native database engine error message, as obtained from
$DBI::errstr
. state
-
The state code in the standard
SQLSTATE
five character format, as obtained from$DBI::state
.
CLASS METHODS
define_exception_classes ( %states )
-
See "SUBCLASSING" below.
exception_class ( $state )
-
Get the exception class for the specified SQLSTATE.
HandleError()
-
Returns a code reference suitable for passing as the
HandleError
database connection parameter forDBI
(orDBIx::Class
, orCatalyst::Model::DBIC::Schema
). exception_action()
-
Returns a code reference suitable for passing as the
exception_action
configuration parameter forDBIx::Class::Schema
.
USAGE PATTERNS
Using plain DBI
use DBI;
use DBIx::Error;
use TryCatch;
my $db = DBI->connect ( $dsn, $user, $password,
{ HandleError => DBIx::Error->HandleError,
ShowErrorStatement => 1 } );
my $sth = $db->prepare ( "INSERT INTO people VALUES ( ? )" );
try {
$sth->execute ( $name );
} catch ( DBIx::Error::UniqueViolation $err ) {
die "$name is already present in the database";
} catch ( DBIx::Error::IntegrityConstraintViolation $err ) {
die "$name is not allowed";
}
Using DBIx::Class
package MyDB::Schema;
use base qw ( DBIx::Class::Schema );
use DBIx::Error;
__PACKAGE__->exception_action ( DBIx::Error->exception_action );
__PACKAGE__->load_namespaces();
use MyDB::Schema;
use TryCatch;
my $db = MyDB::Schema->connect ( {
dsn => $dsn,
user => $user,
password => $password,
HandleError => DBIx::Error->HandleError,
ShowErrorStatment => 1,
unsafe => 1,
} );
try {
$db->resultset ( "People" )->create ( { name => $name } );
} catch ( DBIx::Error::UniqueViolation $err ) {
die "$name is already present in the database";
} catch ( DBIx::Error::IntegrityConstraintViolation $err ) {
die "$name is not allowed";
}
Using Catalyst::Model::DBIC::Schema
package MyDB::Schema;
use base qw ( DBIx::Class::Schema );
use DBIx::Error;
__PACKAGE__->exception_action ( DBIx::Error->exception_action );
__PACKAGE__->load_namespaces();
package MyApp::Model::MyDB;
use base qw ( Catalyst::Model::DBIC::Schema );
use MyDB::Schema;
use DBIx::Error;
__PACKAGE__->config (
schema_class => "MyDB::Schema",
connect_info => {
dsn => $dsn,
user => $user,
password => $password,
HandleError => DBIx::Error->HandleError,
ShowErrorStatment => 1,
unsafe => 1,
},
);
LIMITATIONS
Database engines
DBIx::Error
can produce an exception of the correct subclass only if the underlying database engine supports SQLSTATE. (If the underlying driver does not support SQLSTATE then all exceptions will have the class DBIx::Error
and the only way to determine what actually caused the error will be to examine the text of the error message.)
DBIx::Error
is known to work reliably with PostgreSQL databases.
Bulk operations
Bulk operations using DBI->execute_array()
may not work as expected. The underlying DBI
code will not raise an exception until the entire bulk operation has completed, and the exception will represent the most recent failure at the time the operation completes.
In most (but not all) cases, this will result in a DBIx::Error::InFailedSqlTransaction
exception, since the transaction will already have been aborted. To raise an exception corresponding to the original error (the one which caused the transaction to abort), the caller would have to parse the ArrayTupleStatus
array and use DBI->set_err()
to regenerate the exception corresponding to the appropriate row.
This limitation also applies to operations built on top of DBI->execute_array()
, such as DBIx::Class::Schema->populate()
.
As a general rule: assume that the only sensible exception class that can be caught from any bulk operation is the root class DBIx::Error
:
try {
$sth->execute_array ( @args );
} catch ( DBIx::Error $err ) {
# Could be any type of DBI error
}
CAVEATS
DBIx::Class::Schema->txn_do()
Any plain errors (i.e. die "some_message";
statements) within a txn_do()
block will be converted into DBIx::Error::GeneralError
exceptions:
try {
$db->txn_do ( sub {
die "foo";
} );
} catch ( $err ) {
print "Caught ".( ref $err )." exception with message: ".$err;
}
# will print "Caught DBIx::Error::GeneralError exception with message: foo"
To avoid this behaviour, use exception objects instead of plain die
:
try {
$db->txn_do ( sub {
My::Error->throw ( "foo" );
} );
} catch ( $err ) {
...
}
SUBCLASSING
Applications can define custom SQLSTATE codes to represent application-specific database errors. For example, using PostgreSQL's PL/pgSQL:
CREATE FUNCTION test_custom_exception() RETURNS void AS $$
BEGIN
RAISE EXCEPTION 'Something bad happened'
USING ERRCODE = 'MY001';
END;
$$ LANGUAGE plpgsql VOLATILE;
These custom SQLSTATE codes can be mapped to Perl exception classes by subclassing DBIx::Error
and defining the appropriate mappings using the define_exception_classes()
class method. For example:
package MyApplication::Error::DBI;
use Moose;
extends "DBIx::Error";
__PACKAGE__->define_exception_classes (
"MY000" => "General",
"MY001" => "SomethingBadHappened"
);
__PACKAGE__->meta->make_immutable ( inline_constructor => 0 );
1;
The resulting exception classes will be subclasses of the package which calls define_exception_classes()
. In the above example, the exception classes will be:
C<MyApplication::Error::DBI::General>
C<MyApplication::Error::DBI::SomethingBadHappened>
The exceptions can be caught in the usual way:
use TryCatch;
try {
...
} catch ( MyApplication::Error::DBI::SomethingBadHappened $err ) {
...
} catch ( DBIx::Error::UniqueViolation $err ) {
...
}
Mappings for the generic SQLSTATE codes (ending in "000") act as superclasses for the more specific SQLSTATE codes (ending in anything other than "000"). You must define mappings for the generic SQLSTATE codes corresponding to any application-specific SQLSTATE codes.
By default, the exception classes will be prefixed with the name of the package which calls define_exception_classes()
. You can override this by specifying full class names for the exception subclasses, e.g.
__PACKAGE__->define_exception_classes (
"QA000" => "MyApp::Error::General"
);
EXCEPTION CLASS HIERARCHY
DBIx::Error
-
DBIx::Error::SqlStatementNotYetComplete
(03000
)DBIx::Error::ConnectionException
(08000
)DBIx::Error::TriggeredActionException
(09000
)DBIx::Error::FeatureNotSupported
(0A000
)DBIx::Error::InvalidTransactionInitiation
(0B000
)DBIx::Error::LocatorException
(0F000
)DBIx::Error::InvalidGrantor
(0L000
)DBIx::Error::InvalidRoleSpecification
(0P000
)DBIx::Error::CaseNotFound
(20000
)DBIx::Error::CardinalityViolation
(21000
)DBIx::Error::DataException
(22000
)-
DBIx::Error::StringDataRightTruncation
(22001
)DBIx::Error::NullValueNoIndicatorParameter
(22002
)DBIx::Error::NumericValueOutOfRange
(22003
)DBIx::Error::NullValueNotAllowed
(22004
)DBIx::Error::ErrorInAssignment
(22005
)DBIx::Error::InvalidDatetimeFormat
(22007
)DBIx::Error::DatetimeFieldOverflow
(22008
)DBIx::Error::InvalidTimeZoneDisplacementValue
(22009
)DBIx::Error::EscapeCharacterConflict
(2200B
)DBIx::Error::InvalidUseOfEscapeCharacter
(2200C
)DBIx::Error::InvalidEscapeOctet
(2200D
)DBIx::Error::ZeroLengthCharacterString
(2200F
)DBIx::Error::MostSpecificTypeMismatch
(2200G
)DBIx::Error::NotAnXmlDocument
(2200L
)DBIx::Error::InvalidXmlDocument
(2200M
)DBIx::Error::InvalidXmlContent
(2200N
)DBIx::Error::InvalidXmlComment
(2200S
)DBIx::Error::InvalidXmlProcessingInstruction
(2200T
)DBIx::Error::InvalidIndicatorParameterValue
(22010
)DBIx::Error::SubstringError
(22011
)DBIx::Error::DivisionByZero
(22012
)DBIx::Error::InvalidArgumentForNtileFunction
(22014
)DBIx::Error::IntervalFieldOverflow
(22015
)DBIx::Error::InvalidArgumentForNthValueFunction
(22016
)DBIx::Error::InvalidCharacterValueForCast
(22018
)DBIx::Error::InvalidEscapeCharacter
(22019
)DBIx::Error::InvalidRegularExpression
(2201B
)DBIx::Error::InvalidArgumentForLogarithm
(2201E
)DBIx::Error::InvalidArgumentForPowerFunction
(2201F
)DBIx::Error::InvalidArgumentForWidthBucketFunction
(2201G
)DBIx::Error::InvalidRowCountInLimitClause
(2201W
)DBIx::Error::InvalidRowCountInResultOffsetClause
(2201X
)DBIx::Error::CharacterNotInRepertoire
(22021
)DBIx::Error::IndicatorOverflow
(22022
)DBIx::Error::InvalidParameterValue
(22023
)DBIx::Error::UnterminatedCString
(22024
)DBIx::Error::InvalidEscapeSequence
(22025
)DBIx::Error::StringDataLengthMismatch
(22026
)DBIx::Error::TrimError
(22027
)DBIx::Error::ArraySubscriptError
(2202E
)DBIx::Error::FloatingPointException
(22P01
)DBIx::Error::InvalidTextRepresentation
(22P02
)DBIx::Error::InvalidBinaryRepresentation
(22P03
)DBIx::Error::BadCopyFileFormat
(22P04
)DBIx::Error::UntranslatableCharacter
(22P05
)DBIx::Error::NonstandardUseOfEscapeCharacter
(22P06
)
DBIx::Error::IntegrityConstraintViolation
(23000
)DBIx::Error::InvalidCursorState
(24000
)DBIx::Error::InvalidTransactionState
(25000
)-
DBIx::Error::ActiveSqlTransaction
(25001
)DBIx::Error::BranchTransactionAlreadyActive
(25002
)DBIx::Error::InappropriateAccessModeForBranchTransaction
(25003
)DBIx::Error::InappropriateIsolationLevelForBranchTransaction
(25004
)DBIx::Error::NoActiveSqlTransactionForBranchTransaction
(25005
)DBIx::Error::ReadOnlySqlTransaction
(25006
)DBIx::Error::SchemaAndDataStatementMixingNotSupported
(25007
)DBIx::Error::HeldCursorRequiresSameIsolationLevel
(25008
)DBIx::Error::NoActiveSqlTransaction
(25P01
)DBIx::Error::InFailedSqlTransaction
(25P02
)
DBIx::Error::InvalidSqlStatementName
(26000
)DBIx::Error::TriggeredDataChangeViolation
(27000
)DBIx::Error::InvalidAuthorizationSpecification
(28000
)DBIx::Error::DependentPrivilegeDescriptorsStillExist
(2B000
)DBIx::Error::InvalidTransactionTermination
(2D000
)DBIx::Error::SqlRoutineException
(2F000
)DBIx::Error::InvalidCursorName
(34000
)DBIx::Error::ExternalRoutineException
(38000
)DBIx::Error::ExternalRoutineInvocationException
(39000
)DBIx::Error::SavepointException
(3B000
)DBIx::Error::InvalidCatalogName
(3D000
)DBIx::Error::InvalidSchemaName
(3F000
)DBIx::Error::TransactionRollback
(40000
)DBIx::Error::SyntaxErrorOrAccessRuleViolation
(42000
)-
DBIx::Error::InsufficientPrivilege
(42501
)DBIx::Error::SyntaxError
(42601
)DBIx::Error::InvalidName
(42602
)DBIx::Error::InvalidColumnDefinition
(42611
)DBIx::Error::NameTooLong
(42622
)DBIx::Error::DuplicateColumn
(42701
)DBIx::Error::AmbiguousColumn
(42702
)DBIx::Error::UndefinedColumn
(42703
)DBIx::Error::UndefinedObject
(42704
)DBIx::Error::DuplicateObject
(42710
)DBIx::Error::DuplicateAlias
(42712
)DBIx::Error::DuplicateFunction
(42723
)DBIx::Error::AmbiguousFunction
(42725
)DBIx::Error::GroupingError
(42803
)DBIx::Error::DatatypeMismatch
(42804
)DBIx::Error::WrongObjectType
(42809
)DBIx::Error::InvalidForeignKey
(42830
)DBIx::Error::CannotCoerce
(42846
)DBIx::Error::UndefinedFunction
(42883
)DBIx::Error::ReservedName
(42939
)DBIx::Error::UndefinedTable
(42P01
)DBIx::Error::UndefinedParameter
(42P02
)DBIx::Error::DuplicateCursor
(42P03
)DBIx::Error::DuplicateDatabase
(42P04
)DBIx::Error::DuplicatePreparedStatement
(42P05
)DBIx::Error::DuplicateSchema
(42P06
)DBIx::Error::DuplicateTable
(42P07
)DBIx::Error::AmbiguousParameter
(42P08
)DBIx::Error::AmbiguousAlias
(42P09
)DBIx::Error::InvalidColumnReference
(42P10
)DBIx::Error::InvalidCursorDefinition
(42P11
)DBIx::Error::InvalidDatabaseDefinition
(42P12
)DBIx::Error::InvalidFunctionDefinition
(42P13
)DBIx::Error::InvalidPreparedStatementDefinition
(42P14
)DBIx::Error::InvalidSchemaDefinition
(42P15
)DBIx::Error::InvalidTableDefinition
(42P16
)DBIx::Error::InvalidObjectDefinition
(42P17
)DBIx::Error::IndeterminateDatatype
(42P18
)DBIx::Error::InvalidRecursion
(42P19
)DBIx::Error::WindowingError
(42P20
)DBIx::Error::CollationMismatch
(42P21
)DBIx::Error::IndeterminateCollation
(42P22
)
DBIx::Error::WithCheckOptionViolation
(44000
)DBIx::Error::InsufficientResources
(53000
)DBIx::Error::ProgramLimitExceeded
(54000
)DBIx::Error::ObjectNotInPrerequisiteState
(55000
)DBIx::Error::OperatorIntervention
(57000
)DBIx::Error::SystemError
(58000
)DBIx::Error::ConfigFileError
(F0000
)DBIx::Error::FdwError
(HV000
)-
DBIx::Error::FdwOutOfMemory
(HV001
)DBIx::Error::FdwDynamicParameterValueNeeded
(HV002
)DBIx::Error::FdwInvalidDataType
(HV004
)DBIx::Error::FdwColumnNameNotFound
(HV005
)DBIx::Error::FdwInvalidDataTypeDescriptors
(HV006
)DBIx::Error::FdwInvalidColumnName
(HV007
)DBIx::Error::FdwInvalidColumnNumber
(HV008
)DBIx::Error::FdwInvalidUseOfNullPointer
(HV009
)DBIx::Error::FdwInvalidStringFormat
(HV00A
)DBIx::Error::FdwInvalidHandle
(HV00B
)DBIx::Error::FdwInvalidOptionIndex
(HV00C
)DBIx::Error::FdwInvalidOptionName
(HV00D
)DBIx::Error::FdwOptionNameNotFound
(HV00J
)DBIx::Error::FdwReplyHandle
(HV00K
)DBIx::Error::FdwUnableToCreateExecution
(HV00L
)DBIx::Error::FdwUnableToCreateReply
(HV00M
)DBIx::Error::FdwUnableToEstablishConnection
(HV00N
)DBIx::Error::FdwNoSchemas
(HV00P
)DBIx::Error::FdwSchemaNotFound
(HV00Q
)DBIx::Error::FdwTableNotFound
(HV00R
)DBIx::Error::FdwFunctionSequenceError
(HV010
)DBIx::Error::FdwTooManyHandles
(HV014
)DBIx::Error::FdwInconsistentDescriptorInformation
(HV021
)DBIx::Error::FdwInvalidAttributeValue
(HV024
)DBIx::Error::FdwInvalidStringLengthOrBufferLength
(HV090
)DBIx::Error::FdwInvalidDescriptorFieldIdentifier
(HV091
)
DBIx::Error::PlpgsqlError
(P0000
)DBIx::Error::GeneralError
(S1000
)DBIx::Error::InternalError
(XX000
)
AUTHOR
Michael Brown <mbrown@fensystems.co.uk>