Skip to content

Commit fb3ca8a

Browse files
author
Simon Thörnqvist
committed
wip! tvp columns
1 parent ae19823 commit fb3ca8a

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

@@ -987,8 +988,8 @@ defmodule Tds.Types do
987988

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

994995
{[{bin_type, attr} | attrs], bin}
@@ -1008,9 +1009,66 @@ defmodule Tds.Types do
10081009
column_length <> column_meta <> <<0x00>> <> row_data <> <<0x00>>
10091010
end
10101011

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