moo has asked for the wisdom of the Perl Monks concerning the following question:

I have been trying to pass db handle to a subroutine, but
bombs out every time.  I searched through Programming the
DBI looking for any example of a connection wrapped inside a 
subroutine, but haven't found one.

I'd like to do something like:
sub db_connection { my ( $self ) = @_; # Insert some code to pull out db specifics like user and # password from $self. my $dbh = DBI->connect( " yada yada " ), { PrintError => 0}, or die "cannot open db connection: $DBI::errstr\n"; # should I add the connection to $self? # this looks incorrect; maybe return instance after # adding it to $self? return $dbh; } # sub db_connection
This way I will be able to call a connection and leave it open
for both an insert and a query.

Also, is maintaining the connection while performing multiple
operations optimal?

Thanks,

moo

Replies are listed 'Best First'.
Re: passing DBI database handles to subroutines
by chromatic (Archbishop) on Aug 09, 2000 at 22:28 UTC
    This works for me:
    #!/usr/bin/perl -w use strict; use DBI; my $dbh = connect_db(); print ">>$dbh<<\n"; query($dbh); sub connect_db { my $dbh = DBI->connect('DBI:mysql:dbname', 'user', 'pass') || die "can't open DB: $DBI::errstr\n"; return $dbh; } sub query { my $handle = shift; my $sth = $dbh->prepare('Select id FROM page_data'); my $id; $sth->execute(); $sth->bind_columns(\$id); while ($sth->fetch()) { print "\tFound id $id.\n"; } $sth->finish(); }
    (Database specific stuff is taken from a project I'm working on.)

    However, I'd be more likely to do what you suggest, adding the DB handle to $self and adding wrapper methods to the class to operate on the handle and return data. Why not abstract all the way and make an OO interface around the database object? That way, you don't have to pass it to different objects, you just have one object that knows how to get at your data.

Re: passing DBI database handles to subroutines
by mikfire (Deacon) on Aug 09, 2000 at 22:20 UTC
    Moo, in theory your function should work. Is this the actual code you are using? Without seeing more code and the error message you are getting, I can but guess.

    My main thought is that the

    my $dbh = DBI->connect( " yada yada " ), { PrintError => 0}, or die "cannot open db connection: $DBI::errstr\n";
    is occuring in a different scope than the return call is. For example, if you are doing something like:
    eval { my $dbh = DBI->connect( " yada yada " ), { PrintError => 0}, or die "cannot open db connection: $DBI::errstr\n"; } return $dbh;
    $dbh has gone out of scope before being returned and you will get undef on the other side.

    Try using something like

    sub db_connection { my ( $self ) = @_; my $dbh;

    Standard statements of making sure you are using -w and strict and a request to use the <code></code> tags.

    mikfire

      Thanks everyone for your replies. I am currently experimenting with your suggestions to see which one works best. Any code that works gets stuck in the TIMTOWTDI drawer. I tried Merlyn's suggestion first and it works great. I am also messing around with other people's suggestions and they work too.... I spent several hours researching your responses. I will try to post a "here is what I learned" summary for any monks new to DBI. Thanks, moo
RE: passing DBI database handles to subroutines
by tilly (Archbishop) on Aug 09, 2000 at 22:44 UTC
    I do this one a little differently than most people:
    { my $dbh; sub get_db_prod { unless (defined ($dbh)) { $dbh = DBI->connect( ('DBI:Sybase:' . prod_server), prod_user, prod_pass, {PrintError => 0, AutoCommit => 0} ) or die $DBI::errstr; } return $dbh; } }
    Previously I had already done a "use constant" to create constants for prod_server, prod_user and prod_pass. I won't paste that code here. (VBG) But the key trick is that you can ask for the connection as many times as you want, from wherever you want. The first time you need it it will spring into existence and from then on you get the memoized instance. Bye bye global variable. :-)

    Note that I use a wrapper for errors, so I set PrintError to 0, you may want to change that default. Look at the DBI docs to decide what defaults make sense to you.

    A big win with the above code. This is really, really nice in programs which may or may not need a database connection, but if they do will need it in several places. Just place calls to the above function wherever you need it and don't worry about any custom logic for whether or not to initiate a connection. I have some scripts that this idea has really simplified. Depending on what you are trying to do, YMMV.

    And to answer your final question, building and destroying connections is very expensive, maintaining them is pretty cheap. So you want to open a connection and not close it again if you can at all help it.

      Make sure your named subroutine is inside a BEGIN block, like so:
      BEGIN { my $dbh; sub get_db_handle { .. .. return $dbh; } }
      If you fail to do that, and manage executing that block twice in a program, you'll get the wonderful $dbh won't stay shared error, indicating some serious bad voodoo.

      -- Randal L. Schwartz, Perl hacker

        Right you are of course.

        OTOH another way to guarantee the right behaviour is to put the subroutine in a module. And that module not coincidentally in my case is where the name and password are, making it easy to change the password periodically.

        Of course dealing with mod_perl you probably get to curse the "won't stay shared" error much more than I do. :-)