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

Siblings, I crave wisdom.

I have a big script which does lots of things with a db. So at the start of the script I do $dbh = DBI->connect("DBI:mysql:database=db_name", "user_name", "password"); and then I call lots of methods on $dbh and at the end I do $dbh->disconnect;.

So far so no surprises.

But now I have some things I do over and over again, and not just in this but in similar scripts. So I put them in subroutines and placed them in a module. And of course these subroutines all need a database handle. And I think that I have to open (or declare or whatever) that handle in the module itself in order to get it into the right scope. So now the only options I can see are

(1)open two db handles, one in the script, for use by code that isn't being re-used, and one in the module;

(2)open one db handle in the module and use that in the script

Both of these seem a bit fraught with scoping issues that I don't understand well enough to monkey with, and (1) seems downright wasteful. In fact I'm on the point of falling back on

(3) put the subroutines back in the script

But before I despair, I wondered if some wise monk could suggest a more elegant solution. Just to sum up, I have constructed some example code. What I have at the moment is a script a bit like this:
#!/usr/bin/perl -w use strict; use DBI; use dbmodule; END {DBModule_Close} ## do various things with the db specific to this ## script, such as: my $sth = $dbh->prepare("SELECT Foo FROM bar WHERE Foo = 'baz'"); $sth->execute; ## do various other things, which get done in lots of ## other places too, using subroutines in dbmodule: Write_Bar;
... and then I have a module a bit like this:
package dbmodule; require Exporter; our @ISA = ("Exporter"); $dbh = DBI->connect("DBI:mysql:database=thinweb", "thinweb", "126368") +; sub Write_Bar { $dbh->do("INSERT INTO bar (Foo,Fob,Fib) VALUES ('baz','biz','boz')"); } sub DBModule_Close { $dbh->disconnect; } our @EXPORT = qw/Write_Bar Insert_Into_BookingManager DBModule_Close/; 1;


§ George Sherston

Replies are listed 'Best First'.
Re: Using a single DBI handle in a script and a module
by Masem (Monsignor) on Nov 14, 2001 at 18:52 UTC
    It's probably better to use a single db connection for the same script rather than opening one up several times when needed. I would not be surprised that the latter would see a lot of time spent in the database connection phase compared to a single DBI connection.

    That said, you can still do one single dbh connection, and simply modify your subs in the separate files to handle this concept. That is, instead of a sub like this:

    sub Write_Bar { $dbh->do("INSERT INTO bar (Foo,Fob,Fib) VALUES ('baz','biz','boz')"); }
    You can have it like this instead:
    sub Write_Bar { my $dbh = shift; $dbh->do("INSERT INTO bar (Foo,Fob,Fib) VALUES ('baz','biz','boz')"); }
    and make sure that you pass the dbh when you call those subs.

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

