Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 08:33:51

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