Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
12
Help us understand the problem. What is going on with this article?
@tkm_kj

rake db:migrate:statusのコードを追いかける

More than 3 years have passed since last update.

動機

Railsプロジェクトで複数のfeatureブランチで並行して開発していてかつ共にマイグレーションが絡む処理をしている時に、ブランチを横断して作業している時に、 db:migrate:status でマイグレーションの状況を見てみると

 up     20160913044702  ********** NO FILE **********

のような履歴が出来ている時がありました。ふと気になって、どういう時にこのような履歴が出来るのか実際にコードを追いかけてみました。

バージョン

activerecord-5.0.0

コードリーディング

lib/active_record/railties/databases.rake:109

rake -W を使うと、そのタスクがどこに記述されているかの一覧を見ることが出来ます。便利ですね。
こちらが rake db:migrate:status を走らせたときに通るコードです。

    task :status => [:environment, :load_config] do
      unless ActiveRecord::SchemaMigration.table_exists?
        abort 'Schema migrations table does not exist yet.'
      end
      db_list = ActiveRecord::SchemaMigration.normalized_versions

      file_list =
          ActiveRecord::Tasks::DatabaseTasks.migrations_paths.flat_map do |path|
            Dir.foreach(path).map do |file|
              next unless ActiveRecord::Migrator.match_to_migration_filename?(file)

              version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file)
              version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
              status = db_list.delete(version) ? 'up' : 'down'
              [status, version, (name + scope).humanize]
            end.compact
          end

      db_list.map! do |version|
        ['up', version, '********** NO FILE **********']
      end
      # output
      puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
      puts "#{'Status'.center(8)}  #{'Migration ID'.ljust(14)}  Migration Name"
      puts "-" * 50
      (db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
        puts "#{status.center(8)}  #{version.ljust(14)}  #{name}"
      end
      puts
    end

db_list = ActiveRecord::SchemaMigration.normalized_versions でschema_migrationsテーブルのversionカラムの一覧を配列で取得します。
schema_migrationsテーブルはマイグレートされたバージョンを保持するテーブルです。
つまり、db_listには現在のマイグレート済ファイルのバージョン一覧が入っていることになります。

次のfile_listには db/migrations のマイグレーションファイル名一覧と先程取得したdb_listを元に [ステータス('up' or 'down', バージョン), マイグレーション名] の配列が作られます。
ここの処理で興味深いところは、 status = db_list.delete(version) ? 'up' : 'down' です。
そのマイグレーションファイル名のバージョンがschema_migrationsのversionカラムに含まれていればdb_listから削除できる -> up(マイグレーション済)。含まれていれば削除できないので down(まだマイグレーションされていない) になります。
読み進めていきましょう。

db_list.map! do |version|
  ['up', version, '********** NO FILE **********']
end

気になっていた箇所に到達しました!
どうやら、db_listに残っていたものが NO FILE になるようですね。
確かに、migrationファイルが無いのでdb_listに残るかつschema_migrationsテーブルにはあるのでupになっていると。納得です。

最後は表示のところですが、file_listと先程のNO FILEのものが入っているdb_listを結合してバージョン順に並べて表示してます。

まとめ

結論として、 NO FILE が出来る条件が

  • schema_migrationsテーブルのversionカラムにあって
  • db/migrations には無いファイル

ということがコードベースで理解できました。

12
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
tkm_kj

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
12
Help us understand the problem. What is going on with this article?