10 | 10 |
use Exception::Class (
|
11 | 11 |
'Exception::Class::DBI' => {
|
12 | 12 |
description => 'DBI exception',
|
13 | |
fields => [qw(err errstr state retval)]
|
|
13 |
fields => [qw(err errstr state retval handle)]
|
14 | 14 |
},
|
15 | 15 |
|
16 | 16 |
'Exception::Class::DBI::Unknown' => {
|
|
21 | 21 |
'Exception::Class::DBI::H' => {
|
22 | 22 |
isa => 'Exception::Class::DBI',
|
23 | 23 |
description => 'DBI handle exception',
|
24 | |
fields => [qw(warn active kids active_kids compat_mode
|
25 | |
inactive_destroy trace_level fetch_hash_key_name
|
26 | |
chop_blanks long_read_len long_trunc_ok taint
|
27 | |
)],
|
28 | 24 |
},
|
29 | 25 |
|
30 | 26 |
'Exception::Class::DBI::DRH' => {
|
|
35 | 31 |
'Exception::Class::DBI::DBH' => {
|
36 | 32 |
isa => 'Exception::Class::DBI::H',
|
37 | 33 |
description => 'DBI database handle exception',
|
38 | |
fields => [qw(auto_commit db_name statement row_cache_size)]
|
39 | 34 |
},
|
40 | 35 |
|
41 | 36 |
'Exception::Class::DBI::STH' => {
|
42 | 37 |
isa => 'Exception::Class::DBI::H',
|
43 | 38 |
description => 'DBI statment handle exception',
|
44 | |
fields => [qw(num_of_fields num_of_params field_names type
|
45 | |
precision scale nullable cursor_name param_values
|
46 | |
statement rows_in_cache
|
47 | |
)],
|
48 | 39 |
}
|
49 | 40 |
);
|
50 | 41 |
|
51 | 42 |
sub handler {
|
52 | 43 |
my $pkg = shift;
|
53 | |
my %class_for = map {
|
54 | |
eval "require $pkg\::$_";
|
55 | |
$_ => $@ ? __PACKAGE__ . "::$_" : "$pkg\::$_";
|
|
44 |
|
|
45 |
# Support subclasses.
|
|
46 |
my %class_for = map {
|
|
47 |
$_ => do {
|
|
48 |
my $class = "$pkg\::$_";
|
|
49 |
my $base = __PACKAGE__ . "::$_";
|
|
50 |
no strict 'refs';
|
|
51 |
# Try to load the subclass and check its inheritance.
|
|
52 |
eval "require $class" unless @{"$class\::ISA"};
|
|
53 |
my $isa = \@{"$class\::ISA"};
|
|
54 |
die "$class is not a subclass of $base"
|
|
55 |
if $isa && !$class->isa($base);
|
|
56 |
# If subclass exists and inherits, use it. Otherwise use default.
|
|
57 |
$isa ? $class : $base;
|
|
58 |
}
|
56 | 59 |
} qw(H DRH DBH STH Unknown);
|
57 | |
sub {
|
|
60 |
|
|
61 |
return sub {
|
58 | 62 |
my ($err, $dbh, $retval) = @_;
|
59 | |
if (ref $dbh) {
|
60 | |
# Assemble arguments for a handle exception.
|
61 | |
my @params = (
|
62 | |
error => $err,
|
63 | |
errstr => $dbh->errstr,
|
64 | |
err => $dbh->err,
|
65 | |
state => $dbh->state,
|
66 | |
retval => $retval,
|
67 | |
warn => $dbh->{Warn},
|
68 | |
active => $dbh->{Active},
|
69 | |
kids => $dbh->{Kids},
|
70 | |
active_kids => $dbh->{ActiveKids},
|
71 | |
compat_mode => $dbh->{CompatMode},
|
72 | |
inactive_destroy => $dbh->{InactiveDestroy},
|
73 | |
trace_level => $dbh->{TraceLevel},
|
74 | |
fetch_hash_key_name => $dbh->{FetchHashKeyName},
|
75 | |
chop_blanks => $dbh->{ChopBlanks},
|
76 | |
long_read_len => $dbh->{LongReadLen},
|
77 | |
long_trunc_ok => $dbh->{LongTruncOk},
|
78 | |
taint => $dbh->{Taint},
|
79 | |
);
|
80 | |
if (UNIVERSAL::isa($dbh, 'DBI::dr')) {
|
81 | |
# Just throw a driver exception. It has no extra attributes.
|
82 | |
$class_for{DRH}->throw(@params);
|
83 | |
} elsif (UNIVERSAL::isa($dbh, 'DBI::db')) {
|
84 | |
# Throw a database handle exception.
|
85 | |
$class_for{DBH}->throw(
|
86 | |
@params,
|
87 | |
auto_commit => $dbh->{AutoCommit},
|
88 | |
db_name => $dbh->{Name},
|
89 | |
statement => $dbh->{Statement},
|
90 | |
row_cache_size => $dbh->{RowCacheSize}
|
91 | |
);
|
92 | |
} elsif (UNIVERSAL::isa($dbh, 'DBI::st')) {
|
93 | |
# Throw a statement handle exception.
|
94 | |
$class_for{STH}->throw(
|
95 | |
@params,
|
96 | |
num_of_fields => $dbh->{NUM_OF_FIELDS},
|
97 | |
num_of_params => $dbh->{NUM_OF_PARAMS},
|
98 | |
field_names => $dbh->{NAME},
|
99 | |
type => $dbh->{TYPE},
|
100 | |
precision => $dbh->{PRECISION},
|
101 | |
scale => $dbh->{SCALE},
|
102 | |
nullable => $dbh->{NULLABLE},
|
103 | |
cursor_name => $dbh->{CursorName},
|
104 | |
param_values => $dbh->{ParamValues},
|
105 | |
statement => $dbh->{Statement},
|
106 | |
rows_in_cache => $dbh->{RowsInCache}
|
107 | |
);
|
108 | |
} else {
|
109 | |
# Unknown exception. This shouldn't happen.
|
110 | |
$class_for{Unknown}->throw(@params);
|
111 | |
}
|
112 | |
} else {
|
113 | |
# Set up for a base class exception.
|
114 | |
my $exc = $pkg;
|
115 | |
# Make it an unknown exception if $dbh isn't a DBI class
|
116 | |
# name. Probably shouldn't happen.
|
117 | |
$class_for{Unknown} unless eval { $dbh->isa($dbh, 'DBI') };
|
118 | |
if ($DBI::lasth) {
|
119 | |
# There was a handle. Get the errors. This may be superfluous,
|
120 | |
# since the handle ought to be in $dbh.
|
121 | |
$exc->throw(
|
122 | |
error => $err,
|
123 | |
errstr => $DBI::errstr,
|
124 | |
err => $DBI::err,
|
125 | |
state => $DBI::state,
|
126 | |
retval => $retval
|
127 | |
);
|
128 | |
} else {
|
129 | |
# No handle, no errors.
|
130 | |
$exc->throw(
|
131 | |
error => $err,
|
132 | |
retval => $retval
|
133 | |
);
|
134 | |
}
|
135 | |
}
|
|
63 |
|
|
64 |
# No handle, no choice.
|
|
65 |
$pkg->throw(
|
|
66 |
error => $err,
|
|
67 |
retval => $retval
|
|
68 |
) unless ref($dbh ||= $DBI::lasth);
|
|
69 |
|
|
70 |
# Assemble arguments for a handle exception.
|
|
71 |
my @params = (
|
|
72 |
error => $err,
|
|
73 |
errstr => $dbh->errstr,
|
|
74 |
err => $dbh->err,
|
|
75 |
state => $dbh->state,
|
|
76 |
retval => $retval,
|
|
77 |
handle => $dbh,
|
|
78 |
);
|
|
79 |
|
|
80 |
# Throw the proper exception.
|
|
81 |
$class_for{STH}->throw(@params) if eval { $dbh->isa('DBI::st') };
|
|
82 |
$class_for{DBH}->throw(@params) if eval { $dbh->isa('DBI::db') };
|
|
83 |
$class_for{DRH}->throw(@params) if eval { $dbh->isa('DBI::dr') };
|
|
84 |
|
|
85 |
# Unknown exception. This shouldn't happen.
|
|
86 |
$class_for{Unknown}->throw(@params);
|
136 | 87 |
};
|
137 | 88 |
}
|
138 | 89 |
|
|
90 |
package Exception::Class::DBI::H;
|
|
91 |
sub warn { shift->handle->{Warn} }
|
|
92 |
sub active { shift->handle->{Active} }
|
|
93 |
sub kids { shift->handle->{Kids} }
|
|
94 |
sub active_kids { shift->handle->{ActiveKids} }
|
|
95 |
sub compat_mode { shift->handle->{CompatMode} }
|
|
96 |
sub inactive_destroy { shift->handle->{InactiveDestroy} }
|
|
97 |
sub trace_level { shift->handle->{TraceLevel} }
|
|
98 |
sub fetch_hash_key_name { shift->handle->{FetchHashKeyName} }
|
|
99 |
sub chop_blanks { shift->handle->{ChopBlanks} }
|
|
100 |
sub long_read_len { shift->handle->{LongReadLen} }
|
|
101 |
sub long_trunc_ok { shift->handle->{LongTruncOk} }
|
|
102 |
sub taint { shift->handle->{Taint} }
|
|
103 |
|
|
104 |
package Exception::Class::DBI::DBH;
|
|
105 |
sub auto_commit { shift->handle->{AutoCommit} }
|
|
106 |
sub db_name { shift->handle->{Name} }
|
|
107 |
sub statement { shift->handle->{Statement} }
|
|
108 |
sub row_cache_size { shift->handle->{RowCacheSize} }
|
|
109 |
|
|
110 |
package Exception::Class::DBI::STH;
|
|
111 |
sub num_of_fields { shift->handle->{NUM_OF_FIELDS} }
|
|
112 |
sub num_of_params { shift->handle->{NUM_OF_PARAMS} }
|
|
113 |
sub field_names { shift->handle->{NAME} }
|
|
114 |
sub type { shift->handle->{TYPE} }
|
|
115 |
sub precision { shift->handle->{PRECISION} }
|
|
116 |
sub scale { shift->handle->{SCALE} }
|
|
117 |
sub nullable { shift->handle->{NULLABLE} }
|
|
118 |
sub cursor_name { shift->handle->{CursorName} }
|
|
119 |
sub param_values { shift->handle->{ParamValues} }
|
|
120 |
sub statement { shift->handle->{Statement} }
|
|
121 |
sub rows_in_cache { shift->handle->{RowsInCache} }
|
|
122 |
|
139 | 123 |
1;
|
140 | 124 |
__END__
|
141 | 125 |
|
|
126 |
=begin comment
|
|
127 |
|
|
128 |
Fake-out Module::Build. Delete if it ever changes to support =head1 headers
|
|
129 |
other than all uppercase.
|
|
130 |
|
142 | 131 |
=head1 NAME
|
143 | 132 |
|
144 | 133 |
Exception::Class::DBI - DBI Exception objects
|
145 | 134 |
|
146 | |
=head1 SYNOPSIS
|
|
135 |
=end comment
|
|
136 |
|
|
137 |
=head1 Name
|
|
138 |
|
|
139 |
Exception::Class::DBI - DBI Exception objects
|
|
140 |
|
|
141 |
=head1 Synopsis
|
147 | 142 |
|
148 | 143 |
use DBI;
|
149 | 144 |
use Exception::Class::DBI;
|
150 | 145 |
|
151 | |
my $dbh = DBI->connect($data_source, $username, $auth, {
|
|
146 |
my $dbh = DBI->connect($dsn, $user, $pass, {
|
152 | 147 |
PrintError => 0,
|
153 | 148 |
RaiseError => 0,
|
154 | 149 |
HandleError => Exception::Class::DBI->handler
|
|
159 | 154 |
if (my $ex = $@) {
|
160 | 155 |
print STDERR "DBI Exception:\n";
|
161 | 156 |
print STDERR " Exception Type: ", ref $ex, "\n";
|
162 | |
print STDERR " Error: ", $ex->error, "\n";
|
163 | |
print STDERR " Err: ", $ex->err, "\n";
|
164 | |
print STDERR " Errstr: " $ex->errstr, "\n";
|
165 | |
print STDERR " State: ", $ex->state, "\n";
|
166 | |
my $ret = $ex->retval;
|
167 | |
$ret = 'undef' unless defined $ret;
|
168 | |
print STDERR " Return Value: $ret\n";
|
|
157 |
print STDERR " Error: ", $ex->error, "\n";
|
|
158 |
print STDERR " Err: ", $ex->err, "\n";
|
|
159 |
print STDERR " Errstr: ", $ex->errstr, "\n";
|
|
160 |
print STDERR " State: ", $ex->state, "\n";
|
|
161 |
print STDERR " Return Value: ", ($ex->retval || 'undef'), "\n";
|
169 | 162 |
}
|
170 | 163 |
|
171 | |
=head1 DESCRIPTION
|
|
164 |
=head1 Description
|
172 | 165 |
|
173 | 166 |
This module offers a set of DBI-specific exception classes. They inherit from
|
174 | 167 |
Exception::Class, the base class for all exception objects created by the
|
175 | |
Exception::Class module from the CPAN. Exception::Class::DBI itself offers a
|
176 | |
single class method, C<handler()>, that returns a code reference appropriate
|
177 | |
for passing to the DBI C<HandleError> attribute.
|
|
168 |
L<Exception::Class|Exception::Class> module from the CPAN.
|
|
169 |
Exception::Class::DBI itself offers a single class method, C<handler()>, that
|
|
170 |
returns a code reference appropriate for passing to the DBI C<HandleError>
|
|
171 |
attribute.
|
178 | 172 |
|
179 | 173 |
The exception classes created by Exception::Class::DBI are designed to be
|
180 | 174 |
thrown in certain DBI contexts; the code reference returned by C<handler()>
|
181 | |
and passed to the DBI C<HandleError> attribute determines the context,
|
182 | |
assembles the necessary metadata, and throws the apopropriate exception.
|
|
175 |
and passed to the DBI C<HandleError> attribute determines the context and
|
|
176 |
throws the apopropriate exception.
|
183 | 177 |
|
184 | 178 |
Each of the Exception::Class::DBI classes offers a set of object accessor
|
185 | 179 |
methods in addition to those provided by Exception::Class. These can be used
|
186 | |
to output detailed output in the event of an exception.
|
187 | |
|
188 | |
=head1 INTERFACE
|
|
180 |
to output detailed diagnostic information in the event of an exception.
|
|
181 |
|
|
182 |
=head1 Interface
|
189 | 183 |
|
190 | 184 |
Exception::Class::DBI inherits from Exception::Class, and thus its entire
|
191 | 185 |
interface. Refer to the Exception::Class documentation for details.
|
|
216 | 210 |
|
217 | 211 |
=back
|
218 | 212 |
|
219 | |
=head1 CLASSES
|
|
213 |
=head1 Classes
|
220 | 214 |
|
221 | 215 |
Exception::Class::DBI creates a number of exception classes, each one specific
|
222 | 216 |
to a particular DBI error context. Most of the object methods described below
|
|
275 | 269 |
The first value being returned by the DBI method that failed (typically
|
276 | 270 |
C<undef>).
|
277 | 271 |
|
|
272 |
=item C<handle>
|
|
273 |
|
|
274 |
my $db_handle = $ex->handle;
|
|
275 |
|
|
276 |
The DBI handle appropriate to the exception class. For
|
|
277 |
Exception::Class::DBI::DRH, it will be a driver handle. For
|
|
278 |
Exception::Class::DBI::DBH it will be a database handle. And for
|
|
279 |
Exception::Class::DBI::STH it will be a statement handle. If there is no
|
|
280 |
handle thrown in the exception (because, say, the exception was thrown before
|
|
281 |
a driver handle could be created), the C<handle> will be C<undef>.
|
|
282 |
|
278 | 283 |
=back
|
279 | 284 |
|
280 | 285 |
=head2 Exception::Class::DBI::H
|
|
530 | 535 |
determined. Inherits from L<Exception::Class::DBI|"Exception::Class::DBI">,
|
531 | 536 |
but implements no methods of its own.
|
532 | 537 |
|
533 | |
=head1 NOTE
|
|
538 |
=head1 Note
|
534 | 539 |
|
535 | 540 |
B<Note:> Not I<all> of the attributes offered by the DBI are exploited by
|
536 | 541 |
these exception classes. For example, the C<PrintError> and C<RaiseError>
|
|
538 | 543 |
missing attributes for the sake of completeness, let me know. Enough interest
|
539 | 544 |
will motivate me to get them in.
|
540 | 545 |
|
541 | |
=head1 TO DO
|
|
546 |
=head1 Subclassing
|
|
547 |
|
|
548 |
It is possible to subclass Exception::Class::DBI. The trick is to subclass its
|
|
549 |
subclasses, too. Similar to subclassing DBI itself, this means that the handle
|
|
550 |
subclasses should exist as subnamespaces of your base subclass.
|
|
551 |
|
|
552 |
It's easier to explain with an example. Say that you wanted to add a new
|
|
553 |
method to all DBI exceptions that outputs a nicely formatted error message.
|
|
554 |
You might do it like this:
|
|
555 |
|
|
556 |
package MyApp::Ex::DBI;
|
|
557 |
use base 'Exception::Class::DBI';
|
|
558 |
|
|
559 |
sub full_message {
|
|
560 |
my $self = shift;
|
|
561 |
return $self->SUPER::full_message unless $self->can('statement');
|
|
562 |
return $self->SUPER::full_message
|
|
563 |
. ' [for Statement "'
|
|
564 |
. $self->statement . '"]';
|
|
565 |
}
|
|
566 |
|
|
567 |
You can then use this subclass just like Exception::Class::DBI itself:
|
|
568 |
|
|
569 |
my $dbh = DBI->connect($dsn, $user, $pass, {
|
|
570 |
PrintError => 0,
|
|
571 |
RaiseError => 0,
|
|
572 |
HandleError => MyApp::Ex::DBI->handler,
|
|
573 |
});
|
|
574 |
|
|
575 |
And that's all well and good, except that none of Exception::Class::DBI's own
|
|
576 |
subclasses inherit from your class, so most exceptions won't be able to use
|
|
577 |
your spiffy new method.
|
|
578 |
|
|
579 |
The solution is to create subclasses of both the Exception::Class::DBI
|
|
580 |
subclasses and your own base subclass, as long as they each use the same
|
|
581 |
package name as your subclass, plus "H", "DRH", "DBH", "STH", and "Unknown".
|
|
582 |
Here's what it looks like:
|
|
583 |
|
|
584 |
package MyApp::Ex::DBI::H;
|
|
585 |
use base 'MyApp::Ex::DBI', 'Exception::Class::DBI::H';
|
|
586 |
|
|
587 |
package MyApp::Ex::DBI::DRH;
|
|
588 |
use base 'MyApp::Ex::DBI', 'Exception::Class::DBI::DRH';
|
|
589 |
|
|
590 |
package MyApp::Ex::DBI::DBH;
|
|
591 |
use base 'MyApp::Ex::DBI', 'Exception::Class::DBI::DBH';
|
|
592 |
|
|
593 |
package MyApp::Ex::DBI::STH;
|
|
594 |
use base 'MyApp::Ex::DBI', 'Exception::Class::DBI::STH';
|
|
595 |
|
|
596 |
package MyApp::Ex::DBI::Unknown;
|
|
597 |
use base 'MyApp::Ex::DBI', 'Exception::Class::DBI::Unknown';
|
|
598 |
|
|
599 |
And then things should work just spiffy! Of course, you probably don't need
|
|
600 |
the H subclass unless you want to add other methods for the DRH, DBH, and STH
|
|
601 |
classes to inherit from.
|
|
602 |
|
|
603 |
=head1 To Do
|
542 | 604 |
|
543 | 605 |
=over 4
|
544 | 606 |
|
|
548 | 610 |
DBD::ExampleP works well for DRH and DBH exceptions, but not so well for
|
549 | 611 |
STH exceptions.
|
550 | 612 |
|
551 | |
=item *
|
552 | |
|
553 | |
Change the model to merely store a reference to the DBI handle and get its
|
554 | |
attributes only when methods are called, rather than grabbing them all at once
|
555 | |
when the exception is created.
|
556 | |
|
557 | 613 |
=back
|
558 | 614 |
|
559 | |
=head1 BUGS
|
|
615 |
=head1 Bugs
|
560 | 616 |
|
561 | 617 |
Please send bug reports to <bug-exception-class-dbi@rt.cpan.org>.
|
562 | 618 |
|
|
619 |
=head1 Author
|
|
620 |
|
|
621 |
=begin comment
|
|
622 |
|
|
623 |
Fake-out Module::Build. Delete if it ever changes to support =head1 headers
|
|
624 |
other than all uppercase.
|
|
625 |
|
563 | 626 |
=head1 AUTHOR
|
564 | 627 |
|
|
628 |
=end comment
|
|
629 |
|
565 | 630 |
David Wheeler <david@kineticode.com>
|
566 | 631 |
|
567 | |
=head1 SEE ALSO
|
|
632 |
=head1 See Also
|
568 | 633 |
|
569 | 634 |
You should really only be using this module in conjunction with Tim Bunce's
|
570 | 635 |
L<DBI|DBI>, so it pays to be familiar with its documentation.
|
|
575 | 640 |
|
576 | 641 |
=head1 COPYRIGHT AND LICENSE
|
577 | 642 |
|
578 | |
Copyright (c) 2002-2005, David Wheeler. All Rights Reserved.
|
|
643 |
Copyright (c) 2002-2006, David Wheeler. All Rights Reserved.
|
579 | 644 |
|
580 | 645 |
This module is free software; you can redistribute it and/or modify it under
|
581 | 646 |
the same terms as Perl itself.
|