Skip to content
This repository was archived by the owner on Mar 19, 2021. It is now read-only.

Commit f30e9e8

Browse files
MichaelBakerConnorRigby
authored andcommitted
Adds support for query timeouts
1 parent f580bfe commit f30e9e8

File tree

4 files changed

+28
-25
lines changed

4 files changed

+28
-25
lines changed

lib/sqlitex/query.ex

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ defmodule Sqlitex.Query do
3232
def query(db, sql, opts \\ []) do
3333
with {:ok, stmt} <- Statement.prepare(db, sql, opts),
3434
{:ok, stmt} <- Statement.bind_values(stmt, Keyword.get(opts, :bind, []), opts),
35-
{:ok, res} <- Statement.fetch_all(stmt, Keyword.get(opts, :into, [])),
35+
{:ok, res} <- Statement.fetch_all(stmt, Keyword.get(opts, :db_timeout, 5_000), Keyword.get(opts, :into, [])),
3636
do: {:ok, res}
3737
end
3838

@@ -77,7 +77,7 @@ defmodule Sqlitex.Query do
7777
def query_rows(db, sql, opts \\ []) do
7878
with {:ok, stmt} <- Statement.prepare(db, sql, opts),
7979
{:ok, stmt} <- Statement.bind_values(stmt, Keyword.get(opts, :bind, []), opts),
80-
{:ok, rows} <- Statement.fetch_all(stmt, :raw_list),
80+
{:ok, rows} <- Statement.fetch_all(stmt, Keyword.get(opts, :db_timeout, 5_000), :raw_list),
8181
do: {:ok, %{rows: rows, columns: stmt.column_names, types: stmt.column_types}}
8282
end
8383

lib/sqlitex/server.ex

+2-2
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ defmodule Sqlitex.Server do
178178

179179
with {%Cache{} = new_cache, stmt} <- Cache.prepare(stmt_cache, sql, db_opts),
180180
{:ok, stmt} <- Statement.bind_values(stmt, Keyword.get(opts, :bind, []), db_opts),
181-
{:ok, rows} <- Statement.fetch_all(stmt, Keyword.get(opts, :into, [])),
181+
{:ok, rows} <- Statement.fetch_all(stmt, Keyword.get(opts, :db_timeout, 5_000), Keyword.get(opts, :into, [])),
182182
do: {:ok, rows, new_cache}
183183
end
184184

@@ -187,7 +187,7 @@ defmodule Sqlitex.Server do
187187

188188
with {%Cache{} = new_cache, stmt} <- Cache.prepare(stmt_cache, sql, db_opts),
189189
{:ok, stmt} <- Statement.bind_values(stmt, Keyword.get(opts, :bind, []), db_opts),
190-
{:ok, rows} <- Statement.fetch_all(stmt, :raw_list),
190+
{:ok, rows} <- Statement.fetch_all(stmt, Keyword.get(opts, :db_timeout, 5_000), :raw_list),
191191
do: {:ok,
192192
%{rows: rows, columns: stmt.column_names, types: stmt.column_types},
193193
new_cache}

lib/sqlitex/statement.ex

+18-15
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule Sqlitex.Statement do
2121
iex(6)> Sqlitex.Statement.exec(statement)
2222
:ok
2323
iex(7)> {:ok, statement} = Sqlitex.Statement.prepare(db, "SELECT * FROM data;")
24-
iex(8)> Sqlitex.Statement.fetch_all(statement)
24+
iex(8)> Sqlitex.Statement.fetch_all(statement, 1_000)
2525
{:ok, [[id: 1, name: "hello"]]}
2626
iex(9)> Sqlitex.close(db)
2727
:ok
@@ -46,7 +46,7 @@ defmodule Sqlitex.Statement do
4646
and store it separately in the `Statement` struct. Only the portion of the query
4747
preceding the returning clause is passed to SQLite's prepare function.
4848
49-
Later, when such a statement struct is passed to `fetch_all/2` or `fetch_all!/2`
49+
Later, when such a statement struct is passed to `fetch_all/3` or `fetch_all!/3`
5050
the returning clause is parsed and the query is performed with the following
5151
additional logic:
5252
@@ -73,6 +73,8 @@ defmodule Sqlitex.Statement do
7373
column_names: [],
7474
column_types: []
7575

76+
@chunk_size 5000
77+
7678
alias Sqlitex.Config
7779

7880
@doc """
@@ -170,35 +172,36 @@ defmodule Sqlitex.Statement do
170172
## Parameters
171173
172174
* `statement` - The statement to run.
175+
* `timeout` - The query timeout to be passed to esqlite.
173176
* `into` - The collection to put the results into. Defaults to an empty list.
174177
175178
## Returns
176179
177180
* `{:ok, results}`
178181
* `{:error, error}`
179182
"""
180-
def fetch_all(statement, into \\ []) do
181-
case raw_fetch_all(statement) do
183+
def fetch_all(statement, timeout, into \\ []) do
184+
case raw_fetch_all(statement, timeout) do
182185
{:error, _} = other -> other
183186
raw_data ->
184187
{:ok, Row.from(statement.column_types, statement.column_names, raw_data, into)}
185188
end
186189
end
187190

188-
defp raw_fetch_all(%__MODULE__{returning: nil, statement: statement}) do
189-
:esqlite3.fetchall(statement)
191+
defp raw_fetch_all(%__MODULE__{returning: nil, statement: statement}, timeout) do
192+
:esqlite3.fetchall(statement, @chunk_size, timeout)
190193
end
191-
defp raw_fetch_all(statement) do
192-
returning_query(statement)
194+
defp raw_fetch_all(statement, timeout) do
195+
returning_query(statement, timeout)
193196
end
194197

