@@ -15,20 +15,11 @@ Package: https://hex.pm/packages/exqlite
15
15
16
16
## Caveats
17
17
18
- * Prepared statements are not cached.
19
18
* Prepared statements are not immutable. You must be careful when manipulating
20
19
statements and binding values to statements. Do not try to manipulate the
21
20
statements concurrently. Keep it isolated to one process.
22
- * Simultaneous writing is not supported by SQLite3 and will not be supported
23
- here.
24
- * All native calls are run through the Dirty NIF scheduler.
25
- * Datetimes are stored without offsets. This is due to how SQLite3 handles date
26
- and times. If you would like to store a timezone, you will need to create a
27
- second column somewhere storing the timezone name and shifting it when you
28
- get it from the database. This is more reliable than storing the offset as
29
- ` +03:00 ` as it does not respect daylight savings time.
30
- * When storing ` BLOB ` values, you have to use ` {:blob, the_binary} ` , otherwise
31
- it will be interpreted as a string.
21
+ * Some native calls are run through the Dirty NIF scheduler.
22
+ Some are executed directly on current scheduler.
32
23
33
24
## Installation
34
25
42
33
43
34
44
35
## Configuration
45
-
46
- ### Runtime Configuration
47
-
48
- ``` elixir
49
- config :exqlite , default_chunk_size: 100
50
- ```
51
-
52
- * ` default_chunk_size ` - The chunk size that is used when multi-stepping when
53
- not specifying the chunk size explicitly.
54
36
55
37
### Compile-time Configuration
56
38
@@ -126,47 +108,52 @@ export EXQLITE_SYSTEM_CFLAGS=-I/usr/local/include/sqlcipher
126
108
export EXQLITE_SYSTEM_LDFLAGS=-L/usr/local/lib -lsqlcipher
127
109
```
128
110
129
- Once you have ` exqlite ` configured, you can use the ` : key` option in the database config to enable encryption:
111
+ Once you have ` exqlite ` build configured, you can use the ` key ` pragma to enable encryption:
130
112
131
113
``` elixir
132
- config :exqlite , key: " super-secret'
114
+ {:ok , db} = Exqlite .open (" sqlcipher.db" )
115
+ :ok = Exqlite .execute (db, " pragma key='super-secret'" )
133
116
```
134
117
135
118
## Usage
136
119
137
- The `Exqlite.Sqlite3 ` module usage is fairly straight forward.
120
+ The ` Exqlite ` module usage is fairly straight forward.
138
121
139
122
``` elixir
140
- # We'll just keep it in memory right now
141
- {:ok, conn} = Exqlite.Sqlite3.open(" :memory :")
123
+ {:ok , db} = Exqlite .open (" app.db" , [:readwrite , :create ])
124
+
125
+ :ok = Exqlite .execute (db, " pragma foreign_keys=on" )
126
+ :ok = Exqlite .execute (db, " pragma journal_mode=wal" )
127
+ :ok = Exqlite .execute (db, " pragma busy_timeout=5000" )
142
128
143
129
# Create the table
144
- :ok = Exqlite.Sqlite3. execute(conn , " create table test (id integer primary key, stuff text)" )
130
+ :ok = Exqlite .execute (db , " create table test (id integer primary key, stuff text)" )
145
131
146
132
# Prepare a statement
147
- {:ok, statement } = Exqlite.Sqlite3. prepare(conn , " insert into test (stuff) values (?1 )" )
148
- :ok = Exqlite.Sqlite3.bind(conn, statement , [" Hello world" ])
133
+ {:ok , insert } = Exqlite .prepare (db , " insert into test (stuff) values (?1)" )
134
+ :ok = Exqlite .bind_all (db, insert , [" Hello world" ])
149
135
150
136
# Step is used to run statements
151
- :done = Exqlite.Sqlite3. step(conn, statement )
137
+ :done = Exqlite .step (db, insert )
152
138
153
139
# Prepare a select statement
154
- {:ok, statement } = Exqlite.Sqlite3. prepare(conn , " select id, stuff from test" )
140
+ {:ok , select } = Exqlite .prepare (db , " select id, stuff from test" )
155
141
156
142
# Get the results
157
- {:row, [1, " Hello world" ]} = Exqlite.Sqlite3. step(conn, statement )
143
+ {:row , [1 , " Hello world" ]} = Exqlite .step (db, select )
158
144
159
145
# No more results
160
- :done = Exqlite.Sqlite3. step(conn, statement )
146
+ :done = Exqlite .step (db, select )
161
147
162
- # Release the statement .
148
+ # Release the statements .
163
149
#
164
150
# It is recommended you release the statement after using it to reclaim the memory
165
151
# asap, instead of letting the garbage collector eventually releasing the statement.
166
152
#
167
153
# If you are operating at a high load issuing thousands of statements, it would be
168
154
# possible to run out of memory or cause a lot of pressure on memory.
169
- :ok = Exqlite.Sqlite3.release(conn, statement)
155
+ :ok = Exqlite .finalize (insert)
156
+ :ok = Exqlite .finalize (select)
170
157
```
171
158
172
159
### Using SQLite3 native extensions
@@ -177,52 +164,39 @@ available by installing the [ExSqlean](https://github.com/mindreframer/ex_sqlean
177
164
package. This package wraps [ SQLean: all the missing SQLite functions] ( https://github.com/nalgeon/sqlean ) .
178
165
179
166
``` elixir
180
- alias Exqlite.Basic
181
- {:ok, conn} = Basic.open(" db.sqlite3" )
182
- :ok = Basic.enable_load_extension(conn)
167
+ {:ok , db} = Exqlite .open (" :memory:" , [:readwrite ])
168
+ :ok = Exqlite .enable_load_extension (db, true )
169
+
170
+ exec = fn db, sql, params ->
171
+ with {:ok , stmt} <- Exqlite .prepare (db, sql) do
172
+ try do
173
+ with :ok <- Exqlite .bind_all (db, stmt, params) do
174
+ Exqlite .fetch_all (db, stmt)
175
+ end
176
+ after
177
+ Exqlite .finalize (stmt)
178
+ end
179
+ end
180
+ end
183
181
184
182
# load the regexp extension - https://github.com/nalgeon/sqlean/blob/main/docs/re.md
185
- Basic. load_extension(conn, ExSqlean.path_for(" re" ))
183
+ { :ok , _rows } = exec .(db, " select load_extension(?) " , [ ExSqlean .path_for (" re" )] )
186
184
187
185
# run some queries to test the new `regexp_like` function
188
- {:ok, [[1]], [" value" ]} = Basic. exec(conn , " select regexp_like (' the year is 2021' , ?) as value" , [" 2021 " ]) |> Basic.rows( )
189
- {:ok, [[0]], [" value" ]} = Basic. exec(conn , " select regexp_like (' the year is 2021' , ?) as value" , [" 2020 " ]) |> Basic.rows( )
186
+ {:ok , [[1 ]], [" value" ]} = exec .(db , " select regexp_like('the year is 2021', ?) as value" , [" 2021" ])
187
+ {:ok , [[0 ]], [" value" ]} = exec .(db , " select regexp_like('the year is 2021', ?) as value" , [" 2020" ])
190
188
191
189
# prevent loading further extensions
192
- :ok = Basic.disable_load_extension(conn)
193
- {:error, %Exqlite.Error{message: " not authorized" }, _} = Basic.load_extension(conn, ExSqlean.path_for(" re" ))
190
+ :ok = Exqlite .enable_load_extension (db, false )
194
191
195
- # close connection
196
- Basic.close(conn)
197
- ```
198
-
199
- It is also possible to load extensions using the `Connection` configuration. For example:
192
+ {:error , %Exqlite .Error {message: " not authorized" }} =
193
+ exec .(db, " select load_extension(?)" , [ExSqlean .path_for (" stats" )])
200
194
201
- ```elixir
202
- arch_dir =
203
- System.cmd(" uname" , [" - sm" ])
204
- |> elem(0)
205
- |> String.trim()
206
- |> String.replace(" " , " - " )
207
- |> String.downcase() # => " darwin- arm64"
208
-
209
- config :myapp, arch_dir: arch_dir
210
-
211
- # global
212
- config :exqlite, load_extensions: [ " ./ priv/ sqlite/ \# {arch_dir}/rotate" ]
213
-
214
- # per connection in a Phoenix app
215
- config :myapp , Myapp .Repo ,
216
- database: " path/to/db" ,
217
- load_extensions: [
218
- " ./priv/sqlite/\# {arch_dir}/vector0" ,
219
- " ./priv/sqlite/\# {arch_dir}/vss0"
220
- ]
195
+ # close connection
196
+ Exqlite .close (db)
221
197
```
222
198
223
- See [ Exqlite.Connection.connect/1] ( https://hexdocs.pm/exqlite/Exqlite.Connection.html#connect/1 )
224
- for more information. When using extensions for SQLite3, they must be compiled
225
- for the environment you are targeting.
199
+ When using extensions for SQLite3, they must be compiled for the environment you are targeting.
226
200
227
201
## Why SQLite3
228
202
@@ -239,7 +213,7 @@ that would be resiliant to power outages and still maintain some state that
239
213
240
214
## Under The Hood
241
215
242
- We are using the Dirty NIF scheduler to execute the sqlite calls. The rationale
216
+ We are using the Dirty NIF scheduler to execute most of the sqlite calls. The rationale
243
217
behind this is that maintaining each sqlite's connection command pool is
244
218
complicated and error prone.
245
219
0 commit comments