[ruby-oci8-commit] [376] trunk/ruby-oci8: * ext/oci8/error.c, ext/oci8/oci8.h: add oci8_raise_by_msgno()

nobody at rubyforge.org nobody at rubyforge.org
Sun Feb 7 05:02:22 EST 2010


Revision: 376
Author:   kubo
Date:     2010-02-07 05:02:22 -0500 (Sun, 07 Feb 2010)

Log Message:
-----------
* ext/oci8/error.c, ext/oci8/oci8.h: add oci8_raise_by_msgno()
    to retrieve a Oracle error message which depends on NLS_LANGUAGE.
* ext/oci8/oranumber_util.c, ext/oci8/oranumber_util.h,
  ext/oci8/extconf.rb: add handwritten conversion functions from
    OCINumber internal representation to string and vice versa.
* ext/oci8/ocinumber.c: 1. use handwritten conversion functions
    instead of OCI functions to convert OraNumber to string
    and vice varse. 2. add OraNumber#dump.
* test/test_oranumber.rb: add test cases to check conversion from
    OraNumber to string and vice varse.

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/error.c
    trunk/ruby-oci8/ext/oci8/extconf.rb
    trunk/ruby-oci8/ext/oci8/oci8.h
    trunk/ruby-oci8/ext/oci8/ocinumber.c
    trunk/ruby-oci8/test/test_oranumber.rb

Added Paths:
-----------
    trunk/ruby-oci8/ext/oci8/oranumber_util.c
    trunk/ruby-oci8/ext/oci8/oranumber_util.h

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2010-02-02 13:14:30 UTC (rev 375)
+++ trunk/ruby-oci8/ChangeLog	2010-02-07 10:02:22 UTC (rev 376)
@@ -1,3 +1,15 @@
+2010-02-07  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/error.c, ext/oci8/oci8.h: add oci8_raise_by_msgno()
+	    to retrieve a Oracle error message which depends on NLS_LANGUAGE.
+	* ext/oci8/oranumber_util.c, ext/oci8/oranumber_util.h,
+	  ext/oci8/extconf.rb: add handwritten conversion functions from
+	    OCINumber internal representation to string and vice versa.
+	* ext/oci8/ocinumber.c: 1. use handwritten conversion functions
+	    instead of OCI functions to convert OraNumber to string
+	    and vice varse. 2. add OraNumber#dump.
+	* test/test_oranumber.rb: add test cases to check conversion from
+	    OraNumber to string and vice varse.
+
 2010-02-02  KUBO Takehiro  <kubo at jiubao.org>
 	* ext/oci8/ocinumber.c: fix to support NNUMBERS with scale larger
 	    than 38 by using scientific number notation to convert OraNumber

Modified: trunk/ruby-oci8/ext/oci8/error.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/error.c	2010-02-02 13:14:30 UTC (rev 375)
+++ trunk/ruby-oci8/ext/oci8/error.c	2010-02-07 10:02:22 UTC (rev 376)
@@ -32,6 +32,8 @@
 static char *errbuf;
 static ub4 errbufsiz;
 
+static OCIMsg *msghp;
+
 NORETURN(static void oci8_raise2(dvoid *errhp, sword status, ub4 type, OCIStmt *stmthp, const char *file, int line));
 NORETURN(static void set_backtrace_and_raise(VALUE exc, const char *file, int line));
 
@@ -347,3 +349,30 @@
     rb_ivar_set(exc, oci8_id_message, rb_ary_new3(1, msg));
     set_backtrace_and_raise(exc, file, line);
 }
+
+static VALUE get_error_msg_by_msgno(ub4 msgno)
+{
+    char buf[256];
+    int sz;
+
+    if (msghp == NULL) {
+        OCIMessageOpen(oci8_envhp, oci8_errhp, &msghp, TO_ORATEXT("rdbms"), TO_ORATEXT("ora"), OCI_DURATION_PROCESS);
+    }
+
+    sz = snprintf(buf, sizeof(buf), "ORA-%05u: ", msgno);
+    if (msghp == NULL || OCIMessageGet(msghp, msgno, TO_ORATEXT(buf + sz), sizeof(buf) - sz) == NULL) {
+        strcpy(buf + sz, "could not get the error message");
+    }
+    return rb_external_str_new_with_enc(buf, strlen(buf), oci8_encoding);
+
+}
+
+void oci8_do_raise_by_msgno(ub4 msgno, const char *file, int line)
+{
+    VALUE msg = get_error_msg_by_msgno(msgno);
+    VALUE exc = rb_funcall(eOCIError, oci8_id_new, 1, msg);
+
+    rb_ivar_set(exc, oci8_id_code, rb_ary_new3(1, INT2FIX(-1)));
+    rb_ivar_set(exc, oci8_id_message, rb_ary_new3(1, msg));
+    set_backtrace_and_raise(exc, file, line);
+}

Modified: trunk/ruby-oci8/ext/oci8/extconf.rb
===================================================================
--- trunk/ruby-oci8/ext/oci8/extconf.rb	2010-02-02 13:14:30 UTC (rev 375)
+++ trunk/ruby-oci8/ext/oci8/extconf.rb	2010-02-07 10:02:22 UTC (rev 376)
@@ -86,7 +86,7 @@
          "stmt.o", "bind.o", "metadata.o", "attr.o",
          "lob.o", "oradate.o",
          "ocinumber.o", "ocidatetime.o", "object.o", "apiwrap.o",
-         "encoding.o", "xmldb.o"]
+         "encoding.o", "xmldb.o", "oranumber_util.o"]
 
 if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
   $defs << "-DUSE_WIN32_C"

Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h	2010-02-02 13:14:30 UTC (rev 375)
+++ trunk/ruby-oci8/ext/oci8/oci8.h	2010-02-07 10:02:22 UTC (rev 376)
@@ -336,6 +336,7 @@
 #define oci8_raise(err, status, stmt) oci8_do_raise(err, status, stmt, __FILE__, __LINE__)
 #define oci8_env_raise(err, status) oci8_do_env_raise(err, status, __FILE__, __LINE__)
 #define oci8_raise_init_error() oci8_do_raise_init_error(__FILE__, __LINE__)
+#define oci8_raise_by_msgno(msgno) oci8_do_raise_by_msgno(msgno, __FILE__, __LINE__)
 
 /* raise on error */
 #define oci_lc(rv) do { \
@@ -412,6 +413,7 @@
 NORETURN(void oci8_do_env_raise(OCIEnv *, sword status, const char *file, int line));
 NORETURN(void oci8_do_raise_init_error(const char *file, int line));
 sb4 oci8_get_error_code(OCIError *errhp);
+NORETURN(void oci8_do_raise_by_msgno(ub4 msgno, const char *file, int line));
 
 /* ocihandle.c */
 void Init_oci8_handle(void);

Modified: trunk/ruby-oci8/ext/oci8/ocinumber.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/ocinumber.c	2010-02-02 13:14:30 UTC (rev 375)
+++ trunk/ruby-oci8/ext/oci8/ocinumber.c	2010-02-07 10:02:22 UTC (rev 376)
@@ -8,6 +8,7 @@
 #include "oci8.h"
 #include <orl.h>
 #include <errno.h>
+#include "oranumber_util.h"
 
 #ifndef RUBY_VM
 /* ruby 1.8 */
@@ -36,26 +37,6 @@
 static OCINumber const_PI2;  /* +PI/2 */
 static OCINumber const_mPI2; /* -PI/2 */
 
