Skip to content

Commit 1f028aa

Browse files
authored
Merge pull request #52 from NickNeck/fix/handle
Fix/handle 0 exception
2 parents b433d31 + bf92d56 commit 1f028aa

5 files changed

Lines changed: 105 additions & 53 deletions

File tree

lib/tds/messages.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,15 @@ defmodule Tds.Messages do
8989
<<170::little-size(8), _::binary>> ->
9090
[error: error] = decode_tokens(tail, [])
9191
msg_error(e: error)
92-
_ ->
92+
_ ->
9393
msg_login_ack(type: 4, data: tail)
9494
end
9595
end
9696

9797
def parse(:executing, @tds_pack_reply, _header, tail) do
9898
tokens = []
9999
tokens = decode_tokens(tail, tokens)
100-
100+
101101
case tokens do
102102
[error: error] ->
103103
msg_error(e: error)
@@ -374,7 +374,9 @@ defmodule Tds.Messages do
374374
p_name = to_little_ucs2(name)
375375
p_flags = param |> Tds.Parameter.option_flags
376376
{type_code, type_data, type_attr} = Types.encode_data_type(param)
377+
377378
p_meta_data = <<byte_size(name)>> <> p_name <> p_flags <> type_data
379+
378380
p_meta_data <> Types.encode_data(type_code, param.value, type_attr)
379381
end
380382

lib/tds/protocol.ex

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@ defmodule Tds.Protocol do
6161
@spec ping(any) :: {:ok, any} | {:disconnect, Exception.t, any}
6262
def ping(state) do
6363
case send_query(~s{SELECT 'pong' as [msg]}, state) do
64-
{:ok, _, s} ->
64+
{:ok, _, s} ->
6565
{:ok, s}
66-
{:disconnect, :closed, s} ->
66+
{:disconnect, :closed, s} ->
6767
{:disconnect, %Tds.Error{message: "Connection closed."}, s}
68-
{:error, err, s} ->
68+
{:error, err, s} ->
6969
err = cond do
7070
Exception.exception?(err) -> err
7171
true -> %Tds.Error{message: inspect(err)}
7272
end
7373
{:disconnect, err, s}
74-
any ->
74+
any ->
7575
any
7676
end
7777
end
@@ -162,7 +162,7 @@ defmodule Tds.Protocol do
162162
# :socket_options.
163163
{:ok, [sndbuf: sndbuf, recbuf: recbuf, buffer: buffer]} =
164164
:inet.getopts(sock, [:sndbuf, :recbuf, :buffer])
165-
165+
166166
buffer = buffer
167167
|> max(sndbuf)
168168
|> max(recbuf)
@@ -172,7 +172,7 @@ defmodule Tds.Protocol do
172172
{:error, error, _state} ->
173173
:gen_tcp.close(sock)
174174
{:error, error}
175-
r ->
175+
r ->
176176
r
177177
end
178178
{:error, error} ->
@@ -274,7 +274,7 @@ defmodule Tds.Protocol do
274274
1 ->
275275
#status 1 means last packet of message
276276
#TODO Messages.parse does not use pak_header
277-
277+
278278
msg = parse(state, type, pak_header, pak_data <> package)
279279

280280
case message(state, msg, s) do
@@ -548,16 +548,18 @@ defmodule Tds.Protocol do
548548
Enum.each(paks, fn(pak) ->
549549
mod.send(sock, pak)
550550
end)
551+
551552
case msg_recv(<<>>, s) do
552553
{:disconnect, _ex , _s}=res ->
553554
res
554555
buffer ->
555556
new_data(buffer, %{s | state: :executing, pak_header: ""})
556557
end
557-
558+
558559
end
559-
560+
560561
defp msg_recv(buffer, %{sock: {mod, sock}} = s) do
562+
#IO.puts("buffer: #{inspect buffer}")
561563
case mod.recv(sock, 8) do
562564
# there is more tds packages after this one
563565
{:ok, <<_type::int8, 0x00, length::int16, _spid::int16, _package::int8, _window::int8>> = header} ->
@@ -566,10 +568,11 @@ defmodule Tds.Protocol do
566568
|> msg_recv(s)
567569
# this heder belongs to last package
568570
{:ok, <<_type::int8, 0x01, length::int16, _spid::int16, _package::int8, _window::int8>> = header} ->
571+
#IO.puts("header: #{inspect header}")
569572
buffer <> header
570573
|> package_recv(s, length - 8)
571574
{:ok, _} ->
572-
raise("Other statuses todo!")
575+
raise("Other statuses todo!")
573576
{:error, exception} ->
574577
{:disconnect, exception, s}
575578
end
@@ -581,13 +584,13 @@ defmodule Tds.Protocol do
581584
length = length - byte_size(data)
582585
buffer <> data
583586
|> package_recv(s, length)
584-
{:ok, data} ->
587+
{:ok, data} ->
585588
buffer <> data
586589
{:error, exception} ->
587590
{:disconnect, exception, s}
588591
end
589592
end
590-
593+
591594
# defp msg_cast(msg, %{sock: {mod, sock}, env: env} = s) do
592595
# :inet.setopts(sock, active: false)
593596

@@ -597,7 +600,7 @@ defmodule Tds.Protocol do
597600
# end)
598601
# # NOTE: this method can not be used since it is not receiving packages from SQL server!
599602
# # TODO: add :gen_tcp.recv/flush since it should flush next package if there is such case where we don't care about
600-
# # what package contains.
603+
# # what package contains.
601604
# {:ok, s}
602605
# end
603606

lib/tds/tokens.ex

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,31 @@ defmodule Tds.Tokens do
4848
raise Tds.Error, "Unknown datatype parsed when decoding return value: #{dec}"
4949
end
5050

51-
defp decode_token(<<@tds_token_returnvalue, tail::binary>>, tokens) do
52-
<<_ord::little-unsigned-16, length::size(8), rest::binary>> = tail
51+
defp decode_value(
52+
<<0x26,size::size(8),size::size(8),data::binary>>
53+
) do
54+
<<value::little-size(size)-unit(8), data::binary>> = data
55+
{value, data}
56+
end
5357

54-
length = length * 2
58+
defp decode_token(<<@tds_token_returnvalue, data::binary>>, tokens) do
59+
<<
60+
_ord::little-unsigned-16,
61+
length::size(8),
62+
name::binary-size(length)-unit(16),
63+
_status::size(8),
64+
_usertype::size(32),
65+
_flags::size(16),
66+
data::binary
67+
>> = data
5568

56-
<<name::binary-size(length)-unit(8), rest::binary>> = rest
5769
name = ucs2_to_utf(name)
70+
{value, data} = decode_value(data)
5871

59-
<<_status::size(8), _usertype::size(32), _flags::size(16), datatype::size(8), rest::binary>> = rest
60-
61-
datatype_size = retval_typ_size(datatype)
62-
63-
<<_garbage::binary-size(2), ret_val::size(datatype_size), _empty::binary-size(3), tail::binary>> = rest
64-
65-
{[parameters: {name, ret_val}]++tokens, tail}
72+
{[parameters: {name, value}] ++ tokens, data}
6673
end
67-
defp decode_token(<<@tds_token_returnstatus, _value::little-size(32), tail::binary>>, tokens) do
68-
{tokens, tail}
74+
defp decode_token(<<@tds_token_returnstatus, _value::little-size(32), data::binary>>, tokens) do
75+
{tokens, data}
6976
end
7077
# COLMETADATA
7178
defp decode_token(<<@tds_token_colmetadata, column_count::little-2*8, tail::binary>>, tokens) do
@@ -172,41 +179,41 @@ defmodule Tds.Tokens do
172179
<<0x00, value_size::unsigned-8, _old_value::binary-little-size(value_size)-unit(8), rest::binary>> = tail
173180
{tokens |> Keyword.put(:trans, <<0x00>>), rest}
174181
end
175-
182+
176183
end
177184
## DONE
178185
defp decode_token(<<@tds_token_done, status::int16, cur_cmd::binary(2), row_count::little-size(8)-unit(8), _tail::binary>>, tokens) do
179186
case Keyword.get(tokens, :done) do
180-
nil ->
187+
nil ->
181188
{Keyword.put(tokens, :done, %{status: status, cmd: cur_cmd, rows: row_count}), nil}
182189
%{rows: rows} when row_count > rows ->
183190
{Keyword.put(tokens, :done, %{status: status, cmd: cur_cmd, rows: row_count}), nil}
184-
_ ->
191+
_ ->
185192
{tokens, nil}
186193
end
187194
end
188195
## DONEPROC
189196
defp decode_token(<<@tds_token_doneproc, status::int16, cur_cmd::binary(2), row_count::little-size(8)-unit(8), _tail::binary>>, tokens) do
190197
case Keyword.get(tokens, :done) do
191-
nil ->
198+
nil ->
192199
{Keyword.put(tokens, :done, %{status: status, cmd: cur_cmd, rows: row_count}), nil}
193200
%{rows: rows} when row_count > rows ->
194201
{Keyword.put(tokens, :done, %{status: status, cmd: cur_cmd, rows: row_count}), nil}
195-
_ ->
202+
_ ->
196203
{tokens, nil}
197204
end
198205
end
199206
## DONEINPROC
200207
defp decode_token(<<@tds_token_doneinproc, status::int16, cur_cmd::binary(2), row_count::little-size(8)-unit(8), _something::binary-size(5), tail::binary>>, tokens) do
201208
case Keyword.get(tokens, :done) do
202-
nil ->
209+
nil ->
203210
{Keyword.put(tokens, :done, %{status: status, cmd: cur_cmd, rows: row_count}), tail}
204211
%{rows: rows} when row_count > rows ->
205212
{Keyword.put(tokens, :done, %{status: status, cmd: cur_cmd, rows: row_count}), nil}
206-
_ ->
213+
_ ->
207214
{tokens, nil}
208215
end
209-
216+
210217
# case tokens do
211218
# [done: done] ->
212219
# cond do

