[ruby-oci8-commit] [392] trunk/ruby-oci8: * ext/oci8/bind.c, ext/oci8/oci8.h, lib/oci8/ bindtype.rb:

nobody at rubyforge.org nobody at rubyforge.org
Mon May 3 09:55:18 EDT 2010


Revision: 392
Author:   kubo
Date:     2010-05-03 09:55:17 -0400 (Mon, 03 May 2010)

Log Message:
-----------
* ext/oci8/bind.c, ext/oci8/oci8.h, lib/oci8/bindtype.rb:
    change the length semantics from byte to char for string
    bind variables when the Oracle client is 9i or upper.

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

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2010-05-02 13:15:25 UTC (rev 391)
+++ trunk/ruby-oci8/ChangeLog	2010-05-03 13:55:17 UTC (rev 392)
@@ -1,3 +1,8 @@
+2010-05-03  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/bind.c, ext/oci8/oci8.h, lib/oci8/bindtype.rb:
+	    change the length semantics from byte to char for string
+	    bind variables when the Oracle client is 9i or upper.
+
 2010-05-02  KUBO Takehiro  <kubo at jiubao.org>
 	* lib/oci8/encoding-init.rb: fix for zero-length NLS_LANG.
 	    fix AL32UTF8's nls_ratio.

Modified: trunk/ruby-oci8/ext/oci8/bind.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/bind.c	2010-05-02 13:15:25 UTC (rev 391)
+++ trunk/ruby-oci8/ext/oci8/bind.c	2010-05-03 13:55:17 UTC (rev 392)
@@ -5,14 +5,26 @@
  * $Author$
  * $Date$
  *
- * Copyright (C) 2002-2008 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2010 KUBO Takehiro <kubo at jiubao.org>
  */
 #include "oci8.h"
 
+#ifndef OCI_ATTR_MAXCHAR_SIZE
+#define OCI_ATTR_MAXCHAR_SIZE 164
+#endif
+
 static ID id_bind_type;
+static VALUE sym_length;
+static VALUE sym_char_semantics;
 
 static VALUE cOCI8BindTypeBase;
 
+typedef struct {
+    oci8_bind_t obind;
+    sb4 bytelen;
+    sb4 charlen;
+} oci8_bind_string_t;
+
 /*
  * bind_string
  */
