[ruby-oci8-commit] [246] trunk/ruby-oci8: Add new feature: Array DML

nobody at rubyforge.org nobody at rubyforge.org
Wed Feb 27 03:23:39 EST 2008


Revision: 246
Author:   limlian
Date:     2008-02-27 03:23:39 -0500 (Wed, 27 Feb 2008)

Log Message:
-----------
Add new feature: Array DML
* lib/oci8/oci8.rb:  add three new methods for Array DML to OCI8::Cursor: 
    max_array_size=, bind_param_array and exec_array
    1) OCI8::Cursor#max_array_size=: set the maximum array size for OCI8::Cursor#bind_param_array. 
      This method should be called before OCI8::Cursor#bind_param_array
    2) OCI8::Cursor#bind_param_array: bind array explicitly. This method is used to bind an array of 
      values to a placeholder embedded in the prepared statement which is to be executed with 
      OCI8::Cursor#exec_array
    3) OCI8::Cursor#exec_array: executes the SQL statement assigned the cursor with array bindings. 
      This implementation currently only supports non-data returning statements (INSERT, UPDATE, DELETE 
      but not SELECT). All binding arrays should be the same size and this size will be used as iteration 
      count for OCIStmtExecute()
* ext/oci8/stmt.c: add support for Array DML
    1) Change the parameters for invoking OCIBindByPos and OCIBindByName because 
      currently we only support Array DML for non-PL/SQL binds.
    2) Add a new parameter "Value iteration_count" to function oci8_stmt_execute. 
      This parameter indicates iteration count for OCIStmtExecute. For Non-Array DML, 
      you should set this parameter "nil"
    3) Add three new functions: each_value, clear_binds_iterator_proc, oci8_stmt_clear_binds. 
      Those functions are used to clear all binds from OCI8::Cursor. When calling OCI8::Cursor#max_array_size, 
      all the binds will be clean from cursor if instance variable max_array_size of cursor is set before.
* test/test_array_dml.rb(added): add test cases for Array DML
* test/test_all.rb: call test cases for Array DML 

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/stmt.c
    trunk/ruby-oci8/lib/oci8/oci8.rb
    trunk/ruby-oci8/test/test_all.rb

Added Paths:
-----------
    trunk/ruby-oci8/test/test_array_dml.rb

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2008-02-17 15:29:30 UTC (rev 245)
+++ trunk/ruby-oci8/ChangeLog	2008-02-27 08:23:39 UTC (rev 246)
@@ -1,3 +1,28 @@
+2008-2-27 Liming Lian <liming.lian at oracle.com>
+	Add new feature: Array DML
+	* lib/oci8/oci8.rb:  add three new methods for Array DML to OCI8::Cursor: 
+	    max_array_size=, bind_param_array and exec_array
+	    1) OCI8::Cursor#max_array_size=: set the maximum array size for OCI8::Cursor#bind_param_array. 
+	    This method should be called before OCI8::Cursor#bind_param_array
+	    2) OCI8::Cursor#bind_param_array: bind array explicitly. This method is used to bind an array of 
+	    values to a placeholder embedded in the prepared statement which is to be executed with 
+	    OCI8::Cursor#exec_array
+	    3) OCI8::Cursor#exec_array: executes the SQL statement assigned the cursor with array bindings. 
+	    This implementation currently only supports non-data returning statements (INSERT, UPDATE, DELETE 
+	    but not SELECT). All binding arrays should be the same size and this size will be used as iteration 
+	    count for OCIStmtExecute()
+	* ext/oci8/stmt.c: add support for Array DML
+	    1) Change the parameters for invoking OCIBindByPos and OCIBindByName because 
+	    currently we only support Array DML for non-PL/SQL binds.
+	    2) Add a new parameter "Value iteration_count" to function oci8_stmt_execute. 
+	    This parameter indicates iteration count for OCIStmtExecute. For Non-Array DML, 
+	    you should set this parameter "nil"
+	    3) Add three new functions: each_value, clear_binds_iterator_proc, oci8_stmt_clear_binds. 
+	    Those functions are used to clear all binds from OCI8::Cursor. When calling OCI8::Cursor#max_array_size, 
+	    all the binds will be clean from cursor if instance variable max_array_size of cursor is set before.
+	* test/test_array_dml.rb(added): add test cases for Array DML
+	* test/test_all.rb: call test cases for Array DML 
+
 2008-02-17  KUBO Takehiro  <kubo at jiubao.org>
 	* Makefile: add format_c_source target to fix indentation by
 	    astyle. <URL:http://astyle.sourceforge.net>

Modified: trunk/ruby-oci8/ext/oci8/stmt.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/stmt.c	2008-02-17 15:29:30 UTC (rev 245)
+++ trunk/ruby-oci8/ext/oci8/stmt.c	2008-02-27 08:23:39 UTC (rev 246)
@@ -19,8 +19,13 @@
 static VALUE oci8_sym_declare_stmt;
 static VALUE oci8_sym_other;
 static ID id_at_column_metadata;
+static ID id_at_actual_array_size;
+static ID id_at_max_array_size;
+static ID id_each_value;
 static ID id_at_names;
+static ID id_empty_p;
 static ID id_at_con;
+static ID id_clear;
 static ID id_set;
 
 VALUE cOCIStmt;
@@ -212,9 +217,9 @@
         curelep = &obind->curar_sz;
     }
     if (placeholder_ptr == (char*)-1) {
-        status = OCIBindByPos(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, position, obind->valuep, obind->value_sz, bind_class->dty, indp, NULL, 0, obind->maxar_sz, curelep, mode);
+        status = OCIBindByPos(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, position, obind->valuep, obind->value_sz, bind_class->dty, indp, NULL, 0, 0, 0, mode);
     } else {
-        status = OCIBindByName(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, TO_ORATEXT(placeholder_ptr), placeholder_len, obind->valuep, obind->value_sz, bind_class->dty, indp, NULL, 0, obind->maxar_sz, curelep, mode);
+        status = OCIBindByName(stmt->base.hp.stmt, &obind->base.hp.bnd, oci8_errhp, TO_ORATEXT(placeholder_ptr), placeholder_len, obind->valuep, obind->value_sz, bind_class->dty, indp, NULL, 0, 0, 0, mode);
     }
     if (status != OCI_SUCCESS) {
         oci8_raise(oci8_errhp, status, stmt->base.hp.stmt);
@@ -258,7 +263,7 @@
     return rv;
 }
 
-static VALUE oci8_stmt_execute(VALUE self)
+static VALUE oci8_stmt_execute(VALUE self, VALUE iteration_count)
 {
     oci8_stmt_t *stmt = DATA_PTR(self);
     oci8_svcctx_t *svcctx = oci8_get_svcctx(stmt->svc);
@@ -270,7 +275,10 @@
         iters = 0;
         mode = OCI_DEFAULT;
     } else {
-        iters = 1;
+        if(!NIL_P(iteration_count)) 
+            iters = NUM2INT(iteration_count);
+        else 
+            iters = 1;
         mode = svcctx->is_autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT;
     }
     rv = oci8_call_stmt_execute(svcctx, stmt, iters, mode);
@@ -326,6 +334,32 @@
     return self;
 }
 
+static VALUE each_value(VALUE obj)
+{
+    return rb_funcall(obj, id_each_value, 0);
+}
+
+static VALUE clear_binds_iterator_proc(VALUE val, VALUE arg)
+{
+    if(!NIL_P(val)) {
+        oci8_base_free((oci8_base_t*)oci8_get_bind(val));
+    }
+    return Qnil;
+}
+
+static VALUE oci8_stmt_clear_binds(VALUE self)
+{
+    oci8_stmt_t *stmt = DATA_PTR(self);
+    
+    if(!RTEST(rb_funcall(stmt->binds, id_empty_p, 0)))
+    {
+        rb_iterate(each_value, stmt->binds, clear_binds_iterator_proc, Qnil);
+        rb_funcall(stmt->binds,id_clear,0);
+    }
+
+    return self;
+}
+
 static VALUE oci8_stmt_do_fetch(oci8_stmt_t *stmt, oci8_svcctx_t *svcctx)
 {
     VALUE ary;
@@ -597,11 +631,29 @@
  */
 static VALUE oci8_stmt_aset(VALUE self, VALUE key, VALUE val)
 {
+    long max_array_size;
+    long actual_array_size;
+    long bind_array_size;
+
     oci8_stmt_t *stmt = DATA_PTR(self);
     VALUE obj = rb_hash_aref(stmt->binds, key);
     if (NIL_P(obj)) {
         return Qnil; /* ?? MUST BE ERROR? */
     }
+
+    if(TYPE(val) == T_ARRAY) {
+        max_array_size = NUM2INT(rb_ivar_get(self, id_at_max_array_size));
+        actual_array_size = NUM2INT(rb_ivar_get(self, id_at_actual_array_size));
+        bind_array_size = RARRAY_LEN(val);
+
+        if(actual_array_size > 0 && bind_array_size != actual_array_size) {
+            rb_raise(rb_eRuntimeError, "all binding arrays hould be the same size");        
+        }
+        if(bind_array_size <= max_array_size && actual_array_size == 0) {
+            rb_ivar_set(self, id_at_actual_array_size, INT2NUM(bind_array_size));
+        }
+    }     
+
     return rb_funcall(obj, oci8_id_set, 1, val);
 }
 
@@ -708,14 +760,20 @@
     oci8_sym_declare_stmt = ID2SYM(rb_intern("declare_stmt"));
     oci8_sym_other = ID2SYM(rb_intern("other"));
     id_at_column_metadata = rb_intern("@column_metadata");
+    id_at_actual_array_size = rb_intern("@actual_array_size");
+    id_at_max_array_size = rb_intern("@max_array_size");
+    id_each_value = rb_intern("each_value");
     id_at_names = rb_intern("@names");
     id_at_con = rb_intern("@con");
+    id_empty_p = rb_intern("empty?");
+    id_clear = rb_intern("clear");
     id_set = rb_intern("set");
 
     rb_define_private_method(cOCIStmt, "initialize", oci8_stmt_initialize, -1);
     rb_define_private_method(cOCIStmt, "__define", oci8_define_by_pos, 2);
     rb_define_private_method(cOCIStmt, "__bind", oci8_bind, 2);
-    rb_define_private_method(cOCIStmt, "__execute", oci8_stmt_execute, 0);
+    rb_define_private_method(cOCIStmt, "__execute", oci8_stmt_execute, 1);
+    rb_define_private_method(cOCIStmt, "__clearBinds", oci8_stmt_clear_binds, 0);
     rb_define_method(cOCIStmt, "fetch", oci8_stmt_fetch, 0);
     rb_define_private_method(cOCIStmt, "__paramGet", oci8_stmt_get_param, 1);
     rb_define_method(cOCIStmt, "type", oci8_stmt_get_stmt_type, 0);

Modified: trunk/ruby-oci8/lib/oci8/oci8.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/oci8.rb	2008-02-17 15:29:30 UTC (rev 245)
+++ trunk/ruby-oci8/lib/oci8/oci8.rb	2008-02-27 08:23:39 UTC (rev 246)
@@ -348,9 +348,12 @@
     # true. In contrast with OCI8#exec, it returns true even
     # though PL/SQL. Use OCI8::Cursor#[] explicitly to get bind
     # variables.
+    # 
+    # Pass a "nil" to "__execute" to specify the statement isn't 
+    # an Array DML 
     def exec(*bindvars)
       bind_params(*bindvars)
-      __execute()
+      __execute(nil)
       case type
       when :select_stmt
         define_columns()
@@ -361,6 +364,80 @@
       end
     end # exec
 
+    # Set the maximum array size for bind_param_array
+    #
+    # All the binds will be clean from cursor if instance variable max_array_size is set before
+    #
+    # Instance variable actual_array_size holds the size of the arrays users actually binds through bind_param_array
+    #  all the binding arrays are required to be the same size
+    def max_array_size=(size)
+      raise "expect positive number for max_array_size." if size.nil? && size <=0
+      __clearBinds if !@max_array_size.nil?
+      @max_array_size = size
+      @actual_array_size = nil
+    end # max_array_size=
+
+    # Bind array explicitly
+    #
+    # When key is number, it binds by position, which starts from 1.
+    # When key is string, it binds by the name of placeholder.
+    # 
+    # The max_array_size should be set before calling bind_param_array
+    #
+    # example:
+    #   cursor = conn.parse("INSERT INTO test_table VALUES (:str)")
+    #   cursor.max_array_size = 3
+    #   cursor.bind_param_array(1, ['happy', 'new', 'year'], String, 30)
+    #   cursor.exec_array
+    def bind_param_array(key, var_array, type = nil, max_item_length = nil)
+      raise "please call max_array_size= first." if @max_array_size.nil?
+      raise "expect array as input param for bind_param_array." if !var_array.nil? && !(var_array.is_a? Array) 
+      raise "the size of var_array should not be greater than max_array_size." if !var_array.nil? && var_array.size > @max_array_size
+
+      if var_array.nil? 
+        raise "all binding arrays should be the same size." unless @actual_array_size.nil? || @actual_array_size == 0
+        @actual_array_size = 0
+      else
+        raise "all binding arrays should be the same size." unless @actual_array_size.nil? || var_array.size == @actual_array_size
+        @actual_array_size = var_array.size if @actual_array_size.nil?
+      end
+      
+      param = {:value => var_array, :type => type, :length => max_item_length, :max_array_size => @max_array_size}
+      first_non_nil_elem = var_array.nil? ? nil : var_array.find{|x| x!= nil}
+      
+      if type.nil?
+        if first_non_nil_elem.nil?
+          raise "bind type is not given."
+        else
+          type = first_non_nil_elem.class
+        end
+      end
+      
+      bindclass = OCI8::BindType::Mapping[type]
+      raise "unsupported dataType: #{type}" if bindclass.nil?
+      bindobj = bindclass.create(@con, var_array, param, @max_array_size)
+      __bind(key, bindobj)
+      self
+    end # bind_param_array
+
+    # Executes the SQL statement assigned the cursor with array binding
+    def exec_array
+      raise "please call max_array_size= first." if @max_array_size.nil?
+
+      if !@actual_array_size.nil? && @actual_array_size > 0
+        __execute(@actual_array_size)
+      else
+        raise "please set non-nil values to array binding parameters"
+      end
+
+      case type
+      when :update_stmt, :delete_stmt, :insert_stmt
+        row_count
+      else
+        true
+      end
+    end # exec_array
+
     # Gets the names of select-list as array. Please use this
     # method after exec.
     def get_col_names

Modified: trunk/ruby-oci8/test/test_all.rb
===================================================================
--- trunk/ruby-oci8/test/test_all.rb	2008-02-17 15:29:30 UTC (rev 245)
+++ trunk/ruby-oci8/test/test_all.rb	2008-02-27 08:23:39 UTC (rev 246)
@@ -17,6 +17,7 @@
 require "#{srcdir}/test_datetime"
 require "#{srcdir}/test_connstr"
 require "#{srcdir}/test_metadata"
+require "#{srcdir}/test_array_dml"
 
 # Ruby/DBI
 begin

Added: trunk/ruby-oci8/test/test_array_dml.rb
===================================================================
--- trunk/ruby-oci8/test/test_array_dml.rb	                        (rev 0)
+++ trunk/ruby-oci8/test/test_array_dml.rb	2008-02-27 08:23:39 UTC (rev 246)
@@ -0,0 +1,317 @@
+require 'oci8'
+require 'test/unit'
+require './config'
+
+class TestArrayDML < Test::Unit::TestCase
+  def setup
+    @conn = get_oci8_connection
+  end
+
+  def teardown
+    @conn.logoff
+  end
+
+  # test inserting arrays with different data types
+  #   including char, varchar2, number, date and so on
+  def test_array_insert1
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (C CHAR(10) NOT NULL,
+   V VARCHAR2(20),
+   N NUMBER(10, 2),
+   D DATE,
+   INT NUMBER(30), 
+   BIGNUM NUMBER(30))
+STORAGE (
+   INITIAL 4k
+   NEXT 4k
+   MINEXTENTS 1
+   MAXEXTENTS UNLIMITED
+   PCTINCREASE 0)
+EOS
+    @conn.exec(sql)
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:C, :V, :N, :D, :INT, :BIGNUM)")
+    max_array_size = 3
+    cursor.max_array_size= max_array_size
+    
+    cursor.bind_param_array(1, nil, String)
+    cursor.bind_param_array(2, nil ,String)
+    cursor.bind_param_array(3, nil, Fixnum)
+    cursor.bind_param_array(4, nil, OraDate)    
+    cursor.bind_param_array(5, nil, Integer)
+    cursor.bind_param_array(6, nil, Bignum)
+    
+    c_arr = Array.new
+    v_arr = Array.new
+    n_arr = Array.new
+    d_arr = Array.new
+    int_arr = Array.new
+    bignum_arr = Array.new
+    
+    1.upto(30) do |i|
+      c_arr << format("%10d", i * 10)
+      v_arr << i.to_s
+      n_arr << i
+      d_arr <<  OraDate.new(2000 + i, 12, 24, 23, 59, 59)
+      int_arr << i * 11111111111
+      bignum_arr << i * 10000000000
+      
+      if i%max_array_size == 0
+        cursor[1] = c_arr
+        cursor[2] = v_arr
+        cursor[3] = n_arr
+        cursor[4] = d_arr
+        cursor[5] = int_arr
+        cursor[6] = bignum_arr
+        
+        r = cursor.exec_array
+        assert_equal(max_array_size, r)
+        c_arr.clear
+        v_arr.clear
+        n_arr.clear
+        d_arr.clear
+        int_arr.clear
+        bignum_arr.clear
+      end
+    end
+    cursor.close
+    
+    cursor = @conn.parse("SELECT * FROM test_table ORDER BY c")
+    cursor.define(5, Integer)
+    cursor.define(6, Bignum)
+    cursor.exec
+    assert_equal(["C","V","N","D","INT","BIGNUM"], cursor.get_col_names)
+    1.upto(30) do |i|
+      rv = cursor.fetch
+      assert_equal(format("%10d", i * 10), rv[0])
+      assert_equal(i.to_s, rv[1])
+      assert_equal(i, rv[2])
+      dttm = DateTime.civil(2000 + i, 12, 24, 23, 59, 59, Time.now.utc_offset.to_r/86400)
+      assert_equal(dttm, rv[3])
+      assert_equal(i * 11111111111, rv[4])
+      assert_equal(i * 10000000000, rv[5])
+    end
+    assert_nil(cursor.fetch)
+    drop_table('test_table')
+  end
+
+  # Raise error when binding arrays are not the same size
+  def test_array_insert2
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (N NUMBER(10, 2) NOT NULL,
+   V VARCHAR(20))
+EOS
+    @conn.exec(sql)
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:N, :V)")
+    max_array_size = 10
+    cursor.max_array_size = max_array_size
+    cursor.bind_param_array(1, nil, Fixnum)
+    cursor.bind_param_array(2, nil, String)
+    n_arr = Array.new
+    v_arr = Array.new
+    1.upto(max_array_size) do |i|
+      n_arr << i
+      v_arr << i.to_s if i != max_array_size
+    end
+    cursor[1] = n_arr
+    assert_raise(RuntimeError) { cursor[2] = v_arr }
+    cursor.close
+    
+    drop_table('test_table')
+  end  
+
+  # All binds are clear from cursor after calling "max_array_size=", 
+  #  in that case, you have to re-bind the array parameters 
+  #  otherwise, an error will be raised.
+  def test_array_insert3
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (N NUMBER(10, 2) NOT NULL,
+   V VARCHAR(20))
+EOS
+    @conn.exec(sql)
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:N, :V)")
+    cursor.max_array_size = 3
+    cursor.bind_param_array(1, [1, 2, 3])
+    cursor.bind_param_array(2, ['happy', 'new', 'year'])
+    assert_nothing_raised() { cursor.exec_array }
+    cursor.max_array_size = 2
+    assert_raise(RuntimeError) { cursor.exec_array }
+    drop_table('test_table')
+  end
+
+  # The size of binding arrays are not required to be same as max_array_size. The 
+  #   only requirement is that they should be the same size, and the size will be 
+  #   used as execution count for OCIStmtExecute.
+  def test_array_insert4
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (N NUMBER(10, 2) NOT NULL,
+   V VARCHAR(20))
+EOS
+    @conn.exec(sql)
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:N, :V)")
+    max_array_size = 4
+    cursor.max_array_size = max_array_size
+    cursor.bind_param_array(1, nil, Fixnum)
+    cursor.bind_param_array(2, nil, String)
+    n_arr = Array.new
+    v_arr = Array.new
+    1.upto( max_array_size - 1 ) do |i|
+      n_arr << i
+      v_arr << i.to_s
+    end
+    cursor[1] = n_arr
+    cursor[2] = v_arr
+    assert_nothing_raised() { cursor.exec_array }
+    cursor.close
+
+    cursor = @conn.parse("SELECT * FROM test_table ORDER BY N")
+    cursor.exec
+    1.upto( max_array_size - 1 ) do |i|
+      rv = cursor.fetch
+      assert_equal(i, rv[0])
+      assert_equal(i.to_s, rv[1])
+    end  
+    assert_nil(cursor.fetch)
+    cursor.close
+    drop_table('test_table')    
+  end
+
+  # Inserting "nil" elements with array dml raises an error
+  def test_array_insert5
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (N NUMBER(10, 2),
+   V VARCHAR(20))
+EOS
+    @conn.exec(sql)
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:N, :V)")
+    max_array_size = 3
+    cursor.max_array_size = max_array_size
+    cursor.bind_param_array(1, nil, Fixnum)
+    cursor.bind_param_array(2, nil, String)
+    assert_raise(RuntimeError) { cursor.exec_array }
+    cursor.close
+    drop_table('test_table')
+  end
+
+  # delete with array bindings
+  def test_array_delete
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (N NUMBER(10, 2),
+   V VARCHAR(20))
+EOS
+    @conn.exec(sql)    
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:N, :V)")
+    max_array_size = 10
+    cursor.max_array_size = max_array_size
+    n_arr = Array.new
+    v_arr = Array.new
+    1.upto( max_array_size) do |i|
+      n_arr << i
+      v_arr << i.to_s
+    end    
+    cursor.bind_param_array(1, nil, Fixnum)
+    cursor.bind_param_array(2, nil, String)
+    cursor[1] = n_arr
+    cursor[2] = v_arr
+    cursor.exec_array
+    cursor.close
+
+    cursor = @conn.parse("DELETE FROM test_table WHERE N=:N")
+    cursor.max_array_size = max_array_size
+    delete_arr = Array.new
+    1.upto(max_array_size) do |i|
+      if i%2 == 0
+        delete_arr << i
+      end  
+    end
+    cursor.bind_param_array(1, nil, Fixnum)
+    cursor[1] = delete_arr
+    cursor.exec_array
+    cursor.close
+    
+    cursor = @conn.parse("SELECT * FROM test_table ORDER BY N")
+    cursor.exec
+    1.upto( max_array_size ) do |i|
+      if i%2 != 0
+        rv = cursor.fetch
+        assert_equal(rv[0], i)
+        assert_equal(rv[1], i.to_s)
+      end
+    end  
+    assert_nil(cursor.fetch)    
+    cursor.close
+    
+    drop_table('test_table')
+  end
+
+  # update with array bindings
+  def test_array_update
+    drop_table('test_table')
+    sql = <<-EOS
+CREATE TABLE test_table
+  (N NUMBER(10, 2),
+   V VARCHAR(20))
+EOS
+    @conn.exec(sql)    
+    cursor = @conn.parse("INSERT INTO test_table VALUES (:N, :V)")
+    max_array_size = 10
+    cursor.max_array_size = max_array_size
+    n_arr = Array.new
+    v_arr = Array.new
+    1.upto( max_array_size) do |i|
+      n_arr << i
+      v_arr << i.to_s
+    end    
+    cursor.bind_param_array(1, nil, Fixnum)
+    cursor.bind_param_array(2, nil, String)
+    cursor[1] = n_arr
+    cursor[2] = v_arr
+    cursor.exec_array
+    cursor.close
+
+    cursor = @conn.parse("UPDATE test_table SET V=:V WHERE N=:N")
+    cursor.max_array_size = max_array_size
+    update_arr = Array.new
+    update_v_arr = Array.new
+    1.upto(max_array_size) do |i|
+      if i%2 == 0
+        update_arr << i
+        update_v_arr << (i * 10).to_s
+      end  
+    end
+    cursor.bind_param_array(1, nil, String)
+    cursor.bind_param_array(2, nil, Fixnum)
+    cursor[1] = update_v_arr
+    cursor[2] = update_arr
+    cursor.exec_array
+    cursor.close
+    
+    cursor = @conn.parse("SELECT * FROM test_table ORDER BY N")
+    cursor.exec
+    1.upto( max_array_size ) do |i|
+      rv = cursor.fetch      
+      if i%2 != 0
+        assert_equal(rv[0], i)
+        assert_equal(rv[1], i.to_s)
+      else
+        assert_equal(rv[0], i)
+        assert_equal(rv[1], (i * 10).to_s)
+      end
+    end  
+    assert_nil(cursor.fetch)    
+
+    cursor.close
+    drop_table('test_table')
+  end
+end




More information about the ruby-oci8-commit mailing list