Forums | Admin

Discussion Forums: help

Start New Thread Start New Thread

 

By: Kubo Takehiro
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2012-01-29 00:00
> Will keep an eye out for that patch.

Sorry, I have not done it yet as a official release.
But it works if you add the following code:

OCI8::BindType::Mapping[:date] = OCI8::BindType::LocalTime
OCI8::BindType::Mapping[:timestamp] = OCI8::BindType::LocalTime

The reason I didn't add it to ruby-oci8 2.1.0's new featuers is just one; I have not written its test cases. But it will works well.

By: Wiehann Matthysen
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2011-06-08 12:14
Thanks Kubo,

Will keep an eye out for that patch.

Regards

By: Kubo Takehiro
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2011-06-08 03:58
I'll change ruby-oci8 not to use TIMESTAMP WITH TIMEZONE type for DATE, TIMESTAMP and TIMESTAMP WITH LOCAL TIMEZONE columns.
It will be ruby-oci8 2.1.

The future plan is:
- Release 2.0.5
- Drop Oracle 8i support to make ruby-oci8 simple.
- Change ruby-oci8 to use TIMESTAMP type for DATE, TIMESTAMP and TIMESTAMP WITH LOCAL TIMEZONE columns.
- Release 2.1.0

After that, you can fetch TIMESTAMP columns, excluding TIMESTAMP WITH TIMEZONE, from Oracle even though client and server timezone versions are different.

By: Kubo Takehiro
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2011-06-05 22:34
The error code ORA-01805 provides a hint.
You need to use the same time zone file version for server and client.

In http://download.oracle.com/docs/cd/E14072_01/server.112/e10880/e1500.htm#ORA-01805

> ORA-01805: possible error in date/time operation
> Cause: The timezone files on client and server do not match. Operation can potentially result in incorrect results based on local timezone file.
> Action: Please ensure client and server timezone versions are same.

In http://download.oracle.com/docs/cd/E11882_01/appdev.112/e10646/oci10new.htm#CJADCGBB

> The OCI Datetime and Interval APIs listed here unconditionally raise an error when the input parameters are of TSTZ(TIMESTAMP WITH TIMEZONE) type.

By: Wiehann Matthysen
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2011-05-27 10:53
Hi Kubo,

I'm having the exact same problem as described by Brian. I downloaded the ruby-oci8-2.0.4.tar.gz file, extracted it, applied your timeoffset_patch.dif patch. I then ran the following script:

********************************
gem 'ruby-oci8', '= 2.0.4'
require 'oci8'

conn = OCI8.new('hr', 'hr', '//localhost/orcl')
puts "Client Version: #{OCI8.oracle_client_version}"
puts "Server Version: #{conn.oracle_server_version}"
puts "ENV['TZ'] : #{ENV['TZ']}"

conn.exec 'drop table test_date purge' rescue nil
conn.exec 'create table test_date (dt date)'
conn.exec 'insert into test_date values (sysdate)'
conn.commit

print 'before alter session: '
p conn.select_one 'select * from test_date' rescue p $!

conn.exec "alter session set time_zone = 'US/Eastern'"

puts 'after alter session: '
p conn.select_one 'select * from test_date' rescue p $!
p conn.select_one("select to_timestamp_tz('2010-01-02 03:04:05 06:07', 'YYYY-MM-DD HH24:MI:SS TZH:TZM') from dual") rescue p $!
p conn.select_one("select to_timestamp('2010-01-02 03:04:05', 'YYYY-MM-DD HH24:MI:SS') from dual") rescue p $!
p conn.select_one("select to_date('2010-01-02 03:04:05', 'YYYY-MM-DD HH24:MI:SS') from dual") rescue p $!

conn.logoff
********************************

The output that I'm getting is:

Client Version: 11.2.0.2.0
Server Version: 11.2.0.1.0
ENV['TZ'] :
before alter session: [2011-05-27 12:38:03 +0200]
after alter session:
#<OCIError: ORA-01805: possible error in date/time operation>
[2010-01-02 03:04:05 +0607]
#<OCIError: ORA-01805: possible error in date/time operation>
#<OCIError: ORA-01805: possible error in date/time operation>

I also noticed, when running tests in ruby-plsql that it points to: ocidatetime.c:126:in oci8lib_191.so as the source of the error.

