Ecto not allowing string interpolation in fragments?

I had a similar issue trying to dynamically build fragment strings (from a trusted source) and it seems like a general escape hatch for these kind of issues, when trying to use third party macros with dynamic inputs where not explicitly supported is to use Code.eval_quoted. So, for example, you can wrap an entire select statement and fool Elixir/Ecto into thinking the variables are in fact literals:

Code.eval_quoted(
      quote do
        select(unquote(escaped_q), [table], %{
          dynamic_select: fragment(unquote(some_var), table.field))
        })
      end
    )

I am pretty positive this is the last tool you should reach for and maybe it would be better to use postgrex directly for these cases. I haven’t tried that so I can’t speak to the pros/cons but I’d love to hear people’s thoughts about the practice, and maybe whether Ecto could adopt some ā€œblessedā€ way of circumventing protections like this one because I certainly feel dirty doing this.

4 Likes

Searched long and hard for this solution! Worked like a charm—thank you!

FWIW: table names can be interpolated with Ecto.Query.API — Ecto v3.12.1, e.g.:

      ["join_table_1", "join_table_2"]
      |> Enum.reduce(Tag, fn assoc, q ->
        or_where(q, [t], fragment("exists (select 1 from ? where tag_id = ?)", literal(^assoc), t.id))
      end)
      |> select([t], %{id: t.id})
2 Likes