JdbcAdapterの怪
jruby-1.1RC1 + rails2.0.2でのこと。rake testをしてみたらフィクスチャのロードで以下のようなエラーが出ました。あ、ちなみにDBはHSQLDBね。activerecord-jdbchsqldb-adapter (0.7.1)使ってます。
1) Error: test_should_allow_signup(AccountControllerTest): ActiveRecord::StatementInvalid: ActiveRecord::ActiveRecordError: This function is not supported: INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at ) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:05', 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:05') C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:153:in `log' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-jdbc-adapter-0.7.1/lib/active_record/connection_adapters/jdbc_adapter.rb:503:in `execute' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-jdbc-adapter-0.7.1/lib/active_record/connection_adapters/jdbc_adapter.rb:503:in `insert_fixture' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:631:in `insert_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/reflection.rb:55:in `each' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:568:in `insert_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/reflection.rb:55:in `create_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:516:in `each' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:516:in `create_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:516:in `transaction' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:514:in `create_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:516:in `disable_referential_integrity' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:505:in `create_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:516:in `silence' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:504:in `create_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:516:in `load_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:934:in `setup_with_fixtures' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:978:in `full_setup' C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/fixtures.rb:977:in `run'
executeメソッドで例外?This function is not supported: で出てるのは普通のINSERT文。おかしいと思って、script/consoleで確認。User.createは問題ない。しかし以下の怪奇現象が!
?> c = ActiveRecord::Base.connection => #<ActiveRecord::ConnectionAdapters::JdbcAdapter:0x1901572 ・・・> >> c.insert("INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:06' , 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:06')") => 2 >> c.execute("INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:06 ', 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:06')") ActiveRecord::StatementInvalid: ActiveRecord::ActiveRecordError: This function is not supported: INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at ) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:06', 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:06') from C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:150:in `log' from C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:132:in `execute' from C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-jdbc-adapter-0.7.1/lib/active_record/connection_adapters/jdbc_adapter.rb:503:in `signal_status' >>
なんでActiveRecord::Base.connectionに対してinsertはOKでexecuteはだめなのさ!insertは結局execute呼んでるじゃん!
def execute(sql, name = nil) log(sql, name) do _execute(sql,name) end end # we need to do it this way, to allow Rails stupid tests to always work # even if we define a new execute method. Instead of mixing in a new # execute, an _execute should be mixed in. def _execute(sql, name = nil) if JdbcConnection::select?(sql) @connection.execute_query(sql) elsif JdbcConnection::insert?(sql) @connection.execute_insert(sql) else @connection.execute_update(sql) end end def update(sql, name = nil) #:nodoc: execute(sql, name) end def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) id = execute(sql, name = nil) id_value || id end
むきー!_executeの動きが見たいので、以下のように標準出力に出してみる。
module ActiveRecord::ConnectionAdapters class JdbcAdapter def _execute(sql, name = nil) puts "#{self.class} _execute #0" if JdbcConnection::select?(sql) puts "#{self.class} _execute #1" result = @connection.execute_query(sql) puts "#{self.class} _execute #2" result elsif JdbcConnection::insert?(sql) puts "#{self.class} _execute #3" result = @connection.execute_insert(sql) puts "#{self.class} _execute #4" result else puts "#{self.class} _execute #5" result = @connection.execute_update(sql) puts "#{self.class} _execute #6" result end end end end
で、出た答えがこちら。
>> c.insert("INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:06' , 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:06')") ActiveRecord::ConnectionAdapters::JdbcAdapter _execute #0 ActiveRecord::ConnectionAdapters::JdbcAdapter _execute #1 ActiveRecord::ConnectionAdapters::JdbcAdapter _execute #2 => 2 >> c.execute("INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:06 ', 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:06')") ActiveRecord::ConnectionAdapters::JdbcAdapter _execute #0 ActiveRecord::ConnectionAdapters::JdbcAdapter _execute #3 ActiveRecord::StatementInvalid: ActiveRecord::ActiveRecordError: This function is not supported: INSERT INTO users (crypted_password, login, created_at, email, id, salt, updated_at ) VALUES ('00742970dc9e6319f8019fd54864d3ea740f04b1', 'aaron', '2008-01-29 15:07:06', 'aaron@example.com', 2, '7e3041ebc2fc05a40c60028e2c4901a81035d3cd', '2008-01-30 15:07:06') from C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:150:in `log' from C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:132:in `execute' from C:/dev/jruby/jruby-1.1RC1/lib/ruby/gems/1.8/gems/activerecord-jdbc-adapter-0.7.1/lib/active_record/connection_adapters/jdbc_adapter.rb:503:in `signal_status' >>
JdbcConnection::select?(sql)でINSERT文がtrueと判断されている!マジで?っていうか、insertメソッド経由だと JdbcConnection::select?(sql) => true で、executeメソッドからだとJdbcConnection::select?(sql) => falseってこと?ありえなくない?
で、諸悪の根源はselect?メソッドだと思ってgrepしてみたらJdbcAdapterInternalService.javaで宣言してるのね。文字列中にSELECTが含まれているかどうかを判断しようとしているらしいコード(#173〜)を見てみたら何かすごいことしてる・・・。Java側からRubyのオブジェクトに触るのって大変なのね、まだRC1だしバグもあるよね、と及び腰になってしまいました。っていうか、Javaの方もおかしくないと思うんだけどなー。