Skip to content
This repository was archived by the owner on Mar 19, 2021. It is now read-only.

Commit ec34b3a

Browse files
the-kennyConnorRigby
authored andcommitted
Add Sqlitex.Server.with_transaction
1 parent 1d47a6b commit ec34b3a

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

lib/sqlitex/server.ex

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,19 @@ defmodule Sqlitex.Server do
119119
{:reply, result, {db, stmt_cache, config}}
120120
end
121121

122+
def handle_call({:with_transaction, fun}, _from, {db, stmt_cache, timeout}) do
123+
with :ok <- Sqlitex.exec(db, "begin"),
124+
{:ok, result} <- apply_rescueing(fun, [db]),
125+
:ok <- Sqlitex.exec(db, "commit")
126+
do
127+
{:reply, result, {db, stmt_cache, timeout}}
128+
else
129+
err ->
130+
:ok = Sqlitex.exec(db, "rollback")
131+
{:reply, err, {db, stmt_cache, timeout}}
132+
end
133+
end
134+
122135
def handle_cast(:stop, {db, stmt_cache, config}) do
123136
{:stop, :normal, {db, stmt_cache, config}}
124137
end
@@ -191,6 +204,32 @@ defmodule Sqlitex.Server do
191204
GenServer.cast(pid, :stop)
192205
end
193206

207+
208+
@doc """
209+
Runs `fun` inside a transaction. If `fun` returns without raising an exception,
210+
the transaction will be commited via `commit`. Otherwise, `rollback` will be called.
211+
212+
Statements are executed in the server process and are guaranteed to get executed
213+
sequentially without any interleaved statements from other processes.
214+
215+
It's important to use `Sqlitex.exec`, `Sqlitex.query`, ... instead of
216+
`Sqlitex.Server.exec`, ... inside the transaction as the `db` arg to `fun` is of
217+
type `Sqlitex.connection`.
218+
219+
## Examples
220+
iex> {:ok, s} = Sqlitex.Server.start_link(':memory:')
221+
iex> Sqlitex.Server.with_transaction(s, fn(db) ->
222+
...> Sqlitex.exec(db, "create table foo(id integer)")
223+
...> Sqlitex.exec(db, "insert into foo (id) values(42)")
224+
...> end)
225+
iex> Sqlitex.Server.query(s, "select * from foo")
226+
{:ok, [[{:id, 42}]]}
227+
"""
228+
@spec with_transaction(pid(), (Sqlitex.connection -> any()), Keyword.t) :: any
229+
def with_transaction(pid, fun, opts \\ []) do
230+
GenServer.call(pid, {:with_transaction, fun}, timeout(opts))
231+
end
232+
194233
## Helpers
195234

196235
defp query_impl(sql, stmt_cache, opts) do
@@ -213,4 +252,14 @@ defmodule Sqlitex.Server do
213252
with {%Cache{} = new_cache, stmt} <- Cache.prepare(stmt_cache, sql, opts),
214253
do: {:ok, %{columns: stmt.column_names, types: stmt.column_types}, new_cache}
215254
end
255+
256+
defp timeout(kwopts), do: Keyword.get(kwopts, :timeout, 5000)
257+
258+
defp apply_rescueing(fun, args) do
259+
try do
260+
{:ok, apply(fun, args)}
261+
rescue
262+
error -> {:error, error}
263+
end
264+
end
216265
end

test/server_test.exs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,35 @@
11
defmodule Sqlitex.ServerTest do
22
use ExUnit.Case
33
doctest Sqlitex.Server
4+
5+
test "with_transaction commit" do
6+
alias Sqlitex.Server
7+
8+
{:ok, server} = Server.start_link(':memory:')
9+
:ok = Server.exec(server, "create table foo(id integer)")
10+
11+
Server.with_transaction(server, fn db ->
12+
:ok = Sqlitex.exec(db, "insert into foo (id) values (42)")
13+
end)
14+
15+
assert Server.query(server, "select * from foo") == {:ok, [[{:id, 42}]]}
16+
end
17+
18+
test "with_transaction rollback" do
19+
alias Sqlitex.Server
20+
21+
{:ok, server} = Server.start_link(':memory:')
22+
:ok = Server.exec(server, "create table foo(id integer)")
23+
24+
try do
25+
Server.with_transaction(server, fn db ->
26+
:ok = Sqlitex.exec(db, "insert into foo (id) values (42)")
27+
raise "Error to roll back transaction"
28+
end)
29+
rescue
30+
_ -> nil
31+
end
32+
33+
assert Server.query(server, "select * from foo") == {:ok, []}
34+
end
435
end

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy