22
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-09-17

動機

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 には無いファイル

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

22
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?