Skip to content

Instantly share code, notes, and snippets.

@konk303
Created July 2, 2013 04:51
Show Gist options
  • Save konk303/5906838 to your computer and use it in GitHub Desktop.
Save konk303/5906838 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseStatements
# == rake db:fixture:load のmonkey patch
# 既存のfixture yamlに下記の問題がある
# # 型的に不正な初期値を使用してる箇所がある (sql_mode TRADITIONAL では落ちる)
# # NOT NULL制約のあるカラムに値をセットしていない時がある (sql_mode TRADITIONAL では落ちる)
# # 日付・時間カラムに、システム最小日付(lws_time)が入らない
# ので、ActiveRecord::ConnectionAdapters::DatabaseStatements.insert_fixture
# を書き換え
# === 詳細
# `conncection.executeで INSERT INTO を発行`
# ↓
# modelをfactory.build (factoryがなければModel.new)して値代入
# 最小日付セット後にattributesで INSERT INTO
# === FIXME
# 1レコードずつAR(更にFactoryGirl)をインスタンス化するので、遅い
# === original
# # Inserts the given fixture into the table. Overridden in adapters that require
# # something beyond a simple insert (eg. Oracle).
# def insert_fixture(fixture, table_name)
# columns = schema_cache.columns_hash(table_name)
# key_list = []
# value_list = fixture.map do |name, value|
# key_list << quote_column_name(name)
# quote(value, columns[name])
# end
# execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
# end
def insert_fixture_with_model_instance(fixture, table_name)
puts "insert into #{table_name} id:#{fixture["id"]}"
instance = begin
FactoryGirl.build(table_name.singularize)
rescue
if table_name == 'm_origin_countries'
MOriginCountries.new
else
table_name.classify.constantize.new
end
end
fixture.each do |k, v|
instance.__send__("#{k}=", v)
end
instance.__send__(:set_zero_date)
transaction do
# == insert_fixtureをtransactionで囲む
# autocommit=0なmysql上で、establish_connectionを別実行してるmodelの
# fixtureが入らなかった。
# 原因はActiveRecord::Baseのtransaction上で実行されているので
# model独自connectionにCOMMITが発行されていなかったから。
# ActiveRecord::Fixtures.create_fixturesに修正を入れるべきだが、methodが大きく
# 書き換えずらいので、とりあえずの対処として1レコードごとのexecuteをtransactionで囲む。
insert_fixture_without_model_instance(instance.attributes, table_name)
end
end
alias_method_chain :insert_fixture, :model_instance
end
end
end
# -*- coding: utf-8 -*-
module ActiveRecord
class Migrator
# == rake db:migrateのmonkey patch
# autocommit=0なmysql上で、rake db:migrateに問題がある
# -> 最後に実行されたmigrationがschema_migrationsに記録されない。
# (migration自体は実行されるが、記録が残らない)
# 次回実行時に、前回の最後のmigrationを実行しようとして破綻する。
#
# 原因:
# mysqlはddl_transaction非対応のため、
# ActiveRecord::Base.connection.supports_ddl_transactions? はfalseにセットされ、
# migrationはBEGIN/COMMIT抜きで実行される。
# schemaの変更は自動的にコミットされるのでそれはOKなのだが、
# schema_migrationへのupdate文もブロック内にあるため、
# autocommit=0の環境では最後のinsertがcommitされない
# http://dev.mysql.com/doc/refman/5.1/ja/implicit-commit.html
# http://stackoverflow.com/questions/15146414/rails-mysql-autocommit-and-schema-migrations-table-bug
#
# 対策:
# ddl_transactionの部分はいじらず、
# schema_migration変更部分をtransactionで囲む
# - ddl_transactionでないmigrationではtransactionが2重になるが、
# activerecordが片方しか発行しないので問題ない
private
def record_version_state_after_migrating_with_transaction(version)
Base.transaction do
record_version_state_after_migrating_without_transaction(version)
end
end
alias_method_chain :record_version_state_after_migrating, :transaction
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment