ruby-oci8 2.0.4-x86-mingw32

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