ruby-oci8 2.0.2 → 2.0.3

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.
@@ -353,8 +353,10 @@ EOS
353
353
  end
354
354
  print <<EOS
355
355
  ---------------------------------------------------
356
- error messages:
357
- #{$!.to_s}
356
+ Error Message:
357
+ #{$!.to_s.gsub(/\n/, "\n ")}
358
+ Backtrace:
359
+ #{$!.backtrace.join("\n ")}
358
360
  ---------------------------------------------------
359
361
  See:
360
362
  * http://ruby-oci8.rubyforge.org/#{lang}/HowToInstall.html
@@ -383,15 +385,16 @@ EOS
383
385
  # get library load path names
384
386
  oci_basename = 'libclntsh'
385
387
  oci_glob_postfix = '.[0-9]*'
386
- ocidata_basename = ['libociei', 'libociicus']
388
+ nls_data_basename = ['libociei', 'libociicus']
387
389
  @@ld_envs = %w[LD_LIBRARY_PATH]
388
390
  so_ext = 'so'
391
+ nls_data_ext = nil
389
392
  check_proc = nil
390
393
  case RUBY_PLATFORM
391
394
  when /mswin32|cygwin|mingw32|bccwin32/
392
395
  oci_basename = 'oci'
393
396
  oci_glob_postfix = ''
394
- ocidata_basename = ['oraociei11', 'oraociicus11', 'oraociei10', 'oraociicus10']
397
+ nls_data_basename = ['oraociei11', 'oraociicus11', 'oraociei10', 'oraociicus10']
395
398
  @@ld_envs = %w[PATH]
396
399
  so_ext = 'dll'
397
400
  when /i.86-linux/
@@ -434,6 +437,7 @@ EOS
434
437
  oci_glob_postfix = ''
435
438
  @@ld_envs = %w[LIBPATH]
436
439
  so_ext = 'a'
440
+ nls_data_ext = 'so'
437
441
  when /hppa.*-hpux/
438
442
  if [0].pack('l!').length == 4
439
443
  @@ld_envs = %w[SHLIB_PATH]
@@ -529,8 +533,9 @@ EOS
529
533
  end
530
534
 
531
535
  if ld_path
532
- ocidata_basename.each do |basename|
533
- if File.exist?(File.join(ld_path, "#{basename}.#{so_ext}"))
536
+ nls_data_ext ||= so_ext # nls_data_ext is same with so_ext by default.
537
+ nls_data_basename.each do |basename|
538
+ if File.exist?(File.join(ld_path, "#{basename}.#{nls_data_ext}"))
534
539
  puts " #{file} looks like an instant client."
535
540
  return ld_path
536
541
  end
@@ -613,6 +618,7 @@ You need /usr/include/sys/types.h to compile ruby-oci8.
613
618
  EOS
614
619
  end
615
620
  puts "ok"
621
+ $stdout.flush
616
622
  end # check_ruby_header
617
623
 
618
624
  def try_link_oci
@@ -860,10 +866,33 @@ EOS
860
866
  def get_home
861
867
  oracle_home = ENV['ORACLE_HOME']
862
868
  if oracle_home.nil?
863
- raise <<EOS
869
+ msg = <<EOS
864
870
  Set the environment variable ORACLE_HOME if Oracle Full Client.
865
871
  Append the path of Oracle client libraries to #{OraConf.ld_envs[0]} if Oracle Instant Client.
866
872
  EOS
873
+
874
+ # check sudo environment
875
+ sudo_command = ENV['SUDO_COMMAND']
876
+ if /\w*make\b/ =~ sudo_command
877
+ msg += <<EOS
878
+
879
+ The 'sudo' command unset some environment variables for security reasons.
880
+ Use it only when running 'make install' as follows
881
+ make
882
+ sudo make install
883
+ EOS
884
+ end
885
+ if /\w+\/gem\b/ =~ sudo_command
886
+ msg += <<EOS
887
+
888
+ The 'sudo' command unset some environment variables for security reasons.
889
+ Pass required varialbes as follows
890
+ sudo env #{OraConf.ld_envs[0]}=$#{OraConf.ld_envs[0]} #{sudo_command}
891
+ or
892
+ sudo env ORACLE_HOME=$ORACLE_HOME #{sudo_command}
893
+ EOS
894
+ end
895
+ raise msg
867
896
  end
868
897
  oracle_home
869
898
  end
@@ -953,10 +982,10 @@ EOS
953
982
 
954
983
  # monkey patching!
955
984
  Object.module_eval do
