From ca6f1bc9882f13bd578e6d2ebb3ce5a258ff025f Mon Sep 17 00:00:00 2001 From: Peter Mueller <6015288+petermueller@users.noreply.github.com> Date: Sun, 28 Jul 2024 23:21:56 -0400 Subject: [PATCH] add `Ecto.Adapters.MyXQL.Constraint` and delegate from the connection --- integration_test/myxql/constraints_test.exs | 4 ++ lib/ecto/adapters/myxql/connection.ex | 43 ++------------------ lib/ecto/adapters/myxql/constraint.ex | 45 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 40 deletions(-) create mode 100644 lib/ecto/adapters/myxql/constraint.ex diff --git a/integration_test/myxql/constraints_test.exs b/integration_test/myxql/constraints_test.exs index 2df1c08b..9f544426 100644 --- a/integration_test/myxql/constraints_test.exs +++ b/integration_test/myxql/constraints_test.exs @@ -5,8 +5,11 @@ defmodule Ecto.Integration.ConstraintsTest do alias Ecto.Integration.PoolRepo defmodule CustomConstraintHandler do + @behaviour Ecto.Adapters.SQL.Constraint + @quotes ~w(" ' `) + @impl Ecto.Adapters.SQL.Constraint # An example of a custom handler a user might write def to_constraints(%MyXQL.Error{mysql: %{name: :ER_SIGNAL_EXCEPTION}, message: message}, opts) do # Assumes this is the only use-case of `ER_SIGNAL_EXCEPTION` the user has implemented custom errors for @@ -194,6 +197,7 @@ defmodule Ecto.Integration.ConstraintsTest do assert is_integer(result.id) end + @tag :constraint_handler test "custom handled constraint" do changeset = Ecto.Changeset.change(%Constraint{}, from: 0, to: 10) {:ok, item} = PoolRepo.insert(changeset) diff --git a/lib/ecto/adapters/myxql/connection.ex b/lib/ecto/adapters/myxql/connection.ex index cc951c50..2a3fa902 100644 --- a/lib/ecto/adapters/myxql/connection.ex +++ b/lib/ecto/adapters/myxql/connection.ex @@ -5,6 +5,9 @@ if Code.ensure_loaded?(MyXQL) do @behaviour Ecto.Adapters.SQL.Connection + @impl true + defdelegate to_constraints(exception, options), to: Ecto.Adapters.MyXQL.Constraint + ## Connection @impl true @@ -56,46 +59,6 @@ if Code.ensure_loaded?(MyXQL) do end end - @quotes ~w(" ' `) - - @impl true - def to_constraints(%MyXQL.Error{mysql: %{name: :ER_DUP_ENTRY}, message: message}, opts) do - with [_, quoted] <- :binary.split(message, " for key "), - [_, index | _] <- :binary.split(quoted, @quotes, [:global]) do - [unique: strip_source(index, opts[:source])] - else - _ -> [] - end - end - - def to_constraints(%MyXQL.Error{mysql: %{name: name}, message: message}, _opts) - when name in [:ER_ROW_IS_REFERENCED_2, :ER_NO_REFERENCED_ROW_2] do - with [_, quoted] <- :binary.split(message, [" CONSTRAINT ", " FOREIGN KEY "]), - [_, index | _] <- :binary.split(quoted, @quotes, [:global]) do - [foreign_key: index] - else - _ -> [] - end - end - - def to_constraints( - %MyXQL.Error{mysql: %{name: :ER_CHECK_CONSTRAINT_VIOLATED}, message: message}, - _opts - ) do - with [_, quoted] <- :binary.split(message, ["Check constraint "]), - [_, constraint | _] <- :binary.split(quoted, @quotes, [:global]) do - [check: constraint] - else - _ -> [] - end - end - - def to_constraints(_, _), - do: [] - - defp strip_source(name, nil), do: name - defp strip_source(name, source), do: String.trim_leading(name, "#{source}.") - ## Query @parent_as __MODULE__ diff --git a/lib/ecto/adapters/myxql/constraint.ex b/lib/ecto/adapters/myxql/constraint.ex new file mode 100644 index 00000000..d4196dbd --- /dev/null +++ b/lib/ecto/adapters/myxql/constraint.ex @@ -0,0 +1,45 @@ +defmodule Ecto.Adapters.MyXQL.Constraint do + @moduledoc false + + @behaviour Ecto.Adapters.SQL.Constraint + + @quotes ~w(" ' `) + + @impl true + def to_constraints(%MyXQL.Error{mysql: %{name: :ER_DUP_ENTRY}, message: message}, opts) do + with [_, quoted] <- :binary.split(message, " for key "), + [_, index | _] <- :binary.split(quoted, @quotes, [:global]) do + [unique: strip_source(index, opts[:source])] + else + _ -> [] + end + end + + def to_constraints(%MyXQL.Error{mysql: %{name: name}, message: message}, _opts) + when name in [:ER_ROW_IS_REFERENCED_2, :ER_NO_REFERENCED_ROW_2] do + with [_, quoted] <- :binary.split(message, [" CONSTRAINT ", " FOREIGN KEY "]), + [_, index | _] <- :binary.split(quoted, @quotes, [:global]) do + [foreign_key: index] + else + _ -> [] + end + end + + def to_constraints( + %MyXQL.Error{mysql: %{name: :ER_CHECK_CONSTRAINT_VIOLATED}, message: message}, + _opts + ) do + with [_, quoted] <- :binary.split(message, ["Check constraint "]), + [_, constraint | _] <- :binary.split(quoted, @quotes, [:global]) do + [check: constraint] + else + _ -> [] + end + end + + def to_constraints(_, _), + do: [] + + defp strip_source(name, nil), do: name + defp strip_source(name, source), do: String.trim_leading(name, "#{source}.") +end