%% MySQL/OTP – a MySQL driver for Erlang/OTP %% Copyright (C) 2014 Viktor Söderqvist %% %% This program is free software: you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation, either version 3 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program. If not, see . %% @doc This module handles conversion of values in the form they are %% represented in the text protocol to our prefered Erlang term representations. -module(mysql_text_protocol). -export([text_to_term/2]). -include("records.hrl"). -include("protocol.hrl"). %% The TYPE_* macros. %% @doc When receiving data in the text protocol, we get everything as binaries %% (except NULL). This function is used to parse these strings values. text_to_term(Type, Text) when is_binary(Text) -> case Type of ?TYPE_DECIMAL -> parse_float(Text); %% <-- this will probably change ?TYPE_TINY -> binary_to_integer(Text); ?TYPE_SHORT -> binary_to_integer(Text); ?TYPE_LONG -> binary_to_integer(Text); ?TYPE_FLOAT -> parse_float(Text); ?TYPE_DOUBLE -> parse_float(Text); ?TYPE_TIMESTAMP -> parse_datetime(Text); ?TYPE_LONGLONG -> binary_to_integer(Text); ?TYPE_INT24 -> binary_to_integer(Text); ?TYPE_DATE -> parse_date(Text); ?TYPE_TIME -> parse_time(Text); ?TYPE_DATETIME -> parse_datetime(Text); ?TYPE_YEAR -> binary_to_integer(Text); ?TYPE_VARCHAR -> Text; ?TYPE_BIT -> binary_to_integer(Text); ?TYPE_NEWDECIMAL -> parse_float(Text); %% <-- this will probably change ?TYPE_ENUM -> Text; ?TYPE_SET when Text == <<>> -> sets:new(); ?TYPE_SET -> sets:from_list(binary:split(Text, <<",">>, [global])); ?TYPE_TINY_BLOB -> Text; %% charset? ?TYPE_MEDIUM_BLOB -> Text; ?TYPE_LONG_BLOB -> Text; ?TYPE_BLOB -> Text; ?TYPE_VAR_STRING -> Text; ?TYPE_STRING -> Text; ?TYPE_GEOMETRY -> Text %% <-- what do we want here? end; text_to_term(_, null) -> %% NULL is the only value not represented as a binary. null. parse_datetime(<>) -> {{binary_to_integer(Y), binary_to_integer(M), binary_to_integer(D)}, {binary_to_integer(H), binary_to_integer(Mi), binary_to_integer(S)}}. parse_date(<>) -> {binary_to_integer(Y), binary_to_integer(M), binary_to_integer(D)}. parse_time(<>) -> {binary_to_integer(H), binary_to_integer(Mi), binary_to_integer(S)}. parse_float(Text) -> try binary_to_float(Text) catch error:badarg -> try binary_to_integer(Text) of Int -> float(Int) catch error:badarg -> %% It is something like "4e75" that must be turned into "4.0e75" binary_to_float(binary:replace(Text, <<"e">>, <<".0e">>)) end end. -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). text_to_term_test() -> %% Int types lists:foreach(fun (T) -> ?assertEqual(1, text_to_term(T, <<"1">>)) end, [?TYPE_TINY, ?TYPE_SHORT, ?TYPE_LONG, ?TYPE_LONGLONG, ?TYPE_INT24, ?TYPE_YEAR, ?TYPE_BIT]), %% Floating point and decimal numbers lists:foreach(fun (T) -> ?assertEqual(3.0, text_to_term(T, <<"3.0">>)) end, [?TYPE_FLOAT, ?TYPE_DOUBLE, ?TYPE_DECIMAL, ?TYPE_NEWDECIMAL]), ?assertEqual(3.0, text_to_term(?TYPE_FLOAT, <<"3">>)), ?assertEqual(30.0, text_to_term(?TYPE_FLOAT, <<"3e1">>)), ?assertEqual(3, text_to_term(?TYPE_LONG, <<"3">>)), %% Date and time ?assertEqual({2014, 11, 01}, text_to_term(?TYPE_DATE, <<"2014-11-01">>)), ?assertEqual({23, 59, 01}, text_to_term(?TYPE_TIME, <<"23:59:01">>)), ?assertEqual({{2014, 11, 01}, {23, 59, 01}}, text_to_term(?TYPE_DATETIME, <<"2014-11-01 23:59:01">>)), ?assertEqual({{2014, 11, 01}, {23, 59, 01}}, text_to_term(?TYPE_TIMESTAMP, <<"2014-11-01 23:59:01">>)), %% Strings and blobs lists:foreach(fun (T) -> ?assertEqual(<<"x">>, text_to_term(T, <<"x">>)) end, [?TYPE_VARCHAR, ?TYPE_ENUM, ?TYPE_TINY_BLOB, ?TYPE_MEDIUM_BLOB, ?TYPE_LONG_BLOB, ?TYPE_BLOB, ?TYPE_VAR_STRING, ?TYPE_STRING, ?TYPE_GEOMETRY]), %% Set ?assertEqual(sets:from_list([<<"b">>, <<"a">>]), text_to_term(?TYPE_SET, <<"a,b">>)), ?assertEqual(sets:from_list([]), text_to_term(?TYPE_SET, <<>>)), %% NULL ?assertEqual(null, text_to_term(?TYPE_FLOAT, null)), ok. -endif.