Packages
tds
0.1.5
2.3.8
2.3.7
2.3.6
2.3.5
2.3.4
2.3.2
2.3.1
2.3.0
2.3.0-rc.1
2.3.0-rc.0
2.2.0
2.1.3
2.1.2
2.1.1
2.1.0
2.0.4
2.0.3
2.0.2
2.0.1
2.0.1-rc2
2.0.1-rc1
2.0.0
retired
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.0
1.0.19
1.0.18
1.0.17
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Microsoft SQL Server client (Elixir implementation of the MS TDS protocol)
Current section
Files
Jump to
Current section
Files
lib/tds/tokens.ex
defmodule Tds.Tokens do
import Tds.BinaryUtils
import Tds.Utils
alias Tds.Types
@tds_token_returnstatus 0x79 # 0x79
@tds_token_colmetadata 0x81 # 0x81
@tds_token_order 0xA9 # 0xA9
@tds_token_error 0xAA # 0xAA
@tds_token_info 0xAB # 0xAB
@tds_token_loginack 0xAD # 0xAD
@tds_token_row 0xD1 # 0xD1
@tds_token_nbcrow 0xD2 # 0xD1
@tds_token_envchange 0xE3 # 0xE3
@tds_token_sspi 0xED # 0xED
@tds_token_done 0xFD # 0xFD
@tds_token_doneproc 0xFE # 0xFE
@tds_token_doneinproc 0xFF # 0xFF
@tds_envtype_database 1
@tds_envtype_language 2
@tds_envtype_charset 3
@tds_envtype_packetsize 4
@tds_envtype_begintrans 8
@tds_envtype_committrans 9
@tds_envtype_rollbacktrans 10
## Decode Token Stream
def decode_tokens(tail, tokens) when tail == "" or tail == nil, do: tokens
def decode_tokens(<<tail::binary>>, tokens) do
{tokens, tail} = decode_token(tail, tokens)
decode_tokens(tail, tokens)
end
# COLMETADATA
defp decode_token(<<@tds_token_colmetadata, column_count::little-2*8, tail::binary>>, tokens) do
columns = []
{columns, tail} = decode_columns(tail, columns, column_count)
{[columns: columns]++tokens, tail}
end
# ORDER
defp decode_token(<<@tds_token_order, length::little-unsigned-16, tail::binary>>, tokens) do
length = trunc(length / 2)
{columns, tail} = decode_column_order(tail, length, [])
{[order: columns]++tokens, tail}
end
# ERROR
defp decode_token(<<@tds_token_error, length::little-size(16), number::little-size(32), state, class,
msg_len::little-size(16), msg::binary-size(msg_len)-unit(16),
sn_len, server_name::binary-size(sn_len)-unit(16),
pn_len, proc_name::binary-size(pn_len)-unit(16),
line_number::little-size(32),
_data::binary>>, _tokens) do
e = %{
length: length,
number: number,
state: state,
class: class,
msg_text: ucs2_to_utf(msg),
server_name: ucs2_to_utf(server_name),
proc_name: ucs2_to_utf(proc_name),
line_number: line_number,
}
{[error: e], nil}
end
defp decode_token(<<@tds_token_info, length::little-size(16), number::little-size(32), state, class,
msg_len::little-size(16), msg::binary-size(msg_len)-unit(16),
sn_len, server_name::binary-size(sn_len)-unit(16),
pn_len, proc_name::binary-size(pn_len)-unit(16),
line_number::little-size(32),
tail::binary>>, _tokens) do
i = %{
length: length,
number: number,
state: state,
class: class,
msg_text: ucs2_to_utf(msg),
server_name: ucs2_to_utf(server_name),
proc_name: ucs2_to_utf(proc_name),
line_number: line_number,
}
{[info: i], tail}
end
## ROW
defp decode_token(<<@tds_token_row, tail::binary>>, tokens) do
column_count = Enum.count tokens[:columns]
{row, tail} = decode_row_columns(tail, tokens, {}, column_count, 0)
tokens = Keyword.update(tokens, :rows, [row], fn(_x) -> [row|tokens[:rows]] end)
{tokens, tail}
end
## NBC ROW
defp decode_token(<<@tds_token_nbcrow, tail::binary>>, tokens) do
column_count = Enum.count tokens[:columns]
{bitmap_bytes, _} = column_count / 8
|> Float.ceil
|> Float.to_char_list([decimals: 0])
|> to_string
|> Integer.parse
{bitmap, tail} = bitmap_list(tail, bitmap_bytes)
bitmap = bitmap |> Enum.reverse
{row, tail} = decode_row_columns(tail, tokens, {}, column_count, 0, bitmap)
tokens = Keyword.update(tokens, :rows, [row], fn(_x) -> [row|tokens[:rows]] end)
{tokens, tail}
end
defp decode_token(<<@tds_token_envchange, length::little-unsigned-16, env_type::unsigned-8, tail::binary>> = data, tokens) do
token = case env_type do
@tds_envtype_database ->
<<new_value_size::unsigned-8,
new_value::binary-little-size(new_value_size)-unit(8),
old_value_size::unsigned-8,
old_value::binary-little-size(old_value_size)-unit(8),
tail::binary>> = tail
@tds_envtype_packetsize ->
<<new_value_size::unsigned-8,
new_value::binary-little-size(new_value_size)-unit(8),
old_value_size::unsigned-8,
old_value::binary-little-size(old_value_size)-unit(8),
tail::binary>> = tail
@tds_envtype_begintrans ->
<<value_size::unsigned-8, new_value::binary-little-size(value_size)-unit(8), 0x00, tail::binary>> = tail
#Logger.info "Begin Transaction: #{Tds.Utils.to_hex_string new_value}"
[trans: new_value]
@tds_envtype_committrans ->
<<0x00, value_size::unsigned-8, old_value::binary-little-size(value_size)-unit(8), tail::binary>> = tail
#Logger.info "Commit Transaction"
[trans: <<0x00>>]
@tds_envtype_rollbacktrans ->
<<0x00, value_size::unsigned-8, old_value::binary-little-size(value_size)-unit(8), tail::binary>> = tail
#Logger.info "Rollback Transaction"
[trans: <<0x00>>]
end
{token ++ tokens, tail}
end
## DONE
defp decode_token(<<@tds_token_done, status::int16, cur_cmd::binary(2), row_count::little-size(8)-unit(8), _tail::binary>>, tokens) do
case tokens do
[done: done] ->
cond do
row_count > done.rows -> {[done: %{status: status, cmd: cur_cmd, rows: row_count}] ++ tokens, nil}
true -> {tokens, nil}
end
{tokens, nil}
_ -> {[done: %{status: status, cmd: cur_cmd, rows: row_count}] ++ tokens, nil}
end
end
## DONEPROC
defp decode_token(<<@tds_token_doneproc, status::int16, cur_cmd::binary(2), row_count::little-size(8)-unit(8), tail::binary>>, tokens) do
case tokens do
[done: done] ->
cond do
row_count > done.rows -> {[done: %{status: status, cmd: cur_cmd, rows: row_count}] ++ tokens, nil}
true -> {tokens, nil}
end
{tokens, nil}
_ -> {[done: %{status: status, cmd: cur_cmd, rows: row_count}] ++ tokens, nil}
end
end
## DONEINPROC
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
case tokens do
[done: done] ->
cond do
row_count > done.rows -> {[done: %{status: status, cmd: cur_cmd, rows: row_count}] ++ tokens, nil}
true -> {tokens, nil}
end
{tokens, nil}
_ -> {[done: %{status: status, cmd: cur_cmd, rows: row_count}] ++ tokens, nil}
end
end
defp decode_column_order(<<tail::binary>>, n, columns) when n == 0 do
{columns, tail}
end
defp decode_column_order(<<col_id::little-unsigned-16, tail::binary>>, n, columns) do
decode_column_order(tail, n-1, [col_id|columns])
end
## Row and Column Decoders
defp bitmap_list(bitmap, <<tail::binary>>, n) when n <= 0 do
{bitmap, tail}
end
defp bitmap_list(bitmap \\ [], <<byte::binary-size(1)-unit(8), tail::binary>>, n) when n > 0 do
list = for <<bit::1 <- byte>>, do: bit
bitmap_list(list ++ bitmap, tail, n - 1)
end
defp decode_columns(<<tail::binary>>, columns, n) when n < 1 do
{Enum.reverse(columns), tail}
end
defp decode_columns(<<tail::binary>>, columns, n) do
{column, tail} = decode_column(tail)
decode_columns(tail, [column | columns], n - 1)
end
defp decode_column(<<_usertype::int32, _flags::int16, tail::binary>> = data) do
{info, tail} = Types.decode_info(tail)
{name, tail} = decode_column_name(tail)
info = info
|> Map.put(:name, name)
{info, tail}
end
defp decode_column_name(<<name_length::int8, name::unicode(name_length), tail::binary>>) do
name = name |> :unicode.characters_to_binary({:utf16, :little}, :utf8)
{name, tail}
end
defp decode_row_columns(<<tail::binary>>, _tokens, row, column_count, n) when n >= column_count do
{row, tail}
end
defp decode_row_columns(<<tail::binary>>, tokens, row, column_count, n) do
{:ok, column} = Enum.fetch(tokens[:columns], n)
{value, tail} = decode_row_column(tail, column)
row = Tuple.insert_at(row, tuple_size(row), value)
decode_row_columns(tail, tokens, row, column_count, n + 1)
end
defp decode_row_columns(<<tail::binary>>, _tokens, row, column_count, n, _bitmap) when n >= column_count do
{row, tail}
end
defp decode_row_columns(<<tail::binary>>, tokens, row, column_count, n, bitmap) do
case Enum.fetch(bitmap, n) do
{:ok, 0} ->
{:ok, column} = Enum.fetch(tokens[:columns], n)
{value, tail} = decode_row_column(tail, column)
row = Tuple.insert_at(row, tuple_size(row), value)
decode_row_columns(tail, tokens, row, column_count, n + 1, bitmap)
{_, _} ->
row = Tuple.insert_at(row, tuple_size(row), nil)
decode_row_columns(tail, tokens, row, column_count, n + 1, bitmap)
end
end
defp decode_row_column(<<tail::binary>>, column) do
Types.decode_data(column, tail)
end
end