ruby-oci8 2.2.1 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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