[ruby-oci8-commit] [441] trunk/ruby-oci8: Decimal to float and float to decimal conversions are done as exactly ruby does by default .

nobody at rubyforge.org nobody at rubyforge.org
Fri Aug 19 09:19:36 EDT 2011


Revision: 441
Author:   kubo
Date:     2011-08-19 09:19:35 -0400 (Fri, 19 Aug 2011)

Log Message:
-----------
Decimal to float and float to decimal conversions are done as exactly ruby does by default.
The behavior is customizable by OCI8.properties[:float_conversion_type].

Modified Paths:
--------------
    trunk/ruby-oci8/.gitignore
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/bind.c
    trunk/ruby-oci8/ext/oci8/oci8.c
    trunk/ruby-oci8/ext/oci8/oci8.h
    trunk/ruby-oci8/ext/oci8/ocinumber.c
    trunk/ruby-oci8/lib/oci8/properties.rb

Modified: trunk/ruby-oci8/.gitignore
===================================================================
--- trunk/ruby-oci8/.gitignore	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/.gitignore	2011-08-19 13:19:35 UTC (rev 441)
@@ -5,4 +5,7 @@
 ext/oci8/depend
 ext/oci8/extconf.h
 ext/oci8/mkmf.log
+ext/oci8/*.o
+ext/oci8/*.so
+ext/oci8/*.obj
 lib/oci8.rb

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/ChangeLog	2011-08-19 13:19:35 UTC (rev 441)
@@ -1,4 +1,10 @@
 2011-08-19  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/bind.c, ext/oci8/oci8.c, ext/oci8/oci8.h, ext/oci8/ocinumber.c,
+	  lib/oci8/properties.rb: Decimal to float and float to decimal conversions
+	    are done as exactly ruby does by default. The behavior is customizable by
+	    OCI8.properties[:float_conversion_type].
+
+2011-08-19  KUBO Takehiro  <kubo at jiubao.org>
 	* ext/oci8/oraconf.rb: fix a bug not to find the OCI library location listed in
 	    'ldconfig -p' when LD_LIBRARY_PATH is set.
 	    (Reported by Edgars Beigarts.)

Modified: trunk/ruby-oci8/ext/oci8/bind.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/bind.c	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/ext/oci8/bind.c	2011-08-19 13:19:35 UTC (rev 441)
@@ -2,7 +2,7 @@
 /*
  * bind.c
  *
- * Copyright (C) 2002-2010 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2011 KUBO Takehiro <kubo at jiubao.org>
  */
 #include "oci8.h"
 
@@ -298,41 +298,25 @@
 #endif /* USE_DYNAMIC_FETCH */
 
 /*
- * bind_float
+ * bind_binary_double
  */
-static VALUE bind_float_get(oci8_bind_t *obind, void *data, void *null_struct)
+static VALUE bind_binary_double_get(oci8_bind_t *obind, void *data, void *null_struct)
 {
     return rb_float_new(*(double*)data);
 }
 
-static void bind_float_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
+static void bind_binary_double_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
 {
     /* val is converted to Float if it isn't Float. */
     *(double*)data = RFLOAT_VALUE(rb_Float(val));
 }
 
-static void bind_float_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE length)
+static void bind_binary_double_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE length)
 {
     obind->value_sz = sizeof(double);
     obind->alloc_sz = sizeof(double);
 }
 
-static const oci8_bind_class_t bind_float_class = {
-    {
-        NULL,
-        oci8_bind_free,
-        sizeof(oci8_bind_t)
-    },
-    bind_float_get,
-    bind_float_set,
-    bind_float_init,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    SQLT_FLT
-};
-
 #ifndef SQLT_BDOUBLE
 #define SQLT_BDOUBLE 22
 #endif
@@ -342,9 +326,9 @@
         oci8_bind_free,
         sizeof(oci8_bind_t)
     },
