ruby-oci8 1.0.7 → 2.0.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 (89) hide show
  1. data/ChangeLog +1254 -390
  2. data/Makefile +10 -13
  3. data/README +56 -385
  4. data/VERSION +1 -1
  5. data/dist-files +26 -27
  6. data/ext/oci8/.document +1 -0
  7. data/ext/oci8/MANIFEST +0 -4
  8. data/ext/oci8/apiwrap.c.tmpl +172 -0
  9. data/ext/oci8/apiwrap.h.tmpl +61 -0
  10. data/ext/oci8/apiwrap.rb +91 -0
  11. data/ext/oci8/apiwrap.yml +1243 -0
  12. data/ext/oci8/attr.c +124 -384
  13. data/ext/oci8/bind.c +472 -164
  14. data/ext/oci8/encoding.c +196 -0
  15. data/ext/oci8/env.c +84 -253
  16. data/ext/oci8/error.c +196 -127
  17. data/ext/oci8/extconf.rb +82 -59
  18. data/ext/oci8/lob.c +710 -370
  19. data/ext/oci8/metadata.c +359 -0
  20. data/ext/oci8/object.c +622 -0
  21. data/ext/oci8/oci8.c +577 -161
  22. data/ext/oci8/oci8.h +354 -258
  23. data/ext/oci8/oci8lib.c +493 -0
  24. data/ext/oci8/ocidatetime.c +473 -0
  25. data/ext/oci8/ocinumber.c +1123 -24
  26. data/ext/oci8/oraconf.rb +72 -106
  27. data/ext/oci8/oradate.c +511 -321
  28. data/ext/oci8/stmt.c +752 -572
  29. data/ext/oci8/win32.c +131 -0
  30. data/ext/oci8/xmldb.c +383 -0
  31. data/lib/.document +2 -0
  32. data/lib/dbd/OCI8.rb +2 -17
  33. data/lib/oci8.rb.in +41 -1622
  34. data/lib/oci8/.document +5 -0
  35. data/lib/oci8/compat.rb +108 -0
  36. data/lib/oci8/datetime.rb +489 -0
  37. data/lib/oci8/encoding-init.rb +40 -0
  38. data/lib/oci8/encoding.yml +537 -0
  39. data/lib/oci8/metadata.rb +2077 -0
  40. data/lib/oci8/object.rb +548 -0
  41. data/lib/oci8/oci8.rb +773 -0
  42. data/lib/oci8/oracle_version.rb +144 -0
  43. data/metaconfig +3 -3
  44. data/ruby-oci8.gemspec +5 -5
  45. data/setup.rb +4 -4
  46. data/test/config.rb +64 -84
  47. data/test/test_all.rb +14 -21
  48. data/test/test_array_dml.rb +317 -0
  49. data/test/test_bind_raw.rb +18 -25
  50. data/test/test_bind_time.rb +78 -91
  51. data/test/test_break.rb +37 -35
  52. data/test/test_clob.rb +33 -89
  53. data/test/test_connstr.rb +5 -4
  54. data/test/test_datetime.rb +469 -0
  55. data/test/test_dbi.rb +99 -60
  56. data/test/test_dbi_clob.rb +3 -8
  57. data/test/test_metadata.rb +65 -51
  58. data/test/test_oci8.rb +151 -55
  59. data/test/test_oracle_version.rb +70 -0
  60. data/test/test_oradate.rb +76 -83
  61. data/test/test_oranumber.rb +405 -71
  62. data/test/test_rowid.rb +6 -11
  63. metadata +31 -32
  64. data/NEWS +0 -420
  65. data/ext/oci8/const.c +0 -165
  66. data/ext/oci8/define.c +0 -53
  67. data/ext/oci8/describe.c +0 -81
  68. data/ext/oci8/descriptor.c +0 -39
  69. data/ext/oci8/handle.c +0 -273
  70. data/ext/oci8/oranumber.c +0 -445
  71. data/ext/oci8/param.c +0 -37
  72. data/ext/oci8/server.c +0 -182
  73. data/ext/oci8/session.c +0 -99
  74. data/ext/oci8/svcctx.c +0 -238
  75. data/ruby-oci8.spec +0 -62
  76. data/support/README +0 -4
  77. data/support/runit/assert.rb +0 -281
  78. data/support/runit/cui/testrunner.rb +0 -101
  79. data/support/runit/error.rb +0 -4
  80. data/support/runit/method_mappable.rb +0 -20
  81. data/support/runit/robserver.rb +0 -25
  82. data/support/runit/setuppable.rb +0 -15
  83. data/support/runit/teardownable.rb +0 -16
  84. data/support/runit/testcase.rb +0 -113
  85. data/support/runit/testfailure.rb +0 -25
  86. data/support/runit/testresult.rb +0 -121
  87. data/support/runit/testsuite.rb +0 -43
  88. data/support/runit/version.rb +0 -3
  89. 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,489 @@
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 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
+ begin
115
+ ::Time.send(@@default_timezone, year, month, day, hour, minute, sec)
116
+ rescue StandardError
117
+ ocidate_to_datetime(ary)
118
+ end
119
+ end
120
+
121
+ if OCI8.oracle_client_version >= ORAVER_9_0
122
+
123
+ def ocitimestamp_to_datetime(ary)
124
+ year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
125
+ if sec >= 59 and fsec != 0
126
+ # convert to a DateTime via a String as a last resort.
127
+ if tz_hour >= 0 && tz_min >= 0
128
+ sign = ?+
129
+ else
130
+ sign = ?-
131
+ tz_hour = - tz_hour
132
+ tz_min = - tz_min
133
+ end
134
+ time_str = format("%04d-%02d-%02dT%02d:%02d:%02d.%09d%c%02d:%02d",
135
+ year, month, day, hour, minute, sec, fsec, sign, tz_hour, tz_min)
136
+ ::DateTime.parse(time_str)
137
+ else
138
+ sec += fsec.to_r / 1000000000
139
+ offset = tz_hour.to_r / 24 + tz_min.to_r / 1440
140
+ ::DateTime.civil(year, month, day, hour, minute, sec, offset)
141
+ end
142
+ end
143
+
144
+ def ocitimestamp_to_time(ary)
145
+ year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
146
+
147
+ if tz_hour == 0 and tz_min == 0
148
+ timezone = :utc
149
+ elsif @@time_offset == tz_hour * 3600 + tz_min * 60
150
+ timezone = :local
151
+ end
152
+ if timezone
153
+ begin
154
+ # Ruby 1.9 Time class's resolution is nanosecond.
155
+ # But the last argument type is millisecond.
156
+ # 'fsec' is converted to a Float to pass sub-millisecond part.
157
+ return ::Time.send(timezone, year, month, day, hour, minute, sec, fsec / 1000.0)
158
+ rescue StandardError
159
+ end
160
+ end
161
+ ocitimestamp_to_datetime(ary)
162
+ end
163
+ end
164
+ end
165
+
166
+ class DateTimeViaOCIDate < OCI8::BindType::OCIDate
167
+ include OCI8::BindType::Util
168
+
169
+ def set(val) # :nodoc:
170
+ val &&= datetime_to_array(val, false)
171
+ super(val)
172
+ end
173
+
174
+ def get() # :nodoc:
175
+ val = super()
176
+ val ? ocidate_to_datetime(val) : nil
177
+ end
178
+ end
179
+
180
+ class TimeViaOCIDate < OCI8::BindType::OCIDate
181
+ include OCI8::BindType::Util
182
+
183
+ def set(val) # :nodoc:
184
+ val &&= datetime_to_array(val, false)
185
+ super(val)
186
+ end
187
+
188
+ def get() # :nodoc:
189
+ val = super()
190
+ val ? ocidate_to_time(val) : nil
191
+ end
192
+ end
193
+
194
+ if OCI8.oracle_client_version >= ORAVER_9_0
195
+ class DateTimeViaOCITimestamp < OCI8::BindType::OCITimestamp
196
+ include OCI8::BindType::Util
197
+
198
+ def set(val) # :nodoc:
199
+ val &&= datetime_to_array(val, true)
200
+ super(val)
201
+ end
202
+
203
+ def get() # :nodoc:
204
+ val = super()
205
+ val ? ocitimestamp_to_datetime(val) : nil
206
+ end
207
+ end
208
+
209
+ class TimeViaOCITimestamp < OCI8::BindType::OCITimestamp
210
+ include OCI8::BindType::Util
211
+
212
+ def set(val) # :nodoc:
213
+ val &&= datetime_to_array(val, true)
214
+ super(val)
215
+ end
216
+
217
+ def get() # :nodoc:
218
+ val = super()
219
+ val ? ocitimestamp_to_time(val) : nil
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ #--
226
+ # OCI8::BindType::DateTime
227
+ #++
228
+ # This is a helper class to bind ruby's
229
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]
230
+ # object as Oracle's <tt>TIMESTAMP WITH TIME ZONE</tt> datatype.
231
+ #
232
+ # == Select
233
+ #
234
+ # The fetched value for a <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH
235
+ # TIME ZONE</tt> or <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> column
236
+ # is a DateTime[http://www.ruby-doc.org/core/classes/DateTime.html].
237
+ # The time zone part is a session time zone if the Oracle datatype doesn't
238
+ # have time zone information. The session time zone is the client machine's
239
+ # time zone by default.
240
+ #
241
+ # You can change the session time zone by executing the following SQL.
242
+ #
243
+ # ALTER SESSION SET TIME_ZONE='-05:00'
244
+ #
245
+ # == Bind
246
+ #
247
+ # To bind a DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]
248
+ # value implicitly:
249
+ #
250
+ # conn.exec("INSERT INTO lunar_landings(ship_name, landing_time) VALUES(:1, :2)",
251
+ # 'Apollo 11',
252
+ # DateTime.parse('1969-7-20 20:17:40 00:00'))
253
+ #
254
+ # The bind variable <code>:2</code> is bound as <tt>TIMESTAMP WITH TIME ZONE</tt> on Oracle.
255
+ #
256
+ # To bind explicitly:
257
+ #
258
+ # cursor = conn.exec("INSERT INTO lunar_landings(ship_name, landing_time) VALUES(:1, :2)")
259
+ # cursor.bind_param(':1', nil, String, 60)
260
+ # cursor.bind_param(':2', nil, DateTime)
261
+ # [['Apollo 11', DateTime.parse('1969-07-20 20:17:40 00:00'))],
262
+ # ['Apollo 12', DateTime.parse('1969-11-19 06:54:35 00:00'))],
263
+ # ['Apollo 14', DateTime.parse('1971-02-05 09:18:11 00:00'))],
264
+ # ['Apollo 15', DateTime.parse('1971-07-30 22:16:29 00:00'))],
265
+ # ['Apollo 16', DateTime.parse('1972-04-21 02:23:35 00:00'))],
266
+ # ['Apollo 17', DateTime.parse('1972-12-11 19:54:57 00:00'))]
267
+ # ].each do |ship_name, landing_time|
268
+ # cursor[':1'] = ship_name
269
+ # cursor[':2'] = landing_time
270
+ # cursor.exec
271
+ # end
272
+ # cursor.close
273
+ #
274
+ # On setting a object to the bind variable, you can use any object
275
+ # which has at least three instance methods _year_, _mon_ (or _month_)
276
+ # and _mday_ (or _day_). If the object responses to _hour_, _min_,
277
+ # _sec_ or _sec_fraction_, the responsed values are used for hour,
278
+ # minute, second or fraction of a second respectively.
279
+ # If not, zeros are set. If the object responses to _offset_ or
280
+ # _utc_offset_, it is used for time zone. If not, the session time
281
+ # zone is used.
282
+ #
283
+ # The acceptable value are listed below.
284
+ # _year_:: -4712 to 9999 [excluding year 0]
285
+ # _mon_ (or _month_):: 0 to 12
286
+ # _mday_ (or _day_):: 0 to 31 [depends on the month]
287
+ # _hour_:: 0 to 23
288
+ # _min_:: 0 to 59
289
+ # _sec_:: 0 to 59
290
+ # _sec_fraction_:: 0 to (999_999_999.to_r / (24*60*60* 1_000_000_000)) [999,999,999 nanoseconds]
291
+ # _offset_:: (-12.to_r / 24) to (14.to_r / 24) [-12:00 to +14:00]
292
+ # _utc_offset_:: -12*3600 <= utc_offset <= 24*3600 [-12:00 to +14:00]
293
+ #
294
+ # The output value of the bind varible is always a
295
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html].
296
+ #
297
+ # cursor = conn.exec("BEGIN :ts := current_timestamp; END")
298
+ # cursor.bind_param(:ts, nil, DateTime)
299
+ # cursor.exec
300
+ # cursor[:ts] # => a DateTime.
301
+ # cursor.close
302
+ #
303
+ class DateTime
304
+ if OCI8.oracle_client_version >= ORAVER_9_0
305
+ def self.create(con, val, param, max_array_size)
306
+ if true # TODO: check Oracle server version
307
+ DateTimeViaOCITimestamp.new(con, val, param, max_array_size)
308
+ else
309
+ DateTimeViaOCIDate.new(con, val, param, max_array_size)
310
+ end
311
+ end
312
+ else
313
+ def self.create(con, val, param, max_array_size)
314
+ DateTimeViaOCIDate.new(con, val, param, max_array_size)
315
+ end
316
+ end
317
+ end
318
+
319
+ class Time
320
+ if OCI8.oracle_client_version >= ORAVER_9_0
321
+ def self.create(con, val, param, max_array_size)
322
+ if true # TODO: check Oracle server version
323
+ TimeViaOCITimestamp.new(con, val, param, max_array_size)
324
+ else
325
+ TimeViaOCIDate.new(con, val, param, max_array_size)
326
+ end
327
+ end
328
+ else
329
+ def self.create(con, val, param, max_array_size)
330
+ TimeViaOCIDate.new(con, val, param, max_array_size)
331
+ end
332
+ end
333
+ end
334
+
335
+ if OCI8.oracle_client_version >= ORAVER_9_0
336
+ #--
337
+ # OCI8::BindType::IntervalYM
338
+ #++
339
+ #
340
+ # This is a helper class to bind ruby's
341
+ # Integer[http://www.ruby-doc.org/core/classes/Integer.html]
342
+ # object as Oracle's <tt>INTERVAL YEAR TO MONTH</tt> datatype.
343
+ #
344
+ # == Select
345
+ #
346
+ # The fetched value for a <tt>INTERVAL YEAR TO MONTH</tt> column
347
+ # is an Integer[http://www.ruby-doc.org/core/classes/Integer.html]
348
+ # which means the months between two timestamps.
349
+ #
350
+ # == Bind
351
+ #
352
+ # You cannot bind as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
353
+ # It must be bound explicitly with :interval_ym.
354
+ #
355
+ # # output bind variable
356
+ # cursor = conn.parse(<<-EOS)
357
+ # BEGIN
358
+ # :interval := (:ts1 - :ts2) YEAR TO MONTH;
359
+ # END;
360
+ # EOS
361
+ # cursor.bind_param(:interval, nil, :interval_ym)
362
+ # cursor.bind_param(:ts1, DateTime.parse('1969-11-19 06:54:35 00:00'))
363
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
364
+ # cursor.exec
365
+ # cursor[:interval] # => 4 (months)
366
+ # cursor.close
367
+ #
368
+ # # input bind variable
369
+ # cursor = conn.parse(<<-EOS)
370
+ # BEGIN
371
+ # :ts1 := :ts2 + :interval;
372
+ # END;
373
+ # EOS
374
+ # cursor.bind_param(:ts1, nil, DateTime)
375
+ # cursor.bind_param(:ts2, Date.parse('1969-11-19'))
376
+ # cursor.bind_param(:interval, 4, :interval_ym)
377
+ # cursor.exec
378
+ # cursor[:ts1].strftime('%Y-%m-%d') # => 1970-03-19
379
+ # cursor.close
380
+ #
381
+ class IntervalYM < OCI8::BindType::OCIIntervalYM
382
+ def set(val) # :nodoc:
383
+ unless val.nil?
384
+ val = [val / 12, val % 12]
385
+ end
386
+ super(val)
387
+ end
388
+ def get() # :nodoc:
389
+ val = super()
390
+ return nil if val.nil?
391
+ year, month = val
392
+ year * 12 + month
393
+ end
394
+ end # OCI8::BindType::IntervalYM
395
+
396
+ #--
397
+ # OCI8::BindType::IntervalDS
398
+ #++
399
+ #
400
+ # This is a helper class to bind ruby's
401
+ # Rational[http://www.ruby-doc.org/core/classes/Rational.html]
402
+ # object as Oracle's <tt>INTERVAL DAY TO SECOND</tt> datatype.
403
+ #
404
+ # == Select
405
+ #
406
+ # The fetched value for a <tt>INTERVAL DAY TO SECOND</tt> column
407
+ # is a Rational[http://www.ruby-doc.org/core/classes/Rational.html]
408
+ # or an Integer[http://www.ruby-doc.org/core/classes/Integer.html].
409
+ # The value is usable to apply to
410
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]#+ and
411
+ # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]#-.
412
+ #
413
+ # == Bind
414
+ #
415
+ # You cannot bind as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
416
+ # It must be bound explicitly with :interval_ds.
417
+ #
418
+ # # output
419
+ # ts1 = DateTime.parse('1969-11-19 06:54:35 00:00')
420
+ # ts2 = DateTime.parse('1969-07-20 20:17:40 00:00')
421
+ # cursor = conn.parse(<<-EOS)
422
+ # BEGIN
423
+ # :itv := (:ts1 - :ts2) DAY TO SECOND;
424
+ # END;
425
+ # EOS
426
+ # cursor.bind_param(:itv, nil, :interval_ds)
427
+ # cursor.bind_param(:ts1, ts1)
428
+ # cursor.bind_param(:ts2, ts2)
429
+ # cursor.exec
430
+ # cursor[:itv] # == ts1 - ts2
431
+ # cursor.close
432
+ #
433
+ # # input
434
+ # ts2 = DateTime.parse('1969-07-20 20:17:40 00:00')
435
+ # itv = 121 + 10.to_r/24 + 36.to_r/(24*60) + 55.to_r/(24*60*60)
436
+ # # 121 days, 10 hours, 36 minutes, 55 seconds
437
+ # cursor = conn.parse(<<-EOS)
438
+ # BEGIN
439
+ # :ts1 := :ts2 + :itv;
440
+ # END;
441
+ # EOS
442
+ # cursor.bind_param(:ts1, nil, DateTime)
443
+ # cursor.bind_param(:ts2, ts2)
444
+ # cursor.bind_param(:itv, itv, :interval_ds)
445
+ # cursor.exec
446
+ # cursor[:ts1].strftime('%Y-%m-%d %H:%M:%S') # => 1969-11-19 06:54:35
447
+ # cursor.close
448
+ #
449
+ class IntervalDS < OCI8::BindType::OCIIntervalDS
450
+ @@hour = 1 / 24.to_r
451
+ @@minute = @@hour / 60
452
+ @@sec = @@minute / 60
453
+ @@fsec = @@sec / 1000000000
454
+
455
+ def set(val) # :nodoc:
456
+ unless val.nil?
457
+ if val < 0
458
+ is_minus = true
459
+ val = -val
460
+ else
461
+ is_minus = false
462
+ end
463
+ day, val = val.divmod 1
464
+ hour, val = (val * 24).divmod 1
465
+ minute, val = (val * 60).divmod 1
466
+ sec, val = (val * 60).divmod 1
467
+ fsec, val = (val * 1000000000).divmod 1
468
+ if is_minus
469
+ day = - day
470
+ hour = - hour
471
+ minute = - minute
472
+ sec = - sec
473
+ fsec = - fsec
474
+ end
475
+ val = [day, hour, minute, sec, fsec]
476
+ end
477
+ super(val)
478
+ end
479
+
480
+ def get() # :nodoc:
481
+ val = super()
482
+ return nil if val.nil?
483
+ day, hour, minute, sec, fsec = val
484
+ day + (hour * @@hour) + (minute * @@minute) + (sec * @@sec) + (fsec * @@fsec)
485
+ end
486
+ end # OCI8::BindType::IntervalDS
487
+ end
488
+ end # OCI8::BindType
489
+ end # OCI8