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.

3 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})
1 Like