Subject: | DBD::mysql auto-reconnect does not set Active flag |
This bug has been cross-posted to the MySQL bug database as ticket #44883.
In cases in which auto-reconnect is enabled for a connection (this is especially true
under mod_perl where auto-reconnect is automatically turned on by default), performing a
disconnect on the DB handle and then subsequently performing a database operation on that
handle (resulting in the automatic reconnection of the handle), the Active flag is not
reset to 1, but is "", as set by the disconnect operation. Code that inspects this flag to
determine if the connection is indeed active is misled. Subsequent disconnect attempts on
this reconnected handle will also fail, as disconnect() inspects the Active flag.
To replicate, perform an operation like the following, either under mod_perl or with an
explicit setting of auto-reconnect:
$dbh = DBI->connect(...); # gets mysql thread ID 123 and sets $dbh->{Active} to 1
$dbh->disconnect; # Actually disconnects from the DB and sets $dbh->{Active} to ""
$dbh->do("SELECT 1"); # *Does not complain!*. Automatically reconnects to DB with mysql
thread ID 124 (or next available) BUT leaves $dbh->{Active} as "".
$dbh->disconnect; # Does nothing; Does not actually disconnect mysql thread ID 124
because Active is ""
Attached file includes code patch that simply duplicates the one line of code from
dbd_db_login() that sets the Active flag to mysql_db_reconnect(), as well as a test to
ensure that this condition is corrected.
While it could be argued that an explicit disconnect() should not have a subsequent
implicit reconnect, there are some valid instances in which this may occur (forking of
children that are passed an active database handle). So long as DBD::mysql allows such a
thing to be done, it should handle it the right way.
Subject: | DBD-mysql-4.011-reconnect_active_flag.patch |
diff -PurN DBD-mysql-4.011.orig/dbdimp.c DBD-mysql-4.011/dbdimp.c
--- DBD-mysql-4.011.orig/dbdimp.c 2009-04-13 08:39:38.000000000 -0400
+++ DBD-mysql-4.011/dbdimp.c 2009-05-07 13:19:37.000000000 -0400
@@ -4420,6 +4420,12 @@
++imp_dbh->stats.auto_reconnects_failed;
return FALSE;
}
+
+ /*
+ * Tell DBI, that dbh->disconnect should be called for this handle
+ */
+ DBIc_ACTIVE_on(imp_dbh);
+
++imp_dbh->stats.auto_reconnects_ok;
return TRUE;
}
diff -PurN DBD-mysql-4.011.orig/MANIFEST DBD-mysql-4.011/MANIFEST
--- DBD-mysql-4.011.orig/MANIFEST 2009-04-13 12:00:46.000000000 -0400
+++ DBD-mysql-4.011/MANIFEST 2009-05-07 13:19:37.000000000 -0400
@@ -17,6 +17,7 @@
eg/proc_example3.pl
t/00base.t
t/10connect.t
+t/15reconnect.t
t/20createdrop.t
t/25lockunlock.t
t/29warnings.t
diff -PurN DBD-mysql-4.011.orig/t/15reconnect.t DBD-mysql-4.011/t/15reconnect.t
--- DBD-mysql-4.011.orig/t/15reconnect.t 1969-12-31 19:00:00.000000000 -0500
+++ DBD-mysql-4.011/t/15reconnect.t 2009-05-07 13:20:53.000000000 -0400
@@ -0,0 +1,39 @@
+#!perl -w
+# vim: ft=perl
+
+use Test::More;
+use DBI;
+use DBI::Const::GetInfoType;
+use strict;
+$|= 1;
+
+use vars qw($test_dsn $test_user $test_password);
+use lib 't', '.';
+require 'lib.pl';
+
+my $dbh;
+eval {$dbh= DBI->connect($test_dsn, $test_user, $test_password,
+ { RaiseError => 1, PrintError => 1, AutoCommit => 0 });};
+
+if ($@) {
+ plan skip_all => "ERROR: $DBI::errstr. Can't continue test";
+}
+plan tests => 8;
+
+ok(defined $dbh, "Connected to database");
+
+ok($dbh->{Active}, "checking for active handle");
+
+ok($dbh->{mysql_auto_reconnect} = 1, "enabling reconnect");
+
+ok($dbh->{AutoCommit} = 1, "enabling autocommit");
+
+ok($dbh->disconnect(), "disconnecting active handle");
+
+ok(!$dbh->{Active}, "checking for inactive handle");
+
+ok($dbh->do("SELECT 1"), "implicitly reconnecting handle with 'do'");
+
+ok($dbh->{Active}, "checking for reactivated handle");
+
+$dbh->disconnect();