Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 35 additions & 64 deletions lib/ecto/adapters/sqlite3/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -198,27 +198,14 @@ defmodule Ecto.Adapters.SQLite3.Connection do
cte = cte(query, sources)
{from, name} = get_source(query, sources, 0, source)

fields =
if prefix do
update_fields(:on_conflict, query, sources)
else
update_fields(:update, query, sources)
end

# TODO: Add support for `update or rollback foo`

{join, wheres} = using_join(query, :update_all, "FROM", sources)
prefix = prefix || ["UPDATE ", from, " AS ", name, " SET "]
fields = update_fields(query, sources)
{join, wheres} = using_join(query, :update_all, "FROM", sources)
where = where(%{query | wheres: wheres ++ query.wheres}, sources)

[
cte,
prefix,
fields,
join,
where,
returning(query, sources)
]
[cte, prefix, fields, join, where | returning(query, sources)]
end

@impl true
Expand Down Expand Up @@ -661,41 +648,27 @@ defmodule Ecto.Adapters.SQLite3.Connection do
## Query generation
##

defp on_conflict({:raise, _, []}, _header), do: []

defp on_conflict({:nothing, _, targets}, _header) do
[" ON CONFLICT ", conflict_target(targets) | "DO NOTHING"]
end

defp on_conflict({:replace_all, _, {:constraint, _}}, _header) do
defp on_conflict({:raise, _, []}, _header),
do: []
defp on_conflict({:nothing, _, targets}, _header),
do: [" ON CONFLICT ", conflict_target(targets) | "DO NOTHING"]
defp on_conflict({:replace_all, _, {:constraint, _}}, _header), do:
raise ArgumentError, "Upsert in SQLite3 does not support ON CONSTRAINT"
end

defp on_conflict({:replace_all, _, []}, _header) do
raise ArgumentError, "Upsert in SQLite3 requires :conflict_target"
end

defp on_conflict({:replace_all, _, targets}, header) do
[" ON CONFLICT ", conflict_target(targets), "DO " | replace(header)]
end

defp on_conflict({fields, _, targets}, _header) when is_list(fields) do
[" ON CONFLICT ", conflict_target(targets), "DO " | replace(fields)]
end

defp on_conflict({query, _, targets}, _header) do
[
" ON CONFLICT ",
conflict_target(targets),
"DO " | update_all(query, "UPDATE SET ")
]
end

defp conflict_target([]), do: ""

defp conflict_target(targets) do
[?(, intersperse_map(targets, ?,, &quote_name/1), ?), ?\s]
end
defp on_conflict({fields, _, targets}, _header) when is_list(fields),
do: [" ON CONFLICT ", conflict_target!(targets), "DO " | replace(fields)]
defp on_conflict({query, _, targets}, _header),
do: [" ON CONFLICT ", conflict_target!(targets), "DO " | update_all(query, "UPDATE SET ")]

defp conflict_target!([]),
do: error!(nil, "the :conflict_target option is required on upserts by SQLite3")
defp conflict_target!(target),
do: conflict_target(target)

defp conflict_target({:unsafe_fragment, fragment}),
do: [fragment, ?\s]
defp conflict_target([]), do: []
defp conflict_target(targets),
do: [?(, quote_names(targets), ?), ?\s]

defp replace(fields) do
[
Expand Down Expand Up @@ -873,20 +846,11 @@ defmodule Ecto.Adapters.SQLite3.Connection do
expr(expr, sources, query)
end

defp update_fields(type, %{updates: updates} = query, sources) do
fields =
for(
%{expr: expression} <- updates,
{op, kw} <- expression,
{key, value} <- kw,
do: update_op(op, update_key(type, key, query, sources), value, sources, query)
)

Enum.intersperse(fields, ", ")
end

defp update_key(_kind, key, _query, _sources) do
quote_name(key)
defp update_fields(%{updates: updates} = query, sources) do
for(%{expr: expr} <- updates,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did mix format make this get all aligned on the same column?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me with the default I think not... The lines I carefully copy/pasted from postgrex. No special reason other than I kind of consider it the main upstream implementation and I guess the closer this file is to the upstream the easier to merge in changes going forward?

https://github.com/elixir-ecto/ecto_sql/blob/master/lib/ecto/adapters/postgres/connection.ex#L176

I wonder if they use a custom .formatter file, didn't think to check? For me also, the formatter will add extra row spaces between each of these clauses, but their formatting has them all bunched together?

{op, kw} <- expr,
{key, value} <- kw,
do: update_op(op, key, value, sources, query)) |> Enum.intersperse(", ")
end

defp update_op(:set, quoted_key, value, sources, query) do
Expand Down Expand Up @@ -1801,4 +1765,11 @@ defmodule Ecto.Adapters.SQLite3.Connection do
|> escape_string()
|> :binary.replace("\"", "\\\"", [:global])
end

defp error!(nil, message) do
raise ArgumentError, message
end
defp error!(query, message) do
raise Ecto.QueryError, query: query, message: message
end
end