Gladear
How to I dump an Elixir tuple into a PostgreSQL composite type?
Hello ![]()
I’m using Ecto along with PostgreSQL. I’ve been trying to add a field containing a 2-tuple to an Ecto schema, but can’t seem to make it work.
There is no built-in :tuple type in Ecto, so I tried to create an Ecto.Type, but that does not seem to be enough:
defmodule MyType do
use Ecto.Type
@impl Ecto.Type
def type, do: :my_type
@impl Ecto.Type
def cast({one, two}) when is_binary(one) and is_binary(two), do: {:ok, {one, two}}
def cast(_), do: :error
@impl Ecto.Type
def cast({one, two}) when is_binary(one) and is_binary(two), do: {:ok, {one, two}}
def cast(_), do: :error
@impl Ecto.Type
def load({one, two}) do
{:ok, {one, two}}
end
end
I create the field in an Ecto migration using this command:
execute "CREATE TYPE my_type AS (one text, two text)"
Then, when trying to insert a struct from a schema with a field of this type, I get the following message:
(DBConnection.EncodeError) cannot encode anonymous tuple {"one", "two"}. Please define a custom Postgrex extension that matches on its underlying type:
use Postgrex.BinaryExtension, type: "typeinthedb"
I tried to follow the instructions and create an extension, but could never make it work.
What blows my mind is that the library :ex_money_sql (cc @kip) does not seem to have to create a Postgrex extension (or at least, I could never find it). See type source. Postgrex is pretty clear on the subject: “Anonymous composite types are decoded (read) as tuples but they cannot be encoded (written) to the database” (doc). So how does it work? Why does it work? I’m a bit lost here ![]()
If you see any flaw in my reasoning, please tell me what I’m doing wrong ![]()
Thanks for your time,
Most Liked
LostKobrakai
There’s no dump/1 callback in your type. Not sure if that’s related, but should be fixed non the less.
kip
There is a mix task that defines the money_with_currency type. Hopefully that at least clears that up ![]()
Last Post!
Gladear
@LostKobrakai oops, this was a problem when adapting my “real” schema, the second couple of “cast” clauses were actually “dump”.
Although I did find my issue when trying to make a simple reproduction: the problem was not when inserting the struct, it was when querying without the struct - something along the lines of Repo.get_by(MySchema, composite_field: {"one", "two"}). This indeed does not work, however, it can be worked around by explicitly casting to the type:
from(schema in MySchema,
where: schema.composite_field == type(^{"one", "two"}, MyType)
)
|> Repo.one()
This works perfectly fine ![]()
Thank you for your time, sorry for the bother, I may have been quite tired yesterday ![]()
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










