mysql_protocol.erl 58 KB

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