-    bind_float_get,
-    bind_float_set,
-    bind_float_init,
+    bind_binary_double_get,
+    bind_binary_double_set,
+    bind_binary_double_init,
     NULL,
     NULL,
     NULL,
@@ -516,7 +500,6 @@
     oci8_define_bind_class("Long", &bind_long_class);
     oci8_define_bind_class("LongRaw", &bind_long_raw_class);
 #endif /* USE_DYNAMIC_FETCH */
-    oci8_define_bind_class("Float", &bind_float_class);
     if (oracle_client_version >= ORAVER_10_1) {
         oci8_define_bind_class("BinaryDouble", &bind_binary_double_class);
     }

Modified: trunk/ruby-oci8/ext/oci8/oci8.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.c	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/ext/oci8/oci8.c	2011-08-19 13:19:35 UTC (rev 441)
@@ -2,7 +2,7 @@
 /*
  * oci8.c - part of ruby-oci8
  *
- * Copyright (C) 2002-2010 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2011 KUBO Takehiro <kubo at jiubao.org>
  *
  */
 #include "oci8.h"
@@ -124,6 +124,27 @@
     return oracle_client_vernum;
 }
 
+static VALUE oci8_s_set_property(VALUE klass, VALUE name, VALUE val)
+{
+    const char *name_str;
+
+    Check_Type(name, T_SYMBOL);
+    name_str = rb_id2name(SYM2ID(name));
+    if (strcmp(name_str, "float_conversion_type") == 0) {
+        const char *val_str;
+        Check_Type(val, T_SYMBOL);
+        val_str = rb_id2name(SYM2ID(val));
+        if (strcmp(val_str, "ruby") == 0) {
+            oci8_float_conversion_type_is_ruby = 1;
+        } else if (strcmp(val_str, "oracle") == 0) {
+            oci8_float_conversion_type_is_ruby = 0;
+        } else {
+            rb_raise(rb_eArgError, "float_conversion_type's value should be either :ruby or :oracle.");
+        }
+    }
+    return Qnil;
+}
+
 /*
  * call-seq:
  *   OCI8.error_message(message_no) -> string
@@ -1016,6 +1037,7 @@
 
     rb_define_const(cOCI8, "VERSION", rb_obj_freeze(rb_usascii_str_new_cstr(OCI8LIB_VERSION)));
     rb_define_singleton_method_nodoc(cOCI8, "oracle_client_vernum", oci8_s_oracle_client_vernum, 0);
+    rb_define_singleton_method_nodoc(cOCI8, "__set_property", oci8_s_set_property, 2);
     if (have_OCIMessageOpen && have_OCIMessageGet) {
         rb_define_singleton_method(cOCI8, "error_message", oci8_s_error_message, 1);
     }

Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/ext/oci8/oci8.h	2011-08-19 13:19:35 UTC (rev 441)
@@ -490,6 +490,7 @@
 void Init_ora_date(void);
 
 /* ocinumber.c */
+extern int oci8_float_conversion_type_is_ruby;
 void Init_oci_number(VALUE mOCI, OCIError *errhp);
 OCINumber *oci8_get_ocinumber(VALUE num);
 VALUE oci8_make_ocinumber(OCINumber *s, OCIError *errhp);
@@ -497,6 +498,8 @@
 VALUE oci8_make_float(OCINumber *s, OCIError *errhp);
 OCINumber *oci8_set_ocinumber(OCINumber *result, VALUE self, OCIError *errhp);
 OCINumber *oci8_set_integer(OCINumber *result, VALUE self, OCIError *errhp);
+double oci8_onum_to_dbl(OCINumber *s, OCIError *errhp);
+OCINumber *oci8_dbl_to_onum(OCINumber *result, double dbl, OCIError *errhp);
 
 /* ocidatetim.c */
 void Init_oci_datetime(void);

