Skip to content

Commit

Permalink
Add parent_as support to MyXQL
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Apr 14, 2020
1 parent c9a7cc2 commit 62967b2
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 27 deletions.
43 changes: 26 additions & 17 deletions lib/ecto/adapters/myxql/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ if Code.ensure_loaded?(MyXQL) do

alias Ecto.Query.{BooleanExpr, JoinExpr, QueryExpr, WithExpr}

@parent_as 0

@impl true
def all(query) do
sources = create_names(query)
def all(query, as_prefix \\ []) do
sources = create_names(query, as_prefix)

cte = cte(query, sources)
from = from(query, sources)
Expand All @@ -102,7 +104,7 @@ if Code.ensure_loaded?(MyXQL) do
error!(nil, ":select is not supported in update_all by MySQL")
end

sources = create_names(query)
sources = create_names(query, [])
cte = cte(query, sources)
{from, name} = get_source(query, sources, 0, source)

Expand All @@ -125,7 +127,7 @@ if Code.ensure_loaded?(MyXQL) do
error!(nil, ":select is not supported in delete_all by MySQL")
end

sources = create_names(query)
sources = create_names(query, [])
cte = cte(query, sources)
{_, name, _} = elem(sources, 0)

Expand Down Expand Up @@ -450,6 +452,12 @@ if Code.ensure_loaded?(MyXQL) do
'?'
end

defp expr({{:., _, [{:parent_as, _, [{:&, _, [idx]}]}, field]}, _, []}, _sources, query)
when is_atom(field) do
{_, name, _} = elem(query.aliases[@parent_as], idx)
[name, ?. | quote_name(field)]
end

defp expr({{:., _, [{:&, _, [idx]}, field]}, _, []}, sources, _query)
when is_atom(field) do
{_, name, _} = elem(sources, idx)
Expand Down Expand Up @@ -495,8 +503,9 @@ if Code.ensure_loaded?(MyXQL) do
error!(query, "MySQL adapter does not support aggregate filters")
end

defp expr(%Ecto.SubQuery{query: query}, _sources, _query) do
[?(, all(query), ?)]
defp expr(%Ecto.SubQuery{query: query}, sources, _query) do
query = put_in(query.aliases[@parent_as], sources)
[?(, all(query, [?s]), ?)]
end

defp expr({:fragment, _, [kw]}, _sources, query) when is_list(kw) or tuple_size(kw) == 3 do
Expand Down Expand Up @@ -627,37 +636,37 @@ if Code.ensure_loaded?(MyXQL) do
defp op_to_binary(expr, sources, query),
do: expr(expr, sources, query)

defp create_names(%{sources: sources}) do
create_names(sources, 0, tuple_size(sources)) |> List.to_tuple()
defp create_names(%{sources: sources}, as_prefix) do
create_names(sources, 0, tuple_size(sources), as_prefix) |> List.to_tuple()
end

defp create_names(sources, pos, limit) when pos < limit do
[create_name(sources, pos) | create_names(sources, pos + 1, limit)]
defp create_names(sources, pos, limit, as_prefix) when pos < limit do
[create_name(sources, pos, as_prefix) | create_names(sources, pos + 1, limit, as_prefix)]
end

defp create_names(_sources, pos, pos) do
defp create_names(_sources, pos, pos, _as_prefix) do
[]
end

defp create_name(sources, pos) do
defp create_name(sources, pos, as_prefix) do
case elem(sources, pos) do
{:fragment, _, _} ->
{nil, [?f | Integer.to_string(pos)], nil}
{nil, as_prefix ++ [?f | Integer.to_string(pos)], nil}

{table, schema, prefix} ->
name = [create_alias(table) | Integer.to_string(pos)]
name = as_prefix ++ [create_alias(table) | Integer.to_string(pos)]
{quote_table(prefix, table), name, schema}

%Ecto.SubQuery{} ->
{nil, [?s | Integer.to_string(pos)], nil}
{nil, as_prefix ++ [?s | Integer.to_string(pos)], nil}
end
end

defp create_alias(<<first, _rest::binary>>) when first in ?a..?z when first in ?A..?Z do
<<first>>
first
end
defp create_alias(_) do
"t"
?t
end

## DDL
Expand Down
9 changes: 3 additions & 6 deletions lib/ecto/adapters/tds/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,7 @@ if Code.ensure_loaded?(Tds) do
## Query

