diff --git a/lib/ecto/query/api.ex b/lib/ecto/query/api.ex index 30422a1e18..d831c28f06 100644 --- a/lib/ecto/query/api.ex +++ b/lib/ecto/query/api.ex @@ -480,17 +480,20 @@ defmodule Ecto.Query.API do def fragment(fragments), do: doc!([fragments]) @doc """ - Allows a literal identifier to be injected into a fragment: + Allows a literal identifier or number to be injected into a fragment: collation = "es_ES" fragment("? COLLATE ?", ^name, literal(^collation)) - The example above will inject `collation` into the query as - a literal identifier instead of a query parameter. Note that - each different value of `collation` will emit a different query, - which will be independently prepared and cached. + limit = 10 + limit(query, fragment("?", literal(^limit))) + + The example above will inject `collation` and `limit` into the queries as + literals instead of query parameters. Note that each different value passed + to `literal/1` will emit a different query, which will be independently prepared + and cached. """ - def literal(binary), do: doc!([binary]) + def literal(literal), do: doc!([literal]) @doc """ Allows a list argument to be spliced into a fragment. diff --git a/lib/ecto/query/builder.ex b/lib/ecto/query/builder.ex index 94dbb5649e..6d40a4a38a 100644 --- a/lib/ecto/query/builder.ex +++ b/lib/ecto/query/builder.ex @@ -1256,13 +1256,12 @@ defmodule Ecto.Query.Builder do @doc """ Called by escaper at runtime to verify literal in fragments. """ + def literal!(literal) when is_binary(literal), do: literal + def literal!(literal) when is_number(literal), do: literal + def literal!(literal) do - if is_binary(literal) do - literal - else - raise ArgumentError, - "literal(^value) expects `value` to be a string, got `#{inspect(literal)}`" - end + raise ArgumentError, + "literal(^value) expects `value` to be a string or a number, got `#{inspect(literal)}`" end @doc """ diff --git a/test/ecto/query_test.exs b/test/ecto/query_test.exs index e3b8307acb..dd84544718 100644 --- a/test/ecto/query_test.exs +++ b/test/ecto/query_test.exs @@ -988,9 +988,20 @@ defmodule Ecto.QueryTest do raw: "" ] = parts - assert_raise ArgumentError, "literal(^value) expects `value` to be a string, got `123`", fn -> - from p in "posts", select: fragment("? COLLATE ?", p.name, literal(^123)) - end + query = from p in "posts", limit: fragment("?", literal(^1)) + assert {:fragment, _, parts} = query.limit.expr + + assert [ + raw: "", + expr: {:literal, _, [1]}, + raw: "" + ] = parts + + assert_raise ArgumentError, + "literal(^value) expects `value` to be a string or a number, got `%{}`", + fn -> + from p in "posts", select: fragment("? COLLATE ?", p.name, literal(^%{})) + end end test "supports list splicing" do