如何引发自定义 Postgresql 错误并在 Ecto 中处理它

den*_*lin 3 postgresql elixir ecto

我在 Postgresql 中创建了一个自定义函数,它在插入或更新之前检查数据,并在出现问题时引发错误。

CREATE FUNCTION custom_check() RETURNS TRIGGER AS $$
  BEGIN
    IF <SOME CONDITION> THEN
        RAISE EXCEPTION 'CUSTOM ERROR';
    END IF;
    RETURN NEW;
  END;
  $$ LANGUAGE plpgsql
""")
Run Code Online (Sandbox Code Playgroud)

当我在 Postgresql 中使用约束时,我可以处理由Ecto.Changeset.check_constraint.

但是我没有找到一种方法来处理我提出的这个错误,将它反映在变更集中而不是获取异常并可能在我的代码中捕获它。

我应该Ecto.Changeset.check_constraint以不同的方式提出错误来处理它,还是做不同的事情?

sme*_*fju 6

据我所知,没有处理自定义 PostgreSQL 错误的内置机制。但是,您可以在存储库级别执行此操作。

为此,您必须使用以下命令在 PostgreSQL 中引发错误ERRCODE

RAISE '[message for logs]' USING ERRCODE = 'integrity_constraint_violation';
Run Code Online (Sandbox Code Playgroud)

然后在应用程序中处理它们:

defmodule Core.Repo do
  use Ecto.Repo, otp_app: :core

  defoverridable insert: 2

  def insert(changeset, opts) do
    super(changeset, opts)
  rescue
    exception in Postgrex.Error ->
      handle_postgrex_exception(exception, __STACKTRACE__, changeset)
  end

  # ... other functions

  defp handle_postgrex_exception(exception, stacktrace, changeset \\ nil)

  defp handle_postgrex_exception(%{postgres: %{code: :integrity_constraint_violation}}, _, nil) do
    {:error, :integrity_constraint_violation}
  end

  defp handle_postgrex_exception(
         %{postgres: %{code: :integrity_constraint_violation}},
         _,
         changeset
       ) do
    {:error, %{changeset | valid?: false}}
  end

  defp handle_postgrex_exception(exception, stacktrace, _) do
    reraise(exception, stacktrace)
  end
end
Run Code Online (Sandbox Code Playgroud)

注意{:error, %{changeset | valid?: false}}回复。这意味着到那时,将不会显示任何有用的消息。

PS,您可能会编写一些宏来覆盖 Ecto 的功能并将实现隐藏在那里(而不是建议的解决方案),但我相信维护起来会困难得多。