ruby-oci8 2.2.1 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +1 -6
- data/ChangeLog +98 -0
- data/NEWS +48 -1
- data/README.md +7 -0
- data/dist-files +4 -0
- data/docs/bind-array-to-in_cond.md +38 -0
- data/docs/conflicts-local-connections-and-processes.md +95 -0
- data/docs/report-installation-issue.md +8 -8
- data/docs/timeout-parameters.md +91 -0
- data/ext/oci8/apiwrap.yml +9 -0
- data/ext/oci8/extconf.rb +1 -0
- data/ext/oci8/metadata.c +4 -2
- data/ext/oci8/object.c +47 -2
- data/ext/oci8/oci8.h +2 -1
- data/ext/oci8/oraconf.rb +23 -12
- data/ext/oci8/plthook_win32.c +7 -1
- data/lib/oci8/connection_pool.rb +74 -3
- data/lib/oci8/cursor.rb +13 -6
- data/lib/oci8/object.rb +4 -1
- data/lib/oci8/oci8.rb +157 -21
- data/lib/oci8/ocihandle.rb +0 -16
- data/lib/oci8/properties.rb +33 -0
- data/lib/oci8/version.rb +1 -1
- data/lib/ruby-oci8.rb +4 -0
- data/ruby-oci8.gemspec +1 -1
- data/test/setup_test_object.sql +22 -2
- data/test/test_all.rb +1 -0
- data/test/test_connstr.rb +126 -0
- data/test/test_object.rb +26 -1
- metadata +8 -4
data/ext/oci8/apiwrap.yml
CHANGED
@@ -670,6 +670,15 @@ OCIObjectGetInd:
|
|
670
670
|
- dvoid *instance
|
671
671
|
- dvoid **null_struct
|
672
672
|
|
673
|
+
# round trip: 0
|
674
|
+
OCIObjectGetTypeRef:
|
675
|
+
:version: 800
|
676
|
+
:args:
|
677
|
+
- OCIEnv *env
|
678
|
+
- OCIError *err
|
679
|
+
- dvoid *instance
|
680
|
+
- OCIRef *type_ref
|
681
|
+
|
673
682
|
# round trip: 0
|
674
683
|
OCIObjectNew:
|
675
684
|
:version: 800
|
data/ext/oci8/extconf.rb
CHANGED
@@ -159,6 +159,7 @@ if xsystem(cc_command("").gsub(CONFTEST_C, File.dirname(__FILE__) + "/" + plthoo
|
|
159
159
|
$objs << plthook_src.gsub(/\.c$/, '.o')
|
160
160
|
$objs << "hook_funcs.o"
|
161
161
|
$defs << "-DHAVE_PLTHOOK"
|
162
|
+
have_library('dbghelp', 'ImageDirectoryEntryToData', ['windows.h', 'dbghelp.h']) if RUBY_PLATFORM =~ /cygwin/
|
162
163
|
else
|
163
164
|
puts "no"
|
164
165
|
end
|
data/ext/oci8/metadata.c
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
/*
|
3
3
|
* metadata.c
|
4
4
|
*
|
5
|
-
* Copyright (C) 2006-
|
5
|
+
* Copyright (C) 2006-2016 Kubo Takehiro <kubo@jiubao.org>
|
6
6
|
*
|
7
7
|
* implement private methods of classes in OCI8::Metadata module.
|
8
8
|
*
|
@@ -145,7 +145,7 @@ static VALUE metadata_is_implicit_p(VALUE self)
|
|
145
145
|
return md->is_implicit ? Qtrue : Qfalse;
|
146
146
|
}
|
147
147
|
|
148
|
-
|
148
|
+
VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public)
|
149
149
|
{
|
150
150
|
oci8_svcctx_t *svcctx = oci8_get_svcctx(self);
|
151
151
|
OCIParam *parmhp;
|
@@ -274,4 +274,6 @@ void Init_oci8_metadata(VALUE cOCI8)
|
|
274
274
|
rb_define_private_method(cOCI8, "__describe", oci8_describe, 3);
|
275
275
|
rb_define_private_method(cOCI8MetadataBase, "__type_metadata", metadata_get_type_metadata, 1);
|
276
276
|
rb_define_method(cOCI8MetadataBase, "tdo_id", metadata_get_tdo_id, 0);
|
277
|
+
|
278
|
+
rb_define_class_under(mOCI8Metadata, "Type", cOCI8MetadataBase);
|
277
279
|
}
|
data/ext/oci8/object.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
|
2
2
|
/*
|
3
|
-
* Copyright (C) 2002-
|
3
|
+
* Copyright (C) 2002-2016 Kubo Takehiro <kubo@jiubao.org>
|
4
4
|
*/
|
5
5
|
|
6
6
|
/*
|
@@ -20,8 +20,11 @@ static VALUE cOCI8TDO;
|
|
20
20
|
static VALUE cOCI8NamedType;
|
21
21
|
static VALUE cOCI8NamedCollection;
|
22
22
|
static VALUE cOCI8BindNamedType;
|
23
|
+
static VALUE cOCI8MetadataType;
|
23
24
|
static ID id_to_value;
|
24
25
|
static ID id_set_attributes;
|
26
|
+
static ID id_get_tdo_by_metadata;
|
27
|
+
static ID id_at_is_final_type;
|
25
28
|
|
26
29
|
#define TO_TDO(obj) ((oci8_base_t *)oci8_check_typeddata((obj), &oci8_tdo_data_type, 1))
|
27
30
|
#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)
|
|
137
140
|
return Qnil;
|
138
141
|
}
|
139
142
|
|
143
|
+
typedef struct get_tdo_by_instance_arg {
|
144
|
+
VALUE svc;
|
145
|
+
void *instance;
|
146
|
+
void *typeref;
|
147
|
+
} get_tdo_by_instance_arg_t;
|
148
|
+
|
149
|
+
static VALUE get_tdo_by_instance(VALUE data)
|
150
|
+
{
|
151
|
+
get_tdo_by_instance_arg_t *arg = (get_tdo_by_instance_arg_t *)data;
|
152
|
+
VALUE metadata;
|
153
|
+
|
154
|
+
chkerr(OCIObjectGetTypeRef(oci8_envhp, oci8_errhp, arg->instance, arg->typeref));
|
155
|
+
metadata = oci8_do_describe(arg->svc, arg->typeref, 0, OCI_OTYPE_REF, cOCI8MetadataType, Qfalse);
|
156
|
+
return rb_funcall(arg->svc, id_get_tdo_by_metadata, 1, metadata);
|
157
|
+
}
|
158
|
+
|
140
159
|
static VALUE oci8_named_type_tdo(VALUE self)
|
141
160
|
{
|
142
161
|
oci8_named_type_t *obj = DATA_PTR(self);
|
143
|
-
|
162
|
+
VALUE tdo = obj->tdo;
|
163
|
+
|
164
|
+
if (!RTEST(rb_ivar_get(tdo, id_at_is_final_type))) {
|
165
|
+
/* The type of the instance may be a subtype. */
|
166
|
+
oci8_base_t *svcctx;
|
167
|
+
get_tdo_by_instance_arg_t arg;
|
168
|
+
int state = 0;
|
169
|
+
|
170
|
+
/* search svcctx */
|
171
|
+
svcctx = DATA_PTR(obj->tdo);
|
172
|
+
while (svcctx->type != OCI_HTYPE_SVCCTX) {
|
173
|
+
svcctx = svcctx->parent;
|
174
|
+
}
|
175
|
+
|
176
|
+
arg.svc = svcctx->self;
|
177
|
+
arg.instance = *obj->instancep;
|
178
|
+
chkerr(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_REF, NULL, NULL, OCI_DURATION_DEFAULT, TRUE, &arg.typeref));
|
179
|
+
tdo = rb_protect(get_tdo_by_instance, (VALUE)&arg, &state);
|
180
|
+
chkerr(OCIObjectFree(oci8_envhp, oci8_errhp, arg.typeref, OCI_OBJECTFREE_FORCE));
|
181
|
+
if (state != 0) {
|
182
|
+
rb_jump_tag(state);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
return tdo;
|
144
186
|
}
|
145
187
|
|
146
188
|
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)
|
|
754
796
|
|
755
797
|
void Init_oci_object(VALUE cOCI8)
|
756
798
|
{
|
799
|
+
cOCI8MetadataType = rb_eval_string("OCI8::Metadata::Type");
|
757
800
|
id_to_value = rb_intern("to_value");
|
758
801
|
id_set_attributes = rb_intern("attributes=");
|
802
|
+
id_get_tdo_by_metadata = rb_intern("get_tdo_by_metadata");
|
803
|
+
id_at_is_final_type = rb_intern("@is_final_type");
|
759
804
|
|
760
805
|
/* OCI8::TDO */
|
761
806
|
cOCI8TDO = oci8_define_class_under(cOCI8, "TDO", &oci8_tdo_data_type, oci8_tdo_alloc);
|
data/ext/oci8/oci8.h
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
/*
|
3
3
|
* oci8.h - part of ruby-oci8
|
4
4
|
*
|
5
|
-
* Copyright (C) 2002-
|
5
|
+
* Copyright (C) 2002-2016 Kubo Takehiro <kubo@jiubao.org>
|
6
6
|
*/
|
7
7
|
#ifndef _RUBY_OCI_H_
|
8
8
|
#define _RUBY_OCI_H_ 1
|
@@ -442,6 +442,7 @@ void Init_oci8_bind(VALUE cOCI8BindTypeBase);
|
|
442
442
|
|
443
443
|
/* metadata.c */
|
444
444
|
extern const oci8_handle_data_type_t oci8_metadata_base_data_type;
|
445
|
+
VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public);
|
445
446
|
void Init_oci8_metadata(VALUE cOCI8);
|
446
447
|
VALUE oci8_metadata_create(OCIParam *parmhp, VALUE svc, VALUE parent);
|
447
448
|
|
data/ext/oci8/oraconf.rb
CHANGED
@@ -13,7 +13,7 @@ module MiniRegistry
|
|
13
13
|
@code = code
|
14
14
|
end
|
15
15
|
end
|
16
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
|
16
|
+
if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/
|
17
17
|
# Windows
|
18
18
|
require 'Win32API' # raise LoadError when UNIX.
|
19
19
|
|
@@ -390,7 +390,7 @@ EOS
|
|
390
390
|
is_32bit = size_of_pointer == 4
|
391
391
|
is_big_endian = "\x01\x02".unpack('s')[0] == 0x0102
|
392
392
|
case RUBY_PLATFORM
|
393
|
-
when /mswin32|cygwin|mingw32|bccwin32/
|
393
|
+
when /mswin32|mswin64|cygwin|mingw32|bccwin32/
|
394
394
|
oci_basename = 'oci'
|
395
395
|
oci_glob_postfix = ''
|
396
396
|
nls_data_basename = ['oraociei*', 'oraociicus*']
|
@@ -522,10 +522,11 @@ EOS
|
|
522
522
|
when /darwin/
|
523
523
|
fallback_path = ENV['DYLD_FALLBACK_LIBRARY_PATH']
|
524
524
|
if fallback_path.nil?
|
525
|
-
|
525
|
+
puts " DYLD_FALLBACK_LIBRARY_PATH is not set."
|
526
|
+
else
|
527
|
+
puts " checking DYLD_FALLBACK_LIBRARY_PATH..."
|
528
|
+
ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc)
|
526
529
|
end
|
527
|
-
puts " checking DYLD_FALLBACK_LIBRARY_PATH..."
|
528
|
-
ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc)
|
529
530
|
if ld_path.nil?
|
530
531
|
puts " checking OCI_DIR..."
|
531
532
|
ld_path, file = check_lib_in_path(ENV['OCI_DIR'], glob_name, check_proc)
|
@@ -562,6 +563,14 @@ EOS
|
|
562
563
|
end
|
563
564
|
end
|
564
565
|
end
|
566
|
+
if ld_path.nil?
|
567
|
+
fallback_path = ENV['DYLD_FALLBACK_LIBRARY_PATH']
|
568
|
+
if fallback_path.nil?
|
569
|
+
fallback_path = "#{ENV['HOME']}/lib:/usr/local/lib:/lib:/usr/lib"
|
570
|
+
puts " checking the default value of DYLD_FALLBACK_LIBRARY_PATH..."
|
571
|
+
ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc)
|
572
|
+
end
|
573
|
+
end
|
565
574
|
if ld_path.nil?
|
566
575
|
raise <<EOS
|
567
576
|
Set the environment variable DYLD_LIBRARY_PATH, DYLD_FALLBACK_LIBRARY_PATH or
|
@@ -598,7 +607,7 @@ EOS
|
|
598
607
|
paths.split(File::PATH_SEPARATOR).each do |path|
|
599
608
|
next if path.nil? or path == ''
|
600
609
|
print " checking #{path}... "
|
601
|
-
path.gsub!(/\\/, '/') if /mswin32|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM
|
610
|
+
path.gsub!(/\\/, '/') if /mswin32|mswin64|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM
|
602
611
|
files = Dir.glob(File.join(path, glob_name))
|
603
612
|
if files.empty?
|
604
613
|
puts "no"
|
@@ -702,13 +711,15 @@ EOS
|
|
702
711
|
end
|
703
712
|
end
|
704
713
|
|
705
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
|
714
|
+
if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows
|
706
715
|
|
707
716
|
def get_libs(lib_dir)
|
708
717
|
case RUBY_PLATFORM
|
709
718
|
when /cygwin|x64-mingw32/
|
710
719
|
regex = ([nil].pack('P').size == 8) ? / T (OCI\w+)/ : / T _(OCI\w+)/
|
711
|
-
oci_funcs = YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml'))
|
720
|
+
oci_funcs = YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml')).keys.collect do |func|
|
721
|
+
func =~ /_nb$/ ? $` : func
|
722
|
+
end
|
712
723
|
open("OCI.def", "w") do |f|
|
713
724
|
f.puts("EXPORTS")
|
714
725
|
open("|nm #{lib_dir}/MSVC/OCI.LIB") do |r|
|
@@ -782,7 +793,7 @@ class OraConfFC < OraConf
|
|
782
793
|
use_lib32 = false
|
783
794
|
end
|
784
795
|
|
785
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
|
796
|
+
if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/
|
786
797
|
lib_dir = "#{@oracle_home}/oci/lib"
|
787
798
|
elsif use_lib32
|
788
799
|
lib_dir = "#{@oracle_home}/lib32"
|
@@ -801,7 +812,7 @@ class OraConfFC < OraConf
|
|
801
812
|
print("Get the version of Oracle from SQL*Plus... ")
|
802
813
|
STDOUT.flush
|
803
814
|
version = nil
|
804
|
-
dev_null = RUBY_PLATFORM =~ /mswin32|mingw32|bccwin32/ ? "nul" : "/dev/null"
|
815
|
+
dev_null = RUBY_PLATFORM =~ /mswin32|mswin64|mingw32|bccwin32/ ? "nul" : "/dev/null"
|
805
816
|
if File.exist?("#{@oracle_home}/bin/plus80.exe")
|
806
817
|
sqlplus = "plus80.exe"
|
807
818
|
else
|
@@ -826,7 +837,7 @@ class OraConfFC < OraConf
|
|
826
837
|
version
|
827
838
|
end # get_version
|
828
839
|
|
829
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
|
840
|
+
if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows
|
830
841
|
|
831
842
|
def is_valid_home?(oracle_home)
|
832
843
|
return false if oracle_home.nil?
|
@@ -1026,7 +1037,7 @@ class OraConfIC < OraConf
|
|
1026
1037
|
inc_dir = "#{ic_dir}/sdk/include"
|
1027
1038
|
end
|
1028
1039
|
|
1029
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
|
1040
|
+
if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows
|
1030
1041
|
unless File.exist?("#{ic_dir}/sdk/lib/msvc/oci.lib")
|
1031
1042
|
raise <<EOS
|
1032
1043
|
Could not compile with Oracle instant client.
|
data/ext/oci8/plthook_win32.c
CHANGED
@@ -51,12 +51,18 @@
|
|
51
51
|
#define __attribute__(arg)
|
52
52
|
#endif
|
53
53
|
|
54
|
-
#
|
54
|
+
#if defined _LP64 /* data model: I32/LP64 */
|
55
|
+
#define SIZE_T_FMT "lu"
|
56
|
+
#elif defined _WIN64 /* data model: IL32/P64 */
|
55
57
|
#define SIZE_T_FMT "I64u"
|
56
58
|
#else
|
57
59
|
#define SIZE_T_FMT "u"
|
58
60
|
#endif
|
59
61
|
|
62
|
+
#ifdef __CYGWIN__
|
63
|
+
#define stricmp strcasecmp
|
64
|
+
#endif
|
65
|
+
|
60
66
|
typedef struct {
|
61
67
|
const char *mod_name;
|
62
68
|
const char *name;
|
data/lib/oci8/connection_pool.rb
CHANGED
@@ -13,17 +13,88 @@ class OCI8
|
|
13
13
|
#
|
14
14
|
# This is equivalent to Oracle JDBC Driver {OCI Connection Pooling}[http://docs.oracle.com/cd/E11882_01/java.112/e16548/ociconpl.htm#JJDBC28789].
|
15
15
|
#
|
16
|
-
#
|
16
|
+
# Note that this is different with generally called connection pooling.
|
17
|
+
# Generally connection pooling caches connections in a pool.
|
18
|
+
# When an application needs a new connection, a connection is got from
|
19
|
+
# the pool. So that the amount of time to establish a connection is
|
20
|
+
# reduced. However connection pooling in ruby-oci8 is different with
|
21
|
+
# above.
|
22
|
+
#
|
23
|
+
# One Oracle connection is devided into two layers: One is physical
|
24
|
+
# connection such as TCP/IP. The other is logical connection which
|
25
|
+
# is used by an application. The connection pooling in ruby-oci8
|
26
|
+
# caches physical connections in a pool, not logical connections.
|
27
|
+
# When an application needs a new connection, a logical connection
|
28
|
+
# is created via a physical connection, which needs time to
|
29
|
+
# establish a connection unlike generally called connection pooling.
|
30
|
+
# When an application sends a query via a logical connection, a
|
31
|
+
# physical connection is got from the pool. The physical connection
|
32
|
+
# returns to the pool automatically when the query finishes.
|
33
|
+
# As long as logical connections are used sequentially, only one
|
34
|
+
# physical connection is used. When an application sends
|
35
|
+
# queries at a time, it needs more than one physical connection
|
36
|
+
# because one physical connection cannot transmit more then one
|
37
|
+
# query at a time.
|
38
|
+
#
|
39
|
+
# Example:
|
17
40
|
# # Create a connection pool.
|
41
|
+
# # The number of initial physical connections: 1
|
42
|
+
# # The number of maximum physical connections: 5
|
43
|
+
# # the connection increment parameter: 2
|
18
44
|
# # username and password are required to establish an implicit primary session.
|
19
45
|
# cpool = OCI8::ConnectionPool.new(1, 5, 2, username, password, database)
|
20
46
|
#
|
21
|
-
# #
|
47
|
+
# # The number of physical connections: 1
|
48
|
+
# # The number of logical connections: 0
|
49
|
+
#
|
50
|
+
# # Create a session.
|
22
51
|
# # Pass the connection pool to the third argument.
|
23
52
|
# conn1 = OCI8.new(username, password, cpool)
|
24
53
|
#
|
25
|
-
# #
|
54
|
+
# # One logical connection was created.
|
55
|
+
# # The number of physical connections: 1
|
56
|
+
# # The number of logical connections: 1
|
57
|
+
#
|
58
|
+
# # Create another session.
|
26
59
|
# conn2 = OCI8.new(username, password, cpool)
|
60
|
+
#
|
61
|
+
# # Another logical connection was created.
|
62
|
+
# # The number of physical connections: 1
|
63
|
+
# # The number of logical connections: 2
|
64
|
+
#
|
65
|
+
# # Use conn1 and conn2 sequentially
|
66
|
+
# conn1.exec('...')
|
67
|
+
# conn2.exec('...')
|
68
|
+
#
|
69
|
+
# # Both logical connections use one physical connection.
|
70
|
+
# # The number of physical connections: 1
|
71
|
+
# # The number of logical connections: 2
|
72
|
+
#
|
73
|
+
# thr1 = Thread.new do
|
74
|
+
# conn1.exec('...')
|
75
|
+
# end
|
76
|
+
# thr2 = Thread.new do
|
77
|
+
# conn2.exec('...')
|
78
|
+
# end
|
79
|
+
# thr1.join
|
80
|
+
# thr2.join
|
81
|
+
#
|
82
|
+
# # Logical connections cannot use one physical connection at a time.
|
83
|
+
# # So that the number of physical connections is incremented.
|
84
|
+
# # The number of physical connections: 3
|
85
|
+
# # The number of logical connections: 2
|
86
|
+
#
|
87
|
+
# conn1.logoff
|
88
|
+
#
|
89
|
+
# # One logical connection was closed.
|
90
|
+
# # The number of physical connections: 3
|
91
|
+
# # The number of logical connections: 1
|
92
|
+
#
|
93
|
+
# conn2.logoff
|
94
|
+
#
|
95
|
+
# # All logical connections were closed.
|
96
|
+
# # The number of physical connections: 3
|
97
|
+
# # The number of logical connections: 0
|
27
98
|
#
|
28
99
|
class ConnectionPool
|
29
100
|
|
data/lib/oci8/cursor.rb
CHANGED
@@ -384,12 +384,19 @@ class OCI8
|
|
384
384
|
attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
|
385
385
|
end
|
386
386
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
387
|
+
if OCI8::oracle_client_version >= ORAVER_12_1
|
388
|
+
# Returns the number of processed rows.
|
389
|
+
#
|
390
|
+
# @return [Integer]
|
391
|
+
def row_count
|
392
|
+
# https://docs.oracle.com/database/121/LNOCI/ociaahan.htm#sthref5774
|
393
|
+
attr_get_ub8(457) # OCI_ATTR_UB8_ROW_COUNT(457)
|
394
|
+
end
|
395
|
+
else
|
396
|
+
def row_count
|
397
|
+
# http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5498
|
398
|
+
attr_get_ub4(9) # OCI_ATTR_ROW_COUNT(9)
|
399
|
+
end
|
393
400
|
end
|
394
401
|
|
395
402
|
# Returns the text of the SQL statement prepared in the cursor.
|
data/lib/oci8/object.rb
CHANGED
@@ -78,7 +78,8 @@ EOS
|
|
78
78
|
#
|
79
79
|
# @private
|
80
80
|
def get_tdo_by_typename(typename)
|
81
|
-
|
81
|
+
@name_to_tdo ||= {}
|
82
|
+
tdo = @name_to_tdo[typename]
|
82
83
|
return tdo if tdo
|
83
84
|
|
84
85
|
metadata = describe_any(typename)
|
@@ -383,8 +384,10 @@ EOS
|
|
383
384
|
|
384
385
|
case metadata.typecode
|
385
386
|
when :named_type
|
387
|
+
@is_final_type = metadata.is_final_type?
|
386
388
|
initialize_named_type(con, metadata)
|
387
389
|
when :named_collection
|
390
|
+
@is_final_type = true
|
388
391
|
initialize_named_collection(con, metadata)
|
389
392
|
end
|
390
393
|
end
|
data/lib/oci8/oci8.rb
CHANGED
@@ -63,9 +63,9 @@ class OCI8
|
|
63
63
|
#
|
64
64
|
# === connecting as a privileged user
|
65
65
|
#
|
66
|
-
# Set :SYSDBA
|
67
|
-
#
|
68
|
-
# as a single argument.
|
66
|
+
# Set :SYSDBA, :SYSOPER, :SYSASM, :SYSBACKUP, :SYSDG or :SYSKM
|
67
|
+
# to +privilege+, otherwise "username/password as sysdba",
|
68
|
+
# "username/password as sysoper", etc. as a single argument.
|
69
69
|
#
|
70
70
|
# OCI8.new('sys', 'change_on_install', nil, :SYSDBA)
|
71
71
|
# or
|
@@ -96,29 +96,15 @@ class OCI8
|
|
96
96
|
#
|
97
97
|
def initialize(*args)
|
98
98
|
if args.length == 1
|
99
|
-
username, password, dbname,
|
99
|
+
username, password, dbname, privilege = parse_connect_string(args[0])
|
100
100
|
else
|
101
|
-
username, password, dbname,
|
101
|
+
username, password, dbname, privilege = args
|
102
102
|
end
|
103
103
|
|
104
104
|
if username.nil? and password.nil?
|
105
105
|
cred = OCI_CRED_EXT
|
106
106
|
end
|
107
|
-
|
108
|
-
when :SYSDBA
|
109
|
-
mode = OCI_SYSDBA
|
110
|
-
when :SYSOPER
|
111
|
-
mode = OCI_SYSOPER
|
112
|
-
when :SYSASM
|
113
|
-
if OCI8.oracle_client_version < OCI8::ORAVER_11_1
|
114
|
-
raise "SYSASM is not supported on Oracle version #{OCI8.oracle_client_version}"
|
115
|
-
end
|
116
|
-
mode = OCI_SYSASM
|
117
|
-
when nil
|
118
|
-
# do nothing
|
119
|
-
else
|
120
|
-
raise "unknown privilege type #{mode}"
|
121
|
-
end
|
107
|
+
auth_mode = to_auth_mode(privilege)
|
122
108
|
|
123
109
|
stmt_cache_size = OCI8.properties[:statement_cache_size]
|
124
110
|
stmt_cache_size = nil if stmt_cache_size == 0
|
@@ -128,6 +114,12 @@ class OCI8
|
|
128
114
|
@pool = dbname # to prevent GC from freeing the connection pool.
|
129
115
|
dbname = dbname.send(:pool_name)
|
130
116
|
attach_mode |= 0x0200 # OCI_CPOOL and OCI_LOGON2_CPOOL
|
117
|
+
else
|
118
|
+
tcp_connect_timeout = OCI8::properties[:tcp_connect_timeout]
|
119
|
+
connect_timeout = OCI8::properties[:connect_timeout]
|
120
|
+
if tcp_connect_timeout || connect_timeout
|
121
|
+
dbname = to_connect_descriptor(dbname, tcp_connect_timeout, connect_timeout)
|
122
|
+
end
|
131
123
|
end
|
132
124
|
if stmt_cache_size
|
133
125
|
# enable statement caching
|
@@ -148,7 +140,11 @@ class OCI8
|
|
148
140
|
@session_handle.send(:attr_set_string, 424, "ruby-oci8 : #{OCI8::VERSION}")
|
149
141
|
end
|
150
142
|
server_attach(dbname, attach_mode)
|
151
|
-
|
143
|
+
if OCI8.oracle_client_version >= OCI8::ORAVER_11_1
|
144
|
+
self.send_timeout = OCI8::properties[:send_timeout] if OCI8::properties[:send_timeout]
|
145
|
+
self.recv_timeout = OCI8::properties[:recv_timeout] if OCI8::properties[:recv_timeout]
|
146
|
+
end
|
147
|
+
session_begin(cred ? cred : OCI_CRED_RDBMS, auth_mode)
|
152
148
|
else
|
153
149
|
# logon by the OCI function OCILogon2().
|
154
150
|
logon2(username, password, dbname, attach_mode)
|
@@ -389,6 +385,10 @@ class OCI8
|
|
389
385
|
# Zero means no timeout.
|
390
386
|
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF228 SQLNET.SEND_TIMEOUT} in client-side sqlnet.ora.
|
391
387
|
#
|
388
|
+
# If you need to set send timeout while establishing a connection, use {file:docs/timeout-parameters.md timeout parameters in OCI8::properties} instead.
|
389
|
+
#
|
390
|
+
# Note that the connection becomes unusable on timeout.
|
391
|
+
#
|
392
392
|
# If you have trouble by setting this, don't use it because it uses
|
393
393
|
# {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
|
394
394
|
#
|
@@ -417,6 +417,10 @@ class OCI8
|
|
417
417
|
# Zero means no timeout.
|
418
418
|
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF227 SQLNET.RECV_TIMEOUT} in client-side sqlnet.ora.
|
419
419
|
#
|
420
|
+
# If you need to set receive timeout while establishing a connection, use {file:docs/timeout-parameters.md timeout parameters in OCI8::properties} instead.
|
421
|
+
#
|
422
|
+
# Note that the connection becomes unusable on timeout.
|
423
|
+
#
|
420
424
|
# If you have trouble by setting this, don't use it because it uses
|
421
425
|
# {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
|
422
426
|
#
|
@@ -442,6 +446,138 @@ class OCI8
|
|
442
446
|
raise NotImplementedError, 'revc_timeout= is unimplemented in this Oracle version'
|
443
447
|
end
|
444
448
|
end
|
449
|
+
|
450
|
+
# A helper class to bind an array to paramters in IN-condition.
|
451
|
+
#
|
452
|
+
# See {file:docs/bind-array-to-in_cond.md Bind an Array to IN-condition}
|
453
|
+
class InCondBindHelper
|
454
|
+
def initialize(bind_name_prefix, array, type = nil, length = nil)
|
455
|
+
bind_name_prefix = bind_name_prefix.to_s
|
456
|
+
if bind_name_prefix !~ /^\w+$/
|
457
|
+
raise ArgumentError, "The first argument doesn't consist of alphanumeric characters and underscores."
|
458
|
+
end
|
459
|
+
if array.empty?
|
460
|
+
# This doesn't match anything.
|
461
|
+
# However in-condition requires at least one value.
|
462
|
+
@bind_names = ":#{bind_name_prefix}_0"
|
463
|
+
@bind_values = [[nil, type.nil? ? String : type, length]]
|
464
|
+
else
|
465
|
+
@bind_names = Array.new(array.length) do |index|
|
466
|
+
":#{bind_name_prefix}_#{index}"
|
467
|
+
end.join(', ')
|
468
|
+
first_non_nil = array.find do |e|
|
469
|
+
!e.nil?
|
470
|
+
end
|
471
|
+
first_non_nil = '' if first_non_nil.nil?
|
472
|
+
@bind_values = array.collect do |elem|
|
473
|
+
if elem.nil? and type.nil?
|
474
|
+
[elem, first_non_nil.class]
|
475
|
+
else
|
476
|
+
[elem, type, length]
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
def names
|
483
|
+
@bind_names
|
484
|
+
end
|
485
|
+
|
486
|
+
def values
|
487
|
+
@bind_values
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
# Creates a helper object to bind an array to paramters in IN-condition.
|
492
|
+
#
|
493
|
+
# See {file:docs/bind-array-to-in_cond.md Bind an Array to IN-condition}
|
494
|
+
#
|
495
|
+
# @param [Symbol] bind_name_prefix prefix of the place holder name
|
496
|
+
# @param [Object] array an array of values to be bound.
|
497
|
+
# @param [Class] type data type. This is used as the third argument of {OCI8::Cursor#bind_param}.
|
498
|
+
# @param [Integer] length maximum bind length for string values. This is used as the fourth argument of {OCI8::Cursor#bind_param}.
|
499
|
+
# @return [OCI8::InCondBindHelper]
|
500
|
+
def self.in_cond(bind_name_prefix, array, type = nil, length = nil)
|
501
|
+
InCondBindHelper.new(bind_name_prefix, array, type, length)
|
502
|
+
end
|
503
|
+
|
504
|
+
private
|
505
|
+
|
506
|
+
# Converts the specified privilege name to the value passed to the
|
507
|
+
# fifth argument of OCISessionBegin().
|
508
|
+
#
|
509
|
+
# @private
|
510
|
+
def to_auth_mode(privilege)
|
511
|
+
case privilege
|
512
|
+
when :SYSDBA
|
513
|
+
0x00000002 # OCI_SYSDBA in oci.h
|
514
|
+
when :SYSOPER
|
515
|
+
0x00000004 # OCI_SYSOPER in oci.h
|
516
|
+
when :SYSASM
|
517
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_11_1
|
518
|
+
raise "SYSASM is not supported on Oracle version #{OCI8.oracle_client_version}"
|
519
|
+
end
|
520
|
+
0x00008000 # OCI_SYSASM in oci.h
|
521
|
+
when :SYSBACKUP
|
522
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_12_1
|
523
|
+
raise "SYSBACKUP is not supported on Oracle version #{OCI8.oracle_client_version}"
|
524
|
+
end
|
525
|
+
0x00020000 # OCI_SYSBKP in oci.h
|
526
|
+
when :SYSDG
|
527
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_12_1
|
528
|
+
raise "SYSDG is not supported on Oracle version #{OCI8.oracle_client_version}"
|
529
|
+
end
|
530
|
+
0x00040000 # OCI_SYSDGD in oci.h
|
531
|
+
when :SYSKM
|
532
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_12_1
|
533
|
+
raise "SYSKM is not supported on Oracle version #{OCI8.oracle_client_version}"
|
534
|
+
end
|
535
|
+
0x00080000 # OCI_SYSKMT in oci.h
|
536
|
+
when nil
|
537
|
+
0 # OCI_DEFAULT
|
538
|
+
else
|
539
|
+
raise "unknown privilege type #{privilege}"
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
@@easy_connect_naming_regex = %r{
|
544
|
+
^
|
545
|
+
(//)? # preceding double-slash($1)
|
546
|
+
(?:\[([\h:]+)\]|([^\s:/]+)) # IPv6 enclosed by square brackets($2) or hostname($3)
|
547
|
+
(?::(\d+))? # port($4)
|
548
|
+
(?:
|
549
|
+
/
|
550
|
+
([^\s:/]+)? # service name($5)
|
551
|
+
(?::([^\s:/]+))? # server($6)
|
552
|
+
(?:/([^\s:/]+))? # instance name($7)
|
553
|
+
)?
|
554
|
+
$
|
555
|
+
}x
|
556
|
+
|
557
|
+
# Parse easy connect string as described in https://docs.oracle.com/database/121/NETAG/naming.htm
|
558
|
+
# and add TRANSPORT_CONNECT_TIMEOUT or CONNECT_TIMEOUT.
|
559
|
+
#
|
560
|
+
# @private
|
561
|
+
def to_connect_descriptor(database, tcp_connect_timeout, connect_timeout)
|
562
|
+
if @@easy_connect_naming_regex =~ database && ($1 || $2 || $4 || $5 || $6 || $7)
|
563
|
+
connect_data = []
|
564
|
+
connect_data << "(SERVICE_NAME=#$5)"
|
565
|
+
connect_data << "(SERVER=#$6)" if $6
|
566
|
+
connect_data << "(INSTANCE_NAME=#$7)" if $7
|
567
|
+
desc = []
|
568
|
+
desc << "(CONNECT_DATA=#{connect_data.join})"
|
569
|
+
desc << "(ADDRESS=(PROTOCOL=TCP)(HOST=#{$2 || $3})(PORT=#{$4 || 1521}))"
|
570
|
+
if tcp_connect_timeout
|
571
|
+
desc << "(TRANSPORT_CONNECT_TIMEOUT=#{tcp_connect_timeout})"
|
572
|
+
end
|
573
|
+
if connect_timeout
|
574
|
+
desc << "(CONNECT_TIMEOUT=#{connect_timeout})"
|
575
|
+
end
|
576
|
+
"(DESCRIPTION=#{desc.join})"
|
577
|
+
else
|
578
|
+
database
|
579
|
+
end
|
580
|
+
end
|
445
581
|
end
|
446
582
|
|
447
583
|
class OCIError
|