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