lib/tds/types.ex

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ defmodule Tds.Types do
146146
col_info = def_col_info
147147
|> Map.put(:length, length)
148148
|> Map.put(:data_reader, :bytelen)
149-
{col_info, tail}
149+
{col_info, tail}
150150
data_type_code in [
151151
@tds_data_type_timen,
152152
@tds_data_type_datetime2n,
@@ -182,7 +182,7 @@ defmodule Tds.Types do
182182
|> Map.put(:scale, scale)
183183
|> Map.put(:length, length)
184184
|> Map.put(:data_reader, :bytelen)
185-
{col_info, rest}
185+
{col_info, rest}
186186
data_type_code in [
187187
@tds_data_type_uniqueidentifier,
188188
@tds_data_type_intn,
@@ -201,7 +201,7 @@ defmodule Tds.Types do
201201
col_info = def_col_info
202202
|> Map.put(:length, length)
203203
|> Map.put(:data_reader, :bytelen)
204-
{col_info, rest}
204+
{col_info, rest}
205205
data_type_code == @tds_data_type_xml ->
206206
<<schema::unsigned-8, rest::binary>> = tail
207207
if schema == 1 do
@@ -235,10 +235,10 @@ defmodule Tds.Types do
235235
|> Map.put(:data_reader, if(length == 0xFFFF, do: :plp, else: :shortlen))
236236
|> Map.put(:length, length)
237237
{col_info, rest}
238-
238+
239239
data_type_code in [@tds_data_type_text, @tds_data_type_ntext] ->
240240
<<length::signed-32, collation::binary-5, numparts::signed-8, rest::binary>> = tail
241-
241+
242242
col_info = def_col_info
243243
|> Map.put(:collation, collation)
244244
|> Map.put(:data_reader, :longlen)
@@ -247,7 +247,7 @@ defmodule Tds.Types do
247247
rest = Enum.reduce([1..numparts], rest, fn
248248
(_, <<tsize::little-unsigned-16, _table_name::binary-size(tsize)-unit(16), next_rest::binary>>) -> next_rest
249249
end)
250-
{col_info, rest}
250+
{col_info, rest}
251251
data_type_code == @tds_data_type_image ->
252252
# TODO NumBarts Reader
253253
<<length::signed-32, numparts::signed-8, rest::binary>> = tail
@@ -264,7 +264,7 @@ defmodule Tds.Types do
264264
|> Map.put(:length, length)
265265
|> Map.put(:data_reader, :variant)
266266
{col_info, rest}
267-
267+
268268
end
269269
end
270270

@@ -962,15 +962,15 @@ defmodule Tds.Types do
962962
def encode_data(@tds_data_type_bigvarbinary = data_type, value, attr)
963963
when is_integer(value),
964964
do: encode_data(data_type, <<value>>, attr)
965-
def encode_data(@tds_data_type_bigvarbinary, nil, _),
965+
def encode_data(@tds_data_type_bigvarbinary, nil, _),
966966
do: <<@tds_plp_null::little-unsigned-64>>
967-
def encode_data(@tds_data_type_bigvarbinary, value, _),
967+
def encode_data(@tds_data_type_bigvarbinary, value, _),
968968
do: <<byte_size(value)::little-unsigned-16>> <> value
969969

970970
@doc """
971971
Data Encoding String Types
972972
"""
973-
def encode_data(@tds_data_type_nvarchar, nil, _),
973+
def encode_data(@tds_data_type_nvarchar, nil, _),
974974
do: <<@tds_plp_null::little-unsigned-64>>
975975
def encode_data(@tds_data_type_nvarchar, value, _) do
976976
value = to_little_ucs2(value)
@@ -1025,8 +1025,8 @@ defmodule Tds.Types do
10251025
<<0x04, value::little-float-32>>
10261026
else
10271027
# up to 15 digits of precision https://docs.microsoft.com/en-us/sql/t-sql/data-types/float-and-real-transact-sql
1028-
<<0x08, value::little-float-64>>
1029-
end
1028+
<<0x08, value::little-float-64>>
1029+
end
10301030
end
10311031

10321032
@doc """
@@ -1061,7 +1061,7 @@ defmodule Tds.Types do
10611061
value_binary = value_binary <> <<0::size(padding)-unit(8)>>
10621062
<<byte_len>> <> <<sign>> <> value_binary
10631063
end
1064-
def encode_data(@tds_data_type_decimaln, nil, _),
1064+
def encode_data(@tds_data_type_decimaln, nil, _),
10651065
do: <<0x00::little-unsigned-32>> # <<0, 0, 0, 0>
10661066
def encode_data(@tds_data_type_decimaln = data_type, value, attr) do
10671067
encode_data(data_type, Decimal.new(value), attr)
@@ -1152,7 +1152,7 @@ defmodule Tds.Types do
11521152

11531153
defp int_type_size(int) when int == nil, do: 4
11541154
defp int_type_size(int) when int in 0..255, do: 4
1155-
defp int_type_size(int) when int in -32768..32767, do: 2
1155+
defp int_type_size(int) when int in -32768..32767, do: 4
11561156
defp int_type_size(int) when int in -2147483648..2147483647, do: 4
11571157
defp int_type_size(int) when int in -9223372036854775808..9223372036854775807, do: 8
11581158

@@ -1365,7 +1365,4 @@ defmodule Tds.Types do
13651365
data = <<type, 0x07>>
13661366
{type, data, scale: 7}
13671367
end
1368-
1369-
1370-
13711368
end

test/handle0_test.exs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
defmodule Handle0Test do
2+
3+
use ExUnit.Case, async: true
4+
5+
import Tds.TestHelper
6+
7+
alias Tds
8+
9+
@tag timeout: 50000
10+
@table "foo"
11+
12+
setup do
13+
opts = Application.fetch_env!(:tds, :opts)
14+
{:ok, pid} = Tds.start_link(opts)
15+
16+
{:ok, [pid: pid]}
17+
end
18+
19+
test "Could not find prepared statement with handle 0.", %{pid: pid} = context do
20+
query("DROP TABLE #{@table}", [])
21+
22+
query(
23+
"""
24+
CREATE TABLE #{@table}(
25+
[id] int identity(1,1) not null primary key
26+
)
27+
""",
28+
[]
29+
)
30+
31+
n = 300
32+
for _ <- 1..n do
33+
result =
34+
Tds.query(pid, "SELECT * FROM #{@table} WHERE id = @id", [
35+
%Tds.Parameter{name: "@id", value: "7", type: :int}
36+
])
37+
38+
assert {:ok,_} = result
39+
end
40+
41+
query("DROP TABLE #{@table}", [])
42+
end
43+
end

0 commit comments

Comments
 (0)