Apache-LoggedAuthDBI
view release on metacpan or search on metacpan
to be C<::st>).
The leading 'C<DBI>' is known as the 'root class' and the extra
'C<::db>' or 'C<::st>' are the 'handle type suffixes'. If you want
to subclass the DBI you'll need to put your overriding methods into
the appropriate classes. For example, if you want to use a root class
of C<MySubDBI> and override the do(), prepare() and execute() methods,
then your do() and prepare() methods should be in the C<MySubDBI::db>
class and the execute() method should be in the C<MySubDBI::st> class.
To setup the inheritance hierarchy the @ISA variable in C<MySubDBI::db>
should include C<DBI::db> and the @ISA variable in C<MySubDBI::st>
should include C<DBI::st>. The C<MySubDBI> root class itself isn't
currently used for anything visible and so, apart from setting @ISA
to include C<DBI>, it should be left empty.
So, having put your overriding methods into the right classes, and
setup the inheritance hierarchy, how do you get the DBI to use them?
You have two choices, either a static method call using the name
of your subclass:
$dbh = MySubDBI->connect(...);
or specifying a C<RootClass> attribute:
$dbh = DBI->connect(..., { RootClass => 'MySubDBI' });
The only difference between the two is that using an explicit
RootClass attribute will make the DBI automatically attempt to load
a module by that name if the class doesn't exist.
If both forms are used then the attribute takes precedence.
When subclassing is being used then, after a successful new
connect, the DBI->connect method automatically calls:
$dbh->connected($dsn, $user, $pass, \%attr);
The default method does nothing. The call is made just to simplify
any post-connection setup that your subclass may want to perform.
If your subclass supplies a connected method, it should be part of the
MySubDBI::db package.
Here's a brief example of a DBI subclass. A more thorough example
can be found in t/subclass.t in the DBI distribution.
package MySubDBI;
use strict;
use DBI;
use vars qw(@ISA);
@ISA = qw(DBI);
package MySubDBI::db;
use vars qw(@ISA);
@ISA = qw(DBI::db);
sub prepare {
my ($dbh, @args) = @_;
my $sth = $dbh->SUPER::prepare(@args)
or return;
$sth->{private_mysubdbi_info} = { foo => 'bar' };
return $sth;
}
package MySubDBI::st;
use vars qw(@ISA);
@ISA = qw(DBI::st);
sub fetch {
my ($sth, @args) = @_;
my $row = $sth->SUPER::fetch(@args)
or return;
do_something_magical_with_row_data($row)
or return $sth->set_err(1234, "The magic failed", undef, "fetch");
return $row;
}
When calling a SUPER::method that returns a handle, be careful to
check the return value before trying to do other things with it in
your overridden method. This is especially important if you want
to set a hash attribute on the handle, as Perl's autovivification
will bite you by (in)conveniently creating an unblessed hashref,
which your method will then return with usually baffling results
later on. It's best to check right after the call and return undef
immediately on error, just like DBI would and just like the example
above.
If your method needs to record an error it should call the set_err()
method with the error code and error string, as shown in the example
above. The error code and error string will be recorded in the
handle and available via C<$h-E<gt>err> and C<$DBI::errstr> etc.
The set_err() method always returns an undef or empty list as
approriate. Since your method should nearly always return an undef
or empty list as soon as an error is detected it's handy to simply
return what set_err() returns, as shown in the example above.
If the handle has C<RaiseError>, C<PrintError>, or C<HandleError>
etc. set then the set_err() method will honour them. This means
that if C<RaiseError> is set then set_err() won't return in the
normal way but will 'throw an exception' that can be caught with
an C<eval> block.
You can stash private data into DBI handles
via C<$h-E<gt>{private_..._*}>. See the entry under L</ATTRIBUTES
COMMON TO ALL HANDLES> for info and important caveats.
=head1 TRACING
The DBI has a powerful tracing mechanism built in. It enables you
to see what's going on 'behind the scenes', both within the DBI and
the drivers you're using.
=head2 Trace Settings
Which details are written to the trace output is controlled by a
combination of a I<trace level>, an integer from 0 to 15, and a set
of I<trace flags> that are either on or off. Together these are known
as the I<trace settings> and are stored together in a single integer.
For normal use you only need to set the trace level, and generally
only to a value between 1 and 4.
Each handle has it's own trace settings, and so does the DBI.
When you call a method the DBI merges the handles settings into its
own for the duration of the call: the trace flags of the handle are
OR'd into the trace flags of the DBI, and if the handle has a higher
trace level then the DBI trace level is raised to match it.
The previous DBI trace setings are restored when the called method
returns.
=head2 Trace Levels
Trace I<levels> are as follows:
0 - Trace disabled.
1 - Trace DBI method calls returning with results or errors.
2 - Trace method entry with parameters and returning with results.
3 - As above, adding some high-level information from the driver
( run in 1.692 second using v1.01-cache-2.11-cpan-524268b4103 )