956
- alias :orig_link_command :link_command
957
- def link_command(ldflags, opt="", libpath=$DEFLIBPATH|$LIBPATH)
958
- opt = "" if opt == $libs
959
- orig_link_command(ldflags, opt, libpath)
985
+ alias :link_command_pre_oci8 :link_command
986
+ def link_command(*args)
987
+ args[1] = "" if args[1] == $libs
988
+ link_command_pre_oci8(*args)
960
989
  end
961
990
  end
962
991
 
@@ -3,7 +3,7 @@
3
3
  * oradate.c
4
4
  *
5
5
  * $Author: kubo $
6
- * $Date: 2009-05-17 22:07:16 +0900 (Sun, 17 May 2009) $
6
+ * $Date: 2009-10-21 22:50:01 +0900 (Wed, 21 Oct 2009) $
7
7
  *
8
8
  * Copyright (C) 2002-2008 KUBO Takehiro <kubo@jiubao.org>
9
9
  *
@@ -1,2 +1 @@
1
1
  oci8
2
- oci8.rb
@@ -19,7 +19,7 @@ if RUBY_PLATFORM =~ /cygwin/
19
19
  end
20
20
 
21
21
  case RUBY_VERSION
22
- when /^1\.9\.1/
22
+ when /^1\.9/
23
23
  require 'oci8lib_191'
24
24
  when /^1\.8/
25
25
  require 'oci8lib_18'
@@ -1,5 +1,5 @@
1
- oci8.rb
1
+ datetime.rb
2
2
  object.rb
3
3
  metadata.rb
4
4
  oracle_version.rb
5
-
5
+ oci8.rb
@@ -24,6 +24,28 @@ class OCI8
24
24
  end
25
25
  end
26
26
 
27
+ class BigDecimal < OCI8::BindType::OraNumber
28
+ @@bigdecimal_is_required = false
29
+ def get()
30
+ unless @@bigdecimal_is_required
31
+ require 'bigdecimal'
32
+ @@bigdecimal_is_required = true
33
+ end
34
+ (val = super()) && val.to_d
35
+ end
36
+ end
37
+
38
+ class Rational < OCI8::BindType::OraNumber
39
+ @@rational_is_required = false
40
+ def get()
41
+ unless @@rational_is_required
42
+ require 'rational'
43
+ @@rational_is_required = true
44
+ end
45
+ (val = super()) && val.to_r
46
+ end
47
+ end
48
+
27
49
  # get/set Number (for OCI8::SQLT_NUM)
28
50
  class Number
29
51
  def self.create(con, val, param, max_array_size)
@@ -54,8 +76,8 @@ class OCI8
54
76
  if precision < 15 # the precision of double.
55
77
  klass = OCI8::BindType::Float
56
78
  else
57
- # use BigDecimal instead?
58
- klass = OCI8::BindType::OraNumber
79
+ # use BigDecimal instead
80
+ klass = OCI8::BindType::BigDecimal
59
81
  end
60
82
  end
61
83
  klass.new(con, val, nil, max_array_size)
@@ -156,6 +178,8 @@ end
156
178
  # bind or explicitly define
157
179
  OCI8::BindType::Mapping[String] = OCI8::BindType::String
158
180
  OCI8::BindType::Mapping[OraNumber] = OCI8::BindType::OraNumber
181
+ OCI8::BindType::Mapping['BigDecimal'] = OCI8::BindType::BigDecimal
182
+ OCI8::BindType::Mapping['Rational'] = OCI8::BindType::Rational
159
183
  OCI8::BindType::Mapping[Fixnum] = OCI8::BindType::Integer
160
184
  OCI8::BindType::Mapping[Float] = OCI8::BindType::Float
161
185
  OCI8::BindType::Mapping[Integer] = OCI8::BindType::Integer
@@ -263,9 +287,9 @@ OCI8::BindType::Mapping[:number] = OCI8::BindType::Number
263
287
  # datatypes that have no explicit setting of their precision
264
288
  # and scale.
265
289
  #
266
- # The default mapping is Float for ruby-oci8 1.0. It is OraNumber
267
- # for ruby-oci8 2.0.
268
- OCI8::BindType::Mapping[:number_unknown_prec] = OCI8::BindType::OraNumber
290
+ # The default mapping is Float for ruby-oci8 1.0, OraNumber for 2.0.0 ~ 2.0.2,
291
+ # BigDecimal for 2.0.3 ~.
292
+ OCI8::BindType::Mapping[:number_unknown_prec] = OCI8::BindType::BigDecimal
269
293
 
270
294
  # mapping for number without precision and scale.
271
295
  #
@@ -276,9 +300,9 @@ OCI8::BindType::Mapping[:number_unknown_prec] = OCI8::BindType::OraNumber
276
300
  # note: This is available only on Oracle 9.2.0.3 or above.
277
301
  # see: Oracle 9.2.0.x Patch Set Notes.
278
302
  #
