Skip to content

Commit d937a12

Browse files
authored
Introduce default_transaction_mode to configurations (#308)
The three modes allowed are `deferred`, `immediate`, and `exclusive`. By default the mode is set to `deferred`. For more information about why you would use `immediate` over `deferred`, take a look at the following articles: * https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/ * https://highperformancesqlite.com/watch/transaction-modes Closes: #153
1 parent 81cf923 commit d937a12

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

lib/exqlite/connection.ex

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ defmodule Exqlite.Connection do
3232

3333
defstruct [
3434
:db,
35+
:default_transaction_mode,
3536
:directory,
3637
:path,
3738
:transaction_status,
@@ -55,9 +56,11 @@ defmodule Exqlite.Connection do
5556
@type synchronous() :: :extra | :full | :normal | :off
5657
@type auto_vacuum() :: :none | :full | :incremental
5758
@type locking_mode() :: :normal | :exclusive
59+
@type transaction_mode() :: :deferred | :immediate | :exclusive
5860

5961
@type connection_opt() ::
6062
{:database, String.t()}
63+
| {:default_transaction_mode, transaction_mode()}
6164
| {:mode, Sqlite3.open_opt()}
6265
| {:journal_mode, journal_mode()}
6366
| {:temp_store, temp_store()}
@@ -91,6 +94,9 @@ defmodule Exqlite.Connection do
9194
9295
* `:database` - The path to the database. In memory is allowed. You can use
9396
`:memory` or `":memory:"` to designate that.
97+
* `:default_transaction_mode` - one of `deferred` (default), `immediate`,
98+
or `exclusive`. If a mode is not specified in a call to `Repo.transaction/2`,
99+
this will be the default transaction mode.
94100
* `:mode` - use `:readwrite` to open the database for reading and writing
95101
, `:readonly` to open it in read-only mode or `[:readonly | :readwrite, :nomutex]`
96102
to open it with no mutex mode. `:readwrite` will also create
@@ -260,7 +266,10 @@ defmodule Exqlite.Connection do
260266
# append level on the savepoint. Instead the rollbacks would just completely
261267
# revert the issues when it may be desirable to fix something while in the
262268
# transaction and then commit.
263-
case Keyword.get(options, :mode, :deferred) do
269+
270+
mode = Keyword.get(options, :mode, state.default_transaction_mode)
271+
272+
case mode do
264273
:deferred when transaction_status == :idle ->
265274
handle_transaction(:begin, "BEGIN TRANSACTION", state)
266275

@@ -549,6 +558,8 @@ defmodule Exqlite.Connection do
549558
:ok <- load_extensions(db, options) do
550559
state = %__MODULE__{
551560
db: db,
561+
default_transaction_mode:
562+
Keyword.get(options, :default_transaction_mode, :deferred),
552563
directory: directory,
553564
path: database,
554565
transaction_status: :idle,

test/exqlite/integration_test.exs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,51 @@ defmodule Exqlite.IntegrationTest do
170170
File.rm(path)
171171
end
172172

173+
test "transaction handling with immediate default_transaction_mode" do
174+
path = Temp.path!()
175+
176+
{:ok, conn1} =
177+
Connection.connect(
178+
database: path,
179+
default_transaction_mode: :immediate,
180+
journal_mode: :wal,
181+
cache_size: -64_000,
182+
temp_store: :memory
183+
)
184+
185+
{:ok, _result, conn1} = Connection.handle_begin([], conn1)
186+
assert conn1.transaction_status == :transaction
187+
assert conn1.default_transaction_mode == :immediate
188+
query = %Query{statement: "create table foo(id integer, val integer)"}
189+
{:ok, _query, _result, conn1} = Connection.handle_execute(query, [], [], conn1)
190+
{:ok, _result, conn1} = Connection.handle_rollback([], conn1)
191+
assert conn1.transaction_status == :idle
192+
193+
File.rm(path)
194+
end
195+
196+
test "transaction handling with default default_transaction_mode" do
197+
path = Temp.path!()
198+
199+
{:ok, conn1} =
200+
Connection.connect(
201+
database: path,
202+
journal_mode: :wal,
203+
cache_size: -64_000,
204+
temp_store: :memory
205+
)
206+
207+
{:ok, _result, conn1} = Connection.handle_begin([], conn1)
208+
assert conn1.transaction_status == :transaction
209+
assert conn1.default_transaction_mode == :deferred
210+
query = %Query{statement: "create table foo(id integer, val integer)"}
211+
{:ok, _query, _result, conn1} = Connection.handle_execute(query, [], [], conn1)
212+
{:ok, _result, conn1} = Connection.handle_rollback([], conn1)
213+
assert conn1.transaction_status == :idle
214+
215+
File.rm(path)
216+
end
217+
173218
test "exceeding timeout" do
174219
path = Temp.path!()
175220

0 commit comments

Comments
 (0)