From 549e6b9a00babe1ac019ab460a185b2cff9a285f Mon Sep 17 00:00:00 2001
From: Jessica Severin <jessica@sanger.ac.uk>
Date: Wed, 10 Mar 2004 13:12:40 +0000
Subject: [PATCH] added disconnect method. smart enough to deal with shared
 db_handles 'reconnect' is automatic when any access to the handle (db_handle,
 prepare) is requested. Also added explicit connect method which returns if
 the handle is already connected (shouldn't need to be called)

---
 modules/Bio/EnsEMBL/DBSQL/DBConnection.pm | 110 +++++++++++++++++-----
 modules/t/dbConnection.t                  |  27 +++++-
 2 files changed, 115 insertions(+), 22 deletions(-)

diff --git a/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm b/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm
index 923e01ed74..f0ccb4d881 100644
--- a/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm
+++ b/modules/Bio/EnsEMBL/DBSQL/DBConnection.pm
@@ -100,16 +100,18 @@ sub new {
       throw("Bio::EnsEMBL::DBSQL::DBConnection argument expected.");
     }
 
+    #share a common db_handle, use a shared scalar ref_count to track # connections
     my $rcount = $dbconn->ref_count();
     $$rcount++; # dereference and increment shared var
     $self->ref_count($rcount);
+    $self->db_handle($dbconn->db_handle());
 
     $self->driver($dbconn->driver());
     $self->host($dbconn->host());
+    $self->port($dbconn->port());
+    $self->dbname($dbconn->dbname());
     $self->username($dbconn->username());
     $self->password($dbconn->password());
-    $self->port($dbconn->port());
-    $self->db_handle($dbconn->db_handle());
 
     return Bio::EnsEMBL::Container->new($self);
   }
@@ -127,6 +129,7 @@ sub new {
     $port = 3306;
   }
 
+=head1
   my $dsn = "DBI:$driver:database=$db;host=$host;port=$port";
 
   my $dbh;
@@ -140,6 +143,7 @@ sub new {
   my $ref_count = 1;
   $self->ref_count(\$ref_count);
   $self->db_handle($dbh);
+=cut
 
   $self->username( $user );
   $self->host( $host );
@@ -148,6 +152,8 @@ sub new {
   $self->port($port);
   $self->driver($driver);
 
+  $self->connect();
+  
   # 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.
@@ -155,6 +161,45 @@ sub new {
 }
 
 
+=head2 connect
+
+  Example    : $db_connection->connect()
+  Description: Explicitely connect to database if not connected
+  Returntype : none
+  Exceptions : none
+  Caller     : new, db_handle
+
+=cut
+
+sub connect {
+  my $self = shift;
+
+  if($self->{'_db_handle'}) { return; }
+
+  my $host      = $self->host();
+  my $driver    = $self->driver();
+  my $user      = $self->username();
+  my $password  = $self->password();
+  my $port      = $self->port();
+  my $db        = $self->dbname();
+
+  my $dsn = "DBI:$driver:database=$db;host=$host;port=$port";
+
+  my $dbh;
+  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);
+
+  #print STDERR "DBConnection : CONNECT\n";
+  my $ref_count = 1;
+  $self->ref_count(\$ref_count);
+  $self->{'_db_handle'} = $dbh;
+}
+
+
 =head2 driver
 
   Arg [1]    : (optional) string $arg
@@ -420,12 +465,12 @@ sub db_handle {
 
    if( defined $value) {
       $self->{'_db_handle'} = $value;
-    }
-    return $self->{'_db_handle'};
+   }
+   else { $self->connect(); }
+   return $self->{'_db_handle'};
 }
 
 
-
 =head2 prepare
 
   Arg [1]    : string $string
@@ -446,13 +491,13 @@ sub prepare {
    if( ! $string ) {
        throw("Attempting to prepare an empty SQL query.");
    }
-   if( !defined $self->{_db_handle} ) {
+   if( !defined $self->db_handle ) {
       throw("Database object has lost its database handle.");
    }
 
    #info("SQL(".$self->dbname."):$string");
 
-   return $self->{_db_handle}->prepare($string);
+   return $self->db_handle->prepare($string);
 } 
 
 
@@ -612,23 +657,46 @@ sub DESTROY {
 
    #print STDERR "DESTROYING DBConnection\n";
 
-   my $dbh = $obj->{'_db_handle'};
+   $obj->disconnect();
+}
 
-   if( $dbh ) {
-     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;
-     }
+=head2 disconnect
 
-     $obj->{'_db_handle'} = undef;
-   }
-}
+  Example    : $db_connection->disconnect()
+  Description: Explicitely disconnect from database if connected
+  Returntype : none
+  Exceptions : none
+  Caller     : ?, DESTROY
+
+=cut
+
+sub disconnect {
+  my $self = shift;
+
+  my $dbh = $self->{'_db_handle'};
 
+  if( $dbh ) {
+    my $refcount = $self->ref_count();
+    $$refcount--;
+    #print STDERR "DBConnection : ref_count-- ";
+
+    # 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;
+      #print STDERR ": DISCONNECT ";
+    }
+    #print STDERR "\n";
+
+    $self->{'_db_handle'} = undef;
+    #unlink shared ref_count variable since no longer sharing db_handle
+    my $unlinked_refcount = 0;
+    $self->ref_count(\$unlinked_refcount);  
+  }
+
+}
 
 1;
diff --git a/modules/t/dbConnection.t b/modules/t/dbConnection.t
index 453684b6fc..b96cbac0de 100644
--- a/modules/t/dbConnection.t
+++ b/modules/t/dbConnection.t
@@ -5,7 +5,7 @@ use warnings;
 
 BEGIN { $| = 1;
 	use Test;
-	plan tests => 15;
+	plan tests => 19;
 }
 
 use MultiTestDB;
@@ -91,6 +91,7 @@ ok(test_getter_setter($dbc, 'db_handle', $db->db_handle));
 my $sth = $dbc->prepare('SELECT * from gene limit 1');
 $sth->execute;
 ok($sth->rows);
+$sth->finish;
 
 #
 # 12 add_db_adaptor
@@ -114,7 +115,31 @@ $dbc->remove_db_adaptor('core');
 ok(!defined $dbc->get_db_adaptor('core'));
 ok(!defined $dbc->get_all_db_adaptors->{'core'});
 
+#
+# 16-17 disconnect and auto-reconnect via a prepare
+#
+ok($dbc->disconnect);
+$sth = $dbc->prepare('SELECT * from gene limit 1');
+$sth->execute;
+ok($sth->rows);
+$sth->finish;
 
+#
+# 18-19 make new connection with shared dbhandle, 
+# test copied/shared connection
+# disconnect original, 
+# use copy with shared handle (that shouldn't have been disconnected)
+#
+my $dbc2 = Bio::EnsEMBL::DBSQL::DBConnection->new(-dbconn => $dbc);
+$sth = $dbc2->prepare('SELECT * from gene limit 1');
+$sth->execute;
+ok($sth->rows);
+$sth->finish;
+$dbc->disconnect;
+$sth = $dbc2->prepare('SELECT * from gene limit 1');
+$sth->execute;
+ok($sth->rows);
+$sth->finish;
 
 
 
-- 
GitLab