279
- # The default mapping is Float for ruby-oci8 1.0. It is OraNumber
280
- # for ruby-oci8 2.0.
281
- OCI8::BindType::Mapping[:number_no_prec_setting] = OCI8::BindType::OraNumber
303
+ # The default mapping is Float for ruby-oci8 1.0, OraNumber for 2.0.0 ~ 2.0.2,
304
+ # BigDecimal for 2.0.3 ~.
305
+ OCI8::BindType::Mapping[:number_no_prec_setting] = OCI8::BindType::BigDecimal
282
306
 
283
307
  if defined? OCI8::BindType::BinaryDouble
284
308
  OCI8::BindType::Mapping[:binary_float] = OCI8::BindType::BinaryDouble
@@ -105,4 +105,9 @@ class OCI8
105
105
  # add alias compatible with 'Oracle7 Module for Ruby'.
106
106
  alias getColNames get_col_names
107
107
  end
108
+
109
+ module BindType
110
+ # alias to Integer for compatibility with ruby-oci8 1.0.
111
+ Fixnum = Integer
112
+ end
108
113
  end
@@ -4,23 +4,44 @@ class OCI8
4
4
 
5
5
  module BindType
6
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
+
7
29
  module Util # :nodoc:
8
30
 
9
31
  @@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
32
 
13
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
+
14
41
  def self.default_timezone
15
42
  @@default_timezone
16
43
  end
17
44
 
18
- # Determines default timezone of Time and DateTime retrived from Oracle.
19
- # This accepts :local or :utc. The default is :local.
20
- #
21
- # This parameter is used when both or either of Oracle server and client
22
- # version is Oracle 8i or lower. If both versions are Oracle 9i or upper,
23
- # the default timezone is determined by the session timezone.
24
45
  def self.default_timezone=(tz)
25
46
  if tz != :local and tz != :utc
26
47
  raise ArgumentError, "expected :local or :utc but #{tz}"
@@ -31,6 +52,8 @@ class OCI8
31
52
  private
32
53
 
33
54
  def datetime_to_array(val, full)
55
+ return nil if val.nil?
56
+
34
57
  # year
35
58
  year = val.year
36
59
  # month
@@ -100,9 +123,16 @@ class OCI8
100
123
  end
101
124
 
102
125
  def ocidate_to_datetime(ary)
126
+ return nil if ary.nil?
127
+
103
128
  year, month, day, hour, minute, sec = ary
104
129
  if @@default_timezone == :local
105
- offset = @@datetime_offset
130
+ if ::DateTime.respond_to? :local_offset
131
+ offset = ::DateTime.local_offset # Use a method defined by active support.
132
+ else
133
+ # Do as active support does.
134
+ offset = ::Time.local(2007).utc_offset.to_r / 86400
135
+ end
106
136
  else
107
137
  offset = 0
108
138
  end
@@ -110,8 +140,10 @@ class OCI8
110
140
  end
111
141
 
112
142
  def ocidate_to_time(ary)
143
+ return nil if ary.nil?
144
+
113
145
  year, month, day, hour, minute, sec = ary
114
- if year >= 139
146
+ if @@time_new_accepts_timezone || year >= 139 || year < 0
115
147
  begin
116
148
  return ::Time.send(@@default_timezone, year, month, day, hour, minute, sec)
117
149
  rescue StandardError
@@ -123,6 +155,8 @@ class OCI8
123
155
  if OCI8.oracle_client_version >= ORAVER_9_0
124
156
 
125
157
  def ocitimestamp_to_datetime(ary)
158
+ return nil if ary.nil?
159
+
126
160
  year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
127
161
  if sec >= 59 and fsec != 0
128
162
  # convert to a DateTime via a String as a last resort.
@@ -143,168 +177,159 @@ class OCI8
143
177
  end
144
178
  end
145
179
 
146
- def ocitimestamp_to_time(ary)
147
- year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
180
+ if @@time_new_accepts_timezone
181
+
182
+ # after ruby 1.9.2
183
+ def ocitimestamp_to_time(ary)
184
+ return nil if ary.nil?
148
185
 
149
- if tz_hour == 0 and tz_min == 0
150
- timezone = :utc
151
- elsif @@time_offset == tz_hour * 3600 + tz_min * 60
152
- timezone = :local
186
+ year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
187
+
188
+ sec += fsec / Rational(1000000000)
189
+ utc_offset = tz_hour * 3600 + tz_min * 60
190
+ return ::Time.new(year, month, day, hour, minute, sec, utc_offset)
153
191
  end
