ruby-oci8-master 2.0.7

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