ruby-oci8 2.2.10-x64-mingw-ucrt
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 +14 -0
- data/COPYING +30 -0
- data/COPYING_old +64 -0
- data/ChangeLog +3826 -0
- data/Makefile +92 -0
- data/NEWS +1209 -0
- data/README.md +66 -0
- data/dist-files +112 -0
- 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 +44 -0
- data/docs/install-full-client.md +111 -0
- data/docs/install-instant-client.md +194 -0
- data/docs/install-on-osx.md +46 -0
- data/docs/ldap-auth-and-function-interposition.md +123 -0
- data/docs/number-type-mapping.md +79 -0
- data/docs/platform-specific-issues.md +164 -0
- data/docs/report-installation-issue.md +50 -0
- data/docs/timeout-parameters.md +94 -0
- data/lib/.document +1 -0
- data/lib/dbd/OCI8.rb +591 -0
- data/lib/oci8/.document +8 -0
- data/lib/oci8/bindtype.rb +333 -0
- data/lib/oci8/check_load_error.rb +146 -0
- data/lib/oci8/compat.rb +117 -0
- data/lib/oci8/connection_pool.rb +179 -0
- data/lib/oci8/cursor.rb +605 -0
- data/lib/oci8/datetime.rb +605 -0
- data/lib/oci8/encoding-init.rb +45 -0
- data/lib/oci8/encoding.yml +537 -0
- data/lib/oci8/metadata.rb +2148 -0
- data/lib/oci8/object.rb +641 -0
- data/lib/oci8/oci8.rb +756 -0
- data/lib/oci8/ocihandle.rb +591 -0
- data/lib/oci8/oracle_version.rb +153 -0
- data/lib/oci8/properties.rb +196 -0
- data/lib/oci8/version.rb +3 -0
- data/lib/oci8.rb +190 -0
- data/lib/oci8lib_310.so +0 -0
- data/lib/ruby-oci8.rb +1 -0
- data/metaconfig +142 -0
- data/pre-distclean.rb +7 -0
- data/ruby-oci8.gemspec +85 -0
- data/setup.rb +1342 -0
- data/test/README.md +37 -0
- data/test/config.rb +201 -0
- data/test/setup_test_object.sql +199 -0
- data/test/setup_test_package.sql +59 -0
- data/test/test_all.rb +56 -0
- data/test/test_appinfo.rb +62 -0
- data/test/test_array_dml.rb +332 -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_bind_raw.rb +45 -0
- data/test/test_bind_string.rb +105 -0
- data/test/test_bind_time.rb +177 -0
- data/test/test_break.rb +125 -0
- data/test/test_clob.rb +85 -0
- data/test/test_connection_pool.rb +124 -0
- data/test/test_connstr.rb +220 -0
- data/test/test_datetime.rb +585 -0
- data/test/test_dbi.rb +365 -0
- data/test/test_dbi_clob.rb +53 -0
- data/test/test_encoding.rb +103 -0
- data/test/test_error.rb +87 -0
- data/test/test_metadata.rb +2674 -0
- data/test/test_object.rb +546 -0
- data/test/test_oci8.rb +624 -0
- data/test/test_oracle_version.rb +68 -0
- data/test/test_oradate.rb +255 -0
- data/test/test_oranumber.rb +792 -0
- data/test/test_package_type.rb +981 -0
- data/test/test_properties.rb +17 -0
- data/test/test_rowid.rb +32 -0
- metadata +123 -0
data/lib/oci8/oci8.rb
ADDED
@@ -0,0 +1,756 @@
|
|
1
|
+
# oci8.rb -- OCI8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2002-2015 Kubo Takehiro <kubo@jiubao.org>
|
4
|
+
#
|
5
|
+
# Original Copyright is:
|
6
|
+
# Oracle module for Ruby
|
7
|
+
# 1998-2000 by yoshidam
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'date'
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
# A connection to a Oracle database server.
|
14
|
+
#
|
15
|
+
# example:
|
16
|
+
# # output the emp table's content as CSV format.
|
17
|
+
# conn = OCI8.new(username, password)
|
18
|
+
# conn.exec('select * from emp') do |row|
|
19
|
+
# puts row.join(',')
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # execute PL/SQL block with bind variables.
|
23
|
+
# conn = OCI8.new(username, password)
|
24
|
+
# conn.exec('BEGIN procedure_name(:1, :2); END;',
|
25
|
+
# value_for_the_first_parameter,
|
26
|
+
# value_for_the_second_parameter)
|
27
|
+
class OCI8
|
28
|
+
|
29
|
+
# @return [OCIError]
|
30
|
+
attr_accessor :last_error
|
31
|
+
|
32
|
+
# @overload initialize(username, password, dbname = nil, privilege = nil)
|
33
|
+
#
|
34
|
+
# Connects to an Oracle database server by +username+ and +password+
|
35
|
+
# at +dbname+ as +privilege+.
|
36
|
+
#
|
37
|
+
# === connecting to the local server
|
38
|
+
#
|
39
|
+
# Set +username+ and +password+ or pass "username/password" as a
|
40
|
+
# single argument.
|
41
|
+
#
|
42
|
+
# OCI8.new('scott', 'tiger')
|
43
|
+
# or
|
44
|
+
# OCI8.new('scott/tiger')
|
45
|
+
#
|
46
|
+
# === connecting to a remote server
|
47
|
+
#
|
48
|
+
# Set +username+, +password+ and +dbname+ or pass
|
49
|
+
# "username/password@dbname" as a single argument.
|
50
|
+
#
|
51
|
+
# OCI8.new('scott', 'tiger', 'orcl.world')
|
52
|
+
# or
|
53
|
+
# OCI8.new('scott/tiger@orcl.world')
|
54
|
+
#
|
55
|
+
# The +dbname+ is a net service name or an easy connectection
|
56
|
+
# identifier. The former is a name listed in the file tnsnames.ora.
|
57
|
+
# Ask to your DBA if you don't know what it is. The latter has the
|
58
|
+
# syntax as "//host:port/service_name".
|
59
|
+
#
|
60
|
+
# OCI8.new('scott', 'tiger', '//remote-host:1521/XE')
|
61
|
+
# or
|
62
|
+
# OCI8.new('scott/tiger@//remote-host:1521/XE')
|
63
|
+
#
|
64
|
+
# === connecting as a privileged user
|
65
|
+
#
|
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
|
+
#
|
70
|
+
# OCI8.new('sys', 'change_on_install', nil, :SYSDBA)
|
71
|
+
# or
|
72
|
+
# OCI8.new('sys/change_on_install as sysdba')
|
73
|
+
#
|
74
|
+
# === external OS authentication
|
75
|
+
#
|
76
|
+
# Set nil to +username+ and +password+, or "/" as a single argument.
|
77
|
+
#
|
78
|
+
# OCI8.new(nil, nil)
|
79
|
+
# or
|
80
|
+
# OCI8.new('/')
|
81
|
+
#
|
82
|
+
# To connect to a remote host:
|
83
|
+
#
|
84
|
+
# OCI8.new(nil, nil, 'dbname')
|
85
|
+
# or
|
86
|
+
# OCI8.new('/@dbname')
|
87
|
+
#
|
88
|
+
# === proxy authentication
|
89
|
+
#
|
90
|
+
# Enclose end user's username with square brackets and add it at the
|
91
|
+
# end of proxy user's username.
|
92
|
+
#
|
93
|
+
# OCI8.new('proxy_user_name[end_user_name]', 'proxy_password')
|
94
|
+
# or
|
95
|
+
# OCI8.new('proxy_user_name[end_user_name]/proxy_password')
|
96
|
+
#
|
97
|
+
def initialize(*args)
|
98
|
+
if args.length == 1
|
99
|
+
username, password, dbname, privilege = parse_connect_string(args[0])
|
100
|
+
else
|
101
|
+
username, password, dbname, privilege = args
|
102
|
+
end
|
103
|
+
|
104
|
+
if username.nil? and password.nil?
|
105
|
+
cred = OCI_CRED_EXT
|
106
|
+
end
|
107
|
+
auth_mode = to_auth_mode(privilege)
|
108
|
+
|
109
|
+
stmt_cache_size = OCI8.properties[:statement_cache_size]
|
110
|
+
stmt_cache_size = nil if stmt_cache_size == 0
|
111
|
+
|
112
|
+
attach_mode = 0
|
113
|
+
if dbname.is_a? OCI8::ConnectionPool
|
114
|
+
@pool = dbname # to prevent GC from freeing the connection pool.
|
115
|
+
dbname = dbname.send(:pool_name)
|
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
|
124
|
+
end
|
125
|
+
if stmt_cache_size
|
126
|
+
# enable statement caching
|
127
|
+
attach_mode |= 0x0004 # OCI_STMT_CACHE
|
128
|
+
end
|
129
|
+
|
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}")
|
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)
|
148
|
+
|
149
|
+
if stmt_cache_size
|
150
|
+
# set statement cache size
|
151
|
+
attr_set_ub4(176, stmt_cache_size) # 176: OCI_ATTR_STMTCACHESIZE
|
152
|
+
end
|
153
|
+
|
154
|
+
@prefetch_rows = 100
|
155
|
+
@username = nil
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns a prepared SQL handle.
|
159
|
+
#
|
160
|
+
# @param [String] sql SQL statement
|
161
|
+
# @return [OCI8::Cursor]
|
162
|
+
def parse(sql)
|
163
|
+
@last_error = nil
|
164
|
+
parse_internal(sql)
|
165
|
+
end
|
166
|
+
|
167
|
+
# same with OCI8#parse except that this doesn't reset OCI8#last_error.
|
168
|
+
#
|
169
|
+
# @private
|
170
|
+
def parse_internal(sql)
|
171
|
+
cursor = OCI8::Cursor.new(self, sql)
|
172
|
+
cursor
|
173
|
+
end
|
174
|
+
|
175
|
+
# Executes the sql statement. The type of return value depends on
|
176
|
+
# the type of sql statement: select; insert, update and delete;
|
177
|
+
# create, alter and drop; and PL/SQL.
|
178
|
+
#
|
179
|
+
# When bindvars are specified, they are bound as bind variables
|
180
|
+
# before execution.
|
181
|
+
#
|
182
|
+
# == select statements without block
|
183
|
+
# It returns the instance of OCI8::Cursor.
|
184
|
+
#
|
185
|
+
# example:
|
186
|
+
# conn = OCI8.new('scott', 'tiger')
|
187
|
+
# cursor = conn.exec('SELECT * FROM emp')
|
188
|
+
# while r = cursor.fetch()
|
189
|
+
# puts r.join(',')
|
190
|
+
# end
|
191
|
+
# cursor.close
|
192
|
+
# conn.logoff
|
193
|
+
#
|
194
|
+
# == select statements with a block
|
195
|
+
# It acts as iterator and returns the processed row counts. Fetched
|
196
|
+
# data is passed to the block as array. NULL value becomes nil in ruby.
|
197
|
+
#
|
198
|
+
# example:
|
199
|
+
# conn = OCI8.new('scott', 'tiger')
|
200
|
+
# num_rows = conn.exec('SELECT * FROM emp') do |r|
|
201
|
+
# puts r.join(',')
|
202
|
+
# end
|
203
|
+
# puts num_rows.to_s + ' rows were processed.'
|
204
|
+
# conn.logoff
|
205
|
+
#
|
206
|
+
# == PL/SQL block (ruby-oci8 1.0)
|
207
|
+
# It returns the array of bind variables' values.
|
208
|
+
#
|
209
|
+
# example:
|
210
|
+
# conn = OCI8.new('scott', 'tiger')
|
211
|
+
# conn.exec("BEGIN :str := TO_CHAR(:num, 'FM0999'); END;", 'ABCD', 123)
|
212
|
+
# # => ["0123", 123]
|
213
|
+
# conn.logoff
|
214
|
+
#
|
215
|
+
# Above example uses two bind variables which names are :str
|
216
|
+
# and :num. These initial values are "the string whose width
|
217
|
+
# is 4 and whose value is 'ABCD'" and "the number whose value is
|
218
|
+
# 123". This method returns the array of these bind variables,
|
219
|
+
# which may modified by PL/SQL statement. The order of array is
|
220
|
+
# same with that of bind variables.
|
221
|
+
#
|
222
|
+
# If a block is given, it is ignored.
|
223
|
+
#
|
224
|
+
# == PL/SQL block (ruby-oci8 2.0)
|
225
|
+
# It returns the number of processed rows.
|
226
|
+
#
|
227
|
+
# example:
|
228
|
+
# conn = OCI8.new('scott', 'tiger')
|
229
|
+
# conn.exec("BEGIN :str := TO_CHAR(:num, 'FM0999'); END;", 'ABCD', 123)
|
230
|
+
# # => 1
|
231
|
+
# conn.logoff
|
232
|
+
#
|
233
|
+
# If a block is given, the bind variables' values are passed to the block after
|
234
|
+
# executed.
|
235
|
+
#
|
236
|
+
# conn = OCI8.new('scott', 'tiger')
|
237
|
+
# conn.exec("BEGIN :str := TO_CHAR(:num, 'FM0999'); END;", 'ABCD', 123) do |str, num|
|
238
|
+
# puts str # => '0123'
|
239
|
+
# puts num # => 123
|
240
|
+
# end
|
241
|
+
# conn.logoff
|
242
|
+
#
|
243
|
+
# FYI, the following code do same on ruby-oci8 1.0 and ruby-oci8 2.0.
|
244
|
+
# conn.exec(sql, *bindvars) { |*outvars| outvars }
|
245
|
+
#
|
246
|
+
# == Other SQL statements
|
247
|
+
# It returns the number of processed rows.
|
248
|
+
#
|
249
|
+
# example:
|
250
|
+
# conn = OCI8.new('scott', 'tiger')
|
251
|
+
# num_rows = conn.exec('UPDATE emp SET sal = sal * 1.1')
|
252
|
+
# puts num_rows.to_s + ' rows were updated.'
|
253
|
+
# conn.logoff
|
254
|
+
#
|
255
|
+
# example:
|
256
|
+
# conn = OCI8.new('scott', 'tiger')
|
257
|
+
# conn.exec('CREATE TABLE test (col1 CHAR(6))') # => 0
|
258
|
+
# conn.logoff
|
259
|
+
#
|
260
|
+
def exec(sql, *bindvars, &block)
|
261
|
+
@last_error = nil
|
262
|
+
exec_internal(sql, *bindvars, &block)
|
263
|
+
end
|
264
|
+
|
265
|
+
# same with OCI8#exec except that this doesn't reset OCI8#last_error.
|
266
|
+
#
|
267
|
+
# @private
|
268
|
+
def exec_internal(sql, *bindvars)
|
269
|
+
begin
|
270
|
+
cursor = parse(sql)
|
271
|
+
ret = cursor.exec(*bindvars)
|
272
|
+
case cursor.type
|
273
|
+
when :select_stmt
|
274
|
+
if block_given?
|
275
|
+
cursor.fetch { |row| yield(row) } # for each row
|
276
|
+
ret = cursor.row_count()
|
277
|
+
else
|
278
|
+
ret = cursor
|
279
|
+
cursor = nil # unset cursor to skip cursor.close in ensure block
|
280
|
+
ret
|
281
|
+
end
|
282
|
+
when :begin_stmt, :declare_stmt # PL/SQL block
|
283
|
+
if block_given?
|
284
|
+
ary = []
|
285
|
+
cursor.keys.sort.each do |key|
|
286
|
+
ary << cursor[key]
|
287
|
+
end
|
288
|
+
yield(*ary)
|
289
|
+
else
|
290
|
+
ret
|
291
|
+
end
|
292
|
+
else
|
293
|
+
ret # number of rows processed
|
294
|
+
end
|
295
|
+
ensure
|
296
|
+
cursor.nil? || cursor.close
|
297
|
+
end
|
298
|
+
end # exec
|
299
|
+
|
300
|
+
# Executes a SQL statement and fetches the first one row.
|
301
|
+
#
|
302
|
+
# @param [String] sql SQL statement
|
303
|
+
# @param [Object] bindvars bind variables
|
304
|
+
# @return [Array] an array of first row.
|
305
|
+
def select_one(sql, *bindvars)
|
306
|
+
cursor = self.parse(sql)
|
307
|
+
cursor.prefetch_rows = 1
|
308
|
+
begin
|
309
|
+
cursor.exec(*bindvars)
|
310
|
+
row = cursor.fetch
|
311
|
+
ensure
|
312
|
+
cursor.close
|
313
|
+
end
|
314
|
+
return row
|
315
|
+
end
|
316
|
+
|
317
|
+
def username
|
318
|
+
@username || begin
|
319
|
+
exec('select user from dual') do |row|
|
320
|
+
@username = row[0]
|
321
|
+
end
|
322
|
+
@username
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Sets the prefetch rows size. The default value is 100.
|
327
|
+
# When a select statement is executed, the OCI library allocate
|
328
|
+
# prefetch buffer to reduce the number of network round trips by
|
329
|
+
# retrieving specified number of rows in one round trip.
|
330
|
+
#
|
331
|
+
# Note: The default value had been 1 before ruby-oci8 2.2.0.
|
332
|
+
def prefetch_rows=(num)
|
333
|
+
@prefetch_rows = num
|
334
|
+
end
|
335
|
+
|
336
|
+
# @private
|
337
|
+
def inspect
|
338
|
+
"#<OCI8:#{username}>"
|
339
|
+
end
|
340
|
+
|
341
|
+
# Returns the Oracle server version.
|
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
|
+
#
|
349
|
+
# @see OCI8.oracle_client_version
|
350
|
+
# @return [OCI8::OracleVersion]
|
351
|
+
def oracle_server_version
|
352
|
+
@oracle_server_version ||= OCI8::OracleVersion.new(oracle_server_vernum)
|
353
|
+
end
|
354
|
+
|
355
|
+
# Returns the Oracle database character set name such as AL32UTF8.
|
356
|
+
#
|
357
|
+
# @since 2.1.0
|
358
|
+
# @return [String] Oracle database character set name
|
359
|
+
def database_charset_name
|
360
|
+
charset_id2name(@server_handle.send(:attr_get_ub2, OCI_ATTR_CHARSET_ID))
|
361
|
+
end
|
362
|
+
|
363
|
+
# Returns the client-side Oracle character set name such as AL32UTF8.
|
364
|
+
#
|
365
|
+
# @since 2.1.0
|
366
|
+
# @return [String] client-side character set name
|
367
|
+
# @private
|
368
|
+
# @see OCI8.encoding
|
369
|
+
def self.client_charset_name
|
370
|
+
@@client_charset_name
|
371
|
+
end
|
372
|
+
|
373
|
+
if OCI8.oracle_client_version >= OCI8::ORAVER_11_1
|
374
|
+
# Returns send timeout in seconds.
|
375
|
+
# Zero means no timeout.
|
376
|
+
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF228 SQLNET.SEND_TIMEOUT} in client-side sqlnet.ora.
|
377
|
+
#
|
378
|
+
# @return [Float] seconds
|
379
|
+
# @see #recv_timeout
|
380
|
+
# @since 2.1.8 and Oracle 11.1
|
381
|
+
def send_timeout
|
382
|
+
# OCI_ATTR_SEND_TIMEOUT = 435
|
383
|
+
@server_handle.send(:attr_get_ub4, 435).to_f / 1000
|
384
|
+
end
|
385
|
+
|
386
|
+
# Sets send timeout in seconds.
|
387
|
+
# Zero means no timeout.
|
388
|
+
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF228 SQLNET.SEND_TIMEOUT} in client-side sqlnet.ora.
|
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
|
+
#
|
394
|
+
# If you have trouble by setting this, don't use it because it uses
|
395
|
+
# {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
|
396
|
+
#
|
397
|
+
# @param [Float] timeout
|
398
|
+
# @return [void]
|
399
|
+
# @see #recv_timeout=
|
400
|
+
# @since 2.1.8 and Oracle 11.1
|
401
|
+
def send_timeout=(timeout)
|
402
|
+
# OCI_ATTR_SEND_TIMEOUT = 435
|
403
|
+
@server_handle.send(:attr_set_ub4, 435, timeout * 1000)
|
404
|
+
end
|
405
|
+
|
406
|
+
# Returns receive timeout in seconds.
|
407
|
+
# Zero means no timeout.
|
408
|
+
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF227 SQLNET.RECV_TIMEOUT} in client-side sqlnet.ora.
|
409
|
+
#
|
410
|
+
# @return [Float] seconds
|
411
|
+
# @see #send_timeout
|
412
|
+
# @since 2.1.8 and Oracle 11.1
|
413
|
+
def recv_timeout
|
414
|
+
# OCI_ATTR_RECEIVE_TIMEOUT = 436
|
415
|
+
@server_handle.send(:attr_get_ub4, 436).to_f / 1000
|
416
|
+
end
|
417
|
+
|
418
|
+
# Sets receive timeout in seconds.
|
419
|
+
# Zero means no timeout.
|
420
|
+
# This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF227 SQLNET.RECV_TIMEOUT} in client-side sqlnet.ora.
|
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
|
+
#
|
426
|
+
# If you have trouble by setting this, don't use it because it uses
|
427
|
+
# {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
|
428
|
+
#
|
429
|
+
# @param [Float] timeout
|
430
|
+
# @return [void]
|
431
|
+
# @see #send_timeout=
|
432
|
+
# @since 2.1.8 and Oracle 11.1
|
433
|
+
def recv_timeout=(timeout)
|
434
|
+
# OCI_ATTR_RECEIVE_TIMEOUT = 436
|
435
|
+
@server_handle.send(:attr_set_ub4, 436, timeout * 1000)
|
436
|
+
end
|
437
|
+
else
|
438
|
+
def send_timeout
|
439
|
+
raise NotImplementedError, 'send_timeout is unimplemented in this Oracle version'
|
440
|
+
end
|
441
|
+
def send_timeout=(timeout)
|
442
|
+
raise NotImplementedError, 'send_timeout= is unimplemented in this Oracle version'
|
443
|
+
end
|
444
|
+
def recv_timeout
|
445
|
+
raise NotImplementedError, 'recv_timeout is unimplemented in this Oracle version'
|
446
|
+
end
|
447
|
+
def recv_timeout=(timeout)
|
448
|
+
raise NotImplementedError, 'revc_timeout= is unimplemented in this Oracle version'
|
449
|
+
end
|
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
|
586
|
+
end
|
587
|
+
|
588
|
+
class OCIError
|
589
|
+
|
590
|
+
# @overload initialize(message, error_code = nil, sql_stmt = nil, parse_error_offset = nil)
|
591
|
+
# Creates a new OCIError object with specified parameters.
|
592
|
+
#
|
593
|
+
# @param [String] message error message
|
594
|
+
# @param [Integer] error_code Oracle error code
|
595
|
+
# @param [String] sql_stmt SQL statement
|
596
|
+
# @param [Integer] parse_error_offset
|
597
|
+
#
|
598
|
+
# @example
|
599
|
+
# OCIError.new("ORA-00001: unique constraint (%s.%s) violated", 1, 'insert into table_name values (1)', )
|
600
|
+
# # => #<OCIError: ORA-00001: unique constraint (%s.%s) violated>
|
601
|
+
# #<OCIError: ORA-00923: FROM keyword not found where expected>
|
602
|
+
# "select sysdate"
|
603
|
+
# 923
|
604
|
+
# 14
|
605
|
+
#
|
606
|
+
# @overload initialize(error_code, *params)
|
607
|
+
# Creates a new OCIError object with the error message which corresponds to the specified
|
608
|
+
# Oracle error code.
|
609
|
+
#
|
610
|
+
# @param [Integer] error_code Oracle error code
|
611
|
+
# @param [String, ...] params parameters which replace '%s'
|
612
|
+
#
|
613
|
+
# @example
|
614
|
+
# # without parameters
|
615
|
+
# OCIError.new(4043)
|
616
|
+
# # When NLS_LANG=american_america.AL32UTF8
|
617
|
+
# # => #<OCIError: ORA-04043: object %s does not exist>
|
618
|
+
# # When NLS_LANG=german_germany.AL32UTF8
|
619
|
+
# # => #<OCIError: ORA-04043: Objekt %s ist nicht vorhanden>
|
620
|
+
#
|
621
|
+
# # with one parameter
|
622
|
+
# OCIError.new(4043, 'table_name')
|
623
|
+
# # When NLS_LANG=american_america.AL32UTF8
|
624
|
+
# # => #<OCIError: ORA-04043: object table_name does not exist>
|
625
|
+
# # When NLS_LANG=german_germany.AL32UTF8
|
626
|
+
# # => #<OCIError: ORA-04043: Objekt table_name ist nicht vorhanden>
|
627
|
+
#
|
628
|
+
def initialize(*args)
|
629
|
+
if args.length > 0
|
630
|
+
if args[0].is_a? Integer
|
631
|
+
@code = args.shift
|
632
|
+
super(OCI8.error_message(@code).gsub('%s') {|s| args.empty? ? '%s' : args.shift})
|
633
|
+
@sql = nil
|
634
|
+
@parse_error_offset = nil
|
635
|
+
else
|
636
|
+
msg, @code, @sql, @parse_error_offset = args
|
637
|
+
super(msg)
|
638
|
+
end
|
639
|
+
else
|
640
|
+
super()
|
641
|
+
end
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
class OraDate
|
646
|
+
|
647
|
+
# Returns a Time object which denotes self.
|
648
|
+
def to_time
|
649
|
+
begin
|
650
|
+
Time.local(year, month, day, hour, minute, second)
|
651
|
+
rescue ArgumentError
|
652
|
+
msg = format("out of range of Time (expect between 1970-01-01 00:00:00 UTC and 2037-12-31 23:59:59, but %04d-%02d-%02d %02d:%02d:%02d %s)", year, month, day, hour, minute, second, Time.at(0).zone)
|
653
|
+
raise RangeError.new(msg)
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
# Returns a Date object which denotes self.
|
658
|
+
def to_date
|
659
|
+
Date.new(year, month, day)
|
660
|
+
end
|
661
|
+
|
662
|
+
# timezone offset of the time the command started
|
663
|
+
# @private
|
664
|
+
@@tz_offset = Time.now.utc_offset.to_r/86400
|
665
|
+
|
666
|
+
# Returns a DateTime object which denotes self.
|
667
|
+
#
|
668
|
+
# Note that this is not daylight saving time aware.
|
669
|
+
# The Time zone offset is that of the time the command started.
|
670
|
+
def to_datetime
|
671
|
+
DateTime.new(year, month, day, hour, minute, second, @@tz_offset)
|
672
|
+
end
|
673
|
+
|
674
|
+
# @private
|
675
|
+
def yaml_initialize(type, val)
|
676
|
+
initialize(*val.split(/[ -\/:]+/).collect do |i| i.to_i end)
|
677
|
+
end
|
678
|
+
|
679
|
+
# @private
|
680
|
+
def to_yaml(opts = {})
|
681
|
+
YAML.quick_emit(object_id, opts) do |out|
|
682
|
+
out.scalar(taguri, self.to_s, :plain)
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
# @private
|
687
|
+
def to_json(options=nil)
|
688
|
+
to_datetime.to_json(options)
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
class OraNumber
|
693
|
+
|
694
|
+
if defined? Psych and YAML == Psych
|
695
|
+
|
696
|
+
yaml_tag '!ruby/object:OraNumber'
|
697
|
+
|
698
|
+
# @private
|
699
|
+
def encode_with coder
|
700
|
+
coder.scalar = self.to_s
|
701
|
+
end
|
702
|
+
|
703
|
+
# @private
|
704
|
+
def init_with coder
|
705
|
+
initialize(coder.scalar)
|
706
|
+
end
|
707
|
+
|
708
|
+
else
|
709
|
+
|
710
|
+
# @private
|
711
|
+
def yaml_initialize(type, val)
|
712
|
+
initialize(val)
|
713
|
+
end
|
714
|
+
|
715
|
+
# @private
|
716
|
+
def to_yaml(opts = {})
|
717
|
+
YAML.quick_emit(object_id, opts) do |out|
|
718
|
+
out.scalar(taguri, self.to_s, :plain)
|
719
|
+
end
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
# @private
|
724
|
+
def to_json(options=nil)
|
725
|
+
to_s
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
class Numeric
|
730
|
+
# Converts +self+ to {OraNumber}.
|
731
|
+
#
|
732
|
+
# @return [OraNumber]
|
733
|
+
def to_onum
|
734
|
+
OraNumber.new(self)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
class String # :nodoc:
|
739
|
+
|
740
|
+
# Converts +self+ to {OraNumber}.
|
741
|
+
# Optional <i>format</i> and <i>nls_params</i> is used as
|
742
|
+
# {http://docs.oracle.com/cd/E11882_01/server.112/e17118/functions211.htm Oracle SQL function TO_NUMBER}
|
743
|
+
# does.
|
744
|
+
#
|
745
|
+
# @example
|
746
|
+
# '123456.789'.to_onum # => #<OraNumber:123456.789>
|
747
|
+
# '123,456.789'.to_onum('999,999,999.999') # => #<OraNumber:123456.789>
|
748
|
+
# '123.456,789'.to_onum('999G999G999D999', "NLS_NUMERIC_CHARACTERS = ',.'") # => #<OraNumber:123456.789>
|
749
|
+
#
|
750
|
+
# @param [String] format
|
751
|
+
# @param [String] nls_params
|
752
|
+
# @return [OraNumber]
|
753
|
+
def to_onum(format = nil, nls_params = nil)
|
754
|
+
OraNumber.new(self, format, nls_params)
|
755
|
+
end
|
756
|
+
end
|