154
- if timezone and year >= 139
155
- begin
156
- # Ruby 1.9 Time class's resolution is nanosecond.
157
- # But the last argument type is millisecond.
158
- # 'fsec' is converted to a Float to pass sub-millisecond part.
159
- return ::Time.send(timezone, year, month, day, hour, minute, sec, fsec / 1000.0)
160
- rescue StandardError
192
+
193
+ else
194
+
195
+ # prior to ruby 1.9.2
196
+ def ocitimestamp_to_time(ary)
197
+ return nil if ary.nil?
198
+
199
+ year, month, day, hour, minute, sec, fsec, tz_hour, tz_min = ary
200
+
201
+ if year >= 139 || year < 0
202
+ begin
203
+ if tz_hour == 0 and tz_min == 0
204
+ return ::Time.utc(year, month, day, hour, minute, sec, fsec / Rational(1000))
205
+ else
206
+ tm = ::Time.local(year, month, day, hour, minute, sec, fsec / Rational(1000))
207
+ return tm if tm.utc_offset == tz_hour * 3600 + tz_min * 60
208
+ end
209
+ rescue StandardError
210
+ end
161
211
  end
212
+ ocitimestamp_to_datetime(ary)
162
213
  end
163
- ocitimestamp_to_datetime(ary)
214
+
164
215
  end
165
216
  end
166
217
  end
167
218
 
168
- class DateTimeViaOCIDate < OCI8::BindType::OCIDate
219
+ class DateTimeViaOCIDate < OCI8::BindType::OCIDate # :nodoc:
169
220
  include OCI8::BindType::Util
170
221
 
171
222
  def set(val) # :nodoc:
172
- val &&= datetime_to_array(val, false)
173
- super(val)
223
+ super(datetime_to_array(val, false))
174
224
  end
175
225
 
176
226
  def get() # :nodoc:
177
- val = super()
178
- val ? ocidate_to_datetime(val) : nil
227
+ ocidate_to_datetime(super())
179
228
  end
180
229
  end
181
230
 
182
- class TimeViaOCIDate < OCI8::BindType::OCIDate
231
+ class TimeViaOCIDate < OCI8::BindType::OCIDate # :nodoc:
183
232
  include OCI8::BindType::Util
184
233
 
185
234
  def set(val) # :nodoc:
186
- val &&= datetime_to_array(val, false)
187
- super(val)
235
+ super(datetime_to_array(val, false))
188
236
  end
189
237
 
190
238
  def get() # :nodoc:
191
- val = super()
192
- val ? ocidate_to_time(val) : nil
239
+ ocidate_to_time(super())
193
240
  end
194
241
  end
195
242
 
196
243
  if OCI8.oracle_client_version >= ORAVER_9_0
197
- class DateTimeViaOCITimestamp < OCI8::BindType::OCITimestamp
244
+ class DateTimeViaOCITimestamp < OCI8::BindType::OCITimestamp # :nodoc:
198
245
  include OCI8::BindType::Util
199
246
 
200
247
  def set(val) # :nodoc:
201
- val &&= datetime_to_array(val, true)
202
- super(val)
248
+ super(datetime_to_array(val, true))
203
249
  end
204
250
 
205
251
  def get() # :nodoc:
206
- val = super()
207
- val ? ocitimestamp_to_datetime(val) : nil
252
+ ocitimestamp_to_datetime(super())
208
253
  end
209
254
  end
210
255
 
211
- class TimeViaOCITimestamp < OCI8::BindType::OCITimestamp
256
+ class TimeViaOCITimestamp < OCI8::BindType::OCITimestamp # :nodoc:
212
257
  include OCI8::BindType::Util
213
258
 
214
259
  def set(val) # :nodoc:
215
- val &&= datetime_to_array(val, true)
216
- super(val)
260
+ super(datetime_to_array(val, true))
217
261
  end
218
262
 
219
263
  def get() # :nodoc:
220
- val = super()
221
- val ? ocitimestamp_to_time(val) : nil
264
+ ocitimestamp_to_time(super())
222
265
  end
223
266
  end
224
267
  end
225
268
 
226
-
227
269
  #--
228
270
  # OCI8::BindType::DateTime
229
271
  #++
230
- # This is a helper class to bind ruby's
231
- # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]
232
- # object as Oracle's <tt>TIMESTAMP WITH TIME ZONE</tt> datatype.
272
+ # This is a helper class to select or bind Oracle data types such as
273
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
274
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt>. The retrieved value
275
+ # is a \DateTime.
276
+ #
277
+ # === How to select \DataTime values.
278
+ #
279
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
280
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> are selected as a \Time
281
+ # by default. You change the behaviour by explicitly calling
282
+ # OCI8::Cursor#define as follows:
283
+ #
284
+ # cursor = conn.parse("SELECT hiredate FROM emp")
285
+ # cursor.define(1, nil, DateTime)
286
+ # cursor.exec()
287
+ #
288
+ # Otherwise, you can change the default mapping for all queries.
233
289
  #
