Translating SQL query with row constructors (?) into Ecto

Hi all,

I have a table books with columns name and version. I want to search for books matching one of the provided pairs of (name, version). The SQL query looks like this:

select * from public.books
where (name, version) in (('ruby', 'v1'), ('ruby', 'v2'), ('elixir', 'v1'))

What I have tried:

signatures = [{"ruby", "v1"}, {"ruby", "v2"}, {"elixir", "v1"}]
from(b in Book,
  where: fragment("(?, ?) IN (?)", b.name, b.version, ^signatures)
)

This fails with:

** (DBConnection.EncodeError) Postgrex expected a tuple, got [{“ruby”, “v1”}, {“ruby”, “v2”}, {“elixir”, “v1”}]. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.

Well, I tried a few more options, but they made even less sense. I feel like such a simple expression should be possible to express in Ecto?

(I am not even sure how this constructions (col1, col2, …) are called in SQL but I suspect this is row constructors, because I can replace my query with select * from public.books where ROW(name, version) in (ROW('ruby', '1'), ROW(...), ...)) and it works the same.

Thanks!

1 Like

This can be accomplished with the values query API. Ecto.Query.API — Ecto v3.12.5
You can join with the values using the name and version column.

2 Likes

This comes up from time to time, but I haven’t seen a “great” solution for it. Here’s some previous threads you might find relevant:

3 Likes

You might even be interested in not translating at all and just implementing Enumerable on top of GitHub - elixir-dbvisor/sql: Brings an extensible SQL parser and sigil to Elixir, confidently write SQL with automatic parameterized queries. it’s still in active development but main is conformant to SQL 2016

1 Like

Thanks for the links to the past threads!

I think the solution described in one of the answers works for me and generates exactly the query that I want.

Great library!

1 Like