Skip to content

Commit c87024c

Browse files
authored
Add Repo.transact/2 and soft-deprecate transaction/2 (#4618)
1 parent 86a6896 commit c87024c

7 files changed

Lines changed: 423 additions & 60 deletions

File tree

integration_test/cases/repo.exs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,4 +2304,94 @@ defmodule Ecto.Integration.RepoTest do
23042304
defp uuid_module(Ecto.Adapters.Tds), do: Tds.Ecto.UUID
23052305
defp uuid_module(_), do: Ecto.UUID
23062306
end
2307+
2308+
describe "transact/2 with function" do
2309+
test "return ok" do
2310+
assert {:ok, [post1, post2]} =
2311+
TestRepo.transact(fn ->
2312+
post1 = TestRepo.insert!(%Post{title: "1"})
2313+
post2 = TestRepo.insert!(%Post{title: "2"})
2314+
{:ok, [post1, post2]}
2315+
end)
2316+
2317+
assert TestRepo.all(Post) |> Enum.sort() == [post1, post2]
2318+
end
2319+
2320+
test "return error" do
2321+
assert {:error, :oops} =
2322+
TestRepo.transact(fn ->
2323+
TestRepo.insert!(%Post{title: "1"})
2324+
TestRepo.insert!(%Post{title: "2"})
2325+
{:error, :oops}
2326+
end)
2327+
2328+
assert TestRepo.all(Post) == []
2329+
end
2330+
2331+
test "rollback" do
2332+
assert {:error, :oops} =
2333+
TestRepo.transact(fn ->
2334+
TestRepo.insert!(%Post{title: "1"})
2335+
TestRepo.insert!(%Post{title: "2"})
2336+
TestRepo.rollback(:oops)
2337+
raise "unreachable"
2338+
end)
2339+
2340+
assert TestRepo.all(Post) == []
2341+
end
2342+
2343+
test "raise error" do
2344+
assert_raise RuntimeError, "oops", fn ->
2345+
TestRepo.transact(fn ->
2346+
TestRepo.insert!(%Post{title: "1"})
2347+
TestRepo.insert!(%Post{title: "2"})
2348+
raise "oops"
2349+
end)
2350+
end
2351+
2352+
assert TestRepo.all(Post) == []
2353+
end
2354+
end
2355+
2356+
describe "transact/2 with multi" do
2357+
test "ok" do
2358+
multi = Ecto.Multi.new()
2359+
|> Ecto.Multi.insert(:post1, %Post{title: "1"})
2360+
|> Ecto.Multi.insert(:post2, %Post{title: "2"})
2361+
2362+
assert {:ok, %{post1: post1, post2: post2}} =
2363+
TestRepo.transact(multi)
2364+
2365+
assert TestRepo.all(Post) |> Enum.sort() == [post1, post2]
2366+
end
2367+
2368+
test "error" do
2369+
changeset =
2370+
Ecto.Changeset.change(%Post{})
2371+
|> Ecto.Changeset.add_error(:title, "invalid")
2372+
2373+
multi =
2374+
Ecto.Multi.new()
2375+
|> Ecto.Multi.insert(:post1, %Post{title: "1"})
2376+
|> Ecto.Multi.insert(:post2, fn _ -> changeset end)
2377+
2378+
assert {:error, :post2, changeset, %{post1: %Post{title: "1"}}} =
2379+
TestRepo.transact(multi)
2380+
2381+
refute changeset.valid?
2382+
end
2383+
end
2384+
2385+
describe "transaction/2 (soft-deprecated)" do
2386+
test "ok" do
2387+
assert {:ok, {:ok, [post1, post2]}} =
2388+
TestRepo.transaction(fn ->
2389+
post1 = TestRepo.insert!(%Post{title: "1"})
2390+
post2 = TestRepo.insert!(%Post{title: "2"})
2391+
{:ok, [post1, post2]}
2392+
end)
2393+
2394+
assert TestRepo.all(Post) |> Enum.sort() == [post1, post2]
2395+
end
2396+
end
23072397
end

lib/ecto/multi.ex

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ defmodule Ecto.Multi do
6161
6262
We can later execute it in the integration layer using Repo:
6363
64-
Repo.transaction(PasswordManager.reset(account, params))
64+
Repo.transact(PasswordManager.reset(account, params))
6565
6666
By pattern matching on the result we can differentiate different conditions:
6767
@@ -253,7 +253,7 @@ defmodule Ecto.Multi do
253253
Ecto.Multi.new()
254254
|> Ecto.Multi.insert(:comment, Ecto.build_assoc(post, :comments))
255255
end)
256-
|> MyApp.Repo.transaction()
256+
|> MyApp.Repo.transact()
257257
"""
258258
@spec merge(t, (changes -> t)) :: t
259259
def merge(%Multi{} = multi, merge) when is_function(merge, 1) do
@@ -286,14 +286,14 @@ defmodule Ecto.Multi do
286286
287287
Ecto.Multi.new()
288288
|> Ecto.Multi.insert(:insert, %Post{title: "first"})
289-
|> MyApp.Repo.transaction()
289+
|> MyApp.Repo.transact()
290290
291291
Ecto.Multi.new()
292292
|> Ecto.Multi.insert(:post, %Post{title: "first"})
293293
|> Ecto.Multi.insert(:comment, fn %{post: post} ->
294294
Ecto.build_assoc(post, :comments)
295295
end)
296-
|> MyApp.Repo.transaction()
296+
|> MyApp.Repo.transact()
297297
298298
"""
299299
@spec insert(
@@ -329,14 +329,14 @@ defmodule Ecto.Multi do
329329
changeset = Ecto.Changeset.change(post, title: "New title")
330330
Ecto.Multi.new()
331331
|> Ecto.Multi.update(:update, changeset)
332-
|> MyApp.Repo.transaction()
332+
|> MyApp.Repo.transact()
333333
334334
Ecto.Multi.new()
335335
|> Ecto.Multi.insert(:post, %Post{title: "first"})
336336
|> Ecto.Multi.update(:fun, fn %{post: post} ->
337337
Ecto.Changeset.change(post, title: "New title")
338338
end)
339-
|> MyApp.Repo.transaction()
339+
|> MyApp.Repo.transact()
340340
341341
"""
342342
@spec update(t, name, Changeset.t() | (changes -> Changeset.t()), Keyword.t()) :: t
@@ -362,7 +362,7 @@ defmodule Ecto.Multi do
362362
changeset = Post.changeset(%Post{}, %{title: "New title"})
363363
Ecto.Multi.new()
364364
|> Ecto.Multi.insert_or_update(:insert_or_update, changeset)
365-
|> MyApp.Repo.transaction()
365+
|> MyApp.Repo.transact()
366366
367367
Ecto.Multi.new()
368368
|> Ecto.Multi.run(:post, fn repo, _changes ->
@@ -371,7 +371,7 @@ defmodule Ecto.Multi do
371371
|> Ecto.Multi.insert_or_update(:update, fn %{post: post} ->
372372
Ecto.Changeset.change(post, title: "New title")
373373
end)
374-
|> MyApp.Repo.transaction()
374+
|> MyApp.Repo.transact()
375375
376376
"""
377377
@spec insert_or_update(t, name, Changeset.t() | (changes -> Changeset.t()), Keyword.t()) :: t
@@ -406,7 +406,7 @@ defmodule Ecto.Multi do
406406
post = MyApp.Repo.get!(Post, 1)
407407
Ecto.Multi.new()
408408
|> Ecto.Multi.delete(:delete, post)
409-
|> MyApp.Repo.transaction()
409+
|> MyApp.Repo.transact()
410410
411411
Ecto.Multi.new()
412412
|> Ecto.Multi.run(:post, fn repo, _changes ->
@@ -419,7 +419,7 @@ defmodule Ecto.Multi do
419419
# Others validations
420420
post
421421
end)
422-
|> MyApp.Repo.transaction()
422+
|> MyApp.Repo.transact()
423423
424424
"""
425425
@spec delete(
@@ -456,7 +456,7 @@ defmodule Ecto.Multi do
456456
|> Ecto.Multi.one(:author, fn %{post: post} ->
457457
from(a in Author, where: a.id == ^post.author_id)
458458
end)
459-
|> MyApp.Repo.transaction()
459+
|> MyApp.Repo.transact()
460460
"""
461461
@spec one(
462462
t,
@@ -485,11 +485,11 @@ defmodule Ecto.Multi do
485485
486486
Ecto.Multi.new()
487487
|> Ecto.Multi.all(:all, Post)
488-
|> MyApp.Repo.transaction()
488+
|> MyApp.Repo.transact()
489489
490490
Ecto.Multi.new()
491491
|> Ecto.Multi.all(:all, fn _changes -> Post end)
492-
|> MyApp.Repo.transaction()
492+
|> MyApp.Repo.transact()
493493
"""
494494
@spec all(
495495
t,
@@ -518,11 +518,11 @@ defmodule Ecto.Multi do
518518
519519
Ecto.Multi.new()
520520
|> Ecto.Multi.exists?(:post, Post)
521-
|> MyApp.Repo.transaction()
521+
|> MyApp.Repo.transact()
522522
523523
Ecto.Multi.new()
524524
|> Ecto.Multi.exists?(:post, fn _changes -> Post end)
525-
|> MyApp.Repo.transaction()
525+
|> MyApp.Repo.transact()
526526
"""
527527
@spec exists?(
528528
t,
@@ -614,7 +614,7 @@ defmodule Ecto.Multi do
614614
posts = [%{title: "My first post"}, %{title: "My second post"}]
615615
Ecto.Multi.new()
616616
|> Ecto.Multi.insert_all(:insert_all, Post, posts)
617-
|> MyApp.Repo.transaction()
617+
|> MyApp.Repo.transact()
618618
619619
Ecto.Multi.new()
620620
|> Ecto.Multi.run(:post, fn repo, _changes ->
@@ -631,7 +631,7 @@ defmodule Ecto.Multi do
631631
Map.put(comment, :post_id, post.id)
632632
end)
633633
end)
634-
|> MyApp.Repo.transaction()
634+
|> MyApp.Repo.transact()
635635
636636
"""
637637
@spec insert_all(
@@ -662,7 +662,7 @@ defmodule Ecto.Multi do
662662
663663
Ecto.Multi.new()
664664
|> Ecto.Multi.update_all(:update_all, Post, set: [title: "New title"])
665-
|> MyApp.Repo.transaction()
665+
|> MyApp.Repo.transact()
666666
667667
Ecto.Multi.new()
668668
|> Ecto.Multi.run(:post, fn repo, _changes ->
@@ -675,7 +675,7 @@ defmodule Ecto.Multi do
675675
# Others validations
676676
from(c in Comment, where: c.post_id == ^post.id, update: [set: [title: "New title"]])
677677
end, [])
678-
|> MyApp.Repo.transaction()
678+
|> MyApp.Repo.transact()
679679
680680
"""
681681
@spec update_all(
@@ -707,7 +707,7 @@ defmodule Ecto.Multi do
707707
queryable = from(p in Post, where: p.id < 5)
708708
Ecto.Multi.new()
709709
|> Ecto.Multi.delete_all(:delete_all, queryable)
710-
|> MyApp.Repo.transaction()
710+
|> MyApp.Repo.transact()
711711
712712
Ecto.Multi.new()
713713
|> Ecto.Multi.run(:post, fn repo, _changes ->
@@ -720,7 +720,7 @@ defmodule Ecto.Multi do
720720
# Others validations
721721
from(c in Comment, where: c.post_id == ^post.id)
722722
end)
723-
|> MyApp.Repo.transaction()
723+
|> MyApp.Repo.transact()
724724
725725
"""
726726
@spec delete_all(t, name, Ecto.Queryable.t() | (changes -> Ecto.Queryable.t()), Keyword.t()) ::
@@ -782,7 +782,7 @@ defmodule Ecto.Multi do
782782
|> Ecto.Multi.put(:company, company)
783783
|> Ecto.Multi.insert(:user, fn changes -> User.changeset(changes.company) end)
784784
|> Ecto.Multi.insert(:person, fn changes -> Person.changeset(changes.user, changes.company) end)
785-
|> MyApp.Repo.transaction()
785+
|> MyApp.Repo.transact()
786786
787787
In the example above there isn't a large benefit in putting the
788788
`company` in the multi, because you could also access the
@@ -818,7 +818,7 @@ defmodule Ecto.Multi do
818818
|> Ecto.Multi.insert(:person_a, changeset)
819819
|> Ecto.Multi.insert(:person_b, changeset)
820820
|> Ecto.Multi.inspect()
821-
|> MyApp.Repo.transaction()
821+
|> MyApp.Repo.transact()
822822
823823
Prints:
824824
%{person_a: %Person{...}, person_b: %Person{...}}
@@ -829,7 +829,7 @@ defmodule Ecto.Multi do
829829
|> Ecto.Multi.insert(:person_a, changeset)
830830
|> Ecto.Multi.insert(:person_b, changeset)
831831
|> Ecto.Multi.inspect(only: :person_a)
832-
|> MyApp.Repo.transaction()
832+
|> MyApp.Repo.transact()
833833
834834
Prints:
835835
%{person_a: %Person{...}}

0 commit comments

Comments
 (0)