serialization.hpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. //
  2. // Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP
  8. #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP
  9. #include <boost/mysql/error_code.hpp>
  10. #include <boost/mysql/field_view.hpp>
  11. #include <boost/mysql/string_view.hpp>
  12. #include <boost/mysql/impl/internal/protocol/capabilities.hpp>
  13. #include <boost/mysql/impl/internal/protocol/frame_header.hpp>
  14. #include <boost/mysql/impl/internal/protocol/impl/binary_protocol.hpp>
  15. #include <boost/mysql/impl/internal/protocol/impl/null_bitmap.hpp>
  16. #include <boost/mysql/impl/internal/protocol/impl/protocol_field_type.hpp>
  17. #include <boost/mysql/impl/internal/protocol/impl/protocol_types.hpp>
  18. #include <boost/mysql/impl/internal/protocol/impl/serialization_context.hpp>
  19. #include <boost/assert.hpp>
  20. #include <cstddef>
  21. #include <cstdint>
  22. namespace boost {
  23. namespace mysql {
  24. namespace detail {
  25. // quit
  26. struct quit_command
  27. {
  28. void serialize(serialization_context& ctx) const { ctx.add(0x01); }
  29. };
  30. // ping
  31. struct ping_command
  32. {
  33. void serialize(serialization_context& ctx) const { ctx.add(0x0e); }
  34. };
  35. // reset_connection
  36. struct reset_connection_command
  37. {
  38. void serialize(serialization_context& ctx) const { ctx.add(0x1f); }
  39. };
  40. // query
  41. struct query_command
  42. {
  43. string_view query;
  44. void serialize(serialization_context& ctx) const
  45. {
  46. ctx.add(0x03);
  47. string_eof{query}.serialize(ctx);
  48. }
  49. };
  50. // prepare_statement
  51. struct prepare_stmt_command
  52. {
  53. string_view stmt;
  54. void serialize(serialization_context& ctx) const
  55. {
  56. ctx.add(0x16);
  57. string_eof{stmt}.serialize(ctx);
  58. }
  59. };
  60. // execute statement
  61. struct execute_stmt_command
  62. {
  63. std::uint32_t statement_id;
  64. span<const field_view> params;
  65. inline void serialize(serialization_context& ctx) const;
  66. };
  67. // close statement
  68. struct close_stmt_command
  69. {
  70. std::uint32_t statement_id;
  71. void serialize(serialization_context& ctx) const { ctx.serialize_fixed(int1{0x19}, int4{statement_id}); }
  72. };
  73. // Login request
  74. struct login_request
  75. {
  76. capabilities negotiated_capabilities; // capabilities
  77. std::uint32_t max_packet_size;
  78. std::uint32_t collation_id;
  79. string_view username;
  80. span<const std::uint8_t> auth_response;
  81. string_view database;
  82. string_view auth_plugin_name;
  83. inline void serialize(serialization_context& ctx) const;
  84. };
  85. // SSL request
  86. struct ssl_request
  87. {
  88. capabilities negotiated_capabilities;
  89. std::uint32_t max_packet_size;
  90. std::uint32_t collation_id;
  91. inline void serialize(serialization_context& ctx) const;
  92. };
  93. // Auth switch response
  94. struct auth_switch_response
  95. {
  96. span<const std::uint8_t> auth_plugin_data;
  97. void serialize(serialization_context& ctx) const { ctx.add(auth_plugin_data); }
  98. };
  99. // The result of serialize_top_level (similar to system::result,
  100. // doesn't track source locations)
  101. struct serialize_top_level_result
  102. {
  103. error_code err;
  104. std::uint8_t seqnum{};
  105. constexpr serialize_top_level_result(error_code ec) noexcept : err(ec) {}
  106. constexpr serialize_top_level_result(std::uint8_t seqnum) noexcept : seqnum(seqnum) {}
  107. };
  108. // Serialize a complete message. May fail
  109. template <class Serializable>
  110. inline serialize_top_level_result serialize_top_level(
  111. const Serializable& input,
  112. std::vector<std::uint8_t>& to,
  113. std::uint8_t seqnum = 0,
  114. std::size_t max_buffer_size = static_cast<std::size_t>(-1),
  115. std::size_t max_frame_size = max_packet_size
  116. )
  117. {
  118. std::size_t initial_offset = to.size();
  119. serialization_context ctx(to, max_buffer_size, max_frame_size);
  120. input.serialize(ctx);
  121. auto err = ctx.error();
  122. if (err)
  123. return err;
  124. return ctx.write_frame_headers(seqnum, initial_offset);
  125. }
  126. // Same, but for cases that can't fail. Does not enforce any limit on buffer size
  127. template <class Serializable>
  128. inline std::uint8_t serialize_top_level_checked(
  129. const Serializable& input,
  130. std::vector<std::uint8_t>& to,
  131. std::uint8_t seqnum = 0,
  132. std::size_t max_frame_size = max_packet_size
  133. )
  134. {
  135. auto res = serialize_top_level(input, to, seqnum, static_cast<std::size_t>(-1), max_frame_size);
  136. BOOST_ASSERT(res.err == error_code());
  137. return res.seqnum;
  138. }
  139. } // namespace detail
  140. } // namespace mysql
  141. } // namespace boost
  142. //
  143. // Implementations
  144. //
  145. namespace boost {
  146. namespace mysql {
  147. namespace detail {
  148. // Maps from an actual value to a protocol_field_type (for execute statement)
  149. inline protocol_field_type to_protocol_field_type(field_kind kind)
  150. {
  151. switch (kind)
  152. {
  153. case field_kind::null: return protocol_field_type::null;
  154. case field_kind::int64: return protocol_field_type::longlong;
  155. case field_kind::uint64: return protocol_field_type::longlong;
  156. case field_kind::string: return protocol_field_type::string;
  157. case field_kind::blob: return protocol_field_type::blob;
  158. case field_kind::float_: return protocol_field_type::float_;
  159. case field_kind::double_: return protocol_field_type::double_;
  160. case field_kind::date: return protocol_field_type::date;
  161. case field_kind::datetime: return protocol_field_type::datetime;
  162. case field_kind::time: return protocol_field_type::time;
  163. default: BOOST_ASSERT(false); return protocol_field_type::null; // LCOV_EXCL_LINE
  164. }
  165. }
  166. // Returns the collation ID's first byte (for login packets)
  167. inline std::uint8_t get_collation_first_byte(std::uint32_t collation_id)
  168. {
  169. return static_cast<std::uint8_t>(collation_id % 0xff);
  170. }
  171. } // namespace detail
  172. } // namespace mysql
  173. } // namespace boost
  174. void boost::mysql::detail::execute_stmt_command::serialize(serialization_context& ctx) const
  175. {
  176. // The wire layout is as follows:
  177. // command ID
  178. // std::uint32_t statement_id;
  179. // std::uint8_t flags;
  180. // std::uint32_t iteration_count;
  181. // if num_params > 0:
  182. // NULL bitmap
  183. // std::uint8_t new_params_bind_flag;
  184. // array<meta_packet, num_params> meta;
  185. // protocol_field_type type;
  186. // std::uint8_t unsigned_flag;
  187. // array<field_view, num_params> params;
  188. constexpr int1 command_id{0x17};
  189. constexpr int1 flags{0};
  190. constexpr int4 iteration_count{1};
  191. constexpr int1 new_params_bind_flag{1};
  192. // header
  193. ctx.serialize_fixed(command_id, int4{statement_id}, flags, iteration_count);
  194. // Number of parameters
  195. auto num_params = params.size();
  196. if (num_params > 0)
  197. {
  198. // NULL bitmap
  199. null_bitmap_generator null_gen(params);
  200. while (!null_gen.done())
  201. ctx.add(null_gen.next());
  202. // new parameters bind flag
  203. new_params_bind_flag.serialize(ctx);
  204. // value metadata
  205. for (field_view param : params)
  206. {
  207. field_kind kind = param.kind();
  208. protocol_field_type type = to_protocol_field_type(kind);
  209. std::uint8_t unsigned_flag = kind == field_kind::uint64 ? std::uint8_t(0x80) : std::uint8_t(0);
  210. ctx.serialize_fixed(int1{static_cast<std::uint8_t>(type)}, int1{unsigned_flag});
  211. }
  212. // actual values
  213. for (field_view param : params)
  214. {
  215. serialize_binary_field(ctx, param);
  216. }
  217. }
  218. }
  219. void boost::mysql::detail::login_request::serialize(serialization_context& ctx) const
  220. {
  221. ctx.serialize_fixed(
  222. int4{static_cast<std::uint32_t>(negotiated_capabilities)}, // client_flag
  223. int4{max_packet_size}, // max_packet_size
  224. int1{get_collation_first_byte(collation_id)}, // character_set
  225. string_fixed<23>{} // filler (all zeros)
  226. );
  227. ctx.serialize(
  228. string_null{username},
  229. string_lenenc{to_string(auth_response)} // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
  230. );
  231. if (has_capabilities(negotiated_capabilities, capabilities::connect_with_db))
  232. {
  233. string_null{database}.serialize(ctx); // database
  234. }
  235. string_null{auth_plugin_name}.serialize(ctx); // client_plugin_name
  236. }
  237. void boost::mysql::detail::ssl_request::serialize(serialization_context& ctx) const
  238. {
  239. ctx.serialize_fixed(
  240. int4{static_cast<std::uint32_t>(negotiated_capabilities)}, // client_flag
  241. int4{max_packet_size}, // max_packet_size
  242. int1{get_collation_first_byte(collation_id)}, // character_set,
  243. string_fixed<23>{} // filler, all zeros
  244. );
  245. }
  246. #endif