serialization.hpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. //
  2. // Copyright (c) 2019-2024 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/field_view.hpp>
  10. #include <boost/mysql/string_view.hpp>
  11. #include <boost/mysql/impl/internal/protocol/capabilities.hpp>
  12. #include <boost/mysql/impl/internal/protocol/frame_header.hpp>
  13. #include <boost/mysql/impl/internal/protocol/impl/binary_protocol.hpp>
  14. #include <boost/mysql/impl/internal/protocol/impl/null_bitmap.hpp>
  15. #include <boost/mysql/impl/internal/protocol/impl/protocol_field_type.hpp>
  16. #include <boost/mysql/impl/internal/protocol/impl/protocol_types.hpp>
  17. #include <boost/mysql/impl/internal/protocol/impl/serialization_context.hpp>
  18. #include <boost/assert.hpp>
  19. #include <cstdint>
  20. namespace boost {
  21. namespace mysql {
  22. namespace detail {
  23. // quit
  24. struct quit_command
  25. {
  26. void serialize(serialization_context& ctx) const { ctx.add(0x01); }
  27. };
  28. // ping
  29. struct ping_command
  30. {
  31. void serialize(serialization_context& ctx) const { ctx.add(0x0e); }
  32. };
  33. // reset_connection
  34. struct reset_connection_command
  35. {
  36. void serialize(serialization_context& ctx) const { ctx.add(0x1f); }
  37. };
  38. // query
  39. struct query_command
  40. {
  41. string_view query;
  42. void serialize(serialization_context& ctx) const
  43. {
  44. ctx.add(0x03);
  45. string_eof{query}.serialize_checked(ctx);
  46. }
  47. };
  48. // prepare_statement
  49. struct prepare_stmt_command
  50. {
  51. string_view stmt;
  52. void serialize(serialization_context& ctx) const
  53. {
  54. ctx.add(0x16);
  55. string_eof{stmt}.serialize(ctx);
  56. }
  57. };
  58. // execute statement
  59. struct execute_stmt_command
  60. {
  61. std::uint32_t statement_id;
  62. span<const field_view> params;
  63. inline void serialize(serialization_context& ctx) const;
  64. };
  65. // close statement
  66. struct close_stmt_command
  67. {
  68. std::uint32_t statement_id;
  69. void serialize(serialization_context& ctx) const
  70. {
  71. ctx.add(0x19);
  72. int4{statement_id}.serialize(ctx);
  73. }
  74. };
  75. // Login request
  76. struct login_request
  77. {
  78. capabilities negotiated_capabilities; // capabilities
  79. std::uint32_t max_packet_size;
  80. std::uint32_t collation_id;
  81. string_view username;
  82. span<const std::uint8_t> auth_response;
  83. string_view database;
  84. string_view auth_plugin_name;
  85. inline void serialize(serialization_context& ctx) const;
  86. };
  87. // SSL request
  88. struct ssl_request
  89. {
  90. capabilities negotiated_capabilities;
  91. std::uint32_t max_packet_size;
  92. std::uint32_t collation_id;
  93. inline void serialize(serialization_context& ctx) const;
  94. };
  95. // Auth switch response
  96. struct auth_switch_response
  97. {
  98. span<const std::uint8_t> auth_plugin_data;
  99. void serialize(serialization_context& ctx) const { ctx.add(auth_plugin_data); }
  100. };
  101. // Serialize a complete message
  102. template <class Serializable>
  103. inline std::uint8_t serialize_top_level(
  104. const Serializable& input,
  105. std::vector<std::uint8_t>& to,
  106. std::uint8_t seqnum = 0,
  107. std::size_t frame_size = max_packet_size
  108. )
  109. {
  110. serialization_context ctx(to, frame_size);
  111. input.serialize(ctx);
  112. return ctx.write_frame_headers(seqnum);
  113. }
  114. } // namespace detail
  115. } // namespace mysql
  116. } // namespace boost
  117. //
  118. // Implementations
  119. //
  120. namespace boost {
  121. namespace mysql {
  122. namespace detail {
  123. // Maps from an actual value to a protocol_field_type (for execute statement)
  124. inline protocol_field_type to_protocol_field_type(field_kind kind)
  125. {
  126. switch (kind)
  127. {
  128. case field_kind::null: return protocol_field_type::null;
  129. case field_kind::int64: return protocol_field_type::longlong;
  130. case field_kind::uint64: return protocol_field_type::longlong;
  131. case field_kind::string: return protocol_field_type::string;
  132. case field_kind::blob: return protocol_field_type::blob;
  133. case field_kind::float_: return protocol_field_type::float_;
  134. case field_kind::double_: return protocol_field_type::double_;
  135. case field_kind::date: return protocol_field_type::date;
  136. case field_kind::datetime: return protocol_field_type::datetime;
  137. case field_kind::time: return protocol_field_type::time;
  138. default: BOOST_ASSERT(false); return protocol_field_type::null;
  139. }
  140. }
  141. // Returns the collation ID's first byte (for login packets)
  142. inline std::uint8_t get_collation_first_byte(std::uint32_t collation_id)
  143. {
  144. return static_cast<std::uint8_t>(collation_id % 0xff);
  145. }
  146. } // namespace detail
  147. } // namespace mysql
  148. } // namespace boost
  149. void boost::mysql::detail::execute_stmt_command::serialize(serialization_context& ctx) const
  150. {
  151. // The wire layout is as follows:
  152. // command ID
  153. // std::uint32_t statement_id;
  154. // std::uint8_t flags;
  155. // std::uint32_t iteration_count;
  156. // if num_params > 0:
  157. // NULL bitmap
  158. // std::uint8_t new_params_bind_flag;
  159. // array<meta_packet, num_params> meta;
  160. // protocol_field_type type;
  161. // std::uint8_t unsigned_flag;
  162. // array<field_view, num_params> params;
  163. constexpr int1 command_id{0x17};
  164. constexpr int1 flags{0};
  165. constexpr int4 iteration_count{1};
  166. constexpr int1 new_params_bind_flag{1};
  167. // header
  168. ctx.serialize(command_id, int4{statement_id}, flags, iteration_count);
  169. // Number of parameters
  170. auto num_params = params.size();
  171. if (num_params > 0)
  172. {
  173. // NULL bitmap
  174. null_bitmap_generator null_gen(params);
  175. while (!null_gen.done())
  176. ctx.add(null_gen.next());
  177. // new parameters bind flag
  178. new_params_bind_flag.serialize(ctx);
  179. // value metadata
  180. for (field_view param : params)
  181. {
  182. field_kind kind = param.kind();
  183. protocol_field_type type = to_protocol_field_type(kind);
  184. std::uint8_t unsigned_flag = kind == field_kind::uint64 ? std::uint8_t(0x80) : std::uint8_t(0);
  185. ctx.add(static_cast<std::uint8_t>(type));
  186. ctx.add(unsigned_flag);
  187. }
  188. // actual values
  189. for (field_view param : params)
  190. {
  191. serialize_binary_field(ctx, param);
  192. }
  193. }
  194. }
  195. void boost::mysql::detail::login_request::serialize(serialization_context& ctx) const
  196. {
  197. ctx.serialize(
  198. int4{negotiated_capabilities.get()}, // client_flag
  199. int4{max_packet_size}, // max_packet_size
  200. int1{get_collation_first_byte(collation_id)}, // character_set
  201. string_fixed<23>{}, // filler (all zeros)
  202. string_null{username},
  203. string_lenenc{to_string(auth_response)} // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
  204. );
  205. if (negotiated_capabilities.has(CLIENT_CONNECT_WITH_DB))
  206. {
  207. string_null{database}.serialize(ctx); // database
  208. }
  209. string_null{auth_plugin_name}.serialize(ctx); // client_plugin_name
  210. }
  211. void boost::mysql::detail::ssl_request::serialize(serialization_context& ctx) const
  212. {
  213. ctx.serialize(
  214. int4{negotiated_capabilities.get()}, // client_flag
  215. int4{max_packet_size}, // max_packet_size
  216. int1{get_collation_first_byte(collation_id)}, // character_set,
  217. string_fixed<23>{} // filler, all zeros
  218. );
  219. }
  220. #endif