ruby-oci8 2.2.1 → 2.2.2

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.
@@ -670,6 +670,15 @@ OCIObjectGetInd:
670
670
  - dvoid *instance
671
671
  - dvoid **null_struct
672
672
 
673
+ # round trip: 0
674
+ OCIObjectGetTypeRef:
675
+ :version: 800
676
+ :args:
677
+ - OCIEnv *env
678
+ - OCIError *err
679
+ - dvoid *instance
680
+ - OCIRef *type_ref
681
+
673
682
  # round trip: 0
674
683
  OCIObjectNew:
675
684
  :version: 800
@@ -159,6 +159,7 @@ if xsystem(cc_command("").gsub(CONFTEST_C, File.dirname(__FILE__) + "/" + plthoo
159
159
  $objs << plthook_src.gsub(/\.c$/, '.o')
160
160
  $objs << "hook_funcs.o"
161
161
  $defs << "-DHAVE_PLTHOOK"
162
+ have_library('dbghelp', 'ImageDirectoryEntryToData', ['windows.h', 'dbghelp.h']) if RUBY_PLATFORM =~ /cygwin/
162
163
  else
163
164
  puts "no"
164
165
  end
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * metadata.c
4
4
  *
5
- * Copyright (C) 2006-2015 Kubo Takehiro <kubo@jiubao.org>
5
+ * Copyright (C) 2006-2016 Kubo Takehiro <kubo@jiubao.org>
6
6
  *
7
7
  * implement private methods of classes in OCI8::Metadata module.
8
8
  *
@@ -145,7 +145,7 @@ static VALUE metadata_is_implicit_p(VALUE self)
145
145
  return md->is_implicit ? Qtrue : Qfalse;
146
146
  }
147
147
 
148
- static VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public)
148
+ VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public)
149
149
  {
150
150
  oci8_svcctx_t *svcctx = oci8_get_svcctx(self);
151
151
  OCIParam *parmhp;
@@ -274,4 +274,6 @@ void Init_oci8_metadata(VALUE cOCI8)
274
274
  rb_define_private_method(cOCI8, "__describe", oci8_describe, 3);
275
275
  rb_define_private_method(cOCI8MetadataBase, "__type_metadata", metadata_get_type_metadata, 1);
276
276
  rb_define_method(cOCI8MetadataBase, "tdo_id", metadata_get_tdo_id, 0);
277
+
278
+ rb_define_class_under(mOCI8Metadata, "Type", cOCI8MetadataBase);
277
279
  }
@@ -1,6 +1,6 @@
1
1
  /* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
2
2
  /*
3
- * Copyright (C) 2002-2014 Kubo Takehiro <kubo@jiubao.org>
3
+ * Copyright (C) 2002-2016 Kubo Takehiro <kubo@jiubao.org>
4
4
  */
5
5
 
