Commit 4276bef1 authored by Kubo Takehiro's avatar Kubo Takehiro
Browse files

Check object type from each object instance when an Oracle object is got from a bind parameter.

(github issue #119)
parent e5c77ea2
2016-03-27 Kubo Takehiro <kubo@jiubao.org>
* ext/oci8/apiwrap.yml, ext/oci8/metadata.c, ext/oci8/object.c,
ext/oci8/oci8.h, lib/oci8/object.rb, test/setup_test_object.sql,
test/test_object.rb: Check object type from each object instance
when an Oracle object is got from a bind parameter.
(github issue #119)
2016-03-27 Kubo Takehiro <kubo@jiubao.org>
* lib/ruby-oci8.rb: Added for 'Bundler.require'.
(github issue #114)
......
......@@ -670,6 +670,15 @@ OCIObjectGetInd:
- dvoid *instance
- dvoid **null_struct
# round trip: 0
OCIObjectGetTypeRef:
:version: 800
:args:
- OCIEnv *env
- OCIError *err
- dvoid *instance
- OCIRef *type_ref
# round trip: 0
OCIObjectNew:
:version: 800
......
......@@ -2,7 +2,7 @@
/*
* metadata.c
*
* Copyright (C) 2006-2015 Kubo Takehiro <kubo@jiubao.org>
* Copyright (C) 2006-2016 Kubo Takehiro <kubo@jiubao.org>
*
* implement private methods of classes in OCI8::Metadata module.
*
......@@ -145,7 +145,7 @@ static VALUE metadata_is_implicit_p(VALUE self)
return md->is_implicit ? Qtrue : Qfalse;
}
static VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public)
VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public)
{
oci8_svcctx_t *svcctx = oci8_get_svcctx(self);
OCIParam *parmhp;
......@@ -274,4 +274,6 @@ void Init_oci8_metadata(VALUE cOCI8)
rb_define_private_method(cOCI8, "__describe", oci8_describe, 3);
rb_define_private_method(cOCI8MetadataBase, "__type_metadata", metadata_get_type_metadata, 1);
rb_define_method(cOCI8MetadataBase, "tdo_id", metadata_get_tdo_id, 0);
rb_define_class_under(mOCI8Metadata, "Type", cOCI8MetadataBase);
}
/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
/*
* Copyright (C) 2002-2014 Kubo Takehiro <kubo@jiubao.org>
* Copyright (C) 2002-2016 Kubo Takehiro <kubo@jiubao.org>
*/
/*
......@@ -20,8 +20,11 @@ static VALUE cOCI8TDO;
static VALUE cOCI8NamedType;
static VALUE cOCI8NamedCollection;
static VALUE cOCI8BindNamedType;
static VALUE cOCI8MetadataType;
static ID id_to_value;
static ID id_set_attributes;
static ID id_get_tdo_by_metadata;
static ID id_at_is_final_type;
#define TO_TDO(obj) ((oci8_base_t *)oci8_check_typeddata((obj), &oci8_tdo_data_type, 1))
#define CHECK_TDO(obj) ((void)oci8_check_typeddata((obj), &oci8_tdo_data_type, 1))
......@@ -137,10 +140,49 @@ static VALUE oci8_named_type_initialize(VALUE self)
return Qnil;
}
typedef struct get_tdo_by_instance_arg {
VALUE svc;
void *instance;
void *typeref;
} get_tdo_by_instance_arg_t;
static VALUE get_tdo_by_instance(VALUE data)
{
get_tdo_by_instance_arg_t *arg = (get_tdo_by_instance_arg_t *)data;
VALUE metadata;
chkerr(OCIObjectGetTypeRef(oci8_envhp, oci8_errhp, arg->instance, arg->typeref));
metadata = oci8_do_describe(arg->svc, arg->typeref, 0, OCI_OTYPE_REF, cOCI8MetadataType, Qfalse);
return rb_funcall(arg->svc, id_get_tdo_by_metadata, 1, metadata);
}
static VALUE oci8_named_type_tdo(VALUE self)
{
oci8_named_type_t *obj = DATA_PTR(self);
return obj->tdo;
VALUE tdo = obj->tdo;
if (!RTEST(rb_ivar_get(tdo, id_at_is_final_type))) {
/* The type of the instance may be a subtype. */
oci8_base_t *svcctx;
get_tdo_by_instance_arg_t arg;
int state = 0;
/* search svcctx */
svcctx = DATA_PTR(obj->tdo);
while (svcctx->type != OCI_HTYPE_SVCCTX) {
svcctx = svcctx->parent;
}
arg.svc = svcctx->self;
arg.instance = *obj->instancep;
chkerr(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_REF, NULL, NULL, OCI_DURATION_DEFAULT, TRUE, &arg.typeref));
tdo = rb_protect(get_tdo_by_instance, (VALUE)&arg, &state);
chkerr(OCIObjectFree(oci8_envhp, oci8_errhp, arg.typeref, OCI_OBJECTFREE_FORCE));
if (state != 0) {
rb_jump_tag(state);
}
}
return tdo;
}
static void oci8_named_type_check_offset(VALUE self, VALUE val_offset, VALUE ind_offset, size_t val_size, void **instancep, OCIInd **indp)
......@@ -754,8 +796,11 @@ static VALUE bind_named_type_alloc(VALUE klass)
void Init_oci_object(VALUE cOCI8)
{
cOCI8MetadataType = rb_eval_string("OCI8::Metadata::Type");
id_to_value = rb_intern("to_value");
id_set_attributes = rb_intern("attributes=");
id_get_tdo_by_metadata = rb_intern("get_tdo_by_metadata");
id_at_is_final_type = rb_intern("@is_final_type");
/* OCI8::TDO */
cOCI8TDO = oci8_define_class_under(cOCI8, "TDO", &oci8_tdo_data_type, oci8_tdo_alloc);
......
......@@ -2,7 +2,7 @@
/*
* oci8.h - part of ruby-oci8
*
* Copyright (C) 2002-2015 Kubo Takehiro <kubo@jiubao.org>
* Copyright (C) 2002-2016 Kubo Takehiro <kubo@jiubao.org>
*/
#ifndef _RUBY_OCI_H_
#define _RUBY_OCI_H_ 1
......@@ -442,6 +442,7 @@ void Init_oci8_bind(VALUE cOCI8BindTypeBase);
/* metadata.c */
extern const oci8_handle_data_type_t oci8_metadata_base_data_type;
VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public);
void Init_oci8_metadata(VALUE cOCI8);
VALUE oci8_metadata_create(OCIParam *parmhp, VALUE svc, VALUE parent);
......
......@@ -383,8 +383,10 @@ EOS
case metadata.typecode
when :named_type
@is_final_type = metadata.is_final_type?
initialize_named_type(con, metadata)
when :named_collection
@is_final_type = true
initialize_named_collection(con, metadata)
end
end
......
......@@ -10,6 +10,10 @@ drop type rb_test_obj_elem_array
/
drop type rb_test_obj_elem
/
drop type rb_test_obj_sub
/
drop type rb_test_obj_base
/
create type rb_test_obj_elem as object (
x integer,
y integer
......@@ -116,7 +120,7 @@ create or replace type body rb_test_obj is
static function test_object_version return integer is
begin
return 2;
return 3;
end;
static function class_func(n number) return rb_test_obj is
......@@ -167,5 +171,21 @@ begin
end loop;
end;
/
commit
create type rb_test_obj_base as object (
id varchar2(30)
) not final
/
create type rb_test_obj_sub under rb_test_obj_base (
subid varchar2(30)
) final
/
create or replace function rb_test_obj_get_object(get_base integer) return rb_test_obj_base is
begin
if get_base = 0 then
return rb_test_obj_base('base');
else
return rb_test_obj_sub('sub', 'subid');
end if;
end;
/
......@@ -22,7 +22,7 @@ begin
begin
version = RbTestObj.test_object_version(conn)
error_message = "Invalid test object version" if version != 2
error_message = "Invalid test object version" if version != 3
rescue NoMethodError
raise unless $!.to_s.include?('test_object_version')
error_message = "rb_test_obj.test_object_version is not declared."
......@@ -48,6 +48,12 @@ EOS
class RbTestIntArray < OCI8::Object::Base
end
class RbTestObjBase < OCI8::Object::Base
end
class RbTestObjSub < RbTestObjBase
end
class TestObj1 < Minitest::Test
Delta = 0.00001
......@@ -456,4 +462,23 @@ EOS
assert_equal(ary ? ary[2] : nil, csr[:out3])
end
end
def test_get_subtype
csr = @conn.parse("BEGIN :result := rb_test_obj_get_object(:1); END;")
csr.bind_param(1, nil, RbTestObjBase)
csr.bind_param(2, nil, Integer)
csr[2] = 0
csr.exec
val = csr[1]
assert_instance_of(RbTestObjBase, val)
assert_equal(val.id, 'base')
csr[2] = 1
csr.exec
val = csr[1]
assert_instance_of(RbTestObjSub, val)
assert_equal(val.id, 'sub')
assert_equal(val.subid, 'subid')
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment