Skip to content

Commit ce3555a

Browse files
authoredFeb 6, 2023
Merge pull request #4 from orsinium-labs/handle_empty_sequence_number
Do not raise for missed sequence_number
2 parents 7891843 + 7b9401e commit ce3555a

8 files changed

+55
-14
lines changed
 

‎.credo.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
name: "default",
55
strict: true,
66
checks: [
7-
{Credo.Check.Design.TagTODO, false},
7+
{Credo.Check.Design.TagTODO, false}
88
]
99
}
1010
]

‎.formatter.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Used by "mix format"
22
[
3-
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", ".credo.exs"]
44
]

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ ecto_sqlite3_extras-*.tar
99
/tmp/
1010
/test_database.db
1111
/chinook.db-*
12+
/empty.db*
1213
/tmp*

‎lib/ecto_sqlite3_extras.ex

+27-8
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,24 @@ defmodule EctoSQLite3Extras do
6969
@doc """
7070
Run the query specified by its slug atom and get (or print) the result.
7171
"""
72-
@spec query(module(), repo(), keyword()) :: any()
73-
def query(module_name, repo, opts \\ []) do
74-
query_module = Map.fetch!(queries(repo), module_name)
72+
@spec query(atom(), repo(), keyword()) :: any()
73+
def query(query_name, repo, opts \\ []) do
74+
query_module = Map.fetch!(queries(repo), query_name)
7575
sql_query = query_module.query(Keyword.get(opts, :args, []))
7676
query_opts = Keyword.get(opts, :query_opts, log: false)
7777
format_name = Keyword.get(opts, :format, :ascii)
7878

7979
repo
80-
|> query!(sql_query, query_opts)
80+
|> run_query(sql_query, query_opts)
81+
|> unwrap()
8182
|> preformat()
8283
|> format(format_name, query_module.info)
8384
end
8485

8586
# run query on a remote node
86-
defp query!({repo, node}, query, query_opts) do
87-
case :rpc.call(node, repo, :query!, [query, [], query_opts]) do
87+
@spec run_query(repo(), String.t(), keyword()) :: {:ok, map()} | {:error, Exception.t()}
88+
defp run_query({repo, node}, query, query_opts) do
89+
case :rpc.call(node, repo, :query, [query, [], query_opts]) do
8890
{:badrpc, {:EXIT, {:undef, _}}} ->
8991
raise "repository is not defined on remote node"
9092

@@ -97,11 +99,20 @@ defmodule EctoSQLite3Extras do
9799
end
98100

99101
# run query on the current node
100-
defp query!(repo, query, query_opts) do
101-
repo.query!(query, [], query_opts)
102+
defp run_query(repo, query, query_opts) do
103+
repo.query(query, [], query_opts)
102104
end
103105

106+
@spec unwrap({:ok, map()} | {:error, Exception.t()}) :: map()
107+
defp unwrap({:ok, result}), do: result
108+
109+
defp unwrap({:error, %{message: "no such table: sqlite_sequence"}}),
110+
do: %{rows: [], num_rows: 0, columns: ["table_name", "sequence_number"]}
111+
112+
defp unwrap({:error, err}), do: raise(err)
113+
104114
# format summary tables based on the name in the first row
115+
@spec preformat(map()) :: map()
105116
defp preformat(%{columns: [_, _], rows: rows} = result) do
106117
rows = rows |> Enum.map(&format_row/1)
107118
Map.replace!(result, :rows, rows)
@@ -111,6 +122,7 @@ defmodule EctoSQLite3Extras do
111122
result
112123
end
113124

125+
@spec format(map(), atom(), map()) :: any()
114126
defp format(result, :raw, _info), do: result
115127
defp format(%{rows: []}, :ascii, _info), do: "No results"
116128

@@ -124,6 +136,7 @@ defmodule EctoSQLite3Extras do
124136
|> IO.puts()
125137
end
126138

139+
@spec format_row(list(list()), list(atom())) :: any()
127140
defp format_row(values, types) do
128141
Enum.zip(values, types) |> Enum.map(&format_value/1)
129142
end
@@ -136,10 +149,13 @@ defmodule EctoSQLite3Extras do
136149
end
137150
end
138151

