ruby-oci8 1.0.7 → 2.0.0

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