<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -111,74 +111,118 @@ module PLSQL
         [DateTime, nil]
       when &quot;TIMESTAMP&quot;
         [Time, nil]
-      when &quot;TABLE&quot;
+      when &quot;TABLE&quot;, &quot;OBJECT&quot;
         # create Ruby class for collection
-        klass = Class.new(OCI8::Object::Base)
-        klass.set_typename metadata[:sql_type_name]
+        klass = OCI8::Object::Base.get_class_by_typename(metadata[:sql_type_name])
+        unless klass
+          klass = Class.new(OCI8::Object::Base)
+          klass.set_typename metadata[:sql_type_name]
+        end
         [klass, nil]
       else
         [String, 32767]
       end
     end
 
-    def ruby_value_to_ora_value(val, type)
-      if type == OraNumber
+    def ruby_value_to_ora_value(value, type=nil)
+      type ||= value.class
+      case type.to_s.to_sym
+      when :Fixnum, :BigDecimal, :String
+        value
+      when :OraNumber
         # pass parameters as OraNumber to avoid rounding errors
-        case val
+        case value
         when Bignum
-          OraNumber.new(val.to_s)
+          OraNumber.new(value.to_s)
         when BigDecimal
-          OraNumber.new(val.to_s('F'))
+          OraNumber.new(value.to_s('F'))
         when TrueClass
           OraNumber.new(1)
         when FalseClass
           OraNumber.new(0)
         else
-          val
+          value
         end
-      elsif type == DateTime
-        case val
+      when :DateTime
+        case value
         when Time
-          ::DateTime.civil(val.year, val.month, val.day, val.hour, val.min, val.sec, Rational(val.utc_offset, 86400))
+          ::DateTime.civil(value.year, value.month, value.day, value.hour, value.min, value.sec, Rational(value.utc_offset, 86400))
         when DateTime
-          val
+          value
         when Date
-          ::DateTime.civil(val.year, val.month, val.day, 0, 0, 0, 0)
+          ::DateTime.civil(value.year, value.month, value.day, 0, 0, 0, 0)
         else
-          val
+          value
         end
-      elsif type == OCI8::CLOB
-        # ruby-oci8 cannot create CLOB from ''
-        val = nil if val == ''
-        OCI8::CLOB.new(raw_oci_connection, val)
-      elsif type == OCI8::BLOB
-        # ruby-oci8 cannot create BLOB from ''
-        val = nil if val == ''
-        OCI8::BLOB.new(raw_oci_connection, val)
+      when :&quot;OCI8::CLOB&quot;, :&quot;OCI8::BLOB&quot;
+        # ruby-oci8 cannot create CLOB/BLOB from ''
+        value = nil if value == ''
+        type.new(raw_oci_connection, value)
       else
-        val
+        # collections and object types
+        if type.superclass == OCI8::Object::Base
+          return nil if value.nil?
+          tdo = raw_oci_connection.get_tdo_by_class(type)
+          if tdo.is_collection?
+            raise ArgumentError, &quot;You should pass Array value for collection type parameter&quot; unless value.is_a?(Array)
+            elem_list = value.map do |elem|
+              if (attr_tdo = tdo.coll_attr.typeinfo)
+                attr_type, attr_length = plsql_to_ruby_data_type(:data_type =&gt; 'OBJECT', :sql_type_name =&gt; attr_tdo.typename)
+              else
+                attr_type = elem.class
+              end
+              ruby_value_to_ora_value(elem, attr_type)
+            end
+            # construct collection value
+            # TODO: change setting instance variable to appropriate ruby-oci8 method call when available
+            collection = type.new(raw_oci_connection)
+            collection.instance_variable_set('@attributes', elem_list)
+            collection
+          else # object type
+            raise ArgumentError, &quot;You should pass Hash value for object type parameter&quot; unless value.is_a?(Hash)
+            object_attrs = value.dup
+            object_attrs.keys.each do |key|
+              raise ArgumentError, &quot;Wrong object type field passed to PL/SQL procedure&quot; unless (attr = tdo.attr_getters[key])
+              case attr.datatype
+              when OCI8::TDO::ATTR_NAMED_TYPE, OCI8::TDO::ATTR_NAMED_COLLECTION
+                # nested object type or collection
+                attr_type, attr_length = plsql_to_ruby_data_type(:data_type =&gt; 'OBJECT', :sql_type_name =&gt; attr.typeinfo.typename)
+                object_attrs[key] = ruby_value_to_ora_value(object_attrs[key], attr_type)
+              end
+            end
+            type.new(raw_oci_connection, object_attrs)
+          end
+        # all other cases
+        else
+          value
+        end
       end
     end
 
-    def ora_value_to_ruby_value(val)
-      case val
+    def ora_value_to_ruby_value(value)
+      case value
       when Float, OraNumber
-        ora_number_to_ruby_number(val)
+        ora_number_to_ruby_number(value)
       when DateTime, OraDate
-        ora_date_to_ruby_date(val)
+        ora_date_to_ruby_date(value)
       when OCI8::LOB
-        if val.available?
-          val.rewind
-          val.read
+        if value.available?
+          value.rewind
+          value.read
         else
           nil
         end
       when OCI8::Object::Base
