mysql_encode.erl 3.0 KB

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