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, ¬ 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, "ed_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("ed_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, "ed_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