From cfd74798db0b891316646cef998a1b6dd04e0c2a Mon Sep 17 00:00:00 2001
From: Graham McVicker <mcvicker@sanger.ac.uk>
Date: Fri, 5 Mar 2004 18:02:58 +0000
Subject: [PATCH] Made it possible to share the same database handle between
 multiple DBConnections This is useful if you want to conserve db connections
 and use the same db for compara/pipeline etc.

---
 modules/Bio/EnsEMBL/DBSQL/DBConnection.pm | 125 +++++++++++++++-------
 1 file changed, 86 insertions(+), 39 deletions(-)

diff --git a/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm b/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm
index a5c57dbdb8..923e01ed74 100644
--- a/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm
+++ b/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm
@@ -17,6 +17,11 @@
 
    If you go through prepare you could log all your select statements.
 
+   If you want to share a database handle with other database connections
+   you can do the following:
+
+   $db2 = Bio::EnsEMBL::DBSQL::DBConnection->new(-dbconn => $db);
+
 =head1 DESCRIPTION
 
   This only wraps around the perl DBI->connect call, 
@@ -24,17 +29,15 @@
 
 =head1 CONTACT
 
-Describe contact details here
+  This module is part of the Ensembl project: www.ensembl.org
 
-=head1 APPENDIX
+  Ensembl development mailing list: <ensembl-dev@ebi.ac.uk>
 
-The rest of the documentation details each of the object methods. Internal methods are usually preceded with a _
+=head1 METHODS
 
 =cut
 
 
-# Let the code begin...
-
 package Bio::EnsEMBL::DBSQL::DBConnection;
 
 use vars qw(@ISA);
@@ -45,6 +48,7 @@ use Bio::EnsEMBL::Root;
 use DBI;
 
 use Bio::EnsEMBL::Utils::Exception qw(throw info warning);
+use Bio::EnsEMBL::Utils::Argument qw(rearrange);
 
 @ISA = qw(Bio::EnsEMBL::Root);
 
@@ -66,10 +70,13 @@ use Bio::EnsEMBL::Utils::Exception qw(throw info warning);
   Arg [DRIVER] : (optional) string
                  The type of database driver to use to connect to the DB
                  mysql by default.
-  Example    :$dbc = new Bio::EnsEMBL::DBSQL::DBConnection(-user=> 'anonymous',
-                                                           -dbname => 'pog',
-							   -host   => 'caldy',
-							   -driver => 'mysql');
+  Example    : $dbc = Bio::EnsEMBL::DBSQL::DBConnection->new
+                  (-user=> 'anonymous',
+                   -dbname => 'pog',
+                   -host   => 'caldy',
+                   -driver => 'mysql');
+
+               $dbc2 = Bio::EnsEMBL::DBSQL::DBConnection->new(-DBCONN => $dbc);
   Description: Constructor for a DatabaseConenction. Any adaptors that require
                database connectivity should inherit from this class.
   Returntype : Bio::EnsEMBL::DBSQL::DBConnection 
