Skip to content

Commit 6a31a54

Browse files
authored
Allow insert_all to be called with a query instead of rows (#308)
1 parent 7533407 commit 6a31a54

File tree

7 files changed

+56
-18
lines changed

7 files changed

+56
-18
lines changed

lib/ecto/adapters/myxql/connection.ex

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ if Code.ensure_loaded?(MyXQL) do
142142
@impl true
143143
def insert(prefix, table, header, rows, on_conflict, [], []) do
144144
fields = quote_names(header)
145-
["INSERT INTO ", quote_table(prefix, table), " (", fields, ") VALUES ",
145+
["INSERT INTO ", quote_table(prefix, table), " (", fields, ") ",
146146
insert_all(rows) | on_conflict(on_conflict, header)]
147147
end
148148
def insert(_prefix, _table, _header, _rows, _on_conflict, _returning, []) do
@@ -151,7 +151,7 @@ if Code.ensure_loaded?(MyXQL) do
151151
def insert(_prefix, _table, _header, _rows, _on_conflict, _returning, _placeholders) do
152152
error!(nil, ":placeholders is not supported by MySQL")
153153
end
154-
154+
155155
defp on_conflict({_, _, [_ | _]}, _header) do
156156
error!(nil, "The :conflict_target option is not supported in insert/insert_all by MySQL")
157157
end
@@ -176,10 +176,14 @@ if Code.ensure_loaded?(MyXQL) do
176176
error!(nil, "Using a query with :where in combination with the :on_conflict option is not supported by MySQL")
177177
end
178178

179-
defp insert_all(rows) do
180-
intersperse_map(rows, ?,, fn row ->
179+
defp insert_all(rows) when is_list(rows) do
180+
["VALUES ", intersperse_map(rows, ?,, fn row ->
181181
[?(, intersperse_map(row, ?,, &insert_all_value/1), ?)]
182-
end)
182+
end)]
183+
end
184+
185+
defp insert_all(%Ecto.Query{} = query) do
186+
[?(, all(query), ?)]
183187
end
184188

185189
defp insert_all_value(nil), do: "DEFAULT"

lib/ecto/adapters/postgres/connection.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ if Code.ensure_loaded?(Postgrex) do
158158
if header == [] do
159159
[" VALUES " | intersperse_map(rows, ?,, fn _ -> "(DEFAULT)" end)]
160160
else
161-
[?\s, ?(, quote_names(header), ") VALUES " | insert_all(rows, counter_offset)]
161+
[" (", quote_names(header), ") " | insert_all(rows, counter_offset)]
162162
end
163163

164164
["INSERT INTO ", quote_table(prefix, table), insert_as(on_conflict),
@@ -199,12 +199,16 @@ if Code.ensure_loaded?(Postgrex) do
199199
end)]
200200
end
201201

202+
defp insert_all(query = %Ecto.Query{}, _counter) do
203+
[?(, all(query), ?)]
204+
end
205+
202206
defp insert_all(rows, counter) do
203-
intersperse_reduce(rows, ?,, counter, fn row, counter ->
207+
["VALUES ", intersperse_reduce(rows, ?,, counter, fn row, counter ->
204208
{row, counter} = insert_each(row, counter)
205209
{[?(, row, ?)], counter}
206210
end)
207-
|> elem(0)
211+
|> elem(0)]
208212
end
209213

210214
defp insert_each(values, counter) do
@@ -217,7 +221,7 @@ if Code.ensure_loaded?(Postgrex) do
217221

218222
{:placeholder, placeholder_index}, counter ->
219223
{[?$ | placeholder_index], counter}
220-
224+
221225
_, counter ->
222226
{[?$ | Integer.to_string(counter)], counter + 1}
223227
end)

lib/ecto/adapters/sql.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,13 +642,16 @@ defmodule Ecto.Adapters.SQL do
642642
def insert_all(adapter_meta, schema_meta, conn, header, rows, on_conflict, returning, placeholders, opts) do
643643
%{source: source, prefix: prefix} = schema_meta
644644
{_, conflict_params, _} = on_conflict
645-
{rows, params} = unzip_inserts(header, rows)
645+
{rows, params} = case rows do
646+
{%Ecto.Query{} = query, params} -> {query, Enum.reverse(params)}
647+
rows -> unzip_inserts(header, rows)
648+
end
646649

647650
sql = conn.insert(prefix, source, header, rows, on_conflict, returning, placeholders)
648651

649652
opts = [{:cache_statement, "ecto_insert_all_#{source}"} | opts]
650653

651-
all_params = placeholders ++ Enum.reverse(params, conflict_params)
654+
all_params = placeholders ++ Enum.reverse(params, conflict_params)
652655
%{num_rows: num, rows: rows} =
653656
query!(adapter_meta, sql, all_params, opts)
654657

lib/ecto/adapters/tds/connection.ex

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ if Code.ensure_loaded?(Tds) do
212212
?(,
213213
quote_names(header),
214214
?),
215-
returning,
216-
" VALUES " | insert_all(rows, counter_offset)
215+
returning |
216+
insert_all(rows, counter_offset)
217217
]
218218
end
219219

@@ -228,12 +228,18 @@ if Code.ensure_loaded?(Tds) do
228228
error!(nil, "Tds adapter supports only on_conflict: :raise")
229229
end
230230

231+
defp insert_all(%Ecto.Query{} = query, _counter) do
232+
[?\s, all(query)]
233+
end
231234
defp insert_all(rows, counter) do
232-
intersperse_reduce(rows, ",", counter, fn row, counter ->
233-
{row, counter} = insert_each(row, counter)
234-
{[?(, row, ?)], counter}
235-
end)
236-
|> elem(0)
235+
sql =
236+
intersperse_reduce(rows, ",", counter, fn row, counter ->
237+
{row, counter} = insert_each(row, counter)
238+
{[?(, row, ?)], counter}
239+
end)
240+
|> elem(0)
241+
242+
[" VALUES " | sql]
237243
end
238244

239245
defp insert_each(values, counter) do

test/ecto/adapters/myxql_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,13 @@ defmodule Ecto.Adapters.MyXQLTest do
974974
assert query == ~s{INSERT INTO `schema` (`x`,`y`,`z`) VALUES (?,(SELECT s0.`id` FROM `schema` AS s0),?),(DEFAULT,DEFAULT,(SELECT s0.`id` FROM `schema` AS s0))}
975975
end
976976

977+
test "insert with query as rows" do
978+
query = from(s in "schema", select: %{ foo: fragment("3"), bar: s.bar }) |> plan(:all)
979+
query = insert(nil, "schema", [:foo, :bar], query, {:raise, [], []}, [])
980+
981+
assert query == ~s{INSERT INTO `schema` (`foo`,`bar`) (SELECT 3, s0.`bar` FROM `schema` AS s0)}
982+
end
983+
977984
test "update" do
978985
query = update(nil, "schema", [:id], [x: 1, y: 2], [])
979986
assert query == ~s{UPDATE `schema` SET `id` = ? WHERE `x` = ? AND `y` = ?}

test/ecto/adapters/postgres_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,13 @@ defmodule Ecto.Adapters.PostgresTest do
11471147
assert query == ~s{INSERT INTO "schema" ("x","y","z") VALUES ($1,(SELECT s0."id" FROM "schema" AS s0),$5),(DEFAULT,(SELECT s0."id" FROM "schema" AS s0),$8) RETURNING "id"}
11481148
end
11491149

1150+
test "insert with query as rows" do
1151+
query = from(s in "schema", select: %{ foo: fragment("3"), bar: s.bar }) |> plan(:all)
1152+
query = insert(nil, "schema", [:foo, :bar], query, {:raise, [], []}, [:foo])
1153+
1154+
assert query == ~s{INSERT INTO "schema" ("foo","bar") (SELECT 3, s0."bar" FROM "schema" AS s0) RETURNING "foo"}
1155+
end
1156+
11501157
test "update" do
11511158
query = update(nil, "schema", [:x, :y], [id: 1], [])
11521159
assert query == ~s{UPDATE "schema" SET "x" = $1, "y" = $2 WHERE "id" = $3}

test/ecto/adapters/tds_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,13 @@ defmodule Ecto.Adapters.TdsTest do
10701070
~s{INSERT INTO [schema] ([x],[y],[z]) VALUES (@1, (SELECT s0.[id] FROM [schema] AS s0), @4),(DEFAULT, DEFAULT, (SELECT s0.[id] FROM [schema] AS s0))}
10711071
end
10721072

1073+
test "insert with query as rows" do
1074+
query = from(s in "schema", select: %{ foo: fragment("3"), bar: s.bar }) |> plan(:all)
1075+
query = insert(nil, "schema", [:foo, :bar], query, {:raise, [], []}, [:foo])
1076+
1077+
assert query == ~s{INSERT INTO [schema] ([foo],[bar]) OUTPUT INSERTED.[foo] SELECT 3, s0.[bar] FROM [schema] AS s0}
1078+
end
1079+
10731080
test "update" do
10741081
query = update(nil, "schema", [:id], [:x, :y], [])
10751082
assert query == ~s{UPDATE [schema] SET [id] = @1 WHERE [x] = @2 AND [y] = @3}

0 commit comments

Comments
 (0)