234
- # == Select
290
+ # # Changes the mapping for DATE
291
+ # OCI8::BindType::Mapping[OCI8::SQLT_DAT] = OCI8::BindType::DateTime
292
+ #
293
+ # # Changes the mapping for TIMESTAMP
294
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP] = OCI8::BindType::DateTime
295
+ #
296
+ # # Changes the mapping for TIMESTAMP WITH TIME ZONE
297
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_TZ] = OCI8::BindType::DateTime
298
+ #
299
+ # # Changes the mapping for TIMESTAMP WITH LOCAL TIME ZONE
300
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_LTZ] = OCI8::BindType::DateTime
235
301
  #
236
- # The fetched value for a <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH
237
- # TIME ZONE</tt> or <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> column
238
- # is a DateTime[http://www.ruby-doc.org/core/classes/DateTime.html].
239
- # The time zone part is a session time zone if the Oracle datatype doesn't
240
- # have time zone information. The session time zone is the client machine's
241
- # time zone by default.
302
+ # === Note for default time zone
242
303
  #
243
- # You can change the session time zone by executing the following SQL.
304
+ # The retrieved value's time zone is determined by the session time zone
305
+ # if its data type is <tt>DATE</tt>, <tt>TIMESTAMP</tt> or <tt>TIMESTAMP
306
+ # WITH LOCAL TIME ZONE</tt>.
307
+ #
308
+ # The session time zone is same with local machine's by default.
309
+ # It is changed by the following SQL.
244
310
  #
245
311
  # ALTER SESSION SET TIME_ZONE='-05:00'
246
312
  #
247
- # == Bind
248
- #
249
- # To bind a DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]
250
- # value implicitly:
251
- #
252
- # conn.exec("INSERT INTO lunar_landings(ship_name, landing_time) VALUES(:1, :2)",
253
- # 'Apollo 11',
254
- # DateTime.parse('1969-7-20 20:17:40 00:00'))
255
- #
256
- # The bind variable <code>:2</code> is bound as <tt>TIMESTAMP WITH TIME ZONE</tt> on Oracle.
257
- #
258
- # To bind explicitly:
259
- #
260
- # cursor = conn.exec("INSERT INTO lunar_landings(ship_name, landing_time) VALUES(:1, :2)")
261
- # cursor.bind_param(':1', nil, String, 60)
262
- # cursor.bind_param(':2', nil, DateTime)
263
- # [['Apollo 11', DateTime.parse('1969-07-20 20:17:40 00:00'))],
264
- # ['Apollo 12', DateTime.parse('1969-11-19 06:54:35 00:00'))],
265
- # ['Apollo 14', DateTime.parse('1971-02-05 09:18:11 00:00'))],
266
- # ['Apollo 15', DateTime.parse('1971-07-30 22:16:29 00:00'))],
267
- # ['Apollo 16', DateTime.parse('1972-04-21 02:23:35 00:00'))],
268
- # ['Apollo 17', DateTime.parse('1972-12-11 19:54:57 00:00'))]
269
- # ].each do |ship_name, landing_time|
270
- # cursor[':1'] = ship_name
271
- # cursor[':2'] = landing_time
272
- # cursor.exec
273
- # end
274
- # cursor.close
275
- #
276
- # On setting a object to the bind variable, you can use any object
277
- # which has at least three instance methods _year_, _mon_ (or _month_)
278
- # and _mday_ (or _day_). If the object responses to _hour_, _min_,
279
- # _sec_ or _sec_fraction_, the responsed values are used for hour,
280
- # minute, second or fraction of a second respectively.
281
- # If not, zeros are set. If the object responses to _offset_ or
282
- # _utc_offset_, it is used for time zone. If not, the session time
283
- # zone is used.
284
- #
285
- # The acceptable value are listed below.
286
- # _year_:: -4712 to 9999 [excluding year 0]
287
- # _mon_ (or _month_):: 0 to 12
288
- # _mday_ (or _day_):: 0 to 31 [depends on the month]
289
- # _hour_:: 0 to 23
290
- # _min_:: 0 to 59
291
- # _sec_:: 0 to 59
292
- # _sec_fraction_:: 0 to (999_999_999.to_r / (24*60*60* 1_000_000_000)) [999,999,999 nanoseconds]
293
- # _offset_:: (-12.to_r / 24) to (14.to_r / 24) [-12:00 to +14:00]
294
- # _utc_offset_:: -12*3600 <= utc_offset <= 24*3600 [-12:00 to +14:00]
295
- #
296
- # The output value of the bind varible is always a
297
- # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html].
298
- #
299
- # cursor = conn.exec("BEGIN :ts := current_timestamp; END")
300
- # cursor.bind_param(:ts, nil, DateTime)
301
- # cursor.exec
302
- # cursor[:ts] # => a DateTime.
303
- # cursor.close
313
+ # === Note for Oracle 8.x client
314
+ #
315
+ # Timestamp data types and session time zone are new features in
316
+ # Oracle 9i. This class is available only to fetch or bind <tt>DATE</tt>
317
+ # when using Oracle 8.x client.
318
+ #
319
+ # The retrieved value's time zone is determined not by the session
320
+ # time zone, but by the OCI8::BindType.default_timezone
321
+ # The time zone can be changed as follows:
322
+ #
323
+ # OCI8::BindType.default_timezone = :local
324
+ # # or
325
+ # OCI8::BindType.default_timezone = :utc
326
+ #
327
+ # If you are in the regions where daylight saving time is adopted,
328
+ # you should use OCI8::BindType::Time.
304
329
  #
305
330
  class DateTime
306
331
  if OCI8.oracle_client_version >= ORAVER_9_0
307
- def self.create(con, val, param, max_array_size)
332
+ def self.create(con, val, param, max_array_size) # :nodoc:
308
333
  if true # TODO: check Oracle server version
309
334
  DateTimeViaOCITimestamp.new(con, val, param, max_array_size)
310
335
  else
@@ -312,15 +337,78 @@ class OCI8
312
337
  end
313
338
  end
314
339
  else
315
- def self.create(con, val, param, max_array_size)
340
+ def self.create(con, val, param, max_array_size) # :nodoc:
316
341
  DateTimeViaOCIDate.new(con, val, param, max_array_size)
317
342
  end
318
343
  end
319
344
  end
320
345
 
346
+ #--
347
+ # OCI8::BindType::Time
348
+ #++
349
+ # This is a helper class to select or bind Oracle data types such as
350
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
351
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt>. The retrieved value
352
+ # is a \Time.
353
+ #
354
+ # === How to select \Time values.
355
+ #
356
+ # <tt>DATE</tt>, <tt>TIMESTAMP</tt>, <tt>TIMESTAMP WITH TIME ZONE</tt>
357
+ # and <tt>TIMESTAMP WITH LOCAL TIME ZONE</tt> are selected as a \Time
358
+ # by default. If the default behaviour is changed, you can select it
359
+ # as a \Time by explicitly calling OCI8::Cursor#define as follows:
360
+ #
361
+ # cursor = conn.parse("SELECT hiredate FROM emp")
362
+ # cursor.define(1, nil, Time)
363
+ # cursor.exec()
364
+ #
365
+ # === Note for ruby prior to 1.9.2
366
+ #
367
+ # If the retrieved value cannot be represented by \Time, it become
368
+ # a \DateTime. The fallback is done only when the ruby is before 1.9.2
369
+ # and one of the following conditions are met.
370
+ # - The timezone part is neither local nor utc.
371
+ # - The time is out of the time_t[http://en.wikipedia.org/wiki/Time_t].
372
+ #
373
+ # If the retrieved value has the precision of fractional second more
374
+ # than 6, the fractional second is truncated to microsecond, which
375
+ # is the precision of standard \Time class.
376
+ #
377
+ # To avoid this fractional second truncation:
378
+ # - Upgrade to ruby 1.9.2, whose \Time precision is nanosecond.
379
+ # - Otherwise, change the defalt mapping to use \DateTime as follows.
380
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP] = OCI8::BindType::DateTime
381
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_TZ] = OCI8::BindType::DateTime
382
+ # OCI8::BindType::Mapping[OCI8::SQLT_TIMESTAMP_LTZ] = OCI8::BindType::DateTime
383
+ #
384
+ # === Note for default time zone
385
+ #
386
+ # The retrieved value's time zone is determined by the session time zone
387
+ # if its data type is <tt>DATE</tt>, <tt>TIMESTAMP</tt> or <tt>TIMESTAMP
388
+ # WITH LOCAL TIME ZONE</tt>.
389
+ #
390
+ # The session time zone is same with local machine's by default.
391
+ # It is changed by the following SQL.
392
+ #
393
+ # ALTER SESSION SET TIME_ZONE='-05:00'
394
+ #
395
+ # === Note for Oracle 8.x client
396
+ #
397
+ # Timestamp data types and session time zone are new features in
398
+ # Oracle 9i. This class is available only to fetch or bind <tt>DATE</tt>
399
+ # when using Oracle 8.x client.
400
+ #
401
+ # The retrieved value's time zone is determined not by the session
402
+ # time zone, but by the OCI8::BindType.default_timezone
403
+ # The time zone can be changed as follows:
404
+ #
405
+ # OCI8::BindType.default_timezone = :local
406
+ # # or
407
+ # OCI8::BindType.default_timezone = :utc
408
+ #
321
409
  class Time
322
410
  if OCI8.oracle_client_version >= ORAVER_9_0
323
- def self.create(con, val, param, max_array_size)
411
+ def self.create(con, val, param, max_array_size) # :nodoc:
324
412
  if true # TODO: check Oracle server version
325
413
  TimeViaOCITimestamp.new(con, val, param, max_array_size)
326
414
  else
@@ -328,7 +416,7 @@ class OCI8
328
416
  end
329
417
  end
330
418
  else
331
- def self.create(con, val, param, max_array_size)
419
+ def self.create(con, val, param, max_array_size) # :nodoc:
332
420
  TimeViaOCIDate.new(con, val, param, max_array_size)
333
421
  end
334
422
  end
@@ -339,20 +427,26 @@ class OCI8
339
427
  # OCI8::BindType::IntervalYM
340
428
  #++
341
429
  #
342
- # This is a helper class to bind ruby's
343
- # Integer[http://www.ruby-doc.org/core/classes/Integer.html]
344
- # object as Oracle's <tt>INTERVAL YEAR TO MONTH</tt> datatype.
430
+ # This is a helper class to select or bind Oracle data type
431
+ # <tt>INTERVAL YEAR TO MONTH</tt>. The retrieved value is
432
+ # the number of months between two timestamps.
433
+ #
434
+ # The value can be applied to \DateTime#>> to shift months.
435
+ # It can be applied to \Time#months_since if activisupport has
436
+ # been loaded.
437
+ #
438
+ # === How to select <tt>INTERVAL YEAR TO MONTH</tt>
345
439
  #
346
- # == Select
440
+ # <tt>INTERVAL YEAR TO MONTH</tt> is selected as an Integer.
347
441
  #
348
- # The fetched value for a <tt>INTERVAL YEAR TO MONTH</tt> column
349
- # is an Integer[http://www.ruby-doc.org/core/classes/Integer.html]
350
- # which means the months between two timestamps.
442
+ # conn.exec("select (current_timestamp - hiredate) year to month from emp") do |hired_months|
443
+ # puts "hired_months = #{hired_months}"
444
+ # end
351
445
  #
352
- # == Bind
446
+ # == How to bind <tt>INTERVAL YEAR TO MONTH</tt>
353
447
  #
354
- # You cannot bind as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
355
- # It must be bound explicitly with :interval_ym.
448
+ # You cannot bind a bind variable as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
449
+ # It must be bound explicitly by OCI8::Cursor#bind_param.
356
450
  #
357
451
  # # output bind variable
358
452
  # cursor = conn.parse(<<-EOS)
@@ -399,51 +493,43 @@ class OCI8
399
493
  # OCI8::BindType::IntervalDS
400
494
  #++
401
495
  #
402
- # This is a helper class to bind ruby's
403
- # Rational[http://www.ruby-doc.org/core/classes/Rational.html]
404
- # object as Oracle's <tt>INTERVAL DAY TO SECOND</tt> datatype.
496
+ # (new in 2.0)
405
497
  #
406
- # == Select
498
+ # This is a helper class to select or bind Oracle data type
499
+ # <tt>INTERVAL DAY TO SECOND</tt>. The retrieved value is
500
+ # the number of seconds between two typestamps as a \Float.
407
501
  #
408
- # The fetched value for a <tt>INTERVAL DAY TO SECOND</tt> column
409
- # is a Rational[http://www.ruby-doc.org/core/classes/Rational.html]
410
- # or an Integer[http://www.ruby-doc.org/core/classes/Integer.html].
411
- # The value is usable to apply to
412
- # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]#+ and
413
- # DateTime[http://www.ruby-doc.org/core/classes/DateTime.html]#-.
502
+ # Note that it is the number days as a \Rational if
503
+ # OCI8::BindType::IntervalDS.unit is :day or the ruby-oci8
504
+ # version is prior to 2.0.3.
414
505
  #
415
- # == Bind
506
+ # == How to bind <tt>INTERVAL DAY TO SECOND</tt>
416
507
  #
417
- # You cannot bind as <tt>INTERVAL YEAR TO MONTH</tt> implicitly.
418
- # It must be bound explicitly with :interval_ds.
508
+ # You cannot bind a bind variable as <tt>INTERVAL DAY TO SECOND</tt>
509
+ # implicitly. It must be bound explicitly by OCI8::Cursor#bind_param.
419
510
  #
420
- # # output
421
- # ts1 = DateTime.parse('1969-11-19 06:54:35 00:00')
422
- # ts2 = DateTime.parse('1969-07-20 20:17:40 00:00')
511
+ # # output bind variable
423
512
  # cursor = conn.parse(<<-EOS)
