Tim Bunce wrote:
> On Tue, Oct 27, 2009 at 02:54:43PM +0000, Martin Evans wrote:
>>> The next question is whether overflowing to an NV should be an error.
>>> I'm thinking we could adopt these semantics for bind_col types:
>>>
>>>   SQL_INTEGER  IV or UV via sv_2iv(sv) with error on overflow
>> this would be ideal.
>>
>>>   SQL_DOUBLE   NV via sv_2nv(sv)
>>>   SQL_NUMERIC  IV else UV else NV via grok_number() with no error
>>>
>>> I could sketch out the logic for those cases if you'd be happy to polish
>>> up and test.
>> I would be happy to do that.
> 
> I finally got around to working on this. Here's a first rough draft
> (which a bunch of issues) I thought I'd post here for discussion.
> 
> I've implemented it as a hook in the DBIS structure so drivers can call
> it directly.
> 
> I've added the idea of optionally discarding the string buffer (which
> would save space when storing many rows but waste time if just working
> row-at-a-time). For now I've triggered that based on the sql_type but
> that feels like a hack we'd regret later. A better approach might be an
> attribute to bind_col:
> 
>     $sth->bind_col(..., { MinMemory => 1 });
>     $sth->fetchall_...();
> 
> This code doesn't raise any errors or produce any warnings (directly),
> it just returns a status that the driver should check if it wants to
> implement the SQL_INTEGER == error on overflow semantics, which it
> should if we agree that's what we're going to adopt.
> 
> Tim.
> 
> 
> Index: DBI.xs
> ===================================================================
> --- DBI.xs    (revision 13466)
> +++ DBI.xs    (working copy)
> @@ -78,6 +78,7 @@
>  static int      set_err_char _((SV *h, imp_xxh_t *imp_xxh, const char 
> *err_c, IV err_i, const char *errstr, const char *state, const char *method));
>  static int      set_err_sv   _((SV *h, imp_xxh_t *imp_xxh, SV *err, SV 
> *errstr, SV *state, SV *method));
>  static int      quote_type _((int sql_type, int p, int s, int *base_type, 
> void *v));
> +static int      post_fetch_sv _((pTHX_ SV *h, imp_xxh_t *imp_xxh, SV *sv, 
> int sql_type, U32 flags, void *v));
>  static I32      dbi_hash _((const char *string, long i));
>  static void     dbih_dumphandle _((pTHX_ SV *h, const char *msg, int level));
>  static int      dbih_dumpcom _((pTHX_ imp_xxh_t *imp_xxh, const char *msg, 
> int level));
> @@ -439,6 +440,7 @@
>      DBIS->set_err_sv  = set_err_sv;
>      DBIS->set_err_char= set_err_char;
>      DBIS->bind_col    = dbih_sth_bind_col;
> +    DBIS->post_fetch_sv = post_fetch_sv;
>  
>  
>      /* Remember the last handle used. BEWARE! Sneaky stuff here!        */
> @@ -1714,6 +1718,94 @@
>  }
>  
>  
> +/* Convert a simple string representation of a value into a more specific
> + * perl type based on an sql_type value.
> + * The semantics of SQL standard TYPE values are interpreted _very_ loosely
> + * on the basis of "be liberal in what you accept and let's throw in some
> + * extra semantics while we're here" :)
> + * Returns:
> + *  -1: sv is undef or doesn't
> + *   0: sv couldn't be converted to requested (strict) type
> + *   1: sv was handled without a problem
> + */
> +int
> +post_fetch_sv(pTHX_ SV *h, imp_xxh_t *imp_xxh, SV *sv, int sql_type, U32 
> flags, void *v)
> +{
> +    int discard_pv = 0;
> +
> +    /* do nothing for undef (NULL) or non-string values */
> +    if (!sv || !SvPOK(sv))
> +        return -1;
> +
> +    switch(sql_type) {
> +
> +    /* caller would like IV (but may get UV or NV) */
> +    /* will warn if not numeric. return 0 on overflow */
> +    case SQL_SMALLINT:
> +        discard_pv = 1;
> +    case SQL_INTEGER:
> +        sv_2iv(sv); /* is liberal, may return SvIV, SvUV, or SvNV */
> +        if (SvNOK(sv)) { /* suspicious */
> +            NV nv = SvNV(sv);
> +            /* ignore NV set just to preserve digits after the decimal place 
> */
> +            /* just complain if the value won't fit in an IV or NV  */
> +            if (nv > UV_MAX || nv < IV_MIN) 
> +                return 0;
> +        }
> +        break;
> +
> +    /* caller would like SvNOK/SvIOK true if the value is a number */
> +    /* will warn if not numeric */
> +    case SQL_FLOAT:
> +        discard_pv = 1;
> +    case SQL_DOUBLE:
> +        sv_2nv(sv);
> +        break;
> +
> +    /* caller would like IV else UV else NV */
> +    /* else no error and sv is untouched */
> +    case SQL_NUMERIC:
> +        discard_pv = 1;
> +    case SQL_DECIMAL: {
> +        UV uv;
> +        /* based on the code in perl's toke.c */
> +        int flags = grok_number(SvPVX(sv), SvCUR(sv), &uv);
> +
> +        if (flags == IS_NUMBER_IN_UV) { /* +ve int */
> +            if (uv <= IV_MAX)   /* prefer IV over UV */
> +                 sv_2iv(sv);
> +            else sv_2uv(sv);
> +        }
> +        else if (flags == (IS_NUMBER_IN_UV | IS_NUMBER_NEG)
> +            && uv <= IV_MAX
> +        ) {
> +            sv_2iv(sv);
> +        }
> +        else if (flags) /* is numeric */
> +            sv_2nv(sv);
> +        }
> +        break;
> +
> +#if 0 /* XXX future possibilities */
> +    case SQL_BIGINT:    /* use Math::BigInt if too large for IV/UV */
> +#endif
> +    default:
> +        return 0;   /* value unchanged */
> +    }
> +
> +    if (discard_pv            /* caller wants string buffer discarded */
> +    && SvNIOK(sv)             /* we set a numeric value */
> +    && SvPVX(sv) && SvLEN(sv) /* we have a buffer to discard */
> +    ) {
> +        Safefree(SvPVX(sv));
> +        SvPVX(sv) = NULL;
> +        SvPOK_off(sv);
> +    }
> +    return 1;
> +}
> +
> +
> +
> 
> 

I'm presuming you missed a bit off your patch:

Index: DBIXS.h
===================================================================
--- DBIXS.h     (revision 13478)
+++ DBIXS.h     (working copy)
@@ -431,10 +431,10 @@
     int         (*bind_col)     _((SV *sth, SV *col, SV *ref, SV
*attribs));

     IO *logfp_ref;      /* DAA keep ptr to filehandle for refcounting */
-
+    int      (*post_fetch_sv)   _((pTHX_ SV *h, imp_xxh_t *imp_xxh, SV
*sv, int sql_type, U32 flags, void *v));
     /* WARNING: Only add new structure members here, and reduce pad2 to
keep */
     /* the memory footprint exactly the same */
-    void *pad2[4];
+    void *pad2[3];
 };


Martin
-- 
Martin J. Evans
Easysoft Limited
http://www.easysoft.com

Reply via email to