Skip to content

Commit dd1dd23

Browse files
committed
support subqueries in order_by, group_by, distinct, window
1 parent 8e19684 commit dd1dd23

File tree

8 files changed

+200
-21
lines changed

8 files changed

+200
-21
lines changed

lib/ecto/adapters/myxql/connection.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ if Code.ensure_loaded?(MyXQL) do
7575
## Query
7676

7777
@parent_as __MODULE__
78-
alias Ecto.Query.{BooleanExpr, JoinExpr, QueryExpr, WithExpr}
78+
alias Ecto.Query.{BooleanExpr, ByExpr, JoinExpr, QueryExpr, WithExpr}
7979

8080
@impl true
8181
def all(query, as_prefix \\ []) do
@@ -319,10 +319,10 @@ if Code.ensure_loaded?(MyXQL) do
319319
end
320320

321321
defp distinct(nil, _sources, _query), do: []
322-
defp distinct(%QueryExpr{expr: true}, _sources, _query), do: "DISTINCT "
323-
defp distinct(%QueryExpr{expr: false}, _sources, _query), do: []
322+
defp distinct(%ByExpr{expr: true}, _sources, _query), do: "DISTINCT "
323+
defp distinct(%ByExpr{expr: false}, _sources, _query), do: []
324324

325-
defp distinct(%QueryExpr{expr: exprs}, _sources, query) when is_list(exprs) do
325+
defp distinct(%ByExpr{expr: exprs}, _sources, query) when is_list(exprs) do
326326
error!(query, "DISTINCT with multiple columns is not supported by MySQL")
327327
end
328328

