diff --git a/.gitignore b/.gitignore index b9d8296..71660da 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ erl_crash.dump *.ez /.elixir_ls +/.lexical diff --git a/lib/ecto/soft_delete_repo.ex b/lib/ecto/soft_delete_repo.ex index f0bc4ea..9a0e782 100644 --- a/lib/ecto/soft_delete_repo.ex +++ b/lib/ecto/soft_delete_repo.ex @@ -81,11 +81,37 @@ defmodule Ecto.SoftDelete.Repo do if has_include_deleted_at_clause?(query) || opts[:with_deleted] || !soft_deletable?(query) do {query, opts} else - query = from(x in query, where: is_nil(x.deleted_at)) + query = filter_soft_deleted(query) {query, opts} end end + # We need to check the entire query and apply filtering + # where appropriate. So, we recurse the query here and + # rebuild it with filtering where appropriate. This + # currently only considers the source and does not handle + # things like joins... + defp filter_soft_deleted(%Ecto.Query{from: %{source: {_schema, _module}}} = query) do + if Ecto.SoftDelete.Query.soft_deletable?(query) do + from(x in query, where: is_nil(x.deleted_at)) + else + query + end + end + + defp filter_soft_deleted(%Ecto.SubQuery{query: query} = sub) do + if Ecto.SoftDelete.Query.soft_deletable?(query) do + %{sub | query: from(x in query, where: is_nil(x.deleted_at))} + else + sub + end + end + + defp filter_soft_deleted(%Ecto.Query{from: from} = query) do + updated_from = %{from | source: filter_soft_deleted(from.source)} + %{query | from: updated_from} + end + # Checks the query to see if it contains a where not is_nil(deleted_at) # if it does, we want to be sure that we don't exclude soft deleted records defp has_include_deleted_at_clause?(%Ecto.Query{wheres: wheres}) do diff --git a/test/soft_delete_repo_test.exs b/test/soft_delete_repo_test.exs index 98c6da6..d1cc1da 100644 --- a/test/soft_delete_repo_test.exs +++ b/test/soft_delete_repo_test.exs @@ -115,6 +115,16 @@ defmodule Ecto.SoftDelete.Repo.Test do assert Enum.member?(results, soft_deleted_user) end + test "handles subquery that does not expose deleted_at as 'main' schema" do + Repo.insert!(%User{email: "test0@example.com"}) + Repo.insert!(%Nondeletable{value: "stuff"}) + + subq = User |> where([u], u.email == "test0@example.com") |> select([u], %{id: u.id, email: u.email}) + query = subquery(subq) |> join(:left, [u], nondel in Nondeletable, on: u.id == nondel.id) + + Repo.all(query) + end + test "includes soft deleted records if where not is_nil(deleted_at) clause is present" do user = Repo.insert!(%User{email: "test0@example.com"})