-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
begin_work
retval is sometimes incorrect if connection is RaiseError => 0
#104
Comments
Hello! I have looked at this issue. The problem is that My proposed solution is to extend This means that both DBI module and driver code needs to be changed for fixing this issue. My proposed diffs are below for DBI module and DBD-MariaDB driver. Same would have to be done in every other driver affected by this issue. Unfortunately I do not see easy solution without changing drivers because Please let me know what do you think. DBI code diff: diff --git a/DBI.pm b/DBI.pm
index 96124838ce3a..9dde79a5ca05 100644
--- a/DBI.pm
+++ b/DBI.pm
@@ -1747,7 +1747,7 @@ sub _new_sth { # called by DBD::<drivername>::db::prepare)
my $dbh = shift;
return $dbh->set_err($DBI::stderr, "Already in a transaction")
unless $dbh->FETCH('AutoCommit');
- $dbh->STORE('AutoCommit', 0); # will croak if driver doesn't support it
+ $dbh->STORE('AutoCommit', 0) or return; # will croak or return false if driver doesn't support it or setting failed
$dbh->STORE('BegunWork', 1); # trigger post commit/rollback action
return 1;
}
diff --git a/Driver.xst b/Driver.xst
index c3981f130689..77508db49178 100644
--- a/Driver.xst
+++ b/Driver.xst
@@ -355,19 +355,22 @@ disconnect(dbh)
RETVAL
-void
+bool
STORE(dbh, keysv, valuesv)
SV * dbh
SV * keysv
SV * valuesv
CODE:
D_imp_dbh(dbh);
+ int ret;
if (SvGMAGICAL(valuesv))
mg_get(valuesv);
- ST(0) = &PL_sv_yes;
- if (!dbd_db_STORE_attrib(dbh, imp_dbh, keysv, valuesv))
- if (!DBIc_DBISTATE(imp_dbh)->set_attr(dbh, keysv, valuesv))
- ST(0) = &PL_sv_no;
+ ret = dbd_db_STORE_attrib(dbh, imp_dbh, keysv, valuesv);
+ if (ret == 0)
+ ret = DBIc_DBISTATE(imp_dbh)->set_attr(dbh, keysv, valuesv);
+ RETVAL = (ret > 0);
+ OUTPUT:
+ RETVAL
void
@@ -779,19 +782,22 @@ blob_read(sth, field, offset, len, destrv=Nullsv, destoffset=0)
}
-void
+bool
STORE(sth, keysv, valuesv)
SV * sth
SV * keysv
SV * valuesv
CODE:
D_imp_sth(sth);
+ int ret;
if (SvGMAGICAL(valuesv))
mg_get(valuesv);
- ST(0) = &PL_sv_yes;
- if (!dbd_st_STORE_attrib(sth, imp_sth, keysv, valuesv))
- if (!DBIc_DBISTATE(imp_sth)->set_attr(sth, keysv, valuesv))
- ST(0) = &PL_sv_no;
+ ret = dbd_st_STORE_attrib(sth, imp_sth, keysv, valuesv);
+ if (ret == 0)
+ ret = DBIc_DBISTATE(imp_sth)->set_attr(sth, keysv, valuesv);
+ RETVAL = (ret > 0);
+ OUTPUT:
+ RETVAL
# FETCH renamed and ALIAS'd to avoid case clash on VMS :-( DBD-MariaDB diff: diff --git a/dbdimp.c b/dbdimp.c
index ca4a6c0ede04..319cb85fabd7 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -3230,6 +3230,8 @@ mariadb_db_STORE_attrib(
if (!imp_dbh->pmysql && !mariadb_db_reconnect(dbh, NULL))
{
mariadb_dr_do_error(dbh, CR_SERVER_GONE_ERROR, "MySQL server has gone away", "HY000");
+ if (memEQs(key, kl, "AutoCommit"))
+ return -1; /* -1 means we handled it as failed - important to avoid spurious errors */
return 0;
}
@@ -3248,7 +3250,7 @@ mariadb_db_STORE_attrib(
)
{
mariadb_dr_do_error(dbh, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql), mysql_sqlstate(imp_dbh->pmysql));
- return 1; /* 1 means we handled it - important to avoid spurious errors */
+ return -1; /* -1 means we handled it as failed - important to avoid spurious errors */
}
}
DBIc_set(imp_dbh, DBIcf_AutoCommit, bool_value); |
This seems like a reasonable solution given the constraints. Thank you. |
Hm... I have read available DBI documentation again and also code for handling AutoCommit in DBI and I now think that this is not the correct solution. Important parts are:
If DBI driver does not support "AutoCommit" at all then trying to set Therefore in my opinion the correct solution is just to fix DBD driver to properly die/croak when changing AutoCommit fails even when RaiseError is not enabled. And let DBI code unchanged. Any opinion on this? With this change it means that DBD-MariaDB diff: diff --git a/dbdimp.c b/dbdimp.c
index ca4a6c0ede04..777860f61c87 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -3214,6 +3214,8 @@ void mariadb_db_destroy(SV* dbh, imp_dbh_t* imp_dbh) {
* Returns: 1 for success, 0 otherwise
*
**************************************************************************/
+/* DBI expects that on AutoCommit failure driver croaks and does not return */
+#define croak_autocommit_failure() croak("Changing AutoCommit attribute failed: %" SVf, SVfARG(DBIc_ERRSTR(imp_dbh)))
int
mariadb_db_STORE_attrib(
SV* dbh,
@@ -3230,6 +3232,8 @@ mariadb_db_STORE_attrib(
if (!imp_dbh->pmysql && !mariadb_db_reconnect(dbh, NULL))
{
mariadb_dr_do_error(dbh, CR_SERVER_GONE_ERROR, "MySQL server has gone away", "HY000");
+ if (memEQs(key, kl, "AutoCommit"))
+ croak_autocommit_failure(); /* does not return */
return 0;
}
@@ -3248,7 +3252,7 @@ mariadb_db_STORE_attrib(
)
{
mariadb_dr_do_error(dbh, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql), mysql_sqlstate(imp_dbh->pmysql));
- return 1; /* 1 means we handled it - important to avoid spurious errors */
+ croak_autocommit_failure(); /* does not return */
}
}
DBIc_set(imp_dbh, DBIcf_AutoCommit, bool_value); |
Thank you @pali for your thoughtful investigation and analysis.
I agree. |
Thanks for the detailed consideration. I have a few thoughts in response. The documentation states explicitly that trying to set "AutoCommit" when the database doesn't support it is a fatal error. I don't think that implies that a set operation that fails should invoke a fatal error. I believe the the docs underscore this, saying (emphasis mine):
The proposed change would invalidate that last part. I could not find the documentation matching @pali's assertion:
The part of the DBI::DBD docs which document the dbd_db_STORE_attrib method say:
Other than saying one should invoke dbd_drv_error(), this isn't very proscriptive as far as calling croak() or not. If we should call croak() for these errors, shouldn't the documentation tell us to do so? In the end I guess I'm just looking for agreement between the code and documentation. Alternately, would the following simple change to DBI::begin_work do the right thing for both settings of RaiseError?
|
My understanding is that: if setting attribute fails then (fictional) dbd_drv_error() should be called. And if dbd_drv_error() returns (which means it did not throw exception) then in some cases DBI expects that driver croaks (throw exception), which is for example AutoCommit. And about this: $dbh->STORE('AutoCommit', 0); # will croak if driver doesn't support it
$dbh->err and return; # <--- return failure if we see an error has happened It would not work because error may be set before |
Thanks for the explanation. |
Check that begin_work throw error on failure also when RaiseError is turned off. See: perl5-dbi/dbi#104
Fix for DBD::MariaDB with test case is here: perl5-dbi/DBD-MariaDB#185 |
@noelcragg: Could you test the fix? |
Check that begin_work throw error on failure also when RaiseError is turned off. See: perl5-dbi/dbi#104
Check that begin_work throw error on failure also when RaiseError is turned off. See: perl5-dbi/dbi#104
Fix for DBD::MariaDB was merged. |
Upstream changes: 1.23 2023-09-10 - Add a missing break (perl5-dbi/DBD-MariaDB#163) - Signal error if mariadb_db_async_result() fails (perl5-dbi/DBD-MariaDB#162) - Update links to project website, issues and years - Fix compilation with some MariaDB client library 10.2 and 10.3 versions - Fix mariadb_use_result attribute (perl5-dbi/DBD-MariaDB#173) - Fix statements with multiple result sets in asynchronous mode - Fix mariadb_sockfd attribute for Windows - Croaks when changing AutoCommit attribute fails (perl5-dbi/dbi#104) - Various documentation and tests fixes - Fix support for MariaDB Connector/C prior to 3.1.3 version - Fix usage of Win32::GetShortPathName() in Makefile.PL - Build release tarball in TAR format (instead of PAX) - Allow to query and change mariadb_multi_statements attribute - Add connect option mariadb_auth_plugin for specifying auth plugin - Fix support for MySQL 8.0+ client library (perl5-dbi/DBD-MariaDB#191) (perl5-dbi/DBD-mysql#329) - Add Github Actions CI and Cirrus CI (FreeBSD) for automated testing 1.22 2022-04-22 - Disable usage of libmysqld.a from MySQL 8.x series - Install README.pod into DBD/MariaDB/ subdirectory (perl5-dbi/DBD-MariaDB#146) - Do not export driver private C functions - Fix typo in error message - Fix compatibility with new MariaDB client and server versions (perl5-dbi/DBD-MariaDB#164) (perl5-dbi/DBD-MariaDB#167) (perl5-dbi/DBD-mysql#333)
@pali is work in DBI still required? |
Consider the following order of events:
DBI::connect
is invoked withRaiseError => 0
and returns$handle
$handle->begin_work
currently always returns trueThis is a weird but not uncommon case. For example, in a high-volume environment, one may have database connections being handled by a proxy or load-balancer. Software performing the latter functions sometimes crashes; hardware performing the latter functions sometimes locks up or reboots.
If
begin_work
notices an error performing its duties, it should return false. For some backends there's no error to notice, becausebegin_work
is just setting the values ofAutoCommit
andBeginWork
in memory. For others, modifying the value ofAutoCommit
may result in communication with the back-end database, during which errors happen.Here's a minimal script to demonstrate the problem on u*x talking to a local MySQL db:
To get this to run on your own machine, you might have to
strace
the process and figure out what fd is getting opened if it's not 3 (as it has been on the various machines i've run this). Closing the fd simulates the connection failure I was describing above.When run, you'll see:
Note that we see "begin_work ok" in the output. This could only be displayed if
$dbh->begin_work
returned a true value. Because it does not, we continue and end up invoking thedo
method which does correctly detect that the connection has gone away and returns a false value.I've worked around this issue in my own code by checking both the retval from
begin_work
and theerr
method of the handle. It would be nice not to have to do this.The text was updated successfully, but these errors were encountered: