@@ -158,6 +158,12 @@ defmodule Exqlite.Sqlite3 do
158158 iex> Sqlite3.step(conn, stmt)
159159 {:row, [42, 3.14, "Alice", <<0, 0, 0>>, nil]}
160160
161+ iex> {:ok, conn} = Sqlite3.open(":memory:", [:readonly])
162+ iex> {:ok, stmt} = Sqlite3.prepare(conn, "SELECT :42, @pi, $name, @blob, :null")
163+ iex> Sqlite3.bind(stmt, %{":42" => 42, "@pi" => 3.14, "$name" => "Alice", :"@blob" => {:blob, <<0, 0, 0>>}, ~c":null" => nil})
164+ iex> Sqlite3.step(conn, stmt)
165+ {:row, [42, 3.14, "Alice", <<0, 0, 0>>, nil]}
166+
161167 iex> {:ok, conn} = Sqlite3.open(":memory:", [:readonly])
162168 iex> {:ok, stmt} = Sqlite3.prepare(conn, "SELECT ?")
163169 iex> Sqlite3.bind(stmt, [42, 3.14, "Alice"])
@@ -174,10 +180,13 @@ defmodule Exqlite.Sqlite3 do
174180 ** (ArgumentError) unsupported type: #PID<0.0.0>
175181
176182 """
177- @ spec bind ( statement , [ bind_value ] | nil ) :: :ok
183+ @ spec bind (
184+ statement ,
185+ [ bind_value ] | % { optional ( String . t ( ) ) => bind_value } | nil
186+ ) :: :ok
178187 def bind ( stmt , nil ) , do: bind ( stmt , [ ] )
179188
180- def bind ( stmt , args ) do
189+ def bind ( stmt , args ) when is_list ( args ) do
181190 params_count = bind_parameter_count ( stmt )
182191 args_count = length ( args )
183192
@@ -188,8 +197,40 @@ defmodule Exqlite.Sqlite3 do
188197 end
189198 end
190199
191- # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
200+ def bind ( stmt , args ) when is_map ( args ) do
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
210+ end
211+
192212 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+ # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
233+ defp do_bind ( stmt , idx , param ) do
193234 case convert ( param ) do
194235 i when is_integer ( i ) -> bind_integer ( stmt , idx , i )
195236 f when is_float ( f ) -> bind_float ( stmt , idx , f )
@@ -202,12 +243,8 @@ defmodule Exqlite.Sqlite3 do
202243 { :blob , b } when is_list ( b ) -> bind_blob ( stmt , idx , IO . iodata_to_binary ( b ) )
203244 _other -> raise ArgumentError , "unsupported type: #{ inspect ( param ) } "
204245 end
205-
206- bind_all ( params , stmt , idx + 1 )
207246 end
208247
209- defp bind_all ( [ ] , _stmt , _idx ) , do: :ok
210-
211248 @ spec columns ( db ( ) , statement ( ) ) :: { :ok , [ binary ( ) ] } | { :error , reason ( ) }
212249 def columns ( conn , statement ) , do: Sqlite3NIF . columns ( conn , statement )
213250
0 commit comments