mysql_protocol.erl 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. %% MySQL/OTP – MySQL client library for Erlang/OTP
  2. %% Copyright (C) 2014 Viktor Söderqvist
  3. %%
  4. %% This file is part of MySQL/OTP.
  5. %%
  6. %% MySQL/OTP is free software: you can redistribute it and/or modify it under
  7. %% the terms of the GNU Lesser General Public License as published by the Free
  8. %% Software Foundation, either version 3 of the License, or (at your option)
  9. %% any later version.
  10. %%
  11. %% This program is distributed in the hope that it will be useful, but WITHOUT
  12. %% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. %% FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  14. %% more details.
  15. %%
  16. %% You should have received a copy of the GNU Lesser General Public License
  17. %% along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. %% @doc This module implements parts of the MySQL client/server protocol.
  19. %%
  20. %% The protocol is described in the document "MySQL Internals" which can be
  21. %% found under "MySQL Documentation: Expert Guides" on http://dev.mysql.com/.
  22. %%
  23. %% TCP communication is not handled in this module. Most of the public functions
  24. %% take funs for data communitaction as parameters.
  25. %% @private
  26. -module(mysql_protocol).
  27. -export([handshake/6, quit/2, ping/2,
  28. query/4, fetch_query_response/3,
  29. prepare/3, unprepare/3, execute/5, fetch_execute_response/3]).
  30. %% How much data do we want per packet?
  31. -define(MAX_BYTES_PER_PACKET, 16#1000000).
  32. -include("records.hrl").
  33. -include("protocol.hrl").
  34. -include("server_status.hrl").
  35. %% Macros for pattern matching on packets.
  36. -define(ok_pattern, <<?OK, _/binary>>).
  37. -define(error_pattern, <<?ERROR, _/binary>>).
  38. -define(eof_pattern, <<?EOF, _:4/binary>>).
  39. %% @doc Performs a handshake using the supplied functions for communication.
  40. %% Returns an ok or an error record. Raises errors when various unimplemented
  41. %% features are requested.
  42. -spec handshake(iodata(), iodata(), iodata() | undefined, atom(),
  43. term(), boolean()) -> #handshake{} | #error{}.
  44. handshake(Username, Password, Database, TcpModule, Socket, SetFoundRows) ->
  45. SeqNum0 = 0,
  46. {ok, HandshakePacket, SeqNum1} = recv_packet(TcpModule, Socket, SeqNum0),
  47. Handshake = parse_handshake(HandshakePacket),
  48. Response = build_handshake_response(Handshake, Username, Password,
  49. Database, SetFoundRows),
  50. {ok, SeqNum2} = send_packet(TcpModule, Socket, Response, SeqNum1),
  51. handshake_finish_or_switch_auth(Handshake, Password, TcpModule, Socket, SeqNum2).
  52. handshake_finish_or_switch_auth(Handshake, Password, TcpModule, Socket, SeqNum0) ->
  53. {ok, ConfirmPacket, SeqNum1} = recv_packet(TcpModule, Socket, SeqNum0),
  54. case parse_handshake_confirm(ConfirmPacket) of
  55. #ok{status = OkStatus} ->
  56. OkStatus = Handshake#handshake.status,
  57. Handshake;
  58. #auth_method_switch{auth_plugin_name = AuthPluginName, auth_plugin_data = AuthPluginData} ->
  59. Hash = case AuthPluginName of
  60. <<>> ->
  61. hash_password(Password, AuthPluginData);
  62. <<"mysql_native_password">> ->
  63. hash_password(Password, AuthPluginData);
  64. UnknownAuthMethod ->
  65. error({auth_method, UnknownAuthMethod})
  66. end,
  67. {ok, SeqNum2} = send_packet(TcpModule, Socket, Hash, SeqNum1),
  68. handshake_finish_or_switch_auth(Handshake, Password, TcpModule, Socket, SeqNum2);
  69. Error ->
  70. Error
  71. end.
  72. -spec quit(atom(), term()) -> ok.
  73. quit(TcpModule, Socket) ->
  74. {ok, SeqNum1} = send_packet(TcpModule, Socket, <<?COM_QUIT>>, 0),
  75. case recv_packet(TcpModule, Socket, SeqNum1) of
  76. {error, closed} -> ok; %% MySQL 5.5.40 and more
  77. {ok, ?ok_pattern, _SeqNum2} -> ok %% Some older MySQL versions?
  78. end.
  79. -spec ping(atom(), term()) -> #ok{}.
  80. ping(TcpModule, Socket) ->
  81. {ok, SeqNum1} = send_packet(TcpModule, Socket, <<?COM_PING>>, 0),
  82. {ok, OkPacket, _SeqNum2} = recv_packet(TcpModule, Socket, SeqNum1),
  83. parse_ok_packet(OkPacket).
  84. -spec query(Query :: iodata(), atom(), term(), timeout()) ->
  85. {ok, [#ok{} | #resultset{} | #error{}]} | {error, timeout}.
  86. query(Query, TcpModule, Socket, Timeout) ->
  87. Req = <<?COM_QUERY, (iolist_to_binary(Query))/binary>>,
  88. SeqNum0 = 0,
  89. {ok, _SeqNum1} = send_packet(TcpModule, Socket, Req, SeqNum0),
  90. fetch_query_response(TcpModule, Socket, Timeout).
  91. %% @doc This is used by query/4. If query/4 returns {error, timeout}, this
  92. %% function can be called to retry to fetch the results of the query.
  93. fetch_query_response(TcpModule, Socket, Timeout) ->
  94. fetch_response(TcpModule, Socket, Timeout, text, []).
  95. %% @doc Prepares a statement.
  96. -spec prepare(iodata(), atom(), term()) -> #error{} | #prepared{}.
  97. prepare(Query, TcpModule, Socket) ->
  98. Req = <<?COM_STMT_PREPARE, (iolist_to_binary(Query))/binary>>,
  99. {ok, SeqNum1} = send_packet(TcpModule, Socket, Req, 0),
  100. {ok, Resp, SeqNum2} = recv_packet(TcpModule, Socket, SeqNum1),
  101. case Resp of
  102. ?error_pattern ->
  103. parse_error_packet(Resp);
  104. <<?OK,
  105. StmtId:32/little,
  106. NumColumns:16/little,
  107. NumParams:16/little,
  108. 0, %% reserved_1 -- [00] filler
  109. WarningCount:16/little>> ->
  110. %% This was the first packet.
  111. %% Now: Parameter Definition Block. The parameter definitions don't
  112. %% contain any useful data at all. They are always TYPE_VAR_STRING
  113. %% with charset 'binary' so we have to select a type ourselves for
  114. %% the parameters we have in execute/4.
  115. {_ParamDefs, SeqNum3} =
  116. fetch_column_definitions_if_any(NumParams, TcpModule, Socket,
  117. SeqNum2),
  118. %% Column Definition Block. We get column definitions in execute
  119. %% too, so we don't need them here. We *could* store them to be able
  120. %% to provide the user with some info about a prepared statement.
  121. {_ColDefs, _SeqNum4} =
  122. fetch_column_definitions_if_any(NumColumns, TcpModule, Socket,
  123. SeqNum3),
  124. #prepared{statement_id = StmtId,
  125. orig_query = Query,
  126. param_count = NumParams,
  127. warning_count = WarningCount}
  128. end.
  129. %% @doc Deallocates a prepared statement.
  130. -spec unprepare(#prepared{}, atom(), term()) -> ok.
  131. unprepare(#prepared{statement_id = Id}, TcpModule, Socket) ->
  132. {ok, _SeqNum} = send_packet(TcpModule, Socket,
  133. <<?COM_STMT_CLOSE, Id:32/little>>, 0),
  134. ok.
  135. %% @doc Executes a prepared statement.
  136. -spec execute(#prepared{}, [term()], atom(), term(), timeout()) ->
  137. {ok, [#ok{} | #resultset{} | #error{}]} | {error, timeout}.
  138. execute(#prepared{statement_id = Id, param_count = ParamCount}, ParamValues,
  139. TcpModule, Socket, Timeout) when ParamCount == length(ParamValues) ->
  140. %% Flags Constant Name
  141. %% 0x00 CURSOR_TYPE_NO_CURSOR
  142. %% 0x01 CURSOR_TYPE_READ_ONLY
  143. %% 0x02 CURSOR_TYPE_FOR_UPDATE
  144. %% 0x04 CURSOR_TYPE_SCROLLABLE
  145. Flags = 0,
  146. Req0 = <<?COM_STMT_EXECUTE, Id:32/little, Flags, 1:32/little>>,
  147. Req = case ParamCount of
  148. 0 ->
  149. Req0;
  150. _ ->
  151. %% We can't use the parameter types returned by the prepare call.
  152. %% They are all reported as ?TYPE_VAR_STRING with character
  153. %% set 'binary'.
  154. NullBitMap = build_null_bitmap(ParamValues),
  155. %% What does it mean to *not* bind new params? To use the same
  156. %% params as last time? Right now we always bind params each time.
  157. NewParamsBoundFlag = 1,
  158. Req1 = <<Req0/binary, NullBitMap/binary, NewParamsBoundFlag>>,
  159. %% For each value, first append type and signedness (16#80 signed or
  160. %% 00 unsigned) for all values and then the binary encoded values.
  161. EncodedParams = lists:map(fun encode_param/1, ParamValues),
  162. {TypesAndSigns, EncValues} = lists:unzip(EncodedParams),
  163. iolist_to_binary([Req1, TypesAndSigns, EncValues])
  164. end,
  165. {ok, _SeqNum1} = send_packet(TcpModule, Socket, Req, 0),
  166. fetch_execute_response(TcpModule, Socket, Timeout).
  167. %% @doc This is used by execute/5. If execute/5 returns {error, timeout}, this
  168. %% function can be called to retry to fetch the results of the query.
  169. fetch_execute_response(TcpModule, Socket, Timeout) ->
  170. fetch_response(TcpModule, Socket, Timeout, binary, []).
  171. %% --- internal ---
  172. %% @doc Parses a handshake. This is the first thing that comes from the server
  173. %% when connecting. If an unsupported version or variant of the protocol is used
  174. %% an error is raised.
  175. -spec parse_handshake(binary()) -> #handshake{}.
  176. parse_handshake(<<10, Rest/binary>>) ->
  177. %% Protocol version 10.
  178. {ServerVersion, Rest1} = nulterm_str(Rest),
  179. <<ConnectionId:32/little,
  180. AuthPluginDataPart1:8/binary-unit:8,
  181. 0, %% "filler" -- everything below is optional
  182. CapabilitiesLower:16/little,
  183. CharacterSet:8,
  184. StatusFlags:16/little,
  185. CapabilitiesUpper:16/little,
  186. AuthPluginDataLength:8, %% if cabab & CLIENT_PLUGIN_AUTH, otherwise 0
  187. _Reserved:10/binary-unit:8, %% 10 unused (reserved) bytes
  188. Rest3/binary>> = Rest1,
  189. Capabilities = CapabilitiesLower + 16#10000 * CapabilitiesUpper,
  190. Len = case AuthPluginDataLength of
  191. 0 -> 13; %% Server has not CLIENT_PLUGIN_AUTH
  192. K -> K - 8 %% Part 2 length = Total length minus the 8 bytes in part 1.
  193. end,
  194. <<AuthPluginDataPart2:Len/binary-unit:8, AuthPluginName/binary>> = Rest3,
  195. AuthPluginData = <<AuthPluginDataPart1/binary, AuthPluginDataPart2/binary>>,
  196. %% "Due to Bug#59453 the auth-plugin-name is missing the terminating
  197. %% NUL-char in versions prior to 5.5.10 and 5.6.2."
  198. %% Strip the final NUL byte if any.
  199. %% This may also be <<>> in older versions.
  200. L = byte_size(AuthPluginName) - 1,
  201. AuthPluginName1 = case AuthPluginName of
  202. <<AuthPluginNameTrimmed:L/binary, 0>> -> AuthPluginNameTrimmed;
  203. _ -> AuthPluginName
  204. end,
  205. #handshake{server_version = server_version_to_list(ServerVersion),
  206. connection_id = ConnectionId,
  207. capabilities = Capabilities,
  208. character_set = CharacterSet,
  209. status = StatusFlags,
  210. auth_plugin_data = AuthPluginData,
  211. auth_plugin_name = AuthPluginName1};
  212. parse_handshake(<<Protocol:8, _/binary>>) when Protocol /= 10 ->
  213. error(unknown_protocol).
  214. %% @doc Converts a version on the form `<<"5.6.21">' to a list `[5, 6, 21]'.
  215. -spec server_version_to_list(binary()) -> [integer()].
  216. server_version_to_list(ServerVersion) ->
  217. %% This must work with e.g. "5.5.40-0ubuntu0.12.04.1-log" and "5.5.33a".
  218. {match, Parts} = re:run(ServerVersion, <<"^(\\d+)\\.(\\d+)\\.(\\d+)">>,
  219. [{capture, all_but_first, binary}]),
  220. lists:map(fun binary_to_integer/1, Parts).
  221. %% @doc The response sent by the client to the server after receiving the
  222. %% initial handshake from the server
  223. -spec build_handshake_response(#handshake{}, iodata(), iodata(),
  224. iodata() | undefined, boolean()) -> binary().
  225. build_handshake_response(Handshake, Username, Password, Database, SetFoundRows) ->
  226. %% We require these capabilities. Make sure the server handles them.
  227. CapabilityFlags0 = ?CLIENT_PROTOCOL_41 bor
  228. ?CLIENT_TRANSACTIONS bor
  229. ?CLIENT_SECURE_CONNECTION,
  230. CapabilityFlags1 = case Database of
  231. undefined -> CapabilityFlags0;
  232. _ -> CapabilityFlags0 bor ?CLIENT_CONNECT_WITH_DB
  233. end,
  234. CapabilityFlags = case SetFoundRows of
  235. true -> CapabilityFlags1 bor ?CLIENT_FOUND_ROWS;
  236. _ -> CapabilityFlags1
  237. end,
  238. Handshake#handshake.capabilities band CapabilityFlags == CapabilityFlags
  239. orelse error(old_server_version),
  240. %% Add some extra capability flags only for signalling to the server what
  241. %% the client wants to do. The server doesn't say it handles them although
  242. %% it does. (http://bugs.mysql.com/bug.php?id=42268)
  243. ClientCapabilityFlags = CapabilityFlags bor
  244. ?CLIENT_MULTI_STATEMENTS bor
  245. ?CLIENT_MULTI_RESULTS bor
  246. ?CLIENT_PS_MULTI_RESULTS,
  247. Hash = case Handshake#handshake.auth_plugin_name of
  248. <<>> ->
  249. %% Server doesn't know auth plugins
  250. hash_password(Password, Handshake#handshake.auth_plugin_data);
  251. <<"mysql_native_password">> ->
  252. hash_password(Password, Handshake#handshake.auth_plugin_data);
  253. UnknownAuthMethod ->
  254. error({auth_method, UnknownAuthMethod})
  255. end,
  256. HashLength = size(Hash),
  257. CharacterSet = ?UTF8,
  258. UsernameUtf8 = unicode:characters_to_binary(Username),
  259. DbBin = case Database of
  260. undefined -> <<>>;
  261. _ -> <<(iolist_to_binary(Database))/binary, 0>>
  262. end,
  263. <<ClientCapabilityFlags:32/little,
  264. ?MAX_BYTES_PER_PACKET:32/little,
  265. CharacterSet:8,
  266. 0:23/unit:8, %% reserverd
  267. UsernameUtf8/binary,
  268. 0, %% NUL-terminator for the username
  269. HashLength,
  270. Hash/binary,
  271. DbBin/binary>>.
  272. %% @doc Handles the second packet from the server, when we have replied to the
  273. %% initial handshake. Returns an error if the server returns an error. Raises
  274. %% an error if unimplemented features are required.
  275. -spec parse_handshake_confirm(binary()) -> #ok{} | #error{}.
  276. parse_handshake_confirm(Packet) ->
  277. case Packet of
  278. ?ok_pattern ->
  279. %% Connection complete.
  280. parse_ok_packet(Packet);
  281. ?error_pattern ->
  282. %% Access denied, insufficient client capabilities, etc.
  283. parse_error_packet(Packet);
  284. <<?EOF>> ->
  285. %% "Old Authentication Method Switch Request Packet consisting of a
  286. %% single 0xfe byte. It is sent by server to request client to
  287. %% switch to Old Password Authentication if CLIENT_PLUGIN_AUTH
  288. %% capability is not supported (by either the client or the server)"
  289. error(old_auth);
  290. <<?EOF, AuthMethodSwitch/binary>> ->
  291. %% "Authentication Method Switch Request Packet. If both server and
  292. %% client support CLIENT_PLUGIN_AUTH capability, server can send
  293. %% this packet to ask client to use another authentication method."
  294. parse_auth_method_switch(AuthMethodSwitch)
  295. end.
  296. %% -- both text and binary protocol --
  297. %% @doc Fetches one or more results and and parses the result set(s) using
  298. %% either the text format (for plain queries) or the binary format (for
  299. %% prepared statements).
  300. -spec fetch_response(atom(), term(), timeout(), text | binary, list()) ->
  301. {ok, [#ok{} | #resultset{} | #error{}]} | {error, timeout}.
  302. fetch_response(TcpModule, Socket, Timeout, Proto, Acc) ->
  303. case recv_packet(TcpModule, Socket, Timeout, any) of
  304. {ok, Packet, SeqNum2} ->
  305. Result = case Packet of
  306. ?ok_pattern ->
  307. parse_ok_packet(Packet);
  308. ?error_pattern ->
  309. parse_error_packet(Packet);
  310. ResultPacket ->
  311. %% The first packet in a resultset is only the column count.
  312. {ColCount, <<>>} = lenenc_int(ResultPacket),
  313. R0 = fetch_resultset(TcpModule, Socket, ColCount, SeqNum2),
  314. case R0 of
  315. #error{} = E ->
  316. %% TODO: Find a way to get here + testcase
  317. E;
  318. #resultset{} = R ->
  319. parse_resultset(R, ColCount, Proto)
  320. end
  321. end,
  322. Acc1 = [Result | Acc],
  323. case more_results_exists(Result) of
  324. true ->
  325. fetch_response(TcpModule, Socket, Timeout, Proto, Acc1);
  326. false ->
  327. {ok, lists:reverse(Acc1)}
  328. end;
  329. {error, timeout} ->
  330. {error, timeout}
  331. end.
  332. %% @doc Fetches packets for a result set. The column definitions are parsed but
  333. %% the rows are unparsed binary packages. This function is used for both the
  334. %% text protocol and the binary protocol. This affects the way the rows need to
  335. %% be parsed.
  336. -spec fetch_resultset(atom(), term(), integer(), integer()) ->
  337. #resultset{} | #error{}.
  338. fetch_resultset(TcpModule, Socket, FieldCount, SeqNum) ->
  339. {ok, ColDefs, SeqNum1} = fetch_column_definitions(TcpModule, Socket, SeqNum,
  340. FieldCount, []),
  341. {ok, DelimiterPacket, SeqNum2} = recv_packet(TcpModule, Socket, SeqNum1),
  342. #eof{status = S, warning_count = W} = parse_eof_packet(DelimiterPacket),
  343. case fetch_resultset_rows(TcpModule, Socket, SeqNum2, []) of
  344. {ok, Rows, _SeqNum3} ->
  345. ColDefs1 = lists:map(fun parse_column_definition/1, ColDefs),
  346. #resultset{cols = ColDefs1, rows = Rows,
  347. status = S, warning_count = W};
  348. #error{} = E ->
  349. E
  350. end.
  351. parse_resultset(#resultset{cols = ColDefs, rows = Rows} = R, ColumnCount, text) ->
  352. %% Parse the rows according to the 'text protocol' representation.
  353. Rows1 = [decode_text_row(ColumnCount, ColDefs, Row) || Row <- Rows],
  354. R#resultset{rows = Rows1};
  355. parse_resultset(#resultset{cols = ColDefs, rows = Rows} = R, ColumnCount, binary) ->
  356. %% Parse the rows according to the 'binary protocol' representation.
  357. Rows1 = [decode_binary_row(ColumnCount, ColDefs, Row) || Row <- Rows],
  358. R#resultset{rows = Rows1}.
  359. more_results_exists(#ok{status = S}) ->
  360. S band ?SERVER_MORE_RESULTS_EXISTS /= 0;
  361. more_results_exists(#error{}) ->
  362. false; %% No status bits for error
  363. more_results_exists(#resultset{status = S}) ->
  364. S band ?SERVER_MORE_RESULTS_EXISTS /= 0.
  365. %% @doc Receives NumLeft column definition packets. They are not parsed.
  366. %% @see parse_column_definition/1
  367. -spec fetch_column_definitions(atom(), term(), SeqNum :: integer(),
  368. NumLeft :: integer(), Acc :: [binary()]) ->
  369. {ok, ColDefPackets :: [binary()], NextSeqNum :: integer()}.
  370. fetch_column_definitions(TcpModule, Socket, SeqNum, NumLeft, Acc)
  371. when NumLeft > 0 ->
  372. {ok, Packet, SeqNum1} = recv_packet(TcpModule, Socket, SeqNum),
  373. fetch_column_definitions(TcpModule, Socket, SeqNum1, NumLeft - 1,
  374. [Packet | Acc]);
  375. fetch_column_definitions(_TcpModule, _Socket, SeqNum, 0, Acc) ->
  376. {ok, lists:reverse(Acc), SeqNum}.
  377. %% @doc Fetches rows in a result set. There is a packet per row. The row packets
  378. %% are not decoded. This function can be used for both the binary and the text
  379. %% protocol result sets.
  380. -spec fetch_resultset_rows(atom(), term(), SeqNum :: integer(), Acc) ->
  381. {ok, Rows, integer()} | #error{}
  382. when Acc :: [binary()],
  383. Rows :: [binary()].
  384. fetch_resultset_rows(TcpModule, Socket, SeqNum, Acc) ->
  385. {ok, Packet, SeqNum1} = recv_packet(TcpModule, Socket, SeqNum),
  386. case Packet of
  387. ?error_pattern ->
  388. parse_error_packet(Packet);
  389. ?eof_pattern ->
  390. {ok, lists:reverse(Acc), SeqNum1};
  391. Row ->
  392. fetch_resultset_rows(TcpModule, Socket, SeqNum1, [Row | Acc])
  393. end.
  394. %% Parses a packet containing a column definition (part of a result set)
  395. parse_column_definition(Data) ->
  396. {<<"def">>, Rest1} = lenenc_str(Data), %% catalog (always "def")
  397. {_Schema, Rest2} = lenenc_str(Rest1), %% schema-name
  398. {_Table, Rest3} = lenenc_str(Rest2), %% virtual table-name
  399. {_OrgTable, Rest4} = lenenc_str(Rest3), %% physical table-name
  400. {Name, Rest5} = lenenc_str(Rest4), %% virtual column name
  401. {_OrgName, Rest6} = lenenc_str(Rest5), %% physical column name
  402. {16#0c, Rest7} = lenenc_int(Rest6), %% length of the following fields
  403. %% (always 0x0c)
  404. <<Charset:16/little, %% column character set
  405. Length:32/little, %% maximum length of the field
  406. Type:8, %% type of the column as defined in Column Type
  407. Flags:16/little, %% flags
  408. Decimals:8, %% max shown decimal digits:
  409. 0, %% "filler" %% - 0x00 for integers and static strings
  410. 0, %% - 0x1f for dynamic strings, double, float
  411. Rest8/binary>> = Rest7, %% - 0x00 to 0x51 for decimals
  412. %% Here, if command was COM_FIELD_LIST {
  413. %% default values: lenenc_str
  414. %% }
  415. <<>> = Rest8,
  416. #col{name = Name, type = Type, charset = Charset, length = Length,
  417. decimals = Decimals, flags = Flags}.
  418. %% -- text protocol --
  419. -spec decode_text_row(NumColumns :: integer(),
  420. ColumnDefinitions :: [#col{}],
  421. Data :: binary()) -> [term()].
  422. decode_text_row(_NumColumns, ColumnDefs, Data) ->
  423. decode_text_row_acc(ColumnDefs, Data, []).
  424. %% parses Data using ColDefs and builds the values Acc.
  425. decode_text_row_acc([ColDef | ColDefs], Data, Acc) ->
  426. case Data of
  427. <<16#fb, Rest/binary>> ->
  428. %% NULL
  429. decode_text_row_acc(ColDefs, Rest, [null | Acc]);
  430. _ ->
  431. %% Every thing except NULL
  432. {Text, Rest} = lenenc_str(Data),
  433. Term = decode_text(ColDef, Text),
  434. decode_text_row_acc(ColDefs, Rest, [Term | Acc])
  435. end;
  436. decode_text_row_acc([], <<>>, Acc) ->
  437. lists:reverse(Acc).
  438. %% @doc When receiving data in the text protocol, we get everything as binaries
  439. %% (except NULL). This function is used to parse these string values.
  440. decode_text(#col{type = T}, Text)
  441. when T == ?TYPE_TINY; T == ?TYPE_SHORT; T == ?TYPE_LONG; T == ?TYPE_LONGLONG;
  442. T == ?TYPE_INT24; T == ?TYPE_YEAR ->
  443. binary_to_integer(Text);
  444. decode_text(#col{type = T}, Text)
  445. when T == ?TYPE_STRING; T == ?TYPE_VARCHAR; T == ?TYPE_VAR_STRING;
  446. T == ?TYPE_ENUM; T == ?TYPE_SET; T == ?TYPE_LONG_BLOB;
  447. T == ?TYPE_MEDIUM_BLOB; T == ?TYPE_BLOB; T == ?TYPE_TINY_BLOB;
  448. T == ?TYPE_GEOMETRY; T == ?TYPE_JSON ->
  449. %% As of MySQL 5.6.21 we receive SET and ENUM values as STRING, i.e. we
  450. %% cannot convert them to atom() or sets:set(), etc.
  451. Text;
  452. decode_text(#col{type = ?TYPE_BIT, length = Length}, Text) ->
  453. %% Convert to <<_:Length/bitstring>>
  454. decode_bitstring(Text, Length);
  455. decode_text(#col{type = T, decimals = S, length = L}, Text)
  456. when T == ?TYPE_DECIMAL; T == ?TYPE_NEWDECIMAL ->
  457. %% Length is the max number of symbols incl. dot and minus sign, e.g. the
  458. %% number of digits plus 2.
  459. decode_decimal(Text, L - 2, S);
  460. decode_text(#col{type = ?TYPE_DATE},
  461. <<Y:4/binary, "-", M:2/binary, "-", D:2/binary>>) ->
  462. {binary_to_integer(Y), binary_to_integer(M), binary_to_integer(D)};
  463. decode_text(#col{type = ?TYPE_TIME}, Text) ->
  464. {match, [Sign, Hbin, Mbin, Sbin, Frac]} =
  465. re:run(Text,
  466. <<"^(-?)(\\d+):(\\d+):(\\d+)(\\.?\\d*)$">>,
  467. [{capture, all_but_first, binary}]),
  468. H = binary_to_integer(Hbin),
  469. M = binary_to_integer(Mbin),
  470. S = binary_to_integer(Sbin),
  471. IsNeg = Sign == <<"-">>,
  472. Fraction = case Frac of
  473. <<>> -> 0;
  474. _ when not IsNeg -> binary_to_float(<<"0", Frac/binary>>);
  475. _ when IsNeg -> 1 - binary_to_float(<<"0", Frac/binary>>)
  476. end,
  477. Sec1 = H * 3600 + M * 60 + S,
  478. Sec2 = if IsNeg -> -Sec1; true -> Sec1 end,
  479. Sec3 = if IsNeg and (Fraction /= 0) -> Sec2 - 1;
  480. true -> Sec2
  481. end,
  482. {Days, {Hours, Minutes, Seconds}} = calendar:seconds_to_daystime(Sec3),
  483. {Days, {Hours, Minutes, Seconds + Fraction}};
  484. decode_text(#col{type = T},
  485. <<Y:4/binary, "-", M:2/binary, "-", D:2/binary, " ",
  486. H:2/binary, ":", Mi:2/binary, ":", S:2/binary>>)
  487. when T == ?TYPE_TIMESTAMP; T == ?TYPE_DATETIME ->
  488. %% Without fractions.
  489. {{binary_to_integer(Y), binary_to_integer(M), binary_to_integer(D)},
  490. {binary_to_integer(H), binary_to_integer(Mi), binary_to_integer(S)}};
  491. decode_text(#col{type = T},
  492. <<Y:4/binary, "-", M:2/binary, "-", D:2/binary, " ",
  493. H:2/binary, ":", Mi:2/binary, ":", FloatS/binary>>)
  494. when T == ?TYPE_TIMESTAMP; T == ?TYPE_DATETIME ->
  495. %% With fractions.
  496. {{binary_to_integer(Y), binary_to_integer(M), binary_to_integer(D)},
  497. {binary_to_integer(H), binary_to_integer(Mi), binary_to_float(FloatS)}};
  498. decode_text(#col{type = T}, Text) when T == ?TYPE_FLOAT;
  499. T == ?TYPE_DOUBLE ->
  500. try binary_to_float(Text)
  501. catch error:badarg ->
  502. try binary_to_integer(Text) of
  503. Int -> float(Int)
  504. catch error:badarg ->
  505. %% It is something like "4e75" that must be turned into "4.0e75"
  506. binary_to_float(binary:replace(Text, <<"e">>, <<".0e">>))
  507. end
  508. end.
  509. %% -- binary protocol --
  510. %% @doc If NumColumns is non-zero, fetches this number of column definitions
  511. %% and an EOF packet. Used by prepare/3.
  512. fetch_column_definitions_if_any(0, _TcpModule, _Socket, SeqNum) ->
  513. {[], SeqNum};
  514. fetch_column_definitions_if_any(N, TcpModule, Socket, SeqNum) ->
  515. {ok, Defs, SeqNum1} = fetch_column_definitions(TcpModule, Socket, SeqNum,
  516. N, []),
  517. {ok, ?eof_pattern, SeqNum2} = recv_packet(TcpModule, Socket, SeqNum1),
  518. {Defs, SeqNum2}.
  519. %% @doc Decodes a packet representing a row in a binary result set.
  520. %% It consists of a 0 byte, then a null bitmap, then the values.
  521. %% Returns a list of length NumColumns with terms of appropriate types for each
  522. %% MySQL type in ColumnTypes.
  523. -spec decode_binary_row(NumColumns :: integer(),
  524. ColumnDefs :: [#col{}],
  525. Data :: binary()) -> [term()].
  526. decode_binary_row(NumColumns, ColumnDefs, <<0, Data/binary>>) ->
  527. {NullBitMap, Rest} = null_bitmap_decode(NumColumns, Data, 2),
  528. decode_binary_row_acc(ColumnDefs, NullBitMap, Rest, []).
  529. %% @doc Accumulating helper for decode_binary_row/3.
  530. decode_binary_row_acc([_|ColDefs], <<1:1, NullBitMap/bitstring>>, Data, Acc) ->
  531. %% NULL
  532. decode_binary_row_acc(ColDefs, NullBitMap, Data, [null | Acc]);
  533. decode_binary_row_acc([ColDef | ColDefs], <<0:1, NullBitMap/bitstring>>, Data,
  534. Acc) ->
  535. %% Not NULL
  536. {Term, Rest} = decode_binary(ColDef, Data),
  537. decode_binary_row_acc(ColDefs, NullBitMap, Rest, [Term | Acc]);
  538. decode_binary_row_acc([], _, <<>>, Acc) ->
  539. lists:reverse(Acc).
  540. %% @doc Decodes a null bitmap as stored by MySQL and returns it in a strait
  541. %% bitstring counting bits from left to right in a tuple with remaining data.
  542. %%
  543. %% In the MySQL null bitmap the bits are stored counting bytes from the left and
  544. %% bits within each byte from the right. (Sort of little endian.)
  545. -spec null_bitmap_decode(NumColumns :: integer(), Data :: binary(),
  546. BitOffset :: integer()) ->
  547. {NullBitstring :: bitstring(), Rest :: binary()}.
  548. null_bitmap_decode(NumColumns, Data, BitOffset) ->
  549. %% Binary shift right by 3 is equivallent to integer division by 8.
  550. BitMapLength = (NumColumns + BitOffset + 7) bsr 3,
  551. <<NullBitstring0:BitMapLength/binary, Rest/binary>> = Data,
  552. <<_:BitOffset, NullBitstring:NumColumns/bitstring, _/bitstring>> =
  553. << <<(reverse_byte(B))/binary>> || <<B:1/binary>> <= NullBitstring0 >>,
  554. {NullBitstring, Rest}.
  555. %% @doc The reverse of null_bitmap_decode/3. The number of columns is taken to
  556. %% be the number of bits in NullBitstring. Returns the MySQL null bitmap as a
  557. %% binary (i.e. full bytes). BitOffset is the number of unused bits that should
  558. %% be inserted before the other bits.
  559. -spec null_bitmap_encode(bitstring(), integer()) -> binary().
  560. null_bitmap_encode(NullBitstring, BitOffset) ->
  561. PayloadLength = bit_size(NullBitstring) + BitOffset,
  562. %% Round up to a multiple of 8.
  563. BitMapLength = (PayloadLength + 7) band bnot 7,
  564. PadBitsLength = BitMapLength - PayloadLength,
  565. PaddedBitstring = <<0:BitOffset, NullBitstring/bitstring, 0:PadBitsLength>>,
  566. << <<(reverse_byte(B))/binary>> || <<B:1/binary>> <= PaddedBitstring >>.
  567. %% Reverses the bits in a byte.
  568. reverse_byte(<<A:1, B:1, C:1, D:1, E:1, F:1, G:1, H:1>>) ->
  569. <<H:1, G:1, F:1, E:1, D:1, C:1, B:1, A:1>>.
  570. %% @doc Used for executing prepared statements. The bit offset whould be 0 in
  571. %% this case.
  572. -spec build_null_bitmap([any()]) -> binary().
  573. build_null_bitmap(Values) ->
  574. Bits = << <<(case V of null -> 1; _ -> 0 end):1>> || V <- Values >>,
  575. null_bitmap_encode(Bits, 0).
  576. %% Decodes a value as received in the 'binary protocol' result set.
  577. %%
  578. %% The types are type constants for the binary protocol, such as
  579. %% ProtocolBinary::MYSQL_TYPE_STRING. In the guide "MySQL Internals" these are
  580. %% not listed, but we assume that are the same as for the text protocol.
  581. -spec decode_binary(ColDef :: #col{}, Data :: binary()) ->
  582. {Term :: term(), Rest :: binary()}.
  583. decode_binary(#col{type = T}, Data)
  584. when T == ?TYPE_STRING; T == ?TYPE_VARCHAR; T == ?TYPE_VAR_STRING;
  585. T == ?TYPE_ENUM; T == ?TYPE_SET; T == ?TYPE_LONG_BLOB;
  586. T == ?TYPE_MEDIUM_BLOB; T == ?TYPE_BLOB; T == ?TYPE_TINY_BLOB;
  587. T == ?TYPE_GEOMETRY; T == ?TYPE_JSON ->
  588. %% As of MySQL 5.6.21 we receive SET and ENUM values as STRING, i.e. we
  589. %% cannot convert them to atom() or sets:set(), etc.
  590. lenenc_str(Data);
  591. decode_binary(#col{type = ?TYPE_LONGLONG, flags = F},
  592. <<Value:64/signed-little, Rest/binary>>)
  593. when F band ?UNSIGNED_FLAG == 0 ->
  594. {Value, Rest};
  595. decode_binary(#col{type = ?TYPE_LONGLONG, flags = F},
  596. <<Value:64/unsigned-little, Rest/binary>>)
  597. when F band ?UNSIGNED_FLAG /= 0 ->
  598. {Value, Rest};
  599. decode_binary(#col{type = T, flags = F},
  600. <<Value:32/signed-little, Rest/binary>>)
  601. when (T == ?TYPE_LONG orelse T == ?TYPE_INT24) andalso
  602. F band ?UNSIGNED_FLAG == 0 ->
  603. {Value, Rest};
  604. decode_binary(#col{type = T, flags = F},
  605. <<Value:32/unsigned-little, Rest/binary>>)
  606. when (T == ?TYPE_LONG orelse T == ?TYPE_INT24) andalso
  607. F band ?UNSIGNED_FLAG /= 0 ->
  608. {Value, Rest};
  609. decode_binary(#col{type = ?TYPE_SHORT, flags = F},
  610. <<Value:16/signed-little, Rest/binary>>)
  611. when F band ?UNSIGNED_FLAG == 0 ->
  612. {Value, Rest};
  613. decode_binary(#col{type = T, flags = F},
  614. <<Value:16/unsigned-little, Rest/binary>>)
  615. when (T == ?TYPE_SHORT orelse T == ?TYPE_YEAR) andalso
  616. F band ?UNSIGNED_FLAG /= 0 ->
  617. {Value, Rest};
  618. decode_binary(#col{type = ?TYPE_TINY, flags = F},
  619. <<Value:8/unsigned, Rest/binary>>)
  620. when F band ?UNSIGNED_FLAG /= 0 ->
  621. {Value, Rest};
  622. decode_binary(#col{type = ?TYPE_TINY, flags = F},
  623. <<Value:8/signed, Rest/binary>>)
  624. when F band ?UNSIGNED_FLAG == 0 ->
  625. {Value, Rest};
  626. decode_binary(#col{type = T, decimals = S, length = L}, Data)
  627. when T == ?TYPE_DECIMAL; T == ?TYPE_NEWDECIMAL ->
  628. %% Length is the max number of symbols incl. dot and minus sign, e.g. the
  629. %% number of digits plus 2.
  630. {Binary, Rest} = lenenc_str(Data),
  631. {decode_decimal(Binary, L - 2, S), Rest};
  632. decode_binary(#col{type = ?TYPE_DOUBLE},
  633. <<Value:64/float-little, Rest/binary>>) ->
  634. {Value, Rest};
  635. decode_binary(#col{type = ?TYPE_FLOAT}, <<0.0:32/float-little, Rest/binary>>) ->
  636. %% TYPE_FLOAT conversation fails on math:log10(0.0)
  637. {0.0, Rest};
  638. decode_binary(#col{type = ?TYPE_FLOAT},
  639. <<Value:32/float-little, Rest/binary>>) ->
  640. %% There is a precision loss when storing and fetching a 32-bit float.
  641. %% In the text protocol, it is obviously rounded. Storing 3.14 in a FLOAT
  642. %% column and fetching it using the text protocol, we get "3.14" which we
  643. %% parse to the Erlang double as close as possible to 3.14. Fetching the
  644. %% same value as a binary 32-bit float, we get 3.140000104904175. To achieve
  645. %% the same rounding after receiving it as a 32-bit float, we try to do the
  646. %% same rounding here as MySQL does when sending it over the text protocol.
  647. %%
  648. %% This comment explains the idea:
  649. %%
  650. %% Posted by Geoffrey Downs on March 10 2011 10:26am
  651. %%
  652. %% Following up... I *think* this is correct for the default float
  653. %% columns in mysql:
  654. %%
  655. %% var yourNumber = some floating point value
  656. %% max decimal precision = 10 ^ (-5 + flooring(yourNumber log 10))
  657. %% So:
  658. %% 0 < x < 10 -> max precision is 0.00001
  659. %% 10 <= x < 100 -> max precision is 0.0001
  660. %% 100 <= x < 1000 -> max precision is 0.001
  661. %% etc.
  662. %%
  663. %% (From http://dev.mysql.com/doc/refman/5.7/en/problems-with-float.html
  664. %% fetched 10 Nov 2014)
  665. %%
  666. %% The above is almost correct, except for the example in the interval
  667. %% 0 < x < 1. There are 6 significant digits also for these numbers.
  668. %%
  669. %% Now, instead of P = 0.00001 we want the inverse 100000.0 but if we
  670. %% compute Factor = 1 / P we get a precision loss, so instead we do this:
  671. Factor = math:pow(10, flooring(6 - math:log10(abs(Value)))),
  672. RoundedValue = round(Value * Factor) / Factor,
  673. {RoundedValue, Rest};
  674. decode_binary(#col{type = ?TYPE_BIT, length = Length}, Data) ->
  675. {Binary, Rest} = lenenc_str(Data),
  676. %% Convert to <<_:Length/bitstring>>
  677. {decode_bitstring(Binary, Length), Rest};
  678. decode_binary(#col{type = ?TYPE_DATE}, <<Length, Data/binary>>) ->
  679. %% Coded in the same way as DATETIME and TIMESTAMP below, but returned in
  680. %% a simple triple.
  681. case {Length, Data} of
  682. {0, _} -> {{0, 0, 0}, Data};
  683. {4, <<Y:16/little, M, D, Rest/binary>>} -> {{Y, M, D}, Rest}
  684. end;
  685. decode_binary(#col{type = T}, <<Length, Data/binary>>)
  686. when T == ?TYPE_DATETIME; T == ?TYPE_TIMESTAMP ->
  687. %% length (1) -- number of bytes following (valid values: 0, 4, 7, 11)
  688. case {Length, Data} of
  689. {0, _} ->
  690. {{{0, 0, 0}, {0, 0, 0}}, Data};
  691. {4, <<Y:16/little, M, D, Rest/binary>>} ->
  692. {{{Y, M, D}, {0, 0, 0}}, Rest};
  693. {7, <<Y:16/little, M, D, H, Mi, S, Rest/binary>>} ->
  694. {{{Y, M, D}, {H, Mi, S}}, Rest};
  695. {11, <<Y:16/little, M, D, H, Mi, S, Micro:32/little, Rest/binary>>} ->
  696. {{{Y, M, D}, {H, Mi, S + 0.000001 * Micro}}, Rest}
  697. end;
  698. decode_binary(#col{type = ?TYPE_TIME}, <<Length, Data/binary>>) ->
  699. %% length (1) -- number of bytes following (valid values: 0, 8, 12)
  700. %% is_negative (1) -- (1 if minus, 0 for plus)
  701. %% days (4) -- days
  702. %% hours (1) -- hours
  703. %% minutes (1) -- minutes
  704. %% seconds (1) -- seconds
  705. %% micro_seconds (4) -- micro-seconds
  706. case {Length, Data} of
  707. {0, _} ->
  708. {{0, {0, 0, 0}}, Data};
  709. {8, <<0, D:32/little, H, M, S, Rest/binary>>} ->
  710. {{D, {H, M, S}}, Rest};
  711. {12, <<0, D:32/little, H, M, S, Micro:32/little, Rest/binary>>} ->
  712. {{D, {H, M, S + 0.000001 * Micro}}, Rest};
  713. {8, <<1, D:32/little, H, M, S, Rest/binary>>} ->
  714. %% Negative time. Example: '-00:00:01' --> {-1,{23,59,59}}
  715. Seconds = ((D * 24 + H) * 60 + M) * 60 + S,
  716. %Seconds = D * 86400 + calendar:time_to_seconds({H, M, S}),
  717. {calendar:seconds_to_daystime(-Seconds), Rest};
  718. {12, <<1, D:32/little, H, M, S, Micro:32/little, Rest/binary>>}
  719. when Micro > 0 ->
  720. %% Negate and convert to seconds, excl fractions
  721. Seconds = -(((D * 24 + H) * 60 + M) * 60 + S),
  722. %Seconds = -D * 86400 - calendar:time_to_seconds({H, M, S}),
  723. %% Subtract 1 second for the fractions
  724. {Days, {Hours, Minutes, Sec}} =
  725. calendar:seconds_to_daystime(Seconds - 1),
  726. %% Adding the fractions to Sec again makes it a float
  727. {{Days, {Hours, Minutes, Sec + 1 - 0.000001 * Micro}}, Rest}
  728. end.
  729. %% @doc Like trunc/1 but towards negative infinity instead of towards zero.
  730. flooring(Value) ->
  731. Trunc = trunc(Value),
  732. if
  733. Trunc =< Value -> Trunc;
  734. Trunc > Value -> Trunc - 1 %% for negative values
  735. end.
  736. %% @doc Encodes a term reprenting av value as a binary for use in the binary
  737. %% protocol. As this is used to encode parameters for prepared statements, the
  738. %% encoding is in its required form, namely `<<Type:8, Sign:8, Value/binary>>'.
  739. -spec encode_param(term()) -> {TypeAndSign :: binary(), Data :: binary()}.
  740. encode_param(null) ->
  741. {<<?TYPE_NULL, 0>>, <<>>};
  742. encode_param(Value) when is_binary(Value) ->
  743. EncLength = lenenc_int_encode(byte_size(Value)),
  744. {<<?TYPE_VAR_STRING, 0>>, <<EncLength/binary, Value/binary>>};
  745. encode_param(Value) when is_list(Value) ->
  746. encode_param(unicode:characters_to_binary(Value));
  747. encode_param(Value) when is_integer(Value), Value >= 0 ->
  748. %% We send positive integers with the 'unsigned' flag set.
  749. if
  750. Value =< 16#ff ->
  751. {<<?TYPE_TINY, 16#80>>, <<Value:8>>};
  752. Value =< 16#ffff ->
  753. {<<?TYPE_SHORT, 16#80>>, <<Value:16/little>>};
  754. Value =< 16#ffffffff ->
  755. {<<?TYPE_LONG, 16#80>>, <<Value:32/little>>};
  756. Value =< 16#ffffffffffffffff ->
  757. {<<?TYPE_LONGLONG, 16#80>>, <<Value:64/little>>};
  758. true ->
  759. %% If larger than a 64-bit int we send it as a string. MySQL does
  760. %% silently cast strings in aithmetic expressions. Also, DECIMALs
  761. %% are always sent as strings.
  762. encode_param(integer_to_binary(Value))
  763. end;
  764. encode_param(Value) when is_integer(Value), Value < 0 ->
  765. if
  766. Value >= -16#80 ->
  767. {<<?TYPE_TINY, 0>>, <<Value:8>>};
  768. Value >= -16#8000 ->
  769. {<<?TYPE_SHORT, 0>>, <<Value:16/little>>};
  770. Value >= -16#80000000 ->
  771. {<<?TYPE_LONG, 0>>, <<Value:32/little>>};
  772. Value >= -16#8000000000000000 ->
  773. {<<?TYPE_LONGLONG, 0>>, <<Value:64/little>>};
  774. true ->
  775. encode_param(integer_to_binary(Value))
  776. end;
  777. encode_param(Value) when is_float(Value) ->
  778. {<<?TYPE_DOUBLE, 0>>, <<Value:64/float-little>>};
  779. encode_param(Value) when is_bitstring(Value) ->
  780. Binary = encode_bitstring(Value),
  781. EncLength = lenenc_int_encode(byte_size(Binary)),
  782. {<<?TYPE_VAR_STRING, 0>>, <<EncLength/binary, Binary/binary>>};
  783. encode_param({Y, M, D}) ->
  784. %% calendar:date()
  785. {<<?TYPE_DATE, 0>>, <<4, Y:16/little, M, D>>};
  786. encode_param({{Y, M, D}, {0, 0, 0}}) ->
  787. %% Datetime at midnight
  788. {<<?TYPE_DATETIME, 0>>, <<4, Y:16/little, M, D>>};
  789. encode_param({{Y, M, D}, {H, Mi, S}}) when is_integer(S) ->
  790. %% calendar:datetime()
  791. {<<?TYPE_DATETIME, 0>>, <<7, Y:16/little, M, D, H, Mi, S>>};
  792. encode_param({{Y, M, D}, {H, Mi, S}}) when is_float(S) ->
  793. %% calendar:datetime() with a float for seconds. This way it looks very
  794. %% similar to a datetime. Microseconds in MySQL timestamps are possible but
  795. %% not very common.
  796. Sec = trunc(S),
  797. Micro = round(1000000 * (S - Sec)),
  798. {<<?TYPE_DATETIME, 0>>, <<11, Y:16/little, M, D, H, Mi, Sec,
  799. Micro:32/little>>};
  800. encode_param({D, {H, M, S}}) when is_integer(S), D >= 0 ->
  801. %% calendar:seconds_to_daystime()
  802. {<<?TYPE_TIME, 0>>, <<8, 0, D:32/little, H, M, S>>};
  803. encode_param({D, {H, M, S}}) when is_integer(S), D < 0 ->
  804. %% Convert to seconds, negate and convert back to daystime form.
  805. %% Then set the minus flag.
  806. Seconds = ((D * 24 + H) * 60 + M) * 60 + S,
  807. {D1, {H1, M1, S1}} = calendar:seconds_to_daystime(-Seconds),
  808. {<<?TYPE_TIME, 0>>, <<8, 1, D1:32/little, H1, M1, S1>>};
  809. encode_param({D, {H, M, S}}) when is_float(S), D >= 0 ->
  810. S1 = trunc(S),
  811. Micro = round(1000000 * (S - S1)),
  812. {<<?TYPE_TIME, 0>>, <<12, 0, D:32/little, H, M, S1, Micro:32/little>>};
  813. encode_param({D, {H, M, S}}) when is_float(S), S > 0.0, D < 0 ->
  814. IntS = trunc(S),
  815. Micro = round(1000000 * (1 - S + IntS)),
  816. Seconds = (D * 24 + H) * 3600 + M * 60 + IntS + 1,
  817. {D1, {M1, H1, S1}} = calendar:seconds_to_daystime(-Seconds),
  818. {<<?TYPE_TIME, 0>>, <<12, 1, D1:32/little, H1, M1, S1, Micro:32/little>>};
  819. encode_param({D, {H, M, 0.0}}) ->
  820. encode_param({D, {H, M, 0}}).
  821. %% -- Value representation in both the text and binary protocols --
  822. %% @doc Convert to `<<_:Length/bitstring>>'
  823. decode_bitstring(Binary, Length) ->
  824. PaddingLength = bit_size(Binary) - Length,
  825. <<_:PaddingLength/bitstring, Bitstring:Length/bitstring>> = Binary,
  826. Bitstring.
  827. encode_bitstring(Bitstring) ->
  828. Size = bit_size(Bitstring),
  829. PaddingSize = byte_size(Bitstring) * 8 - Size,
  830. <<0:PaddingSize, Bitstring:Size/bitstring>>.
  831. decode_decimal(Bin, _P, 0) ->
  832. binary_to_integer(Bin);
  833. decode_decimal(Bin, P, S) when P =< 15, S > 0 ->
  834. binary_to_float(Bin);
  835. decode_decimal(Bin, P, S) when P >= 16, S > 0 ->
  836. Bin.
  837. %% -- Protocol basics: packets --
  838. %% @doc Wraps Data in packet headers, sends it by calling TcpModule:send/2 with
  839. %% Socket and returns {ok, SeqNum1} where SeqNum1 is the next sequence number.
  840. -spec send_packet(atom(), term(), Data :: binary(), SeqNum :: integer()) ->
  841. {ok, NextSeqNum :: integer()}.
  842. send_packet(TcpModule, Socket, Data, SeqNum) ->
  843. {WithHeaders, SeqNum1} = add_packet_headers(Data, SeqNum),
  844. ok = TcpModule:send(Socket, WithHeaders),
  845. {ok, SeqNum1}.
  846. %% @see recv_packet/4
  847. recv_packet(TcpModule, Socket, SeqNum) ->
  848. recv_packet(TcpModule, Socket, infinity, SeqNum).
  849. %% @doc Receives data by calling TcpModule:recv/2 and removes the packet
  850. %% headers. Returns the packet contents and the next packet sequence number.
  851. -spec recv_packet(atom(), term(), timeout(), integer() | any) ->
  852. {ok, Data :: binary(), NextSeqNum :: integer()} | {error, term()}.
  853. recv_packet(TcpModule, Socket, Timeout, SeqNum) ->
  854. recv_packet(TcpModule, Socket, Timeout, SeqNum, <<>>).
  855. %% @doc Accumulating helper for recv_packet/4
  856. -spec recv_packet(atom(), term(), timeout(), integer() | any, binary()) ->
  857. {ok, Data :: binary(), NextSeqNum :: integer()} | {error, term()}.
  858. recv_packet(TcpModule, Socket, Timeout, ExpectSeqNum, Acc) ->
  859. case TcpModule:recv(Socket, 4, Timeout) of
  860. {ok, Header} ->
  861. {Size, SeqNum, More} = parse_packet_header(Header),
  862. true = SeqNum == ExpectSeqNum orelse ExpectSeqNum == any,
  863. {ok, Body} = TcpModule:recv(Socket, Size),
  864. Acc1 = <<Acc/binary, Body/binary>>,
  865. NextSeqNum = (SeqNum + 1) band 16#ff,
  866. case More of
  867. false -> {ok, Acc1, NextSeqNum};
  868. true -> recv_packet(TcpModule, Socket, Timeout, NextSeqNum,
  869. Acc1)
  870. end;
  871. {error, Reason} ->
  872. {error, Reason}
  873. end.
  874. %% @doc Parses a packet header (32 bits) and returns a tuple.
  875. %%
  876. %% The client should first read a header and parse it. Then read PacketLength
  877. %% bytes. If there are more packets, read another header and read a new packet
  878. %% length of payload until there are no more packets. The seq num should
  879. %% increment from 0 and may wrap around at 255 back to 0.
  880. %%
  881. %% When all packets are read and the payload of all packets are concatenated, it
  882. %% can be parsed using parse_response/1, etc. depending on what type of response
  883. %% is expected.
  884. -spec parse_packet_header(PackerHeader :: binary()) ->
  885. {PacketLength :: integer(),
  886. SeqNum :: integer(),
  887. MorePacketsExist :: boolean()}.
  888. parse_packet_header(<<PacketLength:24/little-integer, SeqNum:8/integer>>) ->
  889. {PacketLength, SeqNum, PacketLength == 16#ffffff}.
  890. %% @doc Splits a packet body into chunks and wraps them in headers. The
  891. %% resulting list is ready to sent to the socket.
  892. -spec add_packet_headers(PacketBody :: iodata(), SeqNum :: integer()) ->
  893. {PacketWithHeaders :: iodata(), NextSeqNum :: integer()}.
  894. add_packet_headers(PacketBody, SeqNum) ->
  895. Bin = iolist_to_binary(PacketBody),
  896. Size = size(Bin),
  897. SeqNum1 = (SeqNum + 1) band 16#ff,
  898. %% Todo: implement the case when Size >= 16#ffffff.
  899. if Size < 16#ffffff ->
  900. {[<<Size:24/little, SeqNum:8>>, Bin], SeqNum1}
  901. end.
  902. -spec parse_ok_packet(binary()) -> #ok{}.
  903. parse_ok_packet(<<?OK:8, Rest/binary>>) ->
  904. {AffectedRows, Rest1} = lenenc_int(Rest),
  905. {InsertId, Rest2} = lenenc_int(Rest1),
  906. <<StatusFlags:16/little, WarningCount:16/little, Msg/binary>> = Rest2,
  907. %% We have CLIENT_PROTOCOL_41 but not CLIENT_SESSION_TRACK enabled. The
  908. %% protocol is conditional. This is from the protocol documentation:
  909. %%
  910. %% if capabilities & CLIENT_PROTOCOL_41 {
  911. %% int<2> status_flags
  912. %% int<2> warning_count
  913. %% } elseif capabilities & CLIENT_TRANSACTIONS {
  914. %% int<2> status_flags
  915. %% }
  916. %% if capabilities & CLIENT_SESSION_TRACK {
  917. %% string<lenenc> info
  918. %% if status_flags & SERVER_SESSION_STATE_CHANGED {
  919. %% string<lenenc> session_state_changes
  920. %% }
  921. %% } else {
  922. %% string<EOF> info
  923. %% }
  924. #ok{affected_rows = AffectedRows,
  925. insert_id = InsertId,
  926. status = StatusFlags,
  927. warning_count = WarningCount,
  928. msg = Msg}.
  929. -spec parse_error_packet(binary()) -> #error{}.
  930. parse_error_packet(<<?ERROR:8, ErrNo:16/little, "#", SQLState:5/binary-unit:8,
  931. Msg/binary>>) ->
  932. %% Error, 4.1 protocol.
  933. %% (Older protocol: <<?ERROR:8, ErrNo:16/little, Msg/binary>>)
  934. #error{code = ErrNo, state = SQLState, msg = Msg}.
  935. -spec parse_eof_packet(binary()) -> #eof{}.
  936. parse_eof_packet(<<?EOF:8, NumWarnings:16/little, StatusFlags:16/little>>) ->
  937. %% EOF packet, 4.1 protocol.
  938. %% (Older protocol: <<?EOF:8>>)
  939. #eof{status = StatusFlags, warning_count = NumWarnings}.
  940. -spec parse_auth_method_switch(binary()) -> #auth_method_switch{}.
  941. parse_auth_method_switch(AMSData) ->
  942. {AuthPluginName, AuthPluginData} = get_null_terminated_binary(AMSData),
  943. #auth_method_switch{
  944. auth_plugin_name = AuthPluginName,
  945. auth_plugin_data = AuthPluginData
  946. }.
  947. -spec get_null_terminated_binary(binary()) -> {Binary :: binary(), Rest :: binary()}.
  948. get_null_terminated_binary(In) ->
  949. get_null_terminated_binary(In, <<>>).
  950. get_null_terminated_binary(<<0, Rest/binary>>, Acc) ->
  951. {Acc, Rest};
  952. get_null_terminated_binary(<<Ch, Rest/binary>>, Acc) ->
  953. get_null_terminated_binary(Rest, <<Acc/binary, Ch>>).
  954. -spec hash_password(Password :: iodata(), Salt :: binary()) -> Hash :: binary().
  955. hash_password(Password, Salt) ->
  956. %% From the "MySQL Internals" manual:
  957. %% SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat>
  958. %% SHA1( SHA1( password ) ) )
  959. %% ----
  960. %% Make sure the salt is exactly 20 bytes.
  961. %%
  962. %% The auth data is obviously nul-terminated. For the "native" auth
  963. %% method, it should be a 20 byte salt, so let's trim it in this case.
  964. PasswordBin = case erlang:is_binary(Password) of
  965. true -> Password;
  966. false -> erlang:iolist_to_binary(Password)
  967. end,
  968. case PasswordBin =:= <<>> of
  969. true -> <<>>;
  970. false -> hash_non_empty_password(Password, Salt)
  971. end.
  972. -spec hash_non_empty_password(Password :: iodata(), Salt :: binary()) -> Hash :: binary().
  973. hash_non_empty_password(Password, Salt) ->
  974. Salt1 = case Salt of
  975. <<SaltNoNul:20/binary-unit:8, 0>> -> SaltNoNul;
  976. _ when size(Salt) == 20 -> Salt
  977. end,
  978. %% Hash as described above.
  979. <<Hash1Num:160>> = Hash1 = crypto:hash(sha, Password),
  980. Hash2 = crypto:hash(sha, Hash1),
  981. <<Hash3Num:160>> = crypto:hash(sha, <<Salt1/binary, Hash2/binary>>),
  982. <<(Hash1Num bxor Hash3Num):160>>.
  983. %% --- Lowlevel: variable length integers and strings ---
  984. %% lenenc_int/1 decodes length-encoded-integer values
  985. -spec lenenc_int(Input :: binary()) -> {Value :: integer(), Rest :: binary()}.
  986. lenenc_int(<<Value:8, Rest/bits>>) when Value < 251 -> {Value, Rest};
  987. lenenc_int(<<16#fc:8, Value:16/little, Rest/binary>>) -> {Value, Rest};
  988. lenenc_int(<<16#fd:8, Value:24/little, Rest/binary>>) -> {Value, Rest};
  989. lenenc_int(<<16#fe:8, Value:64/little, Rest/binary>>) -> {Value, Rest}.
  990. %% Length-encoded-integer encode. Appends the encoded value to Acc.
  991. %% Values not representable in 64 bits are not accepted.
  992. -spec lenenc_int_encode(0..16#ffffffffffffffff) -> binary().
  993. lenenc_int_encode(Value) when Value >= 0 ->
  994. if Value < 251 -> <<Value>>;
  995. Value =< 16#ffff -> <<16#fc, Value:16/little>>;
  996. Value =< 16#ffffff -> <<16#fd, Value:24/little>>;
  997. Value =< 16#ffffffffffffffff -> <<16#fe, Value:64/little>>
  998. end.
  999. %% lenenc_str/1 decodes length-encoded-string values
  1000. -spec lenenc_str(Input :: binary()) -> {String :: binary(), Rest :: binary()}.
  1001. lenenc_str(Bin) ->
  1002. {Length, Rest} = lenenc_int(Bin),
  1003. <<String:Length/binary, Rest1/binary>> = Rest,
  1004. {String, Rest1}.
  1005. %% nts/1 decodes a nul-terminated string
  1006. -spec nulterm_str(Input :: binary()) -> {String :: binary(), Rest :: binary()}.
  1007. nulterm_str(Bin) ->
  1008. [String, Rest] = binary:split(Bin, <<0>>),
  1009. {String, Rest}.
  1010. -ifdef(TEST).
  1011. -include_lib("eunit/include/eunit.hrl").
  1012. %% Testing some of the internal functions, mostly the cases we don't cover in
  1013. %% other tests.
  1014. decode_text_test() ->
  1015. %% Int types
  1016. lists:foreach(fun (T) ->
  1017. ?assertEqual(1, decode_text(#col{type = T}, <<"1">>))
  1018. end,
  1019. [?TYPE_TINY, ?TYPE_SHORT, ?TYPE_LONG, ?TYPE_LONGLONG,
  1020. ?TYPE_INT24, ?TYPE_YEAR]),
  1021. %% BIT
  1022. <<217>> = decode_text(#col{type = ?TYPE_BIT, length = 8}, <<217>>),
  1023. %% Floating point and decimal numbers
  1024. lists:foreach(fun (T) ->
  1025. ?assertEqual(3.0, decode_text(#col{type = T}, <<"3.0">>))
  1026. end,
  1027. [?TYPE_FLOAT, ?TYPE_DOUBLE]),
  1028. %% Decimal types
  1029. lists:foreach(fun (T) ->
  1030. ColDef = #col{type = T, decimals = 1, length = 4},
  1031. ?assertMatch(3.0, decode_text(ColDef, <<"3.0">>))
  1032. end,
  1033. [?TYPE_DECIMAL, ?TYPE_NEWDECIMAL]),
  1034. ?assertEqual(3.0, decode_text(#col{type = ?TYPE_FLOAT}, <<"3">>)),
  1035. ?assertEqual(30.0, decode_text(#col{type = ?TYPE_FLOAT}, <<"3e1">>)),
  1036. ?assertEqual(3, decode_text(#col{type = ?TYPE_LONG}, <<"3">>)),
  1037. %% Date and time
  1038. ?assertEqual({2014, 11, 01},
  1039. decode_text(#col{type = ?TYPE_DATE}, <<"2014-11-01">>)),
  1040. ?assertEqual({0, {23, 59, 01}},
  1041. decode_text(#col{type = ?TYPE_TIME}, <<"23:59:01">>)),
  1042. ?assertEqual({{2014, 11, 01}, {23, 59, 01}},
  1043. decode_text(#col{type = ?TYPE_DATETIME},
  1044. <<"2014-11-01 23:59:01">>)),
  1045. ?assertEqual({{2014, 11, 01}, {23, 59, 01}},
  1046. decode_text(#col{type = ?TYPE_TIMESTAMP},
  1047. <<"2014-11-01 23:59:01">>)),
  1048. %% Strings and blobs
  1049. lists:foreach(fun (T) ->
  1050. ColDef = #col{type = T},
  1051. ?assertEqual(<<"x">>, decode_text(ColDef, <<"x">>))
  1052. end,
  1053. [?TYPE_VARCHAR, ?TYPE_ENUM, ?TYPE_TINY_BLOB,
  1054. ?TYPE_MEDIUM_BLOB, ?TYPE_LONG_BLOB, ?TYPE_BLOB,
  1055. ?TYPE_VAR_STRING, ?TYPE_STRING, ?TYPE_GEOMETRY]),
  1056. ok.
  1057. decode_binary_test() ->
  1058. %% Test the special rounding we apply to (single precision) floats.
  1059. ?assertEqual({1.0, <<>>},
  1060. decode_binary(#col{type = ?TYPE_FLOAT},
  1061. <<1.0:32/float-little>>)),
  1062. ?assertEqual({0.2, <<>>},
  1063. decode_binary(#col{type = ?TYPE_FLOAT},
  1064. <<0.2:32/float-little>>)),
  1065. ?assertEqual({-33.3333, <<>>},
  1066. decode_binary(#col{type = ?TYPE_FLOAT},
  1067. <<-33.333333:32/float-little>>)),
  1068. ?assertEqual({0.000123457, <<>>},
  1069. decode_binary(#col{type = ?TYPE_FLOAT},
  1070. <<0.00012345678:32/float-little>>)),
  1071. ?assertEqual({1234.57, <<>>},
  1072. decode_binary(#col{type = ?TYPE_FLOAT},
  1073. <<1234.56789:32/float-little>>)),
  1074. ok.
  1075. null_bitmap_test() ->
  1076. ?assertEqual({<<0, 1:1>>, <<>>}, null_bitmap_decode(9, <<0, 4>>, 2)),
  1077. ?assertEqual(<<0, 4>>, null_bitmap_encode(<<0, 1:1>>, 2)),
  1078. ok.
  1079. lenenc_int_test() ->
  1080. %% decode
  1081. ?assertEqual({40, <<>>}, lenenc_int(<<40>>)),
  1082. ?assertEqual({16#ff, <<>>}, lenenc_int(<<16#fc, 255, 0>>)),
  1083. ?assertEqual({16#33aaff, <<>>}, lenenc_int(<<16#fd, 16#ff, 16#aa, 16#33>>)),
  1084. ?assertEqual({16#12345678, <<>>}, lenenc_int(<<16#fe, 16#78, 16#56, 16#34,
  1085. 16#12, 0, 0, 0, 0>>)),
  1086. %% encode
  1087. ?assertEqual(<<40>>, lenenc_int_encode(40)),
  1088. ?assertEqual(<<16#fc, 255, 0>>, lenenc_int_encode(255)),
  1089. ?assertEqual(<<16#fd, 16#ff, 16#aa, 16#33>>,
  1090. lenenc_int_encode(16#33aaff)),
  1091. ?assertEqual(<<16#fe, 16#78, 16#56, 16#34, 16#12, 0, 0, 0, 0>>,
  1092. lenenc_int_encode(16#12345678)),
  1093. ok.
  1094. lenenc_str_test() ->
  1095. ?assertEqual({<<"Foo">>, <<"bar">>}, lenenc_str(<<3, "Foobar">>)).
  1096. nulterm_test() ->
  1097. ?assertEqual({<<"Foo">>, <<"bar">>}, nulterm_str(<<"Foo", 0, "bar">>)).
  1098. parse_header_test() ->
  1099. %% Example from "MySQL Internals", revision 307, section 14.1.3.3 EOF_Packet
  1100. Packet = <<16#05, 16#00, 16#00, 16#05, 16#fe, 16#00, 16#00, 16#02, 16#00>>,
  1101. <<Header:4/binary-unit:8, Body/binary>> = Packet,
  1102. %% Check header contents and body length
  1103. ?assertEqual({size(Body), 5, false}, parse_packet_header(Header)),
  1104. ok.
  1105. add_packet_headers_test() ->
  1106. {Data, 43} = add_packet_headers(<<"foo">>, 42),
  1107. ?assertEqual(<<3, 0, 0, 42, "foo">>, list_to_binary(Data)).
  1108. parse_ok_test() ->
  1109. Body = <<0, 5, 1, 2, 0, 0, 0, "Foo">>,
  1110. ?assertEqual(#ok{affected_rows = 5,
  1111. insert_id = 1,
  1112. status = ?SERVER_STATUS_AUTOCOMMIT,
  1113. warning_count = 0,
  1114. msg = <<"Foo">>},
  1115. parse_ok_packet(Body)).
  1116. parse_error_test() ->
  1117. %% Protocol 4.1
  1118. Body = <<255, 42, 0, "#", "XYZxx", "Foo">>,
  1119. ?assertEqual(#error{code = 42, state = <<"XYZxx">>, msg = <<"Foo">>},
  1120. parse_error_packet(Body)),
  1121. ok.
  1122. parse_eof_test() ->
  1123. %% Example from "MySQL Internals", revision 307, section 14.1.3.3 EOF_Packet
  1124. Packet = <<16#05, 16#00, 16#00, 16#05, 16#fe, 16#00, 16#00, 16#02, 16#00>>,
  1125. <<_Header:4/binary-unit:8, Body/binary>> = Packet,
  1126. %% Ignore header. Parse body as an eof_packet.
  1127. ?assertEqual(#eof{warning_count = 0,
  1128. status = ?SERVER_STATUS_AUTOCOMMIT},
  1129. parse_eof_packet(Body)),
  1130. ok.
  1131. hash_password_test() ->
  1132. ?assertEqual(<<222,207,222,139,41,181,202,13,191,241,
  1133. 234,234,73,127,244,101,205,3,28,251>>,
  1134. hash_password(<<"foo">>, <<"abcdefghijklmnopqrst">>)),
  1135. ?assertEqual(<<>>, hash_password(<<>>, <<"abcdefghijklmnopqrst">>)).
  1136. -endif.