mysql_protocol.erl 66 KB

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