Skip to content

Commit 799e876

Browse files
author
Simon Thörnqvist
committed
wip! tvp columns
1 parent 55ec02c commit 799e876

File tree

3 files changed

+91
-5
lines changed

3 files changed

+91
-5
lines changed

lib/tds/column.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
defmodule Tds.Column do
2+
@type t :: %__MODULE__{
3+
name: String.t | nil,
4+
type: Atom | nil,
5+
opts: Keyword.t
6+
}
7+
8+
defstruct [name: "", type: nil, opts: []]
9+
end

lib/tds/types.ex

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ defmodule Tds.Types do
44
use Bitwise
55

66
alias Tds.Parameter
7+
alias Tds.Column
78
alias Tds.DateTime
89
alias Tds.DateTime2
910

@@ -983,8 +984,8 @@ defmodule Tds.Types do
983984

984985
def encode_data(@tds_data_type_tvp, %{columns: columns, rows: rows}, _attrs) do
985986
column_length = <<length(columns) :: little-unsigned-16>>
986-
{column_attrs, column_meta} = Enum.reduce(columns, {[], <<>>}, fn (%Parameter{} = param, {attrs, acc_bin}) ->
987-
{bin_type, data, attr} = encode_data_type(param)
987+
{column_attrs, column_meta} = Enum.reduce(columns, {[], <<>>}, fn (%Column{} = param, {attrs, acc_bin}) ->
988+
{bin_type, data, attr} = encode_column_type(param)
988989
bin = acc_bin <> <<0x00 :: little-unsigned-32, 0x00 :: little-unsigned-16 >> <> data <> <<0x00>>
989990

990991
{[{bin_type, attr} | attrs], bin}
@@ -1004,9 +1005,66 @@ defmodule Tds.Types do
10041005
column_length <> column_meta <> <<0x00>> <> row_data <> <<0x00>>
10051006
end
10061007

1008+
def encode_column_type(%Column{type: type} = col) when type != nil do
1009+
case type do
1010+
:varchar -> encode_bigvarchar_col_type(col)
1011+
:boolean -> encode_binary_type(%Parameter{type: :boolean, value: nil})
1012+
:varbinary -> encode_varbinary_col_type(col)
1013+
:int -> encode_integer_type(%Parameter{type: :integer, value: nil})
1014+
:decimal -> encode_decimal_type(%Parameter{type: :decimal, value: nil})
1015+
:float -> encode_float_type(%Parameter{type: :float, value: nil})
1016+
:datetime -> encode_datetime_type(%Parameter{type: :datetime, value: nil})
1017+
:smalldatetime -> encode_smalldatetime_type(%Parameter{type: :smalldatetime, value: nil})
1018+
:datetime2 -> encode_datetime2_type(%Parameter{type: :datetime2, value: nil})
1019+
:datetimeoffset -> encode_datetimeoffset_type(%Parameter{type: :datetimeoffset, value: nil})
1020+
:date -> encode_date_type(%Parameter{type: :datetimeoffset, value: nil})
1021+
:time -> encode_time_type(%Parameter{type: :time, value: nil})
1022+
:uuid -> encode_uuid_type(%Parameter{type: :uuid, value: nil})
1023+
end
1024+
end
1025+
1026+
def encode_bigvarchar_col_type(%Column{opts: opts} = col) do
1027+
type = @tds_data_type_bigvarchar
1028+
length = Keyword.get(opts, :length, 0)
1029+
collation = <<0x00, 0x00, 0x00, 0x00, 0x00>>
1030+
bin_length = if length <= 8000, do: <<8000::little-unsigned-16>>, else: <<0xFF, 0xFF>>
1031+
data = <<type>> <> bin_length <> collation
1032+
1033+
{type, data, length: length}
1034+
end
1035+
1036+
def encode_varbinary_col_type(%Column{opts: opts} = col) do
1037+
type = @tds_data_type_bigvarbinary
1038+
length = Keyword.get(opts, :length, 0)
1039+
bin_length = if length <= 8000, do: <<8000::little-unsigned-16>>, else: <<0xFF, 0xFF>>
1040+
data = <<type>> <> bin_length
1041+
1042+
{type, data, length: length}
1043+
end
1044+
10071045
@doc """
10081046
Data Encoding String Types
10091047
"""
1048+
1049+
def encode_data(@tds_data_type_bigvarchar, nil, opts) do
1050+
length = Keyword.get(opts, :length, 0)
1051+
if length <= 8000,
1052+
do: <<65535::little-unsigned-16>>,
1053+
else: <<@tds_plp_null::little-unsigned-64>>
1054+
end
1055+
1056+
def encode_data(@tds_data_type_bigvarchar, value, opts) do
1057+
value_size = byte_size(value)
1058+
cond do
1059+
value_size <= 0 ->
1060+
<<0x00::unsigned-64, 0x00::unsigned-32>>
1061+
value_size > 8000 ->
1062+
encode_plp(value)
1063+
true ->
1064+
<<value_size::little-size(2)-unit(8)>> <> value
1065+
end
1066+
end
1067+
10101068
def encode_data(@tds_data_type_nvarchar, nil, _),
10111069
do: <<@tds_plp_null::little-unsigned-64>>
10121070
def encode_data(@tds_data_type_nvarchar, value, _) do

test/tvp_test.exs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule TvpTest do
33
require Logger
44
use ExUnit.Case, async: true
55
alias Tds.Parameter
6+
alias Tds.Column
67

78
@tag timeout: 50000
89

@@ -17,20 +18,38 @@ defmodule TvpTest do
1718
assert :ok = query("BEGIN TRY DROP PROCEDURE __tvpTest DROP TYPE TvpTestType END TRY BEGIN CATCH END CATCH", [])
1819
assert :ok = query("""
1920
CREATE TYPE TvpTestType AS TABLE (
20-
d int
21+
a int,
22+
b uniqueidentifier,
23+
c varchar(100),
24+
d varbinary(max)
2125
);
2226
""", [])
27+
2328
assert :ok = query("""
2429
CREATE PROCEDURE __tvpTest (@tvp TvpTestType readonly)
2530
AS BEGIN
2631
select * from @tvp
2732
END
2833
""", [])
2934

35+
rows = [1, <<158, 3, 157, 56, 133, 56, 73, 67, 128, 121, 126, 204, 115, 227, 162, 157>>, "foo", "{\"foo\":\"bar\",\"baz\":\"biz\"}"]
3036
params = [
31-
%Parameter{name: "@tvp", value: %{name: "TvpTestType", columns: [%Parameter{name: "d", type: :integer, value: nil}], rows: [[1]]}, type: :tvp}
37+
%Parameter{
38+
name: "@tvp",
39+
value: %{
40+
name: "TvpTestType",
41+
columns: [
42+
%Column{name: "a", type: :int},
43+
%Column{name: "b", type: :uuid},
44+
%Column{name: "c", type: :varchar},
45+
%Column{name: "d", type: :varbinary},
46+
],
47+
rows: [rows]
48+
},
49+
type: :tvp
50+
}
3251
]
3352

34-
assert [[1]] = proc("__tvpTest", params)
53+
assert [^rows] = proc("__tvpTest", params)
3554
end
3655
end

0 commit comments

Comments
 (0)