152+
@spec format_value({any(), atom()}) :: String.t()
139153
defp format_value({integer, :bytes}) when is_integer(integer), do: format_bytes(integer)
140154
defp format_value({binary, _}) when is_binary(binary), do: binary
141155
defp format_value({other, _}), do: inspect(other)
142156

157+
# format integer bytes value as a human-readable string
158+
@spec format_bytes(integer()) :: String.t()
143159
defp format_bytes(bytes) do
144160
cond do
145161
bytes >= memory_unit(:TB) -> format_bytes(bytes, :TB)
@@ -150,11 +166,14 @@ defmodule EctoSQLite3Extras do
150166
end
151167
end
152168

169+
@spec format_bytes(integer(), atom()) :: String.t()
153170
defp format_bytes(bytes, unit) do
154171
value = bytes / memory_unit(unit)
155172
"#{:erlang.float_to_binary(value, decimals: 1)} #{unit}"
156173
end
157174

175+
# get number of bytes for the given memory unit atom
176+
@spec memory_unit(atom()) :: integer()
158177
defp memory_unit(:TB), do: 1024 * 1024 * 1024 * 1024
159178
defp memory_unit(:GB), do: 1024 * 1024 * 1024
160179
defp memory_unit(:MB), do: 1024 * 1024

‎lib/queries/sequence_number.ex

-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ defmodule EctoSQLite3Extras.SequenceNumber do
1616
}
1717
end
1818

19-
# TODO(@orsinium): The query fails if the table doesn't exist.
20-
# The table is created only when the first autoincrement column is created.
21-
# Can we fix it without doing INSERTs?
2219
def query(_args \\ []) do
2320
"""
2421
/* from ecto_sqlite3_extras */

‎mix.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule EctoSQLite3Extras.MixProject do
22
use Mix.Project
33
@github_url "https://github.com/orsinium-labs/ecto_sqlite3_extras"
4-
@version "1.1.6"
4+
@version "1.1.7"
55

66
def project do
77
[

‎test/ecto_sqlite3_extras_test.exs

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule EctoSQLite3ExtrasTest do
22
@moduledoc false
33
use ExUnit.Case
44
doctest EctoSQLite3Extras
5+
alias EctoSQLite3Extras.EmptyTestRepo
56
alias EctoSQLite3Extras.TestRepo
67
import ExUnit.CaptureIO
78

@@ -33,6 +34,7 @@ defmodule EctoSQLite3ExtrasTest do
3334
describe "database interaction" do
3435
setup do
3536
start_supervised!(EctoSQLite3Extras.TestRepo)
37+
start_supervised!(EctoSQLite3Extras.EmptyTestRepo)
3638
:ok
3739
end
3840

@@ -47,6 +49,16 @@ defmodule EctoSQLite3ExtrasTest do
4749
end
4850
end
4951

52+
test "run queries on empty database" do
53+
for query_name <- EctoSQLite3Extras.queries(EmptyTestRepo) |> Map.keys() do
54+
result = EctoSQLite3Extras.query(query_name, EmptyTestRepo, format: :raw)
55+
info = EctoSQLite3Extras.queries()[query_name].info
56+
assert length(result.columns) == length(info.columns)
57+
names = Enum.map(info.columns, &Atom.to_string(&1.name))
58+
assert result.columns == names
59+
end
60+
end
61+
5062
test "run total_size query using the function" do
5163
expected = [
5264
["pages", 864],

‎test/test_repo.exs

+12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
defmodule EctoSQLite3Extras.TestRepo do
2+
@moduledoc false
23
use Ecto.Repo, otp_app: :ecto_sqlite3_extras, adapter: Ecto.Adapters.SQLite3
34

45
def init(type, opts) do
@@ -8,3 +9,14 @@ defmodule EctoSQLite3Extras.TestRepo do
89
{:ok, opts}
910
end
1011
end
12+
13+
defmodule EctoSQLite3Extras.EmptyTestRepo do
14+
@moduledoc false
15+
use Ecto.Repo, otp_app: :ecto_sqlite3_extras, adapter: Ecto.Adapters.SQLite3
16+
17+
def init(type, opts) do
18+
opts = [database: "empty.db"] ++ opts
19+
opts[:parent] && send(opts[:parent], {__MODULE__, type, opts})
20+
{:ok, opts}
21+
end
22+
end

0 commit comments

Comments
 (0)
Please sign in to comment.