Modified: trunk/ruby-oci8/ext/oci8/ocinumber.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/ocinumber.c	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/ext/oci8/ocinumber.c	2011-08-19 13:19:35 UTC (rev 441)
@@ -2,12 +2,13 @@
 /*
  *  ocinumber.c
  *
- * Copyright (C) 2005-2009 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2005-2011 KUBO Takehiro <kubo at jiubao.org>
  *
  */
 #include "oci8.h"
 #include <orl.h>
 #include <errno.h>
+#include <math.h>
 #include "oranumber_util.h"
 
 #ifndef RB_NUM_COERCE_FUNCS_NEED_OPID
@@ -16,6 +17,12 @@
 #define rb_num_coerce_bin(x, y, id) rb_num_coerce_bin((x), (y))
 #endif
 
+int oci8_float_conversion_type_is_ruby = 1;
+
+#ifndef INFINITY
+#define INFINITY (1.0/+0.0)
+#endif
+
 static ID id_power; /* rb_intern("**") */
 static ID id_cmp;   /* rb_intern("<=>") */
 static ID id_finite_p;
@@ -136,10 +143,7 @@
 
 VALUE oci8_make_float(OCINumber *s, OCIError *errhp)
 {
-    double dbl;
-
-    oci_lc(OCINumberToReal(errhp, s, sizeof(double), &dbl));
-    return rb_float_new(dbl);
+    return rb_float_new(oci8_onum_to_dbl(s, errhp));
 }
 
 /* fill C structure (OCINumber) from a string. */
@@ -191,7 +195,6 @@
 static int set_oci_number_from_num(OCINumber *result, VALUE num, int force, OCIError *errhp)
 {
     signed long sl;
-    double dbl;
 
     if (!RTEST(rb_obj_is_kind_of(num, rb_cNumeric)))
         rb_raise(rb_eTypeError, "expect Numeric but %s", rb_class2name(CLASS_OF(num)));
@@ -206,8 +209,7 @@
         return 1;
     case T_FLOAT:
         /* set from double. */
-        dbl = NUM2DBL(num);
-        oci_lc(OCINumberFromReal(errhp, &dbl, sizeof(dbl), result));
+        oci8_dbl_to_onum(result, NUM2DBL(num), errhp);
         return 1;
     case T_BIGNUM:
         /* change via string. */
@@ -313,6 +315,68 @@
     return result;
 }
 
