Browse Source

Fix and extend server version parsing

Jan Uhlig 3 years ago
parent
commit
00bef88fe0
2 changed files with 45 additions and 2 deletions
  1. 2 1
      include/records.hrl
  2. 43 1
      src/mysql_protocol.erl

+ 2 - 1
include/records.hrl

@@ -19,7 +19,8 @@
 %% --- Records ---
 
 %% Returned by parse_handshake/1.
--record(handshake, {server_version :: [integer()],
+-record(handshake, {server_vendor :: mysql | mariadb,
+                    server_version :: [integer()],
                     connection_id :: integer(),
                     capabilities :: integer(),
                     character_set :: integer(),

+ 43 - 1
src/mysql_protocol.erl

@@ -358,7 +358,9 @@ parse_handshake(<<10, Rest/binary>>) ->
         <<AuthPluginNameTrimmed:L/binary, 0>> -> AuthPluginNameTrimmed;
         _ -> AuthPluginName
     end,
-    #handshake{server_version = server_version_to_list(ServerVersion),
+    {Vendor, Version} = get_server_info(ServerVersion),
+    #handshake{server_vendor = Vendor,
+               server_version = Version,
                connection_id = ConnectionId,
                capabilities = Capabilities,
                character_set = CharacterSet,
@@ -372,6 +374,24 @@ parse_handshake(<<?ERROR, ErrNo:16/little, Msg/binary>>) ->
 parse_handshake(<<Protocol:8, _/binary>>) when Protocol /= 10 ->
     error(unknown_protocol).
 
+%% @doc Detects server vendor and version from a version string.
+-spec get_server_info(binary()) -> {mysql | mariadb, [integer()]}.
+get_server_info(<<"5.5.5-", Rest/binary>>) ->
+    %% Iff the version string starts with "5.5.5-", it is MariaDB>=10.
+    %% The real version follows after the "5.5.5-" prefix.
+    %% https://mariadb.com/kb/en/connection/#initial-handshake-packet
+    {mariadb, server_version_to_list(Rest)};
+get_server_info(ServerVersion) ->
+    Version = server_version_to_list(ServerVersion),
+    Vendor = case Version of
+        [5, 1|_] -> mysql; %% There is both MySQL and MariaDB 5.1; we assume it is MySQL
+        [5, 2|_] -> mariadb; %% There is no MySQL 5.2
+        [5, 3|_] -> mariadb; %% There is no MySQL 5.3
+        [5, 5|_] -> mysql; %% There is both MySQL and MariaDB 5.5; we assume it is MySQL
+        _ -> mysql %% Everything else is definitely MySQL
+    end,
+    {Vendor, Version}.
+
 %% @doc Converts a version on the form `<<"5.6.21">' to a list `[5, 6, 21]'.
 -spec server_version_to_list(binary()) -> [integer()].
 server_version_to_list(ServerVersion) ->
@@ -1573,6 +1593,28 @@ nulterm_str(Bin) ->
 %% Testing some of the internal functions, mostly the cases we don't cover in
 %% other tests.
 
+server_info_test() ->
+    lists:foreach(
+        fun ({Input, Exp}) ->
+            ?assertEqual(Exp, get_server_info(Input)),
+            ?assertEqual(Exp, get_server_info(<<Input/binary, $a>>)),
+            ?assertEqual(Exp, get_server_info(<<Input/binary, $-, $a>>)),
+            ?assertEqual(Exp, get_server_info(<<Input/binary, $-, $0>>))
+        end,
+        [
+            {<<"4.1.0">>, {mysql, [4, 1, 0]}},
+            {<<"5.0.0">>, {mysql, [5, 0, 0]}},
+            {<<"5.1.0">>, {mysql, [5, 1, 0]}},
+            {<<"5.2.0">>, {mariadb, [5, 2, 0]}},
+            {<<"5.3.0">>, {mariadb, [5, 3, 0]}},
+            {<<"5.5.0">>, {mysql, [5, 5, 0]}},
+            {<<"5.6.0">>, {mysql, [5, 6, 0]}},
+            {<<"5.7.0">>, {mysql, [5, 7, 0]}},
+            {<<"8.0.0">>, {mysql, [8, 0, 0]}},
+            {<<"5.5.5-10.0.0">>, {mariadb, [10, 0, 0]}}
+        ]
+    ).
+
 decode_text_test() ->
     %% Int types
     lists:foreach(fun (T) ->