diff --git a/lib/exqlite/connection.ex b/lib/exqlite/connection.ex index 25f8d2d..7400a90 100644 --- a/lib/exqlite/connection.ex +++ b/lib/exqlite/connection.ex @@ -32,6 +32,7 @@ defmodule Exqlite.Connection do defstruct [ :db, + :default_transaction_mode, :directory, :path, :transaction_status, @@ -55,9 +56,11 @@ defmodule Exqlite.Connection do @type synchronous() :: :extra | :full | :normal | :off @type auto_vacuum() :: :none | :full | :incremental @type locking_mode() :: :normal | :exclusive + @type transaction_mode() :: :deferred | :immediate | :exclusive @type connection_opt() :: {:database, String.t()} + | {:default_transaction_mode, transaction_mode()} | {:mode, Sqlite3.open_opt()} | {:journal_mode, journal_mode()} | {:temp_store, temp_store()} @@ -91,6 +94,9 @@ defmodule Exqlite.Connection do * `:database` - The path to the database. In memory is allowed. You can use `:memory` or `":memory:"` to designate that. + * `:default_transaction_mode` - one of `deferred` (default), `immediate`, + or `exclusive`. If a mode is not specified in a call to `Repo.transaction/2`, + this will be the default transaction mode. * `:mode` - use `:readwrite` to open the database for reading and writing , `:readonly` to open it in read-only mode or `[:readonly | :readwrite, :nomutex]` to open it with no mutex mode. `:readwrite` will also create @@ -260,7 +266,10 @@ defmodule Exqlite.Connection do # append level on the savepoint. Instead the rollbacks would just completely # revert the issues when it may be desirable to fix something while in the # transaction and then commit. - case Keyword.get(options, :mode, :deferred) do + + mode = Keyword.get(options, :mode, state.default_transaction_mode) + + case mode do :deferred when transaction_status == :idle -> handle_transaction(:begin, "BEGIN TRANSACTION", state) @@ -549,6 +558,8 @@ defmodule Exqlite.Connection do :ok <- load_extensions(db, options) do state = %__MODULE__{ db: db, + default_transaction_mode: + Keyword.get(options, :default_transaction_mode, :deferred), directory: directory, path: database, transaction_status: :idle, diff --git a/test/exqlite/integration_test.exs b/test/exqlite/integration_test.exs index e860cc9..cd90e08 100644 --- a/test/exqlite/integration_test.exs +++ b/test/exqlite/integration_test.exs @@ -170,6 +170,51 @@ defmodule Exqlite.IntegrationTest do File.rm(path) end + test "transaction handling with immediate default_transaction_mode" do + path = Temp.path!() + + {:ok, conn1} = + Connection.connect( + database: path, + default_transaction_mode: :immediate, + journal_mode: :wal, + cache_size: -64_000, + temp_store: :memory + ) + + {:ok, _result, conn1} = Connection.handle_begin([], conn1) + assert conn1.transaction_status == :transaction + assert conn1.default_transaction_mode == :immediate + query = %Query{statement: "create table foo(id integer, val integer)"} + {:ok, _query, _result, conn1} = Connection.handle_execute(query, [], [], conn1) + {:ok, _result, conn1} = Connection.handle_rollback([], conn1) + assert conn1.transaction_status == :idle + + File.rm(path) + end + + test "transaction handling with default default_transaction_mode" do + path = Temp.path!() + + {:ok, conn1} = + Connection.connect( + database: path, + journal_mode: :wal, + cache_size: -64_000, + temp_store: :memory + ) + + {:ok, _result, conn1} = Connection.handle_begin([], conn1) + assert conn1.transaction_status == :transaction + assert conn1.default_transaction_mode == :deferred + query = %Query{statement: "create table foo(id integer, val integer)"} + {:ok, _query, _result, conn1} = Connection.handle_execute(query, [], [], conn1) + {:ok, _result, conn1} = Connection.handle_rollback([], conn1) + assert conn1.transaction_status == :idle + + File.rm(path) + end + test "exceeding timeout" do path = Temp.path!()