I’m struggling with postgres composite types being used as primary keys.
Specifically Repo.delete/get/update don’t seem to be happy with it.
Im able to insert new records without any problem, also querying works if i use type()
in the queries.
To demonstrate the issue i created a github repo at: https://github.com/slapers/ecto-custom-type-pk
There is 1 failing test that demonstrates the problem. Since i could not find much content on this use case i thought it might be interesting to create a topic on it.
Is this solvable, or is it not advisable to use composite types as primary keys ?
Small summary of what is in the test git repo:
Composite type migration:
def up do
execute """
CREATE TYPE public.srn AS (r_id uuid, w_id uuid);
"""
end
def down do
execute """
DROP TYPE IF EXISTS public.srn
"""
end
Custom Ecto type:
defmodule Saas.SRN do
@behaviour Ecto.Type
@type t :: %__MODULE__{
ws: binary,
rs: binary
}
defstruct [:ws, :rs]
def new(ws, rs), do: %__MODULE__{ws: ws, rs: rs}
def type, do: :srn
def cast({ws, rs}), do: {:ok, new(ws, rs)}
def cast(_), do: {:error, :invalid_srn}
def dump(%__MODULE__{ws: ws, rs: rs}), do: {:ok, {uuid_dump!(ws), uuid_dump!(rs)}}
def load({ws, rs}), do: {:ok, new(uuid_load!(ws), uuid_load!(rs))}
defp uuid_dump!(uuid) do
{:ok, uuid_bin} = Ecto.UUID.dump(uuid)
uuid_bin
end
defp uuid_load!(uuid) do
{:ok, uuid_bin} = Ecto.UUID.load(uuid)
uuid_bin
end
end
A simple schema using the type as primary key:
defmodule Saas.User do
@moduledoc false
use Ecto.Schema
alias Saas.SRN
@primary_key {:id, SRN, autogenerate: false}
@foreign_key_type SRN
schema "saas_users" do
field :name, :string
end
end
And the failing test:
defmodule Saas.UserTest do
@moduledoc false
use ExUnit.Case
alias Ecto.UUID
alias Saas.{Repo, User, SRN}
@id SRN.new(UUID.generate(), UUID.generate())
setup do
{:ok, user: Repo.insert!(%User{id: @id, name: "Jack"})}
end
test "delete!/2", %{user: user} do
assert user == Repo.delete!(user)
end
end
When run it errors out with the error below:
** (FunctionClauseError) no function clause matching in Postgrex.DefaultTypes.encode_tuple/5