Skip to content

Commit b4b8996

Browse files
authored
Expand macros in window frames (#4730)
1 parent af35386 commit b4b8996

2 files changed

Lines changed: 47 additions & 2 deletions

File tree

lib/ecto/query/builder/windows.ex

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,21 @@ defmodule Ecto.Query.Builder.Windows do
7272
defp escape_frame({:fragment, _, _} = fragment, params_acc, vars, env) do
7373
Builder.escape(fragment, :any, params_acc, vars, env)
7474
end
75-
defp escape_frame(other, _, _, _) do
76-
Builder.error!("expected a dynamic or fragment in `:frame`, got: `#{inspect other}`")
75+
76+
defp escape_frame(other, params_acc, vars, env) do
77+
macro_env =
78+
case env do
79+
{env, _} -> env
80+
env -> env
81+
end
82+
83+
case Macro.expand_once(other, macro_env) do
84+
^other ->
85+
Builder.error!("expected a dynamic or fragment in `:frame`, got: `#{inspect other}`")
86+
87+
expanded ->
88+
escape_frame(expanded, params_acc, vars, env)
89+
end
7790
end
7891

7992
defp error!(other) do

test/ecto/query/builder/windows_test.exs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ defmodule Ecto.Query.Builder.WindowsTest do
66

77
import Ecto.Query
88

9+
defmacrop rows_between_preceding(count) do
10+
quote do
11+
fragment(unquote("ROWS BETWEEN #{count} PRECEDING AND CURRENT ROW"))
12+
end
13+
end
14+
15+
defmacro invalid_frame do
16+
quote do
17+
[rows: -3, exclude: :current]
18+
end
19+
end
20+
921
describe "escape" do
1022
test "handles expressions and params" do
1123
assert {Macro.escape(quote(do: [partition_by: [&0.y()]])), [], {[], %{}}} ==
@@ -63,6 +75,26 @@ defmodule Ecto.Query.Builder.WindowsTest do
6375
query = "q" |> windows([p], w: [frame: fragment("FOOBAR")])
6476
assert query.windows[:w].expr[:frame] == {:fragment, [], [raw: "FOOBAR"]}
6577
end
78+
79+
test "defines frame with macro that expands to fragment" do
80+
query = "q" |> windows([p], w: [frame: rows_between_preceding(6)])
81+
82+
assert query.windows[:w].expr[:frame] ==
83+
{:fragment, [], [raw: "ROWS BETWEEN 6 PRECEDING AND CURRENT ROW"]}
84+
end
85+
86+
test "raises on frame macro that does not expand to fragment" do
87+
assert_raise Ecto.Query.CompileError, ~r"expected a dynamic or fragment in `:frame`", fn ->
88+
Code.eval_quoted(
89+
quote do
90+
import Ecto.Query
91+
import unquote(__MODULE__)
92+
93+
"q" |> windows([p], w: [frame: invalid_frame()])
94+
end
95+
)
96+
end
97+
end
6698
end
6799

68100
describe "at runtime" do

0 commit comments

Comments
 (0)