Skip to content

Commit a23bf03

Browse files
authored
Add Tds support for :inner_lateral and :left_lateral (#284)
This commit updates the TDS adapter to support the :inner_lateral and :left_lateral joins, which map to OUTER APPLY and CROSS APPLY, respectively. For both of these joins, we don't need an ON clause. Therefore, the join_on/4 function headers have been updated to omit it. Lastly, these require that the join contents be wrapped within brackets (). So, the join_expr function is added to wrap provided join expression in brackets for these joins only.
1 parent 95ce740 commit a23bf03

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

lib/ecto/adapters/tds/connection.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ if Code.ensure_loaded?(Tds) do
491491
end
492492

493493
defp join_on(:cross, true, _sources, _query), do: []
494+
defp join_on(:inner_lateral, true, _sources, _query), do: []
495+
defp join_on(:left_lateral, true, _sources, _query), do: []
494496
defp join_on(_qual, true, _sources, _query), do: [" ON 1 = 1"]
495497
defp join_on(_qual, expr, sources, query), do: [" ON " | expr(expr, sources, query)]
496498

@@ -503,6 +505,8 @@ if Code.ensure_loaded?(Tds) do
503505
defp join_qual(:right), do: "RIGHT OUTER JOIN "
504506
defp join_qual(:full), do: "FULL OUTER JOIN "
505507
defp join_qual(:cross), do: "CROSS JOIN "
508+
defp join_qual(:inner_lateral), do: "CROSS APPLY "
509+
defp join_qual(:left_lateral), do: "OUTER APPLY "
506510

507511
defp where(%Query{wheres: wheres} = query, sources) do
508512
boolean(" WHERE ", wheres, sources, query)
@@ -623,7 +627,7 @@ if Code.ensure_loaded?(Tds) do
623627
defp operator_to_boolean(:or), do: " OR "
624628

625629
defp parens_for_select([first_expr | _] = expr) do
626-
if is_binary(first_expr) and String.starts_with?(first_expr, ["SELECT", "select"]) do
630+
if is_binary(first_expr) and String.match?(first_expr, ~r/^\s*select/i) do
627631
[?(, expr, ?)]
628632
else
629633
expr

test/ecto/adapters/tds_test.exs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@ defmodule Ecto.Adapters.TdsTest do
914914
~s{SELECT CAST(1 as bit) FROM [prefix].[schema] AS s0 INNER JOIN [prefix].[schema2] AS s1 ON s0.[x] = s1.[z]}
915915
end
916916

917-
test "join with fragment" do
917+
test "join with single line fragment" do
918918
query =
919919
Schema
920920
|> join(
@@ -932,6 +932,54 @@ defmodule Ecto.Adapters.TdsTest do
932932
~s{WHERE ((s0.[id] > 0) AND (s0.[id] < @3))}
933933
end
934934

935+
test "join with multi-line fragment" do
936+
query =
937+
Schema
938+
|> join(
939+
:inner,
940+
[p],
941+
q in fragment(~S"""
942+
SELECT *
943+
FROM schema2 AS s2
944+
WHERE s2.id = ? AND s2.field = ?
945+
""", p.x, ^10)
946+
)
947+
|> select([p], {p.id, ^0})
948+
|> where([p], p.id > 0 and p.id < ^100)
949+
|> plan()
950+
951+
assert all(query) ==
952+
~s{SELECT s0.[id], @1 FROM [schema] AS s0 INNER JOIN } <>
953+
~s{( SELECT *\n } <>
954+
~s{ FROM schema2 AS s2\n } <>
955+
~s{ WHERE s2.id = s0.[x] AND s2.field = @2\n} <>
956+
~s{) AS f1 ON 1 = 1 WHERE ((s0.[id] > 0) AND (s0.[id] < @3))}
957+
end
958+
959+
test "inner lateral join with fragment" do
960+
query = Schema
961+
|> join(:inner_lateral, [p], q in fragment("SELECT * FROM schema2 AS s2 WHERE s2.id = ? AND s2.field = ?", p.x, ^10))
962+
|> select([p, q], {p.id, q.z})
963+
|> where([p], p.id > 0 and p.id < ^100)
964+
|> plan()
965+
assert all(query) ==
966+
~s{SELECT s0.[id], f1.[z] FROM [schema] AS s0 CROSS APPLY } <>
967+
~s{(SELECT * FROM schema2 AS s2 WHERE s2.id = s0.[x] AND s2.field = @1) AS f1 } <>
968+
~s{WHERE ((s0.[id] > 0) AND (s0.[id] < @2))}
969+
end
970+
971+
test "left lateral join with fragment" do
972+
query = Schema
973+
|> join(:left_lateral, [p], q in fragment("SELECT * FROM schema2 AS s2 WHERE s2.id = ? AND s2.field = ?", p.x, ^10))
974+
|> select([p, q], {p.id, q.z})
975+
|> where([p], p.id > 0 and p.id < ^100)
976+
|> plan()
977+
assert all(query) ==
978+
~s{SELECT s0.[id], f1.[z] FROM [schema] AS s0 OUTER APPLY } <>
979+
~s{(SELECT * FROM schema2 AS s2 WHERE s2.id = s0.[x] AND s2.field = @1) AS f1 } <>
980+
~s{WHERE ((s0.[id] > 0) AND (s0.[id] < @2))}
981+
end
982+
935983
test "join with fragment and on defined" do
936984
query =
937985
Schema

0 commit comments

Comments
 (0)