Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:42:41

0001 //
0002 // Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_PROTOCOL_IPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_PROTOCOL_IPP
0010 
0011 #pragma once
0012 
0013 #include <boost/mysql/client_errc.hpp>
0014 #include <boost/mysql/common_server_errc.hpp>
0015 #include <boost/mysql/error_categories.hpp>
0016 #include <boost/mysql/error_code.hpp>
0017 #include <boost/mysql/field_kind.hpp>
0018 #include <boost/mysql/string_view.hpp>
0019 
0020 #include <boost/mysql/detail/config.hpp>
0021 
0022 #include <boost/mysql/impl/internal/error/server_error_to_string.hpp>
0023 #include <boost/mysql/impl/internal/make_string_view.hpp>
0024 #include <boost/mysql/impl/internal/protocol/basic_types.hpp>
0025 #include <boost/mysql/impl/internal/protocol/binary_serialization.hpp>
0026 #include <boost/mysql/impl/internal/protocol/capabilities.hpp>
0027 #include <boost/mysql/impl/internal/protocol/constants.hpp>
0028 #include <boost/mysql/impl/internal/protocol/deserialize_binary_field.hpp>
0029 #include <boost/mysql/impl/internal/protocol/deserialize_text_field.hpp>
0030 #include <boost/mysql/impl/internal/protocol/null_bitmap_traits.hpp>
0031 #include <boost/mysql/impl/internal/protocol/protocol.hpp>
0032 #include <boost/mysql/impl/internal/protocol/serialization.hpp>
0033 
0034 #include <boost/core/ignore_unused.hpp>
0035 #include <boost/core/span.hpp>
0036 
0037 #include <cstddef>
0038 
0039 namespace boost {
0040 namespace mysql {
0041 namespace detail {
0042 
0043 // Constants
0044 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t handshake_protocol_version_9 = 9;
0045 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t handshake_protocol_version_10 = 10;
0046 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t error_packet_header = 0xff;
0047 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t ok_packet_header = 0x00;
0048 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t eof_packet_header = 0xfe;
0049 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t auth_switch_request_header = 0xfe;
0050 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t auth_more_data_header = 0x01;
0051 BOOST_MYSQL_STATIC_IF_COMPILED constexpr string_view fast_auth_complete_challenge = make_string_view("\3");
0052 
0053 // Helpers
0054 BOOST_MYSQL_STATIC_OR_INLINE
0055 void serialize_command_id(span<std::uint8_t> buff, std::uint8_t command_id) noexcept
0056 {
0057     BOOST_ASSERT(buff.size() >= 1u);
0058     buff[0] = command_id;
0059 }
0060 
0061 struct frame_header_packet
0062 {
0063     int3 packet_size;
0064     std::uint8_t sequence_number;
0065 };
0066 
0067 // Maps from an actual value to a protocol_field_type (for execute statement). Only value's type is used
0068 static protocol_field_type get_protocol_field_type(field_view input) noexcept
0069 {
0070     switch (input.kind())
0071     {
0072     case field_kind::null: return protocol_field_type::null;
0073     case field_kind::int64: return protocol_field_type::longlong;
0074     case field_kind::uint64: return protocol_field_type::longlong;
0075     case field_kind::string: return protocol_field_type::string;
0076     case field_kind::blob: return protocol_field_type::blob;
0077     case field_kind::float_: return protocol_field_type::float_;
0078     case field_kind::double_: return protocol_field_type::double_;
0079     case field_kind::date: return protocol_field_type::date;
0080     case field_kind::datetime: return protocol_field_type::datetime;
0081     case field_kind::time: return protocol_field_type::time;
0082     default: BOOST_ASSERT(false); return protocol_field_type::null;
0083     }
0084 }
0085 
0086 }  // namespace detail
0087 }  // namespace mysql
0088 }  // namespace boost
0089 
0090 // Frame header
0091 void boost::mysql::detail::serialize_frame_header(
0092     frame_header msg,
0093     span<std::uint8_t, frame_header_size> buffer
0094 ) noexcept
0095 {
0096     BOOST_ASSERT(msg.size <= 0xffffff);  // range check
0097     serialization_context ctx(buffer.data());
0098     frame_header_packet pack{int3{msg.size}, msg.sequence_number};
0099     serialize(ctx, pack.packet_size, pack.sequence_number);
0100 }
0101 
0102 boost::mysql::detail::frame_header boost::mysql::detail::deserialize_frame_header(
0103     span<const std::uint8_t, frame_header_size> buffer
0104 ) noexcept
0105 {
0106     frame_header_packet pack{};
0107     deserialization_context ctx(buffer.data(), buffer.size());
0108     auto err = deserialize(ctx, pack.packet_size, pack.sequence_number);
0109     BOOST_ASSERT(err == deserialize_errc::ok);
0110     boost::ignore_unused(err);
0111     return frame_header{pack.packet_size.value, pack.sequence_number};
0112 }
0113 
0114 // OK packets
0115 boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet(
0116     span<const std::uint8_t> msg,
0117     ok_view& output
0118 ) noexcept
0119 {
0120     struct ok_packet
0121     {
0122         // header: int<1>     header     0x00 or 0xFE the OK packet header
0123         int_lenenc affected_rows;
0124         int_lenenc last_insert_id;
0125         std::uint16_t status_flags;  // server_status_flags
0126         std::uint16_t warnings;
0127         // CLIENT_SESSION_TRACK: not implemented
0128         string_lenenc info;
0129     } pack{};
0130 
0131     deserialization_context ctx(msg);
0132     auto err = deserialize(ctx, pack.affected_rows, pack.last_insert_id, pack.status_flags, pack.warnings);
0133     if (err != deserialize_errc::ok)
0134         return to_error_code(err);
0135 
0136     if (ctx.enough_size(1))  // message is optional, may be omitted
0137     {
0138         err = deserialize(ctx, pack.info);
0139         if (err != deserialize_errc::ok)
0140             return to_error_code(err);
0141     }
0142 
0143     output = {
0144         pack.affected_rows.value,
0145         pack.last_insert_id.value,
0146         pack.status_flags,
0147         pack.warnings,
0148         pack.info.value,
0149     };
0150 
0151     return ctx.check_extra_bytes();
0152 }
0153 
0154 // Error packets
0155 boost::mysql::error_code boost::mysql::detail::deserialize_error_packet(
0156     span<const std::uint8_t> msg,
0157     err_view& output
0158 ) noexcept
0159 {
0160     struct err_packet
0161     {
0162         // int<1>     header     0xFF ERR packet header
0163         std::uint16_t error_code;
0164         string_fixed<1> sql_state_marker;
0165         string_fixed<5> sql_state;
0166         string_eof error_message;
0167     } pack{};
0168 
0169     deserialization_context ctx(msg);
0170     auto err = deserialize(ctx, pack.error_code, pack.sql_state_marker, pack.sql_state, pack.error_message);
0171     if (err != deserialize_errc::ok)
0172         return to_error_code(err);
0173 
0174     output = err_view{
0175         pack.error_code,
0176         pack.error_message.value,
0177     };
0178 
0179     return ctx.check_extra_bytes();
0180 }
0181 
0182 boost::mysql::error_code boost::mysql::detail::process_error_packet(
0183     span<const std::uint8_t> msg,
0184     db_flavor flavor,
0185     diagnostics& diag
0186 )
0187 {
0188     err_view error_packet{};
0189     auto err = deserialize_error_packet(msg, error_packet);
0190     if (err)
0191         return err;
0192 
0193     // Error message
0194     access::get_impl(diag).assign_server(error_packet.error_message);
0195 
0196     // Error code
0197     if (common_error_to_string(error_packet.error_code))
0198     {
0199         // This is an error shared between MySQL and MariaDB, represented as a common_server_errc.
0200         // get_common_error_message will check that the code has a common_server_errc representation
0201         // (the common error range has "holes" because of removed error codes)
0202         return static_cast<common_server_errc>(error_packet.error_code);
0203     }
0204     else
0205     {
0206         // This is a MySQL or MariaDB specific code. There is no fixed list of error codes,
0207         // as they both keep adding more codes, so no validation happens.
0208         const auto& cat = flavor == db_flavor::mysql ? get_mysql_server_category()
0209                                                      : get_mariadb_server_category();
0210         return error_code(error_packet.error_code, cat);
0211     }
0212 }
0213 
0214 // Column definition
0215 boost::mysql::error_code boost::mysql::detail::deserialize_column_definition(
0216     span<const std::uint8_t> input,
0217     coldef_view& output
0218 ) noexcept
0219 {
0220     deserialization_context ctx(input);
0221 
0222     struct column_definition_packet
0223     {
0224         string_lenenc catalog;    // always "def"
0225         string_lenenc schema;     // database
0226         string_lenenc table;      // virtual table
0227         string_lenenc org_table;  // physical table
0228         string_lenenc name;       // virtual column name
0229         string_lenenc org_name;   // physical column name
0230         string_lenenc fixed_fields;
0231     } pack{};
0232 
0233     // pack.fixed_fields itself is a structure like this.
0234     // The proto allows for extensibility here - adding fields just increasing fixed_fields.length
0235     struct fixed_fields_pack
0236     {
0237         std::uint16_t character_set;  // collation id, somehow named character_set in the protocol docs
0238         std::uint32_t column_length;  // maximum length of the field
0239         protocol_field_type type;     // type of the column as defined in enum_field_types
0240         std::uint16_t flags;          // Flags as defined in Column Definition Flags
0241         std::uint8_t decimals;        // max shown decimal digits. 0x00 for int/static strings; 0x1f for
0242                                       // dynamic strings, double, float
0243     } fixed_fields{};
0244 
0245     // Deserialize the main structure
0246     auto err = deserialize(
0247         ctx,
0248         pack.catalog,
0249         pack.schema,
0250         pack.table,
0251         pack.org_table,
0252         pack.name,
0253         pack.org_name,
0254         pack.fixed_fields
0255     );
0256     if (err != deserialize_errc::ok)
0257         return to_error_code(err);
0258 
0259     // Deserialize the fixed_fields structure.
0260     // Intentionally not checking for extra bytes here, since there may be unknown fields that should just get
0261     // ignored
0262     deserialization_context subctx(
0263         reinterpret_cast<const std::uint8_t*>(pack.fixed_fields.value.data()),
0264         pack.fixed_fields.value.size()
0265     );
0266     err = deserialize(
0267         subctx,
0268         fixed_fields.character_set,
0269         fixed_fields.column_length,
0270         fixed_fields.type,
0271         fixed_fields.flags,
0272         fixed_fields.decimals
0273     );
0274     if (err != deserialize_errc::ok)
0275         return to_error_code(err);
0276 
0277     // Compose output
0278     output = coldef_view{
0279         pack.schema.value,
0280         pack.table.value,
0281         pack.org_table.value,
0282         pack.name.value,
0283         pack.org_name.value,
0284         fixed_fields.character_set,
0285         fixed_fields.column_length,
0286         compute_column_type(fixed_fields.type, fixed_fields.flags, fixed_fields.character_set),
0287         fixed_fields.flags,
0288         fixed_fields.decimals,
0289     };
0290 
0291     return ctx.check_extra_bytes();
0292 }
0293 
0294 // quit
0295 std::size_t boost::mysql::detail::quit_command::get_size() const noexcept { return 1u; }
0296 void boost::mysql::detail::quit_command::serialize(span<std::uint8_t> buff) const noexcept
0297 {
0298     serialize_command_id(buff, 0x01);
0299 }
0300 
0301 // ping
0302 std::size_t boost::mysql::detail::ping_command::get_size() const noexcept { return 1u; }
0303 void boost::mysql::detail::ping_command::serialize(span<std::uint8_t> buff) const noexcept
0304 {
0305     serialize_command_id(buff, 0x0e);
0306 }
0307 
0308 // reset connection
0309 std::size_t boost::mysql::detail::reset_connection_command::get_size() const noexcept { return 1u; }
0310 void boost::mysql::detail::reset_connection_command::serialize(span<std::uint8_t> buff) const noexcept
0311 {
0312     serialize_command_id(buff, 0x1f);
0313 }
0314 
0315 boost::mysql::error_code boost::mysql::detail::deserialize_ok_response(
0316     span<const std::uint8_t> message,
0317     db_flavor flavor,
0318     diagnostics& diag
0319 )
0320 {
0321     // Header
0322     std::uint8_t header{};
0323     deserialization_context ctx(message);
0324     auto err = to_error_code(deserialize(ctx, header));
0325     if (err)
0326         return err;
0327 
0328     if (header == ok_packet_header)
0329     {
0330         // Verify that the ok_packet is correct
0331         ok_view ok{};
0332         return deserialize_ok_packet(ctx.to_span(), ok);
0333     }
0334     else if (header == error_packet_header)
0335     {
0336         // Theoretically, the server can answer with an error packet, too
0337         return process_error_packet(ctx.to_span(), flavor, diag);
0338     }
0339     else
0340     {
0341         // Invalid message
0342         return client_errc::protocol_value_error;
0343     }
0344 }
0345 
0346 // query
0347 std::size_t boost::mysql::detail::query_command::get_size() const noexcept
0348 {
0349     return ::boost::mysql::detail::get_size(string_eof{query}) + 1;  // command ID
0350 }
0351 void boost::mysql::detail::query_command::serialize(span<std::uint8_t> buff) const noexcept
0352 {
0353     constexpr std::uint8_t command_id = 0x03;
0354 
0355     BOOST_ASSERT(buff.size() >= get_size());
0356     serialization_context ctx(buff.data());
0357     ::boost::mysql::detail::serialize(ctx, command_id, string_eof{query});
0358 }
0359 
0360 // prepare statement
0361 std::size_t boost::mysql::detail::prepare_stmt_command::get_size() const noexcept
0362 {
0363     return ::boost::mysql::detail::get_size(string_eof{stmt}) + 1;  // command ID
0364 }
0365 void boost::mysql::detail::prepare_stmt_command::serialize(span<std::uint8_t> buff) const noexcept
0366 {
0367     constexpr std::uint8_t command_id = 0x16;
0368 
0369     BOOST_ASSERT(buff.size() >= get_size());
0370     serialization_context ctx(buff.data());
0371     ::boost::mysql::detail::serialize(ctx, command_id, string_eof{stmt});
0372 }
0373 
0374 boost::mysql::error_code boost::mysql::detail::deserialize_prepare_stmt_response_impl(
0375     span<const std::uint8_t> message,
0376     prepare_stmt_response& output
0377 ) noexcept
0378 {
0379     struct com_stmt_prepare_ok_packet
0380     {
0381         // std::uint8_t status: must be 0
0382         std::uint32_t statement_id;
0383         std::uint16_t num_columns;
0384         std::uint16_t num_params;
0385         std::uint8_t reserved_1;  // must be 0
0386         std::uint16_t warning_count;
0387         // std::uint8_t metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA: not implemented
0388     } pack{};
0389 
0390     deserialization_context ctx(message);
0391 
0392     auto err = deserialize(
0393         ctx,
0394         pack.statement_id,
0395         pack.num_columns,
0396         pack.num_params,
0397         pack.reserved_1,
0398         pack.warning_count
0399     );
0400     if (err != deserialize_errc::ok)
0401         return to_error_code(err);
0402 
0403     output = prepare_stmt_response{
0404         pack.statement_id,
0405         pack.num_columns,
0406         pack.num_params,
0407     };
0408 
0409     return ctx.check_extra_bytes();
0410 }
0411 
0412 boost::mysql::error_code boost::mysql::detail::deserialize_prepare_stmt_response(
0413     span<const std::uint8_t> message,
0414     db_flavor flavor,
0415     prepare_stmt_response& output,
0416     diagnostics& diag
0417 )
0418 {
0419     deserialization_context ctx(message);
0420     std::uint8_t msg_type = 0;
0421     auto err = to_error_code(deserialize(ctx, msg_type));
0422     if (err)
0423         return err;
0424 
0425     if (msg_type == error_packet_header)
0426     {
0427         return process_error_packet(ctx.to_span(), flavor, diag);
0428     }
0429     else if (msg_type != 0)
0430     {
0431         return client_errc::protocol_value_error;
0432     }
0433     else
0434     {
0435         return deserialize_prepare_stmt_response_impl(ctx.to_span(), output);
0436     }
0437 }
0438 
0439 // execute statement
0440 // The wire layout is as follows:
0441 //  command ID
0442 //  std::uint32_t statement_id;
0443 //  std::uint8_t flags;
0444 //  std::uint32_t iteration_count;
0445 //  if num_params > 0:
0446 //      NULL bitmap
0447 //      std::uint8_t new_params_bind_flag;
0448 //      array<meta_packet, num_params> meta;
0449 //          protocol_field_type type;
0450 //          std::uint8_t unsigned_flag;
0451 //      array<field_view, num_params> params;
0452 std::size_t boost::mysql::detail::execute_stmt_command::get_size() const noexcept
0453 {
0454     constexpr std::size_t param_meta_packet_size = 2;           // type + unsigned flag
0455     constexpr std::size_t stmt_execute_packet_head_size = 1     // command ID
0456                                                           + 4   // statement_id
0457                                                           + 1   // flags
0458                                                           + 4;  // iteration_count
0459     std::size_t res = stmt_execute_packet_head_size;
0460     auto num_params = params.size();
0461     if (num_params > 0u)
0462     {
0463         res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
0464         res += 1;  // new_params_bind_flag
0465         res += param_meta_packet_size * num_params;
0466         for (field_view param : params)
0467         {
0468             res += ::boost::mysql::detail::get_size(param);
0469         }
0470     }
0471 
0472     return res;
0473 }
0474 
0475 void boost::mysql::detail::execute_stmt_command::serialize(span<std::uint8_t> buff) const noexcept
0476 {
0477     constexpr std::uint8_t command_id = 0x17;
0478 
0479     serialization_context ctx(buff.data());
0480     BOOST_ASSERT(buff.size() >= get_size());
0481 
0482     std::uint32_t statement_id = this->statement_id;
0483     std::uint8_t flags = 0;
0484     std::uint32_t iteration_count = 1;
0485     std::uint8_t new_params_bind_flag = 1;
0486 
0487     ::boost::mysql::detail::serialize(ctx, command_id, statement_id, flags, iteration_count);
0488 
0489     // Number of parameters
0490     auto num_params = params.size();
0491 
0492     if (num_params > 0)
0493     {
0494         // NULL bitmap
0495         null_bitmap_traits traits(stmt_execute_null_bitmap_offset, num_params);
0496         std::memset(ctx.first(), 0, traits.byte_count());  // Initialize to zeroes
0497         for (std::size_t i = 0; i < num_params; ++i)
0498         {
0499             if (params[i].is_null())
0500             {
0501                 traits.set_null(ctx.first(), i);
0502             }
0503         }
0504         ctx.advance(traits.byte_count());
0505 
0506         // new parameters bind flag
0507         ::boost::mysql::detail::serialize(ctx, new_params_bind_flag);
0508 
0509         // value metadata
0510         for (field_view param : params)
0511         {
0512             protocol_field_type type = get_protocol_field_type(param);
0513             std::uint8_t unsigned_flag = param.is_uint64() ? std::uint8_t(0x80) : std::uint8_t(0);
0514             ::boost::mysql::detail::serialize(ctx, type, unsigned_flag);
0515         }
0516 
0517         // actual values
0518         for (field_view param : params)
0519         {
0520             ::boost::mysql::detail::serialize(ctx, param);
0521         }
0522     }
0523 }
0524 
0525 // close statement
0526 std::size_t boost::mysql::detail::close_stmt_command::get_size() const noexcept { return 5u; }
0527 
0528 void boost::mysql::detail::close_stmt_command::serialize(span<std::uint8_t> buff) const noexcept
0529 {
0530     constexpr std::uint8_t command_id = 0x19;
0531 
0532     serialization_context ctx(buff.data());
0533     BOOST_ASSERT(buff.size() >= get_size());
0534 
0535     ::boost::mysql::detail::serialize(ctx, command_id, statement_id);
0536 }
0537 
0538 // execute response
0539 boost::mysql::detail::execute_response boost::mysql::detail::deserialize_execute_response(
0540     span<const std::uint8_t> msg,
0541     db_flavor flavor,
0542     diagnostics& diag
0543 ) noexcept
0544 {
0545     // Response may be: ok_packet, err_packet, local infile request (not implemented)
0546     // If it is none of this, then the message type itself is the beginning of
0547     // a length-encoded int containing the field count
0548     deserialization_context ctx(msg);
0549     std::uint8_t msg_type = 0;
0550     auto err = to_error_code(deserialize(ctx, msg_type));
0551     if (err)
0552         return err;
0553 
0554     if (msg_type == ok_packet_header)
0555     {
0556         ok_view ok{};
0557         err = deserialize_ok_packet(ctx.to_span(), ok);
0558         if (err)
0559             return err;
0560         return ok;
0561     }
0562     else if (msg_type == error_packet_header)
0563     {
0564         return process_error_packet(ctx.to_span(), flavor, diag);
0565     }
0566     else
0567     {
0568         // Resultset with metadata. First packet is an int_lenenc with
0569         // the number of field definitions to expect. Message type is part
0570         // of this packet, so we must rewind the context
0571         ctx.rewind(1);
0572         int_lenenc num_fields;
0573         err = to_error_code(deserialize(ctx, num_fields));
0574         if (err)
0575             return err;
0576         err = ctx.check_extra_bytes();
0577         if (err)
0578             return err;
0579 
0580         // We should have at least one field.
0581         // The max number of fields is some value around 1024. For simplicity/extensibility,
0582         // we accept anything less than 0xffff
0583         if (num_fields.value == 0 || num_fields.value > 0xffffu)
0584         {
0585             return make_error_code(client_errc::protocol_value_error);
0586         }
0587 
0588         return static_cast<std::size_t>(num_fields.value);
0589     }
0590 }
0591 
0592 boost::mysql::detail::row_message boost::mysql::detail::deserialize_row_message(
0593     span<const std::uint8_t> msg,
0594     db_flavor flavor,
0595     diagnostics& diag
0596 )
0597 {
0598     // Message type: row, error or eof?
0599     std::uint8_t msg_type = 0;
0600     deserialization_context ctx(msg);
0601     auto deser_errc = deserialize(ctx, msg_type);
0602     if (deser_errc != deserialize_errc::ok)
0603     {
0604         return to_error_code(deser_errc);
0605     }
0606 
0607     if (msg_type == eof_packet_header)
0608     {
0609         // end of resultset => this is a ok_packet, not a row
0610         ok_view ok{};
0611         auto err = deserialize_ok_packet(ctx.to_span(), ok);
0612         if (err)
0613             return err;
0614         return ok;
0615     }
0616     else if (msg_type == error_packet_header)
0617     {
0618         // An error occurred during the generation of the rows
0619         return process_error_packet(ctx.to_span(), flavor, diag);
0620     }
0621     else
0622     {
0623         // An actual row
0624         ctx.rewind(1);  // keep the 'message type' byte, as it is part of the actual message
0625         return span<const std::uint8_t>(ctx.first(), ctx.size());
0626     }
0627 }
0628 
0629 // Deserialize row
0630 namespace boost {
0631 namespace mysql {
0632 namespace detail {
0633 
0634 BOOST_MYSQL_STATIC_OR_INLINE
0635 bool is_next_field_null(const deserialization_context& ctx) noexcept
0636 {
0637     if (!ctx.enough_size(1))
0638         return false;
0639     return *ctx.first() == 0xfb;
0640 }
0641 
0642 BOOST_MYSQL_STATIC_OR_INLINE
0643 error_code deserialize_text_row(
0644     deserialization_context& ctx,
0645     metadata_collection_view meta,
0646     field_view* output
0647 )
0648 {
0649     for (std::vector<field_view>::size_type i = 0; i < meta.size(); ++i)
0650     {
0651         if (is_next_field_null(ctx))
0652         {
0653             ctx.advance(1);
0654             output[i] = field_view(nullptr);
0655         }
0656         else
0657         {
0658             string_lenenc value_str;
0659             auto err = deserialize(ctx, value_str);
0660             if (err != deserialize_errc::ok)
0661                 return to_error_code(err);
0662             err = deserialize_text_field(value_str.value, meta[i], output[i]);
0663             if (err != deserialize_errc::ok)
0664                 return to_error_code(err);
0665         }
0666     }
0667     if (!ctx.empty())
0668         return client_errc::extra_bytes;
0669     return error_code();
0670 }
0671 
0672 BOOST_MYSQL_STATIC_OR_INLINE
0673 error_code deserialize_binary_row(
0674     deserialization_context& ctx,
0675     metadata_collection_view meta,
0676     field_view* output
0677 )
0678 {
0679     // Skip packet header (it is not part of the message in the binary
0680     // protocol but it is in the text protocol, so we include it for homogeneity)
0681     if (!ctx.enough_size(1))
0682         return client_errc::incomplete_message;
0683     ctx.advance(1);
0684 
0685     // Number of fields
0686     std::size_t num_fields = meta.size();
0687 
0688     // Null bitmap
0689     null_bitmap_traits null_bitmap(binary_row_null_bitmap_offset, num_fields);
0690     const std::uint8_t* null_bitmap_begin = ctx.first();
0691     if (!ctx.enough_size(null_bitmap.byte_count()))
0692         return client_errc::incomplete_message;
0693     ctx.advance(null_bitmap.byte_count());
0694 
0695     // Actual values
0696     for (std::vector<field_view>::size_type i = 0; i < num_fields; ++i)
0697     {
0698         if (null_bitmap.is_null(null_bitmap_begin, i))
0699         {
0700             output[i] = field_view(nullptr);
0701         }
0702         else
0703         {
0704             auto err = deserialize_binary_field(ctx, meta[i], output[i]);
0705             if (err != deserialize_errc::ok)
0706                 return to_error_code(err);
0707         }
0708     }
0709 
0710     // Check for remaining bytes
0711     if (!ctx.empty())
0712         return make_error_code(client_errc::extra_bytes);
0713 
0714     return error_code();
0715 }
0716 
0717 }  // namespace detail
0718 }  // namespace mysql
0719 }  // namespace boost
0720 
0721 boost::mysql::error_code boost::mysql::detail::deserialize_row(
0722     resultset_encoding encoding,
0723     span<const std::uint8_t> buff,
0724     metadata_collection_view meta,
0725     span<field_view> output
0726 )
0727 {
0728     BOOST_ASSERT(meta.size() == output.size());
0729     deserialization_context ctx(buff);
0730     return encoding == detail::resultset_encoding::text ? deserialize_text_row(ctx, meta, output.data())
0731                                                         : deserialize_binary_row(ctx, meta, output.data());
0732 }
0733 
0734 // Server hello
0735 namespace boost {
0736 namespace mysql {
0737 namespace detail {
0738 
0739 BOOST_MYSQL_STATIC_OR_INLINE
0740 capabilities compose_capabilities(string_fixed<2> low, string_fixed<2> high) noexcept
0741 {
0742     std::uint32_t res = 0;
0743     auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&res);
0744     memcpy(capabilities_begin, low.value.data(), 2);
0745     memcpy(capabilities_begin + 2, high.value.data(), 2);
0746     return capabilities(boost::endian::little_to_native(res));
0747 }
0748 
0749 BOOST_MYSQL_STATIC_OR_INLINE
0750 db_flavor parse_db_version(string_view version_string) noexcept
0751 {
0752     return version_string.find("MariaDB") != string_view::npos ? db_flavor::mariadb : db_flavor::mysql;
0753 }
0754 
0755 BOOST_MYSQL_STATIC_IF_COMPILED
0756 constexpr std::uint8_t server_hello_auth1_length = 8;
0757 
0758 }  // namespace detail
0759 }  // namespace mysql
0760 }  // namespace boost
0761 
0762 boost::mysql::error_code boost::mysql::detail::deserialize_server_hello_impl(
0763     span<const std::uint8_t> msg,
0764     server_hello& output
0765 )
0766 {
0767     struct server_hello_packet
0768     {
0769         // int<1>     protocol version     Always 10
0770         string_null server_version;
0771         std::uint32_t connection_id;
0772         string_fixed<server_hello_auth1_length> auth_plugin_data_part_1;
0773         std::uint8_t filler;  // should be 0
0774         string_fixed<2> capability_flags_low;
0775         std::uint8_t character_set;  // default server a_protocol_character_set, only the lower 8-bits
0776         std::uint16_t status_flags;  // server_status_flags
0777         string_fixed<2> capability_flags_high;
0778         std::uint8_t auth_plugin_data_len;
0779         string_fixed<10> reserved;
0780         // auth plugin data, 2nd part. This has a weird representation that doesn't fit any defined type
0781         string_null auth_plugin_name;
0782     } pack{};
0783 
0784     deserialization_context ctx(msg);
0785 
0786     auto err = deserialize(
0787         ctx,
0788         pack.server_version,
0789         pack.connection_id,
0790         pack.auth_plugin_data_part_1,
0791         pack.filler,
0792         pack.capability_flags_low,
0793         pack.character_set,
0794         pack.status_flags,
0795         pack.capability_flags_high
0796     );
0797     if (err != deserialize_errc::ok)
0798         return to_error_code(err);
0799 
0800     // Compose capabilities
0801     auto cap = compose_capabilities(pack.capability_flags_low, pack.capability_flags_high);
0802 
0803     // Check minimum server capabilities to deserialize this frame
0804     if (!cap.has(CLIENT_PLUGIN_AUTH))
0805         return client_errc::server_unsupported;
0806 
0807     // Deserialize next fields
0808     err = deserialize(ctx, pack.auth_plugin_data_len, pack.reserved);
0809     if (err != deserialize_errc::ok)
0810         return to_error_code(err);
0811 
0812     // Auth plugin data, second part
0813     auto auth2_length = static_cast<std::uint8_t>(
0814         (std::max)(13, pack.auth_plugin_data_len - server_hello_auth1_length)
0815     );
0816     const void* auth2_data = ctx.first();
0817     if (!ctx.enough_size(auth2_length))
0818         return client_errc::incomplete_message;
0819     ctx.advance(auth2_length);
0820 
0821     // Auth plugin name
0822     err = deserialize(ctx, pack.auth_plugin_name);
0823     if (err != deserialize_errc::ok)
0824         return to_error_code(err);
0825 
0826     // Compose output
0827     output.server = parse_db_version(pack.server_version.value);
0828     output.server_capabilities = cap;
0829     output.auth_plugin_name = pack.auth_plugin_name.value;
0830 
0831     // Compose auth_plugin_data
0832     output.auth_plugin_data.clear();
0833     output.auth_plugin_data.append(pack.auth_plugin_data_part_1.value.data(), server_hello_auth1_length);
0834     output.auth_plugin_data.append(auth2_data,
0835                                    auth2_length - 1);  // discard an extra trailing NULL byte
0836 
0837     return ctx.check_extra_bytes();
0838 }
0839 
0840 boost::mysql::error_code boost::mysql::detail::deserialize_server_hello(
0841     span<const std::uint8_t> msg,
0842     server_hello& output,
0843     diagnostics& diag
0844 )
0845 {
0846     deserialization_context ctx(msg);
0847 
0848     // Message type
0849     std::uint8_t msg_type = 0;
0850     auto err = to_error_code(deserialize(ctx, msg_type));
0851     if (err)
0852         return err;
0853     if (msg_type == handshake_protocol_version_9)
0854     {
0855         return make_error_code(client_errc::server_unsupported);
0856     }
0857     else if (msg_type == error_packet_header)
0858     {
0859         // We don't know which DB is yet
0860         return process_error_packet(ctx.to_span(), db_flavor::mysql, diag);
0861     }
0862     else if (msg_type != handshake_protocol_version_10)
0863     {
0864         return make_error_code(client_errc::protocol_value_error);
0865     }
0866     else
0867     {
0868         return deserialize_server_hello_impl(ctx.to_span(), output);
0869     }
0870 }
0871 
0872 // Login request
0873 namespace boost {
0874 namespace mysql {
0875 namespace detail {
0876 
0877 BOOST_MYSQL_STATIC_OR_INLINE
0878 std::uint8_t get_collation_first_byte(std::uint32_t collation_id) noexcept
0879 {
0880     return static_cast<std::uint8_t>(collation_id % 0xff);
0881 }
0882 
0883 struct login_request_packet
0884 {
0885     std::uint32_t client_flag;  // capabilities
0886     std::uint32_t max_packet_size;
0887     std::uint8_t character_set;  // collation ID first byte
0888     string_fixed<23> filler;     //     All 0s.
0889     string_null username;
0890     string_lenenc auth_response;     // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
0891     string_null database;            // only to be serialized if CLIENT_CONNECT_WITH_DB
0892     string_null client_plugin_name;  // we require CLIENT_PLUGIN_AUTH
0893     // CLIENT_CONNECT_ATTRS: not implemented
0894 };
0895 
0896 BOOST_MYSQL_STATIC_OR_INLINE
0897 login_request_packet to_packet(const login_request& req) noexcept
0898 {
0899     return {
0900         req.negotiated_capabilities.get(),
0901         req.max_packet_size,
0902         get_collation_first_byte(req.collation_id),
0903         {},
0904         string_null{req.username},
0905         string_lenenc{to_string(req.auth_response)},
0906         string_null{req.database},
0907         string_null{req.auth_plugin_name},
0908     };
0909 }
0910 
0911 }  // namespace detail
0912 }  // namespace mysql
0913 }  // namespace boost
0914 
0915 std::size_t boost::mysql::detail::login_request::get_size() const noexcept
0916 {
0917     auto pack = to_packet(*this);
0918     return ::boost::mysql::detail::get_size(
0919                pack.client_flag,
0920                pack.max_packet_size,
0921                pack.character_set,
0922                pack.filler,
0923                pack.username,
0924                pack.auth_response
0925            ) +
0926            (negotiated_capabilities.has(CLIENT_CONNECT_WITH_DB)
0927                 ? ::boost::mysql::detail::get_size(pack.database)
0928                 : 0) +
0929            ::boost::mysql::detail::get_size(pack.client_plugin_name);
0930 }
0931 
0932 void boost::mysql::detail::login_request::serialize(span<std::uint8_t> buff) const noexcept
0933 {
0934     BOOST_ASSERT(buff.size() >= get_size());
0935     serialization_context ctx(buff.data());
0936 
0937     auto pack = to_packet(*this);
0938     ::boost::mysql::detail::serialize(
0939         ctx,
0940         pack.client_flag,
0941         pack.max_packet_size,
0942         pack.character_set,
0943         pack.filler,
0944         pack.username,
0945         pack.auth_response
0946     );
0947     if (negotiated_capabilities.has(CLIENT_CONNECT_WITH_DB))
0948     {
0949         ::boost::mysql::detail::serialize(ctx, pack.database);
0950     }
0951     ::boost::mysql::detail::serialize(ctx, pack.client_plugin_name);
0952 }
0953 
0954 // ssl_request
0955 std::size_t boost::mysql::detail::ssl_request::get_size() const noexcept { return 4 + 4 + 1 + 23; }
0956 
0957 void boost::mysql::detail::ssl_request::serialize(span<std::uint8_t> buff) const noexcept
0958 {
0959     BOOST_ASSERT(buff.size() >= get_size());
0960     serialization_context ctx(buff.data());
0961 
0962     struct ssl_request_packet
0963     {
0964         std::uint32_t client_flag;
0965         std::uint32_t max_packet_size;
0966         std::uint8_t character_set;
0967         string_fixed<23> filler;
0968     } pack{
0969         negotiated_capabilities.get(),
0970         max_packet_size,
0971         get_collation_first_byte(collation_id),
0972         {},
0973     };
0974 
0975     ::boost::mysql::detail::serialize(
0976         ctx,
0977         pack.client_flag,
0978         pack.max_packet_size,
0979         pack.character_set,
0980         pack.filler
0981     );
0982 }
0983 
0984 // auth_switch
0985 BOOST_ATTRIBUTE_NODISCARD
0986 boost::mysql::error_code boost::mysql::detail::deserialize_auth_switch(
0987     span<const std::uint8_t> msg,
0988     auth_switch& output
0989 ) noexcept
0990 {
0991     struct auth_switch_request_packet
0992     {
0993         string_null plugin_name;
0994         string_eof auth_plugin_data;
0995     } pack{};
0996 
0997     deserialization_context ctx(msg);
0998 
0999     auto err = deserialize(ctx, pack.plugin_name, pack.auth_plugin_data);
1000     if (err != deserialize_errc::ok)
1001         return to_error_code(err);
1002 
1003     // Discard an additional NULL at the end of auth data
1004     string_view auth_data = pack.auth_plugin_data.value;
1005     if (!auth_data.empty() && auth_data.back() == 0)
1006     {
1007         auth_data = auth_data.substr(0, auth_data.size() - 1);
1008     }
1009 
1010     output = {
1011         pack.plugin_name.value,
1012         to_span(auth_data),
1013     };
1014 
1015     return ctx.check_extra_bytes();
1016 }
1017 
1018 boost::mysql::detail::handhake_server_response boost::mysql::detail::deserialize_handshake_server_response(
1019     span<const std::uint8_t> buff,
1020     db_flavor flavor,
1021     diagnostics& diag
1022 )
1023 {
1024     deserialization_context ctx(buff);
1025     std::uint8_t msg_type = 0;
1026     auto err = to_error_code(deserialize(ctx, msg_type));
1027     if (err)
1028         return err;
1029 
1030     if (msg_type == ok_packet_header)
1031     {
1032         ok_view ok{};
1033         err = deserialize_ok_packet(ctx.to_span(), ok);
1034         if (err)
1035             return err;
1036         return ok;
1037     }
1038     else if (msg_type == error_packet_header)
1039     {
1040         return process_error_packet(ctx.to_span(), flavor, diag);
1041     }
1042     else if (msg_type == auth_switch_request_header)
1043     {
1044         // We have received an auth switch request. Deserialize it
1045         auth_switch auth_sw{};
1046         auto err = deserialize_auth_switch(ctx.to_span(), auth_sw);
1047         if (err)
1048             return err;
1049         return auth_sw;
1050     }
1051     else if (msg_type == auth_more_data_header)
1052     {
1053         // We have received an auth more data request. Deserialize it.
1054         // Note that string_eof never fails deserialization (by definition)
1055         string_eof auth_more_data;
1056         auto err = deserialize(ctx, auth_more_data);
1057         BOOST_ASSERT(err == deserialize_errc::ok);
1058         boost::ignore_unused(err);
1059 
1060         // If the special value fast_auth_complete_challenge
1061         // is received as auth data, it means that the auth is complete
1062         // but we must wait for another OK message. We consider this
1063         // a special type of message
1064         string_view challenge = auth_more_data.value;
1065         if (challenge == fast_auth_complete_challenge)
1066         {
1067             return handhake_server_response::ok_follows_t();
1068         }
1069 
1070         // Otherwise, just return the normal data
1071         return handhake_server_response(to_span(challenge));
1072     }
1073     else
1074     {
1075         // Unknown message type
1076         return make_error_code(client_errc::protocol_value_error);
1077     }
1078 }
1079 
1080 std::size_t boost::mysql::detail::auth_switch_response::get_size() const noexcept
1081 {
1082     return ::boost::mysql::detail::get_size(string_eof{to_string(auth_plugin_data)});
1083 }
1084 
1085 void boost::mysql::detail::auth_switch_response::serialize(span<std::uint8_t> buff) const noexcept
1086 {
1087     BOOST_ASSERT(buff.size() >= get_size());
1088     serialization_context ctx(buff.data());
1089     ::boost::mysql::detail::serialize(ctx, string_eof{to_string(auth_plugin_data)});
1090 }
1091 
1092 #endif