From 141025bfc122fe64686bf35bc391df0f8eb37483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Kovac=CC=8C?= Date: Sun, 14 May 2023 00:46:46 +0200 Subject: [PATCH 1/5] Support other unique constraint error format Unique constraints sometimes fail with the following error format: UNIQUE constraint failed: index '' I believe this happens when the index name doesn't match the names of the columns involved. Example of an index for which this happens: CREATE UNIQUE INDEX test_user_id_number_year_index ON invoices (user_id, number, strftime('%Y', created_at)); --- lib/ecto/adapters/sqlite3/connection.ex | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ecto/adapters/sqlite3/connection.ex b/lib/ecto/adapters/sqlite3/connection.ex index b5b8503..b8f0490 100644 --- a/lib/ecto/adapters/sqlite3/connection.ex +++ b/lib/ecto/adapters/sqlite3/connection.ex @@ -130,6 +130,13 @@ defmodule Ecto.Adapters.SQLite3.Connection do end @impl true + def to_constraints( + %Exqlite.Error{message: "UNIQUE constraint failed: index " <> constraint}, + _opts + ) do + [unique: String.trim(constraint, ~s('))] + end + def to_constraints( %Exqlite.Error{message: "UNIQUE constraint failed: " <> constraint}, _opts From daed87c33687282f1263fd1e1dc71a9ef5fad810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Kovac=CC=8C?= Date: Sun, 14 May 2023 01:06:31 +0200 Subject: [PATCH 2/5] Add tests --- test/ecto/adapters/sqlite3/connection_test.exs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/ecto/adapters/sqlite3/connection_test.exs b/test/ecto/adapters/sqlite3/connection_test.exs index b44a015..bbb732c 100644 --- a/test/ecto/adapters/sqlite3/connection_test.exs +++ b/test/ecto/adapters/sqlite3/connection_test.exs @@ -2414,6 +2414,20 @@ defmodule Ecto.Adapters.SQLite3.ConnectionTest do assert execute_ddl(integer) == [~s/CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY)/] end + describe "to_constraints/2" do + alias Ecto.Adapters.SQLite3.Connection + + test "simple unique index" do + error = %Exqlite.Error{message: "UNIQUE constraint failed: one.two.three"} + assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] + end + + test "complex unique index" do + error = %Exqlite.Error{message: "UNIQUE constraint failed: index 'one_two_three_index'"} + assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] + end + end + defp remove_newlines(string) do string |> String.trim() |> String.replace("\n", " ") end From 7ee02e33c06f2d6d5d4627e05c07ba4fea5a38ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Kovac=CC=8C?= Date: Sun, 14 May 2023 01:10:30 +0200 Subject: [PATCH 3/5] Add one more test --- test/ecto/adapters/sqlite3/connection_test.exs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/ecto/adapters/sqlite3/connection_test.exs b/test/ecto/adapters/sqlite3/connection_test.exs index bbb732c..45f83a8 100644 --- a/test/ecto/adapters/sqlite3/connection_test.exs +++ b/test/ecto/adapters/sqlite3/connection_test.exs @@ -2422,6 +2422,11 @@ defmodule Ecto.Adapters.SQLite3.ConnectionTest do assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] end + test "multi-column unique index" do + error = %Exqlite.Error{message: "UNIQUE constraint failed: one.two, one.three"} + assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] + end + test "complex unique index" do error = %Exqlite.Error{message: "UNIQUE constraint failed: index 'one_two_three_index'"} assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] From 88e576ac859e556b390de84ba97cc16155579285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Kovac=CC=8C?= Date: Sun, 14 May 2023 01:11:13 +0200 Subject: [PATCH 4/5] Fix formatting --- test/ecto/adapters/sqlite3/connection_test.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/ecto/adapters/sqlite3/connection_test.exs b/test/ecto/adapters/sqlite3/connection_test.exs index 45f83a8..2767c88 100644 --- a/test/ecto/adapters/sqlite3/connection_test.exs +++ b/test/ecto/adapters/sqlite3/connection_test.exs @@ -2428,7 +2428,10 @@ defmodule Ecto.Adapters.SQLite3.ConnectionTest do end test "complex unique index" do - error = %Exqlite.Error{message: "UNIQUE constraint failed: index 'one_two_three_index'"} + error = %Exqlite.Error{ + message: "UNIQUE constraint failed: index 'one_two_three_index'" + } + assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] end end From 09eaa033b2f475452ba52aff6ae7ddf88cba89cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Kovac=CC=8C?= Date: Tue, 16 May 2023 07:40:47 +0200 Subject: [PATCH 5/5] Make test data more realistic --- .../ecto/adapters/sqlite3/connection_test.exs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/test/ecto/adapters/sqlite3/connection_test.exs b/test/ecto/adapters/sqlite3/connection_test.exs index 2767c88..8a1f5ef 100644 --- a/test/ecto/adapters/sqlite3/connection_test.exs +++ b/test/ecto/adapters/sqlite3/connection_test.exs @@ -2417,22 +2417,34 @@ defmodule Ecto.Adapters.SQLite3.ConnectionTest do describe "to_constraints/2" do alias Ecto.Adapters.SQLite3.Connection - test "simple unique index" do - error = %Exqlite.Error{message: "UNIQUE constraint failed: one.two.three"} - assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] + test "unique index" do + # created with: + # CREATE UNIQUE INDEX users_email_name_index ON users (email); + + error = %Exqlite.Error{message: "UNIQUE constraint failed: users.email"} + assert Connection.to_constraints(error, []) == [unique: "users_email_index"] end test "multi-column unique index" do - error = %Exqlite.Error{message: "UNIQUE constraint failed: one.two, one.three"} - assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] + # created with: + # CREATE UNIQUE INDEX users_email_name_index ON users (email, name); + + error = %Exqlite.Error{ + message: "UNIQUE constraint failed: users.email, users.name" + } + + assert Connection.to_constraints(error, []) == [unique: "users_email_name_index"] end test "complex unique index" do + # created with: + # CREATE UNIQUE INDEX users_email_year_index ON users (email, strftime('%Y', inserted_at)); + error = %Exqlite.Error{ - message: "UNIQUE constraint failed: index 'one_two_three_index'" + message: "UNIQUE constraint failed: index 'users_email_year_index'" } - assert Connection.to_constraints(error, []) == [unique: "one_two_three_index"] + assert Connection.to_constraints(error, []) == [unique: "users_email_year_index"] end end