Skip to content

Commit c8686ae

Browse files
Support selected_as/1 and selected_as/2 in psql, mysql and tds (#432)
1 parent 8150919 commit c8686ae

8 files changed

Lines changed: 129 additions & 2 deletions

File tree

integration_test/tds/test_helper.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ ExUnit.start(
4848
# MSSQL does not support streaming
4949
:stream,
5050
# MSSQL fails the regex matching because it uses square brackets outside of the parameter list
51-
:parameter_logging
51+
:parameter_logging,
52+
# MSSQL can't reference aliased columns in GROUP BY
53+
:selected_as_with_group_by
5254
]
5355
)
5456

lib/ecto/adapters/myxql/connection.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ if Code.ensure_loaded?(MyXQL) do
279279
end
280280
{key, value} ->
281281
[expr(value, sources, query), " AS ", quote_name(key)]
282+
{:selected_as, _, [field_expr, name]} ->
283+
[expr(field_expr, sources, query), " AS ", Atom.to_string(name)]
282284
value ->
283285
expr(value, sources, query)
284286
end)
@@ -572,6 +574,17 @@ if Code.ensure_loaded?(MyXQL) do
572574
quote_name(literal)
573575
end
574576

577+
defp expr({:selected_as, _, [name]}, _sources, _query) do
578+
[Atom.to_string(name)]
579+
end
580+
581+
defp expr({:selected_as, _, [_field_expr, _name]}, _sources, _query) do
582+
raise ArgumentError,
583+
"`selected_as/2` can only be used in the outer most `select` expression. " <>
584+
"If you are attempting to alias a field from a subquery, it is not allowed " <>
585+
"because subquery fields are automatically aliased by the corresponding map/struct key."
586+
end
587+
575588
defp expr({:datetime_add, _, [datetime, count, interval]}, sources, query) do
576589
["date_add(", expr(datetime, sources, query), ", ",
577590
interval(count, interval, sources, query) | ")"]

lib/ecto/adapters/postgres/connection.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ if Code.ensure_loaded?(Postgrex) do
374374
end
375375
{key, value} ->
376376
[expr(value, sources, query), " AS " | quote_name(key)]
377+
{:selected_as, _, [field_expr, name]} ->
378+
[expr(field_expr, sources, query), " AS ", Atom.to_string(name)]
377379
value ->
378380
expr(value, sources, query)
379381
end)
@@ -676,6 +678,17 @@ if Code.ensure_loaded?(Postgrex) do
676678
quote_name(literal)
677679
end
678680

681+
defp expr({:selected_as, _, [name]}, _sources, _query) do
682+
[Atom.to_string(name)]
683+
end
684+
685+
defp expr({:selected_as, _, [_field_expr, _name]}, _sources, _query) do
686+
raise ArgumentError,
687+
"`selected_as/2` can only be used in the outer most `select` expression. " <>
688+
"If you are attempting to alias a field from a subquery, it is not allowed " <>
689+
"because subquery fields are automatically aliased by the corresponding map/struct key."
690+
end
691+
679692
defp expr({:datetime_add, _, [datetime, count, interval]}, sources, query) do
680693
[expr(datetime, sources, query), type_unless_typed(datetime, "timestamp"), " + ",
681694
interval(count, interval, sources, query)]

lib/ecto/adapters/tds/connection.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ if Code.ensure_loaded?(Tds) do
409409
{key, value} ->
410410
[select_expr(value, sources, query), " AS ", quote_name(key)]
411411

412+
{:selected_as, _, [field_expr, name]} ->
413+
[expr(field_expr, sources, query), " AS ", Atom.to_string(name)]
414+
412415
value ->
413416
select_expr(value, sources, query)
414417
end)
@@ -764,6 +767,17 @@ if Code.ensure_loaded?(Tds) do
764767
quote_name(literal)
765768
end
766769

770+
defp expr({:selected_as, _, [name]}, _sources, _query) do
771+
[Atom.to_string(name)]
772+
end
773+
774+
defp expr({:selected_as, _, [_field_expr, _name]}, _sources, _query) do
775+
raise ArgumentError,
776+
"`selected_as/2` can only be used in the outer most `select` expression. " <>
777+
"If you are attempting to alias a field from a subquery, it is not allowed " <>
778+
"because subquery fields are automatically aliased by the corresponding map/struct key."
779+
end
780+
767781
defp expr({:datetime_add, _, [datetime, count, interval]}, sources, query) do
768782
[
769783
"DATEADD(",

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
77
"deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm", "e3bf435a54ed27b0ba3a01eb117ae017988804e136edcbe8a6a14c310daa966e"},
88
"earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"},
9-
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "479484c2dfe637cecb623293a712a2e77632b846", []},
9+
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "4f541892e738a4dfad66601e19db3567e3335c2c", []},
1010
"ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
1111
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
1212
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},

test/ecto/adapters/myxql_test.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,36 @@ defmodule Ecto.Adapters.MyXQLTest do
527527
assert all(query) == ~s{SELECT TRUE FROM `schema` AS s0 WHERE (s0.`foo` = (0 + 123.0))}
528528
end
529529

