はじめに
Rails 6 に追加された新機能を試す第121段。 今回は、PostgreSQL index_exists? 編です。
Rails 6 (と Rails 5.2.4.1) では、 index_exists? が正しく動作しないバグが fix されています。
Ruby 2.6.5, Rails 6.0.2.1, Rails 5.2.4.1, Rails 5.2.3, PostgreSQL 12.0 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)
$ rails --version
Rails 6.0.2.1
今回は、 name の属性を持つ User モデルを作り、インデックスを追加して確認してみます。
Rails プロジェクトを作る
Rails プロジェクトを新たに作成します。
$ rails new rails_sandbox
$ cd rails_sandbox
User モデルを作る
name 属性を持つ User モデルを作ります。
$ bin/rails g model User name
インデックスを追加する
今回は2つのインデックスを作成します。 lower(name) を指定したインデックスと、単純に name カラムを指定したインデックスです。
bin/rails g migration add_index_lower_name_to_users
class AddIndexLowerNameToUsers < ActiveRecord::Migration[6.0]
  def change
    add_index :users, 'lower(name)', name: 'index_lower_name'
    add_index :users, 'name', name: 'index_name'
  end
end
マイグレーションを実行する
マイグレーションを実行します。
bin/rails db:create db:migrate
rails console で確認する
rails c を実行します。
$ bin/rails c
Running via Spring preloader in process 344
Loading development environment (Rails 6.0.2.1)
indexes で登録されているインデックスを調べてみましょう。
(わかりやすいように表示は折り返してます。)
irb(main):001:0> User.connection.indexes(:users)
=> [
# <ActiveRecord::ConnectionAdapters::IndexDefinition:0x00005617bc6ef580 
@table=:users, 
@name="index_lower_name", 
@unique=false, 
@columns="lower((name)::text)",  # <=  ここに注目
@lengths={}, 
@orders={}, 
@opclasses={}, 
@where=nil, 
@type=nil, 
@using=:btree, 
@comment=nil>, 
# <ActiveRecord::ConnectionAdapters::IndexDefinition:0x00005617bc8c3be0 
@table=:users, 
@name="index_name", 
@unique=false, 
@columns=["name"],  # <= ここに注目
@lengths={}, 
@orders={}, 
@opclasses={}, 
@where=nil, 
@type=nil, 
@using=:btree, 
@comment=nil>
]
index_lower_name に対応する @columns が 'lower((name)::text)' で String であるのに対して、 index_name に対応する @columns が ["name"] とArray になっていることに注意してください。
index_exists? を使って index_name が存在することを確認します。
irb(main):002:0> User.connection.index_exists?(:users, "name", name: :index_name)
=> true
今度は、 index_lower_name が存在することを確認します。
irb(main):003:0> User.connection.index_exists?(:users, "lower((name)::text)", name: :index_lower_name)
=> true
Rails 5 では
Rails 5.2.4.1 では、 Rails 6 と同じ動作ですが、 Rails 5.2.3 では、 index_lower_name の存在を確認したとき false を返します。
irb(main):001:0> User.connection.index_exists?(:users, "name", name: :index_name)
=> true
irb(main):002:0> User.connection.index_exists?(:users, "lower((name)::text)", name: :index_lower_name)
=> false
試したソース