Skip to content

Commit bc6e0ed

Browse files
committed
Accept a list of modes to open db with nomutex option.
1 parent 9998c00 commit bc6e0ed

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-8
lines changed

lib/exqlite/connection.ex

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

5959
@type connection_opt() ::
6060
{:database, String.t()}
61-
| {:mode, :readwrite | :readonly}
61+
| {:mode, Sqlite3.open_opt()}
6262
| {:journal_mode, journal_mode()}
6363
| {:temp_store, temp_store()}
6464
| {:synchronous, synchronous()}
@@ -92,8 +92,10 @@ defmodule Exqlite.Connection do
9292
* `:database` - The path to the database. In memory is allowed. You can use
9393
`:memory` or `":memory:"` to designate that.
9494
* `:mode` - use `:readwrite` to open the database for reading and writing
95-
or `:readonly` to open it in read-only mode with no mutex. `: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
9697
the database if it doesn't already exist. Defaults to `:readwrite`.
98+
Note: [:readwrite, :nomutex] is not recommended.
9799
* `:journal_mode` - Sets the journal mode for the sqlite connection. Can be
98100
one of the following `:delete`, `:truncate`, `:persist`, `:memory`,
99101
`:wal`, or `:off`. Defaults to `:delete`. It is recommended that you use

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 with no mutex. `: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, :sqlite_open_nomutex])
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
59+
raise ArgumentError,
60+
"expected mode to be `:readwrite`, `:readonly` or list of modes, but received #{inspect(mode)}"
61+
end
62+
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
4873
raise ArgumentError,
49-
"expected mode to be `:readwrite` or `:readonly`, but received #{inspect(mode)}"
74+
"expected mode to be `:readwrite`, `:readonly` or `:nomutex`, but received #{inspect(mode)}"
5075
end
5176

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)