cover/mysql_encode.COVER.html

1 %% @private
2 %% @doc Functions for encoding a term as an SQL literal. This is not really
3 %% part of the protocol; thus the separate module.
4 -module(mysql_encode).
5
6 -export([encode/1, backslash_escape/1]).
7
8 %% @doc Encodes a term as an ANSI SQL literal so that it can be used to inside
9 %% a query. In strings only single quotes (') are escaped. If backslash escapes
10 %% are enabled for the connection, you should first use backslash_escape/1 to
11 %% escape backslashes in strings.
12 -spec encode(term()) -> iodata().
13 1 encode(null) -> <<"NULL">>;
14 encode(Int) when is_integer(Int) ->
15 1 integer_to_binary(Int);
16 encode(Float) when is_float(Float) ->
17 %% "floats are printed accurately as the shortest, correctly rounded string"
18 2 io_lib:format("~w", [Float]);
19 encode(Bin) when is_binary(Bin) ->
20 6 Escaped = binary:replace(Bin, <<"'">>, <<"''">>, [global]),
21 6 [$', Escaped, $'];
22 encode(String) when is_list(String) ->
23 4 encode(unicode:characters_to_binary(String));
24 encode(Bitstring) when is_bitstring(Bitstring) ->
25 1 ["b'", [ case B of 0 -> $0; 1 -> $1 end || <<B:1>> <= Bitstring ], $'];
26 encode({decimal, Num}) when is_float(Num); is_integer(Num) ->
27 1 encode(Num);
28 encode({decimal, Str}) when is_binary(Str); is_list(Str) ->
29 %% Simple injection block
30 1 nomatch = re:run(Str, <<"[^0-9.+\\-eE]">>),
31 1 Str;
32 encode({Y, M, D}) ->
33 2 io_lib:format("'~4..0b-~2..0b-~2..0b'", [Y, M, D]);
34 encode({{Y, M, D}, {H, Mi, S}}) when is_integer(S) ->
35 3 io_lib:format("'~4..0b-~2..0b-~2..0b ~2..0b:~2..0b:~2..0b'",
36 [Y, M, D, H, Mi, S]);
37 encode({{Y, M, D}, {H, Mi, S}}) when is_float(S) ->
38 1 io_lib:format("'~4..0b-~2..0b-~2..0b ~2..0b:~2..0b:~9.6.0f'",
39 [Y, M, D, H, Mi, S]);
40 encode({D, {H, M, S}}) when D >= 0 ->
41 5 H1 = D * 24 + H,
42 5 Args = [H1, M, S],
43 5 if
44 1 H1 > 99, is_integer(S) -> io_lib:format("'~b:~2..0b:~2..0b'", Args);
45 1 H1 > 99, is_float(S) -> io_lib:format("'~b:~2..0b:~9.6.0f'", Args);
46 2 is_integer(S) -> io_lib:format("'~2..0b:~2..0b:~2..0b'", Args);
47 1 is_float(S) -> io_lib:format("'~2..0b:~2..0b:~9.6.0f'", Args)
48 end;
49 encode({D, {H, M, S}}) when D < 0, is_integer(S) ->
50 5 Sec = (D * 24 + H) * 3600 + M * 60 + S,
51 5 {D1, {H1, M1, S1}} = calendar:seconds_to_daystime(-Sec),
52 5 H2 = D1 * 24 + H1,
53 5 Args = [H2, M1, S1],
54 5 if
55 1 H2 > 99 -> io_lib:format("'-~b:~2..0b:~2..0b'", Args);
56 4 true -> io_lib:format("'-~2..0b:~2..0b:~2..0b'", Args)
57 end;
58 encode({D, {H, M, S}}) when D < 0, is_float(S) ->
59 3 SInt = trunc(S), % trunc(57.654321) = 57
60 3 {SInt1, Frac} = case abs(S - SInt) of % 57.6543 - 57 = 0.654321
61 2 +0.0 -> {SInt, 0.0};
62 1 Rest -> {SInt + 1, 1 - Rest} % {58, 0.345679}
63 end,
64 3 Sec = (D * 24 + H) * 3600 + M * 60 + SInt1,
65 3 {D1, {H1, M1, S1}} = calendar:seconds_to_daystime(-Sec),
66 3 H2 = D1 * 24 + H1,
67 3 Args = [H2, M1, S1 + Frac],
68 3 if
69 1 H2 > 99 -> io_lib:format("'-~b:~2..0b:~9.6.0f'", Args);
70 2 true -> io_lib:format("'-~2..0b:~2..0b:~9.6.0f'", Args)
71 end.
72
73 %% @doc Escapes backslashes with an extra backslash. This is necessary if
74 %% backslash escapes are enabled in the session.
75 backslash_escape(String) ->
76 2 Bin = iolist_to_binary(String),
77 2 binary:replace(Bin, <<"\\">>, <<"\\\\">>, [global]).
Line Hits Source