elixir-sqlite/ecto_sqlite3

Additional quotes being added to index name?

Closed this issue · 6 comments

Hi, I already posted this issue in the ecto repository, but @josevalim pointed me to this repository with the guess, that there might be additional quotes added around the index name in the migration.

Issue description

When creating an unique index with a custom expression as shown as in Ecto.Migration docs

create index(:branches, ["(lower(name))"], name: :branches_lower_name_index, unique: true)

and adding the unique_constraint check to the changeset with the name of the unique index

...
|> unique_constraint(:name, name: :branches_lower_name_index)

it raises the following Ecto.ConstraintError

** (Ecto.ConstraintError) constraint error when attempting to insert struct:

    * index 'branches_lower_name_index'_index (unique_constraint)

If you would like to stop this constraint violation from raising an
exception and instead add it as an error to your changeset, please
call `unique_constraint/3` on your changeset with the constraint
`:name` as an option.

The changeset defined the following constraints:

    * branches_lower_name_index (unique_constraint)

    (ecto 3.8.4) lib/ecto/repo/schema.ex:783: anonymous fn/4 in Ecto.Repo.Schema.constraints_to_errors/3
    (elixir 1.13.2) lib/enum.ex:1593: Enum."-map/2-lists^map/1-0-"/2
    (ecto 3.8.4) lib/ecto/repo/schema.ex:768: Ecto.Repo.Schema.constraints_to_errors/3
    (ecto 3.8.4) lib/ecto/repo/schema.ex:749: Ecto.Repo.Schema.apply/4
    (ecto 3.8.4) lib/ecto/repo/schema.ex:367: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4

Please notice the strange name of the index 'branches_lower_name_index'_index

The expected behavior is that an error for the specified field is added to the changeset, instead of raising an Ecto.ConstraintError.

Version information

  • Elixir: 1.13.2
  • Database: sqlite3 3.39.2
  • Ecto: ecto 3.8.4
  • Database Adapter: ecto_sqlite3 0.8.1

Many thanks 👍

@railsmechanic I'll take a look at this soon.

Cool, thank you! 👍🏻

The funky name is emitted from here

# we want to return the name of the underlying index that caused
# the constraint error, but in SQLite as far as I can tell there
# is no way to do this, so we name the index according to ecto
# convention, even if technically it _could_ have a different name
defp constraint_name_hack(constraint) do
if String.contains?(constraint, ", ") do
# "a.b, a.c" -> a_b_c_index
constraint
|> String.split(", ")
|> Enum.with_index()
|> Enum.map(fn {table_col, idx} ->
case idx do
0 -> table_col |> String.replace(".", "_")
_ -> table_col |> String.split(".") |> List.last()
end
end)
|> Enum.concat(["index"])
|> Enum.join("_")
else
constraint
|> String.split(".")
|> Enum.concat(["index"])
|> Enum.join("_")
end
end

Who'da thought the function name constraint_name_hack/1 would be the culprit 🤣

@railsmechanic I have not forgotten this. Just haven't had time.

@railsmechanic I have not forgotten this. Just haven't had time.

No problem, I also have no time... 👍