-#define SHRESHOLD_FMT_STR "00000000000000000000000000"
-#define SHRESHOLD_FMT (OraText*)SHRESHOLD_FMT_STR
-#define SHRESHOLD_FMT_LEN (sizeof(SHRESHOLD_FMT_STR) - 1)
-#define SHRESHOLD_VAL_STR "10000000000000000000000000"
-#define SHRESHOLD_VAL (OraText*)SHRESHOLD_VAL_STR
-#define SHRESHOLD_VAL_LEN (sizeof(SHRESHOLD_VAL_STR) - 1)
-static OCINumber const_shreshold;
-
-/* TODO: scale: -84 - 127 */
-#define NUMBER_FORMAT1_STR "FM9999999999999999999999990.9999999999999999999999999999999999999"
-#define NUMBER_FORMAT1 (OraText*)NUMBER_FORMAT1_STR
-#define NUMBER_FORMAT1_LEN (sizeof(NUMBER_FORMAT1_STR) - 1)
-#define NUMBER_FORMAT2_STR "FM99999999999999999999999999999999999990.999999999999999999999999"
-#define NUMBER_FORMAT2 (OraText*)NUMBER_FORMAT2_STR
-#define NUMBER_FORMAT2_LEN (sizeof(NUMBER_FORMAT2_STR) - 1)
-#define NUMBER_FORMAT2_DECIMAL                              (sizeof("999999999999999999999999") - 1)
-#define NUMBER_FORMAT_INT_STR "FM99999999999999999999999999999999999990"
-#define NUMBER_FORMAT_INT (OraText*)NUMBER_FORMAT_INT_STR
-#define NUMBER_FORMAT_INT_LEN (sizeof(NUMBER_FORMAT_INT_STR) - 1)
-
 #define _NUMBER(val) ((OCINumber *)DATA_PTR(val)) /* dangerous macro */
 
 #define RBOCI8_T_ORANUMBER (T_MASK + 1)
