diff --git a/lib/ecto/query/planner.ex b/lib/ecto/query/planner.ex index 55a437fbcb..7862c78814 100644 --- a/lib/ecto/query/planner.ex +++ b/lib/ecto/query/planner.ex @@ -1522,6 +1522,11 @@ defmodule Ecto.Query.Planner do {{:fragment, meta, fragments}, acc} end + defp prewalk_source({{:fragment, meta, fragments}, schema}, kind, query, expr, acc, adapter) do + {fragments, acc} = prewalk(fragments, kind, query, expr, acc, adapter) + {{{:fragment, meta, fragments}, schema}, acc} + end + defp prewalk_source({:values, meta, [types, num_rows]}, _kind, _query, _expr, acc, _adapter) do length = num_rows * length(types) # Adapters will use the schema types to cast the values diff --git a/test/ecto/query/planner_test.exs b/test/ecto/query/planner_test.exs index 93be9b9eab..e6fd4b9179 100644 --- a/test/ecto/query/planner_test.exs +++ b/test/ecto/query/planner_test.exs @@ -988,6 +988,26 @@ defmodule Ecto.Query.PlannerTest do ] = cache_key end + test "plan: tuple source with fragment numbers later placeholders after the source" do + good_query = + from(f in fragment("some_sql_function(?)", ^"value"), + where: f.visits in ^[1, 2], + select: f + ) + |> normalize() + + assert Macro.to_string(hd(good_query.wheres).expr) == "&0.visits() in ^(1, 2)" + + bad_query = + from(f in {fragment("some_sql_function(?)", ^"value"), Post}, + where: f.visits in ^[1, 2], + select: f + ) + |> normalize() + + assert Macro.to_string(hd(bad_query.wheres).expr) == "&0.visits() in ^(1, 2)" + end + describe "plan: CTEs" do test "with uncacheable queries are uncacheable" do {_, _, _, cache} =