@@ -82,25 +89,30 @@ use Bio::EnsEMBL::Utils::Exception qw(throw info warning);
 sub new {
   my $class = shift;
 
+  my ($db, $dbconn,$host,$driver,$user,$password,$port) =
+    rearrange([qw(DBNAME DBCONN HOST DRIVER USER PASS PORT)],@_);
+
   my $self = {};
   bless $self, $class;
 
-  my (
-      $db,
-      $host,
-      $driver,
-      $user,
-      $password,
-      $port,
-     ) = $self->_rearrange([qw(
-			       DBNAME
-			       HOST
-			       DRIVER
-			       USER
-			       PASS
-			       PORT
-			      )],@_);
-    
+  if($dbconn) {
+    if(!ref($dbconn) || !$dbconn->isa('Bio::EnsEMBL::DBSQL::DBConnection')) {
+      throw("Bio::EnsEMBL::DBSQL::DBConnection argument expected.");
+    }
+
+    my $rcount = $dbconn->ref_count();
+    $$rcount++; # dereference and increment shared var
+    $self->ref_count($rcount);
+
+    $self->driver($dbconn->driver());
+    $self->host($dbconn->host());
+    $self->username($dbconn->username());
+    $self->password($dbconn->password());
+    $self->port($dbconn->port());
+    $self->db_handle($dbconn->db_handle());
+
+    return Bio::EnsEMBL::Container->new($self);
+  }
 
   $db   || throw("Database object must have a database name");
   $user || throw("Database object must have a user");
@@ -121,10 +133,12 @@ sub new {
   eval{
     $dbh = DBI->connect("$dsn","$user",$password, {RaiseError => 1});
   };
-    
+
   $dbh || throw("Could not connect to database $db user " .
 		       "$user using [$dsn] as a locator\n" . $DBI::errstr);
 
+  my $ref_count = 1;
+  $self->ref_count(\$ref_count);
   $self->db_handle($dbh);
 
   $self->username( $user );
@@ -134,9 +148,9 @@ sub new {
   $self->port($port);
   $self->driver($driver);
 
-  #be very sneaky and actually return a container object which is outside
-  #of the circular reference loops and will perform cleanup when all references
-  #to the container are gone.
+  # be very sneaky and actually return a container object which is outside
+  # of the circular reference loops and will perform cleanup when all 
+  # references to the container are gone.
   return new Bio::EnsEMBL::Container($self);
 }
 
@@ -280,6 +294,34 @@ sub password {
 }
 
 
+=head2 ref_count
+
+  Arg [1]    : (optional) ref to int $ref_count
+  Example    : $count = 1; $self->ref_count(\$count);
+  Description: Getter/setter for the number of existing references to
+               this DBConnections database handle.  This is a reference to
+               a scalar because it is shared by all database connections which
+               share the same database handle.  This is used by the DESTROY
+               method to decide whether it should disconnect from the database.
+  Returntype : reference to int
+  Exceptions : throw on bad argument
+  Caller     : new
+
+=cut
+
+sub ref_count {
+  my $self = shift;
+
+  if(@_) {
+    my $ref_count = shift;
+    if(ref($ref_count) ne 'SCALAR') {
+      throw("Reference to scalar argument expected.");
+    }
+    $self->{'ref_count'} = $ref_count;
+  }
+  return $self->{'ref_count'};
+}
+
 
 =head2 locator
 
@@ -296,7 +338,7 @@ sub password {
 
 sub locator {
   my $self = shift;
-  
+
   my $ref;
 
   if($self->isa('Bio::EnsEMBL::Container')) {
@@ -336,22 +378,22 @@ sub _get_adaptor {
   }
 
   my( $adaptor, $internal_name );
-  
+
   #Create a private member variable name for the adaptor by replacing
   #:: with _
-  
+
   $internal_name = $module;
 
   $internal_name =~ s/::/_/g;
 
   unless (defined $self->{'_adaptors'}{$internal_name}) {
     eval "require $module";
-    
+
     if($@) {
       warning("$module cannot be found.\nException $@\n");
       return undef;
     }
-      
+
     $adaptor = "$module"->new($self, @args);
 
     $self->{'_adaptors'}{$internal_name} = $adaptor;
@@ -408,7 +450,7 @@ sub prepare {
       throw("Database object has lost its database handle.");
    }
 
-   info("SQL(".$self->dbname."):$string");
+   #info("SQL(".$self->dbname."):$string");
 
    return $self->{_db_handle}->prepare($string);
 } 
@@ -573,11 +615,16 @@ sub DESTROY {
    my $dbh = $obj->{'_db_handle'};
 
    if( $dbh ) {
-     #don't disconnect if the InactiveDestroy flag has been set
-     #this can really screw up forked processes
-     if(!$dbh->{'InactiveDestroy'}) {
+     my $refcount = $obj->ref_count();
+     $$refcount--;
+
+     # Do not disconnect if the InactiveDestroy flag has been set
+     # this can really screw up forked processes.
+     # Also: do not disconnect if this database handle is shared by
+     # other DBConnections (as indicated by the refcount)
+     if(!$dbh->{'InactiveDestroy'} && $$refcount == 0) {
        $dbh->disconnect;
-     } 
+     }
 
      $obj->{'_db_handle'} = undef;
    }
-- 
GitLab