// // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace mysql { namespace detail { // quit struct quit_command { void serialize(serialization_context& ctx) const { ctx.add(0x01); } }; // ping struct ping_command { void serialize(serialization_context& ctx) const { ctx.add(0x0e); } }; // reset_connection struct reset_connection_command { void serialize(serialization_context& ctx) const { ctx.add(0x1f); } }; // query struct query_command { string_view query; void serialize(serialization_context& ctx) const { ctx.add(0x03); string_eof{query}.serialize_checked(ctx); } }; // prepare_statement struct prepare_stmt_command { string_view stmt; void serialize(serialization_context& ctx) const { ctx.add(0x16); string_eof{stmt}.serialize(ctx); } }; // execute statement struct execute_stmt_command { std::uint32_t statement_id; span params; inline void serialize(serialization_context& ctx) const; }; // close statement struct close_stmt_command { std::uint32_t statement_id; void serialize(serialization_context& ctx) const { ctx.add(0x19); int4{statement_id}.serialize(ctx); } }; // Login request struct login_request { capabilities negotiated_capabilities; // capabilities std::uint32_t max_packet_size; std::uint32_t collation_id; string_view username; span auth_response; string_view database; string_view auth_plugin_name; inline void serialize(serialization_context& ctx) const; }; // SSL request struct ssl_request { capabilities negotiated_capabilities; std::uint32_t max_packet_size; std::uint32_t collation_id; inline void serialize(serialization_context& ctx) const; }; // Auth switch response struct auth_switch_response { span auth_plugin_data; void serialize(serialization_context& ctx) const { ctx.add(auth_plugin_data); } }; // Serialize a complete message template inline std::uint8_t serialize_top_level( const Serializable& input, std::vector& to, std::uint8_t seqnum = 0, std::size_t frame_size = max_packet_size ) { serialization_context ctx(to, frame_size); input.serialize(ctx); return ctx.write_frame_headers(seqnum); } } // namespace detail } // namespace mysql } // namespace boost // // Implementations // namespace boost { namespace mysql { namespace detail { // Maps from an actual value to a protocol_field_type (for execute statement) inline protocol_field_type to_protocol_field_type(field_kind kind) { switch (kind) { case field_kind::null: return protocol_field_type::null; case field_kind::int64: return protocol_field_type::longlong; case field_kind::uint64: return protocol_field_type::longlong; case field_kind::string: return protocol_field_type::string; case field_kind::blob: return protocol_field_type::blob; case field_kind::float_: return protocol_field_type::float_; case field_kind::double_: return protocol_field_type::double_; case field_kind::date: return protocol_field_type::date; case field_kind::datetime: return protocol_field_type::datetime; case field_kind::time: return protocol_field_type::time; default: BOOST_ASSERT(false); return protocol_field_type::null; } } // Returns the collation ID's first byte (for login packets) inline std::uint8_t get_collation_first_byte(std::uint32_t collation_id) { return static_cast(collation_id % 0xff); } } // namespace detail } // namespace mysql } // namespace boost void boost::mysql::detail::execute_stmt_command::serialize(serialization_context& ctx) const { // The wire layout is as follows: // command ID // std::uint32_t statement_id; // std::uint8_t flags; // std::uint32_t iteration_count; // if num_params > 0: // NULL bitmap // std::uint8_t new_params_bind_flag; // array meta; // protocol_field_type type; // std::uint8_t unsigned_flag; // array params; constexpr int1 command_id{0x17}; constexpr int1 flags{0}; constexpr int4 iteration_count{1}; constexpr int1 new_params_bind_flag{1}; // header ctx.serialize(command_id, int4{statement_id}, flags, iteration_count); // Number of parameters auto num_params = params.size(); if (num_params > 0) { // NULL bitmap null_bitmap_generator null_gen(params); while (!null_gen.done()) ctx.add(null_gen.next()); // new parameters bind flag new_params_bind_flag.serialize(ctx); // value metadata for (field_view param : params) { field_kind kind = param.kind(); protocol_field_type type = to_protocol_field_type(kind); std::uint8_t unsigned_flag = kind == field_kind::uint64 ? std::uint8_t(0x80) : std::uint8_t(0); ctx.add(static_cast(type)); ctx.add(unsigned_flag); } // actual values for (field_view param : params) { serialize_binary_field(ctx, param); } } } void boost::mysql::detail::login_request::serialize(serialization_context& ctx) const { ctx.serialize( int4{negotiated_capabilities.get()}, // client_flag int4{max_packet_size}, // max_packet_size int1{get_collation_first_byte(collation_id)}, // character_set string_fixed<23>{}, // filler (all zeros) string_null{username}, string_lenenc{to_string(auth_response)} // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA ); if (negotiated_capabilities.has(CLIENT_CONNECT_WITH_DB)) { string_null{database}.serialize(ctx); // database } string_null{auth_plugin_name}.serialize(ctx); // client_plugin_name } void boost::mysql::detail::ssl_request::serialize(serialization_context& ctx) const { ctx.serialize( int4{negotiated_capabilities.get()}, // client_flag int4{max_packet_size}, // max_packet_size int1{get_collation_first_byte(collation_id)}, // character_set, string_fixed<23>{} // filler, all zeros ); } #endif