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
6 changes: 5 additions & 1 deletion lib/ecto/query/builder/from.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ defmodule Ecto.Query.Builder.From do

^query ->
case query do
{left, right} -> {left, Macro.expand(right, env)}
{left, right} -> {escape_source(left, env), Macro.expand(right, env)}
_ -> query
end

Expand Down Expand Up @@ -118,6 +118,10 @@ defmodule Ecto.Query.Builder.From do
{:ok, prefix} = prefix || {:ok, nil}
{query(prefix, fragment, params, as, hints, env.file, env.line), binds, 1}

{{{:{}, _, [:fragment, _, _]} = fragment, params}, schema} when is_atom(schema) ->
{:ok, prefix} = prefix || {:ok, nil}
{query(prefix, {fragment, schema}, params, as, hints, env.file, env.line), binds, 1}

{{:{}, _, [:values, _, _]} = values, prelude, params} ->
{:ok, prefix} = prefix || {:ok, nil}
query = query(prefix, values, params, as, hints, env.file, env.line)
Expand Down
3 changes: 2 additions & 1 deletion lib/ecto/query/inspect.ex
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ defimpl Inspect, for: Ecto.Query do
"values (#{Enum.join(fields, ", ")})"
end

defp inspect_source(%{source: {source, schema}}, _names) do
defp inspect_source(%{source: {source, schema}} = part, names) do
source = if is_binary(source), do: source, else: "#{expr(source, names, part)}"
inspect(if source == schema.__schema__(:source), do: schema, else: {source, schema})
end

Expand Down
8 changes: 8 additions & 0 deletions lib/ecto/query/planner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,18 @@ defmodule Ecto.Query.Planner do
when kind in [:fragment, :values],
do: {expr, source}

defp plan_source(_query, %{source: {{:fragment, _, _} = source, schema}, prefix: nil} = expr, _adapter, _cte_names)
when is_atom(schema) do
{expr, {source, schema, nil}}
end

defp plan_source(query, %{source: {kind, _, _}, prefix: prefix} = expr, _adapter, _cte_names)
when kind in [:fragment, :values],
do: error!(query, expr, "cannot set prefix: #{inspect(prefix)} option for #{kind} sources")

defp plan_source(query, %{source: {{:fragment, _, _}, _schema}, prefix: prefix} = expr, _adapter, _cte_names),
do: error!(query, expr, "cannot set prefix: #{inspect(prefix)} option for fragment sources")

defp plan_subquery(subquery, query, prefix, adapter, source?, cte_names) do
%{query: inner_query} = subquery

Expand Down
3 changes: 3 additions & 0 deletions test/ecto/query/inspect_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@

assert i(from(subquery(Post), [])) ==
~s{from p0 in subquery(from p0 in Inspect.Post)}

assert i(from(x in {fragment("select generate_series(?::integer, ?::integer) as num", ^0, ^2), Inspect.Comment}, [])) ==
~s[from c0 in {"fragment(\\"select generate_series(?::integer, ?::integer) as num\\", ^0, ^2)", Inspect.Comment}]
end

test "CTE" do
Expand Down Expand Up @@ -432,7 +435,7 @@
end

test "container values" do
assert i(from(Post, select: <<1, 2, 3>>)) ==

Check warning on line 438 in test/ecto/query/inspect_test.exs

View workflow job for this annotation

GitHub Actions / unit test (1.14.5, 24.3.4.17)

this check/guard will always yield the same result
"from p0 in Inspect.Post, select: \"\\x01\\x02\\x03\""

foo = <<1, 2, 3>>
Expand Down
29 changes: 29 additions & 0 deletions test/ecto/query/planner_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ defmodule Ecto.Query.PlannerTest do
end
end

defmodule Barebone do
use Ecto.Schema

@primary_key false
schema "barebone" do
field :num, :integer
end
end

defp plan(query, operation \\ :all) do
{query, params, key} = Planner.plan(query, operation, Ecto.TestAdapter)
{cast_params, dump_params} = Enum.unzip(params)
Expand Down Expand Up @@ -939,6 +948,16 @@ defmodule Ecto.Query.PlannerTest do
]
end

test "plan: tuple source with fragment" do
{query, cast_params, dump_params, cache_key} =
plan(from {fragment("? as num", ^0), Barebone})

assert {{{:fragment, [], _}, Barebone, nil}} = query.sources
assert cast_params == [0]
assert dump_params == [0]
assert [:all, {:from, {{:fragment, _, _}, Barebone, _, _}, []}] = cache_key
end

describe "plan: CTEs" do
test "with uncacheable queries are uncacheable" do
{_, _, _, cache} =
Expand Down Expand Up @@ -2572,6 +2591,16 @@ defmodule Ecto.Query.PlannerTest do
end
end

test "normalize: tuple source with fragment" do
{query, _, _, select} =
normalize_with_params(from {fragment("? as num", ^0), Barebone})

%{from: {_, {:source, {{:fragment, _, _}, Barebone}, nil, types}}} = select
assert types == [num: :integer]
assert {{:fragment, _, _}, Barebone} = query.from.source
assert query.select.fields == [{{:., [writable: :always], [{:&, [], [0]}, :num]}, [], []}]
end

describe "normalize: subqueries in boolean expressions" do
test "replaces {:subquery, index} with an Ecto.SubQuery struct" do
subquery = from(p in Post, select: p.visits)
Expand Down
Loading