oci_lc(OCIDateTimeGetDate(oci8_envhp, oci8_errhp, dttm, &year, &month, &day));
oci_lc(OCIDateTimeGetTime(oci8_envhp, oci8_errhp, dttm, &hour, &minute, &sec, &fsec));
rv = OCIDateTimeGetTimeZoneOffset(hndl, oci8_errhp, dttm, &tz_hour, &tz_minute);
if (rv != OCI_SUCCESS) {
/* Get time zone of the specified handle. */
OCIDateTime *sys_date;
oci_lc(OCIDescriptorAlloc(oci8_envhp, (dvoid*)&sys_date, OCI_DTYPE_TIMESTAMP_TZ, 0, 0));
=======>oci_lc(OCIDateTimeSysTimeStamp(hndl, oci8_errhp, sys_date));
rv = OCIDateTimeGetTimeZoneOffset(hndl, oci8_errhp, sys_date, &tz_hour, &tz_minute);
OCIDescriptorFree(sys_date, OCI_DTYPE_TIMESTAMP_TZ);
}
have_tz = (rv == OCI_SUCCESS);


From the ruby code I wrapped the datetime_to_array function located in datetime.rb and I don't think the tz_hour = nil, tz_min = nil is the bug as Brian described though. All output produced by this function is correct in the sense that tz_hour and tz_min is not null. I think the source of the problem is the oci_make_ocitimestamp function in ext/oci8/ocidatetime.c.

Do you have any suggestions on how I might fix this?

By: Kubo Takehiro
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2010-12-21 13:44
> I can't easily change that C file and re-compile unfortunately, as I don't have control over the server.

Could you use gcc on the server?
If so, you can compile ruby-oci8 and use it without installation as follows:

tar xvfz ruby-oci8-2.0.4.tar.gz
cd ruby-oci8-2.0.4
make
irb -Iext/oci8 -Ilib -roci8

The key point is "-Iext/oci8 and -Ilib."

Could you try the patch in the following URL?
https://gist.github.com/749935

cd ruby-oci8-2.0.4
patch -p0 < timeoffset_patch.dif
make

Then check the following SQL statements before and after "alter session set time_zone = ..." in irb.

conn.select_one("select to_timestamp_tz('2010-01-02 03:04:05 06:07', 'YYYY-MM-DD HH24:MI:SS TZH:TZM') from dual")
conn.select_one("select to_timestamp('2010-01-02 03:04:05', 'YYYY-MM-DD HH24:MI:SS') from dual")
conn.select_one("select to_date('2010-01-02 03:04:05', 'YYYY-MM-DD HH24:MI:SS') from dual")

By: Brian Ploetz
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2010-12-20 14:00
Hi Kubo,

The script fails for me with the same error I was seeing in my app. Here's the result:


$ gem list

*** LOCAL GEMS ***

bundler (1.0.7)
rake (0.8.7)
ruby-oci8 (2.0.4)

$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]

$ irb
ruby-1.9.2-p0 > gem 'ruby-oci8'
=> true
ruby-1.9.2-p0 > require 'oci8'
=> true
ruby-1.9.2-p0 > conn = OCI8.new "XXX", "XXX", "XXX"
=> #<OCI8:XXX>
ruby-1.9.2-p0 > puts "Client Version: #{OCI8.oracle_client_version}"
Client Version: 11.2.0.2.0
=> nil
ruby-1.9.2-p0 > puts "Server Version: #{conn.oracle_server_version}"
Server Version: 11.2.0.1.0
=> nil
ruby-1.9.2-p0 > puts "ENV['TZ'] : #{ENV['TZ']}"
ENV['TZ'] :
=> nil
ruby-1.9.2-p0 > conn.exec 'drop table test_date purge' rescue nil
=> nil
ruby-1.9.2-p0 > conn.exec 'create table test_date (dt date)'
=> 0
ruby-1.9.2-p0 > conn.exec 'insert into test_date values (sysdate)'
=> 1
ruby-1.9.2-p0 > conn.commit
=> #<OCI8:XXX>
ruby-1.9.2-p0 > print 'before alter session: '
before alter session: => nil
ruby-1.9.2-p0 > p conn.select_one 'select * from test_date' rescue p $!
[2010-12-20 07:47:47 -0600]
=> [2010-12-20 07:47:47 -0600]
ruby-1.9.2-p0 > conn.exec "alter session set time_zone = 'US/Eastern'"
=> 0
ruby-1.9.2-p0 > print 'after alter session: '
after alter session: => nil
ruby-1.9.2-p0 > p conn.select_one 'select * from test_date' rescue p $!
#<NoMethodError: undefined method `*' for nil:NilClass>
=> #<NoMethodError: undefined method `*' for nil:NilClass>
ruby-1.9.2-p0 > conn.logoff
=> true


