Hi !

Following the DBI docs a coworker of mine tried to modify the return value
($_[2]) in a HandleError sub to implement automatic retry on error.


This works as documented with DBI::ExampleP and DBD::mysqlPP but not
with DBD::mysql (and presumably all Driver.xst based DBDs).

DBD::mysql uses DBIs Driver.xst Template to build itself and therefore
uses (among others) XST_mUNDEF to return an error condition.
This return value has the READONLY flag set and is aliased into the
HandleError-subs @_ by XS_DBI_dispatch.

Apparently the $_[2] access in the handler silently creates a local copy
to work on, while I would expect something like
"attempt to modify read-only value". (Why not ?)

My XS-knowledge is _very_ limited but I tried the following patch
(to the execute method only) and it fixes our testcase:

--- DBI-1.37/Driver.xst Tue May 13 16:00:10 2003
+++ DBI-1.37-test/Driver.xst    Sat Jun 14 12:49:44 2003
@@ -456,7 +456,7 @@
     if (retval == 0)           /* ok with no rows affected     */
        XST_mPV(0, "0E0");      /* (true but zero)              */
     else if (retval < -1)      /* -1 == unknown number of rows */
-       XST_mUNDEF(0);          /* <= -2 means error            */
+       ST(0) = sv_newmortal(); /* <= -2 means error            */
     else
        XST_mIV(0, retval);     /* typically 1, rowcount or -1  */


IMHO this is a slight problem with the HandleError interface, I would suggest that XS_DBI_dispatch makes sure that READONLY SVs on the return stack are replaced by a writable copy before passing them to the handler, otherwise this will happen again.

Can someone with more experience please comment on this ?

Thank you very much, regards,

Bjoern

Testcase follows:

Environment:
DBI 1.37, DBD::mysql 1.1027, perl 5.8.0 DEVEL 17013, Mac OS X 10.2.6,
also on 5.6.1 Linux x86

-- 8< --

use DBI;
use strict;
use warnings;
use Data::Dumper;
use Devel::Peek;

sub handler {
    warn Dumper( [EMAIL PROTECTED] );
     $_[1]->set_err(0,"");
     warn "before\n"; Dump( $_[2] );
     $_[2] = 42;
     warn "after\n"; Dump( $_[2] );
     return 1;
}

my $dbh = DBI->connect("DBI:mysql:host=localhost", undef, undef,
   { HandleError => \&handler } );
my $sth = $dbh->prepare("GNARZ");

#DBI->trace(9);
my $r = $sth->execute;
Dump($r);



Reply via email to