PostgreSQL の Function を rails migrate で管理する
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)
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 型で取得することができます。
find_by_sql
は結果を元のモデルの配列を返します。
他にもいろいろとありましたが、基本的に execute
と合わせてこの 3 つを使っていけばあまり困らなさそうです。
さいごに
今回は、PostgreSQL で使用する Function の CREATE 文を migrate ファイルから実行するサンプルを作成しました。
どっちみち管理・メンテナンスが必要なことは間違いないのですが、rails db:migrate
でセットアップできるので個人的にはかなり楽になりますね。
参考
Discussion