zf

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

dynamic.ex (3983B)


      1 import Kernel, except: [apply: 2]
      2 
      3 defmodule Ecto.Query.Builder.Dynamic do
      4   @moduledoc false
      5 
      6   alias Ecto.Query.Builder
      7   alias Ecto.Query.Builder.Select
      8 
      9   @doc """
     10   Builds a dynamic expression.
     11   """
     12   @spec build([Macro.t], Macro.t, Macro.Env.t) :: Macro.t
     13   def build(binding, expr, env) do
     14     {query, vars} = Builder.escape_binding(quote(do: query), binding, env)
     15     {expr, {params, acc}} = escape(expr, {[], %{subqueries: [], aliases: %{}}}, vars, env)
     16     aliases = Builder.escape_select_aliases(acc.aliases)
     17     params = Builder.escape_params(params)
     18 
     19     quote do
     20       %Ecto.Query.DynamicExpr{fun: fn query ->
     21                                 _ = unquote(query)
     22                                 {unquote(expr), unquote(params), unquote(Enum.reverse(acc.subqueries)), unquote(aliases)}
     23                               end,
     24                               binding: unquote(Macro.escape(binding)),
     25                               file: unquote(env.file),
     26                               line: unquote(env.line)}
     27     end
     28   end
     29 
     30   defp escape({:selected_as, _, [_, _]} = expr, _params_acc, vars, env) do
     31     Select.escape(expr, vars, env)
     32   end
     33 
     34   defp escape(expr, params_acc, vars, env) do
     35     Builder.escape(expr, :any, params_acc, vars, {env, &escape_expansion/5})
     36   end
     37 
     38   defp escape_expansion(expr, _type, params_acc, vars, env) do
     39     escape(expr, params_acc, vars, env)
     40   end
     41 
     42   @doc """
     43   Expands a dynamic expression for insertion into the given query.
     44   """
     45   def fully_expand(query, %{file: file, line: line, binding: binding} = dynamic) do
     46     {expr, {binding, params, subqueries, _aliases, _count}} = expand(query, dynamic, {binding, [], [], %{}, 0})
     47     {expr, binding, Enum.reverse(params), Enum.reverse(subqueries), file, line}
     48   end
     49 
     50   @doc """
     51   Expands a dynamic expression as part of an existing expression.
     52 
     53   Any dynamic expression parameter is prepended and the parameters
     54   list is not reversed. This is useful when the dynamic expression
     55   is given in the middle of an expression.
     56   """
     57   def partially_expand(query, %{binding: binding} = dynamic, params, subqueries, aliases, count) do
     58     {expr, {_binding, params, subqueries, aliases, count}} =
     59       expand(query, dynamic, {binding, params, subqueries, aliases, count})
     60 
     61     {expr, params, subqueries, aliases, count}
     62   end
     63 
     64   def partially_expand(kind, query, %{binding: binding} = dynamic, params, count) do
     65     {expr, {_binding, params, subqueries, _aliases, count}} =
     66       expand(query, dynamic, {binding, params, [], %{}, count})
     67 
     68     if subqueries != [] do
     69       raise ArgumentError, "subqueries are not allowed in `#{kind}` expressions"
     70     end
     71 
     72     {expr, params, count}
     73   end
     74 
     75   defp expand(query, %{fun: fun}, {binding, params, subqueries, aliases, count}) do
     76     {dynamic_expr, dynamic_params, dynamic_subqueries, dynamic_aliases} = fun.(query)
     77     aliases = merge_aliases(aliases, dynamic_aliases)
     78 
     79     Macro.postwalk(dynamic_expr, {binding, params, subqueries, aliases, count}, fn
     80       {:^, meta, [ix]}, {binding, params, subqueries, aliases, count} ->
     81         case Enum.fetch!(dynamic_params, ix) do
     82           {%Ecto.Query.DynamicExpr{binding: new_binding} = dynamic, _} ->
     83             binding = if length(new_binding) > length(binding), do: new_binding, else: binding
     84             expand(query, dynamic, {binding, params, subqueries, aliases, count})
     85 
     86           param ->
     87             {{:^, meta, [count]}, {binding, [param | params], subqueries, aliases, count + 1}}
     88         end
     89 
     90       {:subquery, i}, {binding, params, subqueries, aliases, count} ->
     91         subquery = Enum.fetch!(dynamic_subqueries, i)
     92         ix = length(subqueries)
     93         {{:subquery, ix}, {binding, [{:subquery, ix} | params], [subquery | subqueries], aliases, count + 1}}
     94 
     95       expr, acc ->
     96         {expr, acc}
     97     end)
     98   end
     99 
    100   defp merge_aliases(old_aliases, new_aliases) do
    101     Enum.reduce(new_aliases, old_aliases, fn {alias, _}, aliases ->
    102       Builder.add_select_alias(aliases, alias)
    103     end)
    104   end
    105 end