Repository: elixir-ecto/ecto
Pull request #2200 (2017-09-01 18:31:58)

Support `prefix` in subqueries.
I raised an issue a while back (https://github.com/elixir-ecto/ecto/issues/2036) regarding a lack of a `prefix` option in subqueries. I closed it at the time because I found a workaround, and assumed there was no good reason to include it.

I ran into another case where this was required, so attempted to add the required functionality.

Please review that this makes sense, I'm not very familiar with the rest of the code. All tests do still pass though.

ebert[bot]: Ebert has finished reviewing this Pull Request and has found:

* 1 fixed issue! 🎉

But beware that this branch is **[1 commit behind the `elixir-ecto:master` branch](https://github.com/elixir-ecto/ecto/compare/brandonparsons:support-subquery-prefix...elixir-ecto:master)**, and a review of an up to date branch would produce more accurate results.

You can see more details about this review at https://ebertapp.io/github/elixir-ecto/ecto/pulls/2200.

<!-- Ebert auto generated comment -->


Patch:
diff --git a/lib/ecto/query.ex b/lib/ecto/query.ex
index 4c81240b..df2adf57 100644
--- a/lib/ecto/query.ex
+++ b/lib/ecto/query.ex
@@ -409,6 +409,11 @@ defmodule Ecto.Query do
       query = from Employee, order_by: [desc: :salary], limit: 10
       from e in subquery(query), select: avg(e.salary)
 
+  A prefix can be specified for a subquery, similar to standard repo operations:
+
+      query = from Employee, order_by: [desc: :salary], limit: 10
+      from e in subquery(query, prefix: "my_prefix"), select: avg(e.salary)
+
   Although subqueries are not allowed in WHERE expressions,
   most subqueries in WHERE expression can be rewritten as JOINs.
   Imagine you want to write this query:
@@ -438,12 +443,17 @@ defmodule Ecto.Query do
       end)
 
   """
-  def subquery(%Ecto.SubQuery{} = subquery),
-    do: subquery
-  def subquery(%Ecto.Query{} = query),
-    do: %Ecto.SubQuery{query: query}
-  def subquery(queryable),
-    do: %Ecto.SubQuery{query: Ecto.Queryable.to_query(queryable)}
+  def subquery(query, opts \\ []) do
+    subquery = wrap_in_subquery(query)
+    case Keyword.fetch(opts, :prefix) do
+      {:ok, prefix} when is_binary(prefix) -> put_in(subquery.query.prefix, prefix)
+      :error -> subquery
+    end
+  end
+
+  defp wrap_in_subquery(%Ecto.SubQuery{} = subquery), do: subquery
+  defp wrap_in_subquery(%Ecto.Query{} = query), do: %Ecto.SubQuery{query: query}
+  defp wrap_in_subquery(queryable), do: %Ecto.SubQuery{query: Ecto.Queryable.to_query(queryable)}
 
   @doc """
   Resets a previously set field on a query.


Test patch:
diff --git a/test/ecto/query_test.exs b/test/ecto/query_test.exs
index c6474de4..f4f2913d 100644
--- a/test/ecto/query_test.exs
+++ b/test/ecto/query_test.exs
@@ -90,6 +90,18 @@ defmodule Ecto.QueryTest do
       assert subquery(subquery("posts")).query.from == {"posts", nil}
       assert subquery(subquery("posts").query).query.from == {"posts", nil}
     end
+
+    test "prefix is not applied if left blank" do
+      assert subquery("posts").query.prefix == nil
+      assert subquery(subquery("posts")).query.prefix == nil
+      assert subquery(subquery("posts").query).query.prefix == nil
+    end
+
+    test "applies prefix to the subquery's query if provided" do
+      assert subquery("posts", prefix: "my_prefix").query.prefix == "my_prefix"
+      assert subquery(subquery("posts", prefix: "my_prefix")).query.prefix == "my_prefix"
+      assert subquery(subquery("posts", prefix: "my_prefix").query).query.prefix == "my_prefix"
+    end
   end
 
   describe "bindings" do