alias Ecto.Query
alias Ecto.Query.QueryExpr
alias Ecto.Query.JoinExpr
alias Ecto.Query.BooleanExpr
alias Ecto.Query.WithExpr
alias Ecto.Query.{BooleanExpr, JoinExpr, QueryExpr, WithExpr}

@parent_as 0

Expand Down Expand Up @@ -620,13 +617,13 @@ if Code.ensure_loaded?(Tds) do
defp expr({{:., _, [{:parent_as, _, [{:&, _, [idx]}]}, field]}, _, []}, _sources, query)
when is_atom(field) do
{_, name, _} = elem(query.aliases[@parent_as], idx)
[name, ?., quote_name(field)]
[name, ?. | quote_name(field)]
end

defp expr({{:., _, [{:&, _, [idx]}, field]}, _, []}, sources, _query)
when is_atom(field) or is_binary(field) do
{_, name, _} = elem(sources, idx)
[name, ?., quote_name(field)]
[name, ?. | quote_name(field)]
end

defp expr({:&, _, [idx]}, sources, _query) do
Expand Down
14 changes: 10 additions & 4 deletions test/ecto/adapters/myxql_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ defmodule Ecto.Adapters.MyXQLTest do

test "from with subquery" do
query = subquery("posts" |> select([r], %{x: r.x, y: r.y})) |> select([r], r.x) |> plan()
assert all(query) == ~s{SELECT s0.`x` FROM (SELECT p0.`x` AS `x`, p0.`y` AS `y` FROM `posts` AS p0) AS s0}
assert all(query) == ~s{SELECT s0.`x` FROM (SELECT sp0.`x` AS `x`, sp0.`y` AS `y` FROM `posts` AS sp0) AS s0}

query = subquery("posts" |> select([r], %{x: r.x, z: r.y})) |> select([r], r) |> plan()
assert all(query) == ~s{SELECT s0.`x`, s0.`z` FROM (SELECT p0.`x` AS `x`, p0.`y` AS `z` FROM `posts` AS p0) AS s0}
assert all(query) == ~s{SELECT s0.`x`, s0.`z` FROM (SELECT sp0.`x` AS `x`, sp0.`y` AS `z` FROM `posts` AS sp0) AS s0}
end

test "CTE" do
Expand Down Expand Up @@ -818,13 +818,19 @@ defmodule Ecto.Adapters.MyXQLTest do
query = "comments" |> join(:inner, [c], p in subquery(posts), on: true) |> select([_, p], p.x) |> plan()
assert all(query) ==
~s{SELECT s1.`x` FROM `comments` AS c0 } <>
~s{INNER JOIN (SELECT p0.`x` AS `x`, p0.`y` AS `y` FROM `posts` AS p0 WHERE (p0.`title` = ?)) AS s1 ON TRUE}
~s{INNER JOIN (SELECT sp0.`x` AS `x`, sp0.`y` AS `y` FROM `posts` AS sp0 WHERE (sp0.`title` = ?)) AS s1 ON TRUE}

posts = subquery("posts" |> where(title: ^"hello") |> select([r], %{x: r.x, z: r.y}))
query = "comments" |> join(:inner, [c], p in subquery(posts), on: true) |> select([_, p], p) |> plan()
assert all(query) ==
~s{SELECT s1.`x`, s1.`z` FROM `comments` AS c0 } <>
~s{INNER JOIN (SELECT p0.`x` AS `x`, p0.`y` AS `z` FROM `posts` AS p0 WHERE (p0.`title` = ?)) AS s1 ON TRUE}
~s{INNER JOIN (SELECT sp0.`x` AS `x`, sp0.`y` AS `z` FROM `posts` AS sp0 WHERE (sp0.`title` = ?)) AS s1 ON TRUE}

posts = subquery("posts" |> where(title: parent_as(:comment).subtitle) |> select([r], r.title))
query = "comments" |> from(as: :comment) |> join(:inner, [c], p in subquery(posts)) |> select([_, p], p) |> plan()
assert all(query) ==
"SELECT s1.`title` FROM `comments` AS c0 " <>
"INNER JOIN (SELECT sp0.`title` AS `title` FROM `posts` AS sp0 WHERE (sp0.`title` = c0.`subtitle`)) AS s1 ON TRUE"
end

test "join with prefix" do
Expand Down

0 comments on commit 62967b2

Please sign in to comment.