-        if val.instance_variable_get(:@attributes).is_a? Array
-          val.to_ary.map{|e| ora_value_to_ruby_value(e)}
+        tdo = raw_oci_connection.get_tdo_by_class(value.class)
+        if tdo.is_collection?
+          value.to_ary.map{|e| ora_value_to_ruby_value(e)}
+        else # object type
+          Hash[tdo.attributes.map do |attr|
+            [attr.name, ora_value_to_ruby_value(value.instance_variable_get(:@attributes)[attr.name])]
+          end]
         end
       else
-        val
+        value
       end
     end
 </diff>
      <filename>lib/plsql/oci_connection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -143,7 +143,7 @@ module PLSQL
       end
     end
 
-    PLSQL_COMPOSITE_TYPES = ['PL/SQL RECORD', 'TABLE', 'OBJECT', 'REF CURSOR'].freeze
+    PLSQL_COMPOSITE_TYPES = ['PL/SQL RECORD', 'TABLE'].freeze
     def composite_type?(data_type)
       PLSQL_COMPOSITE_TYPES.include? data_type
     end</diff>
      <filename>lib/plsql/procedure.rb</filename>
    </modified>
    <modified>
      <diff>@@ -72,11 +72,11 @@ module PLSQL
 
       # Named arguments
       if args.size == 1 &amp;&amp; args[0].is_a?(Hash) &amp;&amp;
-            # do not use named arguments if procedure has just one PL/SQL record argument -
+            # do not use named arguments if procedure has just one PL/SQL record or object type argument -
             # in that case passed Hash should be used as value for this PL/SQL record argument
             # (which will be processed in sequential arguments bracnh)
             !(argument_list.size == 1 &amp;&amp;
-              arguments[(only_argument=argument_list[0])][:data_type] == 'PL/SQL RECORD' &amp;&amp;
+              ['PL/SQL RECORD','OBJECT'].include?(arguments[(only_argument=argument_list[0])][:data_type]) &amp;&amp;
               args[0].keys != [only_argument])
         @call_sql &lt;&lt; args[0].map do |arg, value|
           &quot;#{arg} =&gt; &quot; &lt;&lt; add_argument(arg, value)</diff>
      <filename>lib/plsql/procedure_call.rb</filename>
    </modified>
    <modified>
      <diff>@@ -681,6 +681,113 @@ describe &quot;Function with boolean parameters&quot; do
 
 end
 
+describe &quot;Function with object type parameter&quot; do
+
+  before(:all) do
+    plsql.connection = get_connection
+    plsql.connection.exec &quot;DROP TYPE t_employee&quot; rescue nil
+    plsql.connection.exec &quot;DROP TYPE t_phones&quot; rescue nil
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE TYPE t_address AS OBJECT (
+        street    VARCHAR2(50),
+        city      VARCHAR2(50),
+        country   VARCHAR2(50)
+      )
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE TYPE t_phone AS OBJECT (
+        type            VARCHAR2(10),
+        phone_number    VARCHAR2(50)
+      )
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE TYPE t_phones AS TABLE OF T_PHONE
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE TYPE t_employee AS OBJECT (
+        employee_id   NUMBER(15),
+        first_name    VARCHAR2(50),
+        last_name     VARCHAR2(50),
+        hire_date     DATE,
+        address       t_address,
+        phones        t_phones
+      )
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE FUNCTION test_full_name (p_employee t_employee)
+        RETURN VARCHAR2
+      IS
+      BEGIN
+        RETURN p_employee.first_name || ' ' || p_employee.last_name;
+      END;
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE FUNCTION test_employee_object (p_employee t_employee)
+        RETURN t_employee
+      IS
+      BEGIN
+        RETURN p_employee;
+      END;
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE FUNCTION test_employee_object2 (p_employee t_employee, x_employee OUT t_employee)
+        RETURN t_employee
+      IS
+      BEGIN
+        x_employee := p_employee;
+        RETURN p_employee;
+      END;
+    SQL
+    @p_employee = {
+      :employee_id =&gt; 1,
+      :first_name =&gt; 'First',
+      :last_name =&gt; 'Last',
+      :hire_date =&gt; Time.local(2000,01,31),
+      :address =&gt; {:street =&gt; 'Main street 1', :city =&gt; 'Riga', :country =&gt; 'Latvia'},
+      :phones =&gt; [{:type =&gt; 'mobile', :phone_number =&gt; '123456'}, {:type =&gt; 'home', :phone_number =&gt; '654321'}]
+    }
+  end
+
+  after(:all) do
+    plsql.connection.exec &quot;DROP FUNCTION test_full_name&quot;
+    plsql.connection.exec &quot;DROP FUNCTION test_employee_object&quot;
+    plsql.connection.exec &quot;DROP FUNCTION test_employee_object2&quot;
+    plsql.connection.exec &quot;DROP TYPE t_employee&quot;
+    plsql.connection.exec &quot;DROP TYPE t_address&quot;
+    plsql.connection.exec &quot;DROP TYPE t_phones&quot;
+    plsql.connection.exec &quot;DROP TYPE t_phone&quot;
+    plsql.logoff
+  end
+
+  it &quot;should find existing function&quot; do
+    PLSQL::Procedure.find(plsql, :test_full_name).should_not be_nil
+  end
+
+  it &quot;should execute function with named parameter and return correct value&quot; do
+    plsql.test_full_name(:p_employee =&gt; @p_employee).should == 'First Last'
+  end
+
+  it &quot;should execute function with sequential parameter and return correct value&quot; do
+    plsql.test_full_name(@p_employee).should == 'First Last'
+  end
+
+  it &quot;should raise error if wrong field name is passed for record parameter&quot; do
+    lambda do
+      plsql.test_full_name(@p_employee.merge :xxx =&gt; 'xxx')
+    end.should raise_error(ArgumentError)
+  end
+
+  it &quot;should return object type return value&quot; do
+    plsql.test_employee_object(@p_employee).should == @p_employee
+  end
+
+  it &quot;should return object type return value and output object type parameter value&quot; do
+    plsql.test_employee_object2(@p_employee, nil).should == [@p_employee, {:x_employee =&gt; @p_employee}]
+  end
+
+end
+
+
 describe &quot;Function with table parameter&quot; do
 
   before(:all) do
@@ -773,6 +880,26 @@ describe &quot;Function with table parameter&quot; do
         END;
       END;
     SQL
+
+    # Array of objects
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE TYPE t_phone AS OBJECT (
+        type            VARCHAR2(10),
+        phone_number    VARCHAR2(50)
+      )
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE TYPE t_phones AS TABLE OF T_PHONE
+    SQL
+    plsql.connection.exec &lt;&lt;-SQL
+      CREATE OR REPLACE FUNCTION test_copy_objects(p_phones IN t_phones, x_phones OUT t_phones)
+        RETURN t_phones
+      IS
+      BEGIN
+        x_phones := p_phones;
+        RETURN x_phones;
+      END;
+    SQL
   end
 
   after(:all) do
@@ -782,6 +909,8 @@ describe &quot;Function with table parameter&quot; do
     plsql.connection.exec &quot;DROP PACKAGE test_collections&quot;
     plsql.connection.exec &quot;DROP TYPE t_numbers&quot;
     plsql.connection.exec &quot;DROP TYPE t_strings&quot;
+    plsql.connection.exec &quot;DROP TYPE t_phones&quot;
+    plsql.connection.exec &quot;DROP TYPE t_phone&quot;
     plsql.logoff
   end
 
@@ -798,7 +927,6 @@ describe &quot;Function with table parameter&quot; do
   end
 
   it &quot;should execute function with string array and return string array output parameter&quot; do
-    pending &quot;ruby-oci8 gives segmentation fault&quot; if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
     strings = ['1','2','3','4']
     plsql.test_copy_strings(strings).should == [strings, {:x_strings =&gt; strings}]
   end
@@ -809,6 +937,16 @@ describe &quot;Function with table parameter&quot; do
     end.should raise_error(ArgumentError)
   end
 
+  it &quot;should execute function with object array and return object array output parameter&quot; do
+    phones = [{:type =&gt; 'mobile', :phone_number =&gt; '123456'}, {:type =&gt; 'home', :phone_number =&gt; '654321'}]
+    plsql.test_copy_objects(phones).should == [phones, {:x_phones =&gt; phones}]
+  end
+
+  it &quot;should execute function with empty object array&quot; do
+    phones = []
+    plsql.test_copy_objects(phones).should == [phones, {:x_phones =&gt; phones}]
+  end
+
 end
 
 describe &quot;Synonym to function&quot; do</diff>
      <filename>spec/plsql/procedure_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,7 +8,7 @@ gem &quot;activerecord-oracle_enhanced-adapter&quot;
 
 if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
   # gem &quot;ruby-oci8&quot;, &quot;=2.0.2&quot;
-  gem &quot;ruby-oci8&quot;, &quot;=2.0.3&quot;
+  gem &quot;ruby-oci8&quot;, &quot;&gt;=2.0.3&quot;
 end
 
 $:.unshift(File.dirname(__FILE__) + '/../lib')</diff>
      <filename>spec/spec_helper.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>b9894be4a5aea44da568b0e4d7c3154591d804fe</id>
    </parent>
  </parents>
  <author>
    <name>Raimonds Simanovskis</name>
    <email>raimonds.simanovskis@gmail.com</email>
  </author>
  <url>http://github.com/rsim/ruby-plsql/commit/7c6fd0d7ae9a4f43a78d21931530bf2909cba706</url>
  <id>7c6fd0d7ae9a4f43a78d21931530bf2909cba706</id>
  <committed-date>2009-11-04T08:36:23-08:00</committed-date>
  <authored-date>2009-11-04T08:36:23-08:00</authored-date>
  <message>support for object type and collection of object type parameters when using MRI / ruby-oci8</message>
  <tree>47b1d8181e72142c354bda43c4b75b741c74444b</tree>
  <committer>
    <name>Raimonds Simanovskis</name>
    <email>raimonds.simanovskis@gmail.com</email>
  </committer>
</commit>