Re: Using a single DBI handle in a script and a module
by Fletch (Bishop) on Nov 14, 2001 at 18:56 UTC

    Some possibilities:

    • pass $dbh as a parameter
    • Have a `setDBIhandle' routine that stashes the handle subs should use in a package variable
    • wrap things up in a class and make the handle an instance variable
    • just use $main::dbh and note the dependency (sometimes globals can make sense)

Re: Using a single DBI handle in a script and a module
by Asim (Hermit) on Nov 14, 2001 at 18:59 UTC

    Aside from Web Server-specific interfaces for persistent database connections like Apache::DBI, I was just looking at Ima::DBI, which might just solve some of the problems you have. According to the docs, amongst it's features are:

    * Sharing of database and sql information amongst inherited classes
    Any SQL statements and connections created by a class are available to its children via normal method inheritance.

    * Guarantees one connection per program.
    One program, one database connection (per database user). One program, one prepared statement handle (per statement, per database user). That's what Ima::DBI enforces. Extremely handy in persistant environments (servers, daemons, mod_perl, FastCGI, etc...)

    Check it out, see if that's a sloution for you. And if you do use it, let us know how it goes, as it'll be a while before I can pay with it... :(

    ----Asim, known to some as Woodrow.

Re: Using a single DBI handle in a script and a module
by grinder (Bishop) on Nov 14, 2001 at 19:45 UTC

    I handled this once by having the module create and tear down the db handle, via BEGIN and END clauses, and when I needed the use the handle directly in my main script, I simply used something like this:

    package My::Module; my $db; BEGIN { $db = DBI->connect($blah, $blah, $blah) or die "...\n"; } END { $db and $db->disconnect; } sub db { $db; }

    Which meant that I could grab the handle whenever I needed to do some ad-hoc querying, and have the module take care of the fiddly bits, as well as all the standard db stuff it was supposed to do anyway, all nicely encapsulated. You can actually do what appears to be rather weird...

    my $ss = My::Module::db->prepare($sql);

    I.e., nary a scalar in sight, but it does what you expect, without having to explicitly save the $db reference in a scalar. It's not exactly walking into some-one's living room without being invited, but it's close to leaning on the window sill and watching the TV set.

    I'm not sure I'd do this in a codebase with multiple people, but it worked well enough for me.

    --
    g r i n d e r
Re: Using a single DBI handle in a script and a module
by andye (Curate) on Nov 14, 2001 at 19:01 UTC
    I'm using something like that right now, and it's not as bad as you think. Here's owtdi:

    Step 1. Connect to the database, and get a database handle. You could do this in your main script, or in the module. If you do it in the module, have it return the database handle to the main script, like this:

    In the module: sub george_connect { return DBI->connect('yada yada'); } In the main script: my $dbh = george_connect;
    Step 2. Pass the database handle to each sub in the module as needed, e.g. select_cakes_by_type($dbh, 'battenburg');

    Step 3. Disconnect in module or main script.

    hth,
    andy.

    update: works fine with Apache::DBI

      Actually, going further with this, if you grab your dbh handle from a function in the 'main' part of the module, you can create a so-called singleton-like system, thus instead of:
      sub george_connect { return DBI->connect('yada yada'); }
      you can do:
      my $dbh; sub george_connect { if ( !$dbh ) { $dbh = DBI->connect('yada yada'); } return $dbh; }
      which maintains a handle one created, but never tried to reconnect it again.

      -----------------------------------------------------
      Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      "I can see my house from here!"
      It's not what you know, but knowing how to find it if you don't know that's important

DBI / module prob: my solution - comments most welcome
by George_Sherston (Vicar) on Nov 14, 2001 at 21:12 UTC
    Well, let me tell you a funny story. I kept finding that my db hung every time I ran a perfectly functional piece of code, that worked fine before I modularised it. The form of the code was:
    # declare a db handle # get a lock on the table # call a subroutine from the module that does something to the db # unlock the table
    Well, you probably spotted the problem right away... but it took me a while to figure out that what was happening was that the subroutine in the module was calling DBI methods on a db handle which, yes, had the same "name" as the one in the script (both called $dbh, right?) but actually... was a different db handle.

    So I get my lock. Fine. Then, over in the module, we do a call to the db but.. hey, somebody's got a lock on this db, better wait until they're done... waiting... hum de dum, gee that guy sure is taking his time with that db operation... whistle whistle, read a magazine... still locked... oh hell, I'll just shoot myself in despair.

    Meantime, back in the script we're waiting for the subroutine to finish... hell, what's going on in there, they've been ages... I want to take the lock off this db, which is bound to be wrecking somebody else's day, but I can't get to that until I get something back from the module... tum de dum... scratches, gets another coffee... looks at memepool... hell, still no answer from that pesky subroutine, I'll just send a connection timed out error and go home for the day.

    Update: In case you should want to learn from a couple of other ways to get tangled whilst using DBI, grinder kindly drew my attention to these

    So that's the background to my choice of solution - I want something bone simple so that even the bear of smallest brain won't end up with two db handles on the go at once. And, drawing together some of the strands above, the way I'm planning to do it is:

    (1) In the script:
    use dbmodule; my $dbh; BEGIN {$dbh = DBModule_Start} END {DBModule_Finish} # then do various stuff including $sth = $dbh->prepare("BLAH BLAH") die $dbh->errstr; $sth->execute or die $dbh->errstr; DBModule_Sub1;
    and (2) In the module:
    my $module_dbh; sub DBModule_Start { unless ($module_dbh) { $module_dbh = DBI->connect("DBI:mysql:database=db_name", "user_ +name", "password"); } return $module_dbh; } sub DBModule_Sub1 { $module_dbh->do{'BLAH BLAH BLAH'} or die $module_dbh->errstr; } sub DBModule_Finish { $module_dbh->disconnect; }
    Giving the same db handle different names in the script and the module may seem confusing, but it makes sense to me, because it emphasises the fact that they ARE different names, albeit, in this case, mercifully, names for the same thing.

    One might also think that the best way to keep from getting one's laces tied together was to pass the db handle as an argument in a subroutine call, and do all the db handle creation in the module. But for portability I like having the db name, password etc in a module; and also I'd like to be sure that the db handle wasn't getting mixed up with the other arguments passed to the subroutine, so that I ended up writing it to my db or something funky like that...

    You might also have some sympathy with the view (as perrin) that this is something to manage in MySQL. I like that plan, but against it I see two arguments, both rather specific to me: (1) it reduces portability - I'm just now looking into going to PostGreSQL (2) I don't know much MySQL, and I want to build on the firmest foundations I've got.


    I've now incorporated the above into my two main scripts and it seems to work. I'd be very grateful for any tweaking comments, however. And I'm more than grateful for kind advice of monks above.

    § George Sherston
Re: Using a single DBI handle in a script and a module
by perrin (Chancellor) on Nov 14, 2001 at 20:24 UTC
    DBI has built-in support for this. Look at DBI->connect_cached().
Re: Using a single DBI handle in a script and a module
by rob_au (Abbot) on Nov 15, 2001 at 14:04 UTC
    As per chatterbox discussions and traded /msg's, the following is a snippet of code as to how I would implement such a piece of logic:

    #!/usr/bin/perl -Tw use strict; use warnings; my $dbh = self->new("DBI:mysql:database=test", undef, undef); # Reference database handle via self package # $dbh->execute("select * from users"); # ... or ... # Reference database handle methods directly from calling script # $dbh->{dbh}->do("select * from users"); exit 0; package self; use DBI; use strict; sub new { my $param = shift; my $self = {}; bless ($self, (ref($param) || $param)); $self->{dbh} = DBI->connect(@_); return $self; }; sub execute { my $self = shift; return $self->{dbh}->do(@_); }; 1; __END__

    This code is fairly straight-forward in that a class self is created and returned to the calling namespace. This class can be accessed either within the class-space as per the execute method or via dereference to the included references to other classes ($self->{dbh}).

     

    Ooohhh, Rob no beer function well without!

Re: Using a single DBI handle in a script and a module
by Sinister (Friar) on Nov 15, 2001 at 13:33 UTC
    The whole thing looks kinda OOP. Why not hang the DB connection in your object. Passing it around through all these routines as a parameter is a waste of namespace, and it makes your coupling to tight. If you have an OO interface, you could do something like
    sub SomeDBRoutine { my $self = shift; $obj->dbh->WhatEver; } sub dbh { my $self = shift; if ( @_ ) { $connection_string = shift; $self->{dbh} = DBI->Connect($connection_string, "user","pass"); } return $self->{dbh}; }
    This way you let the module control the connection and you can quite easily set a connection or use it.

    Did I mention that this code is untested...?

    Sinister greetings.
    "With tying hashes you can do everything God and Larry have forbidden" -- Johan Vromans - YAPC::Europe 2001
    perldoc -q $_