Sequel + ojdbc1.4 で TIMESTAMP 変換エラー
はじめに
Sequel 3.48.0 で ojdbc14.jar (10.2.0.5) を使って TIMESTAMP 型のカラムを含むテーブルを検索したところ下記のようなエラーが発生しました。 (JRuby 1.7.4 で実行)
ただし、ojdbc5.jar・ojdbc6.jar (11.2.0.3) ではこのようなエラーは発生しません。
Sequel::InvalidValue: ArgumentError: no time information in "oracle.sql.TIMESTAMP@4500a0bc"
実行したのは以下のようなスクリプトです。
require 'rubygems' require 'sequel' require_relative 'lib/ojdbc14.jar' DB = Sequel.connect('jdbc:oracle:thin:user1/pass1@localhost:1521/XE') order = DB[:sample_order] ds = order.where { |o| o.value > 200 } ds.all.each do |r| p r end
これは下記のようなテーブルを SELECT * FROM "SAMPLE_ORDER" WHERE ("VALUE" > 200)
で検索しているだけです。
create table sample_order ( order_no varchar(10) not null, value number(10,0) not null, create_date timestamp default sysdate not null, primary key (order_no) )
原因
Sequel の実装が下記のようになっている事と、ojdbc14.jar における oracle.sql.TIMESTAMP クラスの toString メソッドが "oracle.sql.TIMESTAMP@4500a0bc" のような文字列を返す事が原因です。
lib/sequel/adapters/jdbc/oracle.rb (Sequel のソース)
def convert_type_oracle_timestamp(v) db.to_application_timestamp(v.to_string) end
試しに oracle.sql.TIMESTAMP クラスの toString メソッド (JRuby 上では Java::OracleSql::TIMESTAMP の to_string) 等の結果を出力してみると下記のようになりました。
oracle_timestamp_string.rb
require_relative 'lib/ojdbc14.jar' #require_relative 'lib/ojdbc5.jar' #require_relative 'lib/ojdbc6.jar' date = Java::OracleSql::TIMESTAMP.new('2013-06-07 13:20:30') puts date.to_string puts date.to_jdbc.to_string puts date.timestamp_value.to_string puts date.string_value
実行結果1 (ojdbc14.jar の場合)
> jruby oracle_timestamp_string.rb oracle.sql.TIMESTAMP@c3e122 2013-06-07 13:20:30.0 2013-06-07 13:20:30.0 2013-6-7 13.20.30.0
実行結果2 (ojdbc5.jar、ojdbc6.jar の場合)
> jruby oracle_timestamp_string.rb 2013-06-07 13:20:30.0 2013-06-07 13:20:30.0 2013-06-07 13:20:30.0 2013-06-07 13:20:30.0
という事で、ojdbc5.jar や ojdbc6.jar を使うようにした方が良さそうです。
回避方法
とりあえず、ojdbc5.jar や ojdbc6.jar を使うのが抜本的な対策ですが。 ojdbc14.jar を使わなければならない場合は convert_type_oracle_timestamp をオープンクラスで変更すればよいと思います。(oracle.sql.TIMESTAMP の toString を変更するのもあり)
search_order.rb
require 'rubygems' require 'sequel' # convert_type_oracle_timestamp を変更するため下記 2行の require が必要 require 'sequel/adapters/jdbc' require 'sequel/adapters/jdbc/oracle' # Oracle JDBC ドライバーの require require_relative 'lib/ojdbc14.jar' class Sequel::JDBC::Oracle::Dataset # convert_type_oracle_timestamp の変更 def convert_type_oracle_timestamp(v) db.to_application_timestamp(v.to_jdbc.to_string) # 以下でも可 # db.to_application_timestamp(v.timestamp_value.to_string) end end DB = Sequel.connect('jdbc:oracle:thin:user1/pass1@localhost:1521/XE') order = DB[:sample_order] ds = order.where { |o| o.value > 200 } ds.all.each do |r| p r end
実行結果は以下のようになり、正常に処理できている事を確認できました。
実行結果
> bundle exec jruby search_order.rb {:order_no=>"A2", :value=>300, :create_date=>2013-06-07 16:42:00 +0900} {:order_no=>"A3", :value=>600, :create_date=>2013-06-07 16:45:00 +0900}
今回使ったソースは http://github.com/fits/try_samples/tree/master/blog/20130623/