Thanks Tim for the help on this.

Tim Bunce wrote:
> On Mon, Oct 26, 2009 at 05:29:21PM +0000, Martin Evans wrote:
>> What follows is a very rough patch (definitely not finished) which
>> proves you can do what I wanted to do. However, there on no checks on
>> the column being bound existing and I'm not sure how to save the TYPE
>> attribute when bind_col is called before execute (that is when the
>> result-set is not described yet). Basically, I think more is required in
>> dbd_st_bind_col but I've not sure as yet what that is and it is possible
>> returning 1 is a total hack. I'd appreciate any advice to complete this.
>>
>> Index: oci8.c
>> ===================================================================
>> --- oci8.c   (revision 13427)
>> +++ oci8.c   (working copy)
>> @@ -3279,10 +3279,31 @@
>> +
>> +                    if ((fbh->req_type == 3) &&
>> +                        ((fbh->dbtype == 2) || (fbh->dbtype == 3))){
> 
> Best to avoid 'magic numbers'.

As I said = very rough. I'd already changed those to SQLT_NUM and
SQLT_INT as ORA types but I guessed they would need to be SQL_INTEGER,
SQL_NUMERIC, SQL_DOUBLE when finished i.e. you use the DBI types not the
oracle types here since the data is coming back into perl.

>> +                        char *e;
>> +                        char zval[32];
>> +                        long val;
>> +
>> +                        memcpy(zval, p, datalen);
>> +                        zval[datalen] = '\0';
>> +                        val = strtol(zval, &e, 10);
>> +
>> +                        if ((val == LONG_MAX) || (val == LONG_MIN) ||
>> +                            (e && (*e != '\0'))) {
>> +                            oci_error(sth, imp_sth->errhp, OCI_ERROR,
>> +                                      "invalid number or over/under flow");
>> +                            return Nullav;
>> +                        }
>> +                        sv_setiv(sv, val);
>> +                    } else {
>> +                        sv_setpvn(sv, p, (STRLEN)datalen);
>> +                        if (CSFORM_IMPLIES_UTF8(fbh->csform) ){
>> +                            SvUTF8_on(sv);
>> +                        }
>> +                    }

Tried your suggestion of the grok_number but it does not work well for
negative numbers since it returns the abs then and puts the result in a
UV which may not fit signed into an IV. Anyway, you seem to have had
other ideas so I'll not worry about that too much.

> A simpler safer and more portable approach may be to just let the
> existing code store the value in an sv and then add these lines:
> 
>     if (fbh->req_type == 3)
>         sv_2iv(sv);
> 
> If the number is too large for an IV (or UV) you'll get an NV (float).
> The original string of digits is preserved in all cases. That's all very
> natural and predictable perlish behaviour.

Ok, I get that except you keep saying "(or UV)". Are you suggesting
there is some other logic to decide whether you create an IV or a UV?

I tried out various values and sv_2iv(sv) and what was returned looked
ok - I get a string when the number has decimal places or is too big and
an IV when it is an integer and fits.

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

BTW, did you look over the possible hackery I did in dbd_st_bind_col - I
wasn't sure if simply storing the requested type and returning 1 was
acceptable. My current dbd_st_bind_col is:

int dbd_st_bind_col(SV *sth, imp_sth_t *imp_sth, SV *col, SV *ref, IV
type, SV *attribs) {
    dTHX;

    int field = SvIV(col);

    if (field <= DBIc_NUM_FIELDS(imp_sth)) {
        imp_sth->fbh[field-1].req_type = type;
    }

    return 1;
}

This means if someone attempts to bind a non-existent column it falls
back into DBI's bind_col and signals the error but it also means
dbd_st_bind_col in DBD::Oracle is only there to capture the requested
bind type.

Thanks

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

Reply via email to