-
Notifications
You must be signed in to change notification settings - Fork 302
Description
Elixir version
Erlang/OTP 28 [erts-16.1.1] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit]
Database and Version
postgres (PostgreSQL) 18.0
Postgrex Version
postgrex 0.22.0 (Hex package) (mix)
Current behavior
- Create a composite type and a domain over that composite type to enforce some constraints. Create an example table using the domain type:
demodule CreateExampleTable do
use Ecto.Migration
def up do
execute("""
CREATE TYPE composite_example AS (
a text,
b integer
)
""")
execute("""
CREATE DOMAIN non_null_composite AS composite_example
CHECK (
(VALUE).a IS NOT NULL
AND (VALUE).b IS NOT NULL)
""")
create table("examples") do
add :single, :non_null_composite
add :multiple, {:array, :non_null_composite}
end
end
def down do
drop table("examples")
execute("DROP DOMAIN non_null_composite")
execute("DROP TYPE composite_example")
end
end- Define custom
Ecto.Type:
defmodule CompositeExample do
use Ecto.Type
defstruct [:a, :b]
@impl true
def type(), do: :non_null_composite
@impl true
def cast(%__MODULE__{} = value) do
{:ok, value}
end
def cast({a, b}) do
{:ok, %__MODULE__{a: a, b: b}}
end
def cast(_) do
:error
end
@impl true
def dump(%__MODULE__{a: a, b: b}) do
{:ok, {a, b}}
end
@impl true
def load({a, b}) do
{:ok, %__MODULE__{a: a, b: b}}
end
end- Define schema:
defmodule Example do
use Ecto.Schema
@primary_key false
schema "examples" do
field :single, CompositeExample
field :multiple, {:array, CompositeExample}
end
end- Insert records into the schema:
Repo.insert!(%Example{single: %CompositeExample{a: "a", b: 1}})
Repo.insert!(%Example{multiple: [%CompositeExample{a: "a", b: 1}, %CompositeExample{a: "b", b: 2}]})- The first insert is successful (I can use the domain type as a column type).
[debug] QUERY ERROR source="examples" db=4.4ms queue=0.3ms idle=42.5ms
INSERT INTO "examples" ("single") VALUES ($1) [%CompositeExample{a: "a", b: 1}]
The second insert (using the domain type in an array) raises an error about encoding an anonymous tuple:
↳ :elixir_compiler_10.__FILE__/1, at: demo.exs:3
** (DBConnection.EncodeError) cannot encode anonymous tuple {"a", 1}. Please define a custom Postgrex extension that matches on its underlying type:
use Postgrex.BinaryExtension, type: "typeinthedb"
(postgrex 0.22.0) lib/postgrex/type_module.ex:194: Postgrex.DefaultTypes.encode_tuple/3
(postgrex 0.22.0) lib/postgrex/type_module.ex:1084: Postgrex.DefaultTypes.encode_params/3
(postgrex 0.22.0) lib/postgrex/query.ex:73: DBConnection.Query.Postgrex.Query.encode/3
(db_connection 2.9.0) lib/db_connection.ex:1440: DBConnection.encode/5
(db_connection 2.9.0) lib/db_connection.ex:1530: DBConnection.run_prepare_execute/5
(db_connection 2.9.0) lib/db_connection.ex:1587: DBConnection.run_with_retries/5
(db_connection 2.9.0) lib/db_connection.ex:791: DBConnection.parsed_prepare_execute/5
(db_connection 2.9.0) lib/db_connection.ex:783: DBConnection.prepare_execute/4
(postgrex 0.22.0) lib/postgrex.ex:298: Postgrex.query/4
(ecto_sql 3.13.4) lib/ecto/adapters/sql.ex:1181: Ecto.Adapters.SQL.struct/10
(ecto 3.13.5) lib/ecto/repo/schema.ex:1000: Ecto.Repo.Schema.apply/4
(ecto 3.13.5) lib/ecto/repo/schema.ex:500: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto 3.13.5) lib/ecto/repo/schema.ex:381: Ecto.Repo.Schema.insert!/4
Expected behavior
I wanted both inserts to succeed. This example actually works if I use the composite type in the array instead of the domain type. It's only when switching to the domain type inside the array that I get this error.
At first, I thought maybe domain types aren't supported by Postgrex, but I found this merged PR from a long time ago that added support specifically for domain types inside of arrays: #337. Maybe a regression of #337, or an edge-case of how domain types, composite types, and arrays interact with each other?