Skip to content

Commit

Permalink
Allow passing %Tds.Parameter structs as params for named parameter …
Browse files Browse the repository at this point in the history
…usage in `query` (#579)
  • Loading branch information
simonmcconnell authored Apr 24, 2024
1 parent 4b81ea5 commit c994b85
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
19 changes: 16 additions & 3 deletions lib/ecto/adapters/tds/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,15 @@ if Code.ensure_loaded?(Tds) do

def to_constraints(_, _opts), do: []

defp prepare_params(params) do
def prepare_params(params) do
{params, _} =
Enum.map_reduce(params, 1, fn param, acc ->
{value, type} = prepare_param(param)
{%Tds.Parameter{name: "@#{acc}", value: value, type: type}, acc + 1}
case prepare_param(param) do
{value, type} -> {%Tds.Parameter{name: "@#{acc}", value: value, type: type}, acc + 1}
%Tds.Parameter{name: ""} = param -> {%{param | name: "@#{acc}"}, acc + 1}
%Tds.Parameter{name: <<"@", _::binary>>} = param -> {param, acc}
_ -> error!(nil, "Tds parameter names must begin with @")
end
end)

params
Expand All @@ -109,6 +113,15 @@ if Code.ensure_loaded?(Tds) do
{value, :time}
end

defp prepare_param(%Tds.Parameter{type: nil, value: value} = param) do
{_value, type} = prepare_param(value)
%{param | type: type}
end

defp prepare_param(%Tds.Parameter{} = param) do
param
end

defp prepare_param(%{__struct__: module} = _value) do
# just in case dumpers/loaders are not defined for the this struct
error!(
Expand Down
132 changes: 132 additions & 0 deletions test/ecto/adapters/tds_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1912,6 +1912,138 @@ defmodule Ecto.Adapters.TdsTest do
]
end

describe "prepare_params/1" do
test "prepares string params" do
assert SQL.prepare_params(["string", "string" <> <<0>>]) == [
%Tds.Parameter{
name: "@1",
value: "string",
type: :string
},
%Tds.Parameter{
name: "@2",
value: "string" <> <<0>>,
type: :binary
}
]
end

test "prepares integer param" do
assert SQL.prepare_params([6]) == [
%Tds.Parameter{
name: "@1",
value: 6
}
]
end

test "prepares float param" do
assert SQL.prepare_params([4.2]) == [
%Tds.Parameter{
name: "@1",
value: 4.2
}
]
end

test "prepares boolean params" do
assert SQL.prepare_params([true, false]) == [
%Tds.Parameter{
name: "@1",
value: 1,
type: :boolean
},
%Tds.Parameter{
name: "@2",
value: 0,
type: :boolean
}
]
end

test "prepares Decimal param" do
assert SQL.prepare_params([Decimal.new(17)]) == [
%Tds.Parameter{
name: "@1",
value: Decimal.new("17"),
type: :decimal
}
]
end

test "prepares map param" do
assert SQL.prepare_params([%{}]) == [
%Tds.Parameter{
name: "@1",
value: "{}",
type: :string
}
]
end

test "prepares date and time params" do
assert SQL.prepare_params([
~N[2019-01-01 00:00:00],
~U[2019-01-01 00:00:00Z],
~D[2019-01-01],
~T[12:34:56]
]) == [
%Tds.Parameter{
name: "@1",
value: ~N[2019-01-01 00:00:00],
type: :datetime2
},
%Tds.Parameter{
name: "@2",
value: ~U[2019-01-01 00:00:00Z],
type: :datetimeoffset
},
%Tds.Parameter{
name: "@3",
value: ~D[2019-01-01],
type: :date
},
%Tds.Parameter{
name: "@4",
direction: :input,
value: ~T[12:34:56],
type: :time
}
]
end

test "prepares Tds.Parameter params" do
params = [
%Tds.Parameter{name: "@IntParam", value: 17},
%Tds.Parameter{value: "string"},
%Tds.Parameter{name: "@StringIntParam", value: "17", type: :int}
]

assert SQL.prepare_params(params) == [
%Tds.Parameter{
name: "@IntParam",
value: 17
},
%Tds.Parameter{
name: "@1",
value: "string",
type: :string
},
%Tds.Parameter{
name: "@StringIntParam",
value: "17",
type: :int
}
]
end

test "params without a valid name raise an error" do
assert_raise ArgumentError, ~r/Tds parameter names must begin with @/, fn ->
SQL.prepare_params([%Tds.Parameter{name: "Param"}])
end
end
end

defp remove_newlines(string) when is_binary(string) do
string |> String.trim() |> String.replace("\n", " ")
end
Expand Down

0 comments on commit c994b85

Please sign in to comment.