changed
hex_metadata.config
|
@@ -26,4 26,4 @@
|
26
26
|
{<<"optional">>,false},
|
27
27
|
{<<"repository">>,<<"hexpm">>},
|
28
28
|
{<<"requirement">>,<<"~> 1.1">>}]]}.
|
29
|
- {<<"version">>,<<"1.1.3">>}.
|
29
|
{<<"version">>,<<"1.1.4">>}.
|
changed
lib/tds/protocol.ex
|
@@ -16,7 16,13 @@ defmodule Tds.Protocol do
|
16
16
|
@timeout 5000
|
17
17
|
@max_packet 4 * 1024
|
18
18
|
@sock_opts [packet: :raw, mode: :binary, active: false, recbuf: 4096]
|
19
|
- @trans_levels [:read_uncommited, :read_commited, :repeatable_read, :snapshot, :serializable]
|
19
|
@trans_levels [
|
20
|
:read_uncommited,
|
21
|
:read_commited,
|
22
|
:repeatable_read,
|
23
|
:snapshot,
|
24
|
:serializable
|
25
|
]
|
20
26
|
|
21
27
|
defstruct sock: nil,
|
22
28
|
usock: nil,
|
|
@@ -104,23 110,34 @@ defmodule Tds.Protocol do
|
104
110
|
end
|
105
111
|
|
106
112
|
def handle_execute(
|
107
|
- %Query{statement: statement} = query,
|
108
|
- params,
|
109
|
- opts,
|
110
|
- %{sock: _sock} = s
|
111
|
- ) do
|
113
|
%Query{handle: handle, statement: statement} = query,
|
114
|
params,
|
115
|
opts,
|
116
|
%{sock: _sock} = s
|
117
|
) do
|
112
118
|
params = opts[:parameters] || params
|
113
119
|
|
114
|
- if params != [] do
|
115
|
- send_param_query(query, params, s)
|
116
|
- else
|
117
|
- send_query(statement, s)
|
120
|
try do
|
121
|
if params != [] do
|
122
|
send_param_query(query, params, s)
|
123
|
else
|
124
|
send_query(statement, s)
|
125
|
end
|
126
|
rescue
|
127
|
exception ->
|
128
|
stacktrace = System.stacktrace()
|
129
|
reraise exception, stacktrace
|
130
|
after
|
131
|
unless is_nil(handle) do
|
132
|
handle_close(query, opts, s)
|
133
|
end
|
118
134
|
end
|
119
135
|
end
|
120
136
|
|
121
137
|
def handle_prepare(%{statement: statement}, opts, %{sock: _sock} = s) do
|
122
|
- params = opts[:parameters]
|
123
|
- |> Parameter.prepared_params()
|
138
|
params =
|
139
|
opts[:parameters]
|
140
|
|> Parameter.prepared_params()
|
124
141
|
|
125
142
|
send_prepare(statement, params, s)
|
126
143
|
end
|
|
@@ -131,14 148,14 @@ defmodule Tds.Protocol do
|
131
148
|
send_close(query, params, s)
|
132
149
|
end
|
133
150
|
|
134
|
- def handle_begin(opts, %{sock: _, env: env}=s) do
|
151
|
def handle_begin(opts, %{sock: _, env: env} = s) do
|
135
152
|
case Keyword.get(opts, :mode, :transaction) do
|
136
153
|
:transaction ->
|
137
|
- send_transaction("TM_BEGIN_XACT", nil, %{s|transaction: :started})
|
154
|
send_transaction("TM_BEGIN_XACT", nil, %{s | transaction: :started})
|
138
155
|
|
139
156
|
:savepoint ->
|
140
157
|
savepoint = env.savepoint 1
|
141
|
- env = %{env| savepoint: savepoint}
|
158
|
env = %{env | savepoint: savepoint}
|
142
159
|
s = %{s | transaction: :started, env: env}
|
143
160
|
send_transaction("TM_SAVE_XACT", savepoint, s)
|
144
161
|
end
|
|
@@ -159,20 176,21 @@ defmodule Tds.Protocol do
|
159
176
|
# we don't need to call release savepoint as in postgresql for instance,
|
160
177
|
# when transaction DIDN'T failed. SQL will wait for
|
161
178
|
{:ok, %Tds.Result{rows: [], num_rows: 0}, s}
|
162
|
-
|
163
179
|
end
|
164
180
|
end
|
165
181
|
|
166
182
|
def handle_rollback(opts, %{sock: _sock, env: env} = s) do
|
167
183
|
case Keyword.get(opts, :mode, :transaction) do
|
168
184
|
:transaction ->
|
169
|
- env = %{env| savepoint: 0}
|
185
|
env = %{env | savepoint: 0}
|
170
186
|
s = %{s | transaction: :failed, env: env}
|
171
187
|
send_transaction("TM_ROLLBACK_XACT", 0, s)
|
172
188
|
|
173
189
|
:savepoint ->
|
174
|
- send_transaction("TM_ROLLBACK_XACT", env.savepoint, %{s | transaction: :failed})
|
175
|
-
|
190
|
send_transaction("TM_ROLLBACK_XACT", env.savepoint, %{
|
191
|
s
|
192
|
| transaction: :failed
|
193
|
})
|
176
194
|
end
|
177
195
|
end
|
178
196
|
|
|
@@ -504,40 522,44 @@ defmodule Tds.Protocol do
|
504
522
|
# end
|
505
523
|
|
506
524
|
def send_param_query(
|
507
|
- %Query{handle: handle, statement: statement} = _,
|
508
|
- params,
|
509
|
- %{transaction: :started} = s
|
510
|
- ) do
|
511
|
- msg = case handle do
|
512
|
- nil ->
|
513
|
- p = [
|
514
|
- %Parameter{
|
515
|
- name: "@statement",
|
516
|
- type: :string,
|
517
|
- direction: :input,
|
518
|
- value: statement
|
519
|
- },
|
520
|
- %Parameter{
|
521
|
- name: "@params",
|
522
|
- type: :string,
|
523
|
- direction: :input,
|
524
|
- value: Parameter.prepared_params(params)
|
525
|
- }
|
526
|
- | Parameter.prepare_params(params)
|
527
|
- ]
|
528
|
- msg_rpc(proc: :sp_executesql, params: p)
|
529
|
- handle ->
|
530
|
- p = [
|
531
|
- %Parameter{
|
532
|
- name: "@handle",
|
533
|
- type: :integer,
|
534
|
- direction: :input,
|
535
|
- value: handle
|
536
|
- }
|
537
|
- | Parameter.prepare_params(params)
|
538
|
- ]
|
539
|
- msg_rpc(proc: :sp_execute, params: p)
|
540
|
- end
|
525
|
%Query{handle: handle, statement: statement} = _,
|
526
|
params,
|
527
|
%{transaction: :started} = s
|
528
|
) do
|
529
|
msg =
|
530
|
case handle do
|
531
|
nil ->
|
532
|
p = [
|
533
|
%Parameter{
|
534
|
name: "@statement",
|
535
|
type: :string,
|
536
|
direction: :input,
|
537
|
value: statement
|
538
|
},
|
539
|
%Parameter{
|
540
|
name: "@params",
|
541
|
type: :string,
|
542
|
direction: :input,
|
543
|
value: Parameter.prepared_params(params)
|
544
|
}
|
545
|
| Parameter.prepare_params(params)
|
546
|
]
|
547
|
|
548
|
msg_rpc(proc: :sp_executesql, params: p)
|
549
|
|
550
|
handle ->
|
551
|
p = [
|
552
|
%Parameter{
|
553
|
name: "@handle",
|
554
|
type: :integer,
|
555
|
direction: :input,
|
556
|
value: handle
|
557
|
}
|
558
|
| Parameter.prepare_params(params)
|
559
|
]
|
560
|
|
561
|
msg_rpc(proc: :sp_execute, params: p)
|
562
|
end
|
541
563
|
|
542
564
|
case msg_send(msg, s) do
|
543
565
|
{:ok, %{result: result} = s} ->
|
|
@@ -552,40 574,44 @@ defmodule Tds.Protocol do
|
552
574
|
end
|
553
575
|
|
554
576
|
def send_param_query(
|
555
|
- %Query{handle: handle, statement: statement} = _,
|
556
|
- params,
|
557
|
- s
|
558
|
- ) do
|
559
|
- msg = case handle do
|
560
|
- nil ->
|
561
|
- p = [
|
562
|
- %Parameter{
|
563
|
- name: "@statement",
|
564
|
- type: :string,
|
565
|
- direction: :input,
|
566
|
- value: statement
|
567
|
- },
|
568
|
- %Parameter{
|
569
|
- name: "@params",
|
570
|
- type: :string,
|
571
|
- direction: :input,
|
572
|
- value: Parameter.prepared_params(params)
|
573
|
- }
|
574
|
- | Parameter.prepare_params(params)
|
575
|
- ]
|
576
|
- msg_rpc(proc: :sp_executesql, params: p)
|
577
|
- handle ->
|
578
|
- p = [
|
579
|
- %Parameter{
|
580
|
- name: "@handle",
|
581
|
- type: :integer,
|
582
|
- direction: :input,
|
583
|
- value: handle
|
584
|
- }
|
585
|
- | Parameter.prepare_params(params)
|
586
|
- ]
|
587
|
- msg_rpc(proc: :sp_execute, params: p)
|
588
|
- end
|
577
|
%Query{handle: handle, statement: statement} = _,
|
578
|
params,
|
579
|
s
|
580
|
) do
|
581
|
msg =
|
582
|
case handle do
|
583
|
nil ->
|
584
|
p = [
|
585
|
%Parameter{
|
586
|
name: "@statement",
|
587
|
type: :string,
|
588
|
direction: :input,
|
589
|
value: statement
|
590
|
},
|
591
|
%Parameter{
|
592
|
name: "@params",
|
593
|
type: :string,
|
863
|
direction: :input,
|
595
|
value: Parameter.prepared_params(params)
|
596
|
}
|
597
|
| Parameter.prepare_params(params)
|
598
|
]
|
599
|
|
600
|
msg_rpc(proc: :sp_executesql, params: p)
|
601
|
|
602
|
handle ->
|
603
|
p = [
|
604
|
%Parameter{
|
605
|
name: "@handle",
|
606
|
type: :integer,
|
607
|
direction: :input,
|
608
|
value: handle
|
609
|
}
|
610
|
| Parameter.prepare_params(params)
|
611
|
]
|
612
|
|
613
|
msg_rpc(proc: :sp_execute, params: p)
|
614
|
end
|
589
615
|
|
590
616
|
case msg_send(msg, s) do
|
591
617
|
{:ok, %{result: result} = s} ->
|
|
@@ -645,19 671,26 @@ defmodule Tds.Protocol do
|
645
671
|
# def message(:prelogin, _state) do
|
646
672
|
# end
|
647
673
|
|
648
|
- def message(:login, msg_login_ack(redirect: true, tokens: tokens), %{opts: opts} = s) do
|
674
|
def message(
|
675
|
:login,
|
676
|
msg_login_ack(redirect: true, tokens: tokens),
|
677
|
%{opts: opts} = s
|
678
|
) do
|
649
679
|
# we got an ENVCHANGE:redirection token, we need to disconnect and start over with new server
|
650
680
|
disconnect("redirected", s)
|
651
681
|
%{hostname: host, port: port} = tokens[:env_redirect]
|
682
|
|
652
683
|
new_opts =
|
653
684
|
opts
|
654
685
|
|> Keyword.put(:hostname, host)
|
655
686
|
|> Keyword.put(:port, port)
|
687
|
|
656
688
|
connect(new_opts)
|
657
689
|
end
|
658
690
|
|
659
691
|
def message(:login, msg_login_ack(), %{opts: opts} = s) do
|
660
692
|
state = %{s | opts: clean_opts(opts)}
|
693
|
|
661
694
|
opts
|
662
695
|
|> conn_opts()
|
663
696
|
|> IO.iodata_to_binary()
|
|
@@ -697,7 730,13 @@ defmodule Tds.Protocol do
|
697
730
|
def message(:executing, msg_trans(trans: trans), %{env: env} = s) do
|
698
731
|
result = %Tds.Result{columns: [], rows: [], num_rows: 0}
|
699
732
|
|
700
|
- {:ok, %{s | state: :ready, result: result, env: %{trans: trans, savepoint: env.savepoint}}}
|
733
|
{:ok,
|
734
|
%{
|
735
|
s
|
736
|
| state: :ready,
|
737
|
result: result,
|
738
|
env: %{trans: trans, savepoint: env.savepoint}
|
739
|
}}
|
701
740
|
end
|
702
741
|
|
703
742
|
def message(:executing, msg_prepared(params: params), %{} = s) do
|
|
@@ -717,9 756,9 @@ defmodule Tds.Protocol do
|
717
756
|
|
718
757
|
## ATTN Ack
|
719
758
|
def message(:attn, _, %{} = s) do
|
720
|
- result = %Tds.Result{columns: [], rows: [], num_rows: 0}
|
759
|
result = %Tds.Result{columns: [], rows: [], num_rows: 0}
|
721
760
|
|
722
|
- { :ok, %{s | statement: "", state: :ready, result: result} }
|
761
|
{:ok, %{s | statement: "", state: :ready, result: result}}
|
723
762
|
end
|
724
763
|
|
725
764
|
# defp simple_send(msg, %{sock: {mod, sock}, env: env}) do
|
|
@@ -783,16 822,18 @@ defmodule Tds.Protocol do
|
783
822
|
(buffer <> header)
|
784
823
|
|> package_recv(s, length - 8)
|
785
824
|
|
786
|
- {:ok, <<
|
787
|
- _type::int8,
|
788
|
- status::int8,
|
789
|
- length::int16,
|
790
|
- _spid::int16,
|
791
|
- _package::int8,
|
792
|
- _window::int8
|
793
|
- >> = header } ->
|
825
|
{:ok,
|
826
|
<<
|
827
|
_type::int8,
|
828
|
status::int8,
|
829
|
length::int16,
|
830
|
_spid::int16,
|
831
|
_package::int8,
|
832
|
_window::int8
|
833
|
>> = header} ->
|
794
834
|
(buffer <> header)
|
795
835
|
|> package_recv(s, length - 8)
|
836
|
|
796
837
|
raise "Status #{inspect(status)} of tds package is not yer supported!"
|
797
838
|
|
798
839
|
{:error, :closed} ->
|
|
@@ -896,123 937,162 @@ defmodule Tds.Protocol do
|
896
937
|
|
897
938
|
defp append_opts(conn, opts, :set_language) do
|
898
939
|
case Keyword.get(opts, :set_language) do
|
899
|
- nil -> conn
|
940
|
nil -> conn
|
900
941
|
val -> conn ["SET LANGUAGE #{val}; "]
|
901
942
|
end
|
902
943
|
end
|
903
944
|
|
904
945
|
defp append_opts(conn, opts, :set_datefirst) do
|
905
946
|
case Keyword.get(opts, :set_datefirst) do
|
906
|
- nil -> conn
|
907
|
- val when val in 1..7 -> conn ["SET DATEFIRST #{val}; "]
|
908
|
- val -> raise(
|
909
|
- ArgumentError,
|
910
|
- "set_datefirst: #{inspect(val)} is out of bounds, valid range is 1..7"
|
911
|
- )
|
947
|
nil ->
|
948
|
conn
|
949
|
|
950
|
val when val in 1..7 ->
|
951
|
conn ["SET DATEFIRST #{val}; "]
|
952
|
|
953
|
val ->
|
954
|
raise(
|
955
|
ArgumentError,
|
956
|
"set_datefirst: #{inspect(val)} is out of bounds, valid range is 1..7"
|
957
|
)
|
912
958
|
end
|
913
959
|
end
|
914
960
|
|
915
961
|
defp append_opts(conn, opts, :set_dateformat) do
|
916
962
|
case Keyword.get(opts, :set_dateformat) do
|
917
|
- nil -> conn
|
963
|
nil ->
|
964
|
conn
|
965
|
|
918
966
|
val when val in [:mdy, :dmy, :ymd, :ydm, :myd, :dym] ->
|
919
967
|
conn ["SET DATEFORMAT #{val}; "]
|
920
|
- val -> raise(
|
921
|
- ArgumentError,
|
922
|
- "set_dateformat: #{inspect(val)} is an invalid value, " <>
|
923
|
- "valid values are [:mdy, :dmy, :ymd, :ydm, :myd, :dym]"
|
924
|
- )
|
968
|
|
969
|
val ->
|
970
|
raise(
|
971
|
ArgumentError,
|
972
|
"set_dateformat: #{inspect(val)} is an invalid value, " <>
|
973
|
"valid values are [:mdy, :dmy, :ymd, :ydm, :myd, :dym]"
|
974
|
)
|
925
975
|
end
|
926
976
|
end
|
927
977
|
|
928
|
-
|
929
978
|
defp append_opts(conn, opts, :set_deadlock_priority) do
|
930
979
|
case Keyword.get(opts, :set_deadlock_priority) do
|
931
|
- nil -> conn
|
980
|
nil ->
|
981
|
conn
|
982
|
|
932
983
|
val when val in [:low, :high, :normal] ->
|
933
984
|
conn ["SET DEADLOCK_PRIORITY #{val}; "]
|
934
|
- nil -> conn
|
985
|
|
986
|
nil ->
|
987
|
conn
|
988
|
|
935
989
|
val when val in -10..10 ->
|
936
990
|
conn ["SET DEADLOCK_PRIORITY #{val}; "]
|
937
|
- val -> raise(
|
938
|
- ArgumentError,
|
939
|
- "set_deadlock_priority: #{inspect(val)} is an invalid value, " <>
|
940
|
- "valid values are #{inspect([:low, :high, :normal|-10..10])}"
|
941
|
- )
|
991
|
|
992
|
val ->
|
993
|
raise(
|
994
|
ArgumentError,
|
995
|
"set_deadlock_priority: #{inspect(val)} is an invalid value, " <>
|
996
|
"valid values are #{inspect([:low, :high, :normal | -10..10])}"
|
997
|
)
|
942
998
|
end
|
943
999
|
end
|
944
1000
|
|
945
1001
|
defp append_opts(conn, opts, :set_lock_timeout) do
|
946
1002
|
case Keyword.get(opts, :set_lock_timeout) do
|
947
|
- nil -> conn
|
1003
|
nil ->
|
1004
|
conn
|
1005
|
|
948
1006
|
val when val > 0 ->
|
949
1007
|
conn ["SET LOCK_TIMEOUT #{val}; "]
|
950
|
- val -> raise(
|
951
|
- ArgumentError,
|
952
|
- "set_lock_timeout: #{inspect(val)} is an invalid value, " <>
|
953
|
- "must be an positive integer."
|
954
|
- )
|
1008
|
|
1009
|
val ->
|
1010
|
raise(
|
1011
|
ArgumentError,
|
1012
|
"set_lock_timeout: #{inspect(val)} is an invalid value, " <>
|
1013
|
"must be an positive integer."
|
1014
|
)
|
955
1015
|
end
|
956
1016
|
end
|
957
1017
|
|
958
1018
|
defp append_opts(conn, opts, :set_remote_proc_transactions) do
|
959
1019
|
case Keyword.get(opts, :set_remote_proc_transactions) do
|
960
|
- nil -> conn
|
1020
|
nil ->
|
1021
|
conn
|
1022
|
|
961
1023
|
val when val in [:on, :off] ->
|
962
1024
|
val = val |> Atom.to_string() |> String.upcase()
|
963
1025
|
conn ["SET REMOTE_PROC_TRANSACTIONS #{val}; "]
|
964
|
- val -> raise(
|
965
|
- ArgumentError,
|
966
|
- "set_remote_proc_transactions: #{inspect(val)} is an invalid value, " <>
|
967
|
- "should be either :on, :off, nil"
|
968
|
- )
|
1026
|
|
1027
|
val ->
|
1028
|
raise(
|
1029
|
ArgumentError,
|
1030
|
"set_remote_proc_transactions: #{inspect(val)} is an invalid value, " <>
|
1031
|
"should be either :on, :off, nil"
|
1032
|
)
|
969
1033
|
end
|
970
1034
|
end
|
971
1035
|
|
972
1036
|
defp append_opts(conn, opts, :set_implicit_transactions) do
|
973
1037
|
case Keyword.get(opts, :set_implicit_transactions) do
|
974
|
- nil -> conn ["SET IMPLICIT_TRANSACTIONS OFF; "]
|
1038
|
nil ->
|
1039
|
conn ["SET IMPLICIT_TRANSACTIONS OFF; "]
|
1040
|
|
975
1041
|
val when val in [:on, :off] ->
|
976
1042
|
val = val |> Atom.to_string() |> String.upcase()
|
977
1043
|
conn ["SET IMPLICIT_TRANSACTIONS #{val}; "]
|
978
|
- val -> raise(
|
979
|
- ArgumentError,
|
980
|
- "set_implicit_transactions: #{inspect(val)} is an invalid value, " <>
|
981
|
- "should be either :on, :off, nil"
|
982
|
- )
|
1044
|
|
1045
|
val ->
|
1046
|
raise(
|
1047
|
ArgumentError,
|
1048
|
"set_implicit_transactions: #{inspect(val)} is an invalid value, " <>
|
1049
|
"should be either :on, :off, nil"
|
1050
|
)
|
983
1051
|
end
|
984
1052
|
end
|
985
1053
|
|
986
|
-
|
987
1054
|
defp append_opts(conn, opts, :set_transaction_isolation_level) do
|
988
1055
|
case Keyword.get(opts, :set_transaction_isolation_level) do
|
989
|
- nil -> conn
|
1056
|
nil ->
|
1057
|
conn
|
1058
|
|
990
1059
|
val when val in @trans_levels ->
|
991
|
- t = val
|
992
|
- |> Atom.to_string()
|
993
|
- |> String.replace("_", " ")
|
994
|
- |> String.upcase()
|
1060
|
t =
|
1061
|
val
|
1062
|
|> Atom.to_string()
|
1063
|
|> String.replace("_", " ")
|
1064
|
|> String.upcase()
|
1065
|
|
995
1066
|
conn ["SET TRANSACTION ISOLATION LEVEL #{t}; "]
|
996
|
- val -> raise(
|
997
|
- ArgumentError,
|
998
|
- "set_transaction_isolation_level: #{inspect(val)} is an invalid value, " <>
|
999
|
- "should be one of #{inspect(@trans_levels)} or nil"
|
1000
|
- )
|
1067
|
|
1068
|
val ->
|
1069
|
raise(
|
1070
|
ArgumentError,
|
1071
|
"set_transaction_isolation_level: #{inspect(val)} is an invalid value, " <>
|
1072
|
"should be one of #{inspect(@trans_levels)} or nil"
|
1073
|
)
|
1001
1074
|
end
|
1002
1075
|
end
|
1003
1076
|
|
1004
1077
|
defp append_opts(conn, opts, :set_allow_snapshot_isolation) do
|
1005
1078
|
database = Keyword.get(opts, :database)
|
1079
|
|
1006
1080
|
case Keyword.get(opts, :set_allow_snapshot_isolation) do
|
1007
|
- nil -> conn
|
1081
|
nil ->
|
1082
|
conn
|
1083
|
|
1008
1084
|
val when val in [:on, :off] ->
|
1009
1085
|
val = val |> Atom.to_string() |> String.upcase()
|
1010
|
- conn ["ALTER DATABASE [#{database}] SET ALLOW_SNAPSHOT_ISOLATION #{val}; "]
|
1011
|
- val -> raise(
|
1012
|
- ArgumentError,
|
1013
|
- "set_allow_snapshot_isolation: #{inspect(val)} is an invalid value, " <>
|
1014
|
- "should be either :on, :off, nil"
|
1015
|
- )
|
1086
|
|
1087
|
conn
|
1088
|
["ALTER DATABASE [#{database}] SET ALLOW_SNAPSHOT_ISOLATION #{val}; "]
|
1089
|
|
1090
|
val ->
|
1091
|
raise(
|
1092
|
ArgumentError,
|
1093
|
"set_allow_snapshot_isolation: #{inspect(val)} is an invalid value, " <>
|
1094
|
"should be either :on, :off, nil"
|
1095
|
)
|
1016
1096
|
end
|
1017
1097
|
end
|
1018
1098
|
end
|
changed
mix.exs
|
@@ -5,7 5,7 @@ defmodule Tds.Mixfile do
|
5
5
|
def project do
|
6
6
|
[
|
7
7
|
app: :tds,
|
8
|
- version: "1.1.3",
|
8
|
version: "1.1.4",
|
9
9
|
elixir: "~> 1.0",
|
10
10
|
deps: deps(),
|
11
11
|
test_coverage: [tool: ExCoveralls],
|