Skip to content

Commit 19bc195

Browse files
committed
more tests
1 parent 12732e1 commit 19bc195

2 files changed

Lines changed: 49 additions & 61 deletions

File tree

lib/exqlite/sqlite3.ex

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ defmodule Exqlite.Sqlite3 do
174174
** (ArgumentError) unsupported type: #PID<0.0.0>
175175
176176
iex> {:ok, conn} = Sqlite3.open(":memory:", [:readonly])
177-
iex> {:ok, stmt} = Sqlite3.prepare(conn, "SELECT :a, :b")
178-
iex> Sqlite3.bind(stmt, %{":a" => 42, ":b" => "Alice"})
177+
iex> {:ok, stmt} = Sqlite3.prepare(conn, "SELECT :a, @b, $c")
178+
iex> Sqlite3.bind(stmt, %{":a" => 42, "@b" => "Alice", "$c" => nil})
179179
iex> Sqlite3.step(conn, stmt)
180-
{:row, [42, "Alice"]}
180+
{:row, [42, "Alice", nil]}
181181
182182
"""
183183
@spec bind(
@@ -198,21 +198,38 @@ defmodule Exqlite.Sqlite3 do
198198
end
199199

200200
def bind(stmt, args) when is_map(args) do
201-
args =
202-
Enum.map(args, fn {name, value} ->
203-
case Sqlite3NIF.bind_parameter_index(stmt, name) do
204-
0 -> raise ArgumentError, "unknown parameter: #{inspect(name)}"
205-
idx -> {value, idx}
206-
end
207-
end)
208-
|> Enum.sort_by(fn {_param, idx} -> idx end, :asc)
209-
|> Enum.map(fn {param, _idx} -> param end)
210-
211-
bind(stmt, args)
201+
params_count = bind_parameter_count(stmt)
202+
args_count = map_size(args)
203+
204+
if args_count == params_count do
205+
bind_all_named(Map.to_list(args), stmt)
206+
else
207+
raise ArgumentError,
208+
"expected #{params_count} named arguments, got #{args_count}: #{inspect(Map.keys(args))}"
209+
end
212210
end
213211

214-
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
215212
defp bind_all([param | params], stmt, idx) do
213+
do_bind(stmt, idx, param)
214+
bind_all(params, stmt, idx + 1)
215+
end
216+
217+
defp bind_all([], _stmt, _idx), do: :ok
218+
219+
defp bind_all_named([{name, param} | named_params], stmt) do
220+
idx = Sqlite3NIF.bind_parameter_index(stmt, to_string(name))
221+
222+
if idx == 0 do
223+
raise ArgumentError, "unknown named parameter: #{inspect(name)}"
224+
end
225+
226+
do_bind(stmt, idx, param)
227+
bind_all_named(named_params, stmt)
228+
end
229+
230+
defp bind_all_named([], _stmt), do: :ok
231+
232+
defp do_bind(stmt, idx, param) do
216233
case convert(param) do
217234
i when is_integer(i) -> bind_integer(stmt, idx, i)
218235
f when is_float(f) -> bind_float(stmt, idx, f)
@@ -225,12 +242,8 @@ defmodule Exqlite.Sqlite3 do
225242
{:blob, b} when is_list(b) -> bind_blob(stmt, idx, IO.iodata_to_binary(b))
226243
_other -> raise ArgumentError, "unsupported type: #{inspect(param)}"
227244
end
228-
229-
bind_all(params, stmt, idx + 1)
230245
end
231246

232-
defp bind_all([], _stmt, _idx), do: :ok
233-
234247
@spec columns(db(), statement()) :: {:ok, [binary()]} | {:error, reason()}
235248
def columns(conn, statement), do: Sqlite3NIF.columns(conn, statement)
236249

test/exqlite/sqlite3_test.exs

Lines changed: 17 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -303,74 +303,49 @@ defmodule Exqlite.Sqlite3Test do
303303
{:ok, conn} = Sqlite3.open(":memory:")
304304

305305
{:ok, statement} =
306-
Sqlite3.prepare(conn, "select :42, :pi, :name, :emoji, :blob, :null")
306+
Sqlite3.prepare(conn, "select :42, @pi, :name, $emoji, :blob, :null")
307307

308308
:ok =
309309
Sqlite3.bind(statement, %{
310310
":42" => 42,
311-
":pi" => 3.14,
312-
":name" => "Alice",
313-
":emoji" => "👋",
314-
":blob" => {:blob, <<0, 1, 2>>},
315-
":null" => nil
316-
})
317-
318-
assert {:row, [42, 3.14, "Alice", "👋", <<0, 1, 2>>, nil]} =
319-
Sqlite3.step(conn, statement)
320-
end
321-
322-
test "binds named parameters like @VVV" do
323-
{:ok, conn} = Sqlite3.open(":memory:")
324-
325-
{:ok, statement} =
326-
Sqlite3.prepare(conn, "select @42, @pi, @name, @emoji, @blob, @null")
327-
328-
:ok =
329-
Sqlite3.bind(statement, %{
330-
"@42" => 42,
331311
"@pi" => 3.14,
332-
"@name" => "Alice",
333-
"@emoji" => "👋",
334-
"@blob" => {:blob, <<0, 1, 2>>},
335-
"@null" => nil
312+
:":name" => "Alice",
313+
"$emoji" => "👋",
314+
":blob" => {:blob, <<0, 1, 2>>},
315+
~c":null" => nil
336316
})
337317

338318
assert {:row, [42, 3.14, "Alice", "👋", <<0, 1, 2>>, nil]} =
339319
Sqlite3.step(conn, statement)
340320
end
341321

342-
test "binds named parameters like $VVV" do
322+
test "handles repeating named parameters" do
343323
{:ok, conn} = Sqlite3.open(":memory:")
344324

345325
{:ok, statement} =
346-
Sqlite3.prepare(conn, "select $42, $pi, $name, $emoji, $blob, $null")
326+
Sqlite3.prepare(conn, "select :name, :name, :name")
347327

348328
:ok =
349329
Sqlite3.bind(statement, %{
350-
"$42" => 42,
351-
"$pi" => 3.14,
352-
"$name" => "Alice",
353-
"$emoji" => "👋",
354-
"$blob" => {:blob, <<0, 1, 2>>},
355-
"$null" => nil
330+
":name" => "Alice"
356331
})
357332

358-
assert {:row, [42, 3.14, "Alice", "👋", <<0, 1, 2>>, nil]} =
359-
Sqlite3.step(conn, statement)
333+
assert {:row, ["Alice", "Alice", "Alice"]} = Sqlite3.step(conn, statement)
360334
end
361335

362-
test "handles repeating named parameters" do
336+
test "raises an error when too few or too many named parameters" do
363337
{:ok, conn} = Sqlite3.open(":memory:")
364338

365339
{:ok, statement} =
366-
Sqlite3.prepare(conn, "select :name, :name, :name")
340+
Sqlite3.prepare(conn, "select :name, :age")
367341

368-
:ok =
369-
Sqlite3.bind(statement, %{
370-
":name" => "Alice"
371-
})
342+
assert_raise ArgumentError, ~r"expected 2 named arguments, got 1", fn ->
343+
Sqlite3.bind(statement, %{":name" => "Alice"})
344+
end
372345

373-
assert {:row, ["Alice", "Alice", "Alice"]} = Sqlite3.step(conn, statement)
346+
assert_raise ArgumentError, ~r"expected 2 named arguments, got 3", fn ->
347+
Sqlite3.bind(statement, %{":name" => "Alice", ":age" => 30, ":extra" => "value"})
348+
end
374349
end
375350
end
376351

0 commit comments

Comments
 (0)