I can't easily change that C file and re-compile unfortunately, as I don't have control over the server. I'll see if I can get our sysadmin to do that.....

By: Kubo Takehiro
RE: Exception with DATE columns in Oracle 11g [ reply ]  
2010-12-19 05:27
1. Does the following script work for you? It works fine for me.

-----------------------
gem 'ruby-oci8'
require 'oci8'

conn = OCI8.new 'ruby/oci8' # Change This Line!
puts "Client Version: #{OCI8.oracle_client_version}"
puts "Server Version: #{conn.oracle_server_version}"
puts "ENV['TZ'] : #{ENV['TZ']}"

conn.exec 'drop table test_date purge' rescue nil
conn.exec 'create table test_date (dt date)'
conn.exec 'insert into test_date values (sysdate)'
conn.commit

print 'before alter session: '
p conn.select_one 'select * from test_date' rescue p $!

conn.exec "alter session set time_zone = 'US/Eastern'"

print 'after alter session: '
p conn.select_one 'select * from test_date' rescue p $!

conn.logoff
-----------------------

2. Could you change ext/ocidatetime.c near line 122 as follows and test again? I want to know the error message of OCIDateTimeGetTimeZoneOffset.

-----------------------
rv = OCIDateTimeGetTimeZoneOffset(oci8_envhp, oci8_errhp, dttm, &tz_hour, &tz_minute);
if (rv != OCI_SUCCESS) oci8_raise(oci8_errhp, rv, NULL); /* ADD THIS LINE. */
have_tz = (rv == OCI_SUCCESS);
-----------------------

By: Brian Ploetz
Exception with DATE columns in Oracle 11g [ reply ]  
2010-12-14 23:39
Hi,