@@ -24,41 +36,67 @@
 
 static void bind_string_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
 {
+    oci8_bind_string_t *obs = (oci8_bind_string_t *)obind;
     oci8_vstr_t *vstr = (oci8_vstr_t *)data;
 
     OCI8StringValue(val);
-    if (RSTRING_LEN(val) > obind->value_sz - sizeof(vstr->size)) {
-        rb_raise(rb_eArgError, "too long String to set. (%ld for %d)", RSTRING_LEN(val), obind->value_sz - (sb4)sizeof(vstr->size));
+    if (RSTRING_LEN(val) > obs->bytelen) {
+        rb_raise(rb_eArgError, "too long String to set. (%ld for %d)", RSTRING_LEN(val), obs->bytelen);
     }
     memcpy(vstr->buf, RSTRING_PTR(val), RSTRING_LEN(val));
     vstr->size = RSTRING_LEN(val);
 }
 
-static void bind_string_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE length)
+static void bind_string_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE param)
 {
+    oci8_bind_string_t *obs = (oci8_bind_string_t *)obind;
+    VALUE length;
+    VALUE char_semantics;
     sb4 sz;
-    if (NIL_P(length)) {
-        if (NIL_P(val)) {
-            rb_raise(rb_eArgError, "value and length are both null.");
+
+    Check_Type(param, T_HASH);
+    length = rb_hash_aref(param, sym_length);
+    char_semantics = rb_hash_aref(param, sym_char_semantics);
+
+    sz = NUM2INT(length);
+    if (sz < 0) {
+        rb_raise(rb_eArgError, "invalid bind length %d", sz);
+    }
+    if (RTEST(char_semantics)) {
+        obs->charlen = sz;
+        obs->bytelen = sz = sz * oci8_nls_ratio;
+        if (oci8_nls_ratio == 1) {
+            /* sz must be bigger than charlen to suppress ORA-06502.
+             * I don't know the reason...
+             */
+            sz *= 2;
         }
-        StringValue(val);
-        sz = RSTRING_LEN(val);
     } else {
-        sz = NUM2INT(length);
+        obs->bytelen = sz;
+        obs->charlen = 0;
     }
-    if (sz < 0) {
-        rb_raise(rb_eArgError, "invalid bind length %d", sz);
+    if (sz == 0) {
+        sz = 1; /* to avoid ORA-01459. */
     }
     sz += sizeof(sb4);
     obind->value_sz = sz;
     obind->alloc_sz = (sz + (sizeof(sb4) - 1)) & ~(sizeof(sb4) - 1);
 }
 
+static void bind_string_post_bind_hook(oci8_bind_t *obind)
+{
+    oci8_bind_string_t *obs = (oci8_bind_string_t *)obind;
+
+    if (oracle_client_version >= ORAVER_9_0 && obs->charlen != 0) {
+        oci_lc(OCIAttrSet(obind->base.hp.ptr, obind->base.type, (void*)&obs->charlen, 0, OCI_ATTR_MAXCHAR_SIZE, oci8_errhp));
+    }
+}
+
 static const oci8_bind_class_t bind_string_class = {
     {
         NULL,
         oci8_bind_free,
-        sizeof(oci8_bind_t)
+        sizeof(oci8_bind_string_t)
     },
     bind_string_get,
     bind_string_set,
@@ -67,7 +105,8 @@
     NULL,
     NULL,
     NULL,
-    SQLT_LVC
+    SQLT_LVC,
+    bind_string_post_bind_hook,
 };
 
 /*
@@ -83,7 +122,7 @@
     {
         NULL,
         oci8_bind_free,
-        sizeof(oci8_bind_t)
+        sizeof(oci8_bind_string_t)
     },
     bind_raw_get,
     bind_string_set,
@@ -445,6 +484,8 @@
 {
     cOCI8BindTypeBase = klass;
     id_bind_type = rb_intern("bind_type");
+    sym_length = ID2SYM(rb_intern("length"));
+    sym_char_semantics = ID2SYM(rb_intern("char_semantics"));
 
     rb_define_method(cOCI8BindTypeBase, "initialize", oci8_bind_initialize, 4);
     rb_define_method(cOCI8BindTypeBase, "get", oci8_bind_get, 0);

Modified: trunk/ruby-oci8/ext/oci8/oci8.h
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8.h	2010-05-02 13:15:25 UTC (rev 391)
+++ trunk/ruby-oci8/ext/oci8/oci8.h	2010-05-03 13:55:17 UTC (rev 392)
@@ -2,7 +2,7 @@
 /*
  * oci8.h - part of ruby-oci8
  *
- * Copyright (C) 2002-2009 KUBO Takehiro <kubo at jiubao.org>
+ * Copyright (C) 2002-2010 KUBO Takehiro <kubo at jiubao.org>
  */
 #ifndef _RUBY_OCI_H_
 #define _RUBY_OCI_H_ 1
@@ -508,6 +508,7 @@
 /* encoding.c */
 void Init_oci8_encoding(VALUE cOCI8);
 VALUE oci8_charset_id2name(VALUE svc, VALUE charset_id);
+extern int oci8_nls_ratio;
 
 /* win32.c */
 void Init_oci8_win32(VALUE cOCI8);

Modified: trunk/ruby-oci8/lib/oci8/bindtype.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/bindtype.rb	2010-05-02 13:15:25 UTC (rev 391)
+++ trunk/ruby-oci8/lib/oci8/bindtype.rb	2010-05-03 13:55:17 UTC (rev 392)
@@ -1,7 +1,7 @@
 #--
 # bindtype.rb -- OCI8::BindType
 #
-# Copyright (C) 2009 KUBO Takehiro <kubo at jiubao.org>
+# Copyright (C) 2009-2010 KUBO Takehiro <kubo at jiubao.org>
 #++
 
 class OCI8
@@ -99,38 +99,46 @@
       def self.create(con, val, param, max_array_size)
         case param
         when Hash
-          if param[:length]
-            # If length is passed explicitly, use it.
-            length = param[:length]
-          elsif val.is_a? String or (val.respond_to? :to_str and val = val.to_str)
-            if OCI8.respond_to? :encoding and OCI8.encoding != val.encoding
-              # If the string encoding is different with NLS_LANG character set,
-              # convert it to get the length.
-              val = val.encode(OCI8.encoding)
-            end
-            if val.respond_to? :bytesize
-              # ruby 1.8.7 or upper
-              length = val.bytesize
+          param[:char_semantics] = true unless param.has_key? :char_semantics
+          unless param[:length]
+            if val.respond_to? :to_str
+              val = val.to_str
+              if param[:char_semantics]
+                param[:length] = val.size
+              else
+                if OCI8.respond_to? :encoding and OCI8.encoding != val.encoding
+                  # If the string encoding is different with NLS_LANG character set,
+                  # convert it to get the length.
+                  val = val.encode(OCI8.encoding)
+                end
+                if val.respond_to? :bytesize
+                  # ruby 1.8.7 or upper
+                  param[:length] = val.bytesize
+                else
+                  # ruby 1.8.6 or lower
+                  param[:length] = val.size
+                end
+              end
             else
-              # ruby 1.8.6 or lower
-              length = val.size
+              param[:length] = @@minimum_bind_length
             end
           end
         when OCI8::Metadata::Base
           case param.data_type
           when :char, :varchar2
-            length = param.data_size
-            # character size may become large on character set conversion.
-            # The length of a Japanese half-width kana is one in Shift_JIS,
-            # two in EUC-JP, three in UTF-8.
-            length *= 3 unless param.char_used?
+            if param.charset_form == :nchar or param.char_used?
+              param = {:length => param.char_size, :char_semantics => true}
+            else
+              param = {:length => param.data_size}
+            end
           when :raw
             # HEX needs twice space.
-            length = param.data_size * 2
+            param = {:length => param.data_size * 2}
+          else
+            param = {:length => @@minimum_bind_length}
           end
         end
-        length = @@minimum_bind_length if length.nil? or length < @@minimum_bind_length
-        self.new(con, val, length, max_array_size)
+        self.new(con, val, param, max_array_size)
       end
     end
 
@@ -138,28 +146,36 @@
       def self.create(con, val, param, max_array_size)
         case param
         when Hash
-          length = 400 # default length
-          if param[:length]
-            length = param[:length]
-          elsif val.respond_to? :to_str and val.to_str.size > length
-            length = val.to_str.size
+          unless param[:length]
+            if val.respond_to? :to_str
+              val = val.to_str
+              if val.respond_to? :bytesize
+                param[:length] = val.bytesize
+              else
+                param[:length] = val.size
+              end
+            else
+              param[:length] = 400
+            end
           end
         when OCI8::Metadata::Base
-          length = param.data_size
+          param = {:length => param.data_size}
         end
-        self.new(con, val, length, max_array_size)
+        self.new(con, val, param, max_array_size)
       end
     end
 
     class Long < OCI8::BindType::String
       def self.create(con, val, param, max_array_size)
-        self.new(con, val, con.long_read_len, max_array_size)
+        param = {:length => con.long_read_len, :char_semantics => true}
+        self.new(con, val, param, max_array_size)
       end
     end
 
     class LongRaw < OCI8::BindType::RAW
       def self.create(con, val, param, max_array_size)
-        self.new(con, val, con.long_read_len, max_array_size)
+        param = {:length => con.long_read_len, :char_semantics => false}
+        self.new(con, val, param, max_array_size)
       end
     end
 




More information about the ruby-oci8-commit mailing list