+double oci8_onum_to_dbl(OCINumber *s, OCIError *errhp)
+{
+    if (oci8_float_conversion_type_is_ruby) {
+        char buf[256];
+        sword rv;
+
+        double dbl;
+
+        oci_lc(OCINumberToReal(errhp, s, sizeof(double), &dbl));
+
+        rv = oranumber_to_str(s, buf, sizeof(buf));
+        if (rv <= 0) {
+            char buf[ORANUMBER_DUMP_BUF_SIZ];
+
+            oranumber_dump(s, buf);
+            rb_raise(eOCIException, "Invalid internal number format: %s", buf);
+        }
+        if (strcmp(buf, "~") == 0) {
+            return INFINITY;
+        } else if (strcmp(buf, "-~") == 0) {
+            return -INFINITY;
+        }
+        return rb_cstr_to_dbl(buf, Qtrue);
+    } else {
+        double dbl;
+
+        oci_lc(OCINumberToReal(errhp, s, sizeof(double), &dbl));
+        return dbl;
+    }
+}
+
+OCINumber *oci8_dbl_to_onum(OCINumber *result, double dbl, OCIError *errhp)
+{
+    switch (fpclassify(dbl)) {
+    case FP_NAN:
+        rb_raise(rb_eFloatDomainError, "NaN");
+        /* never reach here */
+        break;
+    case FP_INFINITE:
+        if (dbl > 0.0) {
+            oranumber_from_str(result, "~", 1);
+        } else {
+            oranumber_from_str(result, "-~", 2);
+        }
+        return result;
+    }
+
+    if (oci8_float_conversion_type_is_ruby) {
+        VALUE str;
+        sword rv;
+
+        str = rb_obj_as_string(rb_float_new(dbl));
+        rv = oranumber_from_str(result, RSTRING_PTR(str), RSTRING_LEN(str));
+        if (rv != 0) {
+            oci8_raise_by_msgno(rv, NULL);
+        }
+    } else {
+        oci_lc(OCINumberFromReal(errhp, &dbl, sizeof(dbl), result));
+    }
+    return result;
+}
+
 /*
  *  call-seq:
  *     OCI8::Math.atan2(y, x) -> oranumber
@@ -1107,11 +1171,7 @@
  */
 static VALUE onum_to_f(VALUE self)
 {
-    OCIError *errhp = oci8_errhp;
-    double dbl;
-
-    oci_lc(OCINumberToReal(errhp, _NUMBER(self), sizeof(dbl), &dbl));
-    return rb_float_new(dbl);
+    return rb_float_new(oci8_onum_to_dbl(_NUMBER(self), oci8_errhp));
 }
 
 /*
@@ -1363,6 +1423,11 @@
     return oci8_make_integer((OCINumber*)data, oci8_errhp);
 }
 
+static VALUE bind_float_get(oci8_bind_t *obind, void *data, void *null_struct)
+{
+    return oci8_make_float((OCINumber*)data, oci8_errhp);
+}
+
 static void bind_ocinumber_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
 {
     set_oci_number_from_num((OCINumber*)data, val, 1, oci8_errhp);
@@ -1425,6 +1490,22 @@
     SQLT_VNU,
 };
 
+static const oci8_bind_class_t bind_float_class = {
+    {
+        NULL,
+        oci8_bind_free,
+        sizeof(oci8_bind_t)
+    },
+    bind_float_get,
+    bind_ocinumber_set,
+    bind_ocinumber_init,
+    bind_ocinumber_init_elem,
+    NULL,
+    NULL,
+    NULL,
+    SQLT_VNU,
+};
+
 void
 Init_oci_number(VALUE cOCI8, OCIError *errhp)
 {
@@ -1544,6 +1625,7 @@
 
     oci8_define_bind_class("OraNumber", &bind_ocinumber_class);
     oci8_define_bind_class("Integer", &bind_integer_class);
+    oci8_define_bind_class("Float", &bind_float_class);
 }
 
 OCINumber *oci8_get_ocinumber(VALUE num)

Modified: trunk/ruby-oci8/lib/oci8/properties.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/properties.rb	2011-08-19 08:13:31 UTC (rev 440)
+++ trunk/ruby-oci8/lib/oci8/properties.rb	2011-08-19 13:19:35 UTC (rev 441)
@@ -1,11 +1,12 @@
 # properties.rb -- implements OCI8.properties
 #
-# Copyright (C) 2010 KUBO Takehiro <kubo at jiubao.org>
+# Copyright (C) 2010-2011 KUBO Takehiro <kubo at jiubao.org>
 
 class OCI8
 
   @@properties = {
     :bind_string_as_nchar => false,
+    :float_conversion_type => :ruby,
   }
 
   def @@properties.[](name)
@@ -18,6 +19,9 @@
     case name
     when :bind_string_as_nchar
       val = val ? true : false
+    when :float_conversion_type
+      # handled by native code in oci8lib_xx.so.
+      OCI8.__set_property(name, val)
     end
     super(name, val)
   end
@@ -44,6 +48,14 @@
   # [:bind_string_as_nchar]
   #     +true+ when string bind variables are bound as NCHAR,
   #     otherwise +false+. The default value is +false+.
+  #
+  # [:float_conversion_type]
+  #     (new in 2.1.0)
+  #     Specifies who converts decimal to float and vice versa.
+  #     It should be either +:ruby+ or +:oracle+. The default value
+  #     is +:ruby+.
+  #     See: http://rubyforge.org/forum/forum.php?thread_id=50030&forum_id=1078
+  #
   def self.properties
     @@properties
   end




More information about the ruby-oci8-commit mailing list