ruby-oci8 2.2.0.2 → 2.2.12
Sign up to get free protection for your applications and to get access to all the features.
- 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+.
|