Rails2.3をMySQL4.xで使うときのRELEASE SAVEPOINTの問題

MySQL4系ではRELEASE SAVEPOINT をサポートしていないので、Rails2.3がMySQL4.xに向けて、RELEASE SAVEPOINTを使ってもエラーになるという話です。

実験環境

Rails 2.3.11
MySQL 4.1.22

エラーの再現

User.connection.transaction do
  User.connection.transaction(:requires_new => true) do  # CREATE SAVEPOINT active_record_1
    User.find(:all)
  end
end  # RELEASE SAVEPOINT active_record_1 

ActiveRecord::StatementInvalid: Mysql::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'RELEASE SAVEPOINT active_record_1' at line 1: RELEASE SAVEPOINT active_record_1
        from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.3.11/lib/active_record/connection_adapters/abstract_adapter.rb:227:in `log'
        from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.3.11/lib/active_record/connection_adapters/mysql_adapter.rb:324:in `execute'
        from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.3.11/lib/active_record/connection_adapters/mysql_adapter.rb:370:in `release_savepoint'
        from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.3.11/lib/active_record/connection_adapters/abstract/database_statements.rb:161:in `transaction'
        from (irb):87
        from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.3.11/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction'
        from (irb):86

ActiveRecordのコード

activerecord-2.3.11/lib/active_record/connection_adapters/abstract/mysql_adapter.rb

369       def release_savepoint
370         execute("RELEASE SAVEPOINT #{current_savepoint_name}")                                                                                                                                                 
371       end

activerecord-2.3.11/lib/active_record/connection_adapters/abstract/database_statements.rb

153         if outside_transaction?
154           @open_transactions = 0
155         elsif transaction_open
156           decrement_open_transactions
157           begin
158             if open_transactions == 0
159               commit_db_transaction
160             else
161               release_savepoint                                                                                                                                                                               
162             end
163           rescue Exception => database_transaction_rollback
164             if open_transactions == 0
165               rollback_db_transaction
166             else
167               rollback_to_savepoint
168             end
169             raise
170           end
171         end

RELEASE SAVEPOINT を execute で実行していることが分かります。

対応

使えないものは仕方ないので、こちらのように、RELEASE SAVEPOINT を発行しないようにしました。下記の内容を config/initializers/unset_release_savepoint_method.rb に記述しました。

ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
  # Unset release savepoint method 
  def release_savepoint
  end
end 

エラーが再現しないことを確認

User.connection.transaction do
  User.connection.transaction(:requires_new => true) do  # CREATE SAVEPOINT active_record_1
    User.find(:all)
  end
end
=> [#User id・・・・・・・]

もう、MySQL5を使えと言うことですね・・・。