6
6
  /*
@@ -20,8 +20,11 @@ static VALUE cOCI8TDO;
20
20
  static VALUE cOCI8NamedType;
21
21
  static VALUE cOCI8NamedCollection;
22
22
  static VALUE cOCI8BindNamedType;
23
+ static VALUE cOCI8MetadataType;
23
24
  static ID id_to_value;
24
25
  static ID id_set_attributes;
26
+ static ID id_get_tdo_by_metadata;
27
+ static ID id_at_is_final_type;
25
28
 
26
29
  #define TO_TDO(obj) ((oci8_base_t *)oci8_check_typeddata((obj), &oci8_tdo_data_type, 1))
27
30
  #define CHECK_TDO(obj) ((void)oci8_check_typeddata((obj), &oci8_tdo_data_type, 1))
@@ -137,10 +140,49 @@ static VALUE oci8_named_type_initialize(VALUE self)
137
140
  return Qnil;
138
141
  }
139
142
 
143
+ typedef struct get_tdo_by_instance_arg {
144
+ VALUE svc;
145
+ void *instance;
146
+ void *typeref;
147
+ } get_tdo_by_instance_arg_t;
148
+
149
+ static VALUE get_tdo_by_instance(VALUE data)
150
+ {
151
+ get_tdo_by_instance_arg_t *arg = (get_tdo_by_instance_arg_t *)data;
152
+ VALUE metadata;
153
+
154
+ chkerr(OCIObjectGetTypeRef(oci8_envhp, oci8_errhp, arg->instance, arg->typeref));
155
+ metadata = oci8_do_describe(arg->svc, arg->typeref, 0, OCI_OTYPE_REF, cOCI8MetadataType, Qfalse);
156
+ return rb_funcall(arg->svc, id_get_tdo_by_metadata, 1, metadata);
157
+ }
158
+
140
159
  static VALUE oci8_named_type_tdo(VALUE self)
141
160
  {
142
161
  oci8_named_type_t *obj = DATA_PTR(self);
143
- return obj->tdo;
162
+ VALUE tdo = obj->tdo;
163
+
164
+ if (!RTEST(rb_ivar_get(tdo, id_at_is_final_type))) {
165
+ /* The type of the instance may be a subtype. */
166
+ oci8_base_t *svcctx;
167
+ get_tdo_by_instance_arg_t arg;
168
+ int state = 0;
169
+
170
+ /* search svcctx */
171
+ svcctx = DATA_PTR(obj->tdo);
172
+ while (svcctx->type != OCI_HTYPE_SVCCTX) {
173
+ svcctx = svcctx->parent;
174
+ }
175
+
176
+ arg.svc = svcctx->self;
177
+ arg.instance = *obj->instancep;
178
+ chkerr(OCIObjectNew(oci8_envhp, oci8_errhp, svcctx->hp.svc, OCI_TYPECODE_REF, NULL, NULL, OCI_DURATION_DEFAULT, TRUE, &arg.typeref));
179
+ tdo = rb_protect(get_tdo_by_instance, (VALUE)&arg, &state);
180
+ chkerr(OCIObjectFree(oci8_envhp, oci8_errhp, arg.typeref, OCI_OBJECTFREE_FORCE));
181
+ if (state != 0) {
182
+ rb_jump_tag(state);
183
+ }
184
+ }
185
+ return tdo;
144
186
  }
145
187
 
146
188
  static void oci8_named_type_check_offset(VALUE self, VALUE val_offset, VALUE ind_offset, size_t val_size, void **instancep, OCIInd **indp)
@@ -754,8 +796,11 @@ static VALUE bind_named_type_alloc(VALUE klass)
754
796
 
755
797
  void Init_oci_object(VALUE cOCI8)
756
798
  {
799
+ cOCI8MetadataType = rb_eval_string("OCI8::Metadata::Type");
757
800
  id_to_value = rb_intern("to_value");
758
801
  id_set_attributes = rb_intern("attributes=");
802
+ id_get_tdo_by_metadata = rb_intern("get_tdo_by_metadata");
803
+ id_at_is_final_type = rb_intern("@is_final_type");
759
804
 
760
805
  /* OCI8::TDO */
761
806
  cOCI8TDO = oci8_define_class_under(cOCI8, "TDO", &oci8_tdo_data_type, oci8_tdo_alloc);
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * oci8.h - part of ruby-oci8
4
4
  *
5
- * Copyright (C) 2002-2015 Kubo Takehiro <kubo@jiubao.org>
5
+ * Copyright (C) 2002-2016 Kubo Takehiro <kubo@jiubao.org>
6
6
  */
7
7
  #ifndef _RUBY_OCI_H_
8
8
  #define _RUBY_OCI_H_ 1
@@ -442,6 +442,7 @@ void Init_oci8_bind(VALUE cOCI8BindTypeBase);
442
442
 
443
443
  /* metadata.c */
444
444
  extern const oci8_handle_data_type_t oci8_metadata_base_data_type;
445
+ VALUE oci8_do_describe(VALUE self, void *objptr, ub4 objlen, ub1 objtype, VALUE klass, VALUE check_public);
445
446
  void Init_oci8_metadata(VALUE cOCI8);
446
447
  VALUE oci8_metadata_create(OCIParam *parmhp, VALUE svc, VALUE parent);
447
448
 
@@ -13,7 +13,7 @@ module MiniRegistry
13
13
  @code = code
14
14
  end
15
15
  end
16
- if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
16
+ if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/
17
17
  # Windows
18
18
  require 'Win32API' # raise LoadError when UNIX.
19
19
 
@@ -390,7 +390,7 @@ EOS
390
390
  is_32bit = size_of_pointer == 4
391
391
  is_big_endian = "\x01\x02".unpack('s')[0] == 0x0102
392
392
  case RUBY_PLATFORM
393
- when /mswin32|cygwin|mingw32|bccwin32/
393
+ when /mswin32|mswin64|cygwin|mingw32|bccwin32/
394
394
  oci_basename = 'oci'
395
395
  oci_glob_postfix = ''
396
396
  nls_data_basename = ['oraociei*', 'oraociicus*']
@@ -522,10 +522,11 @@ EOS
522
522
  when /darwin/
523
523
  fallback_path = ENV['DYLD_FALLBACK_LIBRARY_PATH']
524
524
  if fallback_path.nil?
525
- fallback_path = "#{ENV['HOME']}/lib:/usr/local/lib:/lib:/usr/lib"
525
+ puts " DYLD_FALLBACK_LIBRARY_PATH is not set."
526
+ else
527
+ puts " checking DYLD_FALLBACK_LIBRARY_PATH..."
528
+ ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc)
526
529
  end
527
- puts " checking DYLD_FALLBACK_LIBRARY_PATH..."
528
- ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc)
529
530
  if ld_path.nil?
530
531
  puts " checking OCI_DIR..."
531
532
  ld_path, file = check_lib_in_path(ENV['OCI_DIR'], glob_name, check_proc)
@@ -562,6 +563,14 @@ EOS
562
563
  end
563
564
  end
564
565
  end
566
+ if ld_path.nil?
567
+ fallback_path = ENV['DYLD_FALLBACK_LIBRARY_PATH']
568
+ if fallback_path.nil?
569
+ fallback_path = "#{ENV['HOME']}/lib:/usr/local/lib:/lib:/usr/lib"
570
+ puts " checking the default value of DYLD_FALLBACK_LIBRARY_PATH..."
571
+ ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc)
572
+ end
573
+ end
565
574
  if ld_path.nil?
566
575
  raise <<EOS
567
576
  Set the environment variable DYLD_LIBRARY_PATH, DYLD_FALLBACK_LIBRARY_PATH or
@@ -598,7 +607,7 @@ EOS
598
607
  paths.split(File::PATH_SEPARATOR).each do |path|
599
608
  next if path.nil? or path == ''
600
609
  print " checking #{path}... "
601
- path.gsub!(/\\/, '/') if /mswin32|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM
610
+ path.gsub!(/\\/, '/') if /mswin32|mswin64|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM
602
611
  files = Dir.glob(File.join(path, glob_name))
603
612
  if files.empty?
604
613
  puts "no"
@@ -702,13 +711,15 @@ EOS
702
711
  end
703
712
  end
704
713
 
705
- if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
714
+ if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows
706
715
 
707
716
  def get_libs(lib_dir)
708
717
  case RUBY_PLATFORM
709
718
  when /cygwin|x64-mingw32/
710
719
  regex = ([nil].pack('P').size == 8) ? / T (OCI\w+)/ : / T _(OCI\w+)/
711
- oci_funcs = YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml'))
720
+ oci_funcs = YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml')).keys.collect do |func|
721
+ func =~ /_nb$/ ? $` : func
722
+ end
712
723
  open("OCI.def", "w") do |f|
713
724
  f.puts("EXPORTS")
714
725
  open("|nm #{lib_dir}/MSVC/OCI.LIB") do |r|
@@ -782,7 +793,7 @@ class OraConfFC < OraConf
782
793
  use_lib32 = false
783
794
  end
784
795
 
785
- if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/
796
+ if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/
786
797
  lib_dir = "#{@oracle_home}/oci/lib"
787
798
  elsif use_lib32
788
799
  lib_dir = "#{@oracle_home}/lib32"
@@ -801,7 +812,7 @@ class OraConfFC < OraConf
801
812
  print("Get the version of Oracle from SQL*Plus... ")
802
813
  STDOUT.flush
803
814
  version = nil
804
- dev_null = RUBY_PLATFORM =~ /mswin32|mingw32|bccwin32/ ? "nul" : "/dev/null"
815
+ dev_null = RUBY_PLATFORM =~ /mswin32|mswin64|mingw32|bccwin32/ ? "nul" : "/dev/null"
805
816
  if File.exist?("#{@oracle_home}/bin/plus80.exe")
806
817
  sqlplus = "plus80.exe"
807
818
  else
@@ -826,7 +837,7 @@ class OraConfFC < OraConf
826
837
  version
827
838
  end # get_version
828
839
 
829
- if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
840
+ if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows
830
841
 
831
842
  def is_valid_home?(oracle_home)
832
843
  return false if oracle_home.nil?
@@ -1026,7 +1037,7 @@ class OraConfIC < OraConf
1026
1037
  inc_dir = "#{ic_dir}/sdk/include"
1027
1038
  end
1028
1039
 
1029
- if RUBY_PLATFORM =~ /mswin32|cygwin|mingw32|bccwin32/ # when Windows
1040
+ if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows
1030
1041
  unless File.exist?("#{ic_dir}/sdk/lib/msvc/oci.lib")
1031
1042
  raise <<EOS
1032
1043
  Could not compile with Oracle instant client.
@@ -51,12 +51,18 @@
51
51
  #define __attribute__(arg)
52
52
  #endif
53
53
 
54
- #ifdef _WIN64
54
+ #if defined _LP64 /* data model: I32/LP64 */
55
+ #define SIZE_T_FMT "lu"
56
+ #elif defined _WIN64 /* data model: IL32/P64 */
55
57
  #define SIZE_T_FMT "I64u"
56
58
  #else
57
59
  #define SIZE_T_FMT "u"
58
60
  #endif
59
61
 
62
+ #ifdef __CYGWIN__
63
+ #define stricmp strcasecmp
64
+ #endif
65
+
60
66
  typedef struct {
61
67
  const char *mod_name;
62
68
  const char *name;
@@ -13,17 +13,88 @@ class OCI8
13
13
  #
14
14
  # This is equivalent to Oracle JDBC Driver {OCI Connection Pooling}[http://docs.oracle.com/cd/E11882_01/java.112/e16548/ociconpl.htm#JJDBC28789].
15
15
  #
16
- # Usage:
16
+ # Note that this is different with generally called connection pooling.
17
+ # Generally connection pooling caches connections in a pool.
18
+ # When an application needs a new connection, a connection is got from
19
+ # the pool. So that the amount of time to establish a connection is
20
+ # reduced. However connection pooling in ruby-oci8 is different with
21
+ # above.
22
+ #
23
+ # One Oracle connection is devided into two layers: One is physical
24
+ # connection such as TCP/IP. The other is logical connection which
25
+ # is used by an application. The connection pooling in ruby-oci8
26
+ # caches physical connections in a pool, not logical connections.
27
+ # When an application needs a new connection, a logical connection
28
+ # is created via a physical connection, which needs time to
29
+ # establish a connection unlike generally called connection pooling.
30
+ # When an application sends a query via a logical connection, a
31
+ # physical connection is got from the pool. The physical connection
32
+ # returns to the pool automatically when the query finishes.
33
+ # As long as logical connections are used sequentially, only one
34
+ # physical connection is used. When an application sends
35
+ # queries at a time, it needs more than one physical connection
36
+ # because one physical connection cannot transmit more then one
37
+ # query at a time.
38
+ #
39
+ # Example:
17
40
  # # Create a connection pool.
41
+ # # The number of initial physical connections: 1
42
+ # # The number of maximum physical connections: 5
43
+ # # the connection increment parameter: 2
18
44
  # # username and password are required to establish an implicit primary session.
19
45
  # cpool = OCI8::ConnectionPool.new(1, 5, 2, username, password, database)
20
46
  #
21
- # # Get a session from the pool.
47
+ # # The number of physical connections: 1
48
+ # # The number of logical connections: 0
49
+ #
50
+ # # Create a session.
22
51
  # # Pass the connection pool to the third argument.
23
52
  # conn1 = OCI8.new(username, password, cpool)
24
53
  #
25
- # # Get another session.
54
+ # # One logical connection was created.
55
+ # # The number of physical connections: 1
56
+ # # The number of logical connections: 1
57
+ #
58
+ # # Create another session.
26
59
  # conn2 = OCI8.new(username, password, cpool)
60
+ #
61
+ # # Another logical connection was created.
62
+ # # The number of physical connections: 1
63
+ # # The number of logical connections: 2
64
+ #
65
+ # # Use conn1 and conn2 sequentially
66
+ # conn1.exec('...')
67
+ # conn2.exec('...')
68
+ #
69
+ # # Both logical connections use one physical connection.
70
+ # # The number of physical connections: 1
71
+ # # The number of logical connections: 2
72
+ #
73
+ # thr1 = Thread.new do
74
+ # conn1.exec('...')
75
+ # end
76
+ # thr2 = Thread.new do
77
+ # conn2.exec('...')
78
+ # end
79
+ # thr1.join
80
+ # thr2.join
81
+ #
82
+ # # Logical connections cannot use one physical connection at a time.
83
+ # # So that the number of physical connections is incremented.
84
+ # # The number of physical connections: 3
85
+ # # The number of logical connections: 2
86
+ #
87
+ # conn1.logoff
88
+ #
89
+ # # One logical connection was closed.
90
+ # # The number of physical connections: 3
91
+ # # The number of logical connections: 1
92
+ #
93
+ # conn2.logoff
94
+ #
95
+ # # All logical connections were closed.
96
+ # # The number of physical connections: 3
97
+ # # The number of logical connections: 0
27
98
  #
28
99
  class ConnectionPool
29
100
 
@@ -384,12 +384,19 @@ class OCI8
384
384
  attr_set_ub4(11, rows) # OCI_ATTR_PREFETCH_ROWS(11)
385
385
  end
386
386
 
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)
387
+ if OCI8::oracle_client_version >= ORAVER_12_1
388
+ # Returns the number of processed rows.
389
+ #
390
+ # @return [Integer]
391
+ def row_count
392
+ # https://docs.oracle.com/database/121/LNOCI/ociaahan.htm#sthref5774
393
+ attr_get_ub8(457) # OCI_ATTR_UB8_ROW_COUNT(457)
394
+ end
395
+ else
396
+ def row_count
397
+ # http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/ociaahan.htm#sthref5498
398
+ attr_get_ub4(9) # OCI_ATTR_ROW_COUNT(9)
399
+ end
393
400
  end
394
401
 
395
402
  # Returns the text of the SQL statement prepared in the cursor.
@@ -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
@@ -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
@@ -128,6 +114,12 @@ class OCI8
128
114
  @pool = dbname # to prevent GC from freeing the connection pool.
129
115
  dbname = dbname.send(:pool_name)
130
116
  attach_mode |= 0x0200 # OCI_CPOOL and OCI_LOGON2_CPOOL
117
+ else
118
+ tcp_connect_timeout = OCI8::properties[:tcp_connect_timeout]
119
+ connect_timeout = OCI8::properties[:connect_timeout]
120
+ if tcp_connect_timeout || connect_timeout
121
+ dbname = to_connect_descriptor(dbname, tcp_connect_timeout, connect_timeout)
122
+ end
131
123
  end
132
124
  if stmt_cache_size
133
125
  # enable statement caching
@@ -148,7 +140,11 @@ class OCI8
148
140
  @session_handle.send(:attr_set_string, 424, "ruby-oci8 : #{OCI8::VERSION}")
149
141
  end
150
142
  server_attach(dbname, attach_mode)
151
- session_begin(cred ? cred : OCI_CRED_RDBMS, mode ? mode : OCI_DEFAULT)
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)
152
148
  else
153
149
  # logon by the OCI function OCILogon2().
154
150
  logon2(username, password, dbname, attach_mode)
@@ -389,6 +385,10 @@ class OCI8
389
385
  # Zero means no timeout.
390
386
  # This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF228 SQLNET.SEND_TIMEOUT} in client-side sqlnet.ora.
391
387
  #
388
+ # If you need to set send timeout while establishing a connection, use {file:docs/timeout-parameters.md timeout parameters in OCI8::properties} instead.
389
+ #
390
+ # Note that the connection becomes unusable on timeout.
391
+ #
392
392
  # If you have trouble by setting this, don't use it because it uses
393
393
  # {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
394
394
  #
@@ -417,6 +417,10 @@ class OCI8
417
417
  # Zero means no timeout.
418
418
  # This is equivalent to {http://docs.oracle.com/database/121/NETRF/sqlnet.htm#NETRF227 SQLNET.RECV_TIMEOUT} in client-side sqlnet.ora.
419
419
  #
420
+ # If you need to set receive timeout while establishing a connection, use {file:docs/timeout-parameters.md timeout parameters in OCI8::properties} instead.
421
+ #
422
+ # Note that the connection becomes unusable on timeout.
423
+ #
420
424
  # If you have trouble by setting this, don't use it because it uses
421
425
  # {http://blog.jiubao.org/2015/01/undocumented-oci-handle-attributes.html an undocumented OCI handle attribute}.
422
426
  #
@@ -442,6 +446,138 @@ class OCI8
442
446
  raise NotImplementedError, 'revc_timeout= is unimplemented in this Oracle version'
443
447
  end
444
448
  end
449
+
450
+ # A helper class to bind an array to paramters in IN-condition.
451
+ #
452
+ # See {file:docs/bind-array-to-in_cond.md Bind an Array to IN-condition}
453
+ class InCondBindHelper
454
+ def initialize(bind_name_prefix, array, type = nil, length = nil)
455
+ bind_name_prefix = bind_name_prefix.to_s
456
+ if bind_name_prefix !~ /^\w+$/
457
+ raise ArgumentError, "The first argument doesn't consist of alphanumeric characters and underscores."
458
+ end
459
+ if array.empty?
460
+ # This doesn't match anything.
461
+ # However in-condition requires at least one value.
462
+ @bind_names = ":#{bind_name_prefix}_0"
463
+ @bind_values = [[nil, type.nil? ? String : type, length]]
464
+ else
465
+ @bind_names = Array.new(array.length) do |index|
466
+ ":#{bind_name_prefix}_#{index}"
467
+ end.join(', ')
468
+ first_non_nil = array.find do |e|
469
+ !e.nil?
470
+ end
471
+ first_non_nil = '' if first_non_nil.nil?
472
+ @bind_values = array.collect do |elem|
473
+ if elem.nil? and type.nil?
474
+ [elem, first_non_nil.class]
475
+ else
476
+ [elem, type, length]
477
+ end
478
+ end
479
+ end
480
+ end
481
+
482
+ def names
483
+ @bind_names
484
+ end
485
+
486
+ def values
487
+ @bind_values
488
+ end
489
+ end
490
+
491
+ # Creates a helper object to bind an array to paramters in IN-condition.
492
+ #
493
+ # See {file:docs/bind-array-to-in_cond.md Bind an Array to IN-condition}
494
+ #
495
+ # @param [Symbol] bind_name_prefix prefix of the place holder name
496
+ # @param [Object] array an array of values to be bound.
497
+ # @param [Class] type data type. This is used as the third argument of {OCI8::Cursor#bind_param}.
498
+ # @param [Integer] length maximum bind length for string values. This is used as the fourth argument of {OCI8::Cursor#bind_param}.
499
+ # @return [OCI8::InCondBindHelper]
500
+ def self.in_cond(bind_name_prefix, array, type = nil, length = nil)
501
+ InCondBindHelper.new(bind_name_prefix, array, type, length)
502
+ end
503
+
504
+ private
505
+
506
+ # Converts the specified privilege name to the value passed to the
507
+ # fifth argument of OCISessionBegin().
508
+ #
509
+ # @private
510
+ def to_auth_mode(privilege)
511
+ case privilege
512
+ when :SYSDBA
513
+ 0x00000002 # OCI_SYSDBA in oci.h
514
+ when :SYSOPER
515
+ 0x00000004 # OCI_SYSOPER in oci.h
516
+ when :SYSASM
517
+ if OCI8.oracle_client_version < OCI8::ORAVER_11_1
518
+ raise "SYSASM is not supported on Oracle version #{OCI8.oracle_client_version}"
519
+ end
520
+ 0x00008000 # OCI_SYSASM in oci.h
521
+ when :SYSBACKUP
522
+ if OCI8.oracle_client_version < OCI8::ORAVER_12_1
523
+ raise "SYSBACKUP is not supported on Oracle version #{OCI8.oracle_client_version}"
524
+ end
525
+ 0x00020000 # OCI_SYSBKP in oci.h
526
+ when :SYSDG
527
+ if OCI8.oracle_client_version < OCI8::ORAVER_12_1
528
+ raise "SYSDG is not supported on Oracle version #{OCI8.oracle_client_version}"
529
+ end
530
+ 0x00040000 # OCI_SYSDGD in oci.h
531
+ when :SYSKM
532
+ if OCI8.oracle_client_version < OCI8::ORAVER_12_1
533
+ raise "SYSKM is not supported on Oracle version #{OCI8.oracle_client_version}"
534
+ end
535
+ 0x00080000 # OCI_SYSKMT in oci.h
536
+ when nil
537
+ 0 # OCI_DEFAULT
538
+ else
539
+ raise "unknown privilege type #{privilege}"
540
+ end
541
+ end
542
+
543
+ @@easy_connect_naming_regex = %r{
544
+ ^
545
+ (//)? # preceding double-slash($1)
546
+ (?:\[([\h:]+)\]|([^\s:/]+)) # IPv6 enclosed by square brackets($2) or hostname($3)
547
+ (?::(\d+))? # port($4)
548
+ (?:
549
+ /
550
+ ([^\s:/]+)? # service name($5)
551
+ (?::([^\s:/]+))? # server($6)
552
+ (?:/([^\s:/]+))? # instance name($7)
553
+ )?
554
+ $
555
+ }x
556
+
557
+ # Parse easy connect string as described in https://docs.oracle.com/database/121/NETAG/naming.htm
558
+ # and add TRANSPORT_CONNECT_TIMEOUT or CONNECT_TIMEOUT.
559
+ #
560
+ # @private
561
+ def to_connect_descriptor(database, tcp_connect_timeout, connect_timeout)
562
+ if @@easy_connect_naming_regex =~ database && ($1 || $2 || $4 || $5 || $6 || $7)
563
+ connect_data = []
564
+ connect_data << "(SERVICE_NAME=#$5)"
565
+ connect_data << "(SERVER=#$6)" if $6
566
+ connect_data << "(INSTANCE_NAME=#$7)" if $7
567
+ desc = []
568
+ desc << "(CONNECT_DATA=#{connect_data.join})"
569
+ desc << "(ADDRESS=(PROTOCOL=TCP)(HOST=#{$2 || $3})(PORT=#{$4 || 1521}))"
570
+ if tcp_connect_timeout
571
+ desc << "(TRANSPORT_CONNECT_TIMEOUT=#{tcp_connect_timeout})"
572
+ end
573
+ if connect_timeout
574
+ desc << "(CONNECT_TIMEOUT=#{connect_timeout})"
575
+ end
576
+ "(DESCRIPTION=#{desc.join})"
577
+ else
578
+ database
579
+ end
580
+ end
445
581
  end
446
582
 
447
583
  class OCIError