@@ -136,24 +117,18 @@
 {
     signed long sl;
     char buf[512];
-    ub4 buf_size = sizeof(buf);
     sword rv;
 
     if (OCINumberToInt(errhp, s, sizeof(sl), OCI_NUMBER_SIGNED, &sl) == OCI_SUCCESS) {
         return LONG2NUM(sl);
     }
     /* convert to Integer via String */
-    rv = OCINumberToText(errhp, s, NUMBER_FORMAT2, NUMBER_FORMAT2_LEN,
-                         NULL, 0, &buf_size, TO_ORATEXT(buf));
-    if (rv == OCI_SUCCESS) {
+    rv = oranumber_to_str(s, buf);
+    if (rv > 0) {
         return rb_cstr2inum(buf, 10);
     }
-    if (rv == OCI_ERROR && oci8_get_error_code(errhp) == 22065) {
-        /* convert to Integer via BigDecimal as a last resort */
-        VALUE d = onum_to_d_real(s, errhp);
-        return rb_funcall(d, rb_intern("to_i"), 0);
-    }
-    oci8_raise(errhp, rv, NULL);
+    oranumber_dump(s, buf);
+    rb_raise(eOCIException, "Could not convert OraNumber(%s) to string", buf);
 }
 
 VALUE oci8_make_float(OCINumber *s, OCIError *errhp)
@@ -175,29 +150,16 @@
     StringValue(str);
     /* set from string. */
     if (NIL_P(fmt)) {
-        int i, cnt = 0;
-        for (i = RSTRING_LEN(str) - 1; i >= 0; i--) {
-            if (RSTRING_PTR(str)[i] != ' ')
-                cnt++;
-            if (RSTRING_PTR(str)[i] == '.') {
-                i = RSTRING_LEN(str) - i;
-                break;
-            }
-        }
-        if (i == -1)
-            cnt = 0;
-        if (cnt <= NUMBER_FORMAT2_DECIMAL) {
-            fmt_ptr = NUMBER_FORMAT2;
-            fmt_len = NUMBER_FORMAT2_LEN;
+        int rv = oranumber_from_str(result, RSTRING_PTR(str), RSTRING_LEN(str));
+        if (rv == ORANUMBER_SUCCESS) {
+            return; /* success */
         } else {
-            fmt_ptr = NUMBER_FORMAT1;
-            fmt_len = NUMBER_FORMAT1_LEN;
+            oci8_raise_by_msgno(rv);
         }
-    } else {
-        StringValue(fmt);
-        fmt_ptr = RSTRING_ORATEXT(fmt);
-        fmt_len = RSTRING_LEN(fmt);
     }
+    StringValue(fmt);
+    fmt_ptr = RSTRING_ORATEXT(fmt);
+    fmt_len = RSTRING_LEN(fmt);
     if (NIL_P(nls_params)) {
         nls_params_ptr = NULL;
         nls_params_len = 0;
@@ -1064,30 +1026,14 @@
 
     rb_scan_args(argc, argv, "02", &fmt /* nil */, &nls_params /* nil */);
     if (NIL_P(fmt)) {
-        OCINumber absval;
-        sword sign;
-        boolean is_int;
-
-        oci_lc(OCINumberIsInt(errhp, _NUMBER(self), &is_int));
-        if (is_int) {
-            fmt_ptr = NUMBER_FORMAT_INT;
-            fmt_len = NUMBER_FORMAT_INT_LEN;
-        } else {
-            oci_lc(OCINumberAbs(errhp, _NUMBER(self), &absval));
-            oci_lc(OCINumberCmp(errhp, &absval, &const_shreshold, &sign));
-            if (sign >= 0) {
-                fmt_ptr = NUMBER_FORMAT2;
-                fmt_len = NUMBER_FORMAT2_LEN;
-            } else {
-                fmt_ptr = NUMBER_FORMAT1;
-                fmt_len = NUMBER_FORMAT1_LEN;
-            }
+        rv = oranumber_to_str(_NUMBER(self), buf);
+        if (rv > 0) {
+            return rb_usascii_str_new(buf, rv);
         }
-    } else {
-        StringValue(fmt);
-        fmt_ptr = RSTRING_ORATEXT(fmt);
-        fmt_len = RSTRING_LEN(fmt);
     }
+    StringValue(fmt);
+    fmt_ptr = RSTRING_ORATEXT(fmt);
+    fmt_len = RSTRING_LEN(fmt);
     if (NIL_P(nls_params)) {
         nls_params_ptr = NULL;
         nls_params_len = 0;
@@ -1285,6 +1231,24 @@
     return oci8_make_ocinumber(&result, errhp);
 }
 
+/*
+ *  call-seq:
+ *     onum.dump    -> string
+ *
+ *  Returns internal representation whose format is same with
+ *  the return value of Oracle SQL function DUMP().
+ *
+ *   OraNumber.new(100).dump  #=> "Typ=2 Len=2: 194,2"
+ *   OraNumber.new(123).dump  #=> "Typ=2 Len=3: 194,2,24"
+ *   OraNumber.new(0.1).dump  #=> "Typ=2 Len=2: 192,11"
+ */
+static VALUE onum_dump(VALUE self)
+{
+    char buf[ORANUMBER_DUMP_BUF_SIZ];
+    int rv = oranumber_dump(_NUMBER(self), buf);
+    return rb_usascii_str_new(buf, rv);
+}
+
 static VALUE onum_hash(VALUE self)
 {
     char *c  = DATA_PTR(self);
@@ -1320,7 +1284,7 @@
  *
  *  Dump <i>onum</i> for marshaling.
  */
-static VALUE onum_dump(int argc, VALUE *argv, VALUE self)
+static VALUE onum__dump(int argc, VALUE *argv, VALUE self)
 {
     char *c  = DATA_PTR(self);
     int size = c[0] + 1;
@@ -1466,9 +1430,6 @@
     OCINumberDiv(errhp, &num1 /* PI */, &num2 /* 2 */, &const_PI2);
     /* set const_mPI2 */
     OCINumberNeg(errhp, &const_PI2 /* PI/2 */, &const_mPI2);
-    /* set const_shreshold */
-    OCINumberFromText(errhp, SHRESHOLD_VAL, SHRESHOLD_VAL_LEN, SHRESHOLD_FMT, SHRESHOLD_FMT_LEN,
-                      NULL, 0, &const_shreshold);
 
     /* PI */
     OCINumberSetPi(errhp, &num1);
@@ -1539,12 +1500,13 @@
     if (have_OCINumberShift) {
         rb_define_method(cOCINumber, "shift", onum_shift, 1);
     }
+    rb_define_method(cOCINumber, "dump", onum_dump, 0);
 
     rb_define_method_nodoc(cOCINumber, "hash", onum_hash, 0);
     rb_define_method_nodoc(cOCINumber, "inspect", onum_inspect, 0);
 
     /* methods for marshaling */
-    rb_define_method(cOCINumber, "_dump", onum_dump, -1);
+    rb_define_method(cOCINumber, "_dump", onum__dump, -1);
     rb_define_singleton_method(cOCINumber, "_load", onum_s_load, 1);
 
     oci8_define_bind_class("OraNumber", &bind_ocinumber_class);

Added: trunk/ruby-oci8/ext/oci8/oranumber_util.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oranumber_util.c	                        (rev 0)
+++ trunk/ruby-oci8/ext/oci8/oranumber_util.c	2010-02-07 10:02:22 UTC (rev 376)
@@ -0,0 +1,301 @@
+/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
+#include <stdio.h>
+#include <string.h>
+#include "oranumber_util.h"
+
+int oranumber_to_str(const OCINumber *on, char *buf)
+{
+    signed char exponent;
+    signed char mantissa[21]; /* terminated by a negative number */
+    int datalen = on->OCINumberPart[0];
+    int len = 0;
+    int idx;
+    int n;
+
+    if (datalen == 0) {
+        /* too short */
+        return -1;
+    }
+    if (datalen == 1) {
+        if (on->OCINumberPart[1] == 0x80) {
+            /* zero */
+            buf[0] = '0';
+            buf[1] = '\0';
+            return 1;
+        }
+        /* unexpected format */
+        return -1;
+    }
+    if (datalen > 21) {
+        /* too long */
+        return -1;
+    }
+    /* normalize exponent and mantissa */
+    if (on->OCINumberPart[1] >= 128) {
+        /* positive number */
+        exponent = on->OCINumberPart[1] - 193;
+        for (idx = 0; idx < on->OCINumberPart[0] - 1; idx++) {
+            mantissa[idx] = on->OCINumberPart[idx + 2] - 1;
+        }
+        mantissa[idx] = -1;
+    } else {
+        /* negative number */
+        exponent = 62 - on->OCINumberPart[1];
+        for (idx = 0; idx < on->OCINumberPart[0] - 1; idx++) {
+            mantissa[idx] = 101 - on->OCINumberPart[idx + 2];
+        }
+        mantissa[idx] = -1;
+        buf[len++] = '-';
+    }
+    /* convert exponent and mantissa to human readable number */
+    idx = 0;
+    if (exponent-- >= 0) {
+        /* integer part */
+        n = mantissa[idx++];
+        if (n / 10 != 0) {
+            buf[len++] = n / 10 + '0';
+        }
+        buf[len++] = n % 10 + '0';
+        while (exponent-- >= 0) {
+            n = mantissa[idx++];
+            if (n < 0) {
+                do {
+                    buf[len++] = '0';
+                    buf[len++] = '0';
+                } while (exponent-- >= 0);
+                buf[len] = '\0';
+                return len;
+            }
+            buf[len++] = n / 10 + '0';
+            buf[len++] = n % 10 + '0';
+        }
+        if (mantissa[idx] < 0) {
+            buf[len] = '\0';
+            return len;
+        }
+    } else {
+        buf[len++] = '0';
+    }
+    buf[len++] = '.';
+    /* fractional number part */
+    while (++exponent < -1) {
+        buf[len++] = '0';
+        buf[len++] = '0';
+    }
+    while ((n = mantissa[idx++]) >= 0) {
+        buf[len++] = n / 10 + '0';
+        buf[len++] = n % 10 + '0';
+    }
+    if (buf[len - 1] == '0') {
+        len--;
+    }
+    buf[len] = '\0';
+    return len;
+}
+
+int oranumber_from_str(OCINumber *on, const char *buf, int buflen)
+{
+    const char *end;
+    int is_positive = 1;
+    char mantissa[41];
+    int dec_point;
+    long exponent = 0;
+    int idx = 0;
+    int i;
+
+    if (buflen < 0) {
+        end = buf + strlen(buf);
+    } else {
+        end = buf + buflen;
+    }
+
+    /* skip leading spaces */
+    while (buf < end && *buf == ' ') {
+        buf++;
+    }
+    if (buf == end) {
+        return ORANUMBER_INVALID_NUMBER;
+    }
+    /* read a sign mark */
+    if (*buf == '+') {
+        buf++;
+    } else if (*buf == '-') {
+        buf++;
+        is_positive = 0;
+    }
+    /* next should be number or a dot */
+    if ((*buf < '0' || '9' < *buf) && *buf != '.') {
+        return ORANUMBER_INVALID_NUMBER;
+    }
+    /* skip leading zeros */
+    while (buf < end) {
+        if (*buf == '0') {
+            buf++;
+        } else {
+            break;
+        }
+    }
+    /* read integer part */
+    while (buf < end) {
+        if ('0' <= *buf && *buf <= '9') {
+            if (idx < 41) {
+                mantissa[idx] = *buf - '0';
+            }
+            idx++;
+        } else if (*buf == '.' || *buf == 'E' || *buf == 'e' || *buf == ' ') {
+            break;
+        } else {
+            return ORANUMBER_INVALID_NUMBER;
+        }
+        buf++;
+    }
+    dec_point = idx;
+    /* read fractional part */
+    if (buf < end && *buf == '.') {
+        buf++;
+        if (idx == 0) {
+            /* skip leading zeros */
+            while (buf < end) {
+                if (*buf == '0') {
+                    dec_point--;
+                    buf++;
+                } else {
+                    break;
+                }
+            }
+        }
+        while (buf < end) {
+            if ('0' <= *buf && *buf <= '9') {
+                if (idx < 41) {
+                    mantissa[idx++] = *buf - '0';
+                }
+            } else if (*buf == 'E' || *buf == 'e' || *buf == ' ') {
+                break;
+            } else {
+                return ORANUMBER_INVALID_NUMBER;
+            }
+            buf++;
+        }
+    }
+    /* read exponent part */
+    if (buf < end && (*buf == 'E' || *buf == 'e')) {
+        int negate = 0;
+        buf++;
+        if (buf < end) {
+            if (*buf == '+') {
+                buf++;
+            } else if (*buf == '-') {
+                buf++;
+                negate = 1;
+            }
+        }
+        while (buf < end) {
+            if ('0' <= *buf && *buf <= '9') {
+                exponent *= 10;
+                exponent += *buf - '0';
+            } else if (*buf == ' ') {
+                break;
+            } else {
+                return ORANUMBER_INVALID_NUMBER;
+            }
+            buf++;
+        }
+        if (negate) {
+            exponent = -exponent;
+        }
+    }
+    /* skip trailing spaces */
+    while (buf < end && *buf == ' ') {
+        buf++;
+    }
+    /* final format check */
+    if (buf != end) {
+        return ORANUMBER_INVALID_NUMBER;
+    }
+    /* determine exponent */
+    exponent += dec_point - 1;
+    if (exponent % 2 == 0) {
+        memmove(mantissa + 1, mantissa, 40);
+        mantissa[0] = 0;
+        idx++;
+    }
+    /* round if needed */
+    if (idx > 40) {
+        idx = 40;
+        if (mantissa[40] >= 5) {
+            /* round up */
+            for (i = 39; i >= 0; i--) {
+                mantissa[i]++;
+                if (mantissa[i] == 10) {
+                    mantissa[i] = 0;
+                } else {
+                    break;
+                }
+            }
+            if (i == -1) {
+                /* all figures are rounded up. */
+                mantissa[0] = 0;
+                mantissa[1] = 1;
+                idx = 2;
+                exponent++;
+            }
+        }
+    }
+    /* shrink mantissa scale */
+    while (idx > 0 && mantissa[idx - 1] == 0) {
+        idx--;
+    }
+    /* check zero or underflow */
+    if (idx == 0 || exponent < -130) {
+        on->OCINumberPart[0] = 1;
+        on->OCINumberPart[1] = 0x80;
+        return ORANUMBER_SUCCESS;
+    }
+    /* check overflow */
+    if (exponent > 125) {
+        return ORANUMBER_NUMERIC_OVERFLOW;
+    }
+    /* change the base number from 10 to 100 */
+    if (idx % 2 == 1) {
+        mantissa[idx++] = 0;
+    }
+    idx /= 2;
+    for (i = 0; i < idx; i++) {
+        mantissa[i] = mantissa[i * 2] * 10 + mantissa[i * 2 + 1];
+    }
+    /* add negative value's terminator */
+    if (!is_positive && idx < 20) {
+        mantissa[idx++] = -1;
+    }
+    /* construct OCINumber */
+    on->OCINumberPart[0] = 1 + idx;
+    if (is_positive) {
+        on->OCINumberPart[1] = (exponent >> 1) + 193;
+        for (i = 0; i < idx; i++) {
+            on->OCINumberPart[i + 2] = mantissa[i] + 1;
+        }
+    } else {
+        on->OCINumberPart[1] = 62 - (exponent >> 1);
+        for (i = 0; i < idx; i++) {
+            on->OCINumberPart[i + 2] = 101 - mantissa[i];
+        }
+    }
+    return ORANUMBER_SUCCESS;
+}
+
+int oranumber_dump(const OCINumber *on, char *buf)
+{
+    int idx;
+    int len = on->OCINumberPart[0];
+    int offset;
+
+    offset = sprintf(buf, "Typ=2 Len=%u: ", len);
+    if (len > 21) {
+        len = 21;
+    }
+    for (idx = 1; idx <= len; idx++) {
+        offset += sprintf(buf + offset, "%hhu,", on->OCINumberPart[idx]);
+    }
+    buf[--offset] = '\0';
+    return offset;
+}

Added: trunk/ruby-oci8/ext/oci8/oranumber_util.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oranumber_util.h	                        (rev 0)
+++ trunk/ruby-oci8/ext/oci8/oranumber_util.h	2010-02-07 10:02:22 UTC (rev 376)
@@ -0,0 +1,21 @@
+/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
+/*
+ * oranumber_util.h - part of ruby-oci8
+ *
+ * Copyright (C) 2010 KUBO Takehiro <kubo at jiubao.org>
+ */
+#ifndef ORANUMBER_UTIL_H
+#define ORANUMBER_UTIL_H 1
+#include <orl.h>
+
+#define ORANUMBER_SUCCESS 0
+#define ORANUMBER_INVALID_NUMBER 1722
+#define ORANUMBER_NUMERIC_OVERFLOW 1426
+
+int oranumber_to_str(const OCINumber *on, char *buf);
+int oranumber_from_str(OCINumber *on, const char *buf, int buflen);
+
+#define ORANUMBER_DUMP_BUF_SIZ 99
+int oranumber_dump(const OCINumber *on, char *buf);
+
+#endif

Modified: trunk/ruby-oci8/test/test_oranumber.rb
===================================================================
--- trunk/ruby-oci8/test/test_oranumber.rb	2010-02-02 13:14:30 UTC (rev 375)
+++ trunk/ruby-oci8/test/test_oranumber.rb	2010-02-07 10:02:22 UTC (rev 376)
@@ -473,6 +473,45 @@
       actual_val = OraNumber.new(x).to_s
       assert_equal(expected_val, actual_val, x)
     end
+
+    conn = get_oci8_connection()
+    begin
+      cursor = conn.parse('select to_number(:1) from dual')
+      cursor.define(1, OraNumber)
+      cursor.bind_param(1, nil, String, 200)
+      [
+       "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # 1E125
+       "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # 1E124
+       "234567890234567890234567890234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+       "23456789023456789023456789023456789000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+       "2345678902345678902345678902345678900",
+       "234567890234567890234567890234567890",
+       "23456789023456789023456789023456789",
+       "2345678902345678902345678902345678.9",
+       "234567890234567890234567890234567.89",
+       "23.456789023456789023456789023456789",
+       "2.3456789023456789023456789023456789",
+       "0.23456789023456789023456789023456789", # 2.34..snip..E-1
+       "0.023456789023456789023456789023456789", # 2.34..snip..E-2
+       "0.0023456789023456789023456789023456789", # 2.34..snip..E-3
+       "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023456789023456789023456789023456789", # 2.34..snip..E-130
+       "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", # 1E-129
+       "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", # 1E-130
+      ].each do |str|
+        cursor[1] = str
+        cursor.exec
+        onum = cursor.fetch[0]
+        assert_equal(str, onum.to_s, "test data: " + str)
+
+        str = '-' + str
+        cursor[1] = str
+        cursor.exec
+        onum = cursor.fetch[0]
+        assert_equal(str, onum.to_s, "test data: " + str)
+      end
+    ensure
+      conn.logoff
+    end
   end
 
   # onum.truncate -> integer
@@ -488,6 +527,109 @@
     end
   end
 
+  def test_new_from_string
+    conn = get_oci8_connection()
+    begin
+      cursor = conn.parse('select to_number(:1) from dual')
+      cursor.define(1, OraNumber)
+      cursor.bind_param(1, nil, String, 200)
+      [
+       "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # 1E125
+       "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", # 1E124
+       "234567890234567890234567890234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+       "23456789023456789023456789023456789000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+       "2345678902345678902345678902345678900",
+       "234567890234567890234567890234567890",
+       "23456789023456789023456789023456789",
+       "2345678902345678902345678902345678.9",
+       "234567890234567890234567890234567.89",
+       "23.456789023456789023456789023456789",
+       "2.3456789023456789023456789023456789",
+       "0.23456789023456789023456789023456789", # 2.34..snip..E-1
+       "0.023456789023456789023456789023456789", # 2.34..snip..E-2
+       "0.0023456789023456789023456789023456789", # 2.34..snip..E-3
+       "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023456789023456789023456789023456789", # 2.34..snip..E-130
+       "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", # 1E-129
+       "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", # 1E-130
+
+       # leading spaces
+       "       123",
+       # trailing spaces
+       "123        ",
+       "123.456    ",
+       "123E10    ",
+       "123.456E10    ",
+       # scientific notation
+       "1234567890000e-9",
+       "123456789000e-8",
+       "123456789e-5",
+       "12345.6789e-1",
+       "12.3456789e+1",
+       "0.0000123456789E7",
+       # round to zero
+       "1E-131",
+       # overflow
+       "1E126",
+       "10E125",
+       "100000000000E115",
+       "1E+126",
+       # invalid number
+       "",
+       "     ",
+       "abc",
+       "1E10a",
+       "11E10a",
+       "1.1.1",
+       # round down
+       "444444444444444444444444444444444444444444444444440000",
+       "44444444444444444444444444444444444444444444444444000",
+       "44444444444444444444444444.444444444444444444444444",
+       "4444444444444444444444444.4444444444444444444444444",
+       "0.000000044444444444444444444444444444444444444444444444444",
+       "0.00000044444444444444444444444444444444444444444444444444",
+       # round up
+       "555555555555555555555555555555555555555555555555550000",
+       "55555555555555555555555555555555555555555555555555000",
+       "55555555555555555555555555.555555555555555555555555",
+       "5555555555555555555555555.5555555555555555555555555",
+       "0.000000055555555555555555555555555555555555555555555555555",
+       "0.00000055555555555555555555555555555555555555555555555555",
+       "999999999999999999999999999999999999999999999999990000",
+       "99999999999999999999999999999999999999999999999999000",
+       "99999999999999999999999999.999999999999999999999999",
+       "9999999999999999999999999.9999999999999999999999999",
+       "0.000000099999999999999999999999999999999999999999999999999",
+       "0.00000099999999999999999999999999999999999999999999999999",
+       # overflow by round up
+       "999999999999999999999999999999999999999999999999999999999900000000000000000000000000000000000000000000000000000000000000000000",
+      ].each do |str|
+        run_test_new_from_string(cursor, str)
+        run_test_new_from_string(cursor, '-' + str)
+      end
+    ensure
+      conn.logoff
+    end
+  end
+
+  def run_test_new_from_string(cursor, str)
+    cursor[1] = str
+    onum = nil
+    begin
+      cursor.exec
+      onum = cursor.fetch[0]
+    rescue OCIError => oraerr
+      begin
+        OraNumber.new(str)
+        flunk("exception expected but none was thrown. test data: " + str)
+      rescue
+        assert_equal(oraerr.to_s, $!.to_s, "test data: " + str)
+      end
+    end
+    if onum
+      assert_equal(onum.dump, OraNumber.new(str).dump, "test data: " + str)
+    end
+  end
+
   def test_new_from_bigdecimal
     ["+Infinity", "-Infinity", "NaN"].each do |n|
       assert_raise TypeError do
@@ -504,7 +646,8 @@
     [
      [Rational(1, 2), "0.5"],
      [Rational(3, 5), "0.6"],
-     [Rational(10, 3), "3.3333333333333333333333333333333333333"],
+     [Rational(10, 3), "3.33333333333333333333333333333333333333"],
+     [Rational(20, 3), "6.66666666666666666666666666666666666667"],
     ].each do |ary|
       assert_equal(ary[1], OraNumber.new(ary[0]).to_s)
     end




More information about the ruby-oci8-commit mailing list