424
513
  # BEGIN
425
- # :itv := (:ts1 - :ts2) DAY TO SECOND;
514
+ # :interval := (:ts1 - :ts2) DAY TO SECOND(9);
426
515
  # END;
427
516
  # EOS
428
- # cursor.bind_param(:itv, nil, :interval_ds)
429
- # cursor.bind_param(:ts1, ts1)
430
- # cursor.bind_param(:ts2, ts2)
517
+ # cursor.bind_param(:interval, nil, :interval_ds)
518
+ # cursor.bind_param(:ts1, DateTime.parse('1969-11-19 06:54:35 00:00'))
519
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
431
520
  # cursor.exec
432
- # cursor[:itv] # == ts1 - ts2
521
+ # cursor[:interval] # => 10492615.0 seconds
433
522
  # cursor.close
434
523
  #
435
- # # input
436
- # ts2 = DateTime.parse('1969-07-20 20:17:40 00:00')
437
- # itv = 121 + 10.to_r/24 + 36.to_r/(24*60) + 55.to_r/(24*60*60)
438
- # # 121 days, 10 hours, 36 minutes, 55 seconds
524
+ # # input bind variable
439
525
  # cursor = conn.parse(<<-EOS)
440
526
  # BEGIN
441
- # :ts1 := :ts2 + :itv;
527
+ # :ts1 := :ts2 + :interval;
442
528
  # END;
443
529
  # EOS
444
530
  # cursor.bind_param(:ts1, nil, DateTime)
445
- # cursor.bind_param(:ts2, ts2)
446
- # cursor.bind_param(:itv, itv, :interval_ds)
531
+ # cursor.bind_param(:ts2, DateTime.parse('1969-07-20 20:17:40 00:00'))
532
+ # cursor.bind_param(:interval, 10492615.0, :interval_ds)
447
533
  # cursor.exec
448
534
  # cursor[:ts1].strftime('%Y-%m-%d %H:%M:%S') # => 1969-11-19 06:54:35
449
535
  # cursor.close
@@ -453,6 +539,32 @@ class OCI8
453
539
  @@minute = @@hour / 60
454
540
  @@sec = @@minute / 60
455
541
  @@fsec = @@sec / 1000000000
542
+ @@unit = :second
543
+
544
+ # call-seq:
545
+ # OCI8::BindType::IntervalDS.unit -> :second or :day
546
+ #
547
+ # (new in 2.0.3)
548
+ #
549
+ # Retrieves the unit of interval.
550
+ def self.unit
551
+ @@unit
552
+ end
553
+
554
+ # call-seq:
555
+ # OCI8::BindType::IntervalDS.unit = :second or :day
556
+ #
557
+ # (new in 2.0.3)
558
+ #
559
+ # Changes the unit of interval. :second is the default.
560
+ def self.unit=(val)
561
+ case val
562
+ when :second, :day
563
+ @@unit = val
564
+ else
565
+ raise 'unit should be :second or :day'
566
+ end
567
+ end
456
568
 
457
569
  def set(val) # :nodoc:
458
570
  unless val.nil?
@@ -462,10 +574,17 @@ class OCI8
462
574
  else
463
575
  is_minus = false
464
576
  end
465
- day, val = val.divmod 1
466
- hour, val = (val * 24).divmod 1
467
- minute, val = (val * 60).divmod 1
468
- sec, val = (val * 60).divmod 1
577
+ if @@unit == :second
578
+ day, val = val.divmod 86400
579
+ hour, val = val.divmod 3600
580
+ minute, val = val.divmod 60
581
+ sec, val = val.divmod 1
582
+ else
583
+ day, val = val.divmod 1
584
+ hour, val = (val * 24).divmod 1
585
+ minute, val = (val * 60).divmod 1
586
+ sec, val = (val * 60).divmod 1
587
+ end
469
588
  fsec, val = (val * 1000000000).divmod 1
470
589
  if is_minus
471
590
  day = - day
@@ -483,7 +602,12 @@ class OCI8
483
602
  val = super()
484
603
  return nil if val.nil?
485
604
  day, hour, minute, sec, fsec = val
486
- day + (hour * @@hour) + (minute * @@minute) + (sec * @@sec) + (fsec * @@fsec)
605
+ if @@unit == :second
606
+ fsec = fsec / 1000000000.0
607
+ day * 86400 + hour * 3600 + minute * 60 + sec + fsec
608
+ else
609
+ day + (hour * @@hour) + (minute * @@minute) + (sec * @@sec) + (fsec * @@fsec)
610
+ end
487
611
  end
488
612
  end # OCI8::BindType::IntervalDS
489
613
  end