530+
test "aliasing a selected value with selected_as/2" do
531+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> plan()
532+
assert all(query) == ~s{SELECT s0.`x` AS integer FROM `schema` AS s0}
533+
534+
query = "schema" |> select([s], s.x |> coalesce(0) |> sum() |> selected_as(:integer)) |> plan()
535+
assert all(query) == ~s{SELECT sum(coalesce(s0.`x`, 0)) AS integer FROM `schema` AS s0}
536+
end
537+
538+
test "raises if selected_as/2 is used in a subquery" do
539+
message = ~r"`selected_as/2` can only be used in the outer most `select` expression."
540+
541+
assert_raise ArgumentError, message, fn ->
542+
subquery = "schema" |> select([s], %{x: selected_as(s.x, :integer)}) |> subquery() |> plan()
543+
all(subquery)
544+
end
545+
end
546+
547+
test "group_by can reference the alias of a selected value with selected_as/1" do
548+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> group_by(selected_as(:integer)) |> plan()
549+
assert all(query) == ~s{SELECT s0.`x` AS integer FROM `schema` AS s0 GROUP BY integer}
550+
end
551+
552+
test "order_by can reference the alias of a selected value with selected_as/1" do
553+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> order_by(selected_as(:integer)) |> plan()
554+
assert all(query) == ~s{SELECT s0.`x` AS integer FROM `schema` AS s0 ORDER BY integer}
555+
556+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> order_by([desc: selected_as(:integer)]) |> plan()
557+
assert all(query) == ~s{SELECT s0.`x` AS integer FROM `schema` AS s0 ORDER BY integer DESC}
558+
end
559+
530560
test "tagged type" do
531561
query = Schema |> select([], type(^"601d74e4-a8d3-4b6e-8365-eddb4c893327", Ecto.UUID)) |> plan()
532562
assert all(query) == ~s{SELECT CAST(? AS binary(16)) FROM `schema` AS s0}

test/ecto/adapters/postgres_test.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,36 @@ defmodule Ecto.Adapters.PostgresTest do
575575
assert all(query) == ~s{SELECT TRUE FROM "schema" AS s0 WHERE (s0."foo" = 123.0::float)}
576576
end
577577

578+
test "aliasing a selected value with selected_as/2" do
579+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> plan()
580+
assert all(query) == ~s{SELECT s0."x" AS integer FROM "schema" AS s0}
581+
582+
query = "schema" |> select([s], s.x |> coalesce(0) |> sum() |> selected_as(:integer)) |> plan()
583+
assert all(query) == ~s{SELECT sum(coalesce(s0."x", 0)) AS integer FROM "schema" AS s0}
584+
end
585+
586+
test "raises if selected_as/2 is used in a subquery" do
587+
message = ~r"`selected_as/2` can only be used in the outer most `select` expression."
588+
589+
assert_raise ArgumentError, message, fn ->
590+
subquery = "schema" |> select([s], %{x: selected_as(s.x, :integer)}) |> subquery() |> plan()
591+
all(subquery)
592+
end
593+
end
594+
595+
test "group_by can reference the alias of a selected value with selected_as/1" do
596+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> group_by(selected_as(:integer)) |> plan()
597+
assert all(query) == ~s{SELECT s0."x" AS integer FROM "schema" AS s0 GROUP BY integer}
598+
end
599+
600+
test "order_by can reference the alias of a selected value with selected_as/1" do
601+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> order_by(selected_as(:integer)) |> plan()
602+
assert all(query) == ~s{SELECT s0."x" AS integer FROM "schema" AS s0 ORDER BY integer}
603+
604+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> order_by([desc: selected_as(:integer)]) |> plan()
605+
assert all(query) == ~s{SELECT s0."x" AS integer FROM "schema" AS s0 ORDER BY integer DESC}
606+
end
607+
578608
test "datetime_add" do
579609
query = "schema" |> where([s], datetime_add(s.foo, 1, "month") > s.bar) |> select([], true) |> plan()
580610
assert all(query) == ~s{SELECT TRUE FROM "schema" AS s0 WHERE (s0."foo"::timestamp + interval '1 month' > s0."bar")}

test/ecto/adapters/tds_test.exs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,31 @@ defmodule Ecto.Adapters.TdsTest do
641641
assert all(query) == ~s{SELECT CAST(1 as bit) FROM [schema] AS s0 WHERE (s0.[foo] = 123.0)}
642642
end
643643

644+
test "aliasing a selected value with selected_as/2" do
645+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> plan()
646+
assert all(query) == ~s{SELECT s0.[x] AS integer FROM [schema] AS s0}
647+
648+
query = "schema" |> select([s], s.x |> coalesce(0) |> sum() |> selected_as(:integer)) |> plan()
649+
assert all(query) == ~s{SELECT sum(coalesce(s0.[x], 0)) AS integer FROM [schema] AS s0}
650+
end
651+
652+
test "raises if selected_as/2 is used in a subquery" do
653+
message = ~r"`selected_as/2` can only be used in the outer most `select` expression."
654+
655+
assert_raise ArgumentError, message, fn ->
656+
subquery = "schema" |> select([s], %{x: selected_as(s.x, :integer)}) |> subquery() |> plan()
657+
all(subquery)
658+
end
659+
end
660+
661+
test "order_by can reference the alias of a selected value with selected_as/1s" do
662+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> order_by(selected_as(:integer)) |> plan()
663+
assert all(query) == ~s{SELECT s0.[x] AS integer FROM [schema] AS s0 ORDER BY integer}
664+
665+
query = "schema" |> select([s], selected_as(s.x, :integer)) |> order_by([desc: selected_as(:integer)]) |> plan()
666+
assert all(query) == ~s{SELECT s0.[x] AS integer FROM [schema] AS s0 ORDER BY integer DESC}
667+
end
668+
644669
test "tagged type" do
645670
query =
646671
Schema |> select([], type(^"601d74e4-a8d3-4b6e-8365-eddb4c893327", Tds.Ecto.UUID)) |> plan()

0 commit comments

Comments
 (0)