123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- //
- // 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 <boost/mysql/field_view.hpp>
- #include <boost/mysql/string_view.hpp>
- #include <boost/mysql/impl/internal/protocol/capabilities.hpp>
- #include <boost/mysql/impl/internal/protocol/frame_header.hpp>
- #include <boost/mysql/impl/internal/protocol/impl/binary_protocol.hpp>
- #include <boost/mysql/impl/internal/protocol/impl/null_bitmap.hpp>
- #include <boost/mysql/impl/internal/protocol/impl/protocol_field_type.hpp>
- #include <boost/mysql/impl/internal/protocol/impl/protocol_types.hpp>
- #include <boost/mysql/impl/internal/protocol/impl/serialization_context.hpp>
- #include <boost/assert.hpp>
- #include <cstdint>
- 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<const field_view> 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<const std::uint8_t> 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<const std::uint8_t> auth_plugin_data;
- void serialize(serialization_context& ctx) const { ctx.add(auth_plugin_data); }
- };
- // Serialize a complete message
- template <class Serializable>
- inline std::uint8_t serialize_top_level(
- const Serializable& input,
- std::vector<std::uint8_t>& 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<std::uint8_t>(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_packet, num_params> meta;
- // protocol_field_type type;
- // std::uint8_t unsigned_flag;
- // array<field_view, num_params> 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<std::uint8_t>(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
|