Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lib/ecto/query/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,19 @@ defmodule Ecto.Query.Builder do
def escape(expr, type, params_acc, vars, env)

# var.x - where var is bound
def escape({{:., _, [callee, field]}, _, []}, _type, params_acc, vars, _env)
def escape({{:., _, [callee, field]}, call_meta, []}, _type, params_acc, vars, env)
when is_atom(field) do
if call_meta[:no_parens] != true do
stacktrace = Macro.Env.stacktrace(get_env(env))

IO.warn(
"using parentheses after a field access, such as `#{Macro.to_string(callee)}.#{field}()`, " <>
"is deprecated and will raise in future Ecto versions. Remove the parentheses to keep " <>
"the current behaviour: `#{Macro.to_string(callee)}.#{field}`",
stacktrace
)
end

{escape_field!(callee, field, vars), params_acc}
end

Expand Down
6 changes: 3 additions & 3 deletions test/ecto/query/builder/distinct_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ defmodule Ecto.Query.Builder.DistinctTest do
escape(true, {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [asc: &0.y()])), {[], %{}}} ==
escape(quote(do: x.y()), {[], %{}}, [x: 0], __ENV__)
escape(quote(do: x.y), {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [asc: &0.x(), asc: &1.y()])), {[], %{}}} ==
escape(quote(do: [x.x(), y.y()]), {[], %{}}, [x: 0, y: 1], __ENV__)
escape(quote(do: [x.x, y.y]), {[], %{}}, [x: 0, y: 1], __ENV__)

assert {Macro.escape(quote(do: [asc: &0.x(), desc: &1.y()])), {[], %{}}} ==
escape(quote(do: [x.x(), desc: y.y()]), {[], %{}}, [x: 0, y: 1], __ENV__)
escape(quote(do: [x.x, desc: y.y]), {[], %{}}, [x: 0, y: 1], __ENV__)

import Kernel, except: [>: 2]

Expand Down
2 changes: 1 addition & 1 deletion test/ecto/query/builder/filter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Ecto.Query.Builder.FilterTest do
assert escape(:where, quote(do: []), 0, [x: 0], __ENV__) ===
{true, {[], %{subqueries: []}}}

assert escape(:where, quote(do: {x.x()} == {^"foo"}), 0, [x: 0], __ENV__) ===
assert escape(:where, quote(do: {x.x} == {^"foo"}), 0, [x: 0], __ENV__) ===
{Macro.escape(quote(do: {&0.x()} == {^0})),
{[{"foo", {0, :x}}], %{subqueries: []}}}

Expand Down
4 changes: 2 additions & 2 deletions test/ecto/query/builder/group_by_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ defmodule Ecto.Query.Builder.GroupByTest do
describe "escape" do
test "handles expressions and params" do
assert {Macro.escape(quote(do: [&0.y()])), {[], %{}}} ==
escape(:group_by, quote(do: x.y()), {[], %{}}, [x: 0], __ENV__)
escape(:group_by, quote(do: x.y), {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [&0.x(), &1.y()])), {[], %{}}} ==
escape(:group_by, quote(do: [x.x(), y.y()]), {[], %{}}, [x: 0, y: 1], __ENV__)
escape(:group_by, quote(do: [x.x, y.y]), {[], %{}}, [x: 0, y: 1], __ENV__)

import Kernel, except: [>: 2]

Expand Down
8 changes: 4 additions & 4 deletions test/ecto/query/builder/order_by_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ defmodule Ecto.Query.Builder.OrderByTest do

test "handles expressions and params" do
assert {Macro.escape(quote(do: [asc: &0.y()])), {[], %{}}} ==
escape(:order_by, quote(do: x.y()), {[], %{}}, [x: 0], __ENV__)
escape(:order_by, quote(do: x.y), {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [asc: &0.x(), asc: &1.y()])), {[], %{}}} ==
escape(:order_by, quote(do: [x.x(), y.y()]), {[], %{}}, [x: 0, y: 1], __ENV__)
escape(:order_by, quote(do: [x.x, y.y]), {[], %{}}, [x: 0, y: 1], __ENV__)

assert {Macro.escape(quote(do: [asc: &0.x(), desc: &1.y()])), {[], %{}}} ==
escape(
:order_by,
quote(do: [asc: x.x(), desc: y.y()]),
quote(do: [asc: x.x, desc: y.y]),
{[], %{}},
[x: 0, y: 1],
__ENV__
Expand All @@ -50,7 +50,7 @@ defmodule Ecto.Query.Builder.OrderByTest do
assert {Macro.escape(quote(do: [asc: &0.x(), desc: &1.y()])), {[], %{}}} ==
escape(
:order_by,
quote(do: [x.x(), desc: y.y()]),
quote(do: [x.x, desc: y.y]),
{[], %{}},
[x: 0, y: 1],
__ENV__
Expand Down
6 changes: 3 additions & 3 deletions test/ecto/query/builder/select_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ defmodule Ecto.Query.Builder.SelectTest do
escape(quote(do: x), [x: 0], __ENV__)

assert {Macro.escape(quote(do: &0.y())), params_acc()} ==
escape(quote(do: x.y()), [x: 0], __ENV__)
escape(quote(do: x.y), [x: 0], __ENV__)

assert {Macro.escape(quote(do: &0)),
params_acc(take: %{0 => {:any, [:foo, :bar, baz: :bat]}})} ==
Expand All @@ -59,13 +59,13 @@ defmodule Ecto.Query.Builder.SelectTest do
escape(quote(do: %{a => b}), [a: 0, b: 1], __ENV__)

assert {[Macro.escape(quote(do: &0.y())), Macro.escape(quote(do: &0.z()))], params_acc()} ==
escape(quote(do: [x.y(), x.z()]), [x: 0], __ENV__)
escape(quote(do: [x.y, x.z]), [x: 0], __ENV__)

assert {[
{:{}, [], [{:{}, [], [:., [], [{:{}, [], [:&, [], [0]]}, :y]]}, [], []]},
{:{}, [], [:^, [], [0]]}
], params_acc(params: [{1, :any}])} ==
escape(quote(do: [x.y(), ^1]), [x: 0], __ENV__)
escape(quote(do: [x.y, ^1]), [x: 0], __ENV__)

assert {{:{}, [], [:%, [], [Foo, {:{}, [], [:%{}, [], [a: {:{}, [], [:&, [], [0]]}]]}]]},
params_acc()} ==
Expand Down
2 changes: 1 addition & 1 deletion test/ecto/query/builder/update_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Ecto.Query.Builder.UpdateTest do
]
]

assert escape(quote(do: [set: [foo: x.bar()]]), [x: 0], __ENV__) |> elem(0) ==
assert escape(quote(do: [set: [foo: x.bar]]), [x: 0], __ENV__) |> elem(0) ==
Macro.escape(quote(do: [set: [foo: &0.bar()]]))
end

Expand Down
4 changes: 2 additions & 2 deletions test/ecto/query/builder/windows_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ defmodule Ecto.Query.Builder.WindowsTest do
describe "escape" do
test "handles expressions and params" do
assert {Macro.escape(quote(do: [partition_by: [&0.y()]])), [], {[], %{}}} ==
escape(quote(do: [partition_by: x.y()]), {[], %{}}, [x: 0], __ENV__)
escape(quote(do: [partition_by: x.y]), {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [partition_by: [&0.y()]])), [], {[], %{}}} ==
escape(quote(do: [partition_by: :y]), {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [order_by: [asc: &0.y()]])), [], {[], %{}}} ==
escape(quote(do: [order_by: x.y()]), {[], %{}}, [x: 0], __ENV__)
escape(quote(do: [order_by: x.y]), {[], %{}}, [x: 0], __ENV__)

assert {Macro.escape(quote(do: [order_by: [asc: &0.y()]])), [], {[], %{}}} ==
escape(quote(do: [order_by: :y]), {[], %{}}, [x: 0], __ENV__)
Expand Down
60 changes: 38 additions & 22 deletions test/ecto/query/builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
x.y()
x.y
end,
[x: 0],
__ENV__
Expand All @@ -34,7 +34,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
x.y() > x.z()
x.y > x.z
end,
[x: 0],
__ENV__
Expand All @@ -47,7 +47,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
x.y() > y.z()
x.y > y.z
end,
[x: 0, y: 1],
__ENV__
Expand All @@ -62,7 +62,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
x.y() + y.z()
x.y + y.z
end,
[x: 0, y: 1],
__ENV__
Expand Down Expand Up @@ -149,7 +149,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
-x.y()
-x.y
end,
[x: 0],
__ENV__
Expand Down Expand Up @@ -344,7 +344,7 @@ defmodule Ecto.Query.BuilderTest do
), [{0, :any}]} ==
escape(
quote do
fragment("date_add(?, ?)", p.created_at(), ^0)
fragment("date_add(?, ?)", p.created_at, ^0)
end,
[p: 0],
__ENV__
Expand All @@ -370,7 +370,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
fragment("query\\?(?)", p.created_at())
fragment("query\\?(?)", p.created_at)
end,
[p: 0],
__ENV__
Expand Down Expand Up @@ -434,13 +434,13 @@ defmodule Ecto.Query.BuilderTest do

test "escape over with window name" do
assert {Macro.escape(quote(do: over(count(&0.id()), :w))), []} ==
escape(quote(do: count(x.id()) |> over(:w)), [x: 0], __ENV__)
escape(quote(do: count(x.id) |> over(:w)), [x: 0], __ENV__)

assert {Macro.escape(quote(do: over(nth_value(&0.id(), 1), :w))), []} ==
escape(quote(do: nth_value(x.id(), 1) |> over(:w)), [x: 0], __ENV__)
escape(quote(do: nth_value(x.id, 1) |> over(:w)), [x: 0], __ENV__)

assert {Macro.escape(quote(do: over(nth_value(&0.id(), 1), :w))), []} ==
escape(quote(do: my_first_value(x.id()) |> over(:w)), [x: 0], __ENV__)
escape(quote(do: my_first_value(x.id) |> over(:w)), [x: 0], __ENV__)
end

defmacro my_custom_field(p) do
Expand All @@ -460,10 +460,10 @@ defmodule Ecto.Query.BuilderTest do
escape(quote(do: over(row_number())), [], __ENV__)

assert {Macro.escape(quote(do: over(nth_value(&0.id(), 1), []))), []} ==
escape(quote(do: over(my_first_value(x.id()))), [x: 0], __ENV__)
escape(quote(do: over(my_first_value(x.id))), [x: 0], __ENV__)

assert {Macro.escape(quote(do: over(nth_value(&0.id(), 1), order_by: [asc: &0.id()]))), []} ==
escape(quote(do: nth_value(x.id(), 1) |> over(order_by: x.id())), [x: 0], __ENV__)
escape(quote(do: nth_value(x.id, 1) |> over(order_by: x.id)), [x: 0], __ENV__)

assert {Macro.escape(
quote(
Expand All @@ -478,22 +478,22 @@ defmodule Ecto.Query.BuilderTest do
)
), []} ==
escape(
quote(do: nth_value(x.id(), 1) |> over(order_by: my_complex_order(x))),
quote(do: nth_value(x.id, 1) |> over(order_by: my_complex_order(x))),
[x: 0],
__ENV__
)

assert {Macro.escape(quote(do: over(nth_value(&0.id(), 1), partition_by: [&0.id()]))), []} ==
escape(
quote(do: nth_value(x.id(), 1) |> over(partition_by: x.id())),
quote(do: nth_value(x.id, 1) |> over(partition_by: x.id)),
[x: 0],
__ENV__
)

assert {Macro.escape(quote(do: over(nth_value(&0.id(), 1), frame: fragment({:raw, "ROWS"})))),
[]} ==
escape(
quote(do: nth_value(x.id(), 1) |> over(frame: fragment("ROWS"))),
quote(do: nth_value(x.id, 1) |> over(frame: fragment("ROWS"))),
[x: 0],
__ENV__
)
Expand All @@ -502,7 +502,7 @@ defmodule Ecto.Query.BuilderTest do
~r"windows definitions given to over/2 do not allow interpolations at the root",
fn ->
escape(
quote(do: nth_value(x.id(), 1) |> over(order_by: ^foo)),
quote(do: nth_value(x.id, 1) |> over(order_by: ^foo)),
[x: 0],
__ENV__
)
Expand All @@ -512,7 +512,7 @@ defmodule Ecto.Query.BuilderTest do

assert {Macro.escape(quote(do: over(filter(avg(&0.value()), is_nil(&0.flag())), []))), []} ==
escape(
quote(do: avg(x.value()) |> filter(is_nil(x.flag())) |> over([])),
quote(do: avg(x.value) |> filter(is_nil(x.flag)) |> over([])),
[x: 0],
__ENV__
)
Expand All @@ -528,7 +528,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
type(x.y() + y.z(), :decimal)
type(x.y + y.z, :decimal)
end,
[x: 0, y: 1],
__ENV__
Expand Down Expand Up @@ -580,7 +580,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
type(sum(x.y()), :decimal)
type(sum(x.y), :decimal)
end,
[x: 0],
{__ENV__, %{}}
Expand Down Expand Up @@ -608,7 +608,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
type(filter(sum(x.y()), x.y() > x.z()), :decimal)
type(filter(sum(x.y), x.y > x.z), :decimal)
end,
[x: 0],
{__ENV__, %{}}
Expand All @@ -624,7 +624,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
type(over(fragment("array_agg(?)", x.id()), :y), {:array, Ecto.UUID})
type(over(fragment("array_agg(?)", x.id), :y), {:array, Ecto.UUID})
end,
[x: 0],
{__ENV__, %{}}
Expand Down Expand Up @@ -661,7 +661,7 @@ defmodule Ecto.Query.BuilderTest do
), []} ==
escape(
quote do
type(wrapped_sum(x.y()), :integer)
type(wrapped_sum(x.y), :integer)
end,
[x: 0],
__ENV__
Expand Down Expand Up @@ -730,6 +730,22 @@ defmodule Ecto.Query.BuilderTest do
end
end

test "warns on parentheses in field access" do
import ExUnit.CaptureIO

warning =
capture_io(:stderr, fn ->
escape(quote(do: x.y()), [x: 0], __ENV__)
end)

assert warning =~ "using parentheses after a field access"
assert warning =~ "x.y()"

assert capture_io(:stderr, fn ->
escape(quote(do: x.y), [x: 0], __ENV__)
end) == ""
end

test "doesn't escape interpolation" do
import Kernel, except: [>: 2, ++: 2]

Expand Down
Loading