ruby-oci8 2.2.10-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +14 -0
  3. data/COPYING +30 -0
  4. data/COPYING_old +64 -0
  5. data/ChangeLog +3826 -0
  6. data/Makefile +92 -0
  7. data/NEWS +1209 -0
  8. data/README.md +66 -0
  9. data/dist-files +112 -0
  10. data/docs/bind-array-to-in_cond.md +38 -0
  11. data/docs/conflicts-local-connections-and-processes.md +98 -0
  12. data/docs/hanging-after-inactivity.md +63 -0
  13. data/docs/install-binary-package.md +44 -0
  14. data/docs/install-full-client.md +111 -0
  15. data/docs/install-instant-client.md +194 -0
  16. data/docs/install-on-osx.md +46 -0
  17. data/docs/ldap-auth-and-function-interposition.md +123 -0
  18. data/docs/number-type-mapping.md +79 -0
  19. data/docs/platform-specific-issues.md +164 -0
  20. data/docs/report-installation-issue.md +50 -0
  21. data/docs/timeout-parameters.md +94 -0
  22. data/lib/.document +1 -0
  23. data/lib/dbd/OCI8.rb +591 -0
  24. data/lib/oci8/.document +8 -0
  25. data/lib/oci8/bindtype.rb +333 -0
  26. data/lib/oci8/check_load_error.rb +146 -0
  27. data/lib/oci8/compat.rb +117 -0
  28. data/lib/oci8/connection_pool.rb +179 -0
  29. data/lib/oci8/cursor.rb +605 -0
  30. data/lib/oci8/datetime.rb +605 -0
  31. data/lib/oci8/encoding-init.rb +45 -0
  32. data/lib/oci8/encoding.yml +537 -0
  33. data/lib/oci8/metadata.rb +2148 -0
  34. data/lib/oci8/object.rb +641 -0
  35. data/lib/oci8/oci8.rb +756 -0
  36. data/lib/oci8/ocihandle.rb +591 -0
  37. data/lib/oci8/oracle_version.rb +153 -0
  38. data/lib/oci8/properties.rb +196 -0
  39. data/lib/oci8/version.rb +3 -0
  40. data/lib/oci8.rb +190 -0
  41. data/lib/oci8lib_310.so +0 -0
  42. data/lib/ruby-oci8.rb +1 -0
  43. data/metaconfig +142 -0
  44. data/pre-distclean.rb +7 -0
  45. data/ruby-oci8.gemspec +85 -0
  46. data/setup.rb +1342 -0
  47. data/test/README.md +37 -0
  48. data/test/config.rb +201 -0
  49. data/test/setup_test_object.sql +199 -0
  50. data/test/setup_test_package.sql +59 -0
  51. data/test/test_all.rb +56 -0
  52. data/test/test_appinfo.rb +62 -0
  53. data/test/test_array_dml.rb +332 -0
  54. data/test/test_bind_array.rb +70 -0
  55. data/test/test_bind_boolean.rb +99 -0
  56. data/test/test_bind_integer.rb +47 -0
  57. data/test/test_bind_raw.rb +45 -0
  58. data/test/test_bind_string.rb +105 -0
  59. data/test/test_bind_time.rb +177 -0
  60. data/test/test_break.rb +125 -0
  61. data/test/test_clob.rb +85 -0
  62. data/test/test_connection_pool.rb +124 -0
  63. data/test/test_connstr.rb +220 -0
  64. data/test/test_datetime.rb +585 -0
  65. data/test/test_dbi.rb +365 -0
  66. data/test/test_dbi_clob.rb +53 -0
  67. data/test/test_encoding.rb +103 -0
  68. data/test/test_error.rb +87 -0
  69. data/test/test_metadata.rb +2674 -0
  70. data/test/test_object.rb +546 -0
  71. data/test/test_oci8.rb +624 -0
  72. data/test/test_oracle_version.rb +68 -0
  73. data/test/test_oradate.rb +255 -0
  74. data/test/test_oranumber.rb +792 -0
  75. data/test/test_package_type.rb +981 -0
  76. data/test/test_properties.rb +17 -0
  77. data/test/test_rowid.rb +32 -0
  78. metadata +123 -0
@@ -0,0 +1,605 @@
1
+ require 'date'
2
+
3
+ class OCI8
4
+
5
+ module BindType
6
+
7
+ # Returns the default time zone when using Oracle 8.x client.
8
+ # The value is unused when using Oracle 9i or upper client.
9
+ #
10
+ # See also: OCI8::BindType::Time
11
+ #
12
+ # @return [:local or :utc]
13
+ def self.default_timezone
14
+ OCI8::BindType::Util.default_timezone
15
+ end
16
+
17
+ # Sets the default time zone when using Oracle 8.x client.
18
+ # The value is unused when using Oracle 9i or upper client.
19
+ #
20
+ # See also: OCI8::BindType::Time
21
+ #
22
+ # @param [:local or :utc] tz
23
+ def self.default_timezone=(tz)
24
+ OCI8::BindType::Util.default_timezone = tz
25
+ end
26
+
27
+ module Util # :nodoc:
28
+
29
+ @@datetime_fsec_base = (1 / ::DateTime.parse('0001-01-01 00:00:00.000000001').sec_fraction).to_i
30
+
31
+ @@default_timezone = :local
32
+ begin
33
+ Time.new(2001, 1, 1, 0, 0, 0, '+00:00')
34
+ @@time_new_accepts_timezone = true # after ruby 1.9.2
35
+ rescue ArgumentError
36
+ @@time_new_accepts_timezone = false # prior to ruby 1.9.2
37
+ end
38
+
39
+ begin
40
+ # 2001-01-01 00:00:59.999
41
+ ::DateTime.civil(2001, 1, 1, 0, 0, Rational(59_999, 1000), 0)
42
+ @@datetime_has_fractional_second_bug = false
43
+ rescue ArgumentError
44
+ @@datetime_has_fractional_second_bug = true
45
+ end
46
+
47
+ def self.default_timezone
48
+ @@default_timezone
49
+ end
50
+
51
+ def self.default_timezone=(tz)
52
+ if tz != :local and tz != :utc
53
+ raise ArgumentError, "expected :local or :utc but #{tz}"
54
+ end
55
+ @@default_timezone = tz
56
+ end
57
+
58
+ private
59
+
60
+ def datetime_to_array(val, datatype)
61
+ return nil if val.nil?
62
+
63
+ # year
64
+ year = val.year
65
+ # month
66
+ if val.respond_to? :mon
67
+ month = val.mon
68
+ elsif val.respond_to? :month
69
+ month = val.month
70
+ else
71
+ raise "expect Time, Date or DateTime but #{val.class}"
72
+ end
73
+ # day
74
+ if val.respond_to? :mday
75
+ day = val.mday
76
+ elsif val.respond_to? :day
77
+ day = val.day
78
+ else
79
+ raise "expect Time, Date or DateTime but #{val.class}"
80
+ end
81
+ # hour
82
+ if val.respond_to? :hour
83
+ hour = val.hour
84
+ else
85
+ hour = 0
86
+ end
87
+ # minute
88
+ if val.respond_to? :min
89
+ minute = val.min
90
+ else
91
+ minute = 0
92
+ end
93
+ # second
94
+ if val.respond_to? :sec
95
+ sec = val.sec
96
+ else
97
+ sec = 0
98
+ end
99
+ return [year, month, day, hour, minute, sec] if datatype == :date
100
+
101
+ # fractional second
102
+ if val.respond_to? :sec_fraction
103
+ fsec = (val.sec_fraction * @@datetime_fsec_base).to_i
104
+ elsif val.respond_to? :nsec
105
+ fsec = val.nsec
106
+ elsif val.respond_to? :usec
107
+ fsec = val.usec * 1000
108
+ else
109
+ fsec = 0
110
+ end
111
+ return [year, month, day, hour, minute, sec, fsec, nil, nil] if datatype == :timestamp
112
+
113
+ # time zone
114
+ if val.respond_to? :offset
115
+ # DateTime
116
+ tz_min = (val.offset * 1440).to_i
117
+ elsif val.respond_to? :utc_offset
118
+ # Time
119
+ tz_min = val.utc_offset / 60
120
+ else
121
+ tz_hour = nil
122
+ tz_min = nil
123
+ end
124
+ if tz_min
125
+ if tz_min < 0
126
+ tz_min = - tz_min
127
+ tz_hour = - (tz_min / 60)
128
+ tz_min = (tz_min % 60)
129
+ else
130
+ tz_hour = tz_min / 60
131
+ tz_min = tz_min % 60
132
+ end
133
+ end
134
+ [year, month, day, hour, minute, sec, fsec, tz_hour, tz_min]
135
+ end
136
+
137
+ def array_to_datetime(ary, timezone)
138
+ return nil if ary.nil?
139
+
140
+ year, month, day, hour, minute, sec, nsec, tz_hour, tz_min = ary
141
+ sec += nsec.to_r / 1000000000 if nsec and nsec != 0
142
+ if tz_hour and tz_min
143
+ offset = tz_hour.to_r / 24 + tz_min.to_r / 1440
144
+ else
145
+ if @@default_timezone == :local
146
+ if ::DateTime.respond_to? :local_offset
147
+ offset = ::DateTime.local_offset # Use a method defined by active support.
148
+ else
149
+ # Do as active support does.
150
+ offset = ::Time.local(2007).utc_offset.to_r / 86400
151
+ end
152
+ else
153
+ offset = 0
154
+ end
155
+ end
156
+
157
+ if @@datetime_has_fractional_second_bug and sec >= 59 and nsec != 0
158
+ # convert to a DateTime via a String as a workaround
159
+ if offset >= 0
160
+ sign = ?+
161
+ else
162
+ sign = ?-
163
+ offset = - offset;
164
+ end
165
+ tz_min = (offset * 1440).to_i
166
+ tz_hour, tz_min = tz_min.divmod 60
167
+ time_str = format("%04d-%02d-%02dT%02d:%02d:%02d.%09d%c%02d:%02d",
168
+ year, month, day, hour, minute, sec, nsec, sign, tz_hour, tz_min)
169
+ ::DateTime.parse(time_str)
170
+ else
171
+ ::DateTime.civil(year, month, day, hour, minute, sec, offset)
172
+ end
173
+ end
174
+
175
+ if @@time_new_accepts_timezone
176
+
177
+ # after ruby 1.9.2
178
+ def array_to_time(ary, timezone)
179
+ return nil if ary.nil?
180
+
181
+ year, month, day, hour, minute, sec, nsec, tz_hour, tz_min = ary
182
+ nsec ||= 0
183
+
184
+ if timezone
185
+ usec = (nsec == 0) ? 0 : nsec.to_r / 1000
186
+ ::Time.send(timezone, year, month, day, hour, minute, sec, usec)
187
+ else
188
+ sec += nsec.to_r / 1_000_000_000 if nsec != 0
189
+ utc_offset = tz_hour * 3600 + tz_min * 60
190
+ ::Time.new(year, month, day, hour, minute, sec, utc_offset)
191
+ end
192
+ end
193
+
194
+ else
195
+
196
+ # prior to ruby 1.9.2
197
+ def array_to_time(ary, timezone)
198
+ return nil if ary.nil?
199
+
200
+ year, month, day, hour, minute, sec, nsec, tz_hour, tz_min = ary
201
+ nsec ||= 0
202
+ usec = (nsec == 0) ? 0 : nsec.to_r / 1000
203
+ begin
204
+ if timezone
205
+ return ::Time.send(timezone, year, month, day, hour, minute, sec, usec)
206
+ else
207
+ if tz_hour == 0 and tz_min == 0
208
+ tm = ::Time.utc(year, month, day, hour, minute, sec, usec)
209
+ # Time.utc(99, ...) returns a time object the year of which is 1999.
210
+ # 'tm.year == year' checks such cases.
211
+ return tm if tm.year == year
212
+ else
213
+ tm = ::Time.local(year, month, day, hour, minute, sec, usec)
214
+ return tm if tm.utc_offset == tz_hour * 3600 + tz_min * 60 and tm.year == year
215
+ end
216
+ end
217
+ rescue StandardError
218
+ end
219
+ array_to_datetime(ary, timezone)
220
+ end
221
+
222
+ end
223
+ end
224
+
225
+ #--
226
+ # OCI8::BindType::DateTime
227
+ #++
228
+ # This is a helper class to select or bind Oracle data types such as
229
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
230
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt>. The retrieved value
231
+ # is a \DateTime.
232
+ #
233
+ # === How to select \DataTime values.
234
+ #
235
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
236
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> are selected as a \Time
237
+ # by default. You change the behaviour by explicitly calling
238
+ # OCI8::Cursor#define as follows:
239
+ #
240
+ # cursor = conn.parse("SELECT hiredate FROM emp")
241
+ # cursor.define(1, nil, DateTime)
242
+ # cursor.exec()
243
+ #
244
+ # Otherwise, you can change the default mapping for all queries.
245
+ #
246
+ # # Changes the mapping for DATE
247
+ # OCI8::BindType::Mapping[OCI8::SQLT_DAT] = OCI8::BindType::DateTime
248
+ #
249
+ # # Changes the mapping for TIMESTAMP
250
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP] = OCI8::BindType::DateTime
251
+ #
252
+ # # Changes the mapping for TIMESTAMP WITH TIME ZONE
253
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_TZ] = OCI8::BindType::DateTime
254
+ #
255
+ # # Changes the mapping for TIMESTAMP WITH LOCAL TIME ZONE
256
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_LTZ] = OCI8::BindType::DateTime
257
+ #
258
+ # === Note for default time zone
259
+ #
260
+ # The retrieved value's time zone is determined by the session time zone
261
+ # if its data type is <tt>DATE</tt>, <tt>TIMESTAMP</tt> or <tt>TIMESTAMP
262
+ # WITH LOCAL TIME ZONE</tt>.
263
+ #
264
+ # The session time zone is same with local machine's by default.
265
+ # It is changed by the following SQL.
266
+ #
267
+ # ALTER SESSION SET TIME_ZONE='-05:00'
268
+ #
269
+ # === Note for Oracle 8.x client
270
+ #
271
+ # Timestamp data types and session time zone are new features in
272
+ # Oracle 9i. This class is available only to fetch or bind <tt>DATE</tt>
273
+ # when using Oracle 8.x client.
274
+ #
275
+ # The retrieved value's time zone is determined not by the session
276
+ # time zone, but by the OCI8::BindType.default_timezone
277
+ # The time zone can be changed as follows:
278
+ #
279
+ # OCI8::BindType.default_timezone = :local
280
+ # # or
281
+ # OCI8::BindType.default_timezone = :utc
282
+ #
283
+ # If you are in the regions where daylight saving time is adopted,
284
+ # you should use OCI8::BindType::Time.
285
+ #
286
+ class DateTime < OCI8::BindType::OCITimestampTZ
287
+ include OCI8::BindType::Util
288
+
289
+ def set(val) # :nodoc:
290
+ super(datetime_to_array(val, :timestamp_tz))
291
+ end
292
+
293
+ def get() # :nodoc:
294
+ array_to_datetime(super(), nil)
295
+ end
296
+ end
297
+
298
+ class LocalDateTime < OCI8::BindType::OCITimestamp
299
+ include OCI8::BindType::Util
300
+
301
+ def set(val) # :nodoc:
302
+ super(datetime_to_array(val, :timestamp))
303
+ end
304
+
305
+ def get() # :nodoc:
306
+ array_to_datetime(super(), :local)
307
+ end
308
+ end
309
+
310
+ class UTCDateTime < OCI8::BindType::OCITimestamp
311
+ include OCI8::BindType::Util
312
+
313
+ def set(val) # :nodoc:
314
+ super(datetime_to_array(val, :timestamp))
315
+ end
316
+
317
+ def get() # :nodoc:
318
+ array_to_datetime(super(), :utc)
319
+ end
320
+ end
321
+
322
+ #--
323
+ # OCI8::BindType::Time
324
+ #++
325
+ # This is a helper class to select or bind Oracle data types such as
326
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
327
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt>. The retrieved value
328
+ # is a \Time.
329
+ #
330
+ # === How to select \Time values.
331
+ #
332
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
333
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> are selected as a \Time
334
+ # by default. If the default behaviour is changed, you can select it
335
+ # as a \Time by explicitly calling OCI8::Cursor#define as follows:
336
+ #
337
+ # cursor = conn.parse("SELECT hiredate FROM emp")
338
+ # cursor.define(1, nil, Time)
339
+ # cursor.exec()
340
+ #
341
+ # === Note for ruby prior to 1.9.2
342
+ #
343
+ # If the retrieved value cannot be represented by \Time, it become
344
+ # a \DateTime. The fallback is done only when the ruby is before 1.9.2
345
+ # and one of the following conditions are met.
346
+ # - The timezone part is neither local nor utc.
347
+ # - The time is out of the time_t[http://en.wikipedia.org/wiki/Time_t].
348
+ #
349
+ # If the retrieved value has the precision of fractional second more
350
+ # than 6, the fractional second is truncated to microsecond, which
351
+ # is the precision of standard \Time class.
352
+ #
353
+ # To avoid this fractional second truncation:
354
+ # - Upgrade to ruby 1.9.2, whose \Time precision is nanosecond.
355
+ # - Otherwise, change the defalt mapping to use \DateTime as follows.
356
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP] = OCI8::BindType::DateTime
357
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_TZ] = OCI8::BindType::DateTime
358
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_LTZ] = OCI8::BindType::DateTime
359
+ #
360
+ # === Note for default time zone
361
+ #
362
+ # The retrieved value's time zone is determined by the session time zone
363
+ # if its data type is <tt>DATE</tt>, <tt>TIMESTAMP</tt> or <tt>TIMESTAMP
364
+ # WITH LOCAL TIME ZONE</tt>.
365
+ #
366
+ # The session time zone is same with local machine's by default.
367
+ # It is changed by the following SQL.
368
+ #
369
+ # ALTER SESSION SET TIME_ZONE='-05:00'
370
+ #
371
+ # === Note for Oracle 8.x client
372
+ #
373
+ # Timestamp data types and session time zone are new features in
374
+ # Oracle 9i. This class is available only to fetch or bind <tt>DATE</tt>
375
+ # when using Oracle 8.x client.
376
+ #
377
+ # The retrieved value's time zone is determined not by the session
378
+ # time zone, but by the OCI8::BindType.default_timezone
379
+ # The time zone can be changed as follows:
380
+ #
381
+ # OCI8::BindType.default_timezone = :local
382
+ # # or
383
+ # OCI8::BindType.default_timezone = :utc
384
+ #
385
+ class Time < OCI8::BindType::OCITimestampTZ
386
+ include OCI8::BindType::Util
387
+
388
+ def set(val) # :nodoc:
389
+ super(datetime_to_array(val, :timestamp_tz))
390
+ end
391
+
392
+ def get() # :nodoc:
393
+ array_to_time(super(), nil)
394
+ end
395
+ end
396
+
397
+ class LocalTime < OCI8::BindType::OCITimestamp
398
+ include OCI8::BindType::Util
399
+
400
+ def set(val) # :nodoc:
401
+ super(datetime_to_array(val, :timestamp))
402
+ end
403
+
404
+ def get() # :nodoc:
405
+ array_to_time(super(), :local)
406
+ end
407
+ end
408
+
409
+ class UTCTime < OCI8::BindType::OCITimestamp
410
+ include OCI8::BindType::Util
411
+
412
+ def set(val) # :nodoc:
413
+ super(datetime_to_array(val, :timestamp))
414
+ end
415
+
416
+ def get() # :nodoc:
417
+ array_to_time(super(), :utc)
418
+ end
419
+ end
420
+
421
+ #--
422
+ # OCI8::BindType::IntervalYM
423
+ #++
424
+ #
425
+ # This is a helper class to select or bind Oracle data type
426
+ # <tt>INTERVAL YEAR TO MONTH</tt>. The retrieved value is
427
+ # the number of months between two timestamps.
428
+ #
429
+ # The value can be applied to \DateTime#>> to shift months.
430
+ # It can be applied to \Time#months_since if activisupport has
431
+ # been loaded.
432
+ #
433
+ # === How to select <tt>INTERVAL YEAR TO MONTH</tt>
434
+ #
435
+ # <tt>INTERVAL YEAR TO MONTH</tt> is selected as an Integer.
436
+ #
437
+ # conn.exec("select (current_timestamp - hiredate) year to month from emp") do |hired_months|
438
+ # puts "hired_months = #{hired_months}"
439
+ # end
440
+ #
441
+ # == How to bind <tt>INTERVAL YEAR TO MONTH</tt>
442
+ #
443
+ # You cannot bind a bind variable as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
444
+ # It must be bound explicitly by OCI8::Cursor#bind_param.
445
+ #
446
+ # # output bind variable
447
+ # cursor = conn.parse(<<-EOS)
448
+ # BEGIN
449
+ # :interval := (:ts1 - :ts2) YEAR TO MONTH;
450
+ # END;
451
+ # EOS
452
+ # cursor.bind_param(:interval, nil, :interval_ym)
453
+ # cursor.bind_param(:ts1, DateTime.parse('1969-11-19 06:54:35 00:00'))
454
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
455
+ # cursor.exec
456
+ # cursor[:interval] # => 4 (months)
457
+ # cursor.close
458
+ #
459
+ # # input bind variable
460
+ # cursor = conn.parse(<<-EOS)
461
+ # BEGIN
462
+ # :ts1 := :ts2 + :interval;
463
+ # END;
464
+ # EOS
465
+ # cursor.bind_param(:ts1, nil, DateTime)
466
+ # cursor.bind_param(:ts2, Date.parse('1969-11-19'))
467
+ # cursor.bind_param(:interval, 4, :interval_ym)
468
+ # cursor.exec
469
+ # cursor[:ts1].strftime('%Y-%m-%d') # => 1970-03-19
470
+ # cursor.close
471
+ #
472
+ class IntervalYM < OCI8::BindType::OCIIntervalYM
473
+ def set(val) # :nodoc:
474
+ unless val.nil?
475
+ val = [val / 12, val % 12]
476
+ end
477
+ super(val)
478
+ end
479
+ def get() # :nodoc:
480
+ val = super()
481
+ return nil if val.nil?
482
+ year, month = val
483
+ year * 12 + month
484
+ end
485
+ end # OCI8::BindType::IntervalYM
486
+
487
+ #--
488
+ # OCI8::BindType::IntervalDS
489
+ #++
490
+ #
491
+ # (new in 2.0)
492
+ #
493
+ # This is a helper class to select or bind Oracle data type
494
+ # <tt>INTERVAL DAY TO SECOND</tt>. The retrieved value is
495
+ # the number of seconds between two typestamps as a \Float.
496
+ #
497
+ # Note that it is the number days as a \Rational if
498
+ # OCI8::BindType::IntervalDS.unit is :day or the ruby-oci8
499
+ # version is prior to 2.0.3.
500
+ #
501
+ # == How to bind <tt>INTERVAL DAY TO SECOND</tt>
502
+ #
503
+ # You cannot bind a bind variable as <tt>INTERVAL DAY TO SECOND</tt>
504
+ # implicitly. It must be bound explicitly by OCI8::Cursor#bind_param.
505
+ #
506
+ # # output bind variable
507
+ # cursor = conn.parse(<<-EOS)
508
+ # BEGIN
509
+ # :interval := (:ts1 - :ts2) DAY TO SECOND(9);
510
+ # END;
511
+ # EOS
512
+ # cursor.bind_param(:interval, nil, :interval_ds)
513
+ # cursor.bind_param(:ts1, DateTime.parse('1969-11-19 06:54:35 00:00'))
514
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
515
+ # cursor.exec
516
+ # cursor[:interval] # => 10492615.0 seconds
517
+ # cursor.close
518
+ #
519
+ # # input bind variable
520
+ # cursor = conn.parse(<<-EOS)
521
+ # BEGIN
522
+ # :ts1 := :ts2 + :interval;
523
+ # END;
524
+ # EOS
525
+ # cursor.bind_param(:ts1, nil, DateTime)
526
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
527
+ # cursor.bind_param(:interval, 10492615.0, :interval_ds)
528
+ # cursor.exec
529
+ # cursor[:ts1].strftime('%Y-%m-%d %H:%M:%S') # => 1969-11-19 06:54:35
530
+ # cursor.close
531
+ #
532
+ class IntervalDS < OCI8::BindType::OCIIntervalDS
533
+ @@hour = 1 / 24.to_r
534
+ @@minute = @@hour / 60
535
+ @@sec = @@minute / 60
536
+ @@fsec = @@sec / 1000000000
537
+ @@unit = :second
538
+
539
+ # Retrieves the unit of interval.
540
+ #
541
+ # @return [:second or :day]
542
+ # @since 2.0.3
543
+ def self.unit
544
+ @@unit
545
+ end
546
+
547
+ # Changes the unit of interval. :second is the default.
548
+ #
549
+ # @param [:second or :day] val
550
+ # @since 2.0.3
551
+ def self.unit=(val)
552
+ case val
553
+ when :second, :day
554
+ @@unit = val
555
+ else
556
+ raise 'unit should be :second or :day'
557
+ end
558
+ end
559
+
560
+ def set(val) # :nodoc:
561
+ unless val.nil?
562
+ if val < 0
563
+ is_minus = true
564
+ val = -val
565
+ else
566
+ is_minus = false
567
+ end
568
+ if @@unit == :second
569
+ day, val = val.divmod 86400
570
+ hour, val = val.divmod 3600
571
+ minute, val = val.divmod 60
572
+ sec, val = val.divmod 1
573
+ else
574
+ day, val = val.divmod 1
575
+ hour, val = (val * 24).divmod 1
576
+ minute, val = (val * 60).divmod 1
577
+ sec, val = (val * 60).divmod 1
578
+ end
579
+ fsec, val = (val * 1000000000).divmod 1
580
+ if is_minus
581
+ day = - day
582
+ hour = - hour
583
+ minute = - minute
584
+ sec = - sec
585
+ fsec = - fsec
586
+ end
587
+ val = [day, hour, minute, sec, fsec]
588
+ end
589
+ super(val)
590
+ end
591
+
592
+ def get() # :nodoc:
593
+ val = super()
594
+ return nil if val.nil?
595
+ day, hour, minute, sec, fsec = val
596
+ if @@unit == :second
597
+ fsec = fsec / 1000000000.0
598
+ day * 86400 + hour * 3600 + minute * 60 + sec + fsec
599
+ else
600
+ day + (hour * @@hour) + (minute * @@minute) + (sec * @@sec) + (fsec * @@fsec)
601
+ end
602
+ end
603
+ end # OCI8::BindType::IntervalDS
604
+ end # OCI8::BindType
605
+ end # OCI8
@@ -0,0 +1,45 @@
1
+ #
2
+ # setup default OCI encoding from NLS_LANG.
3
+ #
4
+
5
+ #
6
+ class OCI8
7
+
8
+ @@client_charset_name = charset_id2name(@@environment_handle.send(:attr_get_ub2, 31))
9
+ # 31 is OCI_ATTR_ENV_CHARSET_ID.
10
+
11
+ if @@client_charset_name == 'US7ASCII'
12
+ # Check whether US7ASCII is explicitly set by NLS_LANG or not.
13
+ nls_lang = ENV['NLS_LANG']
14
+ if nls_lang.nil? and defined? OCI8::Win32Util
15
+ if OCI8::Util::dll_path =~ /\\BIN\\OCI\.DLL$/i
16
+ oracle_home = $`
17
+ OCI8::Win32Util.enum_homes do |home, lang|
18
+ if oracle_home == home.upcase
19
+ nls_lang = lang
20
+ break
21
+ end
22
+ end
23
+ end
24
+ end
25
+ if nls_lang.nil?
26
+ warn "Warning: NLS_LANG is not set. fallback to US7ASCII."
27
+ end
28
+ end
29
+
30
+ if defined? DEFAULT_OCI8_ENCODING
31
+ enc = DEFAULT_OCI8_ENCODING
32
+ else
33
+ require 'yaml'
34
+ yaml_file = File.dirname(__FILE__) + '/encoding.yml'
35
+ enc = YAML::load_file(yaml_file)[@@client_charset_name]
36
+ if enc.nil?
37
+ raise "Cannot convert Oracle charset name #{@@client_charset_name} to Ruby encoding name in #{yaml_file}."
38
+ end
39
+ if enc.is_a? Array
40
+ # Use the first available encoding in the array.
41
+ enc = enc.find do |e| Encoding.find(e) rescue false; end
42
+ end
43
+ end
44
+ OCI8.encoding = enc
45
+ end