mysql_protocol.erl 59 KB

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