I am trying to use where exists ()
, which requires a fragment in ecto. I need to use a different table name for the subquery in different situations, so I thought a variable that gets interpolated would be ideal. No, the value does not come from an unsafe source. I am aware of the dangers of SQL injection attacks and how to avoid them. I am selecting the value of the variable from another ecto schema inside a case expression. Surely there must be some way of doing this, without having to entirely disconnect from the Ecto abstractions we already depend on?
Here is an example:
def thing(schema) do
other_table = Other.Schema.__schema__(:source)
from(a in My.Schema,
where: fragment("exists (
SELECT 1
FROM #{other_table} o
WHERE o.column_name = ?)", ^a.my_field)
)
|> Repo.all()
end
And here is the error:
(Ecto.Query.CompileError) to prevent SQL injection attacks, fragment(...) does not allow strings to be interpolated as the first argument via the `^` operator, got: `"exists (\n SELECT 1\n FROM #{other_table} o\n WHERE o.column_name = ?)"
We are trying to use exists
because it is significantly more performant than the alternatives that ecto provides for our use-case. We are reaching the scale where inefficient queries that can’t use indexes properly are becoming critical problems.