195198
@doc """
196-
Same as `fetch_all/2` but raises a Sqlitex.Statement.FetchAllError on error.
199+
Same as `fetch_all/3` but raises a Sqlitex.Statement.FetchAllError on error.
197200
198201
Returns the results otherwise.
199202
"""
200-
def fetch_all!(statement, into \\ []) do
201-
case fetch_all(statement, into) do
203+
def fetch_all!(statement, timeout, into \\ []) do
204+
case fetch_all(statement, timeout, into) do
202205
{:ok, results} -> results
203206
{:error, reason} -> raise Sqlitex.Statement.FetchAllError, reason: reason
204207
end
@@ -338,11 +341,11 @@ defmodule Sqlitex.Statement do
338341
{:error, :invalid_returning_clause}
339342
end
340343

341-
defp returning_query(%__MODULE__{database: db} = stmt) do
344+
defp returning_query(%__MODULE__{database: db} = stmt, timeout) do
342345
sp = "sp_#{random_id()}"
343346
{:ok, _} = db_exec(db, "SAVEPOINT #{sp}")
344347

345-
case returning_query_in_savepoint(sp, stmt) do
348+
case returning_query_in_savepoint(sp, stmt, timeout) do
346349
{:error, _} = error ->
347350
rollback(db, sp)
348351
error
@@ -354,7 +357,7 @@ defmodule Sqlitex.Statement do
354357

355358
defp returning_query_in_savepoint(sp, %__MODULE__{database: db,
356359
statement: statement,
357-
returning: {table, cols, cmd, ref}})
360+
returning: {table, cols, cmd, ref}}, timeout)
358361
do
359362
temp_table = "t_#{random_id()}"
360363
temp_fields = Enum.join(cols, ", ")
@@ -371,7 +374,7 @@ defmodule Sqlitex.Statement do
371374

372375
with {:ok, _} = db_exec(db, "CREATE TEMP TABLE #{temp_table} (#{temp_fields})"),
373376
{:ok, _} = db_exec(db, trigger),
374-
result = :esqlite3.fetchall(statement),
377+
result = :esqlite3.fetchall(statement, @chunk_size, timeout),
375378
{:ok, rows} = db_exec(db, "SELECT #{column_names} FROM #{temp_table}"),
376379
{:ok, _} = db_exec(db, "DROP TRIGGER IF EXISTS #{trigger_name}"),
377380
{:ok, _} = db_exec(db, "DROP TABLE IF EXISTS #{temp_table}")

test/statement_test.exs

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule StatementTest do
77

88
result = db
99
|> Sqlitex.Statement.prepare!("PRAGMA user_version;")
10-
|> Sqlitex.Statement.fetch_all!
10+
|> Sqlitex.Statement.fetch_all!(1_000)
1111

1212
assert result == [[user_version: 0]]
1313
end
@@ -20,7 +20,7 @@ defmodule StatementTest do
2020
stmt = Sqlitex.Statement.prepare!(db, "INSERT INTO x(str) VALUES (?1) "
2121
<> ";--RETURNING ON INSERT x,id")
2222

23-
rows = Sqlitex.Statement.fetch_all!(stmt)
23+
rows = Sqlitex.Statement.fetch_all!(stmt, 1_000)
2424
assert rows == [[id: 1]]
2525
end
2626

@@ -32,7 +32,7 @@ defmodule StatementTest do
3232
stmt = Sqlitex.Statement.prepare!(db, "INSERT INTO x(str) VALUES (?1) "
3333
<> ";--RETURNING ON INSERT x,id")
3434

35-
rows = Sqlitex.Statement.fetch_all!(stmt, :raw_list)
35+
rows = Sqlitex.Statement.fetch_all!(stmt, 1_000, :raw_list)
3636
assert rows == [[1]]
3737
end
3838

@@ -44,7 +44,7 @@ defmodule StatementTest do
4444
stmt = Sqlitex.Statement.prepare!(db, "INSERT INTO x(str) VALUES ('x'),('y'),('z') "
4545
<> ";--RETURNING ON INSERT x,id")
4646

47-
rows = Sqlitex.Statement.fetch_all!(stmt)
47+
rows = Sqlitex.Statement.fetch_all!(stmt, 1_000)
4848
assert rows == [[id: 1], [id: 2], [id: 3]]
4949
end
5050

@@ -56,7 +56,7 @@ defmodule StatementTest do
5656
stmt = Sqlitex.Statement.prepare!(db, "INSERT INTO x(str) VALUES ('x'),('y'),('z') "
5757
<> ";--RETURNING ON INSERT x,id")
5858

59-
rows = Sqlitex.Statement.fetch_all!(stmt, :raw_list)
59+
rows = Sqlitex.Statement.fetch_all!(stmt, 1_000, :raw_list)
6060
assert rows == [[1], [2], [3]]
6161
end
6262

@@ -71,7 +71,7 @@ defmodule StatementTest do
7171
stmt = Sqlitex.Statement.prepare!(db, "INSERT INTO x(str) VALUES ('x') "
7272
<> ";--RETURNING ON INSERT x,id")
7373

74-
result = Sqlitex.Statement.fetch_all(stmt, :raw_list)
74+
result = Sqlitex.Statement.fetch_all(stmt, 1_000, :raw_list)
7575
assert result == {:error, {:constraint, 'UNIQUE constraint failed: x.str'}}
7676
end
7777
end

0 commit comments

Comments
 (0)