Browse Source

Fix reading of unsigned integers in binary protocol

Before this patch, e.g. -1536 was returned instead of 64000 for small
unsigned integers.
Johannes Weißl 8 years ago
parent
commit
03bee3c266
3 changed files with 62 additions and 8 deletions
  1. 4 0
      include/protocol.hrl
  2. 31 7
      src/mysql_protocol.erl
  3. 27 1
      test/mysql_tests.erl

+ 4 - 0
include/protocol.hrl

@@ -127,3 +127,7 @@
 -define(TYPE_VAR_STRING, 16#fd).
 -define(TYPE_STRING, 16#fe).
 -define(TYPE_GEOMETRY, 16#ff).
+
+%% --- Field flags ---
+
+-define(UNSIGNED_FLAG, 32).

+ 31 - 7
src/mysql_protocol.erl

@@ -629,16 +629,40 @@ decode_binary(#col{type = T}, Data)
     %% As of MySQL 5.6.21 we receive SET and ENUM values as STRING, i.e. we
     %% cannot convert them to atom() or sets:set(), etc.
     lenenc_str(Data);
-decode_binary(#col{type = ?TYPE_LONGLONG},
-              <<Value:64/signed-little, Rest/binary>>) ->
+decode_binary(#col{type = ?TYPE_LONGLONG, flags = F},
+              <<Value:64/signed-little, Rest/binary>>)
+  when F band ?UNSIGNED_FLAG == 0 ->
     {Value, Rest};
-decode_binary(#col{type = T}, <<Value:32/signed-little, Rest/binary>>)
-  when T == ?TYPE_LONG; T == ?TYPE_INT24 ->
+decode_binary(#col{type = ?TYPE_LONGLONG, flags = F},
+              <<Value:64/unsigned-little, Rest/binary>>)
+  when F band ?UNSIGNED_FLAG /= 0 ->
     {Value, Rest};
-decode_binary(#col{type = T}, <<Value:16/signed-little, Rest/binary>>)
-  when T == ?TYPE_SHORT; T == ?TYPE_YEAR ->
+decode_binary(#col{type = T, flags = F},
+              <<Value:32/signed-little, Rest/binary>>)
+  when (T == ?TYPE_LONG orelse T == ?TYPE_INT24) andalso
+       F band ?UNSIGNED_FLAG == 0 ->
     {Value, Rest};
-decode_binary(#col{type = ?TYPE_TINY}, <<Value:8/signed, Rest/binary>>) ->
+decode_binary(#col{type = T, flags = F},
+              <<Value:32/unsigned-little, Rest/binary>>)
+  when (T == ?TYPE_LONG orelse T == ?TYPE_INT24) andalso
+       F band ?UNSIGNED_FLAG /= 0 ->
+    {Value, Rest};
+decode_binary(#col{type = ?TYPE_SHORT, flags = F},
+              <<Value:16/signed-little, Rest/binary>>)
+  when F band ?UNSIGNED_FLAG == 0 ->
+    {Value, Rest};
+decode_binary(#col{type = T, flags = F},
+              <<Value:16/unsigned-little, Rest/binary>>)
+  when (T == ?TYPE_SHORT orelse T == ?TYPE_YEAR) andalso
+       F band ?UNSIGNED_FLAG /= 0 ->
+    {Value, Rest};
+decode_binary(#col{type = ?TYPE_TINY, flags = F},
+              <<Value:8/unsigned, Rest/binary>>)
+  when F band ?UNSIGNED_FLAG /= 0 ->
+    {Value, Rest};
+decode_binary(#col{type = ?TYPE_TINY, flags = F},
+              <<Value:8/signed, Rest/binary>>)
+  when F band ?UNSIGNED_FLAG == 0 ->
     {Value, Rest};
 decode_binary(#col{type = T, decimals = S, length = L}, Data)
   when T == ?TYPE_DECIMAL; T == ?TYPE_NEWDECIMAL ->

+ 27 - 1
test/mysql_tests.erl

@@ -440,18 +440,44 @@ int(Pid) ->
     write_read_text_binary(Pid, 127, <<"1000">>, <<"tint">>, <<"i">>),
     write_read_text_binary(Pid, -128, <<"-1000">>, <<"tint">>, <<"i">>),
     ok = mysql:query(Pid, "DROP TABLE tint"),
+    %% TINYINT UNSIGNED
+    ok = mysql:query(Pid, "CREATE TABLE tuint (i TINYINT UNSIGNED)"),
+    write_read_text_binary(Pid, 240, <<"240">>, <<"tuint">>, <<"i">>),
+    ok = mysql:query(Pid, "DROP TABLE tuint"),
     %% SMALLINT
     ok = mysql:query(Pid, "CREATE TABLE sint (i SMALLINT)"),
     write_read_text_binary(Pid, 32000, <<"32000">>, <<"sint">>, <<"i">>),
     write_read_text_binary(Pid, -32000, <<"-32000">>, <<"sint">>, <<"i">>),
     ok = mysql:query(Pid, "DROP TABLE sint"),
+    %% SMALLINT UNSIGNED
+    ok = mysql:query(Pid, "CREATE TABLE suint (i SMALLINT UNSIGNED)"),
+    write_read_text_binary(Pid, 64000, <<"64000">>, <<"suint">>, <<"i">>),
+    ok = mysql:query(Pid, "DROP TABLE suint"),
+    %% MEDIUMINT
+    ok = mysql:query(Pid, "CREATE TABLE mint (i MEDIUMINT)"),
+    write_read_text_binary(Pid, 8388000, <<"8388000">>,
+                           <<"mint">>, <<"i">>),
+    write_read_text_binary(Pid, -8388000, <<"-8388000">>,
+                           <<"mint">>, <<"i">>),
+    ok = mysql:query(Pid, "DROP TABLE mint"),
+    %% MEDIUMINT UNSIGNED
+    ok = mysql:query(Pid, "CREATE TABLE muint (i MEDIUMINT UNSIGNED)"),
+    write_read_text_binary(Pid, 16777000, <<"16777000">>,
+                           <<"muint">>, <<"i">>),
+    ok = mysql:query(Pid, "DROP TABLE muint"),
     %% BIGINT
     ok = mysql:query(Pid, "CREATE TABLE bint (i BIGINT)"),
     write_read_text_binary(Pid, 123456789012, <<"123456789012">>,
                            <<"bint">>, <<"i">>),
     write_read_text_binary(Pid, -123456789012, <<"-123456789012">>,
                            <<"bint">>, <<"i">>),
-    ok = mysql:query(Pid, "DROP TABLE bint").
+    ok = mysql:query(Pid, "DROP TABLE bint"),
+    %% BIGINT UNSIGNED
+    ok = mysql:query(Pid, "CREATE TABLE buint (i BIGINT UNSIGNED)"),
+    write_read_text_binary(Pid, 18446744073709551000,
+                           <<"18446744073709551000">>,
+                           <<"buint">>, <<"i">>),
+    ok = mysql:query(Pid, "DROP TABLE buint").
 
 %% The BIT(N) datatype in MySQL 5.0.3 and later: the equivallent to bitstring()
 bit(Pid) ->