Skip to content

Commit c994b85

Browse files
Allow passing %Tds.Parameter structs as params for named parameter usage in query (#579)
1 parent 4b81ea5 commit c994b85

3 files changed

Lines changed: 149 additions & 3 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf

lib/ecto/adapters/tds/connection.ex

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,15 @@ if Code.ensure_loaded?(Tds) do
7878

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

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

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

116+
defp prepare_param(%Tds.Parameter{type: nil, value: value} = param) do
117+
{_value, type} = prepare_param(value)
118+
%{param | type: type}
119+
end
120+
121+
defp prepare_param(%Tds.Parameter{} = param) do
122+
param
123+
end
124+
112125
defp prepare_param(%{__struct__: module} = _value) do
113126
# just in case dumpers/loaders are not defined for the this struct
114127
error!(

test/ecto/adapters/tds_test.exs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,138 @@ defmodule Ecto.Adapters.TdsTest do
19121912
]
19131913
end
19141914

1915+
describe "prepare_params/1" do
1916+
test "prepares string params" do
1917+
assert SQL.prepare_params(["string", "string" <> <<0>>]) == [
1918+
%Tds.Parameter{
1919+
name: "@1",
1920+
value: "string",
1921+
type: :string
1922+
},
1923+
%Tds.Parameter{
1924+
name: "@2",
1925+
value: "string" <> <<0>>,
1926+
type: :binary
1927+
}
1928+
]
1929+
end
1930+
1931+
test "prepares integer param" do
1932+
assert SQL.prepare_params([6]) == [
1933+
%Tds.Parameter{
1934+
name: "@1",
1935+
value: 6
1936+
}
1937+
]
1938+
end
1939+
1940+
test "prepares float param" do
1941+
assert SQL.prepare_params([4.2]) == [
1942+
%Tds.Parameter{
1943+
name: "@1",
1944+
value: 4.2
1945+
}
1946+
]
1947+
end
1948+
1949+
test "prepares boolean params" do
1950+
assert SQL.prepare_params([true, false]) == [
1951+
%Tds.Parameter{
1952+
name: "@1",
1953+
value: 1,
1954+
type: :boolean
1955+
},
1956+
%Tds.Parameter{
1957+
name: "@2",
1958+
value: 0,
1959+
type: :boolean
1960+
}
1961+
]
1962+
end
1963+
1964+
test "prepares Decimal param" do
1965+
assert SQL.prepare_params([Decimal.new(17)]) == [
1966+
%Tds.Parameter{
1967+
name: "@1",
1968+
value: Decimal.new("17"),
1969+
type: :decimal
1970+
}
1971+
]
1972+
end
1973+
1974+
test "prepares map param" do
1975+
assert SQL.prepare_params([%{}]) == [
1976+
%Tds.Parameter{
1977+
name: "@1",
1978+
value: "{}",
1979+
type: :string
1980+
}
1981+
]
1982+
end
1983+
1984+
test "prepares date and time params" do
1985+
assert SQL.prepare_params([
1986+
~N[2019-01-01 00:00:00],
1987+
~U[2019-01-01 00:00:00Z],
1988+
~D[2019-01-01],
1989+
~T[12:34:56]
1990+
]) == [
1991+
%Tds.Parameter{
1992+
name: "@1",
1993+
value: ~N[2019-01-01 00:00:00],
1994+
type: :datetime2
1995+
},
1996+
%Tds.Parameter{
1997+
name: "@2",
1998+
value: ~U[2019-01-01 00:00:00Z],
1999+
type: :datetimeoffset
2000+
},
2001+
%Tds.Parameter{
2002+
name: "@3",
2003+
value: ~D[2019-01-01],
2004+
type: :date
2005+
},
2006+
%Tds.Parameter{
2007+
name: "@4",
2008+
direction: :input,
2009+
value: ~T[12:34:56],
2010+
type: :time
2011+
}
2012+
]
2013+
end
2014+
2015+
test "prepares Tds.Parameter params" do
2016+
params = [
2017+
%Tds.Parameter{name: "@IntParam", value: 17},
2018+
%Tds.Parameter{value: "string"},
2019+
%Tds.Parameter{name: "@StringIntParam", value: "17", type: :int}
2020+
]
2021+
2022+
assert SQL.prepare_params(params) == [
2023+
%Tds.Parameter{
2024+
name: "@IntParam",
2025+
value: 17
2026+
},
2027+
%Tds.Parameter{
2028+
name: "@1",
2029+
value: "string",
2030+
type: :string
2031+
},
2032+
%Tds.Parameter{
2033+
name: "@StringIntParam",
2034+
value: "17",
2035+
type: :int
2036+
}
2037+
]
2038+
end
2039+
2040+
test "params without a valid name raise an error" do
2041+
assert_raise ArgumentError, ~r/Tds parameter names must begin with @/, fn ->
2042+
SQL.prepare_params([%Tds.Parameter{name: "Param"}])
2043+
end
2044+
end
2045+
end
2046+
19152047
defp remove_newlines(string) when is_binary(string) do
19162048
string |> String.trim() |> String.replace("\n", " ")
19172049
end

0 commit comments

Comments
 (0)