Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Deriving a class from DBI.

by clueless newbie (Hermit)
on Mar 23, 2010 at 16:46 UTC ( #830326=perlquestion: print w/ replies, xml ) Need Help??
clueless newbie has asked for the wisdom of the Perl Monks concerning the following question:

Greetings!

I need to create a derived class (call it myDBI) using DBI as the base class so I can override a few methods in the example "do". (Okay, assume, just for fun, that I wish to log the sql statement.) I've tried:

package myDBI; use strict; use warnings; use DBI; our @ISA=("DBI"); use strict; use warnings; our $AUTOLOAD; sub AUTOLOAD { warn "$AUTOLOAD\n"; }; sub connect { ### @_ my $class=shift; my $self=DBI->connect(@_); bless $self; return $self; }; # connect sub do { my $self=shift; ### @_ $_[0]=~ s{\bdbi\b}{zdbi}i; ### @_ $self->SUPER::do(@_); }; # do 1;

Using the following for a test

#! use lib '.'; use DBI; use myDBI; use strict; use warnings; my $sql_s=<<'__EOCreate__'; CREATE TABLE dbi (JFF integer) __EOCreate__ my $Database_s='test.db'; my (%dbh_h,%sth_h); { # Connect to DBI and create a table $dbh_h{DBI}=DBI->connect("DBI:SQLite:$Database_s","","",{Raise +Error=>1,PrintError=>1,AutoCommit=>1,LongReadLen=>1024*1024}) or die "Can't connect to 'DBI:SQLite:$Database_s'!"; ### %dbh_h $dbh_h{DBI}->do($sql_s) or die $dbh_h{DBI}->errstr; { # Disconnect from the database $sth_h{$_}->finish() for (keys %sth_h); $dbh_h{$_}->commit() for (keys %dbh_h); $dbh_h{$_}->disconnect() for (keys %dbh_h); }; }; %dbh_h=(); %sth_h=(); { # Connect to myDBI and create a table $dbh_h{myDBI}=myDBI->connect("DBI:SQLite:$Database_s","","",{R +aiseError=>1,PrintError=>1,AutoCommit=>1,LongReadLen=>1024*1024}) or die "Can't connect to 'DBI:SQLite:$Database_s'!"; ### %dbh_h $dbh_h{myDBI}->do($sql_s) or die $dbh_h{myDBI}->errstr; { # Disconnect from the database $sth_h{$_}->finish() for (keys %sth_h); $dbh_h{$_}->commit() for (keys %dbh_h); $dbh_h{$_}->disconnect() for (keys %dbh_h); }; };

I get

C:\Code\Objects>perl Example_02.pl commit ineffective with AutoCommit enabled at Example_02.pl line 24. commit ineffective with AutoCommit at Example_02.pl line 24. Can't locate auto/myDBI/SUPER/do.al in @INC (@INC contains: . C:/Perl/ +site/lib C:/Perl/lib) at myDBI.pm line 29 myDBI::DESTROY

So how do I create a derived class from DBI?

Thanks!

Comment on Deriving a class from DBI.
Select or Download Code
Re: Deriving a class from DBI.
by Corion (Pope) on Mar 23, 2010 at 17:04 UTC

    I wouldn't.

    Just create classes that delegate to DBI and wrap DBI, potentially via AUTOLOAD. You will need:

    1. One DBI-delegator, mainly implementing ->connect , returning
    2. One DBH-delegator, mainly implementing ->prepare and ->do, likely via ->prepare->execute, and
    3. One STH-delegator, mainly implementing ->execute and ->fetchall_*

    I would stay away from inheritance and just store the DBI::* instances, as you seem to be mostly interested in the user side of interactions and not so much in actually modifying the behaviour of DBI and its components.

      Thanks for the suggestion. I hadn't thought of delegation.

Re: Deriving a class from DBI.
by mje (Curate) on Mar 23, 2010 at 17:19 UTC

    You might be able to use DBI callbacks to do what you want to do. They weren't documented but I believe they are now documented in the subversion tree of DBI. Just read the DBI docs for where to get DBI from subversion and read the docs on callbacks with perldoc -F DBI.pm

      Thank you! Found Tim Bunce's blog that covers that here.
Re: Deriving a class from DBI.
by ikegami (Pope) on Mar 23, 2010 at 17:24 UTC
    I wanted to add a few convenience methods to statement handles (as if it didn't have enough already). Specifically, I wanted to be able to do
    $sth->selectrow_arrayref(...)
    when the existing convention is
    $dbh->selectrow_arrayref($sth, ...)

    Here's the approach I took:

    use strict; use warnings; use DBI qw( ); # ---------------------------------------- BEGIN { package MyDBI; our @ISA = 'DBI'; } # ---------------------------------------- BEGIN { package MyDBI::db; our @ISA = 'DBI::db'; } # ---------------------------------------- BEGIN { package MyDBI::st; our @ISA = 'DBI::st'; sub bind_params { my $sth = $_[0]; for my $i (1..$#_) { $sth->bind_param($i, $_[$i]) or return; } return 1; } sub _do_selectrow { my ($method, $sth, $attr) = splice(@_, 0, 3); $sth->execute(@_) or return; my $row = $sth->$method() or return; $sth->finish(); return $row; } sub _do_firstrow { my ($method, $sth, $attr) = splice(@_, 0, 3); $sth->execute(@_) or return; my $row = $sth->$method() or return $sth->set_err($DBI::stderr, "No rows returned"); $sth->finish(); return $row; } sub selectrow_hashref { return _do_selectrow('fetchrow_hashref', @ +_); } sub firstrow_hashref { return _do_firstrow ('fetchrow_hashref', @ +_); } sub selectrow_arrayref { return _do_selectrow('fetchrow_arrayref', +@_); } sub firstrow_arrayref { return _do_firstrow ('fetchrow_arrayref', +@_); } sub selectrow_array { my $row = _do_selectrow('fetchrow_arrayref +', @_) or return; return wantarray ? @$row : $row->[0]; } sub firstrow_array { my $row = _do_firstrow ('fetchrow_arrayref +', @_) or return; return wantarray ? @$row : $row->[0]; } } # ---------------------------------------- 1;

    You could surely override do using this method. Create the three classes, setup their inheritance relationship, and override do in ::db.

      This points out why my attempts failed! Three classes not just one!

      Thank you, ikegami, thank you!

        Database handles and statement handles have different methods, so they are two different classes (::db and ::st). DBI itself provides global static methods. There's a fourth class (::dr) for database drivers.

        DBI determines the name of the database handle class and the statement handle class based on the package used to create the connection ($pkg->connect), so there's no need to tell DBI about your ::db and ::st class. On the flip side, you need to create all three subclasses even if you only want to subclass one.

Re: Deriving a class from DBI.
by moritz (Cardinal) on Mar 23, 2010 at 19:35 UTC
    (Okay, assume, just for fun, that I wish to log the sql statement.)

    Then you'd use DBI's logging facility. Search for trace in the DBI docs.

    Perl 6 - links to (nearly) everything that is Perl 6.
Re: Deriving a class from DBI.
by merlyn (Sage) on Mar 24, 2010 at 02:16 UTC
    This is, in fact, covered in the fine documentation, which you would be nudged to read.

    -- Randal L. Schwartz, Perl hacker

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

      Mea maxima culpa! I should have looked!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://830326]
Approved by Hue-Bond
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (10)
As of 2014-12-27 17:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (177 votes), past polls