On Sat, Jun 14, 2003 at 05:47:53PM +0200, Bjoern Kriews wrote:
> 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.

Oooh, scary.

> 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 ?

I agree entirely.

> Testcase follows:

Thanks. I've no time to test right now, can you try the attached
patch and let me know how it goes?

And I'd be interested in more details about the automatic-retry
mechanism you're using it for.

Tim.

p.s. The ig.co.uk address you used is a few years out of date.
--- DBI.xs      2003/06/16 08:59:22     11.30
+++ DBI.xs      2003/06/16 09:17:02
@@ -2632,34 +2632,43 @@
                &&  hook_svp && SvOK(*hook_svp)
        ) {
            dSP;
-           SV *result = *(sp-outitems+1);
            PerlIO *logfp = DBILOGFP;
            IV items;
            SV *status;
+           SV *result; /* point to result SV that's pointed to by the stack */
+           if (outitems) {
+               result = *(sp-outitems+1);
+               if (SvREADONLY(result)) {
+                   *(sp-outitems+1) = result = sv_2mortal(newSVsv(result));
+               }
+           }
+           else {
+               result = sv_newmortal();
+           }
            if (debug)
                PerlIO_printf(logfp,"    -> HandleError on %s via %s%s%s%s\n",
                    neatsvpv(h,0), neatsvpv(*hook_svp,0),
-                   (result ? " (" : ""),
-                   (result ? neatsvpv(result,0) : ""),
-                   (result ? ")" : "")
+                   (!outitems ? "" : " ("),
+                   (!outitems ? "" : neatsvpv(result ,0)),
+                   (!outitems ? "" : ")")
                );
            PUSHMARK(SP);
            XPUSHs(msg);
            XPUSHs(sv_2mortal(newRV((SV*)DBIc_MY_H(imp_xxh))));
-           XPUSHs( result ? result : sv_newmortal() );
+           XPUSHs( result );
            PUTBACK;
            items = perl_call_sv(*hook_svp, G_SCALAR);
            SPAGAIN;
-           status = POPs;
+           status = (items) ? POPs : &sv_undef;
            PUTBACK;
            if (!SvTRUE(status)) /* handler says it didn't handle it, so... */
                hook_svp = 0;  /* pretend we didn't have a handler...     */
            if (debug)
                PerlIO_printf(logfp,"    <- HandleError= %s%s%s%s\n",
                    neatsvpv(status,0),
-                   (result ? " (" : ""),
-                   (result ? neatsvpv(result,0) : ""),
-                   (result ? ")" : "")
+                   (!outitems ? "" : " ("),
+                   (!outitems ? "" : neatsvpv(result,0)),
+                   (!outitems ? "" : ")")
                );
        }
 

Reply via email to