Skip to content

Commit 8cd3e03

Browse files
authored
Accept a list of modes to open db with nomutex option. (#284)
1 parent c63c4fa commit 8cd3e03

File tree

3 files changed

+83
-9
lines changed

3 files changed

+83
-9
lines changed

lib/exqlite/connection.ex

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ defmodule Exqlite.Connection do
5858

5959
@type connection_opt() ::
6060
{:database, String.t()}
61+
| {:mode, Sqlite3.open_opt()}
6162
| {:journal_mode, journal_mode()}
6263
| {:temp_store, temp_store()}
6364
| {:synchronous, synchronous()}
@@ -91,8 +92,10 @@ defmodule Exqlite.Connection do
9192
* `:database` - The path to the database. In memory is allowed. You can use
9293
`:memory` or `":memory:"` to designate that.
9394
* `:mode` - use `:readwrite` to open the database for reading and writing
94-
or `:readonly` to open it in read-only mode. `:readwrite` will also create
95+
, `:readonly` to open it in read-only mode or `[:readonly | :readwrite, :nomutex]`
96+
to open it with no mutex mode. `:readwrite` will also create
9597
the database if it doesn't already exist. Defaults to `:readwrite`.
98+
Note: [:readwrite, :nomutex] is not recommended.
9699
* `:journal_mode` - Sets the journal mode for the sqlite connection. Can be
97100
one of the following `:delete`, `:truncate`, `:persist`, `:memory`,
98101
`:wal`, or `:off`. Defaults to `:delete`. It is recommended that you use
@@ -159,8 +162,8 @@ defmodule Exqlite.Connection do
159162
"./priv/sqlite/\#{arch_dir}/vss0"
160163
]
161164
```
162-
* `:before_disconnect` - A function to run before disconnect, either a
163-
2-arity fun or `{module, function, args}` with the close reason and
165+
* `:before_disconnect` - A function to run before disconnect, either a
166+
2-arity fun or `{module, function, args}` with the close reason and
164167
`t:Exqlite.Connection.t/0` prepended to `args` or `nil` (default: `nil`)
165168
166169
For more information about the options above, see [sqlite documentation][1]

lib/exqlite/sqlite3.ex

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ defmodule Exqlite.Sqlite3 do
1919
@type statement() :: reference()
2020
@type reason() :: atom() | String.t()
2121
@type row() :: list()
22-
@type open_opt :: {:mode, :readwrite | :readonly}
22+
@type open_mode :: :readwrite | :readonly | :nomutex
23+
@type open_opt :: {:mode, :readwrite | :readonly | [open_mode()]}
2324

2425
@doc """
2526
Opens a new sqlite database at the Path provided.
@@ -29,26 +30,53 @@ defmodule Exqlite.Sqlite3 do
2930
## Options
3031
3132
* `:mode` - use `:readwrite` to open the database for reading and writing
32-
or `:readonly` to open it in read-only mode. `:readwrite` will also create
33+
, `:readonly` to open it in read-only mode or `[:readonly | :readwrite, :nomutex]`
34+
to open it with no mutex mode. `:readwrite` will also create
3335
the database if it doesn't already exist. Defaults to `:readwrite`.
36+
Note: [:readwrite, :nomutex] is not recommended.
3437
"""
3538
@spec open(String.t(), [open_opt()]) :: {:ok, db()} | {:error, reason()}
3639
def open(path, opts \\ []) do
3740
mode = Keyword.get(opts, :mode, :readwrite)
3841
Sqlite3NIF.open(String.to_charlist(path), flags_from_mode(mode))
3942
end
4043

44+
defp flags_from_mode(:nomutex) do
45+
raise ArgumentError,
46+
"expected mode to be `:readwrite` or `:readonly`, can't use a single :nomutex mode"
47+
end
48+
4149
defp flags_from_mode(:readwrite),
42-
do: Flags.put_file_open_flags([:sqlite_open_readwrite, :sqlite_open_create])
50+
do: do_flags_from_mode([:readwrite], [])
4351

4452
defp flags_from_mode(:readonly),
45-
do: Flags.put_file_open_flags([:sqlite_open_readonly])
53+
do: do_flags_from_mode([:readonly], [])
54+
55+
defp flags_from_mode([_ | _] = modes),
56+
do: do_flags_from_mode(modes, [])
4657

4758
defp flags_from_mode(mode) do
4859
raise ArgumentError,
49-
"expected mode to be `:readwrite` or `:readonly`, but received #{inspect(mode)}"
60+
"expected mode to be `:readwrite`, `:readonly` or list of modes, but received #{inspect(mode)}"
5061
end
5162

63+
defp do_flags_from_mode([:readwrite | tail], acc),
64+
do: do_flags_from_mode(tail, [:sqlite_open_readwrite, :sqlite_open_create | acc])
65+
66+
defp do_flags_from_mode([:readonly | tail], acc),
67+
do: do_flags_from_mode(tail, [:sqlite_open_readonly | acc])
68+
69+
defp do_flags_from_mode([:nomutex | tail], acc),
70+
do: do_flags_from_mode(tail, [:sqlite_open_nomutex | acc])
71+
72+
defp do_flags_from_mode([mode | _tail], _acc) do
73+
raise ArgumentError,
74+
"expected mode to be `:readwrite`, `:readonly` or `:nomutex`, but received #{inspect(mode)}"
75+
end
76+
77+
defp do_flags_from_mode([], acc),
78+
do: Flags.put_file_open_flags(acc)
79+
5280
@doc """
5381
Closes the database and releases any underlying resources.
5482
"""

test/exqlite/sqlite3_test.exs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,59 @@ defmodule Exqlite.Sqlite3Test do
5353
Sqlite3.execute(ro_conn, insert_value_query)
5454
end
5555

56+
test "opens a database in a list of mode" do
57+
# Create database with readwrite connection
58+
{:ok, path} = Temp.path()
59+
{:ok, rw_conn} = Sqlite3.open(path)
60+
61+
create_table_query = "create table test (id integer primary key, stuff text)"
62+
:ok = Sqlite3.execute(rw_conn, create_table_query)
63+
64+
insert_value_query = "insert into test (stuff) values ('This is a test')"
65+
:ok = Sqlite3.execute(rw_conn, insert_value_query)
66+
67+
# Read from database with a readonly connection
68+
{:ok, ro_conn} = Sqlite3.open(path, mode: [:readonly, :nomutex])
69+
70+
select_query = "select id, stuff from test order by id asc"
71+
{:ok, statement} = Sqlite3.prepare(ro_conn, select_query)
72+
{:row, columns} = Sqlite3.step(ro_conn, statement)
73+
74+
assert [1, "This is a test"] == columns
75+
end
76+
5677
test "opens a database with invalid mode" do
5778
{:ok, path} = Temp.path()
5879

5980
msg =
60-
"expected mode to be `:readwrite` or `:readonly`, but received :notarealmode"
81+
"expected mode to be `:readwrite`, `:readonly` or list of modes, but received :notarealmode"
6182

6283
assert_raise ArgumentError, msg, fn ->
6384
Sqlite3.open(path, mode: :notarealmode)
6485
end
6586
end
87+
88+
test "opens a database with invalid single nomutex mode" do
89+
{:ok, path} = Temp.path()
90+
91+
msg =
92+
"expected mode to be `:readwrite` or `:readonly`, can't use a single :nomutex mode"
93+
94+
assert_raise ArgumentError, msg, fn ->
95+
Sqlite3.open(path, mode: :nomutex)
96+
end
97+
end
98+
99+
test "opens a database with invalid list of mode" do
100+
{:ok, path} = Temp.path()
101+
102+
msg =
103+
"expected mode to be `:readwrite`, `:readonly` or `:nomutex`, but received :notarealmode"
104+
105+
assert_raise ArgumentError, msg, fn ->
106+
Sqlite3.open(path, mode: [:notarealmode])
107+
end
108+
end
66109
end
67110

68111
describe ".close/2" do

0 commit comments

Comments
 (0)