ruby-oci8 2.0.2 → 2.0.3

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