ruby-oci8 1.0.7-x86-mswin32-60 → 2.0.1-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/ChangeLog +1289 -383
  2. data/Makefile +48 -12
  3. data/NEWS +5 -419
  4. data/README +56 -385
  5. data/VERSION +1 -1
  6. data/dist-files +27 -27
  7. data/lib/.document +2 -0
  8. data/lib/dbd/OCI8.rb +2 -17
  9. data/lib/oci8.rb +48 -1622
  10. data/lib/oci8.rb.in +48 -1622
  11. data/lib/oci8/.document +5 -0
  12. data/lib/oci8/compat.rb +108 -0
  13. data/lib/oci8/datetime.rb +491 -0
  14. data/lib/oci8/encoding-init.rb +40 -0
  15. data/lib/oci8/encoding.yml +537 -0
  16. data/lib/oci8/metadata.rb +2077 -0
  17. data/lib/oci8/object.rb +548 -0
  18. data/lib/oci8/oci8.rb +798 -0
  19. data/lib/oci8/oracle_version.rb +144 -0
  20. data/lib/oci8lib_18.so +0 -0
  21. data/lib/oci8lib_191.so +0 -0
  22. data/metaconfig +3 -3
  23. data/ruby-oci8.gemspec +24 -15
  24. data/setup.rb +4 -4
  25. data/test/config.rb +64 -84
  26. data/test/test_all.rb +14 -21
  27. data/test/test_array_dml.rb +333 -0
  28. data/test/test_bind_raw.rb +18 -25
  29. data/test/test_bind_time.rb +78 -91
  30. data/test/test_break.rb +37 -35
  31. data/test/test_clob.rb +33 -89
  32. data/test/test_connstr.rb +5 -4
  33. data/test/test_datetime.rb +469 -0
  34. data/test/test_dbi.rb +99 -60
  35. data/test/test_dbi_clob.rb +3 -8
  36. data/test/test_metadata.rb +65 -51
  37. data/test/test_oci8.rb +151 -55
  38. data/test/test_oracle_version.rb +70 -0
  39. data/test/test_oradate.rb +76 -83
  40. data/test/test_oranumber.rb +405 -71
  41. data/test/test_rowid.rb +6 -11
  42. metadata +21 -25
  43. data/ext/oci8/oci8lib.so +0 -0
  44. data/ruby-oci8.spec +0 -62
  45. data/support/README +0 -4
  46. data/support/runit/assert.rb +0 -281
  47. data/support/runit/cui/testrunner.rb +0 -101
  48. data/support/runit/error.rb +0 -4
  49. data/support/runit/method_mappable.rb +0 -20
  50. data/support/runit/robserver.rb +0 -25
  51. data/support/runit/setuppable.rb +0 -15
  52. data/support/runit/teardownable.rb +0 -16
  53. data/support/runit/testcase.rb +0 -113
  54. data/support/runit/testfailure.rb +0 -25
  55. data/support/runit/testresult.rb +0 -121
  56. data/support/runit/testsuite.rb +0 -43
  57. data/support/runit/version.rb +0 -3
  58. data/test/test_describe.rb +0 -137
@@ -0,0 +1,5 @@
1
+ oci8.rb
2
+ object.rb
3
+ metadata.rb
4
+ oracle_version.rb
5
+
@@ -0,0 +1,108 @@
1
+ #
2
+ # add compatible code with old versions.
3
+ #
4
+
5
+ OCI_STMT_SELECT = :select_stmt
6
+ OCI_STMT_UPDATE = :update_stmt
7
+ OCI_STMT_DELETE = :delete_stmt
8
+ OCI_STMT_INSERT = :insert_stmt
9
+ OCI_STMT_CREATE = :create_stmt
10
+ OCI_STMT_DROP = :drop_stmt
11
+ OCI_STMT_ALTER = :alter_stmt
12
+ OCI_STMT_BEGIN = :begin_stmt
13
+ OCI_STMT_DECLARE = :declare_stmt
14
+
15
+ class OCI8
16
+
17
+ STMT_SELECT = :select_stmt
18
+ STMT_UPDATE = :update_stmt
19
+ STMT_DELETE = :delete_stmt
20
+ STMT_INSERT = :insert_stmt
21
+ STMT_CREATE = :create_stmt
22
+ STMT_DROP = :drop_stmt
23
+ STMT_ALTER = :alter_stmt
24
+ STMT_BEGIN = :begin_stmt
25
+ STMT_DECLARE = :declare_stmt
26
+
27
+ RAW = :raw
28
+
29
+ # varchar, varchar2
30
+ SQLT_CHR = :varchar2
31
+ # number, double precision, float, real, numeric, int, integer, smallint
32
+ SQLT_NUM = :number
33
+ # long
34
+ SQLT_LNG = :long
35
+ # date
36
+ SQLT_DAT = :date
37
+ # raw
38
+ SQLT_BIN = :raw
39
+ # long raw
40
+ SQLT_LBI = :long_raw
41
+ # char
42
+ SQLT_AFC = :char
43
+ # binary_float
44
+ SQLT_IBFLOAT = :binary_float
45
+ # binary_double
46
+ SQLT_IBDOUBLE = :binary_double
47
+ # rowid
48
+ SQLT_RDD = :rowid
49
+ # clob
50
+ SQLT_CLOB = :clob
51
+ # blob
52
+ SQLT_BLOB = :blob
53
+ # bfile
54
+ SQLT_BFILE = :bfile
55
+ # ref cursor
56
+ SQLT_RSET = 116
57
+ # timestamp
58
+ SQLT_TIMESTAMP = :timestamp
59
+ # timestamp with time zone
60
+ SQLT_TIMESTAMP_TZ = :timestamp_tz
61
+ # interval year to month
62
+ SQLT_INTERVAL_YM = :interval_ym
63
+ # interval day to second
64
+ SQLT_INTERVAL_DS = :interval_ds
65
+ # timestamp with local time zone
66
+ SQLT_TIMESTAMP_LTZ = :timestamp_ltz
67
+
68
+ # mapping of sql type number to sql type name.
69
+ SQLT_NAMES = {}
70
+ constants.each do |name|
71
+ next if name.to_s.index("SQLT_") != 0
72
+ val = const_get name.intern
73
+ if val.is_a? Fixnum
74
+ SQLT_NAMES[val] = name
75
+ end
76
+ end
77
+
78
+ # add alias compatible with 'Oracle7 Module for Ruby'.
79
+ alias autocommit autocommit?
80
+
81
+ class Cursor
82
+ def self.select_number_as=(val)
83
+ if val == Fixnum
84
+ @@bind_unknown_number = OCI8::BindType::Fixnum
85
+ elsif val == Integer
86
+ @@bind_unknown_number = OCI8::BindType::Integer
87
+ elsif val == Float
88
+ @@bind_unknown_number = OCI8::BindType::Float
89
+ else
90
+ raise ArgumentError, "must be Fixnum, Integer or Float"
91
+ end
92
+ end
93
+
94
+ def self.select_number_as
95
+ case @@bind_unknown_number
96
+ when OCI8::BindType::Fixnum
97
+ return Fixnum
98
+ when OCI8::BindType::Integer
99
+ return Integer
100
+ when OCI8::BindType::Float
101
+ return Float
102
+ end
103
+ end
104
+
105
+ # add alias compatible with 'Oracle7 Module for Ruby'.
106
+ alias getColNames get_col_names
107
+ end
108
+ end
@@ -0,0 +1,491 @@
1
+ require 'date'
2
+
3
+ class OCI8
4
+
5
+ module BindType
6
+
7
+ module Util # :nodoc:
8
+
9
+ @@datetime_fsec_base = (1 / ::DateTime.parse('0001-01-01 00:00:00.000000001').sec_fraction).to_i
10
+ @@time_offset = ::Time.now.utc_offset
11
+ @@datetime_offset = ::DateTime.now.offset
12
+
13
+ @@default_timezone = :local
14
+ def self.default_timezone
15
+ @@default_timezone
16
+ end
17
+
18
+ # Determines default timezone of Time and DateTime retrived from Oracle.
19
+ # This accepts :local or :utc. The default is :local.
20
+ #
21
+ # This parameter is used when both or either of Oracle server and client
22
+ # version is Oracle 8i or lower. If both versions are Oracle 9i or upper,
23
+ # the default timezone is determined by the session timezone.
24
+ def self.default_timezone=(tz)
25
+ if tz != :local and tz != :utc
26
+ raise ArgumentError, "expected :local or :utc but #{tz}"
27
+ end
28
+ @@default_timezone = tz
29
+ end
30
+
31
+ private
32
+
33
+ def datetime_to_array(val, full)
34
+ # year
35
+ year = val.year
36
+ # month
37
+ if val.respond_to? :mon
38
+ month = val.mon
39
+ elsif val.respond_to? :month
40
+ month = val.month
41
+ else
42
+ raise "expect Time, Date or DateTime but #{val.class}"
43
+ end
44
+ # day
45
+ if val.respond_to? :mday
46
+ day = val.mday
47
+ elsif val.respond_to? :day
48
+ day = val.day
49
+ else
50
+ raise "expect Time, Date or DateTime but #{val.class}"
51
+ end
52
+ # hour
53
+ if val.respond_to? :hour
54
+ hour = val.hour
55
+ else
56
+ hour = 0
57
+ end
58
+ # minute
59
+ if val.respond_to? :min
60
+ minute = val.min
61
+ else
62
+ minute = 0
63
+ end
64
+ # second
65
+ if val.respond_to? :sec
66
+ sec = val.sec
67
+ else
68
+ sec = 0
69
+ end
70
+ return [year, month, day, hour, minute, sec] unless full
71
+
72
+ # sec_fraction
73
+ if val.respond_to? :sec_fraction
74
+ fsec = (val.sec_fraction * @@datetime_fsec_base).to_i
75
+ else
76
+ fsec = 0
77
+ end
78
+ # time zone
79
+ if val.respond_to? :offset
80
+ # DateTime
81
+ tz_min = (val.offset * 1440).to_i
82
+ elsif val.respond_to? :utc_offset
83
+ # Time
84
+ tz_min = val.utc_offset / 60
85
+ else
86
+ tz_hour = nil
87
+ tz_min = nil
88
+ end
89
+ if tz_min
90
+ if tz_min < 0
91
+ tz_min = - tz_min
92
+ tz_hour = - (tz_min / 60)
93
+ tz_min = (tz_min % 60)
94
+ else
95
+ tz_hour = tz_min / 60
96
+ tz_min = tz_min % 60
97
+ end
98
+ end
99
+ [year, month, day, hour, minute, sec, fsec, tz_hour, tz_min]
100
+ end
101
+
102
+ def ocidate_to_datetime(ary)
103
+ year, month, day, hour, minute, sec = ary
104
+ if @@default_timezone == :local
105
+ offset = @@datetime_offset
106
+ else
107
+ offset = 0
108
+ end
109
+ ::DateTime.civil(year, month, day, hour, minute, sec, offset)
110
+ end
111
+
112
+ def ocidate_to_time(ary)
113
+ year, month, day, hour, minute, sec = ary
114
+ if year >= 139
115
+ begin
116
+ return ::Time.send(@@default_timezone, year, month, day, hour, minute, sec)
117
+ rescue StandardError
118
+ end
119
+ end
120
+ ocidate_to_datetime(ary)
121
+ end
122
+
123
+ if OCI8.oracle_client_version >= ORAVER_9_0
124
+
125
+ def ocitimestamp_to_datetime(ary)
126
+ year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
127
+ if sec >= 59 and fsec != 0
128
+ # convert to a DateTime via a String as a last resort.
129
+ if tz_hour >= 0 && tz_min >= 0
130
+ sign = ?+
131
+ else
132
+ sign = ?-
133
+ tz_hour = - tz_hour
134
+ tz_min = - tz_min
135
+ end
136
+ time_str = format("%04d-%02d-%02dT%02d:%02d:%02d.%09d%c%02d:%02d",
137
+ year, month, day, hour, minute, sec, fsec, sign, tz_hour, tz_min)
138
+ ::DateTime.parse(time_str)
139
+ else
140
+ sec += fsec.to_r / 1000000000
141
+ offset = tz_hour.to_r / 24 + tz_min.to_r / 1440
142
+ ::DateTime.civil(year, month, day, hour, minute, sec, offset)
143
+ end
144
+ end
145
+
146
+ def ocitimestamp_to_time(ary)
147
+ year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
148
+
149
+ if tz_hour == 0 and tz_min == 0
150
+ timezone = :utc
151
+ elsif @@time_offset == tz_hour * 3600 + tz_min * 60
152
+ timezone = :local
153
+ end
154
+ if timezone and year >= 139
155
+ begin
156
+ # Ruby 1.9 Time class's resolution is nanosecond.
157
+ # But the last argument type is millisecond.
158
+ # 'fsec' is converted to a Float to pass sub-millisecond part.
159
+ return ::Time.send(timezone, year, month, day, hour, minute, sec, fsec / 1000.0)
160
+ rescue StandardError
161
+ end
162
+ end
163
+ ocitimestamp_to_datetime(ary)
164
+ end
165
+ end
166
+ end
167
+
168
+ class DateTimeViaOCIDate < OCI8::BindType::OCIDate
169
+ include OCI8::BindType::Util
170
+
171
+ def set(val) # :nodoc:
172
+ val &&= datetime_to_array(val, false)
173
+ super(val)
174
+ end
175
+
176
+ def get() # :nodoc:
177
+ val = super()
178
+ val ? ocidate_to_datetime(val) : nil
179
+ end
180
+ end
181
+
182
+ class TimeViaOCIDate < OCI8::BindType::OCIDate
183
+ include OCI8::BindType::Util
184
+
185
+ def set(val) # :nodoc:
186
+ val &&= datetime_to_array(val, false)
187
+ super(val)
188
+ end
189
+
190
+ def get() # :nodoc:
191
+ val = super()
192
+ val ? ocidate_to_time(val) : nil
193
+ end
194
+ end
195
+
196
+ if OCI8.oracle_client_version >= ORAVER_9_0
197
+ class DateTimeViaOCITimestamp < OCI8::BindType::OCITimestamp
198
+ include OCI8::BindType::Util
199
+
200
+ def set(val) # :nodoc:
201
+ val &&= datetime_to_array(val, true)
202
+ super(val)
203
+ end
204
+
205
+ def get() # :nodoc:
206
+ val = super()
207
+ val ? ocitimestamp_to_datetime(val) : nil
208
+ end
209
+ end
210
+
211
+ class TimeViaOCITimestamp < OCI8::BindType::OCITimestamp
212
+ include OCI8::BindType::Util
213
+
214
+ def set(val) # :nodoc:
215
+ val &&= datetime_to_array(val, true)
216
+ super(val)
217
+ end
218
+
219
+ def get() # :nodoc:
220
+ val = super()
221
+ val ? ocitimestamp_to_time(val) : nil
222
+ end
223
+ end
224
+ end
225
+
226
+
227
+ #--
228
+ # OCI8::BindType::DateTime
229
+ #++
230
+ # This is a helper class to bind ruby's
231
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]
232
+ # object as Oracle's <tt>TIMESTAMP WITH TIME ZONE</tt> datatype.
233
+ #
234
+ # == Select
235
+ #
236
+ # The fetched value for a <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH
237
+ # TIME ZONE</tt> or <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> column
238
+ # is a DateTime[http://www.ruby-doc.org/core/classes/DateTime.html].
239
+ # The time zone part is a session time zone if the Oracle datatype doesn't
240
+ # have time zone information. The session time zone is the client machine's
241
+ # time zone by default.
242
+ #
243
+ # You can change the session time zone by executing the following SQL.
244
+ #
245
+ # ALTER SESSION SET TIME_ZONE='-05:00'
246
+ #
247
+ # == Bind
248
+ #
249
+ # To bind a DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]
250
+ # value implicitly:
251
+ #
252
+ # conn.exec("INSERT INTO lunar_landings(ship_name, landing_time) VALUES(:1, :2)",
253
+ # 'Apollo 11',
254
+ # DateTime.parse('1969-7-20 20:17:40 00:00'))
255
+ #
256
+ # The bind variable <code>:2</code> is bound as <tt>TIMESTAMP WITH TIME ZONE</tt> on Oracle.
257
+ #
258
+ # To bind explicitly:
259
+ #
260
+ # cursor = conn.exec("INSERT INTO lunar_landings(ship_name, landing_time) VALUES(:1, :2)")
261
+ # cursor.bind_param(':1', nil, String, 60)
262
+ # cursor.bind_param(':2', nil, DateTime)
263
+ # [['Apollo 11', DateTime.parse('1969-07-20 20:17:40 00:00'))],
264
+ # ['Apollo 12', DateTime.parse('1969-11-19 06:54:35 00:00'))],
265
+ # ['Apollo 14', DateTime.parse('1971-02-05 09:18:11 00:00'))],
266
+ # ['Apollo 15', DateTime.parse('1971-07-30 22:16:29 00:00'))],
267
+ # ['Apollo 16', DateTime.parse('1972-04-21 02:23:35 00:00'))],
268
+ # ['Apollo 17', DateTime.parse('1972-12-11 19:54:57 00:00'))]
269
+ # ].each do |ship_name, landing_time|
270
+ # cursor[':1'] = ship_name
271
+ # cursor[':2'] = landing_time
272
+ # cursor.exec
273
+ # end
274
+ # cursor.close
275
+ #
276
+ # On setting a object to the bind variable, you can use any object
277
+ # which has at least three instance methods _year_, _mon_ (or _month_)
278
+ # and _mday_ (or _day_). If the object responses to _hour_, _min_,
279
+ # _sec_ or _sec_fraction_, the responsed values are used for hour,
280
+ # minute, second or fraction of a second respectively.
281
+ # If not, zeros are set. If the object responses to _offset_ or
282
+ # _utc_offset_, it is used for time zone. If not, the session time
283
+ # zone is used.
284
+ #
285
+ # The acceptable value are listed below.
286
+ # _year_:: -4712 to 9999 [excluding year 0]
287
+ # _mon_ (or _month_):: 0 to 12
288
+ # _mday_ (or _day_):: 0 to 31 [depends on the month]
289
+ # _hour_:: 0 to 23
290
+ # _min_:: 0 to 59
291
+ # _sec_:: 0 to 59
292
+ # _sec_fraction_:: 0 to (999_999_999.to_r / (24*60*60* 1_000_000_000)) [999,999,999 nanoseconds]
293
+ # _offset_:: (-12.to_r / 24) to (14.to_r / 24) [-12:00 to +14:00]
294
+ # _utc_offset_:: -12*3600 <= utc_offset <= 24*3600 [-12:00 to +14:00]
295
+ #
296
+ # The output value of the bind varible is always a
297
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html].
298
+ #
299
+ # cursor = conn.exec("BEGIN :ts := current_timestamp; END")
300
+ # cursor.bind_param(:ts, nil, DateTime)
301
+ # cursor.exec
302
+ # cursor[:ts] # => a DateTime.
303
+ # cursor.close
304
+ #
305
+ class DateTime
306
+ if OCI8.oracle_client_version >= ORAVER_9_0
307
+ def self.create(con, val, param, max_array_size)
308
+ if true # TODO: check Oracle server version
309
+ DateTimeViaOCITimestamp.new(con, val, param, max_array_size)
310
+ else
311
+ DateTimeViaOCIDate.new(con, val, param, max_array_size)
312
+ end
313
+ end
314
+ else
315
+ def self.create(con, val, param, max_array_size)
316
+ DateTimeViaOCIDate.new(con, val, param, max_array_size)
317
+ end
318
+ end
319
+ end
320
+
321
+ class Time
322
+ if OCI8.oracle_client_version >= ORAVER_9_0
323
+ def self.create(con, val, param, max_array_size)
324
+ if true # TODO: check Oracle server version
325
+ TimeViaOCITimestamp.new(con, val, param, max_array_size)
326
+ else
327
+ TimeViaOCIDate.new(con, val, param, max_array_size)
328
+ end
329
+ end
330
+ else
331
+ def self.create(con, val, param, max_array_size)
332
+ TimeViaOCIDate.new(con, val, param, max_array_size)
333
+ end
334
+ end
335
+ end
336
+
337
+ if OCI8.oracle_client_version >= ORAVER_9_0
338
+ #--
339
+ # OCI8::BindType::IntervalYM
340
+ #++
341
+ #
342
+ # This is a helper class to bind ruby's
343
+ # Integer[http://www.ruby-doc.org/core/classes/Integer.html]
344
+ # object as Oracle's <tt>INTERVAL YEAR TO MONTH</tt> datatype.
345
+ #
346
+ # == Select
347
+ #
348
+ # The fetched value for a <tt>INTERVAL YEAR TO MONTH</tt> column
349
+ # is an Integer[http://www.ruby-doc.org/core/classes/Integer.html]
350
+ # which means the months between two timestamps.
351
+ #
352
+ # == Bind
353
+ #
354
+ # You cannot bind as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
355
+ # It must be bound explicitly with :interval_ym.
356
+ #
357
+ # # output bind variable
358
+ # cursor = conn.parse(<<-EOS)
359
+ # BEGIN
360
+ # :interval := (:ts1 - :ts2) YEAR TO MONTH;
361
+ # END;
362
+ # EOS
363
+ # cursor.bind_param(:interval, nil, :interval_ym)
364
+ # cursor.bind_param(:ts1, DateTime.parse('1969-11-19 06:54:35 00:00'))
365
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
366
+ # cursor.exec
367
+ # cursor[:interval] # => 4 (months)
368
+ # cursor.close
369
+ #
370
+ # # input bind variable
371
+ # cursor = conn.parse(<<-EOS)
372
+ # BEGIN
373
+ # :ts1 := :ts2 + :interval;
374
+ # END;
375
+ # EOS
376
+ # cursor.bind_param(:ts1, nil, DateTime)
377
+ # cursor.bind_param(:ts2, Date.parse('1969-11-19'))
378
+ # cursor.bind_param(:interval, 4, :interval_ym)
379
+ # cursor.exec
380
+ # cursor[:ts1].strftime('%Y-%m-%d') # => 1970-03-19
381
+ # cursor.close
382
+ #
383
+ class IntervalYM < OCI8::BindType::OCIIntervalYM
384
+ def set(val) # :nodoc:
385
+ unless val.nil?
386
+ val = [val / 12, val % 12]
387
+ end
388
+ super(val)
389
+ end
390
+ def get() # :nodoc:
391
+ val = super()
392
+ return nil if val.nil?
393
+ year, month = val
394
+ year * 12 + month
395
+ end
396
+ end # OCI8::BindType::IntervalYM
397
+
398
+ #--
399
+ # OCI8::BindType::IntervalDS
400
+ #++
401
+ #
402
+ # This is a helper class to bind ruby's
403
+ # Rational[http://www.ruby-doc.org/core/classes/Rational.html]
404
+ # object as Oracle's <tt>INTERVAL DAY TO SECOND</tt> datatype.
405
+ #
406
+ # == Select
407
+ #
408
+ # The fetched value for a <tt>INTERVAL DAY TO SECOND</tt> column
409
+ # is a Rational[http://www.ruby-doc.org/core/classes/Rational.html]
410
+ # or an Integer[http://www.ruby-doc.org/core/classes/Integer.html].
411
+ # The value is usable to apply to
412
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]#+ and
413
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]#-.
414
+ #
415
+ # == Bind
416
+ #
417
+ # You cannot bind as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
418
+ # It must be bound explicitly with :interval_ds.
419
+ #
420
+ # # output
421
+ # ts1 = DateTime.parse('1969-11-19 06:54:35 00:00')
422
+ # ts2 = DateTime.parse('1969-07-20 20:17:40 00:00')
423
+ # cursor = conn.parse(<<-EOS)
424
+ # BEGIN
425
+ # :itv := (:ts1 - :ts2) DAY TO SECOND;
426
+ # END;
427
+ # EOS
428
+ # cursor.bind_param(:itv, nil, :interval_ds)
429
+ # cursor.bind_param(:ts1, ts1)
430
+ # cursor.bind_param(:ts2, ts2)
431
+ # cursor.exec
432
+ # cursor[:itv] # == ts1 - ts2
433
+ # cursor.close
434
+ #
435
+ # # input
436
+ # ts2 = DateTime.parse('1969-07-20 20:17:40 00:00')
437
+ # itv = 121 + 10.to_r/24 + 36.to_r/(24*60) + 55.to_r/(24*60*60)
438
+ # # 121 days, 10 hours, 36 minutes, 55 seconds
439
+ # cursor = conn.parse(<<-EOS)
440
+ # BEGIN
441
+ # :ts1 := :ts2 + :itv;
442
+ # END;
443
+ # EOS
444
+ # cursor.bind_param(:ts1, nil, DateTime)
445
+ # cursor.bind_param(:ts2, ts2)
446
+ # cursor.bind_param(:itv, itv, :interval_ds)
447
+ # cursor.exec
448
+ # cursor[:ts1].strftime('%Y-%m-%d %H:%M:%S') # => 1969-11-19 06:54:35
449
+ # cursor.close
450
+ #
451
+ class IntervalDS < OCI8::BindType::OCIIntervalDS
452
+ @@hour = 1 / 24.to_r
453
+ @@minute = @@hour / 60
454
+ @@sec = @@minute / 60
455
+ @@fsec = @@sec / 1000000000
456
+
457
+ def set(val) # :nodoc:
458
+ unless val.nil?
459
+ if val < 0
460
+ is_minus = true
461
+ val = -val
462
+ else
463
+ is_minus = false
464
+ end
465
+ day, val = val.divmod 1
466
+ hour, val = (val * 24).divmod 1
467
+ minute, val = (val * 60).divmod 1
468
+ sec, val = (val * 60).divmod 1
469
+ fsec, val = (val * 1000000000).divmod 1
470
+ if is_minus
471
+ day = - day
472
+ hour = - hour
473
+ minute = - minute
474
+ sec = - sec
475
+ fsec = - fsec
476
+ end
477
+ val = [day, hour, minute, sec, fsec]
478
+ end
479
+ super(val)
480
+ end
481
+
482
+ def get() # :nodoc:
483
+ val = super()
484
+ return nil if val.nil?
485
+ day, hour, minute, sec, fsec = val
486
+ day + (hour * @@hour) + (minute * @@minute) + (sec * @@sec) + (fsec * @@fsec)
487
+ end
488
+ end # OCI8::BindType::IntervalDS
489
+ end
490
+ end # OCI8::BindType
491
+ end # OCI8