ruby-oci8 2.2.0.2 → 2.2.12
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.
- checksums.yaml +7 -0
- data/.yardopts +1 -6
- data/ChangeLog +600 -0
- data/NEWS +426 -35
- data/README.md +27 -9
- data/dist-files +13 -2
- data/docs/bind-array-to-in_cond.md +38 -0
- data/docs/conflicts-local-connections-and-processes.md +98 -0
- data/docs/hanging-after-inactivity.md +63 -0
- data/docs/install-binary-package.md +15 -11
- data/docs/install-full-client.md +18 -21
- data/docs/install-instant-client.md +45 -27
- data/docs/install-on-osx.md +31 -117
- data/docs/ldap-auth-and-function-interposition.md +123 -0
- data/docs/number-type-mapping.md +79 -0
- data/docs/platform-specific-issues.md +17 -50
- data/docs/report-installation-issue.md +11 -8
- data/docs/timeout-parameters.md +94 -0
- data/ext/oci8/apiwrap.c.tmpl +2 -5
- data/ext/oci8/apiwrap.rb +6 -1
- data/ext/oci8/apiwrap.yml +39 -143
- data/ext/oci8/attr.c +4 -2
- data/ext/oci8/bind.c +421 -9
- data/ext/oci8/connection_pool.c +3 -3
- data/ext/oci8/encoding.c +5 -5
- data/ext/oci8/env.c +8 -2
- data/ext/oci8/error.c +24 -16
- data/ext/oci8/extconf.rb +35 -63
- data/ext/oci8/hook_funcs.c +274 -61
- data/ext/oci8/lob.c +31 -75
- data/ext/oci8/metadata.c +8 -6
- data/ext/oci8/object.c +119 -29
- data/ext/oci8/oci8.c +46 -133
- data/ext/oci8/oci8.h +40 -123
- data/ext/oci8/oci8lib.c +178 -46
- data/ext/oci8/ocihandle.c +37 -37
- data/ext/oci8/ocinumber.c +24 -35
- data/ext/oci8/oraconf.rb +168 -337
- data/ext/oci8/oradate.c +19 -19
- data/ext/oci8/plthook.h +10 -0
- data/ext/oci8/plthook_elf.c +433 -268
- data/ext/oci8/plthook_osx.c +40 -9
- data/ext/oci8/plthook_win32.c +16 -1
- data/ext/oci8/stmt.c +52 -17
- data/ext/oci8/win32.c +4 -22
- data/lib/oci8/bindtype.rb +10 -17
- data/lib/oci8/check_load_error.rb +57 -10
- data/lib/oci8/compat.rb +5 -1
- data/lib/oci8/connection_pool.rb +74 -3
- data/lib/oci8/cursor.rb +70 -31
- data/lib/oci8/metadata.rb +9 -1
- data/lib/oci8/object.rb +14 -1
- data/lib/oci8/oci8.rb +184 -58
- data/lib/oci8/ocihandle.rb +0 -16
- data/lib/oci8/oracle_version.rb +11 -1
- data/lib/oci8/properties.rb +55 -0
- data/lib/oci8/version.rb +1 -1
- data/lib/oci8.rb +48 -4
- data/lib/ruby-oci8.rb +1 -0
- data/pre-distclean.rb +1 -3
- data/ruby-oci8.gemspec +4 -9
- data/setup.rb +11 -2
- data/test/README.md +37 -0
- data/test/config.rb +8 -1
- data/test/setup_test_object.sql +42 -14
- data/test/setup_test_package.sql +59 -0
- data/test/test_all.rb +4 -0
- data/test/test_bind_array.rb +70 -0
- data/test/test_bind_boolean.rb +99 -0
- data/test/test_bind_integer.rb +47 -0
- data/test/test_break.rb +11 -9
- data/test/test_clob.rb +5 -17
- data/test/test_connstr.rb +142 -0
- data/test/test_datetime.rb +8 -3
- data/test/test_metadata.rb +2 -1
- data/test/test_object.rb +99 -18
- data/test/test_oci8.rb +170 -46
- data/test/test_oranumber.rb +12 -6
- data/test/test_package_type.rb +17 -3
- data/test/test_properties.rb +17 -0
- metadata +45 -55
- data/docs/osx-install-dev-tools.png +0 -0
- data/test/README +0 -42
data/lib/oci8/cursor.rb
CHANGED
@@ -25,7 +25,11 @@ class OCI8
|
|
25
25
|
@names = nil
|
26
26
|
@con = conn
|
27
27
|
@max_array_size = nil
|
28
|
+
@fetch_array_size = nil
|
29
|
+
@rowbuf_size = 0
|
30
|
+
@rowbuf_index = 0
|
28
31
|
__initialize(conn, sql) # Initialize the internal C structure.
|
32
|
+
self.prefetch_rows = conn.instance_variable_get(:@prefetch_rows)
|
29
33
|
end
|
30
34
|
|
31
35
|
# explicitly indicate the date type of fetched value. run this
|
@@ -38,7 +42,7 @@ class OCI8
|
|
38
42
|
# cursor.define(2, Time) # fetch the second column as Time.
|
39
43
|
# cursor.exec()
|
40
44
|
def define(pos, type, length = nil)
|
41
|
-
bindobj = make_bind_object(:type => type, :length => length)
|
45
|
+
bindobj = make_bind_object({:type => type, :length => length}, @fetch_array_size || 1)
|
42
46
|
__define(pos, bindobj)
|
43
47
|
if old = @define_handles[pos - 1]
|
44
48
|
old.send(:free)
|
@@ -58,15 +62,16 @@ class OCI8
|
|
58
62
|
# # ...or...
|
59
63
|
# cursor.bind_param(':ename', 'SMITH') # bind by name
|
60
64
|
#
|
61
|
-
# To bind as number,
|
62
|
-
#
|
63
|
-
# +type+ and Fixnum or Float to +val+.
|
65
|
+
# To bind as number, set the number intself to +val+. If its initial value
|
66
|
+
# is NULL, please set nil to +type+ and Integer, Float or OraNumber to +val+.
|
64
67
|
#
|
65
68
|
# example:
|
66
|
-
# cursor.bind_param(1, 1234) # bind as
|
69
|
+
# cursor.bind_param(1, 1234) # bind as Integer, Initial value is 1234.
|
67
70
|
# cursor.bind_param(1, 1234.0) # bind as Float, Initial value is 1234.0.
|
68
|
-
# cursor.bind_param(1, nil,
|
71
|
+
# cursor.bind_param(1, nil, Integer) # bind as Integer, Initial value is NULL.
|
69
72
|
# cursor.bind_param(1, nil, Float) # bind as Float, Initial value is NULL.
|
73
|
+
# cursor.bind_param(1, OraNumber(1234)) # bind as OraNumber, Initial value is 1234.
|
74
|
+
# cursor.bind_param(1, nil, OraNumber) # bind as OraNumber, Initial value is NULL.
|
70
75
|
#
|
71
76
|
# In case of binding a string, set the string itself to
|
72
77
|
# +val+. When the bind variable is used as output, set the
|
@@ -124,7 +129,10 @@ class OCI8
|
|
124
129
|
case type
|
125
130
|
when :select_stmt
|
126
131
|
__execute(0)
|
127
|
-
define_columns()
|
132
|
+
define_columns() if @column_metadata.size == 0
|
133
|
+
@rowbuf_size = 0
|
134
|
+
@rowbuf_index = 0
|
135
|
+
@column_metadata.size
|
128
136
|
else
|
129
137
|
__execute(1)
|
130
138
|
row_count
|
@@ -379,17 +387,25 @@ class OCI8
|
|
379
387
|
#
|
380
388
|
# FYI: Rails oracle adaptor uses 100 by default.
|
381
389
|
#
|
382
|
-
# @param [
|
390
|
+
# @param [Integer] rows The number of rows to be prefetched
|
383
391
|
def prefetch_rows=(rows)
|
384
392
|
attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
|
393
|
+
@prefetch_rows = rows
|
385
394
|
end
|
386
395
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
396
|
+
if OCI8::oracle_client_version >= ORAVER_12_1
|
397
|
+
# Returns the number of processed rows.
|
398
|
+
#
|
399
|
+
# @return [Integer]
|
400
|
+
def row_count
|
401
|
+
# https://docs.oracle.com/database/121/LNOCI/ociaahan.htm#sthref5774
|
402
|
+
attr_get_ub8(457) # OCI_ATTR_UB8_ROW_COUNT(457)
|
403
|
+
end
|
404
|
+
else
|
405
|
+
def row_count
|
406
|
+
# http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5498
|
407
|
+
attr_get_ub4(9) # OCI_ATTR_ROW_COUNT(9)
|
408
|
+
end
|
393
409
|
end
|
394
410
|
|
395
411
|
# Returns the text of the SQL statement prepared in the cursor.
|
@@ -423,12 +439,12 @@ class OCI8
|
|
423
439
|
# * OCI8::STMT_ALTER
|
424
440
|
# * OCI8::STMT_BEGIN (PL/SQL block which starts with a BEGIN keyword)
|
425
441
|
# * OCI8::STMT_DECLARE (PL/SQL block which starts with a DECLARE keyword)
|
426
|
-
# * Other
|
442
|
+
# * Other Integer value undocumented in Oracle manuals.
|
427
443
|
#
|
428
444
|
# <em>Changes between ruby-oci8 1.0 and 2.0.</em>
|
429
445
|
#
|
430
446
|
# [ruby-oci8 2.0] OCI8::STMT_* are Symbols. (:select_stmt, :update_stmt, etc.)
|
431
|
-
# [ruby-oci8 1.0] OCI8::STMT_* are
|
447
|
+
# [ruby-oci8 1.0] OCI8::STMT_* are Integers. (1, 2, 3, etc.)
|
432
448
|
#
|
433
449
|
def type
|
434
450
|
# http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5506
|
@@ -459,7 +475,7 @@ class OCI8
|
|
459
475
|
|
460
476
|
private
|
461
477
|
|
462
|
-
def make_bind_object(param)
|
478
|
+
def make_bind_object(param, fetch_array_size = nil)
|
463
479
|
case param
|
464
480
|
when Hash
|
465
481
|
key = param[:type]
|
@@ -501,26 +517,38 @@ class OCI8
|
|
501
517
|
OCI8::BindType::Mapping[key] = bindclass if bindclass
|
502
518
|
end
|
503
519
|
raise "unsupported datatype: #{key}" if bindclass.nil?
|
504
|
-
bindclass.create(@con, val, param, max_array_size)
|
520
|
+
bindclass.create(@con, val, param, fetch_array_size || max_array_size)
|
505
521
|
end
|
506
522
|
|
523
|
+
@@use_array_fetch = false
|
524
|
+
|
507
525
|
def define_columns
|
508
526
|
# http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5494
|
509
527
|
num_cols = attr_get_ub4(18) # OCI_ATTR_PARAM_COUNT(18)
|
510
|
-
1.upto(num_cols) do |i|
|
511
|
-
|
512
|
-
|
513
|
-
|
528
|
+
@column_metadata = 1.upto(num_cols).collect do |i|
|
529
|
+
__paramGet(i)
|
530
|
+
end
|
531
|
+
if @define_handles.size == 0
|
532
|
+
use_array_fetch = @@use_array_fetch
|
533
|
+
@column_metadata.each do |md|
|
534
|
+
case md.data_type
|
535
|
+
when :clob, :blob, :bfile
|
536
|
+
# Rows prefetching doesn't work for CLOB, BLOB and BFILE.
|
537
|
+
# Use array fetching to get more than one row in a network round trip.
|
538
|
+
use_array_fetch = true
|
539
|
+
end
|
540
|
+
end
|
541
|
+
@fetch_array_size = @prefetch_rows if use_array_fetch
|
542
|
+
end
|
543
|
+
@column_metadata.each_with_index do |md, i|
|
544
|
+
define_one_column(i + 1, md) unless @define_handles[i]
|
514
545
|
end
|
515
546
|
num_cols
|
516
547
|
end
|
517
548
|
|
518
549
|
def define_one_column(pos, param)
|
519
|
-
bindobj = make_bind_object(param)
|
550
|
+
bindobj = make_bind_object(param, @fetch_array_size || 1)
|
520
551
|
__define(pos, bindobj)
|
521
|
-
if old = @define_handles[pos - 1]
|
522
|
-
old.send(:free)
|
523
|
-
end
|
524
552
|
@define_handles[pos - 1] = bindobj
|
525
553
|
end
|
526
554
|
|
@@ -534,22 +562,33 @@ class OCI8
|
|
534
562
|
end
|
535
563
|
end
|
536
564
|
|
565
|
+
def fetch_row_internal
|
566
|
+
if @rowbuf_size && @rowbuf_size == @rowbuf_index
|
567
|
+
@rowbuf_size = __fetch(@con, @fetch_array_size || 1)
|
568
|
+
@rowbuf_index = 0
|
569
|
+
end
|
570
|
+
@rowbuf_size
|
571
|
+
end
|
572
|
+
|
537
573
|
def fetch_one_row_as_array
|
538
|
-
if
|
539
|
-
@define_handles.collect do |handle|
|
540
|
-
handle.send(:get_data)
|
574
|
+
if fetch_row_internal
|
575
|
+
ret = @define_handles.collect do |handle|
|
576
|
+
handle.send(:get_data, @rowbuf_index)
|
541
577
|
end
|
578
|
+
@rowbuf_index += 1
|
579
|
+
ret
|
542
580
|
else
|
543
581
|
nil
|
544
582
|
end
|
545
583
|
end
|
546
584
|
|
547
585
|
def fetch_one_row_as_hash
|
548
|
-
if
|
586
|
+
if fetch_row_internal
|
549
587
|
ret = {}
|
550
588
|
get_col_names.each_with_index do |name, idx|
|
551
|
-
ret[name] = @define_handles[idx].send(:get_data)
|
589
|
+
ret[name] = @define_handles[idx].send(:get_data, @rowbuf_index)
|
552
590
|
end
|
591
|
+
@rowbuf_index += 1
|
553
592
|
ret
|
554
593
|
else
|
555
594
|
nil
|
data/lib/oci8/metadata.rb
CHANGED
@@ -1503,7 +1503,11 @@ class OCI8
|
|
1503
1503
|
attr_get_sb1(OCI_ATTR_SCALE)
|
1504
1504
|
end
|
1505
1505
|
|
1506
|
-
# The
|
1506
|
+
# The nest level.
|
1507
|
+
#
|
1508
|
+
# Oracle manual says that it always returns zero. However it returns
|
1509
|
+
# the depth of {OCI8::ArgBase#arguments} calls when #arguments returns
|
1510
|
+
# a non-empty array.
|
1507
1511
|
def level
|
1508
1512
|
attr_get_ub2(OCI_ATTR_LEVEL)
|
1509
1513
|
end
|
@@ -1607,6 +1611,10 @@ class OCI8
|
|
1607
1611
|
|
1608
1612
|
# The list of arguments at the next level (when the argument is
|
1609
1613
|
# of a record or table type).
|
1614
|
+
#
|
1615
|
+
# This method returns an array containing type information when
|
1616
|
+
# the type is a user-defined type and the Oracle server version
|
1617
|
+
# is 12c or earlier. Otherwise, it returns an empty array.
|
1610
1618
|
def arguments
|
1611
1619
|
@arguments ||= list_arguments.to_a
|
1612
1620
|
end
|
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
|
@@ -490,6 +493,16 @@ EOS
|
|
490
493
|
Proc.new do |val| datetime_to_array(val, :date) end, # set_proc
|
491
494
|
Proc.new do |val| array_to_time(val, :local) end, # get_proc
|
492
495
|
]
|
496
|
+
when :timestamp
|
497
|
+
[ATTR_TIMESTAMP, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
|
498
|
+
Proc.new do |val| datetime_to_array(val, :timestamp) end, # set_proc
|
499
|
+
Proc.new do |val| array_to_time(val, :local) end, # get_proc
|
500
|
+
]
|
501
|
+
when :timestamp_tz
|
502
|
+
[ATTR_TIMESTAMP_TZ, con, SIZE_OF_POINTER, 2, ALIGNMENT_OF_POINTER,
|
503
|
+
Proc.new do |val| datetime_to_array(val, :timestamp_tz) end, # set_proc
|
504
|
+
Proc.new do |val| array_to_time(val, nil) end, # get_proc
|
505
|
+
]
|
493
506
|
when :binary_double
|
494
507
|
[ATTR_BINARY_DOUBLE, nil, SIZE_OF_DOUBLE, 2, ALIGNMENT_OF_DOUBLE]
|
495
508
|
when :binary_float
|
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
|
@@ -127,32 +113,38 @@ class OCI8
|
|
127
113
|
if dbname.is_a? OCI8::ConnectionPool
|
128
114
|
@pool = dbname # to prevent GC from freeing the connection pool.
|
129
115
|
dbname = dbname.send(:pool_name)
|
130
|
-
attach_mode |= 0x0200 # OCI_CPOOL
|
116
|
+
attach_mode |= 0x0200 # OCI_CPOOL
|
117
|
+
else
|
118
|
+
tcp_connect_timeout = OCI8::properties[:tcp_connect_timeout]
|
119
|
+
connect_timeout = OCI8::properties[:connect_timeout]
|
120
|
+
tcp_keepalive = OCI8::properties[:tcp_keepalive]
|
121
|
+
if tcp_connect_timeout || connect_timeout || tcp_keepalive
|
122
|
+
dbname = to_connect_descriptor(dbname, tcp_connect_timeout, connect_timeout, tcp_keepalive)
|
123
|
+
end
|
131
124
|
end
|
132
125
|
if stmt_cache_size
|
133
126
|
# enable statement caching
|
134
|
-
attach_mode |= 0x0004 # OCI_STMT_CACHE
|
127
|
+
attach_mode |= 0x0004 # OCI_STMT_CACHE
|
135
128
|
end
|
136
129
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
@session_handle.send(:attr_set_string, 424, "ruby-oci8 : #{OCI8::VERSION}")
|
149
|
-
end
|
150
|
-
server_attach(dbname, attach_mode)
|
151
|
-
session_begin(cred ? cred : OCI_CRED_RDBMS, mode ? mode : OCI_DEFAULT)
|
152
|
-
else
|
153
|
-
# logon by the OCI function OCILogon2().
|
154
|
-
logon2(username, password, dbname, attach_mode)
|
130
|
+
# logon by the OCI function OCISessionBegin().
|
131
|
+
allocate_handles()
|
132
|
+
@session_handle.send(:attr_set_string, OCI_ATTR_USERNAME, username) if username
|
133
|
+
@session_handle.send(:attr_set_string, OCI_ATTR_PASSWORD, password) if password
|
134
|
+
if @@oracle_client_version >= ORAVER_11_1
|
135
|
+
# Sets the driver name displayed in V$SESSION_CONNECT_INFO.CLIENT_DRIVER
|
136
|
+
# if both the client and the server are Oracle 11g or upper.
|
137
|
+
# Only the first 8 chracters "ruby-oci" are displayed when the Oracle
|
138
|
+
# server version is lower than 12.0.1.2.
|
139
|
+
# 424: OCI_ATTR_DRIVER_NAME
|
140
|
+
@session_handle.send(:attr_set_string, 424, "ruby-oci8 : #{OCI8::VERSION}")
|
155
141
|
end
|
142
|
+
server_attach(dbname, attach_mode)
|
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)
|
156
148
|
|
157
149
|
if stmt_cache_size
|
158
150
|
# set statement cache size
|
@@ -177,7 +169,6 @@ class OCI8
|
|
177
169
|
# @private
|
178
170
|
def parse_internal(sql)
|
179
171
|
cursor = OCI8::Cursor.new(self, sql)
|
180
|
-
cursor.prefetch_rows = @prefetch_rows if @prefetch_rows
|
181
172
|
cursor
|
182
173
|
end
|
183
174
|
|
@@ -313,6 +304,7 @@ class OCI8
|
|
313
304
|
# @return [Array] an array of first row.
|
314
305
|
def select_one(sql, *bindvars)
|
315
306
|
cursor = self.parse(sql)
|
307
|
+
cursor.prefetch_rows = 1
|
316
308
|
begin
|
317
309
|
cursor.exec(*bindvars)
|
318
310
|
row = cursor.fetch
|
@@ -348,25 +340,16 @@ class OCI8
|
|
348
340
|
|
349
341
|
# Returns the Oracle server version.
|
350
342
|
#
|
343
|
+
# When the Oracle client version is 12c or earlier and
|
344
|
+
# the Oracle server version is 18c or later, this method
|
345
|
+
# doesn't return *full* version number such as '18.3.0.0.0'.
|
346
|
+
# It returns version number whose number components after
|
347
|
+
# the first dot are zeros such as '18.0.0.0.0'.
|
348
|
+
#
|
351
349
|
# @see OCI8.oracle_client_version
|
352
350
|
# @return [OCI8::OracleVersion]
|
353
351
|
def oracle_server_version
|
354
|
-
|
355
|
-
if vernum = oracle_server_vernum
|
356
|
-
# If the Oracle client is Oracle 9i or upper,
|
357
|
-
# get the server version from the OCI function OCIServerRelease.
|
358
|
-
@oracle_server_version = OCI8::OracleVersion.new(vernum)
|
359
|
-
else
|
360
|
-
# Otherwise, get it from v$version.
|
361
|
-
self.exec('select banner from v$version') do |row|
|
362
|
-
if /^Oracle.*?(\d+\.\d+\.\d+\.\d+\.\d+)/ =~ row[0]
|
363
|
-
@oracle_server_version = OCI8::OracleVersion.new($1)
|
364
|
-
break
|
365
|
-
end
|
366
|
-
end
|
367
|
-
end
|
368
|
-
end
|
369
|
-
@oracle_server_version
|
352
|
+
@oracle_server_version ||= OCI8::OracleVersion.new(oracle_server_vernum)
|
370
353
|
end
|
371
354
|
|
372
355
|
# Returns the Oracle database character set name such as AL32UTF8.
|
@@ -404,6 +387,10 @@ class OCI8
|
|
404
387
|
# Zero means no timeout.
|
405
388
|
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF228 SQLNET.SEND_TIMEOUT} in client-side sqlnet.ora.
|
406
389
|
#
|
390
|
+
# If you need to set send timeout while establishing a connection, use {file:docs/timeout-parameters.md timeout parameters in OCI8::properties} instead.
|
391
|
+
#
|
392
|
+
# Note that the connection becomes unusable on timeout.
|
393
|
+
#
|
407
394
|
# If you have trouble by setting this, don't use it because it uses
|
408
395
|
# {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
|
409
396
|
#
|
@@ -432,6 +419,10 @@ class OCI8
|
|
432
419
|
# Zero means no timeout.
|
433
420
|
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF227 SQLNET.RECV_TIMEOUT} in client-side sqlnet.ora.
|
434
421
|
#
|
422
|
+
# If you need to set receive timeout while establishing a connection, use {file:docs/timeout-parameters.md timeout parameters in OCI8::properties} instead.
|
423
|
+
#
|
424
|
+
# Note that the connection becomes unusable on timeout.
|
425
|
+
#
|
435
426
|
# If you have trouble by setting this, don't use it because it uses
|
436
427
|
# {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
|
437
428
|
#
|
@@ -457,6 +448,141 @@ class OCI8
|
|
457
448
|
raise NotImplementedError, 'revc_timeout= is unimplemented in this Oracle version'
|
458
449
|
end
|
459
450
|
end
|
451
|
+
|
452
|
+
# A helper class to bind an array to paramters in IN-condition.
|
453
|
+
#
|
454
|
+
# See {file:docs/bind-array-to-in_cond.md Bind an Array to IN-condition}
|
455
|
+
class InCondBindHelper
|
456
|
+
def initialize(bind_name_prefix, array, type = nil, length = nil)
|
457
|
+
bind_name_prefix = bind_name_prefix.to_s
|
458
|
+
if bind_name_prefix !~ /^\w+$/
|
459
|
+
raise ArgumentError, "The first argument doesn't consist of alphanumeric characters and underscores."
|
460
|
+
end
|
461
|
+
if array.empty?
|
462
|
+
# This doesn't match anything.
|
463
|
+
# However in-condition requires at least one value.
|
464
|
+
@bind_names = ":#{bind_name_prefix}_0"
|
465
|
+
@bind_values = [[nil, type.nil? ? String : type, length]]
|
466
|
+
else
|
467
|
+
@bind_names = Array.new(array.length) do |index|
|
468
|
+
":#{bind_name_prefix}_#{index}"
|
469
|
+
end.join(', ')
|
470
|
+
first_non_nil = array.find do |e|
|
471
|
+
!e.nil?
|
472
|
+
end
|
473
|
+
first_non_nil = '' if first_non_nil.nil?
|
474
|
+
@bind_values = array.collect do |elem|
|
475
|
+
if elem.nil? and type.nil?
|
476
|
+
[elem, first_non_nil.class]
|
477
|
+
else
|
478
|
+
[elem, type, length]
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
def names
|
485
|
+
@bind_names
|
486
|
+
end
|
487
|
+
|
488
|
+
def values
|
489
|
+
@bind_values
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
# Creates a helper object to bind an array to paramters in IN-condition.
|
494
|
+
#
|
495
|
+
# See {file:docs/bind-array-to-in_cond.md Bind an Array to IN-condition}
|
496
|
+
#
|
497
|
+
# @param [Symbol] bind_name_prefix prefix of the place holder name
|
498
|
+
# @param [Object] array an array of values to be bound.
|
499
|
+
# @param [Class] type data type. This is used as the third argument of {OCI8::Cursor#bind_param}.
|
500
|
+
# @param [Integer] length maximum bind length for string values. This is used as the fourth argument of {OCI8::Cursor#bind_param}.
|
501
|
+
# @return [OCI8::InCondBindHelper]
|
502
|
+
def self.in_cond(bind_name_prefix, array, type = nil, length = nil)
|
503
|
+
InCondBindHelper.new(bind_name_prefix, array, type, length)
|
504
|
+
end
|
505
|
+
|
506
|
+
private
|
507
|
+
|
508
|
+
# Converts the specified privilege name to the value passed to the
|
509
|
+
# fifth argument of OCISessionBegin().
|
510
|
+
#
|
511
|
+
# @private
|
512
|
+
def to_auth_mode(privilege)
|
513
|
+
case privilege
|
514
|
+
when :SYSDBA
|
515
|
+
0x00000002 # OCI_SYSDBA in oci.h
|
516
|
+
when :SYSOPER
|
517
|
+
0x00000004 # OCI_SYSOPER in oci.h
|
518
|
+
when :SYSASM
|
519
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_11_1
|
520
|
+
raise "SYSASM is not supported on Oracle version #{OCI8.oracle_client_version}"
|
521
|
+
end
|
522
|
+
0x00008000 # OCI_SYSASM in oci.h
|
523
|
+
when :SYSBACKUP
|
524
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_12_1
|
525
|
+
raise "SYSBACKUP is not supported on Oracle version #{OCI8.oracle_client_version}"
|
526
|
+
end
|
527
|
+
0x00020000 # OCI_SYSBKP in oci.h
|
528
|
+
when :SYSDG
|
529
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_12_1
|
530
|
+
raise "SYSDG is not supported on Oracle version #{OCI8.oracle_client_version}"
|
531
|
+
end
|
532
|
+
0x00040000 # OCI_SYSDGD in oci.h
|
533
|
+
when :SYSKM
|
534
|
+
if OCI8.oracle_client_version < OCI8::ORAVER_12_1
|
535
|
+
raise "SYSKM is not supported on Oracle version #{OCI8.oracle_client_version}"
|
536
|
+
end
|
537
|
+
0x00080000 # OCI_SYSKMT in oci.h
|
538
|
+
when nil
|
539
|
+
0 # OCI_DEFAULT
|
540
|
+
else
|
541
|
+
raise "unknown privilege type #{privilege}"
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
@@easy_connect_naming_regex = %r{
|
546
|
+
^
|
547
|
+
(//)? # preceding double-slash($1)
|
548
|
+
(?:\[([\h:]+)\]|([^\s:/]+)) # IPv6 enclosed by square brackets($2) or hostname($3)
|
549
|
+
(?::(\d+))? # port($4)
|
550
|
+
(?:
|
551
|
+
/
|
552
|
+
([^\s:/]+)? # service name($5)
|
553
|
+
(?::([^\s:/]+))? # server($6)
|
554
|
+
(?:/([^\s:/]+))? # instance name($7)
|
555
|
+
)?
|
556
|
+
$
|
557
|
+
}x
|
558
|
+
|
559
|
+
# Parse easy connect string as described in https://docs.oracle.com/database/121/NETAG/naming.htm
|
560
|
+
# and add TRANSPORT_CONNECT_TIMEOUT or CONNECT_TIMEOUT.
|
561
|
+
#
|
562
|
+
# @private
|
563
|
+
def to_connect_descriptor(database, tcp_connect_timeout, connect_timeout, tcp_keepalive)
|
564
|
+
if @@easy_connect_naming_regex =~ database && ($1 || $2 || $4 || $5 || $6 || $7)
|
565
|
+
connect_data = []
|
566
|
+
connect_data << "(SERVICE_NAME=#$5)"
|
567
|
+
connect_data << "(SERVER=#$6)" if $6
|
568
|
+
connect_data << "(INSTANCE_NAME=#$7)" if $7
|
569
|
+
desc = []
|
570
|
+
desc << "(CONNECT_DATA=#{connect_data.join})"
|
571
|
+
desc << "(ADDRESS=(PROTOCOL=TCP)(HOST=#{$2 || $3})(PORT=#{$4 || 1521}))"
|
572
|
+
if tcp_connect_timeout
|
573
|
+
desc << "(TRANSPORT_CONNECT_TIMEOUT=#{tcp_connect_timeout})"
|
574
|
+
end
|
575
|
+
if connect_timeout
|
576
|
+
desc << "(CONNECT_TIMEOUT=#{connect_timeout})"
|
577
|
+
end
|
578
|
+
if tcp_keepalive
|
579
|
+
desc << "(ENABLE=BROKEN)"
|
580
|
+
end
|
581
|
+
"(DESCRIPTION=#{desc.join})"
|
582
|
+
else
|
583
|
+
database
|
584
|
+
end
|
585
|
+
end
|
460
586
|
end
|
461
587
|
|
462
588
|
class OCIError
|
@@ -501,7 +627,7 @@ class OCIError
|
|
501
627
|
#
|
502
628
|
def initialize(*args)
|
503
629
|
if args.length > 0
|
504
|
-
if args[0].is_a?
|
630
|
+
if args[0].is_a? Integer
|
505
631
|
@code = args.shift
|
506
632
|
super(OCI8.error_message(@code).gsub('%s') {|s| args.empty? ? '%s' : args.shift})
|
507
633
|
@sql = nil
|
data/lib/oci8/ocihandle.rb
CHANGED
@@ -425,22 +425,6 @@ class OCIHandle
|
|
425
425
|
# @private
|
426
426
|
OCI_CRED_EXT = 2
|
427
427
|
|
428
|
-
#################################
|
429
|
-
#
|
430
|
-
# Authentication Modes
|
431
|
-
#
|
432
|
-
#################################
|
433
|
-
|
434
|
-
# for SYSDBA authorization
|
435
|
-
# @private
|
436
|
-
OCI_SYSDBA = 0x0002
|
437
|
-
# for SYSOPER authorization
|
438
|
-
# @private
|
439
|
-
OCI_SYSOPER = 0x0004
|
440
|
-
# for SYSASM authorization
|
441
|
-
# @private
|
442
|
-
OCI_SYSASM = 0x8000
|
443
|
-
|
444
428
|
#################################
|
445
429
|
#
|
446
430
|
# OCI Parameter Types
|
data/lib/oci8/oracle_version.rb
CHANGED
@@ -66,6 +66,12 @@ class OCI8
|
|
66
66
|
major, minor, update, patch, port_update = arg.split('.').collect do |v|
|
67
67
|
v.to_i
|
68
68
|
end
|
69
|
+
elsif arg >= 0x12000000
|
70
|
+
major = (arg & 0xFF000000) >> 24
|
71
|
+
minor = (arg & 0x00FF0000) >> 16
|
72
|
+
update = (arg & 0x0000F000) >> 12
|
73
|
+
patch = (arg & 0x00000FF0) >> 4
|
74
|
+
port_update = (arg & 0x0000000F)
|
69
75
|
elsif arg >= 0x08000000
|
70
76
|
major = (arg & 0xFF000000) >> 24
|
71
77
|
minor = (arg & 0x00F00000) >> 20
|
@@ -80,7 +86,11 @@ class OCI8
|
|
80
86
|
@update = update || 0
|
81
87
|
@patch = patch || 0
|
82
88
|
@port_update = port_update || 0
|
83
|
-
@vernum =
|
89
|
+
@vernum = if @major >= 18
|
90
|
+
(@major << 24) | (@minor << 16) | (@update << 12) | (@patch << 4) | @port_update
|
91
|
+
else
|
92
|
+
(@major << 24) | (@minor << 20) | (@update << 12) | (@patch << 8) | @port_update
|
93
|
+
end
|
84
94
|
end
|
85
95
|
|
86
96
|
# Compares +self+ and +other+.
|