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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -6
  3. data/ChangeLog +600 -0
  4. data/NEWS +426 -35
  5. data/README.md +27 -9
  6. data/dist-files +13 -2
  7. data/docs/bind-array-to-in_cond.md +38 -0
  8. data/docs/conflicts-local-connections-and-processes.md +98 -0
  9. data/docs/hanging-after-inactivity.md +63 -0
  10. data/docs/install-binary-package.md +15 -11
  11. data/docs/install-full-client.md +18 -21
  12. data/docs/install-instant-client.md +45 -27
  13. data/docs/install-on-osx.md +31 -117
  14. data/docs/ldap-auth-and-function-interposition.md +123 -0
  15. data/docs/number-type-mapping.md +79 -0
  16. data/docs/platform-specific-issues.md +17 -50
  17. data/docs/report-installation-issue.md +11 -8
  18. data/docs/timeout-parameters.md +94 -0
  19. data/ext/oci8/apiwrap.c.tmpl +2 -5
  20. data/ext/oci8/apiwrap.rb +6 -1
  21. data/ext/oci8/apiwrap.yml +39 -143
  22. data/ext/oci8/attr.c +4 -2
  23. data/ext/oci8/bind.c +421 -9
  24. data/ext/oci8/connection_pool.c +3 -3
  25. data/ext/oci8/encoding.c +5 -5
  26. data/ext/oci8/env.c +8 -2
  27. data/ext/oci8/error.c +24 -16
  28. data/ext/oci8/extconf.rb +35 -63
  29. data/ext/oci8/hook_funcs.c +274 -61
  30. data/ext/oci8/lob.c +31 -75
  31. data/ext/oci8/metadata.c +8 -6
  32. data/ext/oci8/object.c +119 -29
  33. data/ext/oci8/oci8.c +46 -133
  34. data/ext/oci8/oci8.h +40 -123
  35. data/ext/oci8/oci8lib.c +178 -46
  36. data/ext/oci8/ocihandle.c +37 -37
  37. data/ext/oci8/ocinumber.c +24 -35
  38. data/ext/oci8/oraconf.rb +168 -337
  39. data/ext/oci8/oradate.c +19 -19
  40. data/ext/oci8/plthook.h +10 -0
  41. data/ext/oci8/plthook_elf.c +433 -268
  42. data/ext/oci8/plthook_osx.c +40 -9
  43. data/ext/oci8/plthook_win32.c +16 -1
  44. data/ext/oci8/stmt.c +52 -17
  45. data/ext/oci8/win32.c +4 -22
  46. data/lib/oci8/bindtype.rb +10 -17
  47. data/lib/oci8/check_load_error.rb +57 -10
  48. data/lib/oci8/compat.rb +5 -1
  49. data/lib/oci8/connection_pool.rb +74 -3
  50. data/lib/oci8/cursor.rb +70 -31
  51. data/lib/oci8/metadata.rb +9 -1
  52. data/lib/oci8/object.rb +14 -1
  53. data/lib/oci8/oci8.rb +184 -58
  54. data/lib/oci8/ocihandle.rb +0 -16
  55. data/lib/oci8/oracle_version.rb +11 -1
  56. data/lib/oci8/properties.rb +55 -0
  57. data/lib/oci8/version.rb +1 -1
  58. data/lib/oci8.rb +48 -4
  59. data/lib/ruby-oci8.rb +1 -0
  60. data/pre-distclean.rb +1 -3
  61. data/ruby-oci8.gemspec +4 -9
  62. data/setup.rb +11 -2
  63. data/test/README.md +37 -0
  64. data/test/config.rb +8 -1
  65. data/test/setup_test_object.sql +42 -14
  66. data/test/setup_test_package.sql +59 -0
  67. data/test/test_all.rb +4 -0
  68. data/test/test_bind_array.rb +70 -0
  69. data/test/test_bind_boolean.rb +99 -0
  70. data/test/test_bind_integer.rb +47 -0
  71. data/test/test_break.rb +11 -9
  72. data/test/test_clob.rb +5 -17
  73. data/test/test_connstr.rb +142 -0
  74. data/test/test_datetime.rb +8 -3
  75. data/test/test_metadata.rb +2 -1
  76. data/test/test_object.rb +99 -18
  77. data/test/test_oci8.rb +170 -46
  78. data/test/test_oranumber.rb +12 -6
  79. data/test/test_package_type.rb +17 -3
  80. data/test/test_properties.rb +17 -0
  81. metadata +45 -55
  82. data/docs/osx-install-dev-tools.png +0 -0
  83. 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, Fixnum and Float are available, but Bignum is
62
- # not supported. If its initial value is NULL, please set nil to
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 Fixnum, Initial value is 1234.
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, Fixnum) # bind as Fixnum, Initial value is NULL.
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 [Fixnum] rows The number of rows to be prefetched
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
- # Returns the number of processed rows.
388
- #
389
- # @return [Integer]
390
- def row_count
391
- # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5498
392
- attr_get_ub4(9) # OCI_ATTR_ROW_COUNT(9)
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 Fixnum value undocumented in Oracle manuals.
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 Fixnums. (1, 2, 3, etc.)
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
- parm = __paramGet(i)
512
- define_one_column(i, parm) unless @define_handles[i - 1]
513
- @column_metadata[i - 1] = parm
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 __fetch(@con)
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 __fetch(@con)
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 datatype levels. This attribute always returns zero.
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
- tdo = @name_to_tdo && @name_to_tdo[typename]
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 or :SYSOPER to +privilege+, otherwise
67
- # "username/password as sysdba" or "username/password as sysoper"
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, mode = parse_connect_string(args[0])
99
+ username, password, dbname, privilege = parse_connect_string(args[0])
100
100
  else
101
- username, password, dbname, mode = args
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
- case mode
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 and OCI_LOGON2_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 and OCI_LOGON2_STMTCACHE
127
+ attach_mode |= 0x0004 # OCI_STMT_CACHE
135
128
  end
136
129
 
137
- if true
138
- # logon by the OCI function OCISessionBegin().
139
- allocate_handles()
140
- @session_handle.send(:attr_set_string, OCI_ATTR_USERNAME, username) if username
141
- @session_handle.send(:attr_set_string, OCI_ATTR_PASSWORD, password) if password
142
- if @@oracle_client_version >= ORAVER_11_1
143
- # Sets the driver name displayed in V$SESSION_CONNECT_INFO.CLIENT_DRIVER
144
- # if both the client and the server are Oracle 11g or upper.
145
- # Only the first 8 chracters "ruby-oci" are displayed when the Oracle
146
- # server version is lower than 12.0.1.2.
147
- # 424: OCI_ATTR_DRIVER_NAME
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
- unless defined? @oracle_server_version
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? Fixnum
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
@@ -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
@@ -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 = (@major << 24) | (@minor << 20) | (@update << 12) | (@patch << 8) | @port_update
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+.