🐰

PostgreSQL の Function を rails migrate で管理する

2024/03/14に公開

ActiveRecord 周りで解決できなかった課題点を PostgreSQL での Function 作成により無理なく解決までたどり着けそうなのですが、DB サーバーに入って直接 CREATE FUNCTION するやり方は別途手順書や .sql の保管場所が必要になり、管理コストが上がりそうなことから、migration に含めることにしましたので、そのメモです。

まとめ

  • ActiveRecord::Base.connection.execute(sql) で生の SQL を実行することができる
  • 他にもselect_all, find_by_sql メソッドで生の SQL を実行することができる

ActiveRecord::Base.connection.execute(sql) で生の SQL を実行することができる

Rails から PostgreSQL の Function を作成するためには、生の SQL で CREATE FUNCTION を実行する必要があります。

CREATE FUNCTION の例
こちらの記事で作成した Funciton を例とします。

CREATE OR REPLACE FUNCTION try_cast_json(json_text text)
  RETURNS json AS
$$
BEGIN
  BEGIN
    RETURN json_text::json;
  EXCEPTION WHEN OTHERS THEN
    RETURN null;
  END;
END;
$$
LANGUAGE plpgsql;

Rails で生 SQL を実行できることを確認する

SQL は execute メソッドで実行することができます。


sql = <<-EOS
    CREATE OR REPLACE FUNCTION try_cast_json(json_text text)
      RETURNS json AS
    $$
    BEGIN
      BEGIN
        RETURN json_text::json;
      EXCEPTION WHEN OTHERS THEN
        RETURN null;
      END;
    END;
    $$
    LANGUAGE plpgsql;
    EOS
ActiveRecord::Base.connection.execute(sql)

https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-execute

migration に含める

生の SQL を ActiveRecord で実行できることが分かったので、migration に含めます。

rails g migration CreateFunctionTryCastJson

20241231053446_create_function_try_cast_json.rb が作成されます。

class CreateFunctionTryCastJson < ActiveRecord::Migration[7.1]
  def up
    sql = <<-EOS
    CREATE OR REPLACE FUNCTION try_cast_json(json_text text)
      RETURNS json AS
    $$
    BEGIN
      BEGIN
        RETURN json_text::json;
      EXCEPTION WHEN OTHERS THEN
        RETURN null;
      END;
    END;
    $$
    LANGUAGE plpgsql;
    EOS
    ActiveRecord::Base.connection.execute(sql)
  end

  def down
    sql = """
    DROP FUNCTION try_cast_json;
    """
    ActiveRecord::Base.connection.execute(sql)
  end
end

これで、migrate コマンドを実行して、DB に Function が作成されているかどうかを確認できれば OK です。

rails db:migrate 

他にもselect_all, find_by_sql メソッドで生の SQL を実行することができる

ちなみにですが、他にも Rails で生の SQL を実行することができます。

select_all メソッドは SELECT した結果を ActiveRecord::Result 型で取得することができます。
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_all

find_by_sql は結果を元のモデルの配列を返します。
https://api.rubyonrails.org/classes/ActiveRecord/Querying.html#method-i-find_by_sql

他にもいろいろとありましたが、基本的に execute と合わせてこの 3 つを使っていけばあまり困らなさそうです。
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html

さいごに

今回は、PostgreSQL で使用する Function の CREATE 文を migrate ファイルから実行するサンプルを作成しました。
どっちみち管理・メンテナンスが必要なことは間違いないのですが、rails db:migrate でセットアップできるので個人的にはかなり楽になりますね。

参考

https://qiita.com/sobameshi0901/items/fd9854a0a5151fdc67f2
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-execute
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_all
https://api.rubyonrails.org/classes/ActiveRecord/Querying.html#method-i-find_by_sql
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html

Discussion