limit_offset.ex (1683B)
1 import Kernel, except: [apply: 3] 2 3 defmodule Ecto.Query.Builder.LimitOffset do 4 @moduledoc false 5 6 alias Ecto.Query.Builder 7 8 @doc """ 9 Builds a quoted expression. 10 11 The quoted expression should evaluate to a query at runtime. 12 If possible, it does all calculations at compile time to avoid 13 runtime work. 14 """ 15 @spec build(:limit | :offset, Macro.t, [Macro.t], Macro.t, Macro.Env.t) :: Macro.t 16 def build(type, query, binding, expr, env) do 17 {query, binding} = Builder.escape_binding(query, binding, env) 18 {expr, {params, _acc}} = Builder.escape(expr, :integer, {[], %{}}, binding, env) 19 params = Builder.escape_params(params) 20 21 if contains_variable?(expr) do 22 Builder.error! "query variables are not allowed in #{type} expression" 23 end 24 25 limoff = quote do: %Ecto.Query.QueryExpr{ 26 expr: unquote(expr), 27 params: unquote(params), 28 file: unquote(env.file), 29 line: unquote(env.line)} 30 31 Builder.apply_query(query, __MODULE__, [type, limoff], env) 32 end 33 34 defp contains_variable?(ast) do 35 ast 36 |> Macro.prewalk(false, fn 37 {:&, _, [_]} = expr, _ -> {expr, true} 38 expr, acc -> {expr, acc} 39 end) 40 |> elem(1) 41 end 42 43 @doc """ 44 The callback applied by `build/4` to build the query. 45 """ 46 @spec apply(Ecto.Queryable.t, :limit | :offset, term) :: Ecto.Query.t 47 def apply(%Ecto.Query{} = query, :limit, expr) do 48 %{query | limit: expr} 49 end 50 def apply(%Ecto.Query{} = query, :offset, expr) do 51 %{query | offset: expr} 52 end 53 def apply(query, kind, expr) do 54 apply(Ecto.Queryable.to_query(query), kind, expr) 55 end 56 end