Skip to content

Commit bf24234

Browse files
Don't require adapter for runtime fragment splicing (#4697)
1 parent 7224c12 commit bf24234

File tree

4 files changed

+57
-44
lines changed

4 files changed

+57
-44
lines changed

lib/ecto/query/builder.ex

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ defmodule Ecto.Query.Builder do
204204
{{:{}, [], [:fragment, [], [expr]]}, params_acc}
205205
end
206206

207-
def escape({:fragment, _, [query | frags]}, _type, params_acc, vars, env) do
207+
def escape({:fragment, _, [query | frags]}, _type, {params, acc}, vars, env) do
208208
pieces = expand_and_split_fragment(query, env)
209209

210210
if length(pieces) != length(frags) + 1 do
@@ -214,8 +214,17 @@ defmodule Ecto.Query.Builder do
214214
)
215215
end
216216

217-
{frags, params_acc} = Enum.map_reduce(frags, params_acc, &escape_fragment(&1, &2, vars, env))
218-
{{:{}, [], [:fragment, [], merge_fragments(pieces, frags, [])]}, params_acc}
217+
{frags, {params, acc, compile_merge?}} =
218+
Enum.map_reduce(frags, {params, acc, true}, &escape_fragment(&1, &2, vars, env))
219+
220+
merged =
221+
if compile_merge? do
222+
merge_fragments(pieces, frags, [])
223+
else
224+
quote do: Ecto.Query.Builder.merge_fragments(unquote(pieces), unquote(frags), [])
225+
end
226+
227+
{{:{}, [], [:fragment, [], merged]}, {params, acc}}
219228
end
220229

221230
# subqueries
@@ -790,12 +799,10 @@ defmodule Ecto.Query.Builder do
790799
end
791800
end
792801

793-
defp escape_fragment({:splice, _meta, [{:^, _, [value]} = expr]}, params_acc, vars, env) do
794-
checked = quote do: Ecto.Query.Builder.splice!(unquote(value))
795-
length = quote do: length(unquote(checked))
796-
{expr, params_acc} = escape(expr, {:splice, :any}, params_acc, vars, env)
797-
escaped = {:{}, [], [:splice, [], [expr, length]]}
798-
{escaped, params_acc}
802+
defp escape_fragment({:splice, _meta, [{:^, _, [value]}]}, {params, acc, _}, _vars, _env) do
803+
checked = quote do: Ecto.Query.Builder.splice!(unquote(value), length(unquote(params)))
804+
param = {value, {:splice, :any}}
805+
{{:splice, checked}, {[param | params], acc, false}}
799806
end
800807

801808
defp escape_fragment({:splice, _meta, [exprs]}, params_acc, vars, env) when is_list(exprs) do
@@ -811,29 +818,31 @@ defmodule Ecto.Query.Builder do
811818
)
812819
end
813820

814-
defp escape_fragment(expr, params_acc, vars, env) do
815-
escape(expr, :any, params_acc, vars, env)
821+
defp escape_fragment(expr, {params, acc, compile_merge?}, vars, env) do
822+
{expr, {params, acc}} =
823+
escape(expr, :any, {params, acc}, vars, env)
824+
825+
{expr, {params, acc, compile_merge?}}
816826
end
817827

818-
defp merge_fragments([raw_h | raw_t], [{:splice, exprs} | expr_t], []),
828+
def merge_fragments([raw_h | raw_t], [{:splice, exprs} | expr_t], []),
819829
do: [{:raw, raw_h} | merge_fragments(raw_t, expr_t, exprs)]
820830

821-
defp merge_fragments([raw_h | raw_t], [expr_h | expr_t], []),
831+
def merge_fragments([raw_h | raw_t], [expr_h | expr_t], []),
822832
do: [{:raw, raw_h}, {:expr, expr_h} | merge_fragments(raw_t, expr_t, [])]
823833

824-
defp merge_fragments([raw_h], [], []),
834+
def merge_fragments([raw_h], [], []),
825835
do: [{:raw, raw_h}]
826836

827-
defp merge_fragments(raw, expr, [{:splice, exprs} | splice_t]),
837+
def merge_fragments(raw, expr, [{:splice, exprs} | splice_t]),
828838
do: merge_fragments(raw, expr, exprs ++ splice_t)
829839

830-
defp merge_fragments(raw, expr, [splice_h]),
840+
def merge_fragments(raw, expr, [splice_h]),
831841
do: [{:expr, splice_h} | merge_fragments(raw, expr, [])]
832842

833-
defp merge_fragments(raw, expr, [splice_h | splice_t]),
843+
def merge_fragments(raw, expr, [splice_h | splice_t]),
834844
do: [{:expr, splice_h}, {:raw, ","} | merge_fragments(raw, expr, splice_t)]
835845

836-
837846
for {agg, arity} <- @dynamic_aggregates do
838847
defp call_type(unquote(agg), unquote(arity)), do: {:any, :any}
839848
end
@@ -1336,9 +1345,9 @@ defmodule Ecto.Query.Builder do
13361345
@doc """
13371346
Called by escaper at runtime to verify splice in fragments.
13381347
"""
1339-
def splice!(value) do
1348+
def splice!(value, param_num) do
13401349
if is_list(value) do
1341-
value
1350+
Enum.map(value, fn _ -> {:^, [], [param_num]} end)
13421351
else
13431352
raise ArgumentError,
13441353
"splice(^value) expects `value` to be a list, got `#{inspect(value)}`"

lib/ecto/query/planner.ex

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,18 +1642,6 @@ defmodule Ecto.Query.Planner do
16421642
{{quantifier, meta, [subquery]}, acc}
16431643
end
16441644

1645-
defp prewalk(
1646-
{:splice, splice_meta, [{:^, meta, [_]}, length]},
1647-
_kind,
1648-
_query,
1649-
_expr,
1650-
acc,
1651-
_adapter
1652-
) do
1653-
param = {:^, meta, [acc, length]}
1654-
{{:splice, splice_meta, [param]}, acc + length}
1655-
end
1656-
16571645
defp prewalk({{:., dot_meta, [left, field]}, meta, []}, kind, query, expr, acc, _adapter) do
16581646
{ix, ix_expr, ix_query} = get_ix!(left, kind, query)
16591647
extra = if kind == :select, do: [type: type!(kind, ix_query, expr, ix, field)], else: []

test/ecto/query/planner_test.exs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,9 +1914,20 @@ defmodule Ecto.Query.PlannerTest do
19141914
assert dump_params == [1, 2, 3, 4, 5]
19151915

19161916
{:in, _, [_, {:fragment, _, parts}]} = hd(query.wheres).expr
1917-
assert [_, _, _, {:expr, {:splice, _, [{:^, _, [start_ix, length]}]}}, _, _, _] = parts
1918-
assert start_ix == 1
1919-
assert length == 3
1917+
1918+
assert [
1919+
_,
1920+
{:expr, {:^, _, [0]}},
1921+
_,
1922+
{:expr, {:^, _, [1]}},
1923+
_,
1924+
{:expr, {:^, _, [2]}},
1925+
_,
1926+
{:expr, {:^, _, [3]}},
1927+
_,
1928+
{:expr, {:^, _, [4]}},
1929+
_
1930+
] = parts
19201931
end
19211932

19221933
test "normalize: fragment with nested splicing" do
@@ -1938,14 +1949,13 @@ defmodule Ecto.Query.PlannerTest do
19381949
_,
19391950
{:expr, 2},
19401951
_,
1941-
{:expr, {:splice, _, [{:^, _, [start_ix, length]}]}},
1952+
{:expr, {:^, _, [1]}},
1953+
_,
1954+
{:expr, {:^, _, [2]}},
19421955
_,
19431956
{:expr, {:^, _, [3]}},
19441957
_
19451958
] = parts
1946-
1947-
assert start_ix == 1
1948-
assert length == 2
19491959
end
19501960

19511961
test "normalize: from values list" do

test/ecto/query_test.exs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,16 +1134,20 @@ defmodule Ecto.QueryTest do
11341134
three = 3
11351135

11361136
query =
1137-
from p in "posts", where: p.id in fragment("(?, ?, ?)", ^1, splice(^[two, three, 4]), ^5)
1137+
from p in "posts", where: p.id in fragment("(?,?,?)", ^1, splice(^[two, three, 4]), ^5)
11381138

11391139
assert {:in, _, [_, {:fragment, _, parts}]} = hd(query.wheres).expr
11401140

11411141
assert [
11421142
raw: "(",
11431143
expr: {:^, _, [0]},
1144-
raw: ", ",
1145-
expr: {:splice, _, [{:^, _, [1]}, 3]},
1146-
raw: ", ",
1144+
raw: ",",
1145+
expr: {:^, _, [1]},
1146+
raw: ",",
1147+
expr: {:^, _, [1]},
1148+
raw: ",",
1149+
expr: {:^, _, [1]},
1150+
raw: ",",
11471151
expr: {:^, _, [2]},
11481152
raw: ")"
11491153
] = parts
@@ -1208,7 +1212,9 @@ defmodule Ecto.QueryTest do
12081212
raw: ",",
12091213
expr: 2,
12101214
raw: ",",
1211-
expr: {:splice, _, [{:^, _, [1]}, 2]},
1215+
expr: {:^, _, [1]},
1216+
raw: ",",
1217+
expr: {:^, _, [1]},
12121218
raw: ",",
12131219
expr: {:^, _, [2]},
12141220
raw: ")"

0 commit comments

Comments
 (0)