RailsでPostgreSQLを使う際にgen_random_uuid関数を有効化したい

こんにちは!kossyです!




今回はRailsPostgreSQLを使う際にgen_random_uuid関数を有効化する方法について、

備忘録としてブログに残してみたいと思います。




環境

Ruby 2.6.9
Rails 6.0.4
PostgreSQL 13系



マイグレーションファイルの作成

gen_random_uuid関数を有効化するために、マイグレーションファイルを作成し、以下のように変更します。

class EnableUuidExtension < ActiveRecord::Migration[6.0]
  def change
    enable_extension 'pgcrypto'
  end
end

その後migrateを実行。

$ rails db:migrate

== 20211216142135 EnableUuidExtension: migrating ==============================
-- enable_extension("pgcrypto")
   -> 0.0554s
== 20211216142135 EnableUuidExtension: migrated (0.0556s) =====================

これでマイグレーションファイル内でgen_random_uuid関数が使えるようになります。

テーブルの作成

試しにテーブルを作成して、uuid型のデフォルト値にgen_random_uuid関数を指定してみます。

class CreateTenants < ActiveRecord::Migration[6.0]
  def change
    create_table :tenants do |t|
      t.string :name, null: false
      t.uuid :uuid, null: false, default: 'gen_random_uuid()'

      t.timestamps
    end
  end
end

migrateコマンドを実行した後、schema.rbを確認してみます。

  create_table "tenants", force: :cascade do |t|
    t.string "name", null: false
    t.uuid "uuid", default: -> { "gen_random_uuid()" }, null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

default値にgen_random_uuid関数が指定できているようです。


動作確認

試しにtenantsテーブルにレコードを新規作成してみます。

$ Tenant.create!(name: 'その辺にいるWebエンジニア株式会社')
   (0.5ms)  BEGIN
  Tenant Exists? (4.5ms)  SELECT 1 AS one FROM "tenants" WHERE "tenants"."id" = $1 LIMIT $2  [["id", nil], ["LIMIT", 1]]
  Tenant Create (3.7ms)  INSERT INTO "tenants" ("name", "uuid", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "その辺にいるWebエンジニア株式会社"], ["uuid", "b5188d11-d9a3-423b-be17-dae29800977e"], ["created_at", "2021-12-16 13:16:51.489288"], ["updated_at", "2021-12-16 13:16:51.489288"]]
   (3.8ms)  COMMIT

uuidにデフォルト値が挿入されていることがわかります。

念のためきちんとランダムな値になっているかどうかも確認してみます。

$ Tenant.create!(name: 'ランダム株式会社')
   (0.8ms)  BEGIN
  Tenant Exists? (7.4ms)  SELECT 1 AS one FROM "tenants" WHERE "tenants"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Tenant Exists? (4.4ms)  SELECT 1 AS one FROM "tenants" WHERE "tenants"."id" = $1 LIMIT $2  [["id", nil], ["LIMIT", 1]]
  Tenant Create (0.9ms)  INSERT INTO "tenants" ("name", "uuid", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "ランダム株式会社"], ["uuid", "a46244c4-8a3a-4b48-8236-1e76b475b4da"], ["created_at", "2021-12-16 13:19:26.898640"], ["updated_at", "2021-12-16 13:19:26.898640"]]
   (1.4ms)  COMMIT

先ほどと異なるuuidが生成されているので、ランダムな値になっているようです。


uuid_generate_v4じゃダメなの?

uuid_generate_v4という関数もあるんですが、PostgreSQLのドキュメントを見たところ、

注意: ランダムに生成された(バージョン4)UUIDのみが必要な場合には、代わりにpgcryptoモジュールのgen_random_uuid()を利用すること検討してください。

出典: www.postgresql.jp

という記述があったため、ランダムに生成された(バージョン4)UUIDのみが必要な場合にはpgcryptoモジュールを有効化するのがデファクトスタンダートのようです。


まとめ

Railsだとfriendly_id Gemを用いる際にuuidを設定することがあると思いますが、その場合にはpgcryptoモジュールを有効化するのを忘れないようにしましょう。(私は10分ハマりました)