@@ -511,7 +511,7 @@ if Code.ensure_loaded?(MyXQL) do
511511
defp group_by(%{group_bys: group_bys} = query, sources) do
512512
[
513513
" GROUP BY "
514-
| Enum.map_intersperse(group_bys, ", ", fn %QueryExpr{expr: expr} ->
514+
| Enum.map_intersperse(group_bys, ", ", fn %ByExpr{expr: expr} ->
515515
Enum.map_intersperse(expr, ", ", &expr(&1, sources, query))
516516
end)
517517
]
@@ -549,7 +549,7 @@ if Code.ensure_loaded?(MyXQL) do
549549
defp order_by(%{order_bys: order_bys} = query, sources) do
550550
[
551551
" ORDER BY "
552-
| Enum.map_intersperse(order_bys, ", ", fn %QueryExpr{expr: expr} ->
552+
| Enum.map_intersperse(order_bys, ", ", fn %ByExpr{expr: expr} ->
553553
Enum.map_intersperse(expr, ", ", &order_by_expr(&1, sources, query))
554554
end)
555555
]

lib/ecto/adapters/postgres/connection.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ if Code.ensure_loaded?(Postgrex) do
155155
end
156156

157157
@parent_as __MODULE__
158-
alias Ecto.Query.{BooleanExpr, JoinExpr, QueryExpr, WithExpr}
158+
alias Ecto.Query.{BooleanExpr, ByExpr, JoinExpr, QueryExpr, WithExpr}
159159

160160
@impl true
161161
def all(query, as_prefix \\ []) do
@@ -551,11 +551,11 @@ if Code.ensure_loaded?(Postgrex) do
551551
end
552552

553553
defp distinct(nil, _, _), do: {[], []}
554-
defp distinct(%QueryExpr{expr: []}, _, _), do: {[], []}
555-
defp distinct(%QueryExpr{expr: true}, _, _), do: {" DISTINCT", []}
556-
defp distinct(%QueryExpr{expr: false}, _, _), do: {[], []}
554+
defp distinct(%ByExpr{expr: []}, _, _), do: {[], []}
555+
defp distinct(%ByExpr{expr: true}, _, _), do: {" DISTINCT", []}
556+
defp distinct(%ByExpr{expr: false}, _, _), do: {[], []}
557557

558-
defp distinct(%QueryExpr{expr: exprs}, sources, query) do
558+
defp distinct(%ByExpr{expr: exprs}, sources, query) do
559559
{[
560560
" DISTINCT ON (",
561561
Enum.map_intersperse(exprs, ", ", fn {_, expr} -> expr(expr, sources, query) end),
@@ -772,7 +772,7 @@ if Code.ensure_loaded?(Postgrex) do
772772
[
773773
" GROUP BY "
774774
| Enum.map_intersperse(group_bys, ", ", fn
775-
%QueryExpr{expr: expr} ->
775+
%ByExpr{expr: expr} ->
776776
Enum.map_intersperse(expr, ", ", &expr(&1, sources, query))
777777
end)
778778
]

lib/ecto/adapters/tds/connection.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ if Code.ensure_loaded?(Tds) do
149149

150150
@parent_as __MODULE__
151151
alias Ecto.Query
152-
alias Ecto.Query.{BooleanExpr, JoinExpr, QueryExpr, WithExpr}
152+
alias Ecto.Query.{BooleanExpr, ByExpr, JoinExpr, QueryExpr, WithExpr}
153153

154154
@impl true
155155
def all(query, as_prefix \\ []) do
@@ -390,10 +390,10 @@ if Code.ensure_loaded?(Tds) do
390390
end
391391

392392
defp distinct(nil, _sources, _query), do: []
393-
defp distinct(%QueryExpr{expr: true}, _sources, _query), do: "DISTINCT "
394-
defp distinct(%QueryExpr{expr: false}, _sources, _query), do: []
393+
defp distinct(%ByExpr{expr: true}, _sources, _query), do: "DISTINCT "
394+
defp distinct(%ByExpr{expr: false}, _sources, _query), do: []
395395

396-
defp distinct(%QueryExpr{expr: exprs}, _sources, query) when is_list(exprs) do
396+
defp distinct(%ByExpr{expr: exprs}, _sources, query) when is_list(exprs) do
397397
error!(
398398
query,
399399
"DISTINCT with multiple columns is not supported by MsSQL. " <>
@@ -584,7 +584,7 @@ if Code.ensure_loaded?(Tds) do
584584
defp group_by(%{group_bys: group_bys} = query, sources) do
585585
[
586586
" GROUP BY "
587-
| Enum.map_intersperse(group_bys, ", ", fn %QueryExpr{expr: expr} ->
587+
| Enum.map_intersperse(group_bys, ", ", fn %ByExpr{expr: expr} ->
588588
Enum.map_intersperse(expr, ", ", &expr(&1, sources, query))
589589
end)
590590
]
@@ -595,7 +595,7 @@ if Code.ensure_loaded?(Tds) do
595595
defp order_by(%{order_bys: order_bys} = query, sources) do
596596
[
597597
" ORDER BY "
598-
| Enum.map_intersperse(order_bys, ", ", fn %QueryExpr{expr: expr} ->
598+
| Enum.map_intersperse(order_bys, ", ", fn %ByExpr{expr: expr} ->
599599
Enum.map_intersperse(expr, ", ", &order_by_expr(&1, sources, query))
600600
end)
601601
]

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ defmodule EctoSQL.MixProject do
7676
if path = System.get_env("ECTO_PATH") do
7777
{:ecto, path: path}
7878
else
79-
{:ecto, github: "elixir-ecto/ecto"}
79+
{:ecto, github: "zachdaniel/ecto", branch: "subqueries-in-order-by"}
8080
end
8181
end
8282

mix.lock

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
55
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
66
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
7-
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "bed81b9a69f3425147fb57df1b3dd5fb4c95792c", []},
8-
"ex_doc": {:hex, :ex_doc, "0.32.2", "f60bbeb6ccbe75d005763e2a328e6f05e0624232f2393bc693611c2d3ae9fa0e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "a4480305cdfe7fdfcbb77d1092c76161626d9a7aa4fb698aee745996e34602df"},
7+
"ecto": {:git, "https://github.com/zachdaniel/ecto.git", "e15b707b6c623517f18906e970a77adef6c9f96b", [branch: "subqueries-in-order-by"]},
98
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
109
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
1110
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},

test/ecto/adapters/myxql_test.exs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,22 @@ defmodule Ecto.Adapters.MyXQLTest do
392392

393393
all(query)
394394
end
395+
396+
assert_raise Ecto.QueryError,
397+
~r"DISTINCT with multiple columns is not supported by MySQL", fn ->
398+
query =
399+
from(row in Schema, as: :r, select: row.x)
400+
|> distinct(
401+
exists(
402+
from other_schema in "schema",
403+
where: other_schema.x == parent_as(:r).x,
404+
select: [other_schema.x]
405+
)
406+
)
407+
|> plan()
408+
409+
all(query)
410+
end
395411
end
396412

397413
test "coalesce" do
@@ -446,6 +462,23 @@ defmodule Ecto.Adapters.MyXQLTest do
446462
Schema |> order_by([r], [{^dir, r.x}]) |> select([r], r.x) |> plan() |> all()
447463
end
448464
end
465+
466+
query =
467+
from(row in Schema, as: :r)
468+
|> order_by(
469+
asc:
470+
exists(
471+
from other_schema in "schema",
472+
where: other_schema.x == parent_as(:r).x,
473+
select: [other_schema.x]
474+
)
475+
)
476+
|> select([r], r.x)
477+
|> plan()
478+
479+
assert all(query) ==
480+
~s{SELECT s0.`x` FROM `schema` AS s0 ORDER BY (SELECT exists(SELECT ss0.`x` AS `result` FROM `schema` AS ss0 WHERE (ss0.`x` = s0.`x`)))}
481+
449482
end
450483

451484
test "union and union all" do
@@ -835,6 +868,21 @@ defmodule Ecto.Adapters.MyXQLTest do
835868

836869
query = Schema |> group_by([r], []) |> select([r], r.x) |> plan()
837870
assert all(query) == ~s{SELECT s0.`x` FROM `schema` AS s0}
871+
872+
query =
873+
from(row in Schema, as: :r, select: row.x)
874+
|> group_by(
875+
[r],
876+
exists(
877+
from other_schema in "schema",
878+
where: other_schema.x == parent_as(:r).x,
879+
select: [other_schema.x]
880+
)
881+
)
882+
|> plan()
883+
884+
assert all(query) ==
885+
~s{SELECT s0.`x` FROM `schema` AS s0 GROUP BY (SELECT exists(SELECT ss0.`x` AS `result` FROM `schema` AS ss0 WHERE (ss0.`x` = s0.`x`)))}
838886
end
839887

840888
test "interpolated values" do
@@ -1024,6 +1072,26 @@ defmodule Ecto.Adapters.MyXQLTest do
10241072
~s{SELECT s0.`x` FROM `schema` AS s0 WINDOW `w` AS (PARTITION BY s0.`x`)}
10251073
end
10261074

1075+
test "window with subquery" do
1076+
query =
1077+
from(row in Schema, as: :r)
1078+
|> select([r], r.x)
1079+
|> windows([r],
1080+
w: [
1081+
order_by:
1082+
exists(
1083+
from other_schema in "schema",
1084+
where: other_schema.x == parent_as(:r).x,
1085+
select: [other_schema.x]
1086+
)
1087+
]
1088+
)
1089+
|> plan
1090+
1091+
assert all(query) ==
1092+
~s{SELECT s0.`x` FROM `schema` AS s0 WINDOW `w` AS (ORDER BY exists(SELECT ss0.`x` AS `result` FROM `schema` AS ss0 WHERE (ss0.`x` = s0.`x`)))}
1093+
end
1094+
10271095
test "two windows" do
10281096
query =
10291097
Schema

test/ecto/adapters/postgres_test.exs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,20 @@ defmodule Ecto.Adapters.PostgresTest do
530530

531531
query = Schema |> distinct(false) |> select([r], {r.x, r.y}) |> plan()
532532
assert all(query) == ~s{SELECT s0."x", s0."y" FROM "schema" AS s0}
533+
534+
query =
535+
from(row in Schema, as: :r, select: row.x)
536+
|> distinct(
537+
exists(
538+
from other_schema in "schema",
539+
where: other_schema.x == parent_as(:r).x,
540+
select: [other_schema.x]
541+
)
542+
)
543+
|> plan()
544+
545+
assert all(query) ==
546+
~s{SELECT DISTINCT ON (exists((SELECT ss0."x" AS "result" FROM "schema" AS ss0 WHERE (ss0."x" = s0."x")))) s0."x" FROM "schema" AS s0}
533547
end
534548

535549
test "distinct with order by" do
@@ -618,6 +632,22 @@ defmodule Ecto.Adapters.PostgresTest do
618632
assert all(query) ==
619633
~s{SELECT s0."x" FROM "schema" AS s0 ORDER BY s0."x" ASC NULLS FIRST, s0."y" DESC NULLS FIRST}
620634

635+
query =
636+
from(row in Schema, as: :r)
637+
|> order_by(
638+
asc:
639+
exists(
640+
from other_schema in "schema",
641+
where: other_schema.x == parent_as(:r).x,
642+
select: [other_schema.x]
643+
)
644+
)
645+
|> select([r], r.x)
646+
|> plan()
647+
648+
assert all(query) ==
649+
~s{SELECT s0."x" FROM "schema" AS s0 ORDER BY exists((SELECT ss0."x" AS "result" FROM "schema" AS ss0 WHERE (ss0."x" = s0."x")))}
650+
621651
query =
622652
Schema
623653
|> order_by([r], asc_nulls_last: r.x, desc_nulls_last: r.y)
@@ -1057,6 +1087,21 @@ defmodule Ecto.Adapters.PostgresTest do
10571087

10581088
query = Schema |> group_by([r], []) |> select([r], r.x) |> plan()
10591089
assert all(query) == ~s{SELECT s0."x" FROM "schema" AS s0}
1090+
1091+
query =
1092+
from(row in Schema, as: :r, select: row.x)
1093+
|> group_by(
1094+
[r],
1095+
exists(
1096+
from other_schema in "schema",
1097+
where: other_schema.x == parent_as(:r).x,
1098+
select: [other_schema.x]
1099+
)
1100+
)
1101+
|> plan()
1102+
1103+
assert all(query) ==
1104+
~s{SELECT s0."x" FROM "schema" AS s0 GROUP BY exists((SELECT ss0."x" AS "result" FROM "schema" AS ss0 WHERE (ss0."x" = s0."x")))}
10601105
end
10611106

10621107
test "arrays and sigils" do
@@ -1364,6 +1409,26 @@ defmodule Ecto.Adapters.PostgresTest do
13641409
~s{SELECT s0."x" FROM "schema" AS s0 WINDOW "w" AS (PARTITION BY s0."x")}
13651410
end
13661411

1412+
test "window with subquery" do
1413+
query =
1414+
from(row in Schema, as: :r)
1415+
|> select([r], r.x)
1416+
|> windows([r],
1417+
w: [
1418+
order_by:
1419+
exists(
1420+
from other_schema in "schema",
1421+
where: other_schema.x == parent_as(:r).x,
1422+
select: [other_schema.x]
1423+
)
1424+
]
1425+
)
1426+
|> plan
1427+
1428+
assert all(query) ==
1429+
~s{SELECT s0."x" FROM "schema" AS s0 WINDOW "w" AS (ORDER BY exists((SELECT ss0."x" AS "result" FROM "schema" AS ss0 WHERE (ss0."x" = s0."x"))))}
1430+
end
1431+
13671432
test "two windows" do
13681433
query =
13691434
Schema

test/ecto/adapters/tds_test.exs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,22 @@ defmodule Ecto.Adapters.TdsTest do
463463

464464
all(query)
465465
end
466+
467+
assert_raise Ecto.QueryError,
468+
~r"DISTINCT with multiple columns is not supported by MsSQL", fn ->
469+
query =
470+
from(row in Schema, as: :r, select: row.x)
471+
|> distinct(
472+
exists(
473+
from other_schema in "schema",
474+
where: other_schema.x == parent_as(:r).x,
475+
select: [other_schema.x]
476+
)
477+
)
478+
|> plan()
479+
480+
all(query)
481+
end
466482
end
467483

468484
test "select with operation" do
@@ -499,6 +515,22 @@ defmodule Ecto.Adapters.TdsTest do
499515
query = Schema |> order_by([r], []) |> select([r], r.x) |> plan()
500516
assert all(query) == ~s{SELECT s0.[x] FROM [schema] AS s0}
501517

518+
query =
519+
from(row in Schema, as: :r)
520+
|> order_by(
521+
asc:
522+
exists(
523+
from other_schema in "schema",
524+
where: other_schema.x == parent_as(:r).x,
525+
select: [other_schema.x]
526+
)
527+
)
528+
|> select([r], r.x)
529+
|> plan()
530+
531+
assert all(query) ==
532+
~s{SELECT s0.[x] FROM [schema] AS s0 ORDER BY exists((SELECT ss0.[x] AS [result] FROM [schema] AS ss0 WHERE (ss0.[x] = s0.[x])))}
533+
502534
for dir <- [:asc_nulls_first, :asc_nulls_last, :desc_nulls_first, :desc_nulls_last] do
503535
assert_raise Ecto.QueryError, ~r"#{dir} is not supported in ORDER BY in MSSQL", fn ->
504536
Schema |> order_by([r], [{^dir, r.x}]) |> select([r], r.x) |> plan() |> all()
@@ -854,6 +886,21 @@ defmodule Ecto.Adapters.TdsTest do
854886

855887
query = Schema |> group_by([r], []) |> select([r], r.x) |> plan()
856888
assert all(query) == ~s{SELECT s0.[x] FROM [schema] AS s0}
889+
890+
query =
891+
from(row in Schema, as: :r, select: row.x)
892+
|> group_by(
893+
[r],
894+
exists(
895+
from other_schema in "schema",
896+
where: other_schema.x == parent_as(:r).x,
897+
select: [other_schema.x]
898+
)
899+
)
900+
|> plan()
901+
902+
assert all(query) ==
903+
~s{SELECT s0.[x] FROM [schema] AS s0 GROUP BY exists((SELECT ss0.[x] AS [result] FROM [schema] AS ss0 WHERE (ss0.[x] = s0.[x])))}
857904
end
858905

859906
test "interpolated values" do

0 commit comments

Comments
 (0)