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.
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+.