Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:39:17

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