zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

builder.ex (45754B)


      1 defmodule Ecto.Query.Builder do
      2   @moduledoc false
      3 
      4   alias Ecto.Query
      5 
      6   @comparisons [
      7     is_nil: 1,
      8     ==: 2,
      9     !=: 2,
     10     <: 2,
     11     >: 2,
     12     <=: 2,
     13     >=: 2
     14   ]
     15 
     16   @dynamic_aggregates [
     17     max: 1,
     18     min: 1,
     19     first_value: 1,
     20     last_value: 1,
     21     nth_value: 2,
     22     lag: 3,
     23     lead: 3,
     24     lag: 2,
     25     lead: 2,
     26     lag: 1,
     27     lead: 1
     28   ]
     29 
     30   @static_aggregates [
     31     count: {0, :integer},
     32     count: {1, :integer},
     33     count: {2, :integer},
     34     avg: {1, :any},
     35     sum: {1, :any},
     36     row_number: {0, :integer},
     37     rank: {0, :integer},
     38     dense_rank: {0, :integer},
     39     percent_rank: {0, :any},
     40     cume_dist: {0, :any},
     41     ntile: {1, :integer}
     42   ]
     43 
     44   @select_alias_dummy_value []
     45 
     46   @typedoc """
     47   Quoted types store primitive types and types in the format
     48   {source, quoted}. The latter are handled directly in the planner,
     49   never forwarded to Ecto.Type.
     50 
     51   The Ecto.Type module concerns itself only with runtime types,
     52   which include all primitive types and custom user types. Also
     53   note custom user types do not show up during compilation time.
     54   """
     55   @type quoted_type :: Ecto.Type.primitive | {non_neg_integer, atom | Macro.t}
     56 
     57   @typedoc """
     58   The accumulator during escape.
     59 
     60   If the subqueries field is available, subquery escaping must take place.
     61   """
     62   @type acc :: %{
     63           optional(:subqueries) => list(Macro.t()),
     64           optional(:take) => %{non_neg_integer => Macro.t()},
     65           optional(any) => any
     66         }
     67 
     68   @doc """
     69   Smart escapes a query expression and extracts interpolated values in
     70   a map.
     71 
     72   Everything that is a query expression will be escaped, interpolated
     73   expressions (`^foo`) will be moved to a map unescaped and replaced
     74   with `^index` in the query where index is a number indexing into the
     75   map.
     76   """
     77   @spec escape(Macro.t, quoted_type | {:in, quoted_type} | {:out, quoted_type}, {list, acc},
     78                Keyword.t, Macro.Env.t | {Macro.Env.t, fun}) :: {Macro.t, {list, acc}}
     79   def escape(expr, type, params_acc, vars, env)
     80 
     81   # var.x - where var is bound
     82   def escape({{:., _, [callee, field]}, _, []}, _type, params_acc, vars, _env) when is_atom(field) do
     83     {escape_field!(callee, field, vars), params_acc}
     84   end
     85 
     86   # field macro
     87   def escape({:field, _, [callee, field]}, _type, params_acc, vars, _env) do
     88     {escape_field!(callee, field, vars), params_acc}
     89   end
     90 
     91   # param interpolation
     92   def escape({:^, _, [arg]}, type, {params, acc}, _vars, _env) do
     93     expr = {:{}, [], [:^, [], [length(params)]]}
     94     params = [{arg, type} | params]
     95     {expr, {params, acc}}
     96   end
     97 
     98   # tagged types
     99   def escape({:type, _, [{:^, _, [arg]}, type]}, _type, {params, acc}, vars, env) do
    100     type = validate_type!(type, vars, env)
    101     expr = {:{}, [], [:type, [], [{:{}, [], [:^, [], [length(params)]]}, type]]}
    102     params = [{arg, type} | params]
    103     {expr, {params, acc}}
    104   end
    105 
    106   def escape({:type, _, [{{:., _, [{var, _, context}, field]}, _, []} = expr, type]}, _type, params_acc, vars, env)
    107       when is_atom(var) and is_atom(context) and is_atom(field) do
    108     escape_with_type(expr, type, params_acc, vars, env)
    109   end
    110 
    111   def escape({:type, _, [{:coalesce, _, [_ | _]} = expr, type]}, _type, params_acc, vars, env) do
    112     escape_with_type(expr, type, params_acc, vars, env)
    113   end
    114 
    115   def escape({:type, _, [{:field, _, [_ | _]} = expr, type]}, _type, params_acc, vars, env) do
    116     escape_with_type(expr, type, params_acc, vars, env)
    117   end
    118 
    119   def escape({:type, _, [{math_op, _, [_, _]} = op_expr, type]}, _type, params_acc, vars, env)
    120       when math_op in ~w(+ - * /)a do
    121     escape_with_type(op_expr, type, params_acc, vars, env)
    122   end
    123 
    124   def escape({:type, _, [{fun, _, args} = expr, type]}, _type, params_acc, vars, env)
    125       when is_list(args) and fun in ~w(fragment avg count max min sum over filter)a do
    126     escape_with_type(expr, type, params_acc, vars, env)
    127   end
    128 
    129   def escape({:type, _, [{:json_extract_path, _, [_ | _]} = expr, type]}, _type, params_acc, vars, env) do
    130     escape_with_type(expr, type, params_acc, vars, env)
    131   end
    132 
    133   def escape({:type, _, [{{:., _, [Access, :get]}, _, _} = access_expr, type]}, _type, params_acc, vars, env) do
    134     escape_with_type(access_expr, type, params_acc, vars, env)
    135   end
    136 
    137   def escape({:type, _, [{{:., _, [{:parent_as, _, [_parent]}, _field]}, _, []} = expr, type]}, _type, params_acc, vars, env) do
    138     escape_with_type(expr, type, params_acc, vars, env)
    139   end
    140 
    141   def escape({:type, meta, [expr, type]}, given_type, params_acc, vars, env) do
    142     case Macro.expand_once(expr, get_env(env)) do
    143       ^expr ->
    144         error! """
    145         the first argument of type/2 must be one of:
    146 
    147           * interpolations, such as ^value
    148           * fields, such as p.foo or field(p, :foo)
    149           * fragments, such as fragment("foo(?)", value)
    150           * an arithmetic expression (+, -, *, /)
    151           * an aggregation or window expression (avg, count, min, max, sum, over, filter)
    152           * a conditional expression (coalesce)
    153           * access/json paths (p.column[0].field)
    154           * parent_as/1 (parent_as(:parent).field)
    155 
    156         Got: #{Macro.to_string(expr)}
    157         """
    158 
    159       expanded ->
    160         escape({:type, meta, [expanded, type]}, given_type, params_acc, vars, env)
    161     end
    162   end
    163 
    164   # fragments
    165   def escape({:fragment, _, [query]}, _type, params_acc, vars, env) when is_list(query) do
    166     {escaped, params_acc} =
    167       Enum.map_reduce(query, params_acc, &escape_kw_fragment(&1, &2, vars, env))
    168     {{:{}, [], [:fragment, [], [escaped]]}, params_acc}
    169   end
    170 
    171   def escape({:fragment, _, [{:^, _, [var]} = _expr]}, _type, params_acc, _vars, _env) do
    172     expr = quote do: Ecto.Query.Builder.fragment!(unquote(var))
    173     {{:{}, [], [:fragment, [], [expr]]}, params_acc}
    174   end
    175 
    176   def escape({:fragment, _, [query | frags]}, _type, params_acc, vars, env) do
    177     pieces = expand_and_split_fragment(query, env)
    178 
    179     if length(pieces) != length(frags) + 1 do
    180       error! "fragment(...) expects extra arguments in the same amount of question marks in string. " <>
    181                "It received #{length(frags)} extra argument(s) but expected #{length(pieces) - 1}"
    182     end
    183 
    184     {frags, params_acc} = Enum.map_reduce(frags, params_acc, &escape_fragment(&1, &2, vars, env))
    185     {{:{}, [], [:fragment, [], merge_fragments(pieces, frags)]}, params_acc}
    186   end
    187 
    188   # subqueries
    189   def escape({:subquery, _, [expr]}, _, {params, %{subqueries: subqueries} = acc}, _vars, _env) do
    190     subquery = quote(do: Ecto.Query.subquery(unquote(expr)))
    191     index = length(subqueries)
    192     # used both in ast and in parameters, as a placeholder.
    193     expr = {:subquery, index}
    194     acc = %{acc | subqueries: [subquery | subqueries]}
    195     {expr, {[expr | params], acc}}
    196   end
    197 
    198   # interval
    199 
    200   def escape({:from_now, meta, [count, interval]}, type, params_acc, vars, env) do
    201     utc = quote do: ^DateTime.utc_now()
    202     escape({:datetime_add, meta, [utc, count, interval]}, type, params_acc, vars, env)
    203   end
    204 
    205   def escape({:ago, meta, [count, interval]}, type, params_acc, vars, env) do
    206     utc = quote do: ^DateTime.utc_now()
    207     count =
    208       case count do
    209         {:^, meta, [value]} ->
    210           negate = quote do: Ecto.Query.Builder.negate!(unquote(value))
    211           {:^, meta, [negate]}
    212         value ->
    213           {:-, [], [value]}
    214       end
    215     escape({:datetime_add, meta, [utc, count, interval]}, type, params_acc, vars, env)
    216   end
    217 
    218   def escape({:datetime_add, _, [datetime, count, interval]} = expr, type, params_acc, vars, env) do
    219     assert_type!(expr, type, {:param, :any_datetime})
    220     {datetime, params_acc} = escape(datetime, {:param, :any_datetime}, params_acc, vars, env)
    221     {count, interval, params_acc} = escape_interval(count, interval, params_acc, vars, env)
    222     {{:{}, [], [:datetime_add, [], [datetime, count, interval]]}, params_acc}
    223   end
    224 
    225   def escape({:date_add, _, [date, count, interval]} = expr, type, params_acc, vars, env) do
    226     assert_type!(expr, type, :date)
    227     {date, params_acc} = escape(date, :date, params_acc, vars, env)
    228     {count, interval, params_acc} = escape_interval(count, interval, params_acc, vars, env)
    229     {{:{}, [], [:date_add, [], [date, count, interval]]}, params_acc}
    230   end
    231 
    232   # json
    233   def escape({:json_extract_path, _, [field, path]} = expr, type, params_acc, vars, env) do
    234     case field do
    235       {{:., _, _}, _, _} ->
    236         path = escape_json_path(path)
    237         {field, params_acc} = escape(field, type, params_acc, vars, env)
    238         {{:{}, [], [:json_extract_path, [], [field, path]]}, params_acc}
    239 
    240       _ ->
    241         error!("`#{Macro.to_string(expr)}` is not a valid query expression")
    242     end
    243   end
    244 
    245   def escape({{:., meta, [Access, :get]}, _, [left, _]} = expr, type, params_acc, vars, env) do
    246     case left do
    247       {{:., _, _}, _, _} ->
    248         {expr, path} = parse_access_get(expr, [])
    249         escape({:json_extract_path, meta, [expr, path]}, type, params_acc, vars, env)
    250 
    251       _ ->
    252         error!("`#{Macro.to_string(expr)}` is not a valid query expression")
    253     end
    254   end
    255 
    256   # sigils
    257   def escape({name, _, [_, []]} = sigil, type, params_acc, vars, _env)
    258       when name in ~w(sigil_s sigil_S sigil_w sigil_W)a do
    259     {literal(sigil, type, vars), params_acc}
    260   end
    261 
    262   # lists
    263   def escape(list, type, params_acc, vars, env) when is_list(list) do
    264     if Enum.all?(list, &is_binary(&1) or is_number(&1) or is_boolean(&1)) do
    265       {literal(list, type, vars), params_acc}
    266     else
    267       fun =
    268         case type do
    269           {:array, inner_type} ->
    270             &escape(&1, inner_type, &2, vars, env)
    271 
    272           _ ->
    273             # In case we don't have an array nor a literal at compile-time,
    274             # such as p.links == [^value], we don't do any casting nor validation.
    275             # We may want to tackle this if the expression above is ever used.
    276             &escape(&1, :any, &2, vars, env)
    277         end
    278 
    279       Enum.map_reduce(list, params_acc, fun)
    280     end
    281   end
    282 
    283   # literals
    284   def escape({:<<>>, _, args} = expr, type, params_acc, vars, _env) do
    285     valid? = Enum.all?(args, fn
    286       {:"::", _, [left, _]} -> is_integer(left) or is_binary(left)
    287       left -> is_integer(left) or is_binary(left)
    288     end)
    289 
    290     unless valid? do
    291       error! "`#{Macro.to_string(expr)}` is not a valid query expression. " <>
    292              "Only literal binaries and strings are allowed, " <>
    293              "dynamic values need to be explicitly interpolated in queries with ^"
    294     end
    295 
    296     {literal(expr, type, vars), params_acc}
    297   end
    298 
    299   def escape({:-, _, [number]}, type, params_acc, vars, _env) when is_number(number),
    300     do: {literal(-number, type, vars), params_acc}
    301   def escape(number, type, params_acc, vars, _env) when is_number(number),
    302     do: {literal(number, type, vars), params_acc}
    303   def escape(binary, type, params_acc, vars, _env) when is_binary(binary),
    304     do: {literal(binary, type, vars), params_acc}
    305   def escape(nil, _type, params_acc, _vars, _env),
    306     do: {nil, params_acc}
    307   def escape(atom, type, params_acc, vars, _env) when is_atom(atom),
    308     do: {literal(atom, type, vars), params_acc}
    309 
    310   # negate any expression
    311   def escape({:-, meta, arg}, type, params_acc, vars, env) do
    312     {escaped_arg, params_acc} = escape(arg, type, params_acc, vars, env)
    313     expr = {:{}, [], [:-, meta, escaped_arg]}
    314     {expr, params_acc}
    315   end
    316 
    317   # comparison operators
    318   def escape({comp_op, _, [left, right]} = expr, type, params_acc, vars, env)
    319       when comp_op in ~w(== != < > <= >=)a do
    320     assert_type!(expr, type, :boolean)
    321 
    322     if is_nil(left) or is_nil(right) do
    323       error! "comparison with nil is forbidden as it is unsafe. " <>
    324              "If you want to check if a value is nil, use is_nil/1 instead"
    325     end
    326 
    327     ltype = quoted_type(right, vars)
    328     rtype = quoted_type(left, vars)
    329 
    330     {left,  params_acc} = escape(left, ltype, params_acc, vars, env)
    331     {right, params_acc} = escape(right, rtype, params_acc, vars, env)
    332 
    333     {params, acc} = params_acc
    334     {{:{}, [], [comp_op, [], [left, right]]},
    335      {params |> wrap_nil(left) |> wrap_nil(right), acc}}
    336   end
    337 
    338   # mathematical operators
    339   def escape({math_op, _, [left, right]}, type, params_acc, vars, env)
    340       when math_op in ~w(+ - * /)a do
    341     {left,  params_acc} = escape(left, type, params_acc, vars, env)
    342     {right, params_acc} = escape(right, type, params_acc, vars, env)
    343 
    344     {{:{}, [], [math_op, [], [left, right]]}, params_acc}
    345   end
    346 
    347   # in operator
    348   def escape({:in, _, [left, right]} = expr, type, params_acc, vars, env)
    349       when is_list(right)
    350       when is_tuple(right) and elem(right, 0) in ~w(sigil_w sigil_W)a do
    351     assert_type!(expr, type, :boolean)
    352 
    353     {:array, ltype} = quoted_type(right, vars)
    354     rtype = {:array, quoted_type(left, vars)}
    355 
    356     {left, params_acc} = escape(left, ltype, params_acc, vars, env)
    357     {right, params_acc} = escape(right, rtype, params_acc, vars, env)
    358     {{:{}, [], [:in, [], [left, right]]}, params_acc}
    359   end
    360 
    361   def escape({:in, _, [left, right]} = expr, type, params_acc, vars, env) do
    362     assert_type!(expr, type, :boolean)
    363 
    364     ltype = {:out, quoted_type(right, vars)}
    365     rtype = {:in, quoted_type(left, vars)}
    366 
    367     {left, params_acc} = escape(left, ltype, params_acc, vars, env)
    368     {right, params_acc} = escape(right, rtype, params_acc, vars, env)
    369 
    370     # Remove any type wrapper from the right side
    371     right =
    372       case right do
    373         {:{}, [], [:type, [], [right, _]]} -> right
    374         _ -> right
    375       end
    376 
    377     {{:{}, [], [:in, [], [left, right]]}, params_acc}
    378   end
    379 
    380   def escape({:count, _, [arg, :distinct]}, type, params_acc, vars, env) do
    381     {arg, params_acc} = escape(arg, type, params_acc, vars, env)
    382     expr = {:{}, [], [:count, [], [arg, :distinct]]}
    383     {expr, params_acc}
    384   end
    385 
    386   def escape({:filter, _, [aggregate]}, type, params_acc, vars, env) do
    387     escape(aggregate, type, params_acc, vars, env)
    388   end
    389 
    390   def escape({:filter, _, [aggregate, filter_expr]}, type, params_acc, vars, env) do
    391     {aggregate, params_acc} = escape(aggregate, type, params_acc, vars, env)
    392     {filter_expr, params_acc} = escape(filter_expr, :boolean, params_acc, vars, env)
    393     {{:{}, [], [:filter, [], [aggregate, filter_expr]]}, params_acc}
    394   end
    395 
    396   def escape({:coalesce, _, [left, right]}, type, params_acc, vars, env) do
    397     {left, params_acc} = escape(left, type, params_acc, vars, env)
    398     {right, params_acc} = escape(right, type, params_acc, vars, env)
    399     {{:{}, [], [:coalesce, [], [left, right]]}, params_acc}
    400   end
    401 
    402   def escape({:over, _, [{agg_name, _, agg_args} | over_args]}, type, params_acc, vars, env) do
    403     aggregate = {agg_name, [], agg_args || []}
    404     {aggregate, params_acc} = escape_window_function(aggregate, type, params_acc, vars, env)
    405     {window, params_acc} = escape_window_description(over_args, params_acc, vars, env)
    406     {{:{}, [], [:over, [], [aggregate, window]]}, params_acc}
    407   end
    408 
    409   def escape({:selected_as, _, [_expr, _name]}, _type, _params_acc, _vars, _env) do
    410     error! """
    411     selected_as/2 can only be used at the root of a select statement. \
    412     If you are trying to use it inside of an expression, consider putting the \
    413     expression inside of `selected_as/2` instead. For instance, instead of:
    414 
    415         from p in Post, select: coalesce(selected_as(p.visits, :v), 0)
    416 
    417     use:
    418 
    419         from p in Post, select: selected_as(coalesce(p.visits, 0), :v)
    420     """
    421   end
    422 
    423   def escape({:selected_as, _, [name]}, _type, params_acc, _vars, _env) when is_atom(name) do
    424     expr = {:{}, [], [:selected_as, [], [name]]}
    425     {expr, params_acc}
    426   end
    427 
    428   def escape({:selected_as, _, [name]}, _type, _params_acc, _vars, _env) do
    429     error! "selected_as/1 expects `name` to be an atom, got `#{inspect(name)}`"
    430   end
    431 
    432   def escape({quantifier, meta, [subquery]}, type, params_acc, vars, env) when quantifier in [:all, :any, :exists] do
    433     {subquery, params_acc} = escape({:subquery, meta, [subquery]}, type, params_acc, vars, env)
    434     {{:{}, [], [quantifier, [], [subquery]]}, params_acc}
    435   end
    436 
    437   def escape({:=, _, _} = expr, _type, _params_acc, _vars, _env) do
    438     error! "`#{Macro.to_string(expr)}` is not a valid query expression. " <>
    439             "The match operator is not supported: `=`. " <>
    440             "Did you mean to use `==` instead?"
    441   end
    442 
    443   def escape({op, _, _}, _type, _params_acc, _vars, _env) when op in ~w(|| && !)a do
    444     error! "short-circuit operators are not supported: `#{op}`. " <>
    445            "Instead use boolean operators: `and`, `or`, and `not`"
    446   end
    447 
    448   # Tuple
    449   def escape({left, right}, type, params_acc, vars, env) do
    450     escape({:{}, [], [left, right]}, type, params_acc, vars, env)
    451   end
    452 
    453   # Tuple
    454   def escape({:{}, _, list}, {:tuple, types}, params_acc, vars, env) do
    455     if Enum.count(list) == Enum.count(types) do
    456       {list, params_acc} =
    457         list
    458         |> Enum.zip(types)
    459         |> Enum.map_reduce(params_acc, fn {expr, type}, params_acc ->
    460              escape(expr, type, params_acc, vars, env)
    461            end)
    462       expr = {:{}, [], [:{}, [], list]}
    463       {expr, params_acc}
    464     else
    465       escape({:{}, [], list}, :any, params_acc, vars, env)
    466     end
    467   end
    468 
    469   # Tuple
    470   def escape({:{}, _, _}, _, _, _, _) do
    471     error! "Tuples can only be used in comparisons with literal tuples of the same size"
    472   end
    473 
    474   # Unnecessary parentheses around an expression
    475   def escape({:__block__, _, [expr]}, type, params_acc, vars, env) do
    476     escape(expr, type, params_acc, vars, env)
    477   end
    478 
    479   # Other functions - no type casting
    480   def escape({name, _, args} = expr, type, params_acc, vars, env) when is_atom(name) and is_list(args) do
    481     case call_type(name, length(args)) do
    482       {in_type, out_type} ->
    483         assert_type!(expr, type, out_type)
    484         escape_call(expr, in_type, params_acc, vars, env)
    485       nil ->
    486         try_expansion(expr, type, params_acc, vars, env)
    487     end
    488   end
    489 
    490   # Finally handle vars
    491   def escape({var, _, context}, _type, params_acc, vars, _env) when is_atom(var) and is_atom(context) do
    492     {escape_var!(var, vars), params_acc}
    493   end
    494 
    495   # Raise nice error messages for fun calls.
    496   def escape({fun, _, args} = other, _type, _params_acc, _vars, _env)
    497       when is_atom(fun) and is_list(args) do
    498     error! """
    499     `#{Macro.to_string(other)}` is not a valid query expression. \
    500     If you are trying to invoke a function that is not supported by Ecto, \
    501     you can use fragments:
    502 
    503         fragment("some_function(?, ?, ?)", m.some_field, 1)
    504 
    505     See Ecto.Query.API to learn more about the supported functions and \
    506     Ecto.Query.API.fragment/1 to learn more about fragments.
    507     """
    508   end
    509 
    510   # Raise nice error message for remote calls
    511   def escape({{:., _, [_, fun]}, _, _} = other, type, params_acc, vars, env)
    512       when is_atom(fun) do
    513     try_expansion(other, type, params_acc, vars, env)
    514   end
    515 
    516   # For everything else we raise
    517   def escape(other, _type, _params_acc, _vars, _env) do
    518     error! "`#{Macro.to_string(other)}` is not a valid query expression"
    519   end
    520 
    521   defp escape_with_type(expr, {:^, _, [type]}, params_acc, vars, env) do
    522     {expr, params_acc} = escape(expr, :any, params_acc, vars, env)
    523     {{:{}, [], [:type, [], [expr, type]]}, params_acc}
    524   end
    525 
    526   defp escape_with_type(expr, type, params_acc, vars, env) do
    527     type = validate_type!(type, vars, env)
    528     {expr, params_acc} = escape(expr, type, params_acc, vars, env)
    529     {{:{}, [], [:type, [], [expr, escape_type(type)]]}, params_acc}
    530   end
    531 
    532   defp escape_type({:parameterized, _, _} = param), do: Macro.escape(param)
    533   defp escape_type(type), do: type
    534 
    535   defp wrap_nil(params, {:{}, _, [:^, _, [ix]]}), do: wrap_nil(params, length(params) - ix - 1, [])
    536   defp wrap_nil(params, _other), do: params
    537 
    538   defp wrap_nil([{val, type} | params], 0, acc) do
    539     val = quote do: Ecto.Query.Builder.not_nil!(unquote(val))
    540     Enum.reverse(acc, [{val, type} | params])
    541   end
    542 
    543   defp wrap_nil([pair | params], i, acc) do
    544     wrap_nil(params, i - 1, [pair | acc])
    545   end
    546 
    547   defp expand_and_split_fragment(query, env) do
    548     case Macro.expand(query, get_env(env)) do
    549       binary when is_binary(binary) ->
    550         split_fragment(binary, "")
    551 
    552       _ ->
    553         error! bad_fragment_message(Macro.to_string(query))
    554     end
    555   end
    556 
    557   defp bad_fragment_message(arg) do
    558     "to prevent SQL injection attacks, fragment(...) does not allow strings " <>
    559       "to be interpolated as the first argument via the `^` operator, got: `#{arg}`"
    560   end
    561 
    562   defp split_fragment(<<>>, consumed),
    563     do: [consumed]
    564   defp split_fragment(<<??, rest :: binary>>, consumed),
    565     do: [consumed | split_fragment(rest, "")]
    566   defp split_fragment(<<?\\, ??, rest :: binary>>, consumed),
    567     do: split_fragment(rest, consumed <> <<??>>)
    568   defp split_fragment(<<first :: utf8, rest :: binary>>, consumed),
    569     do: split_fragment(rest, consumed <> <<first :: utf8>>)
    570 
    571   @doc "Returns fragment pieces, given a fragment string and arguments."
    572   def fragment_pieces(frag, args) do
    573     frag
    574     |> split_fragment("")
    575     |> merge_fragments(args)
    576   end
    577 
    578   defp escape_window_description([], params_acc, _vars, _env),
    579     do: {[], params_acc}
    580   defp escape_window_description([window_name], params_acc, _vars, _env) when is_atom(window_name),
    581     do: {window_name, params_acc}
    582   defp escape_window_description([kw], params_acc, vars, env) do
    583     case Ecto.Query.Builder.Windows.escape(kw, params_acc, vars, env) do
    584       {runtime, [], params_acc} ->
    585         {runtime, params_acc}
    586 
    587       {_, [{key, _} | _], _} ->
    588         error! "windows definitions given to over/2 do not allow interpolations at the root of " <>
    589                  "`#{key}`. Please use Ecto.Query.windows/3 to explicitly define a window instead"
    590     end
    591   end
    592 
    593   defp escape_window_function(expr, type, params_acc, vars, env) do
    594     expr
    595     |> validate_window_function!(env)
    596     |> escape(type, params_acc, vars, env)
    597   end
    598 
    599   defp validate_window_function!({:fragment, _, _} = expr, _env), do: expr
    600 
    601   defp validate_window_function!({agg, _, args} = expr, env)
    602        when is_atom(agg) and is_list(args) do
    603     if Code.ensure_loaded?(Ecto.Query.WindowAPI) and
    604          function_exported?(Ecto.Query.WindowAPI, agg, length(args)) do
    605       expr
    606     else
    607       case Macro.expand_once(expr, get_env(env)) do
    608         ^expr ->
    609           error! "unknown window function #{agg}/#{length(args)}. " <>
    610                    "See Ecto.Query.WindowAPI for all available functions"
    611         expr ->
    612           validate_window_function!(expr, env)
    613       end
    614     end
    615   end
    616 
    617   defp validate_window_function!(expr, _), do: expr
    618 
    619   defp escape_call({name, _, args}, type, params_acc, vars, env) do
    620     {args, params_acc} = Enum.map_reduce(args, params_acc, &escape(&1, type, &2, vars, env))
    621     expr = {:{}, [], [name, [], args]}
    622     {expr, params_acc}
    623   end
    624 
    625   defp escape_field!({var, _, context}, field, vars)
    626        when is_atom(var) and is_atom(context) do
    627     var   = escape_var!(var, vars)
    628     field = quoted_atom!(field, "field/2")
    629     dot   = {:{}, [], [:., [], [var, field]]}
    630     {:{}, [], [dot, [], []]}
    631   end
    632 
    633   defp escape_field!({kind, _, [value]}, field, _vars)
    634        when kind in [:as, :parent_as] do
    635     value =
    636       case value do
    637         {:^, _, [value]} ->
    638           value
    639 
    640         other ->
    641           quoted_atom!(other, "#{kind}/1")
    642       end
    643     as    = {:{}, [], [kind, [], [value]]}
    644     field = quoted_atom!(field, "field/2")
    645     dot   = {:{}, [], [:., [], [as, field]]}
    646     {:{}, [], [dot, [], []]}
    647   end
    648 
    649   defp escape_field!(expr, field, _vars) do
    650     error!("""
    651     cannot fetch field `#{field}` from `#{Macro.to_string(expr)}`. Can only fetch fields from:
    652 
    653       * sources, such as `p` in `from p in Post`
    654       * named bindings, such as `as(:post)` in `from Post, as: :post`
    655       * parent named bindings, such as `parent_as(:post)` in a subquery
    656     """)
    657   end
    658 
    659   defp escape_interval(count, interval, params_acc, vars, env) do
    660     type =
    661       cond do
    662         is_float(count)   -> :float
    663         is_integer(count) -> :integer
    664         true              -> :decimal
    665       end
    666 
    667     {count, params_acc} = escape(count, type, params_acc, vars, env)
    668     {count, quoted_interval!(interval), params_acc}
    669   end
    670 
    671   defp escape_kw_fragment({key, [{_, _}|_] = exprs}, params_acc, vars, env) when is_atom(key) do
    672     {escaped, params_acc} = Enum.map_reduce(exprs, params_acc, &escape_kw_fragment(&1, &2, vars, env))
    673     {{key, escaped}, params_acc}
    674   end
    675 
    676   defp escape_kw_fragment({key, expr}, params_acc, vars, env) when is_atom(key) do
    677     {escaped, params_acc} = escape(expr, :any, params_acc, vars, env)
    678     {{key, escaped}, params_acc}
    679   end
    680 
    681   defp escape_kw_fragment({key, _expr}, _params_acc, _vars, _env) do
    682     error! "fragment(...) with keywords accepts only atoms as keys, got `#{Macro.to_string(key)}`"
    683   end
    684 
    685   defp escape_fragment({:literal, _meta, [expr]}, params_acc, _vars, _env) do
    686     case expr do
    687       {:^, _, [expr]} ->
    688         checked = quote do: Ecto.Query.Builder.literal!(unquote(expr))
    689         escaped = {:{}, [], [:literal, [], [checked]]}
    690         {escaped, params_acc}
    691 
    692       _ ->
    693         error! "literal/1 in fragment expects an interpolated value, such as literal(^value), got `#{Macro.to_string(expr)}`"
    694     end
    695   end
    696 
    697   defp escape_fragment(expr, params_acc, vars, env) do
    698     escape(expr, :any, params_acc, vars, env)
    699   end
    700 
    701   defp merge_fragments([h1|t1], [h2|t2]),
    702     do: [{:raw, h1}, {:expr, h2} | merge_fragments(t1, t2)]
    703 
    704   defp merge_fragments([h1], []),
    705     do: [{:raw, h1}]
    706 
    707   for {agg, arity} <- @dynamic_aggregates do
    708     defp call_type(unquote(agg), unquote(arity)), do: {:any, :any}
    709   end
    710 
    711   for {agg, {arity, return}} <- @static_aggregates do
    712     defp call_type(unquote(agg), unquote(arity)), do: {:any, unquote(return)}
    713   end
    714 
    715   for {comp, arity} <- @comparisons do
    716     defp call_type(unquote(comp), unquote(arity)), do: {:any, :boolean}
    717   end
    718 
    719   defp call_type(:or, 2), do: {:boolean, :boolean}
    720   defp call_type(:and, 2), do: {:boolean, :boolean}
    721   defp call_type(:not, 1), do: {:boolean, :boolean}
    722   defp call_type(:like, 2), do: {:string, :boolean}
    723   defp call_type(:ilike, 2), do: {:string, :boolean}
    724   defp call_type(_, _), do: nil
    725 
    726   defp assert_type!(expr, type, actual) do
    727     cond do
    728       not is_atom(type) and not Ecto.Type.primitive?(type) ->
    729         :ok
    730 
    731       Ecto.Type.match?(type, actual) ->
    732         :ok
    733 
    734       true ->
    735         error! "expression `#{Macro.to_string(expr)}` does not type check. " <>
    736                "It returns a value of type #{inspect actual} but a value of " <>
    737                "type #{inspect type} is expected"
    738     end
    739   end
    740 
    741   @doc """
    742   Validates the type with the given vars.
    743   """
    744   def validate_type!({composite, type}, vars, env),
    745     do: {composite, validate_type!(type, vars, env)}
    746   def validate_type!({:^, _, [type]}, _vars, _env),
    747     do: type
    748   def validate_type!({:__aliases__, _, _} = type, _vars, env),
    749     do: Macro.expand(type, get_env(env))
    750   def validate_type!({:parameterized, _, _} = type, _vars, _env),
    751     do: type
    752   def validate_type!(type, _vars, _env) when is_atom(type),
    753     do: type
    754   def validate_type!({{:., _, [{var, _, context}, field]}, _, []}, vars, _env)
    755     when is_atom(var) and is_atom(context) and is_atom(field),
    756     do: {find_var!(var, vars), field}
    757   def validate_type!({:field, _, [{var, _, context}, field]}, vars, _env)
    758     when is_atom(var) and is_atom(context) and is_atom(field),
    759     do: {find_var!(var, vars), field}
    760 
    761   def validate_type!(type, _vars, _env) do
    762     error! "type/2 expects an alias, atom, initialized parameterized type or " <>
    763            "source.field as second argument, got: `#{Macro.to_string(type)}`"
    764   end
    765 
    766   @always_tagged [:binary]
    767 
    768   defp literal(value, expected, vars),
    769     do: do_literal(value, expected, quoted_type(value, vars))
    770 
    771   defp do_literal(value, _, current) when current in @always_tagged,
    772     do: {:%, [], [Ecto.Query.Tagged, {:%{}, [], [value: value, type: current]}]}
    773   defp do_literal(value, :any, _current),
    774     do: value
    775   defp do_literal(value, expected, expected),
    776     do: value
    777   defp do_literal(value, expected, _current),
    778     do: {:%, [], [Ecto.Query.Tagged, {:%{}, [], [value: value, type: expected]}]}
    779 
    780   @doc """
    781   Escape the params entries list.
    782   """
    783   @spec escape_params(list()) :: list()
    784   def escape_params(list), do: Enum.reverse(list)
    785 
    786   @doc """
    787   Escape the select alias map
    788   """
    789   @spec escape_select_aliases(map()) :: Macro.t
    790   def escape_select_aliases(%{} = aliases), do: {:%{}, [], Map.to_list(aliases)}
    791 
    792   @doc """
    793   Escapes a variable according to the given binds.
    794 
    795   A escaped variable is represented internally as
    796   `&0`, `&1` and so on.
    797   """
    798   @spec escape_var!(atom, Keyword.t) :: Macro.t
    799   def escape_var!(var, vars) do
    800     {:{}, [], [:&, [], [find_var!(var, vars)]]}
    801   end
    802 
    803   @doc """
    804   Escapes a list of bindings as a list of atoms.
    805 
    806   Only variables or `{:atom, value}` tuples are allowed in the `bindings` list,
    807   otherwise an `Ecto.Query.CompileError` is raised.
    808 
    809   ## Examples
    810 
    811       iex> escape_binding(%Ecto.Query{}, quote(do: [x, y, z]), __ENV__)
    812       {%Ecto.Query{}, [x: 0, y: 1, z: 2]}
    813 
    814       iex> escape_binding(%Ecto.Query{}, quote(do: [{x, 0}, {z, 2}]), __ENV__)
    815       {%Ecto.Query{}, [x: 0, z: 2]}
    816 
    817       iex> escape_binding(%Ecto.Query{}, quote(do: [x, y, x]), __ENV__)
    818       ** (Ecto.Query.CompileError) variable `x` is bound twice
    819 
    820       iex> escape_binding(%Ecto.Query{}, quote(do: [a, b, :foo]), __ENV__)
    821       ** (Ecto.Query.CompileError) binding list should contain only variables or `{as, var}` tuples, got: :foo
    822 
    823   """
    824   @spec escape_binding(Macro.t, list, Macro.Env.t) :: {Macro.t, Keyword.t}
    825   def escape_binding(query, binding, _env) when is_list(binding) do
    826     vars = binding |> Enum.with_index |> Enum.map(&escape_bind/1)
    827     assert_no_duplicate_binding!(vars)
    828 
    829     {positional_vars, named_vars} = Enum.split_while(vars, &not named_bind?(&1))
    830     assert_named_binds_in_tail!(named_vars, binding)
    831 
    832     {query, positional_binds} = calculate_positional_binds(query, positional_vars)
    833     {query, named_binds} = calculate_named_binds(query, named_vars)
    834     {query, positional_binds ++ named_binds}
    835   end
    836   def escape_binding(_query, bind, _env) do
    837     error! "binding should be list of variables and `{as, var}` tuples " <>
    838              "at the end, got: #{Macro.to_string(bind)}"
    839   end
    840 
    841   defp named_bind?({kind, _, _}), do: kind == :named
    842 
    843   defp assert_named_binds_in_tail!(named_vars, binding) do
    844     if Enum.all?(named_vars, &named_bind?/1) do
    845       :ok
    846     else
    847       error! "named binds in the form of `{as, var}` tuples must be at the end " <>
    848                "of the binding list, got: #{Macro.to_string(binding)}"
    849     end
    850   end
    851 
    852   defp assert_no_duplicate_binding!(vars) do
    853     bound_vars = for {_, var, _} <- vars, var != :_, do: var
    854 
    855     case bound_vars -- Enum.uniq(bound_vars) do
    856       []  -> :ok
    857       [var | _] -> error! "variable `#{var}` is bound twice"
    858     end
    859   end
    860 
    861   defp calculate_positional_binds(query, vars) do
    862     case Enum.split_while(vars, &elem(&1, 1) != :...) do
    863       {vars, []} ->
    864         vars = for {:pos, var, count} <- vars, do: {var, count}
    865         {query, vars}
    866       {vars, [_ | tail]} ->
    867         query =
    868           quote do
    869             query = Ecto.Queryable.to_query(unquote(query))
    870             escape_count = Ecto.Query.Builder.count_binds(query)
    871             query
    872           end
    873 
    874         tail =
    875           tail
    876           |> Enum.with_index(-length(tail))
    877           |> Enum.map(fn {{:pos, k, _}, count} -> {k, quote(do: escape_count + unquote(count))} end)
    878 
    879         vars = for {:pos, var, count} <- vars, do: {var, count}
    880         {query, vars ++ tail}
    881     end
    882   end
    883 
    884   defp calculate_named_binds(query, []), do: {query, []}
    885   defp calculate_named_binds(query, vars) do
    886     assignments =
    887       for {:named, key, name} <- vars do
    888         quote do
    889           unquote({key, [], __MODULE__}) = unquote(__MODULE__).count_alias!(query, unquote(name))
    890         end
    891       end
    892 
    893     query =
    894       quote do
    895         query = Ecto.Queryable.to_query(unquote(query))
    896         unquote_splicing(assignments)
    897         query
    898       end
    899 
    900     pairs =
    901       for {:named, key, _name} <- vars do
    902         {key, {key, [], __MODULE__}}
    903       end
    904 
    905     {query, pairs}
    906   end
    907 
    908   @doc """
    909   Count the alias for the given query.
    910   """
    911   def count_alias!(%{aliases: aliases} = query, name) do
    912     case aliases do
    913       %{^name => ix} ->
    914         ix
    915 
    916       %{} ->
    917         raise Ecto.QueryError, message: "unknown bind name `#{inspect name}`", query: query
    918     end
    919   end
    920 
    921   defp escape_bind({{{var, _, context}, ix}, _}) when is_atom(var) and is_atom(context),
    922     do: {:pos, var, ix}
    923   defp escape_bind({{var, _, context}, ix}) when is_atom(var) and is_atom(context),
    924     do: {:pos, var, ix}
    925   defp escape_bind({{name, {var, _, context}}, _ix}) when is_atom(name) and is_atom(var) and is_atom(context),
    926     do: {:named, var, name}
    927   defp escape_bind({{{:^, _, [expr]}, {var, _, context}}, _ix}) when is_atom(var) and is_atom(context),
    928     do: {:named, var, expr}
    929   defp escape_bind({bind, _ix}),
    930     do: error!("binding list should contain only variables or " <>
    931           "`{as, var}` tuples, got: #{Macro.to_string(bind)}")
    932 
    933   defp try_expansion(expr, type, params, vars, %Macro.Env{} = env) do
    934     try_expansion(expr, type, params, vars, {env, &escape/5})
    935   end
    936 
    937   defp try_expansion(expr, type, params, vars, {env, fun}) do
    938     case Macro.expand_once(expr, env) do
    939       ^expr ->
    940         error! """
    941         `#{Macro.to_string(expr)}` is not a valid query expression.
    942 
    943         * If you intended to call an Elixir function or introduce a value,
    944           you need to explicitly interpolate it with ^
    945 
    946         * If you intended to call a database function, please check the documentation
    947           for Ecto.Query.API to see the supported database expressions
    948 
    949         * If you intended to extend Ecto's query DSL, make sure that you have required
    950           the module or imported the relevant function. Note that you need macros to
    951           extend Ecto's querying capabilities
    952         """
    953 
    954       expanded ->
    955         fun.(expanded, type, params, vars, env)
    956     end
    957   end
    958 
    959   @doc """
    960   Finds the index value for the given var in vars or raises.
    961   """
    962   def find_var!(var, vars) do
    963     vars[var] || error! "unbound variable `#{var}` in query. If you are attempting to interpolate a value, use ^var"
    964   end
    965 
    966   @doc """
    967   Checks if the field is an atom at compilation time or
    968   delegate the check to runtime for interpolation.
    969   """
    970   def quoted_atom!({:^, _, [expr]}, used_ref),
    971     do: quote(do: Ecto.Query.Builder.atom!(unquote(expr), unquote(used_ref)))
    972 
    973   def quoted_atom!(atom, _used_ref) when is_atom(atom),
    974     do: atom
    975 
    976   def quoted_atom!(other, used_ref),
    977     do:
    978       error!(
    979         "expected literal atom or interpolated value in #{used_ref}, got: " <>
    980         "`#{Macro.to_string(other)}`"
    981       )
    982 
    983   @doc """
    984   Called by escaper at runtime to verify that value is an atom.
    985   """
    986   def atom!(atom, _used_ref) when is_atom(atom),
    987     do: atom
    988 
    989   def atom!(other, used_ref),
    990     do: error!("expected atom in #{used_ref}, got: `#{inspect other}`")
    991 
    992   defp escape_json_path(path) when is_list(path) do
    993     Enum.map(path, &quoted_json_path_element!/1)
    994   end
    995 
    996   defp escape_json_path(other) do
    997     error!("expected JSON path to be compile-time list, got: `#{Macro.to_string(other)}`")
    998   end
    999 
   1000   defp quoted_json_path_element!({:^, _, [expr]}),
   1001     do: quote(do: Ecto.Query.Builder.json_path_element!(unquote(expr)))
   1002 
   1003   defp quoted_json_path_element!(binary) when is_binary(binary),
   1004     do: binary
   1005 
   1006   defp quoted_json_path_element!(integer) when is_integer(integer),
   1007     do: integer
   1008 
   1009   defp quoted_json_path_element!(other),
   1010     do:
   1011       error!(
   1012         "expected JSON path to contain literal strings, literal integers, or interpolated values, got: " <>
   1013         "`#{Macro.to_string(other)}`"
   1014       )
   1015 
   1016   @doc """
   1017   Called by escaper at runtime to verify that value is a string or an integer.
   1018   """
   1019   def json_path_element!(binary) when is_binary(binary),
   1020     do: binary
   1021   def json_path_element!(integer) when is_integer(integer),
   1022     do: integer
   1023   def json_path_element!(other),
   1024     do: error!("expected string or integer in json_extract_path/2, got: `#{inspect other}`")
   1025 
   1026   @doc """
   1027   Called by escaper at runtime to verify that a value is not nil.
   1028   """
   1029   def not_nil!(nil) do
   1030     raise ArgumentError, "comparison with nil is forbidden as it is unsafe. " <>
   1031                          "If you want to check if a value is nil, use is_nil/1 instead"
   1032   end
   1033   def not_nil!(not_nil) do
   1034     not_nil
   1035   end
   1036 
   1037   @doc """
   1038   Checks if the field is a valid interval at compilation time or
   1039   delegate the check to runtime for interpolation.
   1040   """
   1041   def quoted_interval!({:^, _, [expr]}),
   1042     do: quote(do: Ecto.Query.Builder.interval!(unquote(expr)))
   1043   def quoted_interval!(other),
   1044     do: interval!(other)
   1045 
   1046   @doc """
   1047   Called by escaper at runtime to verify fragment keywords.
   1048   """
   1049   def fragment!(kw) do
   1050     if Keyword.keyword?(kw) do
   1051       kw
   1052     else
   1053       raise ArgumentError, bad_fragment_message(inspect(kw))
   1054     end
   1055   end
   1056 
   1057   @doc """
   1058   Called by escaper at runtime to verify literal in fragments.
   1059   """
   1060   def literal!(literal) do
   1061     if is_binary(literal) do
   1062       literal
   1063     else
   1064       raise ArgumentError,
   1065             "literal(^value) expects `value` to be a string, got `#{inspect(literal)}`"
   1066     end
   1067   end
   1068 
   1069   @doc """
   1070   Called by escaper at runtime to verify that value is a valid interval.
   1071   """
   1072   @interval ~w(year month week day hour minute second millisecond microsecond)
   1073   def interval!(interval) when interval in @interval,
   1074     do: interval
   1075   def interval!(other_string) when is_binary(other_string),
   1076     do: error!("invalid interval: `#{inspect other_string}` (expected one of #{Enum.join(@interval, ", ")})")
   1077   def interval!(not_string),
   1078     do: error!("invalid interval: `#{inspect not_string}` (expected a string)")
   1079 
   1080   @doc """
   1081   Negates the given number.
   1082   """
   1083   # TODO: Remove check when we depend on decimal v2.0
   1084   if Code.ensure_loaded?(Decimal) and function_exported?(Decimal, :negate, 1) do
   1085     def negate!(%Decimal{} = decimal), do: Decimal.negate(decimal)
   1086   else
   1087     def negate!(%Decimal{} = decimal), do: Decimal.minus(decimal)
   1088   end
   1089 
   1090   def negate!(number) when is_number(number), do: -number
   1091 
   1092   @doc """
   1093   Returns the type of an expression at build time.
   1094   """
   1095   @spec quoted_type(Macro.t, Keyword.t) :: quoted_type
   1096 
   1097   # Fields
   1098   def quoted_type({{:., _, [{var, _, context}, field]}, _, []}, vars)
   1099     when is_atom(var) and is_atom(context) and is_atom(field),
   1100     do: {find_var!(var, vars), field}
   1101 
   1102   def quoted_type({:field, _, [{var, _, context}, field]}, vars)
   1103     when is_atom(var) and is_atom(context) and is_atom(field),
   1104     do: {find_var!(var, vars), field}
   1105 
   1106   # Unquoting code here means the second argument of field will
   1107   # always be unquoted twice, one by the type checking and another
   1108   # in the query itself. We are assuming this is not an issue
   1109   # as the solution is somewhat complicated.
   1110   def quoted_type({:field, _, [{var, _, context}, {:^, _, [code]}]}, vars)
   1111     when is_atom(var) and is_atom(context),
   1112     do: {find_var!(var, vars), code}
   1113 
   1114   # Interval
   1115   def quoted_type({:datetime_add, _, [_, _, _]}, _vars), do: :naive_datetime
   1116   def quoted_type({:date_add, _, [_, _, _]}, _vars), do: :date
   1117 
   1118   # Tagged
   1119   def quoted_type({:<<>>, _, _}, _vars), do: :binary
   1120   def quoted_type({:type, _, [_, type]}, _vars), do: type
   1121 
   1122   # Sigils
   1123   def quoted_type({sigil, _, [_, []]}, _vars) when sigil in ~w(sigil_s sigil_S)a, do: :string
   1124   def quoted_type({sigil, _, [_, []]}, _vars) when sigil in ~w(sigil_w sigil_W)a, do: {:array, :string}
   1125 
   1126   # Lists
   1127   def quoted_type(list, vars) when is_list(list) do
   1128     case list |> Enum.map(&quoted_type(&1, vars)) |> Enum.uniq() do
   1129       [type] -> {:array, type}
   1130       _ -> {:array, :any}
   1131     end
   1132   end
   1133 
   1134   # Negative numbers
   1135   def quoted_type({:-, _, [number]}, _vars) when is_integer(number), do: :integer
   1136   def quoted_type({:-, _, [number]}, _vars) when is_float(number), do: :float
   1137 
   1138   # Dynamic aggregates
   1139   for {agg, arity} <- @dynamic_aggregates do
   1140     args = 1..arity |> Enum.map(fn _ -> Macro.var(:_, __MODULE__) end) |> tl()
   1141 
   1142     def quoted_type({unquote(agg), _, [expr, unquote_splicing(args)]}, vars) do
   1143       quoted_type(expr, vars)
   1144     end
   1145   end
   1146 
   1147   # Literals
   1148   def quoted_type(literal, _vars) when is_float(literal),   do: :float
   1149   def quoted_type(literal, _vars) when is_binary(literal),  do: :string
   1150   def quoted_type(literal, _vars) when is_boolean(literal), do: :boolean
   1151   def quoted_type(literal, _vars) when is_atom(literal) and not is_nil(literal), do: :atom
   1152   def quoted_type(literal, _vars) when is_integer(literal), do: :integer
   1153 
   1154   # Tuples
   1155   def quoted_type({left, right}, vars), do: quoted_type({:{}, [], [left, right]}, vars)
   1156   def quoted_type({:{}, _, elems}, vars), do: {:tuple, Enum.map(elems, &quoted_type(&1, vars))}
   1157 
   1158   def quoted_type({name, _, args}, _vars) when is_atom(name) and is_list(args) do
   1159     case call_type(name, length(args)) do
   1160       {_in, out} -> out
   1161       nil        -> :any
   1162     end
   1163   end
   1164 
   1165   def quoted_type(_, _vars), do: :any
   1166 
   1167   defp get_env({env, _}), do: env
   1168   defp get_env(env), do: env
   1169 
   1170   @doc """
   1171   Raises a query building error.
   1172   """
   1173   def error!(message) when is_binary(message) do
   1174     {:current_stacktrace, [_|t]} = Process.info(self(), :current_stacktrace)
   1175 
   1176     t = Enum.drop_while t, fn
   1177       {mod, _, _, _} ->
   1178         String.starts_with?(Atom.to_string(mod), ["Elixir.Ecto.Query.", "Elixir.Enum"])
   1179       _ ->
   1180         false
   1181     end
   1182 
   1183     reraise Ecto.Query.CompileError, [message: message], t
   1184   end
   1185 
   1186   @doc """
   1187   Counts the bindings in a query expression.
   1188 
   1189   ## Examples
   1190 
   1191       iex> count_binds(%Ecto.Query{joins: [1,2,3]})
   1192       4
   1193 
   1194   """
   1195   @spec count_binds(Ecto.Query.t) :: non_neg_integer
   1196   def count_binds(%Query{joins: joins}) do
   1197     1 + length(joins)
   1198   end
   1199 
   1200   @doc """
   1201   Bump interpolations by the length of parameters.
   1202   """
   1203   def bump_interpolations(expr, []), do: expr
   1204 
   1205   def bump_interpolations(expr, params) do
   1206     len = length(params)
   1207 
   1208     Macro.prewalk(expr, fn
   1209       {:^, meta, [counter]} when is_integer(counter) -> {:^, meta, [len + counter]}
   1210       other -> other
   1211     end)
   1212   end
   1213 
   1214   @doc """
   1215   Bump subqueries by the count of pre-existing subqueries.
   1216   """
   1217   def bump_subqueries(expr, []), do: expr
   1218 
   1219   def bump_subqueries(expr, subqueries) do
   1220     len = length(subqueries)
   1221 
   1222     Macro.prewalk(expr, fn
   1223       {:subquery, counter} -> {:subquery, len + counter}
   1224       other -> other
   1225     end)
   1226   end
   1227 
   1228   @doc """
   1229   Called by the select escaper at compile time and dynamic builder at runtime to track select aliases
   1230   """
   1231   def add_select_alias(aliases, name) do
   1232     case aliases do
   1233       %{^name => _} ->
   1234         error! "the alias `#{inspect(name)}` has been specified more than once using `selected_as/2`"
   1235 
   1236       aliases ->
   1237         Map.put(aliases, name, @select_alias_dummy_value)
   1238     end
   1239   end
   1240 
   1241   @doc """
   1242   Applies a query at compilation time or at runtime.
   1243 
   1244   This function is responsible for checking if a given query is an
   1245   `Ecto.Query` struct at compile time. If it is not it will act
   1246   accordingly.
   1247 
   1248   If a query is available, it invokes the `apply` function in the
   1249   given `module`, otherwise, it delegates the call to runtime.
   1250 
   1251   It is important to keep in mind the complexities introduced
   1252   by this function. In particular, a %Query{} is a mixture of escaped
   1253   and unescaped expressions which makes it impossible for this
   1254   function to properly escape or unescape it at compile/runtime.
   1255   For this reason, the apply function should be ready to handle
   1256   arguments in both escaped and unescaped form.
   1257 
   1258   For example, take into account the `Builder.OrderBy`:
   1259 
   1260       select = %Ecto.Query.QueryExpr{expr: expr, file: env.file, line: env.line}
   1261       Builder.apply_query(query, __MODULE__, [order_by], env)
   1262 
   1263   `expr` is already an escaped expression and we must not escape
   1264   it again. However, it is wrapped in an Ecto.Query.QueryExpr,
   1265   which must be escaped! Furthermore, the `apply/2` function
   1266   in `Builder.OrderBy` very likely will inject the QueryExpr inside
   1267   Query, which again, is a mixture of escaped and unescaped expressions.
   1268 
   1269   That said, you need to obey the following rules:
   1270 
   1271   1. In order to call this function, the arguments must be escapable
   1272      values supported by the `escape/1` function below;
   1273 
   1274   2. The apply function may not manipulate the given arguments,
   1275      with exception to the query.
   1276 
   1277   In particular, when invoked at compilation time, all arguments
   1278   (except the query) will be escaped, so they can be injected into
   1279   the query properly, but they will be in their runtime form
   1280   when invoked at runtime.
   1281   """
   1282   @spec apply_query(Macro.t, Macro.t, Macro.t, Macro.Env.t) :: Macro.t
   1283   def apply_query(query, module, args, env) do
   1284     case Macro.expand(query, env) |> unescape_query() do
   1285       %Query{} = compiletime_query ->
   1286         apply(module, :apply, [compiletime_query | args])
   1287         |> escape_query()
   1288 
   1289       runtime_query ->
   1290         quote do
   1291           # Unquote the query before `module.apply()` for any binding variable.
   1292           query = unquote(runtime_query)
   1293           unquote(module).apply(query, unquote_splicing(args))
   1294         end
   1295     end
   1296   end
   1297 
   1298   # Unescapes an `Ecto.Query` struct.
   1299   @spec unescape_query(Macro.t) :: Query.t | Macro.t
   1300   defp unescape_query({:%, _, [Query, {:%{}, _, list}]}) do
   1301     struct(Query, list)
   1302   end
   1303   defp unescape_query({:%{}, _, list} = ast) do
   1304     if List.keyfind(list, :__struct__, 0) == {:__struct__, Query} do
   1305       Map.new(list)
   1306     else
   1307       ast
   1308     end
   1309   end
   1310   defp unescape_query(other) do
   1311     other
   1312   end
   1313 
   1314   # Escapes an `Ecto.Query` and associated structs.
   1315   @spec escape_query(Query.t) :: Macro.t
   1316   defp escape_query(%Query{} = query), do: {:%{}, [], Map.to_list(query)}
   1317 
   1318   defp parse_access_get({{:., _, [Access, :get]}, _, [left, right]}, acc) do
   1319     parse_access_get(left, [right | acc])
   1320   end
   1321 
   1322   defp parse_access_get({{:., _, [{var, _, context}, field]}, _, []} = expr, acc)
   1323        when is_atom(var) and is_atom(context) and is_atom(field) do
   1324     {expr, acc}
   1325   end
   1326 end