I first reported this over at the Oracle Enhanced Adapter for ActiveRecord forum (http://groups.google.com/group/oracle-enhanced/browse_thread/thread/cf63b8079dd02926) but after some debugging we believe this is a bug in the ruby-oci8 driver.

Environment:
Ruby 1.9.2
Rails 3.0.3
Oracle Enhanced Adapter 1.3.1
Ruby OCI 8 2.0.4
Oracle 11g (with full 11.2 client)
Ubuntu 10.04

I'm getting the following error when running a simple select on a table which has columns defined as DATEs:

Service Load (1.6ms) SELECT "SW_SERVICE".* FROM "SW_SERVICE" WHERE
(namespace = 'SW') AND ROWNUM <= 1
NoMethodError: undefined method `*' for nil:NilClass: SELECT
"SW_SERVICE".* FROM "SW_SERVICE" WHERE (namespace = 'SW') AND ROWNUM
<= 1
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract_adapter.rb:202:in `rescue
in log'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract_adapter.rb:194:in `log'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_adapter.rb:948:in `log'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_adapter.rb:906:in `select'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/database_statements.rb:7:in
`select_all'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/query_cache.rb:54:in `block
in select_all'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/query_cache.rb:68:in
`cache_sql'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/query_cache.rb:54:in
`select_all'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/base.rb:467:in `find_by_sql'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/relation.rb:64:in `to_a'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/relation/finder_methods.rb:333:in `find_first'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/relation/finder_methods.rb:122:in `first'
/app/sw-api/releases/20101213215753/app/controllers/
users_controller.rb:35:in `create'


The root cause is being swallowed by the Oracle enhanced ActiveRecord adapter, so by adding a line to see the root cause Exception, we get this:

ROOT CAUSE:
NoMethodError: undefined method `*' for nil:NilClass
/app/sw-api/shared/bundle/ruby/1.9.1/gems/ruby-oci8-2.0.4/lib/oci8/
datetime.rb:193:in `ocitimestamp_to_time'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/ruby-oci8-2.0.4/lib/oci8/
datetime.rb:268:in `get'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_oci_connection.rb:110:in `fetch'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_oci_connection.rb:110:in `select'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_adapter.rb:907:in `block in select'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract_adapter.rb:197:in `block in
log'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activesupport-3.0.3/lib/
active_support/notifications/instrumenter.rb:21:in `instrument'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract_adapter.rb:195:in `log'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_adapter.rb:948:in `log'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-oracle_enhanced-
adapter-1.3.1/lib/active_record/connection_adapters/
oracle_enhanced_adapter.rb:906:in `select'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/database_statements.rb:7:in
`select_all'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/query_cache.rb:54:in `block
in select_all'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/query_cache.rb:68:in
`cache_sql'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/connection_adapters/abstract/query_cache.rb:54:in
`select_all'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/base.rb:467:in `find_by_sql'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/relation.rb:64:in `to_a'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/relation/finder_methods.rb:333:in `find_first'
/app/sw-api/shared/bundle/ruby/1.9.1/gems/activerecord-3.0.3/lib/
active_record/relation/finder_methods.rb:122:in `first'
/app/sw-api/releases/20101214143730/app/controllers/
users_controller.rb:35:in `create'


If we take the Oracle enhanced ActiveRecord adapter out of the picture and use the ruby-oci8 driver directly in irb (mimicking what the Oracle enhanced ActiveRecord adapter is doing), we're able to re-produce this:

deploy@web1:~$ irb
ruby-1.9.2-p0 > require "rubygems"
=> true
ruby-1.9.2-p0 > gem "ruby-oci8"
=> true
ruby-1.9.2-p0 > require "oci8"
=> true
ruby-1.9.2-p0 > ENV['TZ'] = 'US/Eastern'
=> "US/Eastern"
ruby-1.9.2-p0 > conn = OCI8.new "XXXX", "XXXX", "XXXX"
=> #<OCI8:XXXX>
ruby-1.9.2-p0 > conn.exec "alter session set time_zone =
'#{ENV['TZ']}'"
=> 0
ruby-1.9.2-p0 > cursor = conn.exec "SELECT \"SW_SERVICE\".* FROM
\"SW_SERVICE\" WHERE
ruby-1.9.2-p0"> (namespace = 'SW') AND ROWNUM <= 1"
=> #<OCI8::Cursor:0x00000001836e08>
ruby-1.9.2-p0 > cursor.fetch
NoMethodError: undefined method `*' for nil:NilClass
from /usr/local/rvm/gems/ruby-1.9.2-p0/gems/ruby-oci8-2.0.4/lib/oci8/
datetime.rb:193:in `ocitimestamp_to_time'
from /usr/local/rvm/gems/ruby-1.9.2-p0/gems/ruby-oci8-2.0.4/lib/oci8/
datetime.rb:268:in `get'
from (irb):9:in `fetch'
from (irb):9
from /usr/local/rvm/rubies/ruby-1.9.2-p0/bin/irb:16:in `<main>'


Removing the "alter session set time_zone = '#{ENV['TZ']}'" eliminates the exception:

deploy@web1:~$ irb
ruby-1.9.2-p0 > require "rubygems"
=> true
ruby-1.9.2-p0 > gem "ruby-oci8"
=> true
ruby-1.9.2-p0 > require "oci8"
=> true
ruby-1.9.2-p0 > conn = OCI8.new "XXXX", "XXXX", "XXXX"
=> #<OCI8:XXXX>
ruby-1.9.2-p0 > cursor = conn.exec "SELECT \"SW_SERVICE\".* FROM
\"SW_SERVICE\" WHERE
ruby-1.9.2-p0"> (namespace = 'SW') AND ROWNUM <= 1"
=> #<OCI8::Cursor:0x00000001eabb80>
ruby-1.9.2-p0 > cursor.fetch
=> [3, "/sw/", "SW", "Y", 2004-11-14 00:00:00 -0600, 2025-12-31
00:00:00 -0600]



Here's the code in ruby-oci8-2.0.4/lib/oci8/datetime.rb it's failing on:

if @@time_new_accepts_timezone

# after ruby 1.9.2
def ocitimestamp_to_time(ary)
return nil if ary.nil?

year, month, day, hour, minute, sec, fsec, tz_hour, tz_min
= ary

sec += fsec / Rational(1000000000)
HERE ---> utc_offset = tz_hour * 3600 + tz_min * 60
return ::Time.new(year, month, day, hour, minute, sec,
utc_offset)
end

else

Given that the error is "undefined method `*' for nil:NilClass", it looks like that tz_hour object is the one that's nil. The columns in question are defined as DATE:

START_DATE NOT NULL DATE
END_DATE DATE

Note this snippet from datetime_to_array() where it's setting tz_hour and tz_min to nil:

# time zone
if val.respond_to? :offset
# DateTime
tz_min = (val.offset * 1440).to_i
elsif val.respond_to? :utc_offset
# Time
tz_min = val.utc_offset / 60
else
tz_hour = nil
tz_min = nil
end