ruby-oci8 2.0.6 → 2.1.0

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 (59) hide show
  1. data/ChangeLog +366 -19
  2. data/Makefile +2 -8
  3. data/NEWS +111 -0
  4. data/README +4 -85
  5. data/VERSION +1 -1
  6. data/dist-files +9 -2
  7. data/ext/oci8/.document +1 -0
  8. data/ext/oci8/apiwrap.c.tmpl +12 -2
  9. data/ext/oci8/apiwrap.yml +37 -21
  10. data/ext/oci8/attr.c +23 -74
  11. data/ext/oci8/bind.c +93 -225
  12. data/ext/oci8/connection_pool.c +201 -0
  13. data/ext/oci8/encoding.c +117 -24
  14. data/ext/oci8/env.c +5 -10
  15. data/ext/oci8/error.c +171 -189
  16. data/ext/oci8/extconf.rb +6 -2
  17. data/ext/oci8/lob.c +81 -79
  18. data/ext/oci8/metadata.c +42 -177
  19. data/ext/oci8/object.c +55 -28
  20. data/ext/oci8/oci8.c +426 -294
  21. data/ext/oci8/oci8.h +84 -51
  22. data/ext/oci8/oci8lib.c +75 -53
  23. data/ext/oci8/ocidatetime.c +67 -88
  24. data/ext/oci8/ocihandle.c +78 -37
  25. data/ext/oci8/ocinumber.c +166 -109
  26. data/ext/oci8/oraconf.rb +68 -157
  27. data/ext/oci8/oradate.c +2 -7
  28. data/ext/oci8/stmt.c +40 -183
  29. data/ext/oci8/thread_util.c +85 -0
  30. data/ext/oci8/thread_util.h +30 -0
  31. data/lib/oci8.rb.in +19 -13
  32. data/lib/oci8/.document +2 -0
  33. data/lib/oci8/bindtype.rb +62 -45
  34. data/lib/oci8/connection_pool.rb +118 -0
  35. data/lib/oci8/datetime.rb +304 -320
  36. data/lib/oci8/encoding-init.rb +62 -30
  37. data/lib/oci8/encoding.yml +3 -3
  38. data/lib/oci8/metadata.rb +552 -497
  39. data/lib/oci8/object.rb +9 -9
  40. data/lib/oci8/oci8.rb +161 -2
  41. data/lib/oci8/ocihandle.rb +427 -0
  42. data/lib/oci8/properties.rb +31 -1
  43. data/ruby-oci8.gemspec +10 -3
  44. data/test/README +41 -3
  45. data/test/config.rb +16 -0
  46. data/test/test_all.rb +3 -0
  47. data/test/test_bind_string.rb +106 -0
  48. data/test/test_break.rb +33 -7
  49. data/test/test_clob.rb +13 -10
  50. data/test/test_connection_pool.rb +125 -0
  51. data/test/test_connstr.rb +2 -2
  52. data/test/test_datetime.rb +26 -66
  53. data/test/test_encoding.rb +7 -3
  54. data/test/test_error.rb +88 -0
  55. data/test/test_metadata.rb +1356 -204
  56. data/test/test_oci8.rb +27 -8
  57. data/test/test_oranumber.rb +41 -0
  58. metadata +34 -9
  59. data/ext/oci8/xmldb.c +0 -383
@@ -0,0 +1,85 @@
1
+ /* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * thread_util.c - part of ruby-oci8
4
+ *
5
+ * Copyright (C) 2011 KUBO Takehiro <kubo@jiubao.org>
6
+ */
7
+ #include "oci8.h"
8
+ #include <errno.h>
9
+
10
+ #ifdef USE_THREAD_LOCAL_ERRHP
11
+
12
+ #ifndef WIN32
13
+ #include <pthread.h>
14
+ static pthread_attr_t detached_thread_attr;
15
+ #endif
16
+
17
+ typedef struct {
18
+ rb_blocking_function_t *func;
19
+ void *arg;
20
+ } adapter_arg_t;
21
+
22
+ void Init_oci8_thread_util(void)
23
+ {
24
+ #ifndef WIN32
25
+ pthread_attr_init(&detached_thread_attr);
26
+ pthread_attr_setdetachstate(&detached_thread_attr, PTHREAD_CREATE_DETACHED);
27
+ #endif
28
+ }
29
+
30
+ #ifdef WIN32
31
+
32
+ static void __cdecl adapter(void *arg)
33
+ {
34
+ adapter_arg_t *aa = (adapter_arg_t *)arg;
35
+ aa->func(aa->arg);
36
+ free(aa);
37
+ }
38
+
39
+ int oci8_run_native_thread(rb_blocking_function_t func, void *arg)
40
+ {
41
+ adapter_arg_t *aa = malloc(sizeof(adapter_arg_t));
42
+ if (aa == NULL) {
43
+ return ENOMEM;
44
+ }
45
+
46
+ aa->func = func;
47
+ aa->arg = arg;
48
+ if (_beginthread(adapter, 0, aa) == (uintptr_t)-1L) {
49
+ int err = errno;
50
+ free(aa);
51
+ return err;
52
+ }
53
+ return 0;
54
+ }
55
+
56
+ #else /* WIN32 */
57
+
58
+ static void *adapter(void *arg)
59
+ {
60
+ adapter_arg_t *aa = (adapter_arg_t *)arg;
61
+ aa->func(aa->arg);
62
+ free(aa);
63
+ return NULL;
64
+ }
65
+
66
+ int oci8_run_native_thread(rb_blocking_function_t func, void *arg)
67
+ {
68
+ pthread_t thread;
69
+ adapter_arg_t *aa = malloc(sizeof(adapter_arg_t));
70
+ int rv;
71
+ if (aa == NULL) {
72
+ return ENOMEM;
73
+ }
74
+
75
+ aa->func = func;
76
+ aa->arg = arg;
77
+ rv = pthread_create(&thread, &detached_thread_attr, adapter, aa);
78
+ if (rv != 0) {
79
+ free(aa);
80
+ }
81
+ return rv;
82
+ }
83
+ #endif /* WIN32 */
84
+
85
+ #endif /* USE_THREAD_LOCAL_ERRHP */
@@ -0,0 +1,30 @@
1
+ /* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * thread_util.h - part of ruby-oci8
4
+ *
5
+ * Copyright (C) 2011 KUBO Takehiro <kubo@jiubao.org>
6
+ */
7
+ #ifdef USE_THREAD_LOCAL_ERRHP
8
+
9
+ /*
10
+ * Prepare to execute thread-related functions.
11
+ */
12
+ void Init_oci8_thread_util(void);
13
+
14
+ /*
15
+ * Run the func in a new native thread.
16
+ * Don't call any ruby functions in the func.
17
+ * The return value is errno.
18
+ */
19
+ int oci8_run_native_thread(rb_blocking_function_t func, void *arg);
20
+
21
+ #else
22
+
23
+ /*
24
+ * For ruby 1.8 configured without --enable-pthread on Unix.
25
+ */
26
+
27
+ #define Init_oci8_thread_util() do {} while (0)
28
+ #define oci8_run_native_thread(func, arg) ((func)(arg), 0)
29
+
30
+ #endif
@@ -6,16 +6,21 @@
6
6
  # ruby -r oci8 -e 'OCI8.new("scott", "tiger", nil).exec("select * from emp") do |r| puts r.join(","); end'
7
7
  # # select all data from emp and print them as CVS format.
8
8
 
9
+ ENV['ORA_SDTZ'] = ENV['TZ'] unless ENV['ORA_SDTZ']
10
+
9
11
  if RUBY_PLATFORM =~ /cygwin/
10
12
  # Cygwin manages environment variables by itself.
11
13
  # They don't synchroize with Win32's ones.
12
14
  # This set some Oracle's environment variables to win32's enviroment.
13
15
  require 'Win32API'
14
16
  win32setenv = Win32API.new('Kernel32.dll', 'SetEnvironmentVariableA', 'PP', 'I')
15
- ['NLS_LANG', 'ORA_NLS10', 'ORA_NLS32', 'ORA_NLS33', 'ORACLE_BASE', 'ORACLE_HOME', 'ORACLE_SID', 'TNS_ADMIN', 'LOCAL'].each do |name|
17
+ ['NLS_LANG', 'TNS_ADMIN', 'LOCAL'].each do |name|
16
18
  val = ENV[name]
17
19
  win32setenv.call(name, val && val.dup)
18
20
  end
21
+ ENV.each do |name, val|
22
+ win32setenv.call(name, val && val.dup) if name =~ /^ORA/
23
+ end
19
24
  end
20
25
 
21
26
  so_basename = 'oci8lib_'
@@ -28,7 +33,7 @@ end
28
33
  # 191 - ruby 1.9.1 and 1.9.2
29
34
  # 19x - ruby 1.9.x future version which will break the API compatibility
30
35
  case RUBY_VERSION
31
- when /^1\.9/
36
+ when /^1\.9/, /^2\.0/
32
37
  so_basename += '191'
33
38
  when /^1\.8/
34
39
  so_basename += '18'
@@ -37,14 +42,13 @@ else
37
42
  end
38
43
  require so_basename
39
44
 
40
- if OCI8.respond_to? :encoding
41
- if defined? DEFAULT_OCI8_ENCODING
42
- OCI8.encoding = DEFAULT_OCI8_ENCODING
43
- else
44
- load 'oci8/encoding-init.rb'
45
- end
45
+ if OCI8::VERSION != '@@OCI8_MODULE_VERSION@@'
46
+ require 'rbconfig'
47
+ so_name = so_basename + "." + Config::CONFIG['DLEXT']
48
+ raise "VERSION MISMATCH! #{so_name} version is #{OCI8::VERSION}, but oci8.rb version is @@OCI8_MODULE_VERSION@@."
46
49
  end
47
50
 
51
+ require 'oci8/encoding-init.rb'
48
52
  require 'oci8/oracle_version.rb'
49
53
 
50
54
  class OCI8
@@ -77,17 +81,19 @@ class OCI8
77
81
  def self.oracle_client_version
78
82
  @@oracle_client_version
79
83
  end
84
+
85
+ # defined for backward compatibility.
86
+ CLIENT_VERSION = @@oracle_client_version.major.to_s +
87
+ @@oracle_client_version.minor.to_s +
88
+ @@oracle_client_version.update.to_s
80
89
  end
81
90
 
91
+ require 'oci8/ocihandle.rb'
82
92
  require 'oci8/datetime.rb'
83
93
  require 'oci8/oci8.rb'
84
94
  require 'oci8/bindtype.rb'
85
95
  require 'oci8/metadata.rb'
86
96
  require 'oci8/compat.rb'
87
97
  require 'oci8/object.rb'
98
+ require 'oci8/connection_pool.rb'
88
99
  require 'oci8/properties.rb'
89
-
90
- class OCI8
91
- VERSION = '@@OCI8_MODULE_VERSION@@'
92
- CLIENT_VERSION = '@@OCI8_CLIENT_VERSION@@'
93
- end
@@ -3,4 +3,6 @@ object.rb
3
3
  metadata.rb
4
4
  oracle_version.rb
5
5
  oci8.rb
6
+ ocihandle.rb
7
+ connection_pool.rb
6
8
  properties.rb
@@ -1,7 +1,7 @@
1
1
  #--
2
2
  # bindtype.rb -- OCI8::BindType
3
3
  #
4
- # Copyright (C) 2009-2010 KUBO Takehiro <kubo@jiubao.org>
4
+ # Copyright (C) 2009-2011 KUBO Takehiro <kubo@jiubao.org>
5
5
  #++
6
6
 
7
7
  class OCI8
@@ -105,45 +105,56 @@ class OCI8
105
105
  def self.create(con, val, param, max_array_size)
106
106
  case param
107
107
  when Hash
108
- if param[:length]
109
- # If length is passed explicitly, use it.
110
- length = param[:length]
111
- elsif val.is_a? String or (val.respond_to? :to_str and val = val.to_str)
112
- if OCI8.respond_to? :encoding and OCI8.encoding != val.encoding
113
- # If the string encoding is different with NLS_LANG character set,
114
- # convert it to get the length.
115
- val = val.encode(OCI8.encoding)
116
- end
117
- if val.respond_to? :bytesize
118
- # ruby 1.8.7 or upper
119
- length = val.bytesize
108
+ param[:length_semantics] = OCI8::properties[:length_semantics] unless param.has_key? :length_semantics
109
+ unless param[:length]
110
+ if val.respond_to? :to_str
111
+ val = val.to_str
112
+ if param[:length_semantics] == :char
113
+ # character semantics
114
+ param[:length] = val.size
115
+ else
116
+ # byte semantics
117
+ if OCI8.respond_to? :encoding and OCI8.encoding != val.encoding
118
+ # If the string encoding is different with NLS_LANG character set,
119
+ # convert it to get the length.
120
+ val = val.encode(OCI8.encoding)
121
+ end
122
+ if val.respond_to? :bytesize
123
+ # ruby 1.8.7 or upper
124
+ param[:length] = val.bytesize
125
+ else
126
+ # ruby 1.8.6 or lower
127
+ param[:length] = val.size
128
+ end
129
+ end
120
130
  else
121
- # ruby 1.8.6 or lower
122
- length = val.size
131
+ param[:length] = @@minimum_bind_length
123
132
  end
124
133
  end
125
134
  # use the default value when :nchar is not set explicitly.
126
- nchar = OCI8.properties[:bind_string_as_nchar] unless param.has_key?(:nchar)
135
+ param[:nchar] = OCI8.properties[:bind_string_as_nchar] unless param.has_key?(:nchar)
127
136
  when OCI8::Metadata::Base
128
137
  case param.data_type
129
138
  when :char, :varchar2
130
- length = param.data_size
131
- # character size may become large on character set conversion.
132
- # The length of a Japanese half-width kana is one in Shift_JIS,
133
- # two in EUC-JP, three in UTF-8.
134
- length *= 3 unless param.char_used?
135
- nchar = (param.charset_form == :nchar)
139
+ length_semantics = OCI8.properties[:length_semantics]
140
+ if length_semantics == :char
141
+ length = param.char_size
142
+ else
143
+ length = param.data_size * OCI8.nls_ratio
144
+ end
145
+ param = {
146
+ :length => length,
147
+ :length_semantics => length_semantics,
148
+ :nchar => (param.charset_form == :nchar),
149
+ }
136
150
  when :raw
137
151
  # HEX needs twice space.
138
- length = param.data_size * 2
152
+ param = {:length => param.data_size * 2}
153
+ else
154
+ param = {:length => @@minimum_bind_length}
139
155
  end
140
156
  end
141
- length = @@minimum_bind_length if length.nil? or length < @@minimum_bind_length
142
- if nchar
143
- OCI8::BindType::NCHAR.new(con, val, length, max_array_size)
144
- else
145
- OCI8::BindType::CHAR.new(con, val, length, max_array_size)
146
- end
157
+ self.new(con, val, param, max_array_size)
147
158
  end
148
159
  end
149
160
 
@@ -151,28 +162,36 @@ class OCI8
151
162
  def self.create(con, val, param, max_array_size)
152
163
  case param
153
164
  when Hash
154
- length = 400 # default length
155
- if param[:length]
156
- length = param[:length]
157
- elsif val.respond_to? :to_str and val.to_str.size > length
158
- length = val.to_str.size
165
+ unless param[:length]
166
+ if val.respond_to? :to_str
167
+ val = val.to_str
168
+ if val.respond_to? :bytesize
169
+ param[:length] = val.bytesize
170
+ else
171
+ param[:length] = val.size
172
+ end
173
+ else
174
+ param[:length] = 400
175
+ end
159
176
  end
160
177
  when OCI8::Metadata::Base
161
- length = param.data_size
178
+ param = {:length => param.data_size}
162
179
  end
163
- self.new(con, val, length, max_array_size)
180
+ self.new(con, val, param, max_array_size)
164
181
  end
165
182
  end
166
183
 
167
184
  class Long < OCI8::BindType::String
168
185
  def self.create(con, val, param, max_array_size)
169
- super(con, val, {:length => con.long_read_len}, max_array_size)
186
+ param = {:length => con.long_read_len, :char_semantics => true}
187
+ super(con, val, param, max_array_size)
170
188
  end
171
189
  end
172
190
 
173
191
  class LongRaw < OCI8::BindType::RAW
174
192
  def self.create(con, val, param, max_array_size)
175
- self.new(con, val, con.long_read_len, max_array_size)
193
+ param = {:length => con.long_read_len, :char_semantics => false}
194
+ self.new(con, val, param, max_array_size)
176
195
  end
177
196
  end
178
197
 
@@ -260,13 +279,11 @@ OCI8::BindType::Mapping[:bfile] = OCI8::BindType::BFILE
260
279
  # DATE SQLT_DAT 7 0 0
261
280
  OCI8::BindType::Mapping[:date] = OCI8::BindType::Time
262
281
 
263
- if OCI8.oracle_client_version >= OCI8::ORAVER_9_0
264
- OCI8::BindType::Mapping[:timestamp] = OCI8::BindType::Time
265
- OCI8::BindType::Mapping[:timestamp_tz] = OCI8::BindType::Time
266
- OCI8::BindType::Mapping[:timestamp_ltz] = OCI8::BindType::Time
267
- OCI8::BindType::Mapping[:interval_ym] = OCI8::BindType::IntervalYM
268
- OCI8::BindType::Mapping[:interval_ds] = OCI8::BindType::IntervalDS
269
- end
282
+ OCI8::BindType::Mapping[:timestamp] = OCI8::BindType::Time
283
+ OCI8::BindType::Mapping[:timestamp_tz] = OCI8::BindType::Time
284
+ OCI8::BindType::Mapping[:timestamp_ltz] = OCI8::BindType::Time
285
+ OCI8::BindType::Mapping[:interval_ym] = OCI8::BindType::IntervalYM
286
+ OCI8::BindType::Mapping[:interval_ds] = OCI8::BindType::IntervalDS
270
287
 
271
288
  # datatype type size prec scale
272
289
  # -------------------------------------------------
@@ -0,0 +1,118 @@
1
+ #--
2
+ # connection_pool.rb -- OCI8::ConnectionPool
3
+ #
4
+ # Copyright (C) 2010 KUBO Takehiro <kubo@jiubao.org>
5
+ #++
6
+
7
+ class OCI8
8
+
9
+ # Connection pooling is the use of a group (the pool) of reusable
10
+ # physical connections by several sessions to balance loads.
11
+ # See: {Oracle Call Interface Manual}[http://docs.oracle.com/cd/E11882_01/appdev.112/e10646/oci09adv.htm#sthref1479]
12
+ #
13
+ # This is equivalent to Oracle JDBC Driver {OCI Connection Pooling}[http://docs.oracle.com/cd/E11882_01/java.112/e16548/ociconpl.htm#JJDBC28789].
14
+ #
15
+ # Usage:
16
+ # # Create a connection pool.
17
+ # # username and password are required to establish an implicit primary session.
18
+ # cpool = OCI8::ConnectionPool.new(1, 5, 2, username, password, database)
19
+ #
20
+ # # Get a session from the pool.
21
+ # # Pass the connection pool to the third argument.
22
+ # conn1 = OCI8.new(username, password, cpool)
23
+ #
24
+ # # Get another session.
25
+ # conn2 = OCI8.new(username, password, cpool)
26
+ #
27
+ class ConnectionPool
28
+
29
+ # call-seq:
30
+ # timeout -> integer
31
+ #
32
+ # Connections idle for more than this time value (in seconds) are
33
+ # terminated, to maintain an optimum number of open
34
+ # connections. If it is zero, the connections are never timed out.
35
+ # The default value is zero.
36
+ #
37
+ # <b>Note:</b> Shrinkage of the pool only occurs when there is a network
38
+ # round trip. If there are no operations, then the connections
39
+ # stay alive.
40
+ def timeout
41
+ attr_get_ub4(OCI_ATTR_CONN_TIMEOUT)
42
+ end
43
+
44
+ # call-seq:
45
+ # timeout = integer
46
+ #
47
+ # Changes the timeout in seconds to terminate idle connections.
48
+ def timeout=(val)
49
+ attr_set_ub4(OCI_ATTR_CONN_TIMEOUT, val)
50
+ end
51
+
52
+ # call-seq:
53
+ # nowait? -> true or false
54
+ #
55
+ # If true, an error is thrown when all the connections in the pool
56
+ # are busy and the number of connections has already reached the
57
+ # maximum. Otherwise the call waits till it gets a connection.
58
+ # The default value is false.
59
+ def nowait?
60
+ attr_get_ub1(OCI_ATTR_CONN_NOWAIT) != 0
61
+ end
62
+
63
+ # call-seq:
64
+ # nowait = true or false
65
+ #
66
+ # Changes the behavior when all the connections in the pool
67
+ # are busy and the number of connections has already reached the
68
+ # maximum.
69
+ def nowait=(val)
70
+ attr_set_ub1(OCI_ATTR_CONN_NOWAIT, val ? 1 : 0)
71
+ end
72
+
73
+ # call-seq:
74
+ # busy_count -> integer
75
+ #
76
+ # Returns the number of busy physical connections.
77
+ def busy_count
78
+ attr_get_ub4(OCI_ATTR_CONN_BUSY_COUNT)
79
+ end
80
+
81
+ # call-seq:
82
+ # open_count -> integer
83
+ #
84
+ # Returns the number of open physical connections.
85
+ def open_count
86
+ attr_get_ub4(OCI_ATTR_CONN_OPEN_COUNT)
87
+ end
88
+
89
+ # call-seq:
90
+ # min -> integer
91
+ #
92
+ # Returns the number of minimum physical connections.
93
+ def min
94
+ attr_get_ub4(OCI_ATTR_CONN_MIN)
95
+ end
96
+
97
+ # call-seq:
98
+ # max -> integer
99
+ #
100
+ # Returns the number of maximum physical connections.
101
+ def max
102
+ attr_get_ub4(OCI_ATTR_CONN_MAX)
103
+ end
104
+
105
+ # call-seq:
106
+ # incr -> integer
107
+ #
108
+ # Returns the connection increment parameter.
109
+ def incr
110
+ attr_get_ub4(OCI_ATTR_CONN_INCR)
111
+ end
112
+
113
+ #
114
+ def destroy
115
+ free
116
+ end
117
+ end
118
+ end