From 7ac3b5ce2bda37a12e11148a55c3000762914901 Mon Sep 17 00:00:00 2001 From: Andrzej Religa Date: Fri, 10 Aug 2018 18:45:46 +0200 Subject: [PATCH 01/88] wl#10799 integrate MySQL Router into MySQL Server repository The patch includes dependent WLs: wl#11127 Packaging Router as part of Server Source Tree wl#11666 Unify MySQL Router cmake scripts with MySQL-server's wl#11129 Allow building Router as part of server source-tree Reviewed-by: Tor Didriksen Revieved-by: Terje Rosten Reviewed-by: Lars Tangvald Reviewed-by: Piotr Obrzut Reviewed-by: Jan Kneschke --- mysqlrouter/mysql_protocol.h | 68 ++ mysqlrouter/mysql_protocol/base_packet.h | 627 ++++++++++++++++++ mysqlrouter/mysql_protocol/constants.h | 194 ++++++ mysqlrouter/mysql_protocol/error_packet.h | 123 ++++ mysqlrouter/mysql_protocol/handshake_packet.h | 345 ++++++++++ 5 files changed, 1357 insertions(+) create mode 100644 mysqlrouter/mysql_protocol.h create mode 100644 mysqlrouter/mysql_protocol/base_packet.h create mode 100644 mysqlrouter/mysql_protocol/constants.h create mode 100644 mysqlrouter/mysql_protocol/error_packet.h create mode 100644 mysqlrouter/mysql_protocol/handshake_packet.h diff --git a/mysqlrouter/mysql_protocol.h b/mysqlrouter/mysql_protocol.h new file mode 100644 index 0000000..023bdbe --- /dev/null +++ b/mysqlrouter/mysql_protocol.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#ifdef mysql_protocol_DEFINE_STATIC +#define MYSQL_PROTOCOL_API +#else +#ifdef mysql_protocol_EXPORTS +#define MYSQL_PROTOCOL_API __declspec(dllexport) +#else +#define MYSQL_PROTOCOL_API __declspec(dllimport) +#endif +#endif +#else +#define MYSQL_PROTOCOL_API +#endif + +#include "mysql_protocol/base_packet.h" +#include "mysql_protocol/constants.h" +#include "mysql_protocol/error_packet.h" +#include "mysql_protocol/handshake_packet.h" + +namespace mysql_protocol { + +/** @class packet_error + * @brief Exception raised for any errors with MySQL packets + * + */ +class MYSQL_PROTOCOL_API packet_error : public std::runtime_error { + public: + explicit packet_error(const std::string &what_arg) + : std::runtime_error(what_arg) {} +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED diff --git a/mysqlrouter/mysql_protocol/base_packet.h b/mysqlrouter/mysql_protocol/base_packet.h new file mode 100644 index 0000000..949c21c --- /dev/null +++ b/mysqlrouter/mysql_protocol/base_packet.h @@ -0,0 +1,627 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "constants.h" +#include "harness_assert.h" + +// GCC 4.8.4 requires all classes to be forward-declared before being used with +// "friend class ", if they're in a different namespace than the +// friender +#ifdef FRIEND_TEST +#include "mysqlrouter/utils.h" // DECLARE_TEST +DECLARE_TEST(HandshakeResponseParseTest, server_does_not_support_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, no_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, bad_payload_length); +DECLARE_TEST(HandshakeResponseParseTest, bad_seq_number); +DECLARE_TEST(HandshakeResponseParseTest, max_packet_size); +DECLARE_TEST(HandshakeResponseParseTest, character_set); +DECLARE_TEST(HandshakeResponseParseTest, reserved); +DECLARE_TEST(HandshakeResponseParseTest, username); +DECLARE_TEST(HandshakeResponseParseTest, auth_response); +DECLARE_TEST(HandshakeResponseParseTest, database); +DECLARE_TEST(HandshakeResponseParseTest, auth_plugin); +DECLARE_TEST(HandshakeResponseParseTest, connection_attrs); +DECLARE_TEST(HandshakeResponseParseTest, all); +#endif + +namespace mysql_protocol { + +/** @class Packet + * @brief Interface to MySQL packets + * + * This class is the base class for all the types of MySQL packets + * such as ErrorPacket and HandshakeResponsePacket. + * + */ +class MYSQL_PROTOCOL_API Packet : public std::vector { + /** @note This class exposes several types of methods for data manipulation. + * + * Packet buffer operations, they work like standard stream operations: + * seek()/tell() - set/get buffer position + * write_*() - write data at current buffer position + * read_*() - read data at current buffer position + * + * Packet buffer operations with specified position: + * read_*_from() - read data from specified buffer position + * + * Packet field setters/getters: + * get_*() - return fields from this class (packet needs to be parsed + * first) set_*() - set fields in this class + */ + + public: + using vector_t = std::vector; + + //////////////////////////////////////////////////////////////////////////////// + // constructors, destructors, assignment operators + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Header length of packets */ + static const unsigned int kHeaderSize{4}; + + /** @brief Default of max_allowed_packet defined by the MySQL Server (2^30) */ + static const unsigned int kMaxAllowedSize{1073741824}; + + /** @brief Constructor */ + Packet() : Packet(0, Capabilities::ALL_ZEROS) {} + + /** @overload + * + * This constructor takes a buffer, stores the data, and tries to get + * information out of the buffer. + * + * When buffer is 4 or bigger, the payload size and sequence ID of the packet + * is read from the first 4 bytes (packet header). + * + * When allow_partial is false, the payload size is not enforced and buffer + * can be smaller than payload size given in the header. Allow partial packets + * can be useful when all you need is to parse the heade + * + * @param buffer Vector of uint8_t + * @param allow_partial Whether to allow buffers which have incomplete payload + */ + explicit Packet(const vector_t &buffer, bool allow_partial = false) + : Packet(buffer, Capabilities::ALL_ZEROS, allow_partial) {} + + /** @overload + * + * @param buffer Vector of uint8_t + * @param capabilities Server or Client capability flags + * @param allow_partial Whether to allow buffers which have incomplete payload + */ + Packet(const vector_t &buffer, Capabilities::Flags capabilities, + bool allow_partial = false); + + /** @overload + * + * @param sequence_id Sequence ID of MySQL packet + */ + explicit Packet(uint8_t sequence_id) + : Packet(sequence_id, Capabilities::ALL_ZEROS) {} + + /** @overload + * + * @param sequence_id Sequence ID of MySQL packet + * @param capabilities Server or Client capability flags + */ + Packet(uint8_t sequence_id, Capabilities::Flags capabilities) + : vector(), + sequence_id_(sequence_id), + payload_size_(0), + capability_flags_(capabilities) {} + + /** @overload */ + Packet(std::initializer_list ilist); + + /** @brief Destructor */ + virtual ~Packet() {} + + /** @brief Copy Constructor */ + Packet(const Packet &) = default; + + /** @brief Move Constructor */ + Packet(Packet &&other) + : vector(std::move(other)), + sequence_id_(other.get_sequence_id()), + payload_size_(other.get_payload_size()), + capability_flags_(other.get_capabilities()) { + other.sequence_id_ = 0; + other.capability_flags_ = Capabilities::ALL_ZEROS; + other.payload_size_ = 0; + } + + /** @brief Copy Assignment */ + Packet &operator=(const Packet &) = default; + + /** @brief Move Assigment */ + Packet &operator=(Packet &&other) { + swap(other); + sequence_id_ = other.sequence_id_; + payload_size_ = other.payload_size_; + capability_flags_ = other.get_capabilities(); + other.sequence_id_ = 0; + other.capability_flags_ = Capabilities::ALL_ZEROS; + other.payload_size_ = 0; + return *this; + } + + //////////////////////////////////////////////////////////////////////////////// + // packet buffer operations: stream interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Sets current read/write position used by read_*()/write_*() calls + */ + void seek(size_t position) const { + if (position > size()) throw std::range_error("seek past EOF"); + position_ = position; + } + + /** @brief Returns current read/write position used by read_*()/write_*() + * calls */ + size_t tell() const { return position_; } + + /** @brief Gets an integral from given packet + * + * Gets an integral from packet buffer at the current position and advances it + * by the length of the read. The size of the integral is deduced from the + * give type but can be overwritten using the size parameter. + * + * Supported are integral of 1, 2, 3, 4, or 8 bytes. To retrieve an 24 bit + * integral it is necessary to use a 32-bit integral type and + * provided the size, for example: + * + * auto id = Packet::read_int_from(buffer, 0, 3); + * + * In MySQL packets, integrals are stored using little-endian format. + * + * @param length size of the integer to parse + * @return integer type + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + * + * @see read_int_from() + */ + template ::value>> + Type read_int(size_t length = sizeof(Type)) const { + Type res = read_int_from(position_, + length); // throws range_error/runtime_error + position_ += length; + return res; + } + + /** @brief Gets a length encoded integer from given packet + * + * Gets a length encoded integer from packet buffer at the current position + * and advances it by the length of the read. Function also returns the length + * of the parsed integer token (you will need to advance your read position by + * this value to get to next field in the packet) + * + * @return uint64_t + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + * + * @see read_lenenc_uint_from() + */ + uint64_t read_lenenc_uint() const; + + /** @brief Gets raw bytes from packet + * + * Gets raw byes from packet buffer at the current position and advances it + * by the length of the read. + * + * @param length Number of bytes to read + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + * (strong exception safety guarrantee) + * + * @see read_bytes_from() + */ + std::vector read_bytes(size_t length) const; + + /** @brief Gets raw bytes from packet using length encoded size + * + * Gets raw bytes with length encoded size from packet buffer at the current + * position and advances it by the length of the read. + * + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + * + * @see read_lenenc_bytes_from() + */ + std::vector read_lenenc_bytes() const; + + /** @brief Gets zero-terminated string from packet + * + * Gets zero-terminated string from packet buffer at the current position and + * advances it by the length of the read. + * + * @return std::string + * + * @see read_string_nul_from() + */ + std::string read_string_nul() const; + + /** @brief Gets raw bytes from packet from position until EOF + * + * Gets raw bytes from packet buffer at the current position and advances it + * by the length of the read. + * + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start beyond EOF, + * std::runtime_error on zero-terminator not found + * (strong exception safety guarrantee) + * + * @see read_bytes_eof_from() + */ + std::vector read_bytes_eof() const; + + /** @brief Packs and adds an integral to the buffer + * + * Packs and adds an integral to the given buffer. + * + * @param value Integral to add to the packet + * @param length Size of the integral (default: size of integral) + * + */ + template ::value>> + void write_int(T value, size_t length = sizeof(T)) { + reserve(size() + length); + while (length-- > 0) { + // Assignment to temporary variable `b` prevents too aggressive inlining + // optimization in some compilers (e.g. GCC 4.9.2 on Solaris, with -O2). + // Without it, `value` wasn't getting updated before push_back() under + // certain conditions, and resulted in filling packet's buffer with + // invalid data. + uint8_t b = static_cast(value); + update_or_append(b); + value = static_cast(value >> CHAR_BIT); + } + } + + /** @brief Packs and adds a length-encoded integral to the buffer + * + * Packs and adds a length-encoded integral to the given buffer. + * + * @param value Integral to add to the packet + * @return Size of the encoded integral (one of: 1, 3, 4 or 9 bytes) + */ + size_t write_lenenc_uint(uint64_t value); + + /** @brief Adds bytes to the given packet + * + * Adds the given bytes to the buffer. + * + * @param bytes Bytes to add to the packet + * + */ + void write_bytes(const Packet::vector_t &bytes) { + write_bytes_impl(bytes.data(), bytes.size()); + } + + /** @brief Adds a string to the given packet + * + * Adds the given string to the buffer. It does not append a zero-terminator + * after this string. + * + * @param str String to add to the packet + */ + void write_string(const std::string &str) { + write_bytes_impl(reinterpret_cast(str.data()), str.size()); + } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ std::string contains + // signed chars + + /** @brief Adds bytes at the end of the buffer + * + * Appends a byte many times to the packet buffer at EOF and advances current + * position by the length of the write (so that it points to EOF once again) + * + * @param count number of times to append the byte + * @param byte byte to append + * + * @throws std::range_error (std::runtime_error) if current position is not + * currently at EOF + */ + void append_bytes(size_t count, uint8_t byte); + + //////////////////////////////////////////////////////////////////////////////// + // packet buffer operations: direct position interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Gets an integral from given packet + * + * Gets an integral from a buffer at the given position. The size of the + * integral is deduced from the give type but can be overwritten using + * the size parameter. + * + * Supported are integral of 1, 2, 3, 4, or 8 bytes. To retrieve an 24 bit + * integral it is necessary to use a 32-bit integral type and + * provided the size, for example: + * + * auto id = Packet::read_int_from(buffer, 0, 3); + * + * In MySQL packets, integrals are stored using little-endian format. + * + * @param position Position where to start reading + * @param length size of the integer to parse + * @return integer type + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + */ + template ::value>> + Type read_int_from(size_t position, size_t length = sizeof(Type)) const { + harness_assert((length >= 1 && length <= 4) || length == 8); + if (position + length > size()) + throw std::range_error("start or end beyond EOF"); + + if (length == 1) { + return static_cast((*this)[position]); + } + + uint64_t result = 0; + auto it = begin() + static_cast(position + length); + while (length-- > 0) { + result <<= 8; + result |= *--it; + } + + return static_cast(result); + } + + /** @brief Gets a length encoded integer from given packet + * + * Function also returns the length of the parsed integer token (you will need + * to advance your read position by this value to get to next field in the + * packet) + * + * @param position Position where to start reading + * @return std::pair + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + */ + std::pair read_lenenc_uint_from(size_t position) const; + + /** @brief Gets a string from packet + * + * Gets a string from the given buffer at the given position. When size is + * not given, we read until the end of the buffer. + * When nil byte is found before we reach the requested size, the string will + * be not be size long (if size is not 0). + * + * When pos is greater than the size of the buffer, an empty string is + * returned. + * + * @param position Position from which to start reading + * @param length Length of the string to read (default 0) + * @return std::string + */ + std::string read_string_from(unsigned long position, + unsigned long length = UINT_MAX) const; + + /** @brief Gets a zero-terminated string from packet + * + * @param position Position from which to start reading + * @return std::string + * + * @throws std::range_error (std::runtime_error) on start beyond EOF, + * std::runtime_error on zero-terminator not found + * (strong exception safety guarrantee) + */ + std::string read_string_nul_from(size_t position) const; + + /** @brief Gets raw bytes from packet + * + * @param position Position from which to start reading + * @param length Number of bytes to read + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + * (strong exception safety guarrantee) + */ + std::vector read_bytes_from(size_t position, size_t length) const; + + /** @brief Gets raw bytes from packet using length encoded size + * + * Function also returns the length of the parsed bytes token (you will need + * to advance your read position by this value to get to next field in the + * packet) + * + * @param position Position from which to start reading + * @return std::pair, size_t> + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + */ + std::pair, size_t> read_lenenc_bytes_from( + size_t position) const; + + /** @brief Gets raw bytes from packet from position until EOF + * + * @param position Position from which to start reading + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start beyond EOF, + * std::runtime_error on zero-terminator not found + * (strong exception safety guarrantee) + */ + std::vector read_bytes_eof_from(size_t position) const; + + //////////////////////////////////////////////////////////////////////////////// + // packet buffer operations: static method interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Gets the packet sequence ID from supplied buffer + * + * @param header 4-byte header + * @return uint8_t + */ + static uint8_t read_sequence_id(const uint8_t header[4]) noexcept { + return header[3]; + } + + /** @brief Gets the payload size from supplied buffer + * + * @param header 4-byte header + * @return uint32_t payload size of the packet + */ + static uint32_t read_payload_size(const uint8_t header[4]) noexcept { + return header[0] + (header[1] << 8) + (header[2] << 16); + } + + //////////////////////////////////////////////////////////////////////////////// + // packet field setter/getter interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Returns header length of MySQL Protocol packet + * + * @return header length (4 bytes) + */ + static constexpr size_t get_header_length() noexcept { return 4; } + + /** @brief Gets the packet sequence ID + * + * @return uint8_t + */ + uint8_t get_sequence_id() const noexcept { return sequence_id_; } + + /** @brief Sets the packet sequence ID + * + * @param id Sequence ID of the packet + */ + void set_sequence_id(uint8_t id) noexcept { sequence_id_ = id; } + + /** @brief Gets server/client capabilities + * + * @return Capabilities + */ + Capabilities::Flags get_capabilities() const noexcept { + return capability_flags_; + } + + /** @brief Gets the payload size + * + * Returns the payload size parsed retrieved from the packet header. + * + * @return uint32_t + */ + uint32_t get_payload_size() const noexcept { return payload_size_; } + + protected: + /** @brief Resets packet + * + * Resets the packet and sets the sequence id. + */ + void reset() { this->assign({0x0, 0x0, 0x0, sequence_id_}); } + + /** @brief Updates payload size in packet header + * + * Updates the size of the payload storing it in the first 3 bytes + * of the packet. This method is called after preparing the packet. + */ + void update_packet_size(); + + /** @brief MySQL packet sequence ID */ + uint8_t sequence_id_; + + /** @brief Payload of the packet */ + std::vector payload_; + + /** @brief Payload size */ + uint32_t payload_size_; + + /** @brief Capability flags */ + Capabilities::Flags capability_flags_; + + /** @brief read/write position for stream operations */ + mutable size_t position_; + + private: + void parse_header(bool allow_partial = false); + + void write_bytes_impl(const unsigned char *bytes, size_t length); + + static inline void update_or_append(std::vector &vec, + size_t &position, uint8_t value) { + harness_assert(position <= vec.size()); // allow write before or at EOF + + if (position < vec.size()) + vec[position] = value; + else + vec.push_back(value); + + position++; + } + + inline void update_or_append(size_t &position, uint8_t value) { + update_or_append(*this, position, value); + } + + inline void update_or_append(uint8_t value) { + update_or_append(position_, value); + } + +#ifdef FRIEND_TEST + FRIEND_TEST(::HandshakeResponseParseTest, + server_does_not_support_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); + FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); + FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); + FRIEND_TEST(::HandshakeResponseParseTest, character_set); + FRIEND_TEST(::HandshakeResponseParseTest, reserved); + FRIEND_TEST(::HandshakeResponseParseTest, username); + FRIEND_TEST(::HandshakeResponseParseTest, auth_response); + FRIEND_TEST(::HandshakeResponseParseTest, database); + FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); + FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); + FRIEND_TEST(::HandshakeResponseParseTest, all); +#endif +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQLV10_BASE_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/constants.h b/mysqlrouter/mysql_protocol/constants.h new file mode 100644 index 0000000..a7a8df2 --- /dev/null +++ b/mysqlrouter/mysql_protocol/constants.h @@ -0,0 +1,194 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED + +#include + +namespace mysql_protocol { + +namespace Capabilities { + +/** Type used to pass capability bitset as one number */ +typedef uint32_t AllFlags; + +/** Type used to pass high/low half of capability bitset as one number */ +typedef uint16_t HalfFlags; + +class Flags { + public: + constexpr Flags() : flags_(0) {} + explicit constexpr Flags(AllFlags flags) : flags_(flags) {} + + Flags &operator=(const Flags &other) = default; + + constexpr bool operator==(const Flags &other) const { + return flags_ == other.flags_; + } + + constexpr bool operator!=(const Flags &other) const { + return flags_ != other.flags_; + } + + constexpr Flags operator|(const Flags &other) const { + return Flags(flags_ | other.flags_); + } + + constexpr Flags operator&(const Flags &other) const { + return Flags(flags_ & other.flags_); + } + + bool test(const Flags &want) const { + return (flags_ & want.flags_) == want.flags_; + } + Flags &set(const Flags &other) { + flags_ |= other.flags_; + return *this; + } + Flags &clear(const Flags &other) { + flags_ &= ~other.flags_; + return *this; + } + Flags &reset() { + flags_ = 0; + return *this; + } + + constexpr AllFlags bits() const { return flags_; } + constexpr HalfFlags high_16_bits() const { + return static_cast(flags_ >> 16); + } + constexpr HalfFlags low_16_bits() const { + return static_cast(flags_ & 0x0000ffff); + } + + Flags &clear_high_16_bits() { + flags_ &= 0x0000ffff; + return *this; + } + Flags &clear_low_16_bits() { + flags_ &= 0xffff0000; + return *this; + } + + private: + AllFlags flags_; +}; + +/** @brief Capability flags passed in handshake packet. + * + * See https://dev.mysql.com/doc/internals/en/capability-flags.html + * See also MySQL Server source include/mysql_com.h + * To search for documentation of a particular flag, prepend CLIENT_ to its name + * (it was removed on purpose to prevent name collisions when including mysql.h) + **/ +static constexpr Flags LONG_PASSWORD(1 << 0); +static constexpr Flags FOUND_ROWS(1 << 1); +static constexpr Flags LONG_FLAG(1 << 2); +static constexpr Flags CONNECT_WITH_DB(1 << 3); + +static constexpr Flags NO_SCHEMA(1 << 4); +static constexpr Flags COMPRESS(1 << 5); +static constexpr Flags ODBC(1 << 6); +static constexpr Flags LOCAL_FILES(1 << 7); + +static constexpr Flags IGNORE_SPACE(1 << 8); +static constexpr Flags PROTOCOL_41( + 1 + << 9); // Server: supports the 4.1 protocol, Client: uses the 4.1 protocol +static constexpr Flags INTERACTIVE(1 << 10); +static constexpr Flags SSL( + 1 << 11); // Server: supports SSL, Client: switch to SSL + +static constexpr Flags SIG_PIPE(1 << 12); +static constexpr Flags TRANSACTIONS(1 << 13); +static constexpr Flags RESERVED_14(1 << 14); // deprecated in 8.0.3 +static constexpr Flags SECURE_CONNECTION(1 << 15); // deprecated in 8.0.3 + +static constexpr Flags MULTI_STATEMENTS(1 << 16); +static constexpr Flags MULTI_RESULTS(1 << 17); +static constexpr Flags MULTI_PS_MULTO_RESULTS(1 << 18); +static constexpr Flags PLUGIN_AUTH(1 << 19); + +static constexpr Flags CONNECT_ATTRS(1 << 20); +static constexpr Flags PLUGIN_AUTH_LENENC_CLIENT_DATA(1 << 21); +static constexpr Flags EXPIRED_PASSWORDS(1 << 22); +static constexpr Flags SESSION_TRACK(1 << 23); + +static constexpr Flags DEPRECATE_EOF(1 << 24); +static constexpr Flags OPTIONAL_RESULTSET_METADATA( + 1 << 25); // \ docs for 5.7 don't +static constexpr Flags SSL_VERIFY_SERVER_CERT(1UL << 30); // > mention these +static constexpr Flags REMEMBER_OPTIONS(1UL << 31); // / + +// other useful flags (our invention, mysql_com.h does not define them) +static constexpr Flags ALL_ZEROS(0U); +static constexpr Flags ALL_ONES(~0U); + +} // namespace Capabilities + +/** @enum Command + * + * Types of the supported commands from the client. + * + **/ +enum Command { + SLEEP = 0x00, + QUIT = 0x01, + INIT_DB = 0x02, + QUERY = 0x03, + FIELD_LIST = 0x04, + CREATE_DB = 0x05, + DROP_DB = 0x06, + REFRESH = 0x07, + SHUTDOWN = 0x08, + STATISTICS = 0x09, + PROCESS_INFO = 0x0a, + CONNECT = 0x0b, + PROCESS_KILL = 0x0c, + DEBUG = 0x0d, + PING = 0x0e, + TIME = 0x0f, + DELAYED_INSERT = 0x10, + CHANGE_USER = 0x11, + BINLOG_DUMP = 0x12, + TABLE_DUMP = 0x13, + CONNECT_OUT = 0x14, + REGISTER_SLAVE = 0x15, + STMT_PREPARE = 0x16, + STMT_EXECUTE = 0x17, + STMT_SEND_LOG_DATA = 0x18, + STMT_CLOSE = 0x19, + STMT_RESET = 0x1a, + SET_OPTION = 0x1b, + STMT_FETCH = 0x1c, + DAEMON = 0x1d, + BINLOG_DUMP_GTID = 0x1e, + RESET_CONNECTION = 0x1f, +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h new file mode 100644 index 0000000..7745b6a --- /dev/null +++ b/mysqlrouter/mysql_protocol/error_packet.h @@ -0,0 +1,123 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED + +#include "base_packet.h" + +namespace mysql_protocol { + +/** @class ErrorPacket + * @brief Creates a MySQL error packet + * + * This class creates a MySQL error packet which is send to the MySQL Client. + * + */ +class MYSQL_PROTOCOL_API ErrorPacket final : public Packet { + public: + /** @brief Constructor + * + * @note The default constructor will set the error code to 1105, message + * "Unknown error", and SQL State "HY000". These values come from the + * MySQL Server server errors. + */ + ErrorPacket() + : Packet(0), code_(1105), message_("Unknown error"), sql_state_("HY000") { + prepare_packet(); + }; + + /** @overload + * + * @param buffer bytes of the error packet + */ + ErrorPacket(const std::vector &buffer) + : ErrorPacket(buffer, Capabilities::ALL_ZEROS) {} + + ErrorPacket(const std::vector &buffer, + Capabilities::Flags capabilities); + + /** @overload + * + * @param sequence_id MySQL Packet number + * @param err_code Error code provided to MySQL client + * @param err_msg Error message provided to MySQL client + * @param sql_state SQL State used in error message + * @param capabilities Server/Client capability flags (default 0) + */ + ErrorPacket(uint8_t sequence_id, uint16_t err_code, + const std::string &err_msg, const std::string &sql_state, + Capabilities::Flags capabilities = Capabilities::ALL_ZEROS); + + /** @brief Gets error code + * + * Gets the MySQL error code of the MySQL error packet. + * + * @return unsigned short + */ + unsigned short get_code() const noexcept { return code_; } + + /** @brief Gets error message + * + * Gets the MySQL error message of the MySQL error packet. + * + * @return const std::string reference + */ + const std::string &get_message() const noexcept { return message_; } + + /** @brief Gets SQL state + * + * Gets the SQL state of the MySQL error packet. + * + * @return const std::string reference + */ + const std::string &get_sql_state() const noexcept { return sql_state_; } + + private: + /** @brief Prepares the packet + * + * Prepares the actual MySQL Error packet and stores it. The header is + * created using the sequence id and the size of the payload. + */ + void prepare_packet(); + + /** @brief Parses the packet + * + * Parses the packet from the given buffer. + */ + void parse_payload(); + + /** @brief MySQL error code */ + unsigned short code_; + + /** @brief MySQL error message */ + std::string message_; + + /** @brief MySQL SQL state */ + std::string sql_state_; +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h new file mode 100644 index 0000000..56c319b --- /dev/null +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -0,0 +1,345 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED + +#include +#include "base_packet.h" + +// GCC 4.8.4 requires all classes to be forward-declared before being used with +// "friend class ", if they're in a different namespace than the +// friender +#ifdef FRIEND_TEST +#include "mysqlrouter/utils.h" // DECLARE_TEST +DECLARE_TEST(HandshakeResponseParseTest, server_does_not_support_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, no_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, bad_payload_length); +DECLARE_TEST(HandshakeResponseParseTest, bad_seq_number); +DECLARE_TEST(HandshakeResponseParseTest, max_packet_size); +DECLARE_TEST(HandshakeResponseParseTest, character_set); +DECLARE_TEST(HandshakeResponseParseTest, reserved); +DECLARE_TEST(HandshakeResponseParseTest, username); +DECLARE_TEST(HandshakeResponseParseTest, auth_response); +DECLARE_TEST(HandshakeResponseParseTest, database); +DECLARE_TEST(HandshakeResponseParseTest, auth_plugin); +DECLARE_TEST(HandshakeResponseParseTest, connection_attrs); +DECLARE_TEST(HandshakeResponseParseTest, all); +#endif + +namespace mysql_protocol { + +/** @brief Default capability flags + * + * Default capability flags + * + * NOTE: do not put inside the class, this makes SunStudio on + * Solaris to generate invalid code + * + */ +static constexpr Capabilities::Flags kDefaultClientCapabilities = + Capabilities::LONG_PASSWORD | Capabilities::LONG_FLAG | + Capabilities::CONNECT_WITH_DB | Capabilities::LOCAL_FILES | + Capabilities::PROTOCOL_41 | Capabilities::TRANSACTIONS | + Capabilities::SECURE_CONNECTION | Capabilities::MULTI_STATEMENTS | + Capabilities::MULTI_RESULTS; + +/** @class HandshakeResponsePacket + * @brief Creates a MySQL handshake response packet + * + * This class creates a MySQL handshake response packet which is send by + * the MySQL client after receiving the server's handshake packet. + * + */ +class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { + public: + /** @brief Constructor + * + * This version of constructor just creates an uninitialized packet + */ + HandshakeResponsePacket() + : Packet(0), + username_(""), + password_(""), + char_set_(8), + auth_plugin_("mysql_native_password"), + auth_response_({}) { + prepare_packet(); + } + + /** @overload + * + * This version of constructor takes in packet bytes, parses it and writes + * results in object's fields + * + * @param buffer Packet payload (including packet header) + * @param server_capabilities Capabilities sent by the server in Handshake + * Packet; see note in parse_payload() + * @param auto_parse_payload Disables automatic parsing of payload if set to + * false Note that header is still parsed (sequence_id_ and payload_size_ are + * set) + * + * @throws std::runtime_error on unrecognised or invalid packet, when parsing + */ + HandshakeResponsePacket( + const std::vector &buffer, bool auto_parse_payload = false, + Capabilities::Flags server_capabilities = Capabilities::ALL_ZEROS) + : Packet(buffer) { + if (auto_parse_payload) parse_payload(server_capabilities); + } + + /** @overload + * + * This version of constructor takes in fields, and generates packet bytes + * + * @param sequence_id MySQL Packet number + * @param auth_response Authentication data from the MySQL server handshake + * @param username MySQL username to use + * @param password MySQL password to use + * @param database MySQL database to use when connecting (default is empty) + * @param char_set MySQL character set code (default 8, latin1) + * @param auth_plugin MySQL authentication plugin name (default + * 'mysql_native_password') + */ + HandshakeResponsePacket( + uint8_t sequence_id, const std::vector &auth_response, + const std::string &username, const std::string &password, + const std::string &database = "", unsigned char char_set = 8, + const std::string &auth_plugin = "mysql_native_password"); + + /** @brief Parses packet payload, results written to object's field + * + * @param server_capabilities Capabilities sent by the server in Handshake + * Packet, see note below + * + * @throws std::runtime_error on unrecognised or invalid packet + * + * @note MySQL Protocol has a quirk: In the Handshake Packet, server sends to + * client its capability flags, then in Handshake Response Packet, client + * sends its own, possibly including some that the server did not advertise. + * Despite advertising these flags unique to client, it does not actually use + * them. This is vital in understanding packets. If data chunk dataX depeneded + * on capability X, then how should a packet be parsed when it comes in? + * {data1, data2, dataX, data3, data4} + * or + * {data1, data2, data3, data4} + * Apparently the latter + */ + void parse_payload(Capabilities::Flags server_capabilities) { + init_parser_if_not_initialized(); + parser_->parse(server_capabilities); + } + + /** @brief returns username specified in the packet */ + const std::string &get_username() const { return username_; } + + /** @brief returns database name specified in the packet */ + const std::string &get_database() const { return database_; } + + /** @brief returns character set specified in the packet */ + uint8_t get_character_set() const { return char_set_; } + + /** @brief returns auth-plugin-name specified in the packet */ + const std::string &get_auth_plugin() const { return auth_plugin_; } + + /** @brief returns auth-plugin-data specified in the packet */ + const std::vector &get_auth_response() const { + return auth_response_; + } + + /** @brief returns max packet size specified in the packet */ + uint32_t get_max_packet_size() const { return max_packet_size_; } + + /** @brief (debug tool) parse packet contents and print this info on stdout */ + void debug_dump() { + init_parser_if_not_initialized(); + parser_->debug_dump(); + } + + private: + class Parser; + + /** @brief Prepares the packet + * + * Prepares the actual MySQL Error packet and stores it. The header is + * created using the sequence id and the size of the payload. + */ + void prepare_packet(); + + /** @brief Initializes Parser needed to parse the packet payload */ + void init_parser_if_not_initialized() { + if (!parser_) { + if (Parser41::is_protocol41(*this)) { + parser_.reset(new Parser41(*this)); + } else if (Parser320::is_protocol320(*this)) { + parser_.reset(new Parser320(*this)); + } else { + assert(0); + } + } + } + + /** @brief MySQL username */ + std::string username_; + + /** @brief MySQL password */ + std::string password_; + + /** @brief MySQL database */ + std::string database_; + + /** @brief MySQL character set */ + unsigned char char_set_; + + /** @brief MySQL authentication plugin name */ + std::string auth_plugin_; + + /** @brief MySQL auth-response */ + std::vector auth_response_; + + /** @brief Max size that of a command packet that the client wants to send to + * the server */ + uint32_t max_packet_size_; + + /** @brief Parser used to parse this packet */ + std::unique_ptr parser_; + + class MYSQL_PROTOCOL_API Parser { + public: + virtual ~Parser() = default; + virtual void parse(Capabilities::Flags server_capabilities) = 0; + + // debug tools + static std::string bytes2str(const uint8_t *bytes, size_t length, + size_t bytes_per_group = 4) noexcept; + virtual void debug_dump() const = 0; + }; + + class MYSQL_PROTOCOL_API Parser41 : public Parser { + public: + Parser41(HandshakeResponsePacket &packet) : packet_(packet) {} + + /** @brief Tests if handshake response packet has PROTOCOL_41 flag set + * + * This is a very simple method, it only checks that single flag and does + * nothing else (in particular, it doesn't perform any kind of validation) + */ + static bool is_protocol41(const HandshakeResponsePacket &packet); + + /** @brief Parses handshake response packet + * + * This method assumes that the current packet is a PROTOCOL41 handshake + * response. + * + * @param server_capabilities Capability flags of the server. Client's flags + * will be &-ed with them before applying rules for packet parsing. + * @throws std::runtime_error on unrecognised or invalid packet + */ + void parse(Capabilities::Flags server_capabilities) override; + + // debug tools + void debug_dump() const noexcept override; + + private: + /** @brief Helper functions called by parse() + * + * All these methods throw std::runtime_error on parse errors; in + * particular, std::range_error (std::runtime_error specialization) is + * thrown on EOF + * */ + void part1_max_packet_size(); + void part2_character_set(); + void part3_reserved(); + void part4_username(); + void part5_auth_response(); + void part6_database(); + void part7_auth_plugin(); + void part8_connection_attrs(); + + HandshakeResponsePacket &packet_; + Capabilities::Flags effective_capability_flags_; + +#ifdef FRIEND_TEST + FRIEND_TEST(::HandshakeResponseParseTest, + server_does_not_support_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); + FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); + FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); + FRIEND_TEST(::HandshakeResponseParseTest, character_set); + FRIEND_TEST(::HandshakeResponseParseTest, reserved); + FRIEND_TEST(::HandshakeResponseParseTest, username); + FRIEND_TEST(::HandshakeResponseParseTest, auth_response); + FRIEND_TEST(::HandshakeResponseParseTest, database); + FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); + FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); + FRIEND_TEST(::HandshakeResponseParseTest, all); +#endif + }; + + class MYSQL_PROTOCOL_API Parser320 : public Parser { + public: + Parser320(HandshakeResponsePacket &packet) : packet_(packet) {} + + /** @brief Tests if handshake response packet DOES NOT have PROTOCOL_41 flag + * set + * + * This is a very simple method, it only checks that single flag and does + * nothing else (in particular, it doesn't perform any kind of validation) + */ + static bool is_protocol320(const HandshakeResponsePacket &packet); + + /** @brief Parses handshake response packet + * + * Currently not implemented + */ + void parse(Capabilities::Flags server_capabilities) override; + void debug_dump() const override; + + HandshakeResponsePacket &packet_; + Capabilities::Flags effective_capability_flags_; + }; + +#ifdef FRIEND_TEST + FRIEND_TEST(::HandshakeResponseParseTest, + server_does_not_support_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); + FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); + FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); + FRIEND_TEST(::HandshakeResponseParseTest, character_set); + FRIEND_TEST(::HandshakeResponseParseTest, reserved); + FRIEND_TEST(::HandshakeResponseParseTest, username); + FRIEND_TEST(::HandshakeResponseParseTest, auth_response); + FRIEND_TEST(::HandshakeResponseParseTest, database); + FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); + FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); + FRIEND_TEST(::HandshakeResponseParseTest, all); +#endif + +}; // class HandshakeResponsePacket + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED From bd08fe9126bfa6b31d8b1272bcad77ffb7479995 Mon Sep 17 00:00:00 2001 From: Andrzej Religa Date: Fri, 10 Aug 2018 18:45:46 +0200 Subject: [PATCH 02/88] wl#10799 integrate MySQL Router into MySQL Server repository The patch includes dependent WLs: wl#11127 Packaging Router as part of Server Source Tree wl#11666 Unify MySQL Router cmake scripts with MySQL-server's wl#11129 Allow building Router as part of server source-tree Reviewed-by: Tor Didriksen Revieved-by: Terje Rosten Reviewed-by: Lars Tangvald Reviewed-by: Piotr Obrzut Reviewed-by: Jan Kneschke --- mysqlrouter/mysql_protocol.h | 68 ++ mysqlrouter/mysql_protocol/base_packet.h | 627 ++++++++++++++++++ mysqlrouter/mysql_protocol/constants.h | 194 ++++++ mysqlrouter/mysql_protocol/error_packet.h | 123 ++++ mysqlrouter/mysql_protocol/handshake_packet.h | 345 ++++++++++ 5 files changed, 1357 insertions(+) create mode 100644 mysqlrouter/mysql_protocol.h create mode 100644 mysqlrouter/mysql_protocol/base_packet.h create mode 100644 mysqlrouter/mysql_protocol/constants.h create mode 100644 mysqlrouter/mysql_protocol/error_packet.h create mode 100644 mysqlrouter/mysql_protocol/handshake_packet.h diff --git a/mysqlrouter/mysql_protocol.h b/mysqlrouter/mysql_protocol.h new file mode 100644 index 0000000..023bdbe --- /dev/null +++ b/mysqlrouter/mysql_protocol.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#ifdef mysql_protocol_DEFINE_STATIC +#define MYSQL_PROTOCOL_API +#else +#ifdef mysql_protocol_EXPORTS +#define MYSQL_PROTOCOL_API __declspec(dllexport) +#else +#define MYSQL_PROTOCOL_API __declspec(dllimport) +#endif +#endif +#else +#define MYSQL_PROTOCOL_API +#endif + +#include "mysql_protocol/base_packet.h" +#include "mysql_protocol/constants.h" +#include "mysql_protocol/error_packet.h" +#include "mysql_protocol/handshake_packet.h" + +namespace mysql_protocol { + +/** @class packet_error + * @brief Exception raised for any errors with MySQL packets + * + */ +class MYSQL_PROTOCOL_API packet_error : public std::runtime_error { + public: + explicit packet_error(const std::string &what_arg) + : std::runtime_error(what_arg) {} +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED diff --git a/mysqlrouter/mysql_protocol/base_packet.h b/mysqlrouter/mysql_protocol/base_packet.h new file mode 100644 index 0000000..949c21c --- /dev/null +++ b/mysqlrouter/mysql_protocol/base_packet.h @@ -0,0 +1,627 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "constants.h" +#include "harness_assert.h" + +// GCC 4.8.4 requires all classes to be forward-declared before being used with +// "friend class ", if they're in a different namespace than the +// friender +#ifdef FRIEND_TEST +#include "mysqlrouter/utils.h" // DECLARE_TEST +DECLARE_TEST(HandshakeResponseParseTest, server_does_not_support_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, no_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, bad_payload_length); +DECLARE_TEST(HandshakeResponseParseTest, bad_seq_number); +DECLARE_TEST(HandshakeResponseParseTest, max_packet_size); +DECLARE_TEST(HandshakeResponseParseTest, character_set); +DECLARE_TEST(HandshakeResponseParseTest, reserved); +DECLARE_TEST(HandshakeResponseParseTest, username); +DECLARE_TEST(HandshakeResponseParseTest, auth_response); +DECLARE_TEST(HandshakeResponseParseTest, database); +DECLARE_TEST(HandshakeResponseParseTest, auth_plugin); +DECLARE_TEST(HandshakeResponseParseTest, connection_attrs); +DECLARE_TEST(HandshakeResponseParseTest, all); +#endif + +namespace mysql_protocol { + +/** @class Packet + * @brief Interface to MySQL packets + * + * This class is the base class for all the types of MySQL packets + * such as ErrorPacket and HandshakeResponsePacket. + * + */ +class MYSQL_PROTOCOL_API Packet : public std::vector { + /** @note This class exposes several types of methods for data manipulation. + * + * Packet buffer operations, they work like standard stream operations: + * seek()/tell() - set/get buffer position + * write_*() - write data at current buffer position + * read_*() - read data at current buffer position + * + * Packet buffer operations with specified position: + * read_*_from() - read data from specified buffer position + * + * Packet field setters/getters: + * get_*() - return fields from this class (packet needs to be parsed + * first) set_*() - set fields in this class + */ + + public: + using vector_t = std::vector; + + //////////////////////////////////////////////////////////////////////////////// + // constructors, destructors, assignment operators + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Header length of packets */ + static const unsigned int kHeaderSize{4}; + + /** @brief Default of max_allowed_packet defined by the MySQL Server (2^30) */ + static const unsigned int kMaxAllowedSize{1073741824}; + + /** @brief Constructor */ + Packet() : Packet(0, Capabilities::ALL_ZEROS) {} + + /** @overload + * + * This constructor takes a buffer, stores the data, and tries to get + * information out of the buffer. + * + * When buffer is 4 or bigger, the payload size and sequence ID of the packet + * is read from the first 4 bytes (packet header). + * + * When allow_partial is false, the payload size is not enforced and buffer + * can be smaller than payload size given in the header. Allow partial packets + * can be useful when all you need is to parse the heade + * + * @param buffer Vector of uint8_t + * @param allow_partial Whether to allow buffers which have incomplete payload + */ + explicit Packet(const vector_t &buffer, bool allow_partial = false) + : Packet(buffer, Capabilities::ALL_ZEROS, allow_partial) {} + + /** @overload + * + * @param buffer Vector of uint8_t + * @param capabilities Server or Client capability flags + * @param allow_partial Whether to allow buffers which have incomplete payload + */ + Packet(const vector_t &buffer, Capabilities::Flags capabilities, + bool allow_partial = false); + + /** @overload + * + * @param sequence_id Sequence ID of MySQL packet + */ + explicit Packet(uint8_t sequence_id) + : Packet(sequence_id, Capabilities::ALL_ZEROS) {} + + /** @overload + * + * @param sequence_id Sequence ID of MySQL packet + * @param capabilities Server or Client capability flags + */ + Packet(uint8_t sequence_id, Capabilities::Flags capabilities) + : vector(), + sequence_id_(sequence_id), + payload_size_(0), + capability_flags_(capabilities) {} + + /** @overload */ + Packet(std::initializer_list ilist); + + /** @brief Destructor */ + virtual ~Packet() {} + + /** @brief Copy Constructor */ + Packet(const Packet &) = default; + + /** @brief Move Constructor */ + Packet(Packet &&other) + : vector(std::move(other)), + sequence_id_(other.get_sequence_id()), + payload_size_(other.get_payload_size()), + capability_flags_(other.get_capabilities()) { + other.sequence_id_ = 0; + other.capability_flags_ = Capabilities::ALL_ZEROS; + other.payload_size_ = 0; + } + + /** @brief Copy Assignment */ + Packet &operator=(const Packet &) = default; + + /** @brief Move Assigment */ + Packet &operator=(Packet &&other) { + swap(other); + sequence_id_ = other.sequence_id_; + payload_size_ = other.payload_size_; + capability_flags_ = other.get_capabilities(); + other.sequence_id_ = 0; + other.capability_flags_ = Capabilities::ALL_ZEROS; + other.payload_size_ = 0; + return *this; + } + + //////////////////////////////////////////////////////////////////////////////// + // packet buffer operations: stream interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Sets current read/write position used by read_*()/write_*() calls + */ + void seek(size_t position) const { + if (position > size()) throw std::range_error("seek past EOF"); + position_ = position; + } + + /** @brief Returns current read/write position used by read_*()/write_*() + * calls */ + size_t tell() const { return position_; } + + /** @brief Gets an integral from given packet + * + * Gets an integral from packet buffer at the current position and advances it + * by the length of the read. The size of the integral is deduced from the + * give type but can be overwritten using the size parameter. + * + * Supported are integral of 1, 2, 3, 4, or 8 bytes. To retrieve an 24 bit + * integral it is necessary to use a 32-bit integral type and + * provided the size, for example: + * + * auto id = Packet::read_int_from(buffer, 0, 3); + * + * In MySQL packets, integrals are stored using little-endian format. + * + * @param length size of the integer to parse + * @return integer type + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + * + * @see read_int_from() + */ + template ::value>> + Type read_int(size_t length = sizeof(Type)) const { + Type res = read_int_from(position_, + length); // throws range_error/runtime_error + position_ += length; + return res; + } + + /** @brief Gets a length encoded integer from given packet + * + * Gets a length encoded integer from packet buffer at the current position + * and advances it by the length of the read. Function also returns the length + * of the parsed integer token (you will need to advance your read position by + * this value to get to next field in the packet) + * + * @return uint64_t + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + * + * @see read_lenenc_uint_from() + */ + uint64_t read_lenenc_uint() const; + + /** @brief Gets raw bytes from packet + * + * Gets raw byes from packet buffer at the current position and advances it + * by the length of the read. + * + * @param length Number of bytes to read + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + * (strong exception safety guarrantee) + * + * @see read_bytes_from() + */ + std::vector read_bytes(size_t length) const; + + /** @brief Gets raw bytes from packet using length encoded size + * + * Gets raw bytes with length encoded size from packet buffer at the current + * position and advances it by the length of the read. + * + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + * + * @see read_lenenc_bytes_from() + */ + std::vector read_lenenc_bytes() const; + + /** @brief Gets zero-terminated string from packet + * + * Gets zero-terminated string from packet buffer at the current position and + * advances it by the length of the read. + * + * @return std::string + * + * @see read_string_nul_from() + */ + std::string read_string_nul() const; + + /** @brief Gets raw bytes from packet from position until EOF + * + * Gets raw bytes from packet buffer at the current position and advances it + * by the length of the read. + * + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start beyond EOF, + * std::runtime_error on zero-terminator not found + * (strong exception safety guarrantee) + * + * @see read_bytes_eof_from() + */ + std::vector read_bytes_eof() const; + + /** @brief Packs and adds an integral to the buffer + * + * Packs and adds an integral to the given buffer. + * + * @param value Integral to add to the packet + * @param length Size of the integral (default: size of integral) + * + */ + template ::value>> + void write_int(T value, size_t length = sizeof(T)) { + reserve(size() + length); + while (length-- > 0) { + // Assignment to temporary variable `b` prevents too aggressive inlining + // optimization in some compilers (e.g. GCC 4.9.2 on Solaris, with -O2). + // Without it, `value` wasn't getting updated before push_back() under + // certain conditions, and resulted in filling packet's buffer with + // invalid data. + uint8_t b = static_cast(value); + update_or_append(b); + value = static_cast(value >> CHAR_BIT); + } + } + + /** @brief Packs and adds a length-encoded integral to the buffer + * + * Packs and adds a length-encoded integral to the given buffer. + * + * @param value Integral to add to the packet + * @return Size of the encoded integral (one of: 1, 3, 4 or 9 bytes) + */ + size_t write_lenenc_uint(uint64_t value); + + /** @brief Adds bytes to the given packet + * + * Adds the given bytes to the buffer. + * + * @param bytes Bytes to add to the packet + * + */ + void write_bytes(const Packet::vector_t &bytes) { + write_bytes_impl(bytes.data(), bytes.size()); + } + + /** @brief Adds a string to the given packet + * + * Adds the given string to the buffer. It does not append a zero-terminator + * after this string. + * + * @param str String to add to the packet + */ + void write_string(const std::string &str) { + write_bytes_impl(reinterpret_cast(str.data()), str.size()); + } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ std::string contains + // signed chars + + /** @brief Adds bytes at the end of the buffer + * + * Appends a byte many times to the packet buffer at EOF and advances current + * position by the length of the write (so that it points to EOF once again) + * + * @param count number of times to append the byte + * @param byte byte to append + * + * @throws std::range_error (std::runtime_error) if current position is not + * currently at EOF + */ + void append_bytes(size_t count, uint8_t byte); + + //////////////////////////////////////////////////////////////////////////////// + // packet buffer operations: direct position interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Gets an integral from given packet + * + * Gets an integral from a buffer at the given position. The size of the + * integral is deduced from the give type but can be overwritten using + * the size parameter. + * + * Supported are integral of 1, 2, 3, 4, or 8 bytes. To retrieve an 24 bit + * integral it is necessary to use a 32-bit integral type and + * provided the size, for example: + * + * auto id = Packet::read_int_from(buffer, 0, 3); + * + * In MySQL packets, integrals are stored using little-endian format. + * + * @param position Position where to start reading + * @param length size of the integer to parse + * @return integer type + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + */ + template ::value>> + Type read_int_from(size_t position, size_t length = sizeof(Type)) const { + harness_assert((length >= 1 && length <= 4) || length == 8); + if (position + length > size()) + throw std::range_error("start or end beyond EOF"); + + if (length == 1) { + return static_cast((*this)[position]); + } + + uint64_t result = 0; + auto it = begin() + static_cast(position + length); + while (length-- > 0) { + result <<= 8; + result |= *--it; + } + + return static_cast(result); + } + + /** @brief Gets a length encoded integer from given packet + * + * Function also returns the length of the parsed integer token (you will need + * to advance your read position by this value to get to next field in the + * packet) + * + * @param position Position where to start reading + * @return std::pair + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + */ + std::pair read_lenenc_uint_from(size_t position) const; + + /** @brief Gets a string from packet + * + * Gets a string from the given buffer at the given position. When size is + * not given, we read until the end of the buffer. + * When nil byte is found before we reach the requested size, the string will + * be not be size long (if size is not 0). + * + * When pos is greater than the size of the buffer, an empty string is + * returned. + * + * @param position Position from which to start reading + * @param length Length of the string to read (default 0) + * @return std::string + */ + std::string read_string_from(unsigned long position, + unsigned long length = UINT_MAX) const; + + /** @brief Gets a zero-terminated string from packet + * + * @param position Position from which to start reading + * @return std::string + * + * @throws std::range_error (std::runtime_error) on start beyond EOF, + * std::runtime_error on zero-terminator not found + * (strong exception safety guarrantee) + */ + std::string read_string_nul_from(size_t position) const; + + /** @brief Gets raw bytes from packet + * + * @param position Position from which to start reading + * @param length Number of bytes to read + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF + * (strong exception safety guarrantee) + */ + std::vector read_bytes_from(size_t position, size_t length) const; + + /** @brief Gets raw bytes from packet using length encoded size + * + * Function also returns the length of the parsed bytes token (you will need + * to advance your read position by this value to get to next field in the + * packet) + * + * @param position Position from which to start reading + * @return std::pair, size_t> + * + * @throws std::range_error (std::runtime_error) on start or end beyond EOF, + * std::runtime_error on bad first byte (which determines int length) + * (strong exception safety guarrantee) + */ + std::pair, size_t> read_lenenc_bytes_from( + size_t position) const; + + /** @brief Gets raw bytes from packet from position until EOF + * + * @param position Position from which to start reading + * @return std::vector + * + * @throws std::range_error (std::runtime_error) on start beyond EOF, + * std::runtime_error on zero-terminator not found + * (strong exception safety guarrantee) + */ + std::vector read_bytes_eof_from(size_t position) const; + + //////////////////////////////////////////////////////////////////////////////// + // packet buffer operations: static method interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Gets the packet sequence ID from supplied buffer + * + * @param header 4-byte header + * @return uint8_t + */ + static uint8_t read_sequence_id(const uint8_t header[4]) noexcept { + return header[3]; + } + + /** @brief Gets the payload size from supplied buffer + * + * @param header 4-byte header + * @return uint32_t payload size of the packet + */ + static uint32_t read_payload_size(const uint8_t header[4]) noexcept { + return header[0] + (header[1] << 8) + (header[2] << 16); + } + + //////////////////////////////////////////////////////////////////////////////// + // packet field setter/getter interface + //////////////////////////////////////////////////////////////////////////////// + + /** @brief Returns header length of MySQL Protocol packet + * + * @return header length (4 bytes) + */ + static constexpr size_t get_header_length() noexcept { return 4; } + + /** @brief Gets the packet sequence ID + * + * @return uint8_t + */ + uint8_t get_sequence_id() const noexcept { return sequence_id_; } + + /** @brief Sets the packet sequence ID + * + * @param id Sequence ID of the packet + */ + void set_sequence_id(uint8_t id) noexcept { sequence_id_ = id; } + + /** @brief Gets server/client capabilities + * + * @return Capabilities + */ + Capabilities::Flags get_capabilities() const noexcept { + return capability_flags_; + } + + /** @brief Gets the payload size + * + * Returns the payload size parsed retrieved from the packet header. + * + * @return uint32_t + */ + uint32_t get_payload_size() const noexcept { return payload_size_; } + + protected: + /** @brief Resets packet + * + * Resets the packet and sets the sequence id. + */ + void reset() { this->assign({0x0, 0x0, 0x0, sequence_id_}); } + + /** @brief Updates payload size in packet header + * + * Updates the size of the payload storing it in the first 3 bytes + * of the packet. This method is called after preparing the packet. + */ + void update_packet_size(); + + /** @brief MySQL packet sequence ID */ + uint8_t sequence_id_; + + /** @brief Payload of the packet */ + std::vector payload_; + + /** @brief Payload size */ + uint32_t payload_size_; + + /** @brief Capability flags */ + Capabilities::Flags capability_flags_; + + /** @brief read/write position for stream operations */ + mutable size_t position_; + + private: + void parse_header(bool allow_partial = false); + + void write_bytes_impl(const unsigned char *bytes, size_t length); + + static inline void update_or_append(std::vector &vec, + size_t &position, uint8_t value) { + harness_assert(position <= vec.size()); // allow write before or at EOF + + if (position < vec.size()) + vec[position] = value; + else + vec.push_back(value); + + position++; + } + + inline void update_or_append(size_t &position, uint8_t value) { + update_or_append(*this, position, value); + } + + inline void update_or_append(uint8_t value) { + update_or_append(position_, value); + } + +#ifdef FRIEND_TEST + FRIEND_TEST(::HandshakeResponseParseTest, + server_does_not_support_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); + FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); + FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); + FRIEND_TEST(::HandshakeResponseParseTest, character_set); + FRIEND_TEST(::HandshakeResponseParseTest, reserved); + FRIEND_TEST(::HandshakeResponseParseTest, username); + FRIEND_TEST(::HandshakeResponseParseTest, auth_response); + FRIEND_TEST(::HandshakeResponseParseTest, database); + FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); + FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); + FRIEND_TEST(::HandshakeResponseParseTest, all); +#endif +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQLV10_BASE_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/constants.h b/mysqlrouter/mysql_protocol/constants.h new file mode 100644 index 0000000..a7a8df2 --- /dev/null +++ b/mysqlrouter/mysql_protocol/constants.h @@ -0,0 +1,194 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED + +#include + +namespace mysql_protocol { + +namespace Capabilities { + +/** Type used to pass capability bitset as one number */ +typedef uint32_t AllFlags; + +/** Type used to pass high/low half of capability bitset as one number */ +typedef uint16_t HalfFlags; + +class Flags { + public: + constexpr Flags() : flags_(0) {} + explicit constexpr Flags(AllFlags flags) : flags_(flags) {} + + Flags &operator=(const Flags &other) = default; + + constexpr bool operator==(const Flags &other) const { + return flags_ == other.flags_; + } + + constexpr bool operator!=(const Flags &other) const { + return flags_ != other.flags_; + } + + constexpr Flags operator|(const Flags &other) const { + return Flags(flags_ | other.flags_); + } + + constexpr Flags operator&(const Flags &other) const { + return Flags(flags_ & other.flags_); + } + + bool test(const Flags &want) const { + return (flags_ & want.flags_) == want.flags_; + } + Flags &set(const Flags &other) { + flags_ |= other.flags_; + return *this; + } + Flags &clear(const Flags &other) { + flags_ &= ~other.flags_; + return *this; + } + Flags &reset() { + flags_ = 0; + return *this; + } + + constexpr AllFlags bits() const { return flags_; } + constexpr HalfFlags high_16_bits() const { + return static_cast(flags_ >> 16); + } + constexpr HalfFlags low_16_bits() const { + return static_cast(flags_ & 0x0000ffff); + } + + Flags &clear_high_16_bits() { + flags_ &= 0x0000ffff; + return *this; + } + Flags &clear_low_16_bits() { + flags_ &= 0xffff0000; + return *this; + } + + private: + AllFlags flags_; +}; + +/** @brief Capability flags passed in handshake packet. + * + * See https://dev.mysql.com/doc/internals/en/capability-flags.html + * See also MySQL Server source include/mysql_com.h + * To search for documentation of a particular flag, prepend CLIENT_ to its name + * (it was removed on purpose to prevent name collisions when including mysql.h) + **/ +static constexpr Flags LONG_PASSWORD(1 << 0); +static constexpr Flags FOUND_ROWS(1 << 1); +static constexpr Flags LONG_FLAG(1 << 2); +static constexpr Flags CONNECT_WITH_DB(1 << 3); + +static constexpr Flags NO_SCHEMA(1 << 4); +static constexpr Flags COMPRESS(1 << 5); +static constexpr Flags ODBC(1 << 6); +static constexpr Flags LOCAL_FILES(1 << 7); + +static constexpr Flags IGNORE_SPACE(1 << 8); +static constexpr Flags PROTOCOL_41( + 1 + << 9); // Server: supports the 4.1 protocol, Client: uses the 4.1 protocol +static constexpr Flags INTERACTIVE(1 << 10); +static constexpr Flags SSL( + 1 << 11); // Server: supports SSL, Client: switch to SSL + +static constexpr Flags SIG_PIPE(1 << 12); +static constexpr Flags TRANSACTIONS(1 << 13); +static constexpr Flags RESERVED_14(1 << 14); // deprecated in 8.0.3 +static constexpr Flags SECURE_CONNECTION(1 << 15); // deprecated in 8.0.3 + +static constexpr Flags MULTI_STATEMENTS(1 << 16); +static constexpr Flags MULTI_RESULTS(1 << 17); +static constexpr Flags MULTI_PS_MULTO_RESULTS(1 << 18); +static constexpr Flags PLUGIN_AUTH(1 << 19); + +static constexpr Flags CONNECT_ATTRS(1 << 20); +static constexpr Flags PLUGIN_AUTH_LENENC_CLIENT_DATA(1 << 21); +static constexpr Flags EXPIRED_PASSWORDS(1 << 22); +static constexpr Flags SESSION_TRACK(1 << 23); + +static constexpr Flags DEPRECATE_EOF(1 << 24); +static constexpr Flags OPTIONAL_RESULTSET_METADATA( + 1 << 25); // \ docs for 5.7 don't +static constexpr Flags SSL_VERIFY_SERVER_CERT(1UL << 30); // > mention these +static constexpr Flags REMEMBER_OPTIONS(1UL << 31); // / + +// other useful flags (our invention, mysql_com.h does not define them) +static constexpr Flags ALL_ZEROS(0U); +static constexpr Flags ALL_ONES(~0U); + +} // namespace Capabilities + +/** @enum Command + * + * Types of the supported commands from the client. + * + **/ +enum Command { + SLEEP = 0x00, + QUIT = 0x01, + INIT_DB = 0x02, + QUERY = 0x03, + FIELD_LIST = 0x04, + CREATE_DB = 0x05, + DROP_DB = 0x06, + REFRESH = 0x07, + SHUTDOWN = 0x08, + STATISTICS = 0x09, + PROCESS_INFO = 0x0a, + CONNECT = 0x0b, + PROCESS_KILL = 0x0c, + DEBUG = 0x0d, + PING = 0x0e, + TIME = 0x0f, + DELAYED_INSERT = 0x10, + CHANGE_USER = 0x11, + BINLOG_DUMP = 0x12, + TABLE_DUMP = 0x13, + CONNECT_OUT = 0x14, + REGISTER_SLAVE = 0x15, + STMT_PREPARE = 0x16, + STMT_EXECUTE = 0x17, + STMT_SEND_LOG_DATA = 0x18, + STMT_CLOSE = 0x19, + STMT_RESET = 0x1a, + SET_OPTION = 0x1b, + STMT_FETCH = 0x1c, + DAEMON = 0x1d, + BINLOG_DUMP_GTID = 0x1e, + RESET_CONNECTION = 0x1f, +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h new file mode 100644 index 0000000..7745b6a --- /dev/null +++ b/mysqlrouter/mysql_protocol/error_packet.h @@ -0,0 +1,123 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED + +#include "base_packet.h" + +namespace mysql_protocol { + +/** @class ErrorPacket + * @brief Creates a MySQL error packet + * + * This class creates a MySQL error packet which is send to the MySQL Client. + * + */ +class MYSQL_PROTOCOL_API ErrorPacket final : public Packet { + public: + /** @brief Constructor + * + * @note The default constructor will set the error code to 1105, message + * "Unknown error", and SQL State "HY000". These values come from the + * MySQL Server server errors. + */ + ErrorPacket() + : Packet(0), code_(1105), message_("Unknown error"), sql_state_("HY000") { + prepare_packet(); + }; + + /** @overload + * + * @param buffer bytes of the error packet + */ + ErrorPacket(const std::vector &buffer) + : ErrorPacket(buffer, Capabilities::ALL_ZEROS) {} + + ErrorPacket(const std::vector &buffer, + Capabilities::Flags capabilities); + + /** @overload + * + * @param sequence_id MySQL Packet number + * @param err_code Error code provided to MySQL client + * @param err_msg Error message provided to MySQL client + * @param sql_state SQL State used in error message + * @param capabilities Server/Client capability flags (default 0) + */ + ErrorPacket(uint8_t sequence_id, uint16_t err_code, + const std::string &err_msg, const std::string &sql_state, + Capabilities::Flags capabilities = Capabilities::ALL_ZEROS); + + /** @brief Gets error code + * + * Gets the MySQL error code of the MySQL error packet. + * + * @return unsigned short + */ + unsigned short get_code() const noexcept { return code_; } + + /** @brief Gets error message + * + * Gets the MySQL error message of the MySQL error packet. + * + * @return const std::string reference + */ + const std::string &get_message() const noexcept { return message_; } + + /** @brief Gets SQL state + * + * Gets the SQL state of the MySQL error packet. + * + * @return const std::string reference + */ + const std::string &get_sql_state() const noexcept { return sql_state_; } + + private: + /** @brief Prepares the packet + * + * Prepares the actual MySQL Error packet and stores it. The header is + * created using the sequence id and the size of the payload. + */ + void prepare_packet(); + + /** @brief Parses the packet + * + * Parses the packet from the given buffer. + */ + void parse_payload(); + + /** @brief MySQL error code */ + unsigned short code_; + + /** @brief MySQL error message */ + std::string message_; + + /** @brief MySQL SQL state */ + std::string sql_state_; +}; + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h new file mode 100644 index 0000000..56c319b --- /dev/null +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -0,0 +1,345 @@ +/* + Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED +#define MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED + +#include +#include "base_packet.h" + +// GCC 4.8.4 requires all classes to be forward-declared before being used with +// "friend class ", if they're in a different namespace than the +// friender +#ifdef FRIEND_TEST +#include "mysqlrouter/utils.h" // DECLARE_TEST +DECLARE_TEST(HandshakeResponseParseTest, server_does_not_support_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, no_PROTOCOL_41); +DECLARE_TEST(HandshakeResponseParseTest, bad_payload_length); +DECLARE_TEST(HandshakeResponseParseTest, bad_seq_number); +DECLARE_TEST(HandshakeResponseParseTest, max_packet_size); +DECLARE_TEST(HandshakeResponseParseTest, character_set); +DECLARE_TEST(HandshakeResponseParseTest, reserved); +DECLARE_TEST(HandshakeResponseParseTest, username); +DECLARE_TEST(HandshakeResponseParseTest, auth_response); +DECLARE_TEST(HandshakeResponseParseTest, database); +DECLARE_TEST(HandshakeResponseParseTest, auth_plugin); +DECLARE_TEST(HandshakeResponseParseTest, connection_attrs); +DECLARE_TEST(HandshakeResponseParseTest, all); +#endif + +namespace mysql_protocol { + +/** @brief Default capability flags + * + * Default capability flags + * + * NOTE: do not put inside the class, this makes SunStudio on + * Solaris to generate invalid code + * + */ +static constexpr Capabilities::Flags kDefaultClientCapabilities = + Capabilities::LONG_PASSWORD | Capabilities::LONG_FLAG | + Capabilities::CONNECT_WITH_DB | Capabilities::LOCAL_FILES | + Capabilities::PROTOCOL_41 | Capabilities::TRANSACTIONS | + Capabilities::SECURE_CONNECTION | Capabilities::MULTI_STATEMENTS | + Capabilities::MULTI_RESULTS; + +/** @class HandshakeResponsePacket + * @brief Creates a MySQL handshake response packet + * + * This class creates a MySQL handshake response packet which is send by + * the MySQL client after receiving the server's handshake packet. + * + */ +class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { + public: + /** @brief Constructor + * + * This version of constructor just creates an uninitialized packet + */ + HandshakeResponsePacket() + : Packet(0), + username_(""), + password_(""), + char_set_(8), + auth_plugin_("mysql_native_password"), + auth_response_({}) { + prepare_packet(); + } + + /** @overload + * + * This version of constructor takes in packet bytes, parses it and writes + * results in object's fields + * + * @param buffer Packet payload (including packet header) + * @param server_capabilities Capabilities sent by the server in Handshake + * Packet; see note in parse_payload() + * @param auto_parse_payload Disables automatic parsing of payload if set to + * false Note that header is still parsed (sequence_id_ and payload_size_ are + * set) + * + * @throws std::runtime_error on unrecognised or invalid packet, when parsing + */ + HandshakeResponsePacket( + const std::vector &buffer, bool auto_parse_payload = false, + Capabilities::Flags server_capabilities = Capabilities::ALL_ZEROS) + : Packet(buffer) { + if (auto_parse_payload) parse_payload(server_capabilities); + } + + /** @overload + * + * This version of constructor takes in fields, and generates packet bytes + * + * @param sequence_id MySQL Packet number + * @param auth_response Authentication data from the MySQL server handshake + * @param username MySQL username to use + * @param password MySQL password to use + * @param database MySQL database to use when connecting (default is empty) + * @param char_set MySQL character set code (default 8, latin1) + * @param auth_plugin MySQL authentication plugin name (default + * 'mysql_native_password') + */ + HandshakeResponsePacket( + uint8_t sequence_id, const std::vector &auth_response, + const std::string &username, const std::string &password, + const std::string &database = "", unsigned char char_set = 8, + const std::string &auth_plugin = "mysql_native_password"); + + /** @brief Parses packet payload, results written to object's field + * + * @param server_capabilities Capabilities sent by the server in Handshake + * Packet, see note below + * + * @throws std::runtime_error on unrecognised or invalid packet + * + * @note MySQL Protocol has a quirk: In the Handshake Packet, server sends to + * client its capability flags, then in Handshake Response Packet, client + * sends its own, possibly including some that the server did not advertise. + * Despite advertising these flags unique to client, it does not actually use + * them. This is vital in understanding packets. If data chunk dataX depeneded + * on capability X, then how should a packet be parsed when it comes in? + * {data1, data2, dataX, data3, data4} + * or + * {data1, data2, data3, data4} + * Apparently the latter + */ + void parse_payload(Capabilities::Flags server_capabilities) { + init_parser_if_not_initialized(); + parser_->parse(server_capabilities); + } + + /** @brief returns username specified in the packet */ + const std::string &get_username() const { return username_; } + + /** @brief returns database name specified in the packet */ + const std::string &get_database() const { return database_; } + + /** @brief returns character set specified in the packet */ + uint8_t get_character_set() const { return char_set_; } + + /** @brief returns auth-plugin-name specified in the packet */ + const std::string &get_auth_plugin() const { return auth_plugin_; } + + /** @brief returns auth-plugin-data specified in the packet */ + const std::vector &get_auth_response() const { + return auth_response_; + } + + /** @brief returns max packet size specified in the packet */ + uint32_t get_max_packet_size() const { return max_packet_size_; } + + /** @brief (debug tool) parse packet contents and print this info on stdout */ + void debug_dump() { + init_parser_if_not_initialized(); + parser_->debug_dump(); + } + + private: + class Parser; + + /** @brief Prepares the packet + * + * Prepares the actual MySQL Error packet and stores it. The header is + * created using the sequence id and the size of the payload. + */ + void prepare_packet(); + + /** @brief Initializes Parser needed to parse the packet payload */ + void init_parser_if_not_initialized() { + if (!parser_) { + if (Parser41::is_protocol41(*this)) { + parser_.reset(new Parser41(*this)); + } else if (Parser320::is_protocol320(*this)) { + parser_.reset(new Parser320(*this)); + } else { + assert(0); + } + } + } + + /** @brief MySQL username */ + std::string username_; + + /** @brief MySQL password */ + std::string password_; + + /** @brief MySQL database */ + std::string database_; + + /** @brief MySQL character set */ + unsigned char char_set_; + + /** @brief MySQL authentication plugin name */ + std::string auth_plugin_; + + /** @brief MySQL auth-response */ + std::vector auth_response_; + + /** @brief Max size that of a command packet that the client wants to send to + * the server */ + uint32_t max_packet_size_; + + /** @brief Parser used to parse this packet */ + std::unique_ptr parser_; + + class MYSQL_PROTOCOL_API Parser { + public: + virtual ~Parser() = default; + virtual void parse(Capabilities::Flags server_capabilities) = 0; + + // debug tools + static std::string bytes2str(const uint8_t *bytes, size_t length, + size_t bytes_per_group = 4) noexcept; + virtual void debug_dump() const = 0; + }; + + class MYSQL_PROTOCOL_API Parser41 : public Parser { + public: + Parser41(HandshakeResponsePacket &packet) : packet_(packet) {} + + /** @brief Tests if handshake response packet has PROTOCOL_41 flag set + * + * This is a very simple method, it only checks that single flag and does + * nothing else (in particular, it doesn't perform any kind of validation) + */ + static bool is_protocol41(const HandshakeResponsePacket &packet); + + /** @brief Parses handshake response packet + * + * This method assumes that the current packet is a PROTOCOL41 handshake + * response. + * + * @param server_capabilities Capability flags of the server. Client's flags + * will be &-ed with them before applying rules for packet parsing. + * @throws std::runtime_error on unrecognised or invalid packet + */ + void parse(Capabilities::Flags server_capabilities) override; + + // debug tools + void debug_dump() const noexcept override; + + private: + /** @brief Helper functions called by parse() + * + * All these methods throw std::runtime_error on parse errors; in + * particular, std::range_error (std::runtime_error specialization) is + * thrown on EOF + * */ + void part1_max_packet_size(); + void part2_character_set(); + void part3_reserved(); + void part4_username(); + void part5_auth_response(); + void part6_database(); + void part7_auth_plugin(); + void part8_connection_attrs(); + + HandshakeResponsePacket &packet_; + Capabilities::Flags effective_capability_flags_; + +#ifdef FRIEND_TEST + FRIEND_TEST(::HandshakeResponseParseTest, + server_does_not_support_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); + FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); + FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); + FRIEND_TEST(::HandshakeResponseParseTest, character_set); + FRIEND_TEST(::HandshakeResponseParseTest, reserved); + FRIEND_TEST(::HandshakeResponseParseTest, username); + FRIEND_TEST(::HandshakeResponseParseTest, auth_response); + FRIEND_TEST(::HandshakeResponseParseTest, database); + FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); + FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); + FRIEND_TEST(::HandshakeResponseParseTest, all); +#endif + }; + + class MYSQL_PROTOCOL_API Parser320 : public Parser { + public: + Parser320(HandshakeResponsePacket &packet) : packet_(packet) {} + + /** @brief Tests if handshake response packet DOES NOT have PROTOCOL_41 flag + * set + * + * This is a very simple method, it only checks that single flag and does + * nothing else (in particular, it doesn't perform any kind of validation) + */ + static bool is_protocol320(const HandshakeResponsePacket &packet); + + /** @brief Parses handshake response packet + * + * Currently not implemented + */ + void parse(Capabilities::Flags server_capabilities) override; + void debug_dump() const override; + + HandshakeResponsePacket &packet_; + Capabilities::Flags effective_capability_flags_; + }; + +#ifdef FRIEND_TEST + FRIEND_TEST(::HandshakeResponseParseTest, + server_does_not_support_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); + FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); + FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); + FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); + FRIEND_TEST(::HandshakeResponseParseTest, character_set); + FRIEND_TEST(::HandshakeResponseParseTest, reserved); + FRIEND_TEST(::HandshakeResponseParseTest, username); + FRIEND_TEST(::HandshakeResponseParseTest, auth_response); + FRIEND_TEST(::HandshakeResponseParseTest, database); + FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); + FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); + FRIEND_TEST(::HandshakeResponseParseTest, all); +#endif + +}; // class HandshakeResponsePacket + +} // namespace mysql_protocol + +#endif // MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED From bb14e58d188ba6cff4d34c6784385a7bf419daa4 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Wed, 5 Sep 2018 16:46:45 +0200 Subject: [PATCH 03/88] Bug #26399073: MYSQL DOES NOT COMPILE WITH CLANG ON WINDOWS [noclose] Fix all instances of -Wextra-semi warnings (mostly using Clang's Fix-It function), so that we can enable the warning. Change-Id: I0c64be78b276efb545bae259b08d7278fa5e64e4 --- mysqlrouter/mysql_protocol/error_packet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h index 7745b6a..3a3fe48 100644 --- a/mysqlrouter/mysql_protocol/error_packet.h +++ b/mysqlrouter/mysql_protocol/error_packet.h @@ -46,7 +46,7 @@ class MYSQL_PROTOCOL_API ErrorPacket final : public Packet { ErrorPacket() : Packet(0), code_(1105), message_("Unknown error"), sql_state_("HY000") { prepare_packet(); - }; + } /** @overload * From fab0848e550e9fddbf7ef1bfb1907e8f306e43d7 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 6 Sep 2018 16:22:37 +0200 Subject: [PATCH 04/88] Bug #26399073: MYSQL DOES NOT COMPILE WITH CLANG ON WINDOWS [noclose] Fix all instances of -Wmissing-noreturn (for Clang) and -Wsuggest-attribute=noreturn (for GCC), so that we can enable those warnings. Change-Id: I0b26870c5e68587ca9c0ee3c116108abb1c3c2bb --- mysqlrouter/mysql_protocol/handshake_packet.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h index 56c319b..d5ee1b7 100644 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -314,8 +314,8 @@ class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { * * Currently not implemented */ - void parse(Capabilities::Flags server_capabilities) override; - void debug_dump() const override; + [[noreturn]] void parse(Capabilities::Flags server_capabilities) override; + [[noreturn]] void debug_dump() const override; HandshakeResponsePacket &packet_; Capabilities::Flags effective_capability_flags_; From 0201cebd582a574106954687940e63a90a140ce6 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 7 Sep 2018 16:42:01 +0200 Subject: [PATCH 05/88] Revert "Bug #26399073: MYSQL DOES NOT COMPILE WITH CLANG ON WINDOWS [noclose]" This reverts commit 30c9c892a662129195fe4b7069154592b96880e4. --- mysqlrouter/mysql_protocol/handshake_packet.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h index d5ee1b7..56c319b 100644 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -314,8 +314,8 @@ class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { * * Currently not implemented */ - [[noreturn]] void parse(Capabilities::Flags server_capabilities) override; - [[noreturn]] void debug_dump() const override; + void parse(Capabilities::Flags server_capabilities) override; + void debug_dump() const override; HandshakeResponsePacket &packet_; Capabilities::Flags effective_capability_flags_; From 86509cd3aaa9fc57203ae61a1b0d7956d8321d17 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 25 Sep 2018 09:50:13 +0200 Subject: [PATCH 06/88] Bug#28699828: FIX -WDEPRECATED WARNINGS Enable -Wdeprecated when building with Clang. This compiler option warns about using deprecated C++ features. Fix warnings reported by -Wdeprecated. Change-Id: If1a1c476516d67008cd2b8d883bd754ca60aa303 --- mysqlrouter/mysql_protocol/constants.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mysqlrouter/mysql_protocol/constants.h b/mysqlrouter/mysql_protocol/constants.h index a7a8df2..3d68c8c 100644 --- a/mysqlrouter/mysql_protocol/constants.h +++ b/mysqlrouter/mysql_protocol/constants.h @@ -42,6 +42,11 @@ class Flags { constexpr Flags() : flags_(0) {} explicit constexpr Flags(AllFlags flags) : flags_(flags) {} +// Developer Studio does not like this even if it's deprecated not to have it. +#ifndef __SUNPRO_CC + constexpr Flags(const Flags &) = default; +#endif + Flags &operator=(const Flags &other) = default; constexpr bool operator==(const Flags &other) const { From 4a45eb1f59cf53d5983eddabd748033c2c092de5 Mon Sep 17 00:00:00 2001 From: Pawel Mroszczyk Date: Mon, 22 Oct 2018 06:30:48 -0400 Subject: [PATCH 07/88] BUG#28793334 Router process is aborted after receiving invalid packet When Router receives an invalid packet during the handshake phase of the connection, it will throw an exception. However, this exception was not caught anywhere, causing Router process to terminate. This patch fixes this - now the Router will deal with this situation like it deals with all other handshake-phase errors (it will close the client connection and increment the error counter on the user side, and hijack the handshake on the Server side and complete it so the Server does not increment its error counter with respect to Router) RB: 20797 Reviewed by: Andrzej Religa --- mysqlrouter/mysql_protocol/base_packet.h | 3 +++ mysqlrouter/mysql_protocol/error_packet.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/mysqlrouter/mysql_protocol/base_packet.h b/mysqlrouter/mysql_protocol/base_packet.h index 949c21c..10e649f 100644 --- a/mysqlrouter/mysql_protocol/base_packet.h +++ b/mysqlrouter/mysql_protocol/base_packet.h @@ -122,6 +122,7 @@ class MYSQL_PROTOCOL_API Packet : public std::vector { * @param capabilities Server or Client capability flags * @param allow_partial Whether to allow buffers which have incomplete payload */ + /** @throws mysql_protocol::packet_error */ Packet(const vector_t &buffer, Capabilities::Flags capabilities, bool allow_partial = false); @@ -144,6 +145,7 @@ class MYSQL_PROTOCOL_API Packet : public std::vector { capability_flags_(capabilities) {} /** @overload */ + /** @throws mysql_protocol::packet_error */ Packet(std::initializer_list ilist); /** @brief Destructor */ @@ -580,6 +582,7 @@ class MYSQL_PROTOCOL_API Packet : public std::vector { mutable size_t position_; private: + /** @throws mysql_protocol::packet_error */ void parse_header(bool allow_partial = false); void write_bytes_impl(const unsigned char *bytes, size_t length); diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h index 3a3fe48..fc030c0 100644 --- a/mysqlrouter/mysql_protocol/error_packet.h +++ b/mysqlrouter/mysql_protocol/error_packet.h @@ -51,6 +51,7 @@ class MYSQL_PROTOCOL_API ErrorPacket final : public Packet { /** @overload * * @param buffer bytes of the error packet + * @throws mysql_protocol::packet_error */ ErrorPacket(const std::vector &buffer) : ErrorPacket(buffer, Capabilities::ALL_ZEROS) {} @@ -105,6 +106,8 @@ class MYSQL_PROTOCOL_API ErrorPacket final : public Packet { /** @brief Parses the packet * * Parses the packet from the given buffer. + * + * @throws mysql_protocol::packet_error */ void parse_payload(); From 38de8aaa814f3052613d11827db6bdb67364528c Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 7 Nov 2018 15:13:10 +0100 Subject: [PATCH 08/88] Bug#28892711 MYSQL BUILD FAILED ON MSVC ON WINDOWS include for files using std::runtime_error. Change-Id: I44c7ba0f929a18ee4cc21e7f731010558eb68183 --- mysqlrouter/mysql_protocol.h | 1 + mysqlrouter/mysql_protocol/handshake_packet.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mysqlrouter/mysql_protocol.h b/mysqlrouter/mysql_protocol.h index 023bdbe..ba85e87 100644 --- a/mysqlrouter/mysql_protocol.h +++ b/mysqlrouter/mysql_protocol.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h index 56c319b..7572edb 100644 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -26,6 +26,8 @@ #define MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED #include +#include + #include "base_packet.h" // GCC 4.8.4 requires all classes to be forward-declared before being used with From 31435e968feea8d2b2f462450f2315ab1760b926 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 28 Nov 2018 13:22:11 +0100 Subject: [PATCH 09/88] Bug#29003649 ROUTER FAILS BUILD AND REPORTS WARNINGS WITH CLANG7 ON WINDOWS * fixed case libnames * fixed compile-error on windows due to wrong type for socket error: non-constant-expression cannot be narrowed from type 'int' to 'SOCKET' (aka 'unsigned long long') in initializer list [-Wc++11-narrowing] {sock, POLLOUT, 0}, * fixed wrong dllexports * made message-check more resilent to localisation * error-msg may be localiced * error-msg may change with different CRT versions * fixed const char * comparison with == * fixed clang-warning for RVO/std::move() * explicitly define copy-constructor if destructor is declared * fixes clang-7 warning: definition of implicit copy assignment operator for 'RandomGeneratorInterface' is deprecated because it has a user-declared destructor [-Wdeprecated] * fixed format warning for DWORDs * fixed error-msg handling on windows err_code was passed to get_last_error(), but then ignore. Instead GetLastError() was called all the time which may belong to another intemediate function-call that set the errno. * removed unused code * removed unused lambda captures warning: lambda capture 'this' is not used [-Wunused-lambda-capture] * removed unused variable --- mysqlrouter/mysql_protocol/handshake_packet.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h index 7572edb..574cf7d 100644 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -229,7 +229,13 @@ class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { class MYSQL_PROTOCOL_API Parser { public: + Parser() = default; + // disable copy as it isn't needed right now. Feel free to enable + // must be explicitly defined though. + explicit Parser(const Parser &) = delete; + Parser &operator=(const Parser &) = delete; virtual ~Parser() = default; + virtual void parse(Capabilities::Flags server_capabilities) = 0; // debug tools From ae510a0f6ed646a6ebffb73b6247d4b1a8aafc74 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Sun, 14 Jun 2020 12:44:04 +0200 Subject: [PATCH 10/88] Bug#31503429 export-header mysql-protocol Problem ======= The 'mysql_protocol' library of the router uses handwritten export-header to export symbols into DLLs on windows. cmake provides a GenerateExportHeader() function which: - generates export header - allows to control the visibility of symbols on non-windows platforms too. Change ====== - use cmake's GenerateExportHeadera - replace MYSQL_PROTOCOL_API with MYSQL_PROTOCOL_EXPORT RB: 24637 --- mysqlrouter/mysql_protocol.h | 19 +++---------------- mysqlrouter/mysql_protocol/base_packet.h | 5 +++-- mysqlrouter/mysql_protocol/error_packet.h | 6 ++++-- mysqlrouter/mysql_protocol/handshake_packet.h | 10 +++++----- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/mysqlrouter/mysql_protocol.h b/mysqlrouter/mysql_protocol.h index ba85e87..f7e620f 100644 --- a/mysqlrouter/mysql_protocol.h +++ b/mysqlrouter/mysql_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2016, 2020, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -33,24 +33,11 @@ #include #include -#ifdef _WIN32 -#ifdef mysql_protocol_DEFINE_STATIC -#define MYSQL_PROTOCOL_API -#else -#ifdef mysql_protocol_EXPORTS -#define MYSQL_PROTOCOL_API __declspec(dllexport) -#else -#define MYSQL_PROTOCOL_API __declspec(dllimport) -#endif -#endif -#else -#define MYSQL_PROTOCOL_API -#endif - #include "mysql_protocol/base_packet.h" #include "mysql_protocol/constants.h" #include "mysql_protocol/error_packet.h" #include "mysql_protocol/handshake_packet.h" +#include "mysqlrouter/mysql_protocol_export.h" namespace mysql_protocol { @@ -58,7 +45,7 @@ namespace mysql_protocol { * @brief Exception raised for any errors with MySQL packets * */ -class MYSQL_PROTOCOL_API packet_error : public std::runtime_error { +class MYSQL_PROTOCOL_EXPORT packet_error : public std::runtime_error { public: explicit packet_error(const std::string &what_arg) : std::runtime_error(what_arg) {} diff --git a/mysqlrouter/mysql_protocol/base_packet.h b/mysqlrouter/mysql_protocol/base_packet.h index 10e649f..c436181 100644 --- a/mysqlrouter/mysql_protocol/base_packet.h +++ b/mysqlrouter/mysql_protocol/base_packet.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2016, 2020, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -36,6 +36,7 @@ #include "constants.h" #include "harness_assert.h" +#include "mysqlrouter/mysql_protocol_export.h" // MYSQL_PROTOCOL_EXPORT // GCC 4.8.4 requires all classes to be forward-declared before being used with // "friend class ", if they're in a different namespace than the @@ -66,7 +67,7 @@ namespace mysql_protocol { * such as ErrorPacket and HandshakeResponsePacket. * */ -class MYSQL_PROTOCOL_API Packet : public std::vector { +class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { /** @note This class exposes several types of methods for data manipulation. * * Packet buffer operations, they work like standard stream operations: diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h index fc030c0..a9a661b 100644 --- a/mysqlrouter/mysql_protocol/error_packet.h +++ b/mysqlrouter/mysql_protocol/error_packet.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2016, 2020, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -27,6 +27,8 @@ #include "base_packet.h" +#include "mysqlrouter/mysql_protocol_export.h" // MYSQL_PROTOCOL_EXPORT + namespace mysql_protocol { /** @class ErrorPacket @@ -35,7 +37,7 @@ namespace mysql_protocol { * This class creates a MySQL error packet which is send to the MySQL Client. * */ -class MYSQL_PROTOCOL_API ErrorPacket final : public Packet { +class MYSQL_PROTOCOL_EXPORT ErrorPacket final : public Packet { public: /** @brief Constructor * diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h index 574cf7d..6b00099 100644 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2016, 2020, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -74,7 +74,7 @@ static constexpr Capabilities::Flags kDefaultClientCapabilities = * the MySQL client after receiving the server's handshake packet. * */ -class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { +class MYSQL_PROTOCOL_EXPORT HandshakeResponsePacket final : public Packet { public: /** @brief Constructor * @@ -227,7 +227,7 @@ class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { /** @brief Parser used to parse this packet */ std::unique_ptr parser_; - class MYSQL_PROTOCOL_API Parser { + class MYSQL_PROTOCOL_EXPORT Parser { public: Parser() = default; // disable copy as it isn't needed right now. Feel free to enable @@ -244,7 +244,7 @@ class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { virtual void debug_dump() const = 0; }; - class MYSQL_PROTOCOL_API Parser41 : public Parser { + class MYSQL_PROTOCOL_EXPORT Parser41 : public Parser { public: Parser41(HandshakeResponsePacket &packet) : packet_(packet) {} @@ -306,7 +306,7 @@ class MYSQL_PROTOCOL_API HandshakeResponsePacket final : public Packet { #endif }; - class MYSQL_PROTOCOL_API Parser320 : public Parser { + class MYSQL_PROTOCOL_EXPORT Parser320 : public Parser { public: Parser320(HandshakeResponsePacket &packet) : packet_(packet) {} From c9734500cac7fb211d575cd42d71d555aed5026c Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 16 Jun 2020 14:00:17 +0200 Subject: [PATCH 11/88] Bug#31503554 do not inherit from std::vector Problem ======= The BasePacket class from MySQL Router's 'mysql_protocol' library currently inherits from std::vector<>. As the Packet classes are dll-exported it later leads to linker errors when the std::vector methods are exported into both the mysql_protocol class and the msvcrt. Furthermore, there is no need for using inheritance here. Change ====== - instead inheriting from std::vector, just use the vector as private member. - only include headers that are needed. RB: 24638 --- mysqlrouter/mysql_protocol/base_packet.h | 70 +++++++++++-------- mysqlrouter/mysql_protocol/error_packet.h | 11 ++- mysqlrouter/mysql_protocol/handshake_packet.h | 12 ++-- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/mysqlrouter/mysql_protocol/base_packet.h b/mysqlrouter/mysql_protocol/base_packet.h index c436181..0dd12e2 100644 --- a/mysqlrouter/mysql_protocol/base_packet.h +++ b/mysqlrouter/mysql_protocol/base_packet.h @@ -25,13 +25,13 @@ #ifndef MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED #define MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED -#include -#include -#include -#include -#include +#include // CHAR_BIT +#include // size_t +#include +#include // range_error #include -#include +#include // enable_if +#include // move, pair #include #include "constants.h" @@ -67,7 +67,7 @@ namespace mysql_protocol { * such as ErrorPacket and HandshakeResponsePacket. * */ -class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { +class MYSQL_PROTOCOL_EXPORT Packet { /** @note This class exposes several types of methods for data manipulation. * * Packet buffer operations, they work like standard stream operations: @@ -85,6 +85,8 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { public: using vector_t = std::vector; + using iterator = vector_t::iterator; + using const_iterator = vector_t::const_iterator; //////////////////////////////////////////////////////////////////////////////// // constructors, destructors, assignment operators @@ -114,8 +116,8 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { * @param buffer Vector of uint8_t * @param allow_partial Whether to allow buffers which have incomplete payload */ - explicit Packet(const vector_t &buffer, bool allow_partial = false) - : Packet(buffer, Capabilities::ALL_ZEROS, allow_partial) {} + explicit Packet(vector_t buffer, bool allow_partial = false) + : Packet(std::move(buffer), Capabilities::ALL_ZEROS, allow_partial) {} /** @overload * @@ -124,7 +126,7 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { * @param allow_partial Whether to allow buffers which have incomplete payload */ /** @throws mysql_protocol::packet_error */ - Packet(const vector_t &buffer, Capabilities::Flags capabilities, + Packet(vector_t buffer, Capabilities::Flags capabilities, bool allow_partial = false); /** @overload @@ -140,24 +142,21 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { * @param capabilities Server or Client capability flags */ Packet(uint8_t sequence_id, Capabilities::Flags capabilities) - : vector(), - sequence_id_(sequence_id), - payload_size_(0), - capability_flags_(capabilities) {} + : sequence_id_(sequence_id), capability_flags_(capabilities) {} /** @overload */ /** @throws mysql_protocol::packet_error */ Packet(std::initializer_list ilist); /** @brief Destructor */ - virtual ~Packet() {} + virtual ~Packet() = default; /** @brief Copy Constructor */ Packet(const Packet &) = default; /** @brief Move Constructor */ Packet(Packet &&other) - : vector(std::move(other)), + : msg_(std::move(other.msg_)), sequence_id_(other.get_sequence_id()), payload_size_(other.get_payload_size()), capability_flags_(other.get_capabilities()) { @@ -171,7 +170,7 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { /** @brief Move Assigment */ Packet &operator=(Packet &&other) { - swap(other); + msg_ = std::move(other.msg_); sequence_id_ = other.sequence_id_; payload_size_ = other.payload_size_; capability_flags_ = other.get_capabilities(); @@ -188,7 +187,7 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { /** @brief Sets current read/write position used by read_*()/write_*() calls */ void seek(size_t position) const { - if (position > size()) throw std::range_error("seek past EOF"); + if (position > msg_.size()) throw std::range_error("seek past EOF"); position_ = position; } @@ -309,14 +308,14 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { */ template ::value>> void write_int(T value, size_t length = sizeof(T)) { - reserve(size() + length); + msg_.reserve(msg_.size() + length); while (length-- > 0) { // Assignment to temporary variable `b` prevents too aggressive inlining // optimization in some compilers (e.g. GCC 4.9.2 on Solaris, with -O2). // Without it, `value` wasn't getting updated before push_back() under // certain conditions, and resulted in filling packet's buffer with // invalid data. - uint8_t b = static_cast(value); + const auto b = static_cast(value); update_or_append(b); value = static_cast(value >> CHAR_BIT); } @@ -342,6 +341,15 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { write_bytes_impl(bytes.data(), bytes.size()); } + const vector_t &message() const { return msg_; } + + size_t size() const { return msg_.size(); } + + iterator begin() { return msg_.begin(); } + const_iterator begin() const { return msg_.begin(); } + iterator end() { return msg_.end(); } + const_iterator end() const { return msg_.end(); } + /** @brief Adds a string to the given packet * * Adds the given string to the buffer. It does not append a zero-terminator @@ -395,15 +403,15 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { typename = std::enable_if::value>> Type read_int_from(size_t position, size_t length = sizeof(Type)) const { harness_assert((length >= 1 && length <= 4) || length == 8); - if (position + length > size()) + if (position + length > msg_.size()) throw std::range_error("start or end beyond EOF"); if (length == 1) { - return static_cast((*this)[position]); + return static_cast(msg_[position]); } uint64_t result = 0; - auto it = begin() + static_cast(position + length); + auto it = msg_.begin() + static_cast(position + length); while (length-- > 0) { result <<= 8; result |= *--it; @@ -558,7 +566,7 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { * * Resets the packet and sets the sequence id. */ - void reset() { this->assign({0x0, 0x0, 0x0, sequence_id_}); } + void reset() { msg_.assign({0x0, 0x0, 0x0, sequence_id_}); } /** @brief Updates payload size in packet header * @@ -567,20 +575,22 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { */ void update_packet_size(); + vector_t msg_; + /** @brief MySQL packet sequence ID */ - uint8_t sequence_id_; + uint8_t sequence_id_{}; /** @brief Payload of the packet */ std::vector payload_; /** @brief Payload size */ - uint32_t payload_size_; + uint32_t payload_size_{}; /** @brief Capability flags */ Capabilities::Flags capability_flags_; /** @brief read/write position for stream operations */ - mutable size_t position_; + mutable size_t position_{}; private: /** @throws mysql_protocol::packet_error */ @@ -601,7 +611,7 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { } inline void update_or_append(size_t &position, uint8_t value) { - update_or_append(*this, position, value); + update_or_append(msg_, position, value); } inline void update_or_append(uint8_t value) { @@ -626,6 +636,10 @@ class MYSQL_PROTOCOL_EXPORT Packet : public std::vector { #endif }; +inline bool operator==(const Packet &a, const Packet &b) { + return a.message() == b.message(); +} + } // namespace mysql_protocol #endif // MYSQLROUTER_MYSQLV10_BASE_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h index a9a661b..50c6d06 100644 --- a/mysqlrouter/mysql_protocol/error_packet.h +++ b/mysqlrouter/mysql_protocol/error_packet.h @@ -55,11 +55,10 @@ class MYSQL_PROTOCOL_EXPORT ErrorPacket final : public Packet { * @param buffer bytes of the error packet * @throws mysql_protocol::packet_error */ - ErrorPacket(const std::vector &buffer) - : ErrorPacket(buffer, Capabilities::ALL_ZEROS) {} + ErrorPacket(std::vector buffer) + : ErrorPacket(std::move(buffer), Capabilities::ALL_ZEROS) {} - ErrorPacket(const std::vector &buffer, - Capabilities::Flags capabilities); + ErrorPacket(std::vector buffer, Capabilities::Flags capabilities); /** @overload * @@ -69,8 +68,8 @@ class MYSQL_PROTOCOL_EXPORT ErrorPacket final : public Packet { * @param sql_state SQL State used in error message * @param capabilities Server/Client capability flags (default 0) */ - ErrorPacket(uint8_t sequence_id, uint16_t err_code, - const std::string &err_msg, const std::string &sql_state, + ErrorPacket(uint8_t sequence_id, uint16_t err_code, std::string err_msg, + std::string sql_state, Capabilities::Flags capabilities = Capabilities::ALL_ZEROS); /** @brief Gets error code diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h index 6b00099..01d7e42 100644 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ b/mysqlrouter/mysql_protocol/handshake_packet.h @@ -105,7 +105,7 @@ class MYSQL_PROTOCOL_EXPORT HandshakeResponsePacket final : public Packet { * @throws std::runtime_error on unrecognised or invalid packet, when parsing */ HandshakeResponsePacket( - const std::vector &buffer, bool auto_parse_payload = false, + std::vector buffer, bool auto_parse_payload = false, Capabilities::Flags server_capabilities = Capabilities::ALL_ZEROS) : Packet(buffer) { if (auto_parse_payload) parse_payload(server_capabilities); @@ -124,11 +124,11 @@ class MYSQL_PROTOCOL_EXPORT HandshakeResponsePacket final : public Packet { * @param auth_plugin MySQL authentication plugin name (default * 'mysql_native_password') */ - HandshakeResponsePacket( - uint8_t sequence_id, const std::vector &auth_response, - const std::string &username, const std::string &password, - const std::string &database = "", unsigned char char_set = 8, - const std::string &auth_plugin = "mysql_native_password"); + HandshakeResponsePacket(uint8_t sequence_id, + std::vector auth_response, + std::string username, std::string password, + std::string database = "", unsigned char char_set = 8, + std::string auth_plugin = "mysql_native_password"); /** @brief Parses packet payload, results written to object's field * From 18f73410f855480c71bd959adfc13ef4cff9e58f Mon Sep 17 00:00:00 2001 From: Daniel Blanchard Date: Tue, 1 Sep 2020 11:10:30 +0100 Subject: [PATCH 12/88] Bug#31820147: FIX MSVC C4275 WARNINGS Suppress the C4275 warnings at the specific points in the source where they are currently generated when building on Windows. RB: 25043 --- mysqlrouter/mysql_protocol.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mysqlrouter/mysql_protocol.h b/mysqlrouter/mysql_protocol.h index f7e620f..e772761 100644 --- a/mysqlrouter/mysql_protocol.h +++ b/mysqlrouter/mysql_protocol.h @@ -33,6 +33,7 @@ #include #include +#include "my_compiler.h" #include "mysql_protocol/base_packet.h" #include "mysql_protocol/constants.h" #include "mysql_protocol/error_packet.h" @@ -45,11 +46,14 @@ namespace mysql_protocol { * @brief Exception raised for any errors with MySQL packets * */ +MY_COMPILER_DIAGNOSTIC_PUSH() +MY_COMPILER_MSVC_DIAGNOSTIC_IGNORE(4275) class MYSQL_PROTOCOL_EXPORT packet_error : public std::runtime_error { public: explicit packet_error(const std::string &what_arg) : std::runtime_error(what_arg) {} }; +MY_COMPILER_DIAGNOSTIC_POP() } // namespace mysql_protocol From c2b444df074a49fbccab9cd83a7e88eead1b952d Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 23 Oct 2020 23:02:44 +0200 Subject: [PATCH 13/88] WL#12012 tls-endpoint, codec [1/7] Change ====== Classic Protocol implementation - POD types - encoding - decoding RB: 25178 --- mysqlrouter/classic_protocol.h | 34 + mysqlrouter/classic_protocol_codec.h | 37 + mysqlrouter/classic_protocol_codec_base.h | 375 +++ mysqlrouter/classic_protocol_codec_error.h | 91 + mysqlrouter/classic_protocol_codec_frame.h | 191 ++ mysqlrouter/classic_protocol_codec_message.h | 2277 +++++++++++++++++ .../classic_protocol_codec_session_track.h | 388 +++ mysqlrouter/classic_protocol_codec_wire.h | 441 ++++ mysqlrouter/classic_protocol_constants.h | 306 +++ mysqlrouter/classic_protocol_frame.h | 108 + mysqlrouter/classic_protocol_message.h | 832 ++++++ mysqlrouter/classic_protocol_session_track.h | 241 ++ mysqlrouter/classic_protocol_wire.h | 162 ++ mysqlrouter/partial_buffer_sequence.h | 155 ++ 14 files changed, 5638 insertions(+) create mode 100644 mysqlrouter/classic_protocol.h create mode 100644 mysqlrouter/classic_protocol_codec.h create mode 100644 mysqlrouter/classic_protocol_codec_base.h create mode 100644 mysqlrouter/classic_protocol_codec_error.h create mode 100644 mysqlrouter/classic_protocol_codec_frame.h create mode 100644 mysqlrouter/classic_protocol_codec_message.h create mode 100644 mysqlrouter/classic_protocol_codec_session_track.h create mode 100644 mysqlrouter/classic_protocol_codec_wire.h create mode 100644 mysqlrouter/classic_protocol_constants.h create mode 100644 mysqlrouter/classic_protocol_frame.h create mode 100644 mysqlrouter/classic_protocol_message.h create mode 100644 mysqlrouter/classic_protocol_session_track.h create mode 100644 mysqlrouter/classic_protocol_wire.h create mode 100644 mysqlrouter/partial_buffer_sequence.h diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h new file mode 100644 index 0000000..ae30f40 --- /dev/null +++ b/mysqlrouter/classic_protocol.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_H_ + +#include "mysqlrouter/classic_protocol_codec.h" +#include "mysqlrouter/classic_protocol_constants.h" +#include "mysqlrouter/classic_protocol_frame.h" +#include "mysqlrouter/classic_protocol_message.h" +#include "mysqlrouter/classic_protocol_wire.h" + +#endif diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h new file mode 100644 index 0000000..1291991 --- /dev/null +++ b/mysqlrouter/classic_protocol_codec.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_H_ + +// convenience header for all codec headers + +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_error.h" +#include "mysqlrouter/classic_protocol_codec_frame.h" +#include "mysqlrouter/classic_protocol_codec_message.h" +#include "mysqlrouter/classic_protocol_codec_session_track.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" + +#endif diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h new file mode 100644 index 0000000..fec210d --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -0,0 +1,375 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_BASE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_BASE_H_ + +#include // size_t +#include // uint8_t +#include // error_code +#include +#include // move + +#include "mysql/harness/net_ts/buffer.h" +#include "mysql/harness/stdx/bit.h" +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_constants.h" +#include "mysqlrouter/partial_buffer_sequence.h" + +namespace classic_protocol { + +// bytes needed to encode x bits +// +// bits | bytes +// -----+------ +// 0 | 0 +// 1 | 1 +// ... | ... +// 8 | 1 +// 9 | 2 +// ... | ... +// +constexpr size_t bytes_per_bits(size_t bits) { return (bits + 7) / 8; } + +static_assert(bytes_per_bits(0) == 0, ""); +static_assert(bytes_per_bits(1) == 1, ""); +static_assert(bytes_per_bits(8) == 1, ""); +static_assert(bytes_per_bits(9) == 2, ""); + +/** + * Codec for a type. + * + * requirements for T: + * - size_t size() + * - stdx::expected encode(net::mutable_buffer); + * - static size_t max_size(); + * - static stdx::expected decode(buffer_sequence, + * capabilities); + */ +template +class Codec; + +/** + * encode a message into a dynamic buffer. + * + * @param v message to encode + * @param caps protocol capabilities + * @param dyn_buffer dynamic buffer to write into + * @returns number of bytes written into dynamic buffer or std::error_code on + * error + */ +template +stdx::expected encode(const T &v, + capabilities::value_type caps, + DynamicBuffer &&dyn_buffer) { + // static_assert(net::is_dynamic_buffer::value, + // "dyn_buffer MUST be a DynamicBuffer"); + + Codec codec(v, caps); + + const auto orig_size = dyn_buffer.size(); + const auto codec_size = codec.size(); + + // reserve some space to write into + dyn_buffer.grow(codec_size); + + const auto res = codec.encode(dyn_buffer.data(orig_size, codec_size)); + if (!res) { + dyn_buffer.shrink(codec_size); + return res; + } + + dyn_buffer.shrink(codec_size - res.value()); + + return res; +} + +/** + * decode a message from a buffer sequence. + * + * @param buffers buffer sequence to read from + * @param caps protocol capabilities + * @returns number of bytes read from 'buffers' and a T on success, or + * std::error_code on error + */ +template +stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + static_assert(net::is_const_buffer_sequence::value, + "buffers MUST be a const buffer sequence"); + + return Codec::decode(buffers, caps); +} + +namespace impl { + +/** + * Generator of decoded Types of a buffer-sequence. + * + * - .step() + */ +template +class DecodeBufferAccumulator { + public: + using buffer_type = net::const_buffer; + using result_type = stdx::expected; + + /** + * construct a DecodeBufferAccumulator. + * + * @param buffers a ConstBufferSequence + * @param caps classic-protocol capabilities + * @param consumed bytes to skip from the buffers + */ + DecodeBufferAccumulator(const ConstBufferSequence &buffers, + capabilities::value_type caps, size_t consumed = 0) + : buffers_{buffers}, caps_{caps} { + static_assert(net::is_const_buffer_sequence::value, + "buffers MUST be a const buffer sequence"); + buffers_.consume(consumed); + } + + /** + * decode a Type from the buffer sequence. + * + * if it succeeds, moves position in buffer-sequence forward and returns + * decoded Type, otherwise returns error and updates the global error-code in + * result() + */ + template + stdx::expected::value_type, std::error_code> step( + size_t max_size = classic_protocol::Codec::max_size()) { + return step_(max_size); + } + + /** + * try decoding a Type from the buffer sequence. + * + * if it succeeds, moves position in buffer-sequence forward and returns + * decoded Type, otherwise returns error and does NOT update the global + * error-code in result() + */ + template + stdx::expected::value_type, std::error_code> try_step( + size_t max_size = classic_protocol::Codec::max_size()) { + return step_(max_size); + } + + /** + * get result of the step(). + * + * if a step() failed, result is the error-code of the first failed step() + * + * @returns consumed bytes by all steps(), or error of first failed step() + * + */ + result_type result() const { + if (!res_) { + return res_; + } else { + return buffers_.total_consumed(); + } + } + + private: + /** + * execute a step. + * + * Note: 'Try' is used a compile time selector for step() and try_step() only. + */ + template + stdx::expected::value_type, std::error_code> step_( + size_t max_size = std::numeric_limits::max()) { + if (!res_) return stdx::make_unexpected(res_.error()); + + stdx::expected::value_type>, + std::error_code> + decode_res = Codec::decode(buffers_.prepare(max_size), caps_); + if (decode_res) { + buffers_.consume(decode_res->first); + + return decode_res->second; + } else { + if (!Try) { + // capture the first failure + res_ = stdx::make_unexpected(decode_res.error()); + } + return stdx::make_unexpected(decode_res.error()); + } + } + + PartialBufferSequence buffers_; + const capabilities::value_type caps_; + + result_type res_; +}; + +/** + * accumulator of encoded buffers. + * + * writes the .step()ed encoded types into buffer. + * + * EncodeBufferAccumulator(buffer, caps) + * .step(wire::VarInt(42)) + * .step(wire::VarInt(512)) + * .result() + * + * The class should be used together with EncodeSizeAccumulator which shares + * the same interface. + */ +class EncodeBufferAccumulator { + public: + using buffer_type = net::mutable_buffer; + using result_type = stdx::expected; + + /** + * construct a encode-buffer-accumulator. + * + * @param buffer mutable-buffer to encode into + * @param caps protocol capabilities + * @param consumed bytes already used in the in buffer + */ + EncodeBufferAccumulator(buffer_type buffer, capabilities::value_type caps, + size_t consumed = 0) + : buffer_{std::move(buffer)}, caps_{caps}, consumed_{consumed} {} + + /** + * encode a T into the buffer and move position forward. + * + * no-op of a previous step failed. + */ + template + EncodeBufferAccumulator &step(const T &v) { + if (!res_) return *this; + + res_ = Codec(v, caps_).encode(buffer_ + consumed_); + if (res_) { + consumed_ += res_.value(); + } + + return *this; + } + + /** + * get result the steps(). + * + * @returns last used position in buffer, or first error in case of a step() + * failed. + */ + result_type result() const { + if (!res_) { + return res_; + } else { + return {consumed_}; + } + } + + private: + const buffer_type buffer_; + const capabilities::value_type caps_; + size_t consumed_{}; + + result_type res_; +}; + +/** + * accumulates the sizes of encoded T's. + * + * e.g. the size of tw + * + * EncodeSizeAccumulator(caps) + * .step(wire::VarInt(42)) // 1 + * .step(wire::VarInt(512)) // 2 + * .result() // = 3 + * + * The class should be used together with EncodeBufferAccumulator which shares + * the same interface. + */ +class EncodeSizeAccumulator { + public: + using result_type = size_t; + + /** + * construct a EncodeSizeAccumulator. + */ + constexpr explicit EncodeSizeAccumulator(capabilities::value_type caps) + : caps_{caps} {} + + /** + * accumulate the size() of encoded T. + * + * calls Codec(v, caps).size() + */ + template + constexpr EncodeSizeAccumulator &step(const T &v) noexcept { + consumed_ += Codec(v, caps_).size(); + + return *this; + } + + /** + * @returns size of all steps(). + */ + constexpr result_type result() const { return consumed_; } + + private: + size_t consumed_{}; + const capabilities::value_type caps_; +}; + +/** + * CRTP base for the Codec's encode part. + * + * derived classes must provide a 'accumulate_fields()' which + * maps each field by the Mapper and returns the result + * + * used by .size() and .encode() as both have to process the same + * fields in the same order, just with different mappers + */ +template +class EncodeBase { + public: + constexpr explicit EncodeBase(capabilities::value_type caps) : caps_{caps} {} + + constexpr size_t size() const noexcept { + return static_cast(this)->accumulate_fields( + EncodeSizeAccumulator(caps_)); + } + + stdx::expected encode( + const net::mutable_buffer &buffer) const { + return static_cast(this)->accumulate_fields( + EncodeBufferAccumulator(buffer, caps_)); + } + + constexpr capabilities::value_type caps() const noexcept { return caps_; } + + private: + const capabilities::value_type caps_; +}; + +} // namespace impl +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h new file mode 100644 index 0000000..2a8f7ff --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_ERROR_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_ERROR_H_ + +// error-domain for classic_protocol::codec errors + +#include // error_code + +namespace classic_protocol { + +enum class codec_errc { + // precondition failed like "first byte == cmd_byte()" + invalid_input = 1, + // not enough input to satisfy the length requirements like "FixedInt<1>" + not_enough_input, + // no nul-terminator found in input + missing_nul_term, + // capability not supported for this message + capability_not_supported, + // statement-id not found + statement_id_not_found, + // field-type unknown + field_type_unknown +}; +} // namespace classic_protocol + +namespace std { +template <> +struct is_error_code_enum + : public std::true_type {}; +} // namespace std + +namespace classic_protocol { +inline const std::error_category &codec_category() noexcept { + class error_category_impl : public std::error_category { + public: + const char *name() const noexcept override { return "codec"; } + std::string message(int ev) const override { + switch (static_cast(ev)) { + case codec_errc::invalid_input: + return "invalid input"; + case codec_errc::not_enough_input: + return "input too short"; + case codec_errc::missing_nul_term: + return "missing nul-terminator"; + case codec_errc::capability_not_supported: + return "capability not supported"; + case codec_errc::statement_id_not_found: + return "statement-id not found"; + case codec_errc::field_type_unknown: + return "unknown field-type"; + default: + return "unknown"; + } + } + }; + + static error_category_impl instance; + return instance; +} + +inline std::error_code make_error_code(codec_errc e) noexcept { + return {static_cast(e), codec_category()}; +} + +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h new file mode 100644 index 0000000..e293c3f --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -0,0 +1,191 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_FRAME_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_FRAME_H_ + +#include // size_t +#include // uint8_t +#include +#include // error_code +#include // move + +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_error.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" +#include "mysqlrouter/classic_protocol_frame.h" +#include "mysqlrouter/classic_protocol_wire.h" + +namespace classic_protocol { + +/** + * Codec of a Frame Header. + */ +template <> +class Codec : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<3>(v_.payload_size())) + .step(wire::FixedInt<1>(v_.seq_id())) + .result(); + } + + public: + using value_type = frame::Header; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr size_t max_size() noexcept { return 4; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto payload_size_res = accu.template step>(); + auto seq_id_res = accu.template step>(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(payload_size_res->value(), seq_id_res->value())); + } + + private: + const value_type v_; +}; + +/** + * Codec of Compressed Header. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<3>(v_.payload_size())) + .step(wire::FixedInt<1>(v_.seq_id())) + .step(wire::FixedInt<3>(v_.uncompressed_size())) + .result(); + } + + public: + using value_type = frame::CompressedHeader; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static size_t max_size() noexcept { return 7; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto payload_size_res = accu.template step>(); + auto seq_id_res = accu.template step>(); + auto uncompressed_size_res = accu.template step>(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(payload_size_res->value(), seq_id_res->value(), + uncompressed_size_res->value())); + } + + private: + const value_type v_; +}; + +/** + * Codec for a Frame. + * + * Frame is + * + * - header + * - payload + */ +template +class Codec> + : public impl::EncodeBase>> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu + .step(frame::Header( + Codec(v_.payload(), this->caps()).size(), v_.seq_id())) + .step(PayloadType(v_.payload())) + .result(); + } + + public: + using value_type = frame::Frame; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto header_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + constexpr const size_t header_size{Codec::max_size()}; + + // check the payload is at least what we expect. + if (net::buffer_size(buffers) < header_size + header_res->payload_size()) { + return stdx::make_unexpected( + make_error_code(classic_protocol::codec_errc::not_enough_input)); + } + + auto payload_res = + accu.template step(header_res->payload_size()); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(header_res->seq_id(), payload_res.value())); + } + + private: + const value_type v_; +}; +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h new file mode 100644 index 0000000..1962166 --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -0,0 +1,2277 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_MESSAGE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_MESSAGE_H_ + +#include // size_t +#include // uint8_t +#include // error_code +#include // move + +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_error.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" +#include "mysqlrouter/classic_protocol_message.h" +#include "mysqlrouter/classic_protocol_wire.h" +#include "mysqlrouter/partial_buffer_sequence.h" + +namespace classic_protocol { + +/** + * codec for server Greeting message. + * + * 3.21 (protocol_version 9) + * + * FixedInt<1> protocol_version [0x09] + * NulTermString server_version + * FixedInt<4> connection_id + * NulTermString auth-method-data + * + * 3.21 and later (protocol_version 10) + * + * FixedInt<1> protocol_version [0x0a] + * NulTermString server_version + * FixedInt<4> connection_id + * NulTermString auth-method-data + * FixedInt<2> capabilities (lower 16bit) + * + * 3.23 and later add: + * + * FixedInt<1> collation + * FixedInt<2> status flags + * FixedInt<2> capabilities (upper 16bit) + * FixedInt<1> length of auth-method-data or 0x00 + * String<10> reserved + * + * if capabilities.secure_connection is set, adds + * + * String auth-method-data-2 + * + * if capabilities.plugin_auth is set, adds + * + * NulTermString auth-method + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + if (v_.protocol_version() == 0x09) { + return accu.step(wire::FixedInt<1>(v_.protocol_version())) + .step(wire::NulTermString(v_.version())) + .step(wire::FixedInt<4>(v_.connection_id())) + .step(wire::NulTermString(v_.auth_method_data().substr(0, 8))) + .result(); + } else { + uint8_t auth_method_data_size{0}; + if (v_.capabilities()[classic_protocol::capabilities::pos::plugin_auth]) { + auth_method_data_size = v_.auth_method_data().size(); + } + + accu.step(wire::FixedInt<1>(v_.protocol_version())) + .step(wire::NulTermString(v_.version())) + .step(wire::FixedInt<4>(v_.connection_id())) + .step(wire::NulTermString(v_.auth_method_data().substr(0, 8))) + .step(wire::FixedInt<2>(v_.capabilities().to_ulong() & 0xffff)); + + if ((v_.capabilities().to_ullong() >= (1 << 16)) || + v_.status_flags().any() || (v_.collation() != 0)) { + accu.step(wire::FixedInt<1>(v_.collation())) + .step(wire::FixedInt<2>(v_.status_flags().to_ulong())) + .step(wire::FixedInt<2>((v_.capabilities().to_ulong() >> 16) & + 0xffff)) + .step(wire::FixedInt<1>(auth_method_data_size)) + .step(wire::String(std::string(10, '\0'))); + if (v_.capabilities() + [classic_protocol::capabilities::pos::secure_connection]) { + accu.step(wire::String(v_.auth_method_data().substr(8))); + if (v_.capabilities() + [classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + } + } + return accu.result(); + } + } + + public: + using value_type = message::server::Greeting; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + // proto-version + auto protocol_version_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (protocol_version_res->value() != 0x09 && + protocol_version_res->value() != 0x0a) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto version_res = accu.template step(); + auto connection_id_res = accu.template step>(); + auto auth_method_data_res = accu.template step(); + + if (protocol_version_res->value() == 0x09) { + return std::make_pair( + accu.result().value(), + value_type(protocol_version_res->value(), version_res->value(), + connection_id_res->value(), auth_method_data_res->value(), + 0, 0, 0, {})); + } else { + // capabilities are split into two a lower-2-byte part and a + // higher-2-byte + auto cap_lower_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + // 3.21.x doesn't send more. + if (buffer_size(buffers) <= accu.result().value()) { + return std::make_pair( + accu.result().value(), + value_type(protocol_version_res->value(), version_res->value(), + connection_id_res->value(), + auth_method_data_res->value(), cap_lower_res->value(), + 0x0, 0x0, {})); + } + + // if there's more data + auto collation_res = accu.template step>(); + auto status_flags_res = accu.template step>(); + auto cap_hi_res = accu.template step>(); + + // before we use cap_hi|cap_low check they don't have an error + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + classic_protocol::capabilities::value_type capabilities( + cap_lower_res->value() | (cap_hi_res->value() << 16)); + + size_t auth_method_data_len{13}; + if (capabilities[classic_protocol::capabilities::pos::plugin_auth]) { + auto auth_method_data_len_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + // should be 21, but least 8 + if (auth_method_data_len_res->value() < 8) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + auth_method_data_len = auth_method_data_len_res->value() - 8; + } else { + accu.template step(1); // should be 0 ... + } + + accu.template step(10); // skip the filler + + stdx::expected auth_method_data_2_res; + stdx::expected auth_method_res; + if (capabilities + [classic_protocol::capabilities::pos::secure_connection]) { + // auth-method-data + auth_method_data_2_res = + accu.template step(auth_method_data_len); + + if (capabilities[classic_protocol::capabilities::pos::plugin_auth]) { + // auth_method + auth_method_res = accu.template step(); + } + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type( + protocol_version_res->value(), version_res->value(), + connection_id_res->value(), + auth_method_data_res->value() + auth_method_data_2_res->value(), + capabilities, collation_res->value(), status_flags_res->value(), + auth_method_res->value())); + } + } + + private: + const value_type v_; +}; + +/** + * codec for server::AuthMethodSwitch message. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())); + if (caps()[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(wire::NulTermString(v_.auth_method())) + .step(wire::String(v_.auth_method_data())); + } + + return accu.result(); + } + + public: + using value_type = message::server::AuthMethodSwitch; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + // proto-version + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + if (!caps[classic_protocol::capabilities::pos::plugin_auth]) { + return std::make_pair(accu.result().value(), value_type()); + } + + auto auth_method_res = accu.template step(); + auto auth_method_data_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(auth_method_res->value(), auth_method_data_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for server::AuthMethodData message. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(v_.packet_type())) + .step(wire::String(v_.auth_method_data())) + .result(); + } + + public: + using value_type = message::server::AuthMethodData; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto packet_type_res = accu.template step>(); + auto auth_method_data_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(packet_type_res->value(), auth_method_data_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for server-side Ok message. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::VarInt(v_.affected_rows())) + .step(wire::VarInt(v_.last_insert_id())); + + if (caps()[capabilities::pos::protocol_41] || + caps()[capabilities::pos::transactions]) { + accu.step(wire::FixedInt<2>(v_.status_flags().to_ulong())); + if (caps()[capabilities::pos::protocol_41]) { + accu.step(wire::FixedInt<2>(v_.warning_count())); + } + } + + if (caps()[capabilities::pos::session_track]) { + accu.step(wire::VarString(v_.message())); + if (v_.status_flags()[status::pos::session_state_changed]) { + accu.step(wire::VarString(v_.session_changes())); + } + } else { + accu.step(wire::String(v_.message())); + } + + return accu.result(); + } + + public: + using value_type = message::server::Ok; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x00; } + + /** + * decode a server::Ok message from a buffer-sequence. + * + * precondition: + * - input starts with cmd_byte() + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with bytes + * processed + * @retval codec_errc::invalid_input if preconditions aren't met + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto affected_rows_res = accu.template step(); + auto last_insert_id_res = accu.template step(); + + stdx::expected, std::error_code> status_flags_res(0); + stdx::expected, std::error_code> warning_count_res(0); + if (caps[capabilities::pos::protocol_41] || + caps[capabilities::pos::transactions]) { + status_flags_res = accu.template step>(); + if (caps[capabilities::pos::protocol_41]) { + warning_count_res = accu.template step>(); + } + } + + stdx::expected message_res; + stdx::expected session_changes_res; + if (caps[capabilities::pos::session_track]) { + auto var_message_res = accu.template step(); + if (var_message_res) { + // set the message from the var-string + message_res = var_message_res.value(); + } + + if (status_flags_res->value() & + status::session_state_changed.to_ulong()) { + session_changes_res = accu.template step(); + } + } else { + message_res = accu.template step(); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(affected_rows_res->value(), last_insert_id_res->value(), + status_flags_res->value(), warning_count_res->value(), + message_res->value(), session_changes_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for server-side Eof message. + * + * Eof message is encoded differently dependending on protocol capabiltiies, + * but always starts with: + * + * - 0xef + * + * If capabilties has text_result_with_session_tracking, it is followed by + * - [rest of Ok packet] + * + * otherwise, if capabilities has protocol_41 + * - FixedInt<2> warning-count + * - FixedInt<2> status flags + * + * otherwise + * - nothing + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())); + + if (caps()[capabilities::pos::text_result_with_session_tracking]) { + accu.step(wire::VarInt(v_.affected_rows())) + .step(wire::VarInt(v_.last_insert_id())); + + if (caps()[capabilities::pos::protocol_41] || + caps()[capabilities::pos::transactions]) { + accu.step(wire::FixedInt<2>(v_.status_flags().to_ulong())); + if (caps()[capabilities::pos::protocol_41]) { + accu.step(wire::FixedInt<2>(v_.warning_count())); + } + } + + if (caps()[capabilities::pos::session_track]) { + accu.step(wire::VarString(v_.message())); + if (v_.status_flags()[status::pos::session_state_changed]) { + accu.step(wire::VarString(v_.session_changes())); + } + } else { + accu.step(wire::String(v_.message())); + } + } else if (caps()[capabilities::pos::protocol_41]) { + accu.step(wire::FixedInt<2>(v_.warning_count())) + .step(wire::FixedInt<2>(v_.status_flags().to_ulong())); + } + + return accu.result(); + } + + public: + using value_type = message::server::Eof; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } + + /** + * decode a server::Eof message from a buffer-sequence. + * + * capabilities checked: + * - protocol_41 + * - text_resultset_with_session_tracking + * + * precondition: + * - input starts with cmd_byte() + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with bytes + * processed + * @retval codec_errc::invalid_input if preconditions aren't met + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + const auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + if (caps[capabilities::pos::text_result_with_session_tracking]) { + const auto affected_rows_res = accu.template step(); + const auto last_insert_id_res = accu.template step(); + + stdx::expected, std::error_code> status_flags_res(0); + stdx::expected, std::error_code> warning_count_res(0); + if (caps[capabilities::pos::protocol_41] || + caps[capabilities::pos::transactions]) { + status_flags_res = accu.template step>(); + if (caps[capabilities::pos::protocol_41]) { + warning_count_res = accu.template step>(); + } + } + + stdx::expected message_res; + stdx::expected session_state_info_res; + if (caps[capabilities::pos::session_track]) { + const auto var_message_res = accu.template step(); + if (var_message_res) { + // set the message from the var-string + message_res = var_message_res.value(); + } + + if (status_flags_res->value() & + status::session_state_changed.to_ulong()) { + session_state_info_res = accu.template step(); + } + } else { + message_res = accu.template step(); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(affected_rows_res->value(), last_insert_id_res->value(), + status_flags_res->value(), warning_count_res->value(), + message_res->value(), session_state_info_res->value())); + } else if (caps[capabilities::pos::protocol_41]) { + const auto warning_count_res = accu.template step>(); + const auto status_flags_res = accu.template step>(); + + return std::make_pair( + accu.result().value(), + value_type(status_flags_res->value(), warning_count_res->value())); + } else { + return std::make_pair(accu.result().value(), value_type()); + } + } + + private: + const value_type v_; +}; + +/** + * codec for Error message. + * + * note: Format overview: + * + * 3.21: protocol_version <= 9 [not supported] + * + * FixedInt<1> 0xff + * String message + * + * 3.21: protocol_version > 9 + * + * FixedInt<1> 0xff + * FixedInt<2> error_code + * String message + * + * 4.1 and later: + * + * FixedInt<1> 0xff + * FixedInt<2> error_code + * '#' + * String<5> sql_state + * String message + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<2>(v_.error_code())); + if (caps()[capabilities::pos::protocol_41]) { + accu.step(wire::FixedInt<1>('#')).step(wire::String(v_.sql_state())); + } + + return accu.step(wire::String(v_.message())).result(); + } + + public: + using value_type = message::server::Error; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() { return 0xff; } + + static constexpr size_t max_size() noexcept { + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + // decode all fields, check result later before they are used. + auto error_code_res = accu.template step>(); + stdx::expected sql_state_res; + if (caps[capabilities::pos::protocol_41]) { + auto sql_state_hash_res = accu.template step>(); + sql_state_res = accu.template step(5); + } + auto message_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(error_code_res->value(), message_res->value(), + sql_state_res->value())); + } + + private: + const value_type v_; +}; + +/** + * Codec of ColumnMeta. + * + * capabilities checked: + * - protocol_41 + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + if (!caps()[capabilities::pos::protocol_41]) { + accu.step(wire::VarString(v_.table())) + .step(wire::VarString(v_.name())) + .step(wire::VarInt(3)) + .step(wire::FixedInt<3>(v_.column_length())) + .step(wire::VarInt(1)) + .step(wire::FixedInt<1>(v_.type())); + + if (caps()[capabilities::pos::long_flag]) { + accu.step(wire::VarInt(3)) + .step(wire::FixedInt<2>(v_.flags().to_ulong())) + .step(wire::FixedInt<1>(v_.decimals())); + } else { + accu.step(wire::VarInt(2)) + .step(wire::FixedInt<1>(v_.flags().to_ulong())) + .step(wire::FixedInt<1>(v_.decimals())); + } + + return accu.result(); + } else { + return accu.step(wire::VarString(v_.catalog())) + .step(wire::VarString(v_.schema())) + .step(wire::VarString(v_.table())) + .step(wire::VarString(v_.orig_table())) + .step(wire::VarString(v_.name())) + .step(wire::VarString(v_.orig_name())) + .step(wire::VarInt(12)) + .step(wire::FixedInt<2>(v_.collation())) + .step(wire::FixedInt<4>(v_.column_length())) + .step(wire::FixedInt<1>(v_.type())) + .step(wire::FixedInt<2>(v_.flags().to_ulong())) + .step(wire::FixedInt<1>(v_.decimals())) + .step(wire::FixedInt<2>(0)) + .result(); + } + } + + public: + using value_type = message::server::ColumnMeta; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static size_t max_size() noexcept { + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + if (!caps[capabilities::pos::protocol_41]) { + // 3.2x protocol used up to 4.0.x + + // bit-size of the 'flags' field + const uint8_t flags_size = caps[capabilities::pos::long_flag] ? 2 : 1; + + const auto table_res = accu.template step(); + const auto name_res = accu.template step(); + + const auto column_length_len_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (column_length_len_res->value() != 3) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + + const auto column_length_res = accu.template step>(); + const auto type_len_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (type_len_res->value() != 1) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + + const auto type_res = accu.template step>(); + const auto flags_and_decimals_len_res = + accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (flags_and_decimals_len_res->value() != flags_size + 1) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + + stdx::expected, std::error_code> flags_and_decimals_res( + 0); + if (flags_size == 2) { + flags_and_decimals_res = accu.template step>(); + } else { + const auto small_flags_and_decimals_res = + accu.template step>(); + if (small_flags_and_decimals_res) { + flags_and_decimals_res = + wire::FixedInt<3>(small_flags_and_decimals_res->value()); + } + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + const uint16_t flags = + flags_and_decimals_res->value() & ((1 << (flags_size * 8)) - 1); + const uint8_t decimals = + flags_and_decimals_res->value() >> (flags_size * 8); + + return std::make_pair( + accu.result().value(), + value_type({}, {}, table_res->value(), {}, name_res->value(), {}, {}, + column_length_res->value(), type_res->value(), flags, + decimals)); + } else { + const auto catalog_res = accu.template step(); + const auto schema_res = accu.template step(); + const auto table_res = accu.template step(); + const auto orig_table_res = accu.template step(); + const auto name_res = accu.template step(); + const auto orig_name_res = accu.template step(); + + /* next is a collection of fields which is wrapped inside a varstring of + * 12-bytes size */ + const auto other_len_res = accu.template step(); + + if (other_len_res->value() != 12) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + + const auto collation_res = accu.template step>(); + const auto column_length_res = accu.template step>(); + const auto type_res = accu.template step>(); + const auto flags_res = accu.template step>(); + const auto decimals_res = accu.template step>(); + + accu.template step(2); // fillers + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(catalog_res->value(), schema_res->value(), + table_res->value(), orig_table_res->value(), + name_res->value(), orig_name_res->value(), + collation_res->value(), column_length_res->value(), + type_res->value(), flags_res->value(), + decimals_res->value())); + } + } + + private: + const value_type v_; +}; + +/** + * codec for a Row from the server. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + for (const auto &field : v_) { + if (field) { + accu.step(wire::VarString(*field)); + } else { + accu.step(wire::Null()); + } + } + + return accu.result(); + } + + public: + using value_type = message::server::Row; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static size_t max_size() noexcept { + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + std::vector fields; + + const size_t buf_size = buffer_size(buffers); + + while (accu.result() && (accu.result().value() < buf_size)) { + // field may other be a Null or a VarString + auto null_res = accu.template try_step(); + if (null_res) { + fields.emplace_back(stdx::unexpected()); + } else { + auto field_res = accu.template step(); + if (!field_res) return stdx::make_unexpected(field_res.error()); + + fields.emplace_back(std::move(field_res->value())); + } + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type(fields)); + } + + private: + const value_type v_; +}; + +/** + * codec for a StmtRow from the server. + * + * StmtRow is the Row of a StmtExecute's resultset. + * + * - 0x00 + * - NULL bitmap + * - non-NULL-values in binary encoding + * + * both encode and decode require type information to know: + * + * - size the NULL bitmap + * - length of each field + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(0)); + + std::string nullbits; + nullbits.resize(bytes_per_bits(v_.types().size())); + + // null-bitmap starts with a 2-bit offset + size_t bit_pos{2}; + size_t byte_pos{}; + for (const auto &field : v_) { + if (bit_pos > 7) { + bit_pos = 0; + ++byte_pos; + } + + if (!field) { + nullbits[byte_pos] |= 1 << bit_pos; + } + } + + accu.step(wire::String(nullbits)); + + size_t n{}; + for (const auto &field : v_) { + if (field) { + switch (v_.types()[n++]) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: + accu.step(wire::VarInt(field->size())); + break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: + accu.step(wire::FixedInt<1>(field->size())); + break; + case field_type::LongLong: + case field_type::Double: + case field_type::Long: + case field_type::Int24: + case field_type::Float: + case field_type::Short: + case field_type::Year: + case field_type::Tiny: + // fixed size + break; + } + accu.step(wire::String(*field)); + } + } + + return accu.result(); + } + + public: + using value_type = message::server::StmtRow; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static size_t max_size() noexcept { + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps, + std::vector types) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + const auto row_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + // first byte is 0x00 + if (row_byte_res->value() != 0x00) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + const auto nullbits_res = + accu.template step(bytes_per_bits(types.size())); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + const auto nullbits = std::move(nullbits_res->value()); + + std::vector values; + + for (size_t n{}, bit_pos{2}, byte_pos{}; n < types.size(); ++n, ++bit_pos) { + if (bit_pos > 7) { + bit_pos = 0; + ++byte_pos; + } + + if (!(nullbits[byte_pos] & (1 << bit_pos))) { + stdx::expected field_size_res( + stdx::make_unexpected( + make_error_code(std::errc::invalid_argument))); + switch (types[n]) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: { + auto string_field_size_res = accu.template step(); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + field_size_res = string_field_size_res->value(); + } break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: { + auto time_field_size_res = accu.template step>(); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + field_size_res = time_field_size_res->value(); + } break; + case field_type::LongLong: + case field_type::Double: + field_size_res = 8; + break; + case field_type::Long: + case field_type::Int24: + case field_type::Float: + field_size_res = 4; + break; + case field_type::Short: + case field_type::Year: + field_size_res = 2; + break; + case field_type::Tiny: + field_size_res = 1; + break; + default: + return stdx::make_unexpected( + make_error_code(codec_errc::field_type_unknown)); + } + const auto value_res = + accu.template step(field_size_res.value()); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + values.push_back(value_res->value()); + } else { + values.emplace_back(stdx::make_unexpected()); + } + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type(types, values)); + } + + private: + const value_type v_; +}; + +/** + * CRTP base for client-side commands that are encoded as a single byte. + */ +template +class CodecSimpleCommand + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(Base::cmd_byte())).result(); + } + + public: + using __base = impl::EncodeBase>; + + friend __base; + + constexpr CodecSimpleCommand(capabilities::value_type caps) : __base(caps) {} + + static constexpr size_t max_size() noexcept { return 1; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != Base::cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + return std::make_pair(accu.result().value(), ValueType()); + } +}; + +/** + * codec for client's Quit command. + */ +template <> +class Codec + : public CodecSimpleCommand, + message::client::Quit> { + public: + using value_type = message::client::Quit; + using __base = CodecSimpleCommand, value_type>; + + constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} + + constexpr static uint8_t cmd_byte() noexcept { return 0x01; } +}; + +/** + * codec for client's ResetConnection command. + */ +template <> +class Codec + : public CodecSimpleCommand, + message::client::ResetConnection> { + public: + using value_type = message::client::ResetConnection; + using __base = CodecSimpleCommand, value_type>; + + constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} + + constexpr static uint8_t cmd_byte() noexcept { return 0x1f; } +}; + +/** + * codec for client's Ping command. + */ +template <> +class Codec + : public CodecSimpleCommand, + message::client::Ping> { + public: + using value_type = message::client::Ping; + using __base = CodecSimpleCommand, value_type>; + + constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} + + constexpr static uint8_t cmd_byte() noexcept { return 0x0e; } +}; + +/** + * codec for client's Statistics command. + */ +template <> +class Codec + : public CodecSimpleCommand, + message::client::Statistics> { + public: + using value_type = message::client::Statistics; + using __base = CodecSimpleCommand, value_type>; + + constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} + + constexpr static uint8_t cmd_byte() noexcept { return 0x09; } +}; + +/** + * codec for client's InitSchema command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::String(v_.schema())) + .result(); + } + + public: + using value_type = message::client::InitSchema; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { return 0x02; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto schema_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(schema_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Query command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::String(v_.statement())) + .result(); + } + + public: + using value_type = message::client::Query; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x03; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(statement_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Prepared Statement command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::String(v_.statement())) + .result(); + } + + public: + using value_type = message::client::StmtPrepare; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x16; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(statement_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Execute Statement command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.statement_id())) + .step(wire::FixedInt<1>(v_.flags().to_ullong())) + .step(wire::FixedInt<4>(v_.iteration_count())); + + // values.size() and types.size() MUST be the same + if (!v_.values().empty()) { + // mark all that are NULL in the nullbits + // + // - one bit per parameter to send + // - if a parameter is NULL, the bit is set, and later no value is added. + std::vector nullbits(bytes_per_bits(v_.values().size())); + + { + size_t byte_pos{}, bit_pos{}; + for (auto const &v : v_.values()) { + if (bit_pos > 7) { + bit_pos = 0; + ++byte_pos; + } + + if (!v) { + nullbits[byte_pos] |= 1 << bit_pos; + } + + ++bit_pos; + } + } + + accu.step(wire::String( + std::string(reinterpret_cast(nullbits.data()), + nullbits.size()))) + .step(wire::FixedInt<1>(v_.new_params_bound())); + if (v_.new_params_bound()) { + for (const auto &t : v_.types()) { + accu.step(wire::FixedInt<2>(t)); + } + size_t n{}; + for (const auto &v : v_.values()) { + // add all the values that aren't NULL + if (v.has_value()) { + // write length of the type is a variable length + switch (v_.types()[n++]) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: + accu.step(wire::VarInt(v->size())); + break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: + accu.step(wire::FixedInt<1>(v->size())); + break; + case field_type::LongLong: + case field_type::Double: + case field_type::Long: + case field_type::Int24: + case field_type::Float: + case field_type::Short: + case field_type::Year: + case field_type::Tiny: + // fixed size + break; + } + accu.step(wire::String(v.value())); + } + } + } + } + + return accu.result(); + } + + public: + using value_type = message::client::StmtExecute; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x17; } + + /** + * decode a sequence of buffers into a message::client::ExecuteStmt. + * + * @param buffers sequence of buffers + * @param caps protocol capabilities + * @param param_count_lookup callable that expects a 'uint32_t statement_id' + * that returns and integer that's convertible to 'stdx::expected' representing the parameter count of the prepared + * statement + * + * decoding a ExecuteStmt message requires a parameter count of the prepared + * statement. The param_count_lookup function may be called to get the param + * count for the statement-id. + * + * The function may return a param-count directly + * + * \code + * ExecuteStmt::decode( + * buffers, + * capabilities::protocol_41, + * [](uint32_t stmt_id) { return 1; }); + * \endcode + * + * ... or a stdx::expected if it wants to signal + * that a statement-id wasn't found + * + * \code + * ExecuteStmt::decode( + * buffers, + * capabilities::protocol_41, + * [](uint32_t stmt_id) -> stdx::expected { + * bool found{true}; + * + * if (found) { + * return 1; + * } else { + * return stdx::make_unexpected(make_error_code( + * codec_errc::statement_id_not_found)); + * } + * }); + * \endcode + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps, + Func &¶m_count_lookup) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_id_res = accu.template step>(); + auto flags_res = accu.template step>(); + auto iteration_count_res = accu.template step>(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + stdx::expected param_count_res = + param_count_lookup(statement_id_res->value()); + if (!param_count_res) { + return stdx::make_unexpected( + make_error_code(codec_errc::statement_id_not_found)); + } + + const size_t param_count = param_count_res.value(); + + if (param_count == 0) { + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(statement_id_res->value(), flags_res->value(), + iteration_count_res->value(), false, {}, {})); + } + + auto nullbits_res = + accu.template step(bytes_per_bits(param_count)); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto new_params_bound_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + std::vector types; + std::vector> values; + + if (new_params_bound_res->value()) { + const auto nullbits = std::move(nullbits_res->value()); + + types.reserve(param_count); + values.reserve(param_count); + + for (size_t n{}; n < param_count; ++n) { + auto type_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + types.push_back(type_res->value()); + } + for (size_t n{}, bit_pos{}, byte_pos{}; n < param_count; ++n, ++bit_pos) { + if (bit_pos > 7) { + bit_pos = 0; + ++byte_pos; + } + + if (!(nullbits[byte_pos] & (1 << bit_pos))) { + stdx::expected field_size_res( + stdx::make_unexpected( + make_error_code(std::errc::invalid_argument))); + switch (types[n]) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: { + auto string_field_size_res = accu.template step(); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + field_size_res = string_field_size_res->value(); + } break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: { + auto time_field_size_res = + accu.template step>(); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + field_size_res = time_field_size_res->value(); + } break; + case field_type::LongLong: + case field_type::Double: + field_size_res = 8; + break; + case field_type::Long: + case field_type::Int24: + case field_type::Float: + field_size_res = 4; + break; + case field_type::Short: + case field_type::Year: + field_size_res = 2; + break; + case field_type::Tiny: + field_size_res = 1; + break; + } + auto value_res = + accu.template step(field_size_res.value()); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + values.push_back(value_res->value()); + } else { + values.emplace_back(stdx::make_unexpected()); + } + } + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(statement_id_res->value(), flags_res->value(), + iteration_count_res->value(), new_params_bound_res->value(), + types, values)); + } + + private: + const value_type v_; +}; + +/** + * codec for client's append data Statement command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.statement_id())) + .step(wire::FixedInt<2>(v_.param_id())) + .step(wire::String(v_.data())) + .result(); + } + + public: + using value_type = message::client::StmtParamAppendData; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x18; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_id_res = accu.template step>(); + auto param_id_res = accu.template step>(); + auto data_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(statement_id_res->value(), + param_id_res->value(), data_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Close Statement command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.statement_id())) + .result(); + } + + public: + using value_type = message::client::StmtClose; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x19; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_id_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(statement_id_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Reset Statement command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.statement_id())) + .result(); + } + + public: + using value_type = message::client::StmtReset; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x1a; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_id_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(statement_id_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Fetch Cursor command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<2>(v_.option())) + .result(); + } + + public: + using value_type = message::client::StmtSetOption; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x1b; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto option_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(option_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client's Fetch Cursor command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.statement_id())) + .step(wire::FixedInt<4>(v_.row_count())) + .result(); + } + + public: + using value_type = message::client::StmtFetch; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x1c; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto statement_id_res = accu.template step>(); + auto row_count_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(statement_id_res->value(), row_count_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client side greeting message. + * + * + * in 3.21 ... 4.0: + * + * FixedInt<2> capabilities [protocol_41 not set] + * FixedInt<3> max-allowed-packet + * NulTermString username + * NulTermString auth-method-data + * + * [if not connect_with_schema, there may be no trailing Nul-byte] + * + * if connect_with_schema { + * String schema + * } + * + * the auth-method is "old_password" if "protocol_version == 10 && + * (capabilities & long_password)", it is "older_password" otherwise + * + * FixedInt<2> capabilities_lo [protocol_41 set] + * FixedInt<2> capabilities_hi + * FixedInt<4> max_allowed-packet + * ... + * + * The capabilities that are part of the message are the client's capabilities + * (which may announce more than what the server supports). The codec + * uses the capabilities that are shared between client and server to decide + * which parts and how they are understood, though. + * + * checked capabilities: + * - protocol_41 + * - ssl + * - client_auth_method_data_varint + * - secure_connection + * - connect_with_schema + * - plugin_auth + * - connect_attributes + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + const auto shared_caps = v_.capabilities() & caps(); + + if (shared_caps[classic_protocol::capabilities::pos::protocol_41]) { + accu.step(wire::FixedInt<4>(v_.capabilities().to_ulong())) + .step(wire::FixedInt<4>(v_.max_packet_size())) + .step(wire::FixedInt<1>(v_.collation())) + .step(wire::String(std::string(23, '\0'))); + if (!(shared_caps[classic_protocol::capabilities::pos::ssl] && + v_.username().empty())) { + // the username is empty and SSL is set, this is a short SSL-greeting + // packet + accu.step(wire::NulTermString(v_.username())); + + if (shared_caps[classic_protocol::capabilities::pos:: + client_auth_method_data_varint]) { + accu.step(wire::VarString(v_.auth_method_data())); + } else if (shared_caps[classic_protocol::capabilities::pos:: + secure_connection]) { + accu.step(wire::FixedInt<1>(v_.auth_method_data().size())) + .step(wire::String(v_.auth_method_data())); + } else { + accu.step(wire::NulTermString(v_.auth_method_data())); + } + + if (shared_caps + [classic_protocol::capabilities::pos::connect_with_schema]) { + accu.step(wire::NulTermString(v_.schema())); + } + + if (shared_caps[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + + if (shared_caps + [classic_protocol::capabilities::pos::connect_attributes]) { + accu.step(wire::VarString(v_.attributes())); + } + } + } else { + accu.step(wire::FixedInt<2>(v_.capabilities().to_ulong())) + .step(wire::FixedInt<3>(v_.max_packet_size())) + .step(wire::NulTermString(v_.username())); + if (shared_caps + [classic_protocol::capabilities::pos::connect_with_schema]) { + accu.step(wire::NulTermString(v_.auth_method_data())) + .step(wire::String(v_.schema())); + } else { + accu.step(wire::String(v_.auth_method_data())); + } + } + + return accu.result(); + } + + public: + using value_type = message::client::Greeting; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto capabilities_lo_res = accu.template step>(); + if (!capabilities_lo_res) + return stdx::make_unexpected(capabilities_lo_res.error()); + + auto client_capabilities = classic_protocol::capabilities::value_type( + capabilities_lo_res->value()); + + // decoding depends on the capabilities that both client and server have in + // common + auto shared_capabilities = caps & client_capabilities; + + if (shared_capabilities[classic_protocol::capabilities::pos::protocol_41]) { + // if protocol_41 is set in the capabilities, we expected 2 more bytes + // of capabilities + auto capabilities_hi_res = accu.template step>(); + if (!capabilities_hi_res) + return stdx::make_unexpected(capabilities_hi_res.error()); + + client_capabilities |= classic_protocol::capabilities::value_type( + capabilities_hi_res->value() << 16); + + shared_capabilities = caps & client_capabilities; + + auto max_packet_size_res = accu.template step>(); + auto collation_res = accu.template step>(); + + accu.template step(23); // skip 23 bytes + + auto last_accu_res = accu.result(); + + auto username_res = accu.template step(); + if (!accu.result()) { + // if there isn't enough data for the nul-term-string, but we had the + // 23-bytes ... + if (last_accu_res && + shared_capabilities[classic_protocol::capabilities::pos::ssl]) { + return std::make_pair( + last_accu_res.value(), + value_type(client_capabilities, max_packet_size_res->value(), + collation_res->value(), {}, {}, {}, {}, {})); + } + + return stdx::make_unexpected(accu.result().error()); + } + + // auth-method-data is either + // + // - varint length + // - fixed-int-1 length + // - null-term-string + stdx::expected auth_method_data_res; + if (shared_capabilities[classic_protocol::capabilities::pos:: + client_auth_method_data_varint]) { + auto res = accu.template step(); + if (!res) return stdx::make_unexpected(res.error()); + + auth_method_data_res = wire::String(res->value()); + } else if (shared_capabilities + [classic_protocol::capabilities::pos::secure_connection]) { + auto auth_method_data_len_res = accu.template step>(); + if (!auth_method_data_len_res) + return stdx::make_unexpected(auth_method_data_len_res.error()); + auto auth_method_data_len = auth_method_data_len_res->value(); + + auto res = accu.template step(auth_method_data_len); + if (!res) return stdx::make_unexpected(res.error()); + + auth_method_data_res = wire::String(res->value()); + } else { + auto res = accu.template step(); + if (!res) return stdx::make_unexpected(res.error()); + + auth_method_data_res = wire::String(res->value()); + } + + stdx::expected schema_res; + if (shared_capabilities + [classic_protocol::capabilities::pos::connect_with_schema]) { + schema_res = accu.template step(); + } + if (!schema_res) return stdx::make_unexpected(schema_res.error()); + + stdx::expected auth_method_res; + if (shared_capabilities + [classic_protocol::capabilities::pos::plugin_auth]) { + auth_method_res = accu.template step(); + } + if (!auth_method_res) + return stdx::make_unexpected(auth_method_res.error()); + + stdx::expected attributes_res; + if (shared_capabilities + [classic_protocol::capabilities::pos::connect_attributes]) { + attributes_res = accu.template step(); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(client_capabilities, max_packet_size_res->value(), + collation_res->value(), username_res->value(), + auth_method_data_res->value(), schema_res->value(), + auth_method_res->value(), attributes_res->value())); + + } else { + auto max_packet_size_res = accu.template step>(); + + auto username_res = accu.template step(); + + stdx::expected auth_method_data_res; + stdx::expected schema_res; + + if (caps[classic_protocol::capabilities::pos::connect_with_schema]) { + auto res = accu.template step(); + if (!res) return stdx::make_unexpected(res.error()); + + // auth_method_data is a wire::String, move it over + auth_method_data_res = wire::String(res->value()); + + schema_res = accu.template step(); + } else { + auth_method_data_res = accu.template step(); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + // idea: benchmark in-place constructor where all parameters are passed + // down to the lowest level. + // + // It should involve less copy-construction. + // + // - stdx::in_place is for in-place construction of stdx::expected's + // value + // - std::piecewise_construct for the parts of the std::pair that is + // returned as value of stdx::expected + // + // return {stdx::in_place, std::piecewise_construct, + // std::forward_as_tuple(accu.result().value()), + // std::forward_as_tuple(capabilities, + // max_packet_size_res->value(), + // 0x00, username_res->value(), + // auth_method_data_res->value(), + // schema_res->value(), {}, + // {})}; + return std::make_pair( + accu.result().value(), + value_type(client_capabilities, max_packet_size_res->value(), 0x00, + username_res->value(), auth_method_data_res->value(), + schema_res->value(), {}, {})); + } + } + + private: + const value_type v_; +}; + +/** + * codec for client side change-user message. + * + * checked capabilities: + * - protocol_41 + * - secure_connection + * - plugin_auth + * - connect_attributes + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::NulTermString(v_.username())); + + if (caps()[classic_protocol::capabilities::pos::secure_connection]) { + accu.step(wire::FixedInt<1>(v_.auth_method_data().size())) + .step(wire::String(v_.auth_method_data())); + } else { + accu.step(wire::NulTermString(v_.auth_method_data())); + } + accu.step(wire::NulTermString(v_.schema())); + + // 4.1 and later have a collation + // + // this could be checked via the protocol_41 capability, but that's not + // what the server does + if (v_.collation() != 0x00 || + caps()[classic_protocol::capabilities::pos::plugin_auth] || + caps()[classic_protocol::capabilities::pos::connect_attributes]) { + accu.step(wire::FixedInt<2>(v_.collation())); + if (caps()[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + + if (caps()[classic_protocol::capabilities::pos::connect_attributes]) { + accu.step(wire::VarString(v_.attributes())); + } + } + + return accu.result(); + } + + public: + using value_type = message::client::ChangeUser; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x11; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + auto username_res = accu.template step(); + + // auth-method-data is either + // + // - fixed-int-1 length + // - null-term-string + stdx::expected auth_method_data_res; + if (caps[classic_protocol::capabilities::pos::secure_connection]) { + auto auth_method_data_len_res = accu.template step>(); + if (!auth_method_data_len_res) + return stdx::make_unexpected(auth_method_data_len_res.error()); + auto auth_method_data_len = auth_method_data_len_res->value(); + + auto res = accu.template step(auth_method_data_len); + if (!res) return stdx::make_unexpected(res.error()); + + auth_method_data_res = wire::String(res->value()); + } else { + auto res = accu.template step(); + if (!res) return stdx::make_unexpected(res.error()); + + auth_method_data_res = wire::String(res->value()); + } + + auto schema_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + // 3.23.x-4.0 don't send more. + if (buffer_size(buffers) <= accu.result().value()) { + return std::make_pair( + accu.result().value(), + value_type(username_res->value(), auth_method_data_res->value(), + schema_res->value(), 0x00, {}, {})); + } + + // added in 4.1 + auto collation_res = accu.template step>(); + + stdx::expected auth_method_name_res; + if (caps[classic_protocol::capabilities::pos::plugin_auth]) { + auth_method_name_res = accu.template step(); + } + + stdx::expected attributes_res; + if (caps[classic_protocol::capabilities::pos::connect_attributes]) { + attributes_res = accu.template step(); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(username_res->value(), auth_method_data_res->value(), + schema_res->value(), collation_res->value(), + auth_method_name_res->value(), attributes_res->value())); + } + + private: + const value_type v_; +}; + +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h new file mode 100644 index 0000000..96265e3 --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -0,0 +1,388 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_SESSION_TRACK_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_SESSION_TRACK_H_ + +// codecs for classic_protocol::session_track:: messages + +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" +#include "mysqlrouter/classic_protocol_session_track.h" +#include "mysqlrouter/classic_protocol_wire.h" + +namespace classic_protocol { + +/** + * codec for session_track::TransactionState. + * + * part of session_track::Field + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(v_.trx_type())) + .step(wire::FixedInt<1>(v_.read_unsafe())) + .step(wire::FixedInt<1>(v_.read_trx())) + .step(wire::FixedInt<1>(v_.write_unsafe())) + .step(wire::FixedInt<1>(v_.write_trx())) + .step(wire::FixedInt<1>(v_.stmt_unsafe())) + .step(wire::FixedInt<1>(v_.resultset())) + .step(wire::FixedInt<1>(v_.locked_tables())) + .result(); + } + + public: + using value_type = session_track::TransactionState; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::TransactionState from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with bytes + * processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + static_assert(net::is_const_buffer_sequence::value, + "buffers MUST be a const buffer sequence"); + impl::DecodeBufferAccumulator accu(buffers, caps); + + const auto try_type_res = accu.template step>(); + const auto read_unsafe_res = accu.template step>(); + const auto read_trx_res = accu.template step>(); + const auto write_unsafe_res = accu.template step>(); + const auto write_trx_res = accu.template step>(); + const auto stmt_unsafe_res = accu.template step>(); + const auto resultset_res = accu.template step>(); + const auto locked_tables_res = accu.template step>(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(try_type_res->value(), read_unsafe_res->value(), + read_trx_res->value(), write_unsafe_res->value(), + write_trx_res->value(), stmt_unsafe_res->value(), + resultset_res->value(), locked_tables_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for session_track::TransactionCharacteristics. + * + * part of session_track::Field + */ +template <> +class Codec + : public impl::EncodeBase< + Codec> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::VarString(v_.statements())).result(); + } + + public: + using value_type = session_track::TransactionCharacteristics; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::TransactionCharacteristics from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on + * sucess, with bytes processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + static_assert(net::is_const_buffer_sequence::value, + "buffers MUST be a const buffer sequence"); + impl::DecodeBufferAccumulator accu(buffers, caps); + + const auto statements_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(statements_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for session_track::State. + * + * part of session_track::Field + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::VarString(v_.state())).result(); + } + + public: + using value_type = session_track::State; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::State from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with bytes + * processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto state_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(state_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for session_track::Schema. + * + * part of session_track::Field + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::VarString(v_.schema())).result(); + } + + public: + using value_type = session_track::Schema; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::Schema from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with bytes + * processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + static_assert(net::is_const_buffer_sequence::value, + "buffers MUST be a const buffer sequence"); + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto schema_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(schema_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for session_track::SystemVariable. + * + * part of session_track::Field + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::VarString(v_.key())) + .step(wire::VarString(v_.value())) + .result(); + } + + public: + using value_type = session_track::SystemVariable; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::SystemVariable from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with + * bytes processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + static_assert(net::is_const_buffer_sequence::value, + "buffers MUST be a const buffer sequence"); + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto key_res = accu.template step(); + auto value_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(key_res->value(), value_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for session-track's Field. + * + * sent as part of a server::Ok and server::Eof message. + * + * - FixedInt<1> type + * - VarString data + * + * data is encoded according type: + * + * - 0x00 session_track::SystemVariable + * - 0x01 session_track::Schema + * - 0x02 session_track::StateChanged + * - 0x03 session_track::Gtid + * - 0x04 session_track::TransactionState + * - 0x05 session_track::TransactionCharacteristics + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(v_.type())) + .step(wire::VarString(v_.data())) + .result(); + } + + public: + using value_type = session_track::Field; + + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::Field from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on sucess, with bytes + * processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + static_assert(net::is_const_buffer_sequence::value, + ""); + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto type_res = accu.template step>(); + auto data_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(type_res->value(), data_res->value())); + } + + private: + const value_type v_; +}; + +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h new file mode 100644 index 0000000..282cc80 --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -0,0 +1,441 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_WIRE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_WIRE_H_ + +// codecs for classic_protocol::wire:: + +#include // find +#include // size_t +#include // uint8_t +#include // error_code +#include +#include // move + +#include "mysql/harness/net_ts/buffer.h" +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_error.h" +#include "mysqlrouter/classic_protocol_wire.h" +#include "mysqlrouter/partial_buffer_sequence.h" + +namespace classic_protocol { +/** + * codec of a FixedInt. + * + * classic proto uses 1, 2, 3, 4, 8 for IntSize + */ +template +class Codec> { + public: + static constexpr size_t int_size{IntSize}; + + using value_type = wire::FixedInt; + + constexpr Codec(value_type v, capabilities::value_type /* unused */) + : v_{v} {} + + /** + * size of the encoded object. + */ + constexpr size_t size() const noexcept { return int_size; } + + /** + * encode value_type into buffer. + */ + stdx::expected encode( + const net::mutable_buffer &buffer) const { + auto v = v_.value(); + + if (stdx::endian::native == stdx::endian::big) { + v = stdx::byteswap(v); + } + + return buffer_copy(buffer, net::const_buffer(&v, int_size)); + } + + /** + * maximum bytes which may scanned by the decoder. + */ + static constexpr size_t max_size() noexcept { return int_size; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + typename value_type::value_type v{}; + + size_t copied = buffer_copy(net::buffer(&v, int_size), buffers); + + if (copied != int_size) { + // not enough data in buffers. + return stdx::make_unexpected( + make_error_code(codec_errc::not_enough_input)); + } + + if (stdx::endian::native == stdx::endian::big) { + v = stdx::byteswap(v); + } + + return std::make_pair(copied, value_type(v)); + } + + private: + const value_type v_; +}; + +/** + * codec for variable length integers. + * + * note: encoded as little endian + * + * 0x00 + * ... + * 0xfa -> 0xfa + * 0xfb [undefined] + * 0xfc 0x.. 0x.. + * 0xfd 0x.. 0x.. 0x.. + * + * 3.21: + * 0xfe 0x.. 0x.. 0x.. 0x.. 0x00 + * [1 + 5 bytes read, only 4 bytes used] + * + * 4.0: + * 0xfe 0x.. 0x.. 0x.. 0x.. 0x.. 0x.. 0x.. 0x.. + * [1 + 8 bytes read, only 4 bytes used] + */ +template <> +class Codec : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + if (v_.value() < 251) { + return accu.step(wire::FixedInt<1>(v_.value())).result(); + } else if (v_.value() < 1 << 16) { + return accu.step(wire::FixedInt<1>(varint_16)) + .step(wire::FixedInt<2>(v_.value())) + .result(); + } else if (v_.value() < (1 << 24)) { + return accu.step(wire::FixedInt<1>(varint_24)) + .step(wire::FixedInt<3>(v_.value())) + .result(); + } else { + return accu.step(wire::FixedInt<1>(varint_64)) + .step(wire::FixedInt<8>(v_.value())) + .result(); + } + } + + public: + static constexpr uint8_t varint_16{0xfc}; + static constexpr uint8_t varint_24{0xfd}; + static constexpr uint8_t varint_64{0xfe}; + using value_type = wire::VarInt; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base{caps}, v_{v} {} + + static constexpr size_t max_size() noexcept { return 9; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + // length + auto first_byte_res = accu.template step>(); + if (!first_byte_res) return stdx::make_unexpected(first_byte_res.error()); + + auto first_byte = first_byte_res->value(); + + if (first_byte < 251) { + return std::make_pair(accu.result().value(), value_type(first_byte)); + } else if (first_byte == varint_16) { + auto value_res = accu.template step>(); + if (!value_res) return stdx::make_unexpected(value_res.error()); + return std::make_pair(accu.result().value(), + value_type(value_res->value())); + } else if (first_byte == varint_24) { + auto value_res = accu.template step>(); + if (!value_res) return stdx::make_unexpected(value_res.error()); + return std::make_pair(accu.result().value(), + value_type(value_res->value())); + } else if (first_byte == varint_64) { + auto value_res = accu.template step>(); + if (!value_res) return stdx::make_unexpected(value_res.error()); + return std::make_pair(accu.result().value(), + value_type(value_res->value())); + } + + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + private: + const value_type v_; +}; + +/** + * codec for a NULL value in the Resultset. + */ +template <> +class Codec : public Codec> { + public: + using value_type = wire::Null; + + static constexpr uint8_t nul_byte{0xfb}; + + Codec(value_type, capabilities::value_type caps) + : Codec>(nul_byte, caps) {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + uint8_t v; + + size_t copied = buffer_copy(net::buffer(&v, 1), buffers); + + if (copied != 1) { + return stdx::make_unexpected( + make_error_code(codec_errc::not_enough_input)); + } else if (v != nul_byte) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + return std::make_pair(copied, value_type()); + } +}; + +/** + * codec for ignorable bytes. + * + * limited by length or buffer.size() + */ +template <> +class Codec { + public: + using value_type = size_t; + + Codec(value_type v, capabilities::value_type caps) + : v_{std::move(v)}, caps_{caps} {} + + size_t size() const noexcept { return v_; } + + static size_t max_size() noexcept { + return std::numeric_limits::max(); + } + + stdx::expected encode( + const net::mutable_buffer &buffer) const { + return buffer_copy(buffer, net::buffer(std::vector(size()))); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + size_t buf_size = buffer_size(buffers); + + return std::make_pair(buf_size, buf_size); + } + + private: + const value_type v_; + const capabilities::value_type caps_; +}; + +/** + * codec for wire::String. + * + * limited by length or buffer.size() + */ +template <> +class Codec { + public: + using value_type = wire::String; + + Codec(value_type v, capabilities::value_type caps) + : v_{std::move(v)}, caps_{caps} {} + + size_t size() const noexcept { return v_.value().size(); } + + static size_t max_size() noexcept { + // we actually don't know what the size of the null-term string is ... until + // the end of the buffer + return std::numeric_limits::max(); + } + + stdx::expected encode( + const net::mutable_buffer &buffer) const { + return buffer_copy(buffer, net::const_buffer(v_.value().data(), size())); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + size_t buf_size = buffer_size(buffers); + + // MUST handle the empty case as &s.front() for .empty() std::string is + // undefined and may trigger an assert()ion on glibc's implementation + if (0 == buf_size) { + return std::make_pair(buf_size, value_type(std::string())); + } + std::string s; + s.resize(buf_size); + + size_t len = + buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffers); + + return std::make_pair(len, value_type(s)); + } + + private: + const value_type v_; + const capabilities::value_type caps_; +}; + +/** + * codec for string with known length. + * + * - varint of string length + * - string of length + */ +template <> +class Codec : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::VarInt(v_.value().size())) + .step(wire::String(v_.value())) + .result(); + } + + public: + using value_type = wire::VarString; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static size_t max_size() noexcept { + // we actually don't know what the size of the null-term string is ... + // until the end of the buffer + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + // decode the length + auto var_string_len_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + // decode string of length + auto var_string_res = + accu.template step(var_string_len_res->value()); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(var_string_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for 0-terminated string. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.template step(v_) + .template step>(0) + .result(); + } + + public: + using value_type = wire::NulTermString; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static size_t max_size() noexcept { + // we actually don't know what the size of the null-term string is ... + // until the end of the buffer + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + // length of the string before the \0 + size_t len{}; + + // we don't know where the \0 will be be, scan all buffers for the first + // one. + const auto bufend = buffer_sequence_end(buffers); + for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend; + ++bufcur) { + const auto first = static_cast(bufcur->data()); + const auto last = first + bufcur->size(); + + const auto pos = std::find(first, last, '\0'); + if (pos != last) { + // \0 was found + len += std::distance(first, pos); + + // builds a string from the buffer-sequence's content + std::string s; + if (len > 0) { + // ensure we don't trigger undefined behaviour by using &s.front() if + // s.size() is 0 + s.resize(len); + buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffers, len); + } + + return std::make_pair(len + 1, value_type(s)); // consume the \0 too + } else { + len += buffer_size(*bufcur); + } + } + + // no 0-term found + return stdx::make_unexpected(make_error_code(codec_errc::missing_nul_term)); + } + + private: + const value_type v_; +}; +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h new file mode 100644 index 0000000..4ff6152 --- /dev/null +++ b/mysqlrouter/classic_protocol_constants.h @@ -0,0 +1,306 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CONSTANTS_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CONSTANTS_H_ + +#include +#include + +namespace classic_protocol { + +namespace capabilities { + +namespace pos { +using value_type = uint8_t; +constexpr value_type long_password{0}; +constexpr value_type found_rows{1}; +constexpr value_type long_flag{2}; +constexpr value_type connect_with_schema{3}; +constexpr value_type no_schema{4}; +constexpr value_type compress{5}; +constexpr value_type odbc{6}; +constexpr value_type local_files{7}; +constexpr value_type ignore_space{8}; +constexpr value_type protocol_41{9}; +constexpr value_type interactive{10}; +constexpr value_type ssl{11}; +// 12 is unused +constexpr value_type transactions{13}; +// 14 is unused +constexpr value_type secure_connection{15}; +constexpr value_type multi_statements{16}; +constexpr value_type multi_results{17}; +constexpr value_type ps_multi_results{18}; +constexpr value_type plugin_auth{19}; +constexpr value_type connect_attributes{20}; +constexpr value_type client_auth_method_data_varint{21}; +constexpr value_type expired_passwords{22}; +constexpr value_type session_track{23}; +constexpr value_type text_result_with_session_tracking{24}; +// 25: optional resultset metdata +constexpr value_type compress_zstd{26}; +// +// 29 is an extension flag for >32 bit +// 30 is client only +// 31 is client only + +} // namespace pos + +using value_type = std::bitset<32>; + +// old_password instead of older_password +// version_added: 3.21 +constexpr value_type long_password{1 << pos::long_password}; +// found rows, instead of affected rows. +// version_added: 3.21 +constexpr value_type found_rows{1 << pos::found_rows}; +// version_added: 3.21 +// get all column flags +constexpr value_type long_flag{1 << pos::long_flag}; +// connect with schema +// version_added: 3.21 +constexpr value_type connect_with_schema{1 << pos::connect_with_schema}; +// don't allow schema.table.column +// version_added: 3.21 +constexpr value_type no_schema{1 << pos::no_schema}; +// use deflate compression +// version_added: 3.22 +constexpr value_type compress{1 << pos::compress}; +// odbc client +// version_added: 3.22 +constexpr value_type odbc{1 << pos::odbc}; +// can use LOCAL INFILE +// version_added: 3.22 +constexpr value_type local_files{1 << pos::local_files}; +// ignore space before ( +// version_added: 3.22 +constexpr value_type ignore_space{1 << pos::ignore_space}; +// protocol_version 10 + more fields in server::Greeting +// version_added: 4.1 +constexpr value_type protocol_41{1 << pos::protocol_41}; +// interactive +// version_added: 3.22 +constexpr value_type interactive{1 << pos::interactive}; +// switch to SSL +// version_added: 3.23 +constexpr value_type ssl{1 << pos::ssl}; +// status-field in Ok message +// version_added: 3.23 +constexpr value_type transactions{1 << pos::transactions}; +// mysql_native_password +// version_added: 4.1 +constexpr value_type secure_connection{1 << pos::secure_connection}; +// multi-statement support +// version_added: 4.1 +constexpr value_type multi_statements{1 << pos::multi_statements}; +// multi-result support +// version_added: 4.1 +constexpr value_type multi_results{1 << pos::multi_results}; +// version_added: 5.5 +constexpr value_type ps_multi_results{1 << pos::ps_multi_results}; +// version_added: 5.5 +constexpr value_type plugin_auth{1 << pos::plugin_auth}; +// version_added: 5.6 +constexpr value_type connect_attributes{1 << pos::connect_attributes}; +// version_added: 5.6 +constexpr value_type client_auth_method_data_varint{ + 1 << pos::client_auth_method_data_varint}; +// version_added: 5.6 +constexpr value_type expired_passwords{1 << pos::expired_passwords}; +// version_added: 5.7 +constexpr value_type session_track{1 << pos::session_track}; +// version_added: 5.7 +constexpr value_type text_result_with_session_tracking{ + 1 << pos::text_result_with_session_tracking}; +// version_added: 8.0 +constexpr value_type compress_zstd{1 << pos::compress_zstd}; +} // namespace capabilities + +namespace status { +namespace pos { +using value_type = uint8_t; +constexpr value_type in_transaction{0}; +constexpr value_type autocommit{1}; +// 2 is unused (more-results in 4.1.22) +constexpr value_type more_results_exist{3}; +constexpr value_type no_good_index_used{4}; +constexpr value_type no_index_used{5}; +constexpr value_type cursor_exists{6}; +constexpr value_type last_row_sent{7}; +constexpr value_type schema_dropped{8}; +constexpr value_type no_backslash_escapes{9}; +constexpr value_type metadata_changed{10}; +constexpr value_type query_was_slow{11}; +constexpr value_type ps_out_params{12}; +constexpr value_type in_transaction_readonly{13}; +constexpr value_type session_state_changed{14}; + +} // namespace pos +using value_type = std::bitset<16>; + +// transaction is open +// version_added: 3.23 +constexpr value_type in_transaction{1 << pos::in_transaction}; +// autocommit +// version_added: 3.23 +constexpr value_type autocommit{1 << pos::autocommit}; +// multi-statement more results +// version_added: 4.1 +constexpr value_type more_results_exist{1 << pos::more_results_exist}; +// no good index used +// version_added: 4.1 +constexpr value_type no_good_index_used{1 << pos::no_good_index_used}; +// no index used +// version_added: 4.1 +constexpr value_type no_index_used{1 << pos::no_index_used}; +// cursor exists +// version_added: 5.0 +constexpr value_type cursor_exists{1 << pos::cursor_exists}; +// last row sent +// version_added: 5.0 +constexpr value_type last_row_sent{1 << pos::last_row_sent}; +// schema dropped +// version_added: 4.1 +constexpr value_type schema_dropped{1 << pos::schema_dropped}; +// no backslash escapes +// version_added: 5.0 +constexpr value_type no_backslash_escapes{1 << pos::no_backslash_escapes}; +// metadata changed +// version_added: 5.1 +constexpr value_type metadata_changed{1 << pos::metadata_changed}; +// version_added: 5.5 +constexpr value_type query_was_slow{1 << pos::query_was_slow}; +// version_added: 5.5 +constexpr value_type ps_out_params{1 << pos::ps_out_params}; +// version_added: 5.7 +constexpr value_type in_transaction_readonly{1 << pos::in_transaction_readonly}; +// version_added: 5.7 +constexpr value_type session_state_changed{1 << pos::session_state_changed}; +} // namespace status + +namespace cursor { +namespace pos { +using value_type = uint8_t; +constexpr value_type no_cursor{0}; +constexpr value_type read_only{1}; +constexpr value_type for_update{2}; +constexpr value_type scrollable{3}; + +constexpr value_type _bitset_size{scrollable + 1}; +} // namespace pos +using value_type = std::bitset; + +constexpr value_type no_cursor{1 << pos::no_cursor}; +constexpr value_type read_only{1 << pos::read_only}; +constexpr value_type for_update{1 << pos::for_update}; +constexpr value_type scrollable{1 << pos::scrollable}; +} // namespace cursor + +namespace field_type { +using value_type = uint8_t; +constexpr value_type Decimal{0x00}; +constexpr value_type Tiny{0x01}; +constexpr value_type Short{0x02}; +constexpr value_type Long{0x03}; +constexpr value_type Float{0x04}; +constexpr value_type Double{0x05}; +constexpr value_type Null{0x06}; +constexpr value_type Timestamp{0x07}; +constexpr value_type LongLong{0x08}; +constexpr value_type Int24{0x09}; +constexpr value_type Date{0x0a}; +constexpr value_type Time{0x0b}; +constexpr value_type DateTime{0x0c}; +constexpr value_type Year{0x0d}; +// not used in protocol: constexpr value_type NewDate{0x0e}; +constexpr value_type Varchar{0x0f}; +constexpr value_type Bit{0x10}; +constexpr value_type Timestamp2{0x11}; +// not used in protocol: constexpr value_type Datetime2{0x12}; +// not used in protocol: constexpr value_type Time2{0x13}; +// not used in protocol: constexpr value_type TypedArray{0x14}; +constexpr value_type Json{0xf5}; +constexpr value_type NewDecimal{0xf6}; +constexpr value_type Enum{0xf7}; +constexpr value_type Set{0xf8}; +constexpr value_type TinyBlob{0xf9}; +constexpr value_type MediumBlob{0xfa}; +constexpr value_type LongBlob{0xfb}; +constexpr value_type Blob{0xfc}; +constexpr value_type VarString{0xfd}; +constexpr value_type String{0xfe}; +constexpr value_type Geometry{0xff}; +} // namespace field_type + +namespace column_def { +namespace pos { +using value_type = uint8_t; +constexpr value_type not_null{0}; +constexpr value_type primary_key{1}; +constexpr value_type unique_key{2}; +constexpr value_type multiple_key{3}; +constexpr value_type blob{4}; +constexpr value_type is_unsigned{5}; +constexpr value_type zerofill{6}; +constexpr value_type binary{7}; +constexpr value_type is_enum{8}; +constexpr value_type auto_increment{9}; +constexpr value_type timestamp{10}; +constexpr value_type set{11}; +constexpr value_type no_default_value{12}; +constexpr value_type on_update{13}; +constexpr value_type numeric{14}; + +constexpr value_type _bitset_size{numeric + 1}; +} // namespace pos +using value_type = std::bitset; + +constexpr value_type not_null{1 << pos::not_null}; +constexpr value_type primary_key{1 << pos::primary_key}; +constexpr value_type unique_key{1 << pos::unique_key}; +constexpr value_type multiple_key{1 << pos::multiple_key}; +constexpr value_type blob{1 << pos::blob}; +constexpr value_type is_unsigned{1 << pos::is_unsigned}; +constexpr value_type zerofill{1 << pos::zerofill}; +constexpr value_type binary{1 << pos::binary}; +constexpr value_type is_enum{1 << pos::is_enum}; +constexpr value_type auto_increment{1 << pos::auto_increment}; +constexpr value_type timestamp{1 << pos::timestamp}; +constexpr value_type set{1 << pos::set}; +constexpr value_type no_default_value{1 << pos::no_default_value}; +constexpr value_type on_update{1 << pos::on_update}; +constexpr value_type numeric{1 << pos::numeric}; +} // namespace column_def + +namespace collation { +using value_type = uint8_t; +constexpr value_type Latin1SwedishCi{0x08}; +constexpr value_type Utf8GeneralCi{0x21}; +constexpr value_type Binary{0x3f}; +} // namespace collation + +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h new file mode 100644 index 0000000..8dd02d7 --- /dev/null +++ b/mysqlrouter/classic_protocol_frame.h @@ -0,0 +1,108 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_FRAME_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_FRAME_H_ + +#include // size_t +#include // uint8_t +#include // move + +namespace classic_protocol { + +namespace frame { +class Header { + public: + constexpr Header(size_t payload_size, uint8_t seq_id) noexcept + : payload_size_{payload_size}, seq_id_{seq_id} {} + + constexpr size_t payload_size() const noexcept { return payload_size_; } + constexpr uint8_t seq_id() const noexcept { return seq_id_; } + + private: + size_t payload_size_{0}; + uint8_t seq_id_{0}; +}; + +constexpr bool operator==(const Header &a, const Header &b) { + return a.payload_size() == b.payload_size() && a.seq_id() == b.seq_id(); +} + +/** + * header of a compressed frame. + * + * used if client and server negotiated compression. + */ +class CompressedHeader { + public: + constexpr CompressedHeader(size_t payload_size, uint8_t seq_id, + size_t uncompressed_size) + : payload_size_{payload_size}, + seq_id_{seq_id}, + uncompressed_size_{uncompressed_size} {} + + constexpr size_t payload_size() const noexcept { return payload_size_; } + constexpr uint8_t seq_id() const noexcept { return seq_id_; } + constexpr size_t uncompressed_size() const noexcept { + return uncompressed_size_; + } + + private: + size_t payload_size_; + uint8_t seq_id_; + size_t uncompressed_size_; +}; + +constexpr bool operator==(const CompressedHeader &a, + const CompressedHeader &b) { + return a.payload_size() == b.payload_size() && a.seq_id() == b.seq_id() && + a.uncompressed_size() == b.uncompressed_size(); +} + +template +class Frame { + public: + using value_type = PayloadType; + + constexpr Frame(uint8_t seq_id, value_type v) + : seq_id_{seq_id}, payload_{std::move(v)} {} + + constexpr uint8_t seq_id() const { return seq_id_; } + constexpr value_type payload() const { return payload_; } + + private: + uint8_t seq_id_; + value_type payload_; +}; + +template +bool operator==(const Frame &a, const Frame &b) { + return a.seq_id() == b.seq_id() && a.payload() == b.payload(); +} + +} // namespace frame + +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h new file mode 100644 index 0000000..dc9a57e --- /dev/null +++ b/mysqlrouter/classic_protocol_message.h @@ -0,0 +1,832 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_MESSAGE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_MESSAGE_H_ + +#include // uint8_t +#include +#include + +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_constants.h" + +namespace classic_protocol { + +/** + * AuthMethod of classic protocol. + * + * classic proto supports negotiating the auth-method via capabilities and + * auth-method names. + */ +class AuthMethod { + public: + AuthMethod(classic_protocol::capabilities::value_type capabilities, + std::string auth_method_name) + : capabilities_{capabilities}, + auth_method_name_{std::move(auth_method_name)} {} + + std::string name() const { + if (auth_method_name_.empty() && + !capabilities_[classic_protocol::capabilities::pos::plugin_auth]) { + if (capabilities_ + [classic_protocol::capabilities::pos::secure_connection]) { + return "mysql_native_password"; + } else { + return "old_password"; + } + } + + return auth_method_name_; + } + + private: + const classic_protocol::capabilities::value_type capabilities_; + const std::string auth_method_name_; +}; + +namespace message { + +namespace server { +class Greeting { + public: + Greeting(uint8_t protocol_version, std::string version, + uint32_t connection_id, std::string auth_method_data, + classic_protocol::capabilities::value_type capabilities, + uint8_t collation, classic_protocol::status::value_type status_flags, + std::string auth_method_name) + : protocol_version_{protocol_version}, + version_{std::move(version)}, + connection_id_{connection_id}, + auth_method_data_{std::move(auth_method_data)}, + capabilities_{capabilities}, + collation_{collation}, + status_flags_{status_flags}, + auth_method_name_{std::move(auth_method_name)} {} + + uint8_t protocol_version() const noexcept { return protocol_version_; } + std::string version() const { return version_; } + std::string auth_method_name() const { return auth_method_name_; } + std::string auth_method_data() const { return auth_method_data_; } + classic_protocol::capabilities::value_type capabilities() const noexcept { + return capabilities_; + } + + void capabilities(classic_protocol::capabilities::value_type caps) { + capabilities_ = caps; + } + + uint8_t collation() const noexcept { return collation_; } + classic_protocol::status::value_type status_flags() const noexcept { + return status_flags_; + } + uint32_t connection_id() const noexcept { return connection_id_; } + + private: + uint8_t protocol_version_; + std::string version_; + uint32_t connection_id_; + std::string auth_method_data_; + classic_protocol::capabilities::value_type capabilities_; + uint8_t collation_; + classic_protocol::status::value_type status_flags_; + std::string auth_method_name_; +}; + +inline bool operator==(const Greeting &a, const Greeting &b) { + return (a.protocol_version() == b.protocol_version()) && + (a.version() == b.version()) && + (a.connection_id() == b.connection_id()) && + (a.auth_method_data() == b.auth_method_data()) && + (a.capabilities() == b.capabilities()) && + (a.collation() == b.collation()) && + (a.status_flags() == b.status_flags()) && + (a.auth_method_name() == b.auth_method_name()); +} + +class AuthMethodSwitch { + public: + AuthMethodSwitch() = default; + + AuthMethodSwitch(std::string auth_method, std::string auth_method_data) + : auth_method_{std::move(auth_method)}, + auth_method_data_{std::move(auth_method_data)} {} + + std::string auth_method() const { return auth_method_; } + std::string auth_method_data() const { return auth_method_data_; } + + private: + std::string auth_method_; + std::string auth_method_data_; +}; + +inline bool operator==(const AuthMethodSwitch &a, const AuthMethodSwitch &b) { + return (a.auth_method_data() == b.auth_method_data()) && + (a.auth_method() == b.auth_method()); +} + +/** + * Opaque auth-method-data message. + * + * used for server messages the handshake phase that aren't + * + * - Ok + * - Error + * - AuthMethodSwitch + * + * like: + * + * - 0x01 (more auth data) + * - 0x03 (fast path) + */ +class AuthMethodData { + public: + AuthMethodData(uint8_t packet_type, std::string auth_method_data) + : packet_type_{packet_type}, + auth_method_data_{std::move(auth_method_data)} {} + + uint8_t packet_type() const noexcept { return packet_type_; } + std::string auth_method_data() const { return auth_method_data_; } + + private: + uint8_t packet_type_; + std::string auth_method_data_; +}; + +inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { + return (a.auth_method_data() == b.auth_method_data()) && + (a.packet_type() == b.packet_type()); +} + +/** + * Ok message. + * + * - affected_rows + * - last_insert_id + * - status_flags + * - warning_count + * - optional message + * - optional server-side tracked session_changes + */ +class Ok { + public: + Ok() = default; + + Ok(uint64_t affected_rows, uint64_t last_insert_id, + classic_protocol::status::value_type status_flags, uint16_t warning_count, + std::string message = "", std::string session_changes = "") + : status_flags_{status_flags}, + warning_count_{warning_count}, + last_insert_id_{last_insert_id}, + affected_rows_{affected_rows}, + message_{std::move(message)}, + session_changes_{std::move(session_changes)} {} + + classic_protocol::status::value_type status_flags() const noexcept { + return status_flags_; + } + uint16_t warning_count() const noexcept { return warning_count_; } + uint64_t last_insert_id() const noexcept { return last_insert_id_; } + uint64_t affected_rows() const noexcept { return affected_rows_; } + + std::string message() const { return message_; } + + /** + * get session-changes. + * + * @returns encoded array of session_track::Field + */ + std::string session_changes() const { return session_changes_; } + + private: + classic_protocol::status::value_type status_flags_{}; + uint16_t warning_count_{}; + uint64_t last_insert_id_{}; + uint64_t affected_rows_{}; + + std::string message_{}; + std::string session_changes_{}; +}; + +inline bool operator==(const Ok &a, const Ok &b) { + return (a.status_flags() == b.status_flags()) && + (a.warning_count() == b.warning_count()) && + (a.last_insert_id() == b.last_insert_id()) && + (a.affected_rows() == b.affected_rows()) && + (a.message() == b.message()) && + (a.session_changes() == b.session_changes()); +} + +/** + * End of Resultset message. + */ +class Eof : public Ok { + public: + using Ok::Ok; + + // 3.23-like constructor + Eof() : Ok(0, 0, 0, 0) {} + + // 4.1-like constructor + Eof(classic_protocol::status::value_type status_flags, uint16_t warning_count) + : Ok(0, 0, status_flags, warning_count) {} +}; + +/** + * Error message. + */ +class Error { + public: + /** + * construct an Error message. + * + * @param error_code error code + * @param message error message + * @param sql_state SQL state + */ + Error(uint16_t error_code, std::string message, + std::string sql_state = "HY000") + : error_code_{error_code}, + message_{std::move(message)}, + sql_state_{std::move(sql_state)} {} + + uint16_t error_code() const noexcept { return error_code_; } + std::string sql_state() const { return sql_state_; } + std::string message() const { return message_; } + + private: + uint16_t error_code_; + std::string message_; + std::string sql_state_; +}; + +inline bool operator==(const Error &a, const Error &b) { + return (a.error_code() == b.error_code()) && + (a.sql_state() == b.sql_state()) && (a.message() == b.message()); +} + +class ColumnMeta { + public: + ColumnMeta(std::string catalog, std::string schema, std::string table, + std::string orig_table, std::string name, std::string orig_name, + uint16_t collation, uint32_t column_length, uint8_t type, + classic_protocol::column_def::value_type flags, uint8_t decimals) + : catalog_{std::move(catalog)}, + schema_{std::move(schema)}, + table_{std::move(table)}, + orig_table_{std::move(orig_table)}, + name_{std::move(name)}, + orig_name_{std::move(orig_name)}, + collation_{collation}, + column_length_{column_length}, + type_{type}, + flags_{flags}, + decimals_{decimals} {} + + std::string catalog() const { return catalog_; } + std::string schema() const { return schema_; } + std::string table() const { return table_; } + std::string orig_table() const { return orig_table_; } + std::string name() const { return name_; } + std::string orig_name() const { return orig_name_; } + uint16_t collation() const { return collation_; } + uint32_t column_length() const { return column_length_; } + uint8_t type() const { return type_; } + classic_protocol::column_def::value_type flags() const { return flags_; } + uint8_t decimals() const { return decimals_; } + + private: + std::string catalog_; + std::string schema_; + std::string table_; + std::string orig_table_; + std::string name_; + std::string orig_name_; + uint16_t collation_; + uint32_t column_length_; + uint8_t type_; + classic_protocol::column_def::value_type flags_; + uint8_t decimals_; +}; + +inline bool operator==(const ColumnMeta &a, const ColumnMeta &b) { + return (a.catalog() == b.catalog()) && (a.schema() == b.schema()) && + (a.table() == b.table()) && (a.orig_table() == b.orig_table()) && + (a.name() == b.name()) && (a.orig_name() == b.orig_name()) && + (a.collation() == b.collation()) && + (a.column_length() == b.column_length()) && (a.type() == b.type()) && + (a.flags() == b.flags()) && (a.decimals() == b.decimals()); +} + +/** + * Row in a resultset. + * + * each Row is sent as its own frame::Frame + * + * each Field in a row may either be NULL or a std::string. + */ +class Row { + public: + using value_type = stdx::expected; + using const_iterator = typename std::vector::const_iterator; + + Row(std::vector fields) : fields_{std::move(fields)} {} + + auto begin() const { return fields_.begin(); } + auto end() const { return fields_.end(); } + + private: + std::vector fields_; +}; + +inline bool operator==(const Row &a, const Row &b) { + auto a_iter = a.begin(); + const auto a_end = a.end(); + auto b_iter = b.begin(); + const auto b_end = b.end(); + + for (; a_iter != a_end && b_iter != b_end; ++a_iter, ++b_iter) { + if (*a_iter != *b_iter) return false; + } + + return true; +} + +class ResultSet { + public: + ResultSet(std::vector column_metas, std::vector rows) + : column_metas_{std::move(column_metas)}, rows_{std::move(rows)} {} + + std::vector column_metas() const { return column_metas_; } + std::vector rows() const { return rows_; } + + private: + std::vector column_metas_; + std::vector rows_; +}; + +/** + * StmtPrepareOk message. + * + * response to a client::StmtPrepare + */ +class StmtPrepareOk { + public: + StmtPrepareOk(uint32_t stmt_id, uint16_t warning_count, + std::vector params, std::vector columns) + : statement_id_{stmt_id}, + warning_count_{warning_count}, + params_{std::move(params)}, + columns_{std::move(columns)} {} + + uint32_t statement_id() const noexcept { return statement_id_; } + uint16_t warning_count() const noexcept { return warning_count_; } + std::vector params() const { return params_; } + std::vector columns() const { return columns_; } + + private: + uint32_t statement_id_; + uint16_t warning_count_; + std::vector params_; + std::vector columns_; +}; + +inline bool operator==(const StmtPrepareOk &a, const StmtPrepareOk &b) { + return (a.statement_id() == b.statement_id()) && + (a.columns() == b.columns()) && (a.params() == b.params()) && + (a.warning_count() == b.warning_count()); +} + +/** + * StmtRow message. + * + * holds the same information as a Row. + * + * needs 'types' to be able to encode a Field of the Row. + */ +class StmtRow : public Row { + public: + StmtRow(std::vector types, + std::vector fields) + : Row{std::move(fields)}, types_{std::move(types)} {} + + std::vector types() const { return types_; } + + private: + std::vector types_; +}; + +} // namespace server + +namespace client { + +class Greeting { + public: + /** + * construct a client::Greeting message. + * + * @param capabilities protocol capabilities of the client + * @param max_packet_size max size of the frame::Frame client wants to send + * @param collation initial collation of connection + * @param username username to authenticate as + * @param auth_method_data auth-method specific data like hashed password + * @param schema initial schema of the newly authenticated session + * @param auth_method_name auth-method the data is for + * @param attributes session-attributes + */ + Greeting(classic_protocol::capabilities::value_type capabilities, + uint32_t max_packet_size, uint8_t collation, std::string username, + std::string auth_method_data, std::string schema, + std::string auth_method_name, std::string attributes) + : capabilities_{capabilities}, + max_packet_size_{max_packet_size}, + collation_{collation}, + username_{std::move(username)}, + auth_method_data_{std::move(auth_method_data)}, + schema_{std::move(schema)}, + auth_method_name_{std::move(auth_method_name)}, + attributes_{std::string(attributes)} {} + + classic_protocol::capabilities::value_type capabilities() const { + return capabilities_; + } + + void capabilities(classic_protocol::capabilities::value_type caps) { + capabilities_ = caps; + } + + uint32_t max_packet_size() const noexcept { return max_packet_size_; } + uint8_t collation() const noexcept { return collation_; } + std::string username() const { return username_; } + std::string auth_method_data() const { return auth_method_data_; } + std::string schema() const { return schema_; } + + /** + * name of the auth-method that was explicitly set. + * + * use classic_protocol::AuthMethod() to get the effective auth-method + * which may be announced though capability flags (like if + * capabilities::plugin_auth wasn't set) + */ + std::string auth_method_name() const { return auth_method_name_; } + + // [key, value]* in Codec encoding + std::string attributes() const { return attributes_; } + + private: + classic_protocol::capabilities::value_type capabilities_; + uint32_t max_packet_size_; + uint8_t collation_; + std::string username_; + std::string auth_method_data_; + std::string schema_; + std::string auth_method_name_; + std::string attributes_; +}; + +inline bool operator==(const Greeting &a, const Greeting &b) { + return (a.capabilities() == b.capabilities()) && + (a.max_packet_size() == b.max_packet_size()) && + (a.collation() == b.collation()) && (a.username() == b.username()) && + (a.auth_method_data() == b.auth_method_data()) && + (a.schema() == b.schema()) && + (a.auth_method_name() == b.auth_method_name()) && + (a.attributes() == b.attributes()); +} + +class Query { + public: + /** + * construct a Query message. + * + * @param statement statement to prepare + */ + Query(std::string statement) : statement_{std::move(statement)} {} + + std::string statement() const { return statement_; } + + private: + std::string statement_; +}; + +inline bool operator==(const Query &a, const Query &b) { + return a.statement() == b.statement(); +} + +class InitSchema { + public: + /** + * construct a InitSchema message. + * + * @param schema schema to change to + */ + InitSchema(std::string schema) : schema_{std::move(schema)} {} + + std::string schema() const { return schema_; } + + private: + std::string schema_; +}; + +inline bool operator==(const InitSchema &a, const InitSchema &b) { + return a.schema() == b.schema(); +} + +class ChangeUser { + public: + /** + * construct a ChangeUser message. + * + * @param username username to change to + * @param auth_method_data auth-method specific data like hashed password + * @param schema initial schema of the newly authenticated session + * @param auth_method_name auth-method the data is for + * @param collation collation + * @param attributes session-attributes + */ + ChangeUser(std::string username, std::string auth_method_data, + std::string schema, uint16_t collation, + std::string auth_method_name, std::string attributes) + : username_{std::move(username)}, + auth_method_data_{std::move(auth_method_data)}, + schema_{std::move(schema)}, + collation_{collation}, + auth_method_name_{std::move(auth_method_name)}, + attributes_{std::move(attributes)} {} + + uint8_t collation() const noexcept { return collation_; } + std::string username() const { return username_; } + std::string auth_method_data() const { return auth_method_data_; } + std::string schema() const { return schema_; } + std::string auth_method_name() const { return auth_method_name_; } + + // [key, value]* in Codec encoding + std::string attributes() const { return attributes_; } + + private: + std::string username_; + std::string auth_method_data_; + std::string schema_; + uint16_t collation_; + std::string auth_method_name_; + std::string attributes_; +}; + +inline bool operator==(const ChangeUser &a, const ChangeUser &b) { + return (a.collation() == b.collation()) && (a.username() == b.username()) && + (a.auth_method_data() == b.auth_method_data()) && + (a.schema() == b.schema()) && + (a.auth_method_name() == b.auth_method_name()) && + (a.attributes() == b.attributes()); +} + +// no content +class ResetConnection {}; + +constexpr bool operator==(const ResetConnection &, const ResetConnection &) { + return true; +} + +// no content +class Statistics {}; + +constexpr bool operator==(const Statistics &, const Statistics &) { + return true; +} + +class StmtPrepare { + public: + /** + * construct a PrepareStmt message. + * + * @param statement statement to prepare + */ + StmtPrepare(std::string statement) : statement_{std::move(statement)} {} + + std::string statement() const { return statement_; } + + private: + std::string statement_; +}; + +inline bool operator==(const StmtPrepare &a, const StmtPrepare &b) { + return a.statement() == b.statement(); +} + +/** + * append data to a parameter of a prepared statement. + */ +class StmtParamAppendData { + public: + /** + * construct a ResetStmt message. + * + * @param statement_id statement-id to close + * @param param_id parameter-id to append data to + * @param data data to append to param_id of statement_id + */ + StmtParamAppendData(uint32_t statement_id, uint16_t param_id, + std::string data) + : statement_id_{statement_id}, + param_id_{param_id}, + data_{std::move(data)} {} + + uint32_t statement_id() const { return statement_id_; } + uint16_t param_id() const { return param_id_; } + std::string data() const { return data_; } + + private: + uint32_t statement_id_; + uint16_t param_id_; + std::string data_; +}; + +inline bool operator==(const StmtParamAppendData &a, + const StmtParamAppendData &b) { + return a.statement_id() == b.statement_id() && a.param_id() == b.param_id() && + a.data() == b.data(); +} + +/** + * execute a prepared statement. + * + * 'values' raw bytes as encoded by the binary codec + */ +class StmtExecute { + public: + using value_type = stdx::expected; + + /** + * construct a ExecuteStmt message. + * + * @param statement_id statement id + * @param flags cursor flags + * @param iteration_count iteration_count + * @param new_params_bound new params bound + * @param types field types of the parameters + * @param values binary-encoded values without length-bytes + */ + StmtExecute(uint32_t statement_id, classic_protocol::cursor::value_type flags, + uint32_t iteration_count, bool new_params_bound, + std::vector types, + std::vector values) + : statement_id_{statement_id}, + flags_{flags}, + iteration_count_{iteration_count}, + new_params_bound_{new_params_bound}, + types_{std::move(types)}, + values_{std::move(values)} {} + + uint32_t statement_id() const noexcept { return statement_id_; } + classic_protocol::cursor::value_type flags() const noexcept { return flags_; } + uint32_t iteration_count() const noexcept { return iteration_count_; } + bool new_params_bound() const noexcept { return new_params_bound_; } + std::vector types() const { + return types_; + } + std::vector values() const { return values_; } + + private: + uint32_t statement_id_; + classic_protocol::cursor::value_type flags_; + uint32_t iteration_count_; + bool new_params_bound_; + std::vector types_; + std::vector values_; +}; + +inline bool operator==(const StmtExecute &a, const StmtExecute &b) { + return a.statement_id() == b.statement_id() && a.flags() == b.flags() && + a.iteration_count() == b.iteration_count() && + a.new_params_bound() == b.new_params_bound() && + a.types() == b.types() && a.values() == b.values(); +} + +/** + * close a prepared statement. + */ +class StmtClose { + public: + /** + * construct a StmtClose message. + * + * @param statement_id statement-id to close + */ + constexpr StmtClose(uint32_t statement_id) : statement_id_{statement_id} {} + + constexpr uint32_t statement_id() const { return statement_id_; } + + private: + uint32_t statement_id_; +}; + +constexpr bool operator==(const StmtClose &a, const StmtClose &b) { + return a.statement_id() == b.statement_id(); +} + +/** + * reset a prepared statement. + */ +class StmtReset { + public: + /** + * construct a ResetStmt message. + * + * @param statement_id statement-id to close + */ + constexpr StmtReset(uint32_t statement_id) : statement_id_{statement_id} {} + + constexpr uint32_t statement_id() const { return statement_id_; } + + private: + uint32_t statement_id_; +}; + +constexpr bool operator==(const StmtReset &a, const StmtReset &b) { + return a.statement_id() == b.statement_id(); +} + +/** + * fetch rows from an executed statement. + */ +class StmtFetch { + public: + /** + * construct a ResetStmt message. + * + * @param statement_id statement-id to close + * @param row_count statement-id to close + */ + constexpr StmtFetch(uint32_t statement_id, uint32_t row_count) + : statement_id_{statement_id}, row_count_{row_count} {} + + constexpr uint32_t statement_id() const { return statement_id_; } + constexpr uint32_t row_count() const { return row_count_; } + + private: + uint32_t statement_id_; + uint32_t row_count_; +}; + +constexpr bool operator==(const StmtFetch &a, const StmtFetch &b) { + return a.statement_id() == b.statement_id() && a.row_count() == b.row_count(); +} + +/** + * fetch rows from an executed statement. + */ +class StmtSetOption { + public: + /** + * construct a ResetStmt message. + * + * @param option options to set + */ + constexpr StmtSetOption(uint16_t option) : option_{option} {} + + constexpr uint16_t option() const { return option_; } + + private: + uint16_t option_; +}; + +constexpr bool operator==(const StmtSetOption &a, const StmtSetOption &b) { + return a.option() == b.option(); +} + +// no content +class Quit {}; + +constexpr bool operator==(const Quit &, const Quit &) { return true; } + +// no content +class Ping {}; + +constexpr bool operator==(const Ping &, const Ping &) { return true; } + +} // namespace client +} // namespace message +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h new file mode 100644 index 0000000..fa95805 --- /dev/null +++ b/mysqlrouter/classic_protocol_session_track.h @@ -0,0 +1,241 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_SESSION_TRACK_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_SESSION_TRACK_H_ + +// session_track as used by message::server::Ok and message::server::Eof + +#include + +namespace classic_protocol { + +namespace session_track { + +/** + * Field of a session-track array. + * + * used in server::Ok and server::Eof + */ +class Field { + public: + Field(uint8_t type, std::string data) : type_{type}, data_{std::move(data)} {} + + uint8_t type() const noexcept { return type_; } + std::string data() const noexcept { return data_; } + + private: + uint8_t type_; + std::string data_; +}; + +inline bool operator==(const Field &a, const Field &b) { + return (a.type() == b.type()) && (a.data() == b.data()); +} + +/** + * system-variable changed. + * + * see: session_track_system_variable + */ +class SystemVariable { + public: + SystemVariable(std::string key, std::string value) + : key_{std::move(key)}, value_{std::move(value)} {} + + std::string key() const noexcept { return key_; } + std::string value() const noexcept { return value_; } + + private: + std::string key_; + std::string value_; +}; + +inline bool operator==(const SystemVariable &a, const SystemVariable &b) { + return (a.key() == b.key()) && (a.value() == b.value()); +} + +/** + * schema changed. + * + * see: session_track_schema + */ +class Schema { + public: + Schema(std::string schema) : schema_{std::move(schema)} {} + + std::string schema() const noexcept { return schema_; } + + private: + std::string schema_; +}; + +inline bool operator==(const Schema &a, const Schema &b) { + return (a.schema() == b.schema()); +} + +/** + * state changed. + * + * see: session_track_session_state + */ +class State { + public: + State(std::string state) : state_{std::move(state)} {} + + std::string state() const noexcept { return state_; } + + private: + std::string state_; +}; + +inline bool operator==(const State &a, const State &b) { + return (a.state() == b.state()); +} + +/** + * gtid changed. + * + * - FixedInt<1> spec + * - gtid-string + * - + * + * see: session_track_gtid + */ +class Gtid { + public: + Gtid(uint8_t spec, std::string gtid) : spec_{spec}, gtid_{std::move(gtid)} {} + + uint8_t spec() const noexcept { return spec_; } + std::string gtid() const { return gtid_; } + + private: + uint8_t spec_; + std::string gtid_; +}; + +inline bool operator==(const Gtid &a, const Gtid &b) { + return (a.spec() == b.spec()) && (a.gtid() == b.gtid()); +} + +/** + * TransactionState changed. + * + * - trx_type: Explicit|Implicit|none + * - read_unsafe: one_or_more|none + * - read_trx: one_or_more|none + * - write_unsafe: one_or_more|none + * - write_trx: one_or_more|none + * - stmt_unsafe: one_or_more|none + * - resultset: one_or_more|none + * - locked_tables: one_or_more|none + * + * implicit transaction: no autocommit, stmt against transactionable table + * without START TRANSACTION + * explicit transaction: START TRANSACTION + * + * read_unsafe: read-operation against non-transactionable table + * read_trx: read-operation against transactionable table + * write_unsafe: write-operation against non-transactionable table + * write_trx: write-operation against transactionable table + * stmt_unsafe: an unusafe statement was executed like RAND() + * resultset: some resultset was sent + * locked_tables: some tables got locked explicitly + * + * 'resultset' may be triggered without 'read_trx' and 'read_unsafe' if a + * 'SELECT' was executed against 'dual' or without table. + * + * see: session_track_transaction_info + */ +class TransactionState { + public: + constexpr TransactionState(char trx_type, char read_unsafe, char read_trx, + char write_unsafe, char write_trx, + char stmt_unsafe, char resultset, + char locked_tables) + : trx_type_{trx_type}, + read_unsafe_{read_unsafe}, + read_trx_{read_trx}, + write_unsafe_{write_unsafe}, + write_trx_{write_trx}, + stmt_unsafe_{stmt_unsafe}, + resultset_{resultset}, + locked_tables_{locked_tables} {} + + constexpr char trx_type() const noexcept { return trx_type_; } + constexpr char read_unsafe() const noexcept { return read_unsafe_; } + constexpr char read_trx() const noexcept { return read_trx_; } + constexpr char write_unsafe() const noexcept { return write_unsafe_; } + constexpr char write_trx() const noexcept { return write_trx_; } + constexpr char stmt_unsafe() const noexcept { return stmt_unsafe_; } + constexpr char resultset() const noexcept { return resultset_; } + constexpr char locked_tables() const noexcept { return locked_tables_; } + + private: + char trx_type_; // T|I|_ + char read_unsafe_; // r|_ + char read_trx_; // R|_ + char write_unsafe_; // w|_ + char write_trx_; // W|_ + char stmt_unsafe_; // s|_ + char resultset_; // S|_ + char locked_tables_; // L|_ +}; + +inline bool operator==(const TransactionState &a, const TransactionState &b) { + return (a.trx_type() == b.trx_type()) && + (a.read_unsafe() == b.read_unsafe()) && + (a.read_trx() == b.read_trx()) && + (a.write_unsafe() == b.write_unsafe()) && + (a.write_trx() == b.write_trx()) && + (a.stmt_unsafe() == b.stmt_unsafe()) && + (a.resultset() == b.resultset()) && + (a.locked_tables() == b.locked_tables()); +} + +/** + * TransactionCharacteristics changed. + * + * see: session_track_transaction_info + */ +class TransactionCharacteristics { + public: + TransactionCharacteristics(std::string statements) + : statements_{std::move(statements)} {} + + std::string statements() const { return statements_; } + + private: + std::string statements_; +}; + +inline bool operator==(const TransactionCharacteristics &a, + const TransactionCharacteristics &b) { + return (a.statements() == b.statements()); +} + +} // namespace session_track +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h new file mode 100644 index 0000000..4977b86 --- /dev/null +++ b/mysqlrouter/classic_protocol_wire.h @@ -0,0 +1,162 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_WIRE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_WIRE_H_ + +#include +#include + +namespace classic_protocol { + +namespace wire { +// basic POD types of the mysql classic-protocol's wire encoding: +// +// - fixed size integers +// - variable sized integers +// - fixed size strings +// - variable sized strings +// - nul-terminated strings +// - NULL + +class VarInt { + public: + using value_type = int64_t; + + constexpr VarInt(value_type v) : v_{v} {} + + constexpr value_type value() const noexcept { return v_; } + + private: + value_type v_; +}; + +constexpr bool operator==(const VarInt &a, const VarInt &b) { + return a.value() == b.value(); +} + +class String { + public: + String() : s_{} {} + String(std::string s) : s_{std::move(s)} {} + + std::string value() const { return s_; } + + private: + std::string s_; +}; + +inline bool operator==(const String &a, const String &b) { + return a.value() == b.value(); +} + +class NulTermString : public String { + public: + using String::String; +}; + +class VarString : public String { + public: + using String::String; +}; + +template +class FixedInt; + +template <> +class FixedInt<1> { + public: + using value_type = uint8_t; + + constexpr FixedInt(value_type v) : v_{std::move(v)} {} + + constexpr value_type value() const { return v_; } + + private: + value_type v_; +}; + +template +constexpr bool operator==(const FixedInt &a, const FixedInt &b) { + return a.value() == b.value(); +} + +template <> +class FixedInt<2> { + public: + using value_type = uint16_t; + + constexpr FixedInt(value_type v) : v_{std::move(v)} {} + + constexpr value_type value() const { return v_; } + + private: + value_type v_; +}; + +template <> +class FixedInt<3> { + public: + using value_type = uint32_t; + + constexpr FixedInt(value_type v) : v_{std::move(v)} {} + + constexpr value_type value() const { return v_; } + + private: + value_type v_; +}; + +template <> +class FixedInt<4> { + public: + using value_type = uint32_t; + + constexpr FixedInt(value_type v) : v_{std::move(v)} {} + + constexpr value_type value() const { return v_; } + + private: + value_type v_; +}; + +template <> +class FixedInt<8> { + public: + using value_type = uint64_t; + + constexpr FixedInt(value_type v) : v_{std::move(v)} {} + + constexpr value_type value() const { return v_; } + + private: + value_type v_; +}; + +class Null {}; + +} // namespace wire +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/partial_buffer_sequence.h b/mysqlrouter/partial_buffer_sequence.h new file mode 100644 index 0000000..7cff68c --- /dev/null +++ b/mysqlrouter/partial_buffer_sequence.h @@ -0,0 +1,155 @@ +/* + Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_PARTIAL_BUFFER_SEQUENCE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_PARTIAL_BUFFER_SEQUENCE_H_ + +#include + +#include "mysql/harness/net_ts/buffer.h" + +namespace classic_protocol { + +/** + * partial buffer sequence. + * + * a sub-range of a buffer-sequence which returns a buffer-sequence itself. + * + * - consume() moves the position in the buffer-sequence forward. + * - prepare() returns a buffer-sequence from the current position up to n bytes + * (or end of sequence) + */ +template +class PartialBufferSequence { + public: + using buffer_sequence_type = BufferSequence; + using sequence_type = std::vector; + + PartialBufferSequence(const buffer_sequence_type &seq) + : seq_{seq}, + seq_cur_{net::buffer_sequence_begin(seq)}, + seq_end_{net::buffer_sequence_end(seq)} {} + + /** + * prepare a buffer-sequence for consumption. + * + * skips empty buffers. + */ + sequence_type prepare(size_t n) const noexcept { + sequence_type buf_seq; + + size_t pos = pos_; + + for (auto seq_cur = seq_cur_; n > 0 && seq_cur != seq_end_; ++seq_cur) { + // slice of the current buffer in the sequence + auto b = net::buffer(net::buffer(*seq_cur) + pos, n); + + // add a buffer to output if it is not empty + if (b.size() > 0) { + buf_seq.push_back(b); + n -= b.size(); + pos = 0; + } + } + + return buf_seq; + } + + /** + * consume n bytes of buffer-sequence. + * + * moves the position in the buffer-sequence forward. + */ + void consume(size_t n) noexcept { + pos_ += n; + consumed_ += n; + + // skip buffers that are already done or empty() + for (; seq_cur_ != seq_end_; ++seq_cur_) { + auto buf = *seq_cur_; + + if (buf.size() <= pos_) { + pos_ -= buf.size(); + } else { + break; + } + } + + // exit-condition: + // + // pos_ < seq_cur_->size() + } + + size_t total_consumed() const noexcept { return consumed_; } + + private: + // note: only captured to call decltype() on it get the return type of + // buffer_sequence_begin() and buffer_sequence_end(). If there is another + // way found to + const BufferSequence &seq_; + + // current pointer into the buffer-sequence + decltype(net::buffer_sequence_begin(seq_)) seq_cur_; + + // end of the buffer sequence + const decltype(net::buffer_sequence_begin(seq_)) seq_end_; + + // position into the first buffer + size_t pos_{}; + + // total consumed bytes + size_t consumed_{}; +}; + +/** + * partial buffer sequence. + * + * specialization for the common case where the BufferSequence is a single + * net::const_buffer. + * + * The partial sequence that's created by prepare() also creates a + * net::const_buffer which allows passing it to this specialization again. + */ +template <> +class PartialBufferSequence { + public: + using buffer_sequence_type = net::const_buffer; + using sequence_type = net::const_buffer; + + PartialBufferSequence(const buffer_sequence_type &seq) : seq_{seq} {} + + sequence_type prepare(size_t n) const noexcept { + return net::buffer(net::buffer(seq_) + pos_, n); + } + + void consume(size_t n) noexcept { pos_ += n; } + + size_t total_consumed() const noexcept { return pos_; } + + private: + const buffer_sequence_type &seq_; + size_t pos_{}; +}; +} // namespace classic_protocol +#endif From bc1955ac1cae3b42cd09b5271b02702cc45becf9 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 30 Sep 2020 09:24:24 +0200 Subject: [PATCH 14/88] WL#12012 tls-endpoint, splicing [5/7] Change ====== - accept TLS connections on the client side if requested by config: client_ssl_mode != DISABLE|PASSTHROUGH - open TLS connection to server if requested by config server_ssl_mode != DISABLED Track protocol handshake and the switch to TLS (and possible switch to PLAIN). splice client and server connections. RB: 25178 --- mysqlrouter/classic_protocol_message.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index dc9a57e..5f8dd4b 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -494,6 +494,8 @@ class Greeting { // [key, value]* in Codec encoding std::string attributes() const { return attributes_; } + void attributes(const std::string &attrs) { attributes_ = attrs; } + private: classic_protocol::capabilities::value_type capabilities_; uint32_t max_packet_size_; From 9dcae0d9e323f290b2f948826b532944d228810f Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 30 Oct 2020 11:17:37 +0100 Subject: [PATCH 15/88] Bug#32146402 remove superseeded protocol classes [3/3] Problem ======= The router's mysql_protocol/src/base_packet.cc based classes are superseeded by the new classic_protocol encoder/decoder. mysql_server_mock is the last user of the old classes. Change ====== - removed old base_packet.cc based codec and its tests - removed mysql_protocol shared library RB: 25472 --- mysqlrouter/mysql_protocol.h | 60 -- mysqlrouter/mysql_protocol/base_packet.h | 645 ------------------ mysqlrouter/mysql_protocol/constants.h | 199 ------ mysqlrouter/mysql_protocol/error_packet.h | 127 ---- mysqlrouter/mysql_protocol/handshake_packet.h | 353 ---------- 5 files changed, 1384 deletions(-) delete mode 100644 mysqlrouter/mysql_protocol.h delete mode 100644 mysqlrouter/mysql_protocol/base_packet.h delete mode 100644 mysqlrouter/mysql_protocol/constants.h delete mode 100644 mysqlrouter/mysql_protocol/error_packet.h delete mode 100644 mysqlrouter/mysql_protocol/handshake_packet.h diff --git a/mysqlrouter/mysql_protocol.h b/mysqlrouter/mysql_protocol.h deleted file mode 100644 index e772761..0000000 --- a/mysqlrouter/mysql_protocol.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (c) 2016, 2020, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED -#define MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED - -#include -#include -#include -#include -#include -#include -#include - -#include "my_compiler.h" -#include "mysql_protocol/base_packet.h" -#include "mysql_protocol/constants.h" -#include "mysql_protocol/error_packet.h" -#include "mysql_protocol/handshake_packet.h" -#include "mysqlrouter/mysql_protocol_export.h" - -namespace mysql_protocol { - -/** @class packet_error - * @brief Exception raised for any errors with MySQL packets - * - */ -MY_COMPILER_DIAGNOSTIC_PUSH() -MY_COMPILER_MSVC_DIAGNOSTIC_IGNORE(4275) -class MYSQL_PROTOCOL_EXPORT packet_error : public std::runtime_error { - public: - explicit packet_error(const std::string &what_arg) - : std::runtime_error(what_arg) {} -}; -MY_COMPILER_DIAGNOSTIC_POP() - -} // namespace mysql_protocol - -#endif // MYSQLROUTER_MYSQL_PROTOCOL_INCLUDED diff --git a/mysqlrouter/mysql_protocol/base_packet.h b/mysqlrouter/mysql_protocol/base_packet.h deleted file mode 100644 index 0dd12e2..0000000 --- a/mysqlrouter/mysql_protocol/base_packet.h +++ /dev/null @@ -1,645 +0,0 @@ -/* - Copyright (c) 2016, 2020, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED -#define MYSQLROUTER_MYSQL_PROTOCOL_BASE_PACKET_INCLUDED - -#include // CHAR_BIT -#include // size_t -#include -#include // range_error -#include -#include // enable_if -#include // move, pair -#include - -#include "constants.h" -#include "harness_assert.h" -#include "mysqlrouter/mysql_protocol_export.h" // MYSQL_PROTOCOL_EXPORT - -// GCC 4.8.4 requires all classes to be forward-declared before being used with -// "friend class ", if they're in a different namespace than the -// friender -#ifdef FRIEND_TEST -#include "mysqlrouter/utils.h" // DECLARE_TEST -DECLARE_TEST(HandshakeResponseParseTest, server_does_not_support_PROTOCOL_41); -DECLARE_TEST(HandshakeResponseParseTest, no_PROTOCOL_41); -DECLARE_TEST(HandshakeResponseParseTest, bad_payload_length); -DECLARE_TEST(HandshakeResponseParseTest, bad_seq_number); -DECLARE_TEST(HandshakeResponseParseTest, max_packet_size); -DECLARE_TEST(HandshakeResponseParseTest, character_set); -DECLARE_TEST(HandshakeResponseParseTest, reserved); -DECLARE_TEST(HandshakeResponseParseTest, username); -DECLARE_TEST(HandshakeResponseParseTest, auth_response); -DECLARE_TEST(HandshakeResponseParseTest, database); -DECLARE_TEST(HandshakeResponseParseTest, auth_plugin); -DECLARE_TEST(HandshakeResponseParseTest, connection_attrs); -DECLARE_TEST(HandshakeResponseParseTest, all); -#endif - -namespace mysql_protocol { - -/** @class Packet - * @brief Interface to MySQL packets - * - * This class is the base class for all the types of MySQL packets - * such as ErrorPacket and HandshakeResponsePacket. - * - */ -class MYSQL_PROTOCOL_EXPORT Packet { - /** @note This class exposes several types of methods for data manipulation. - * - * Packet buffer operations, they work like standard stream operations: - * seek()/tell() - set/get buffer position - * write_*() - write data at current buffer position - * read_*() - read data at current buffer position - * - * Packet buffer operations with specified position: - * read_*_from() - read data from specified buffer position - * - * Packet field setters/getters: - * get_*() - return fields from this class (packet needs to be parsed - * first) set_*() - set fields in this class - */ - - public: - using vector_t = std::vector; - using iterator = vector_t::iterator; - using const_iterator = vector_t::const_iterator; - - //////////////////////////////////////////////////////////////////////////////// - // constructors, destructors, assignment operators - //////////////////////////////////////////////////////////////////////////////// - - /** @brief Header length of packets */ - static const unsigned int kHeaderSize{4}; - - /** @brief Default of max_allowed_packet defined by the MySQL Server (2^30) */ - static const unsigned int kMaxAllowedSize{1073741824}; - - /** @brief Constructor */ - Packet() : Packet(0, Capabilities::ALL_ZEROS) {} - - /** @overload - * - * This constructor takes a buffer, stores the data, and tries to get - * information out of the buffer. - * - * When buffer is 4 or bigger, the payload size and sequence ID of the packet - * is read from the first 4 bytes (packet header). - * - * When allow_partial is false, the payload size is not enforced and buffer - * can be smaller than payload size given in the header. Allow partial packets - * can be useful when all you need is to parse the heade - * - * @param buffer Vector of uint8_t - * @param allow_partial Whether to allow buffers which have incomplete payload - */ - explicit Packet(vector_t buffer, bool allow_partial = false) - : Packet(std::move(buffer), Capabilities::ALL_ZEROS, allow_partial) {} - - /** @overload - * - * @param buffer Vector of uint8_t - * @param capabilities Server or Client capability flags - * @param allow_partial Whether to allow buffers which have incomplete payload - */ - /** @throws mysql_protocol::packet_error */ - Packet(vector_t buffer, Capabilities::Flags capabilities, - bool allow_partial = false); - - /** @overload - * - * @param sequence_id Sequence ID of MySQL packet - */ - explicit Packet(uint8_t sequence_id) - : Packet(sequence_id, Capabilities::ALL_ZEROS) {} - - /** @overload - * - * @param sequence_id Sequence ID of MySQL packet - * @param capabilities Server or Client capability flags - */ - Packet(uint8_t sequence_id, Capabilities::Flags capabilities) - : sequence_id_(sequence_id), capability_flags_(capabilities) {} - - /** @overload */ - /** @throws mysql_protocol::packet_error */ - Packet(std::initializer_list ilist); - - /** @brief Destructor */ - virtual ~Packet() = default; - - /** @brief Copy Constructor */ - Packet(const Packet &) = default; - - /** @brief Move Constructor */ - Packet(Packet &&other) - : msg_(std::move(other.msg_)), - sequence_id_(other.get_sequence_id()), - payload_size_(other.get_payload_size()), - capability_flags_(other.get_capabilities()) { - other.sequence_id_ = 0; - other.capability_flags_ = Capabilities::ALL_ZEROS; - other.payload_size_ = 0; - } - - /** @brief Copy Assignment */ - Packet &operator=(const Packet &) = default; - - /** @brief Move Assigment */ - Packet &operator=(Packet &&other) { - msg_ = std::move(other.msg_); - sequence_id_ = other.sequence_id_; - payload_size_ = other.payload_size_; - capability_flags_ = other.get_capabilities(); - other.sequence_id_ = 0; - other.capability_flags_ = Capabilities::ALL_ZEROS; - other.payload_size_ = 0; - return *this; - } - - //////////////////////////////////////////////////////////////////////////////// - // packet buffer operations: stream interface - //////////////////////////////////////////////////////////////////////////////// - - /** @brief Sets current read/write position used by read_*()/write_*() calls - */ - void seek(size_t position) const { - if (position > msg_.size()) throw std::range_error("seek past EOF"); - position_ = position; - } - - /** @brief Returns current read/write position used by read_*()/write_*() - * calls */ - size_t tell() const { return position_; } - - /** @brief Gets an integral from given packet - * - * Gets an integral from packet buffer at the current position and advances it - * by the length of the read. The size of the integral is deduced from the - * give type but can be overwritten using the size parameter. - * - * Supported are integral of 1, 2, 3, 4, or 8 bytes. To retrieve an 24 bit - * integral it is necessary to use a 32-bit integral type and - * provided the size, for example: - * - * auto id = Packet::read_int_from(buffer, 0, 3); - * - * In MySQL packets, integrals are stored using little-endian format. - * - * @param length size of the integer to parse - * @return integer type - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF - * - * @see read_int_from() - */ - template ::value>> - Type read_int(size_t length = sizeof(Type)) const { - Type res = read_int_from(position_, - length); // throws range_error/runtime_error - position_ += length; - return res; - } - - /** @brief Gets a length encoded integer from given packet - * - * Gets a length encoded integer from packet buffer at the current position - * and advances it by the length of the read. Function also returns the length - * of the parsed integer token (you will need to advance your read position by - * this value to get to next field in the packet) - * - * @return uint64_t - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF, - * std::runtime_error on bad first byte (which determines int length) - * (strong exception safety guarrantee) - * - * @see read_lenenc_uint_from() - */ - uint64_t read_lenenc_uint() const; - - /** @brief Gets raw bytes from packet - * - * Gets raw byes from packet buffer at the current position and advances it - * by the length of the read. - * - * @param length Number of bytes to read - * @return std::vector - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF - * (strong exception safety guarrantee) - * - * @see read_bytes_from() - */ - std::vector read_bytes(size_t length) const; - - /** @brief Gets raw bytes from packet using length encoded size - * - * Gets raw bytes with length encoded size from packet buffer at the current - * position and advances it by the length of the read. - * - * @return std::vector - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF, - * std::runtime_error on bad first byte (which determines int length) - * (strong exception safety guarrantee) - * - * @see read_lenenc_bytes_from() - */ - std::vector read_lenenc_bytes() const; - - /** @brief Gets zero-terminated string from packet - * - * Gets zero-terminated string from packet buffer at the current position and - * advances it by the length of the read. - * - * @return std::string - * - * @see read_string_nul_from() - */ - std::string read_string_nul() const; - - /** @brief Gets raw bytes from packet from position until EOF - * - * Gets raw bytes from packet buffer at the current position and advances it - * by the length of the read. - * - * @return std::vector - * - * @throws std::range_error (std::runtime_error) on start beyond EOF, - * std::runtime_error on zero-terminator not found - * (strong exception safety guarrantee) - * - * @see read_bytes_eof_from() - */ - std::vector read_bytes_eof() const; - - /** @brief Packs and adds an integral to the buffer - * - * Packs and adds an integral to the given buffer. - * - * @param value Integral to add to the packet - * @param length Size of the integral (default: size of integral) - * - */ - template ::value>> - void write_int(T value, size_t length = sizeof(T)) { - msg_.reserve(msg_.size() + length); - while (length-- > 0) { - // Assignment to temporary variable `b` prevents too aggressive inlining - // optimization in some compilers (e.g. GCC 4.9.2 on Solaris, with -O2). - // Without it, `value` wasn't getting updated before push_back() under - // certain conditions, and resulted in filling packet's buffer with - // invalid data. - const auto b = static_cast(value); - update_or_append(b); - value = static_cast(value >> CHAR_BIT); - } - } - - /** @brief Packs and adds a length-encoded integral to the buffer - * - * Packs and adds a length-encoded integral to the given buffer. - * - * @param value Integral to add to the packet - * @return Size of the encoded integral (one of: 1, 3, 4 or 9 bytes) - */ - size_t write_lenenc_uint(uint64_t value); - - /** @brief Adds bytes to the given packet - * - * Adds the given bytes to the buffer. - * - * @param bytes Bytes to add to the packet - * - */ - void write_bytes(const Packet::vector_t &bytes) { - write_bytes_impl(bytes.data(), bytes.size()); - } - - const vector_t &message() const { return msg_; } - - size_t size() const { return msg_.size(); } - - iterator begin() { return msg_.begin(); } - const_iterator begin() const { return msg_.begin(); } - iterator end() { return msg_.end(); } - const_iterator end() const { return msg_.end(); } - - /** @brief Adds a string to the given packet - * - * Adds the given string to the buffer. It does not append a zero-terminator - * after this string. - * - * @param str String to add to the packet - */ - void write_string(const std::string &str) { - write_bytes_impl(reinterpret_cast(str.data()), str.size()); - } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ std::string contains - // signed chars - - /** @brief Adds bytes at the end of the buffer - * - * Appends a byte many times to the packet buffer at EOF and advances current - * position by the length of the write (so that it points to EOF once again) - * - * @param count number of times to append the byte - * @param byte byte to append - * - * @throws std::range_error (std::runtime_error) if current position is not - * currently at EOF - */ - void append_bytes(size_t count, uint8_t byte); - - //////////////////////////////////////////////////////////////////////////////// - // packet buffer operations: direct position interface - //////////////////////////////////////////////////////////////////////////////// - - /** @brief Gets an integral from given packet - * - * Gets an integral from a buffer at the given position. The size of the - * integral is deduced from the give type but can be overwritten using - * the size parameter. - * - * Supported are integral of 1, 2, 3, 4, or 8 bytes. To retrieve an 24 bit - * integral it is necessary to use a 32-bit integral type and - * provided the size, for example: - * - * auto id = Packet::read_int_from(buffer, 0, 3); - * - * In MySQL packets, integrals are stored using little-endian format. - * - * @param position Position where to start reading - * @param length size of the integer to parse - * @return integer type - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF - */ - template ::value>> - Type read_int_from(size_t position, size_t length = sizeof(Type)) const { - harness_assert((length >= 1 && length <= 4) || length == 8); - if (position + length > msg_.size()) - throw std::range_error("start or end beyond EOF"); - - if (length == 1) { - return static_cast(msg_[position]); - } - - uint64_t result = 0; - auto it = msg_.begin() + static_cast(position + length); - while (length-- > 0) { - result <<= 8; - result |= *--it; - } - - return static_cast(result); - } - - /** @brief Gets a length encoded integer from given packet - * - * Function also returns the length of the parsed integer token (you will need - * to advance your read position by this value to get to next field in the - * packet) - * - * @param position Position where to start reading - * @return std::pair - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF, - * std::runtime_error on bad first byte (which determines int length) - * (strong exception safety guarrantee) - */ - std::pair read_lenenc_uint_from(size_t position) const; - - /** @brief Gets a string from packet - * - * Gets a string from the given buffer at the given position. When size is - * not given, we read until the end of the buffer. - * When nil byte is found before we reach the requested size, the string will - * be not be size long (if size is not 0). - * - * When pos is greater than the size of the buffer, an empty string is - * returned. - * - * @param position Position from which to start reading - * @param length Length of the string to read (default 0) - * @return std::string - */ - std::string read_string_from(unsigned long position, - unsigned long length = UINT_MAX) const; - - /** @brief Gets a zero-terminated string from packet - * - * @param position Position from which to start reading - * @return std::string - * - * @throws std::range_error (std::runtime_error) on start beyond EOF, - * std::runtime_error on zero-terminator not found - * (strong exception safety guarrantee) - */ - std::string read_string_nul_from(size_t position) const; - - /** @brief Gets raw bytes from packet - * - * @param position Position from which to start reading - * @param length Number of bytes to read - * @return std::vector - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF - * (strong exception safety guarrantee) - */ - std::vector read_bytes_from(size_t position, size_t length) const; - - /** @brief Gets raw bytes from packet using length encoded size - * - * Function also returns the length of the parsed bytes token (you will need - * to advance your read position by this value to get to next field in the - * packet) - * - * @param position Position from which to start reading - * @return std::pair, size_t> - * - * @throws std::range_error (std::runtime_error) on start or end beyond EOF, - * std::runtime_error on bad first byte (which determines int length) - * (strong exception safety guarrantee) - */ - std::pair, size_t> read_lenenc_bytes_from( - size_t position) const; - - /** @brief Gets raw bytes from packet from position until EOF - * - * @param position Position from which to start reading - * @return std::vector - * - * @throws std::range_error (std::runtime_error) on start beyond EOF, - * std::runtime_error on zero-terminator not found - * (strong exception safety guarrantee) - */ - std::vector read_bytes_eof_from(size_t position) const; - - //////////////////////////////////////////////////////////////////////////////// - // packet buffer operations: static method interface - //////////////////////////////////////////////////////////////////////////////// - - /** @brief Gets the packet sequence ID from supplied buffer - * - * @param header 4-byte header - * @return uint8_t - */ - static uint8_t read_sequence_id(const uint8_t header[4]) noexcept { - return header[3]; - } - - /** @brief Gets the payload size from supplied buffer - * - * @param header 4-byte header - * @return uint32_t payload size of the packet - */ - static uint32_t read_payload_size(const uint8_t header[4]) noexcept { - return header[0] + (header[1] << 8) + (header[2] << 16); - } - - //////////////////////////////////////////////////////////////////////////////// - // packet field setter/getter interface - //////////////////////////////////////////////////////////////////////////////// - - /** @brief Returns header length of MySQL Protocol packet - * - * @return header length (4 bytes) - */ - static constexpr size_t get_header_length() noexcept { return 4; } - - /** @brief Gets the packet sequence ID - * - * @return uint8_t - */ - uint8_t get_sequence_id() const noexcept { return sequence_id_; } - - /** @brief Sets the packet sequence ID - * - * @param id Sequence ID of the packet - */ - void set_sequence_id(uint8_t id) noexcept { sequence_id_ = id; } - - /** @brief Gets server/client capabilities - * - * @return Capabilities - */ - Capabilities::Flags get_capabilities() const noexcept { - return capability_flags_; - } - - /** @brief Gets the payload size - * - * Returns the payload size parsed retrieved from the packet header. - * - * @return uint32_t - */ - uint32_t get_payload_size() const noexcept { return payload_size_; } - - protected: - /** @brief Resets packet - * - * Resets the packet and sets the sequence id. - */ - void reset() { msg_.assign({0x0, 0x0, 0x0, sequence_id_}); } - - /** @brief Updates payload size in packet header - * - * Updates the size of the payload storing it in the first 3 bytes - * of the packet. This method is called after preparing the packet. - */ - void update_packet_size(); - - vector_t msg_; - - /** @brief MySQL packet sequence ID */ - uint8_t sequence_id_{}; - - /** @brief Payload of the packet */ - std::vector payload_; - - /** @brief Payload size */ - uint32_t payload_size_{}; - - /** @brief Capability flags */ - Capabilities::Flags capability_flags_; - - /** @brief read/write position for stream operations */ - mutable size_t position_{}; - - private: - /** @throws mysql_protocol::packet_error */ - void parse_header(bool allow_partial = false); - - void write_bytes_impl(const unsigned char *bytes, size_t length); - - static inline void update_or_append(std::vector &vec, - size_t &position, uint8_t value) { - harness_assert(position <= vec.size()); // allow write before or at EOF - - if (position < vec.size()) - vec[position] = value; - else - vec.push_back(value); - - position++; - } - - inline void update_or_append(size_t &position, uint8_t value) { - update_or_append(msg_, position, value); - } - - inline void update_or_append(uint8_t value) { - update_or_append(position_, value); - } - -#ifdef FRIEND_TEST - FRIEND_TEST(::HandshakeResponseParseTest, - server_does_not_support_PROTOCOL_41); - FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); - FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); - FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); - FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); - FRIEND_TEST(::HandshakeResponseParseTest, character_set); - FRIEND_TEST(::HandshakeResponseParseTest, reserved); - FRIEND_TEST(::HandshakeResponseParseTest, username); - FRIEND_TEST(::HandshakeResponseParseTest, auth_response); - FRIEND_TEST(::HandshakeResponseParseTest, database); - FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); - FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); - FRIEND_TEST(::HandshakeResponseParseTest, all); -#endif -}; - -inline bool operator==(const Packet &a, const Packet &b) { - return a.message() == b.message(); -} - -} // namespace mysql_protocol - -#endif // MYSQLROUTER_MYSQLV10_BASE_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/constants.h b/mysqlrouter/mysql_protocol/constants.h deleted file mode 100644 index 3d68c8c..0000000 --- a/mysqlrouter/mysql_protocol/constants.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED -#define MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED - -#include - -namespace mysql_protocol { - -namespace Capabilities { - -/** Type used to pass capability bitset as one number */ -typedef uint32_t AllFlags; - -/** Type used to pass high/low half of capability bitset as one number */ -typedef uint16_t HalfFlags; - -class Flags { - public: - constexpr Flags() : flags_(0) {} - explicit constexpr Flags(AllFlags flags) : flags_(flags) {} - -// Developer Studio does not like this even if it's deprecated not to have it. -#ifndef __SUNPRO_CC - constexpr Flags(const Flags &) = default; -#endif - - Flags &operator=(const Flags &other) = default; - - constexpr bool operator==(const Flags &other) const { - return flags_ == other.flags_; - } - - constexpr bool operator!=(const Flags &other) const { - return flags_ != other.flags_; - } - - constexpr Flags operator|(const Flags &other) const { - return Flags(flags_ | other.flags_); - } - - constexpr Flags operator&(const Flags &other) const { - return Flags(flags_ & other.flags_); - } - - bool test(const Flags &want) const { - return (flags_ & want.flags_) == want.flags_; - } - Flags &set(const Flags &other) { - flags_ |= other.flags_; - return *this; - } - Flags &clear(const Flags &other) { - flags_ &= ~other.flags_; - return *this; - } - Flags &reset() { - flags_ = 0; - return *this; - } - - constexpr AllFlags bits() const { return flags_; } - constexpr HalfFlags high_16_bits() const { - return static_cast(flags_ >> 16); - } - constexpr HalfFlags low_16_bits() const { - return static_cast(flags_ & 0x0000ffff); - } - - Flags &clear_high_16_bits() { - flags_ &= 0x0000ffff; - return *this; - } - Flags &clear_low_16_bits() { - flags_ &= 0xffff0000; - return *this; - } - - private: - AllFlags flags_; -}; - -/** @brief Capability flags passed in handshake packet. - * - * See https://dev.mysql.com/doc/internals/en/capability-flags.html - * See also MySQL Server source include/mysql_com.h - * To search for documentation of a particular flag, prepend CLIENT_ to its name - * (it was removed on purpose to prevent name collisions when including mysql.h) - **/ -static constexpr Flags LONG_PASSWORD(1 << 0); -static constexpr Flags FOUND_ROWS(1 << 1); -static constexpr Flags LONG_FLAG(1 << 2); -static constexpr Flags CONNECT_WITH_DB(1 << 3); - -static constexpr Flags NO_SCHEMA(1 << 4); -static constexpr Flags COMPRESS(1 << 5); -static constexpr Flags ODBC(1 << 6); -static constexpr Flags LOCAL_FILES(1 << 7); - -static constexpr Flags IGNORE_SPACE(1 << 8); -static constexpr Flags PROTOCOL_41( - 1 - << 9); // Server: supports the 4.1 protocol, Client: uses the 4.1 protocol -static constexpr Flags INTERACTIVE(1 << 10); -static constexpr Flags SSL( - 1 << 11); // Server: supports SSL, Client: switch to SSL - -static constexpr Flags SIG_PIPE(1 << 12); -static constexpr Flags TRANSACTIONS(1 << 13); -static constexpr Flags RESERVED_14(1 << 14); // deprecated in 8.0.3 -static constexpr Flags SECURE_CONNECTION(1 << 15); // deprecated in 8.0.3 - -static constexpr Flags MULTI_STATEMENTS(1 << 16); -static constexpr Flags MULTI_RESULTS(1 << 17); -static constexpr Flags MULTI_PS_MULTO_RESULTS(1 << 18); -static constexpr Flags PLUGIN_AUTH(1 << 19); - -static constexpr Flags CONNECT_ATTRS(1 << 20); -static constexpr Flags PLUGIN_AUTH_LENENC_CLIENT_DATA(1 << 21); -static constexpr Flags EXPIRED_PASSWORDS(1 << 22); -static constexpr Flags SESSION_TRACK(1 << 23); - -static constexpr Flags DEPRECATE_EOF(1 << 24); -static constexpr Flags OPTIONAL_RESULTSET_METADATA( - 1 << 25); // \ docs for 5.7 don't -static constexpr Flags SSL_VERIFY_SERVER_CERT(1UL << 30); // > mention these -static constexpr Flags REMEMBER_OPTIONS(1UL << 31); // / - -// other useful flags (our invention, mysql_com.h does not define them) -static constexpr Flags ALL_ZEROS(0U); -static constexpr Flags ALL_ONES(~0U); - -} // namespace Capabilities - -/** @enum Command - * - * Types of the supported commands from the client. - * - **/ -enum Command { - SLEEP = 0x00, - QUIT = 0x01, - INIT_DB = 0x02, - QUERY = 0x03, - FIELD_LIST = 0x04, - CREATE_DB = 0x05, - DROP_DB = 0x06, - REFRESH = 0x07, - SHUTDOWN = 0x08, - STATISTICS = 0x09, - PROCESS_INFO = 0x0a, - CONNECT = 0x0b, - PROCESS_KILL = 0x0c, - DEBUG = 0x0d, - PING = 0x0e, - TIME = 0x0f, - DELAYED_INSERT = 0x10, - CHANGE_USER = 0x11, - BINLOG_DUMP = 0x12, - TABLE_DUMP = 0x13, - CONNECT_OUT = 0x14, - REGISTER_SLAVE = 0x15, - STMT_PREPARE = 0x16, - STMT_EXECUTE = 0x17, - STMT_SEND_LOG_DATA = 0x18, - STMT_CLOSE = 0x19, - STMT_RESET = 0x1a, - SET_OPTION = 0x1b, - STMT_FETCH = 0x1c, - DAEMON = 0x1d, - BINLOG_DUMP_GTID = 0x1e, - RESET_CONNECTION = 0x1f, -}; - -} // namespace mysql_protocol - -#endif // MYSQLROUTER_MYSQL_PROTOCOL_CONSTANTS_INCLUDED diff --git a/mysqlrouter/mysql_protocol/error_packet.h b/mysqlrouter/mysql_protocol/error_packet.h deleted file mode 100644 index 50c6d06..0000000 --- a/mysqlrouter/mysql_protocol/error_packet.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (c) 2016, 2020, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED -#define MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED - -#include "base_packet.h" - -#include "mysqlrouter/mysql_protocol_export.h" // MYSQL_PROTOCOL_EXPORT - -namespace mysql_protocol { - -/** @class ErrorPacket - * @brief Creates a MySQL error packet - * - * This class creates a MySQL error packet which is send to the MySQL Client. - * - */ -class MYSQL_PROTOCOL_EXPORT ErrorPacket final : public Packet { - public: - /** @brief Constructor - * - * @note The default constructor will set the error code to 1105, message - * "Unknown error", and SQL State "HY000". These values come from the - * MySQL Server server errors. - */ - ErrorPacket() - : Packet(0), code_(1105), message_("Unknown error"), sql_state_("HY000") { - prepare_packet(); - } - - /** @overload - * - * @param buffer bytes of the error packet - * @throws mysql_protocol::packet_error - */ - ErrorPacket(std::vector buffer) - : ErrorPacket(std::move(buffer), Capabilities::ALL_ZEROS) {} - - ErrorPacket(std::vector buffer, Capabilities::Flags capabilities); - - /** @overload - * - * @param sequence_id MySQL Packet number - * @param err_code Error code provided to MySQL client - * @param err_msg Error message provided to MySQL client - * @param sql_state SQL State used in error message - * @param capabilities Server/Client capability flags (default 0) - */ - ErrorPacket(uint8_t sequence_id, uint16_t err_code, std::string err_msg, - std::string sql_state, - Capabilities::Flags capabilities = Capabilities::ALL_ZEROS); - - /** @brief Gets error code - * - * Gets the MySQL error code of the MySQL error packet. - * - * @return unsigned short - */ - unsigned short get_code() const noexcept { return code_; } - - /** @brief Gets error message - * - * Gets the MySQL error message of the MySQL error packet. - * - * @return const std::string reference - */ - const std::string &get_message() const noexcept { return message_; } - - /** @brief Gets SQL state - * - * Gets the SQL state of the MySQL error packet. - * - * @return const std::string reference - */ - const std::string &get_sql_state() const noexcept { return sql_state_; } - - private: - /** @brief Prepares the packet - * - * Prepares the actual MySQL Error packet and stores it. The header is - * created using the sequence id and the size of the payload. - */ - void prepare_packet(); - - /** @brief Parses the packet - * - * Parses the packet from the given buffer. - * - * @throws mysql_protocol::packet_error - */ - void parse_payload(); - - /** @brief MySQL error code */ - unsigned short code_; - - /** @brief MySQL error message */ - std::string message_; - - /** @brief MySQL SQL state */ - std::string sql_state_; -}; - -} // namespace mysql_protocol - -#endif // MYSQLROUTER_MYSQL_PROTOCOL_ERROR_PACKET_INCLUDED diff --git a/mysqlrouter/mysql_protocol/handshake_packet.h b/mysqlrouter/mysql_protocol/handshake_packet.h deleted file mode 100644 index 01d7e42..0000000 --- a/mysqlrouter/mysql_protocol/handshake_packet.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - Copyright (c) 2016, 2020, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED -#define MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED - -#include -#include - -#include "base_packet.h" - -// GCC 4.8.4 requires all classes to be forward-declared before being used with -// "friend class ", if they're in a different namespace than the -// friender -#ifdef FRIEND_TEST -#include "mysqlrouter/utils.h" // DECLARE_TEST -DECLARE_TEST(HandshakeResponseParseTest, server_does_not_support_PROTOCOL_41); -DECLARE_TEST(HandshakeResponseParseTest, no_PROTOCOL_41); -DECLARE_TEST(HandshakeResponseParseTest, bad_payload_length); -DECLARE_TEST(HandshakeResponseParseTest, bad_seq_number); -DECLARE_TEST(HandshakeResponseParseTest, max_packet_size); -DECLARE_TEST(HandshakeResponseParseTest, character_set); -DECLARE_TEST(HandshakeResponseParseTest, reserved); -DECLARE_TEST(HandshakeResponseParseTest, username); -DECLARE_TEST(HandshakeResponseParseTest, auth_response); -DECLARE_TEST(HandshakeResponseParseTest, database); -DECLARE_TEST(HandshakeResponseParseTest, auth_plugin); -DECLARE_TEST(HandshakeResponseParseTest, connection_attrs); -DECLARE_TEST(HandshakeResponseParseTest, all); -#endif - -namespace mysql_protocol { - -/** @brief Default capability flags - * - * Default capability flags - * - * NOTE: do not put inside the class, this makes SunStudio on - * Solaris to generate invalid code - * - */ -static constexpr Capabilities::Flags kDefaultClientCapabilities = - Capabilities::LONG_PASSWORD | Capabilities::LONG_FLAG | - Capabilities::CONNECT_WITH_DB | Capabilities::LOCAL_FILES | - Capabilities::PROTOCOL_41 | Capabilities::TRANSACTIONS | - Capabilities::SECURE_CONNECTION | Capabilities::MULTI_STATEMENTS | - Capabilities::MULTI_RESULTS; - -/** @class HandshakeResponsePacket - * @brief Creates a MySQL handshake response packet - * - * This class creates a MySQL handshake response packet which is send by - * the MySQL client after receiving the server's handshake packet. - * - */ -class MYSQL_PROTOCOL_EXPORT HandshakeResponsePacket final : public Packet { - public: - /** @brief Constructor - * - * This version of constructor just creates an uninitialized packet - */ - HandshakeResponsePacket() - : Packet(0), - username_(""), - password_(""), - char_set_(8), - auth_plugin_("mysql_native_password"), - auth_response_({}) { - prepare_packet(); - } - - /** @overload - * - * This version of constructor takes in packet bytes, parses it and writes - * results in object's fields - * - * @param buffer Packet payload (including packet header) - * @param server_capabilities Capabilities sent by the server in Handshake - * Packet; see note in parse_payload() - * @param auto_parse_payload Disables automatic parsing of payload if set to - * false Note that header is still parsed (sequence_id_ and payload_size_ are - * set) - * - * @throws std::runtime_error on unrecognised or invalid packet, when parsing - */ - HandshakeResponsePacket( - std::vector buffer, bool auto_parse_payload = false, - Capabilities::Flags server_capabilities = Capabilities::ALL_ZEROS) - : Packet(buffer) { - if (auto_parse_payload) parse_payload(server_capabilities); - } - - /** @overload - * - * This version of constructor takes in fields, and generates packet bytes - * - * @param sequence_id MySQL Packet number - * @param auth_response Authentication data from the MySQL server handshake - * @param username MySQL username to use - * @param password MySQL password to use - * @param database MySQL database to use when connecting (default is empty) - * @param char_set MySQL character set code (default 8, latin1) - * @param auth_plugin MySQL authentication plugin name (default - * 'mysql_native_password') - */ - HandshakeResponsePacket(uint8_t sequence_id, - std::vector auth_response, - std::string username, std::string password, - std::string database = "", unsigned char char_set = 8, - std::string auth_plugin = "mysql_native_password"); - - /** @brief Parses packet payload, results written to object's field - * - * @param server_capabilities Capabilities sent by the server in Handshake - * Packet, see note below - * - * @throws std::runtime_error on unrecognised or invalid packet - * - * @note MySQL Protocol has a quirk: In the Handshake Packet, server sends to - * client its capability flags, then in Handshake Response Packet, client - * sends its own, possibly including some that the server did not advertise. - * Despite advertising these flags unique to client, it does not actually use - * them. This is vital in understanding packets. If data chunk dataX depeneded - * on capability X, then how should a packet be parsed when it comes in? - * {data1, data2, dataX, data3, data4} - * or - * {data1, data2, data3, data4} - * Apparently the latter - */ - void parse_payload(Capabilities::Flags server_capabilities) { - init_parser_if_not_initialized(); - parser_->parse(server_capabilities); - } - - /** @brief returns username specified in the packet */ - const std::string &get_username() const { return username_; } - - /** @brief returns database name specified in the packet */ - const std::string &get_database() const { return database_; } - - /** @brief returns character set specified in the packet */ - uint8_t get_character_set() const { return char_set_; } - - /** @brief returns auth-plugin-name specified in the packet */ - const std::string &get_auth_plugin() const { return auth_plugin_; } - - /** @brief returns auth-plugin-data specified in the packet */ - const std::vector &get_auth_response() const { - return auth_response_; - } - - /** @brief returns max packet size specified in the packet */ - uint32_t get_max_packet_size() const { return max_packet_size_; } - - /** @brief (debug tool) parse packet contents and print this info on stdout */ - void debug_dump() { - init_parser_if_not_initialized(); - parser_->debug_dump(); - } - - private: - class Parser; - - /** @brief Prepares the packet - * - * Prepares the actual MySQL Error packet and stores it. The header is - * created using the sequence id and the size of the payload. - */ - void prepare_packet(); - - /** @brief Initializes Parser needed to parse the packet payload */ - void init_parser_if_not_initialized() { - if (!parser_) { - if (Parser41::is_protocol41(*this)) { - parser_.reset(new Parser41(*this)); - } else if (Parser320::is_protocol320(*this)) { - parser_.reset(new Parser320(*this)); - } else { - assert(0); - } - } - } - - /** @brief MySQL username */ - std::string username_; - - /** @brief MySQL password */ - std::string password_; - - /** @brief MySQL database */ - std::string database_; - - /** @brief MySQL character set */ - unsigned char char_set_; - - /** @brief MySQL authentication plugin name */ - std::string auth_plugin_; - - /** @brief MySQL auth-response */ - std::vector auth_response_; - - /** @brief Max size that of a command packet that the client wants to send to - * the server */ - uint32_t max_packet_size_; - - /** @brief Parser used to parse this packet */ - std::unique_ptr parser_; - - class MYSQL_PROTOCOL_EXPORT Parser { - public: - Parser() = default; - // disable copy as it isn't needed right now. Feel free to enable - // must be explicitly defined though. - explicit Parser(const Parser &) = delete; - Parser &operator=(const Parser &) = delete; - virtual ~Parser() = default; - - virtual void parse(Capabilities::Flags server_capabilities) = 0; - - // debug tools - static std::string bytes2str(const uint8_t *bytes, size_t length, - size_t bytes_per_group = 4) noexcept; - virtual void debug_dump() const = 0; - }; - - class MYSQL_PROTOCOL_EXPORT Parser41 : public Parser { - public: - Parser41(HandshakeResponsePacket &packet) : packet_(packet) {} - - /** @brief Tests if handshake response packet has PROTOCOL_41 flag set - * - * This is a very simple method, it only checks that single flag and does - * nothing else (in particular, it doesn't perform any kind of validation) - */ - static bool is_protocol41(const HandshakeResponsePacket &packet); - - /** @brief Parses handshake response packet - * - * This method assumes that the current packet is a PROTOCOL41 handshake - * response. - * - * @param server_capabilities Capability flags of the server. Client's flags - * will be &-ed with them before applying rules for packet parsing. - * @throws std::runtime_error on unrecognised or invalid packet - */ - void parse(Capabilities::Flags server_capabilities) override; - - // debug tools - void debug_dump() const noexcept override; - - private: - /** @brief Helper functions called by parse() - * - * All these methods throw std::runtime_error on parse errors; in - * particular, std::range_error (std::runtime_error specialization) is - * thrown on EOF - * */ - void part1_max_packet_size(); - void part2_character_set(); - void part3_reserved(); - void part4_username(); - void part5_auth_response(); - void part6_database(); - void part7_auth_plugin(); - void part8_connection_attrs(); - - HandshakeResponsePacket &packet_; - Capabilities::Flags effective_capability_flags_; - -#ifdef FRIEND_TEST - FRIEND_TEST(::HandshakeResponseParseTest, - server_does_not_support_PROTOCOL_41); - FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); - FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); - FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); - FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); - FRIEND_TEST(::HandshakeResponseParseTest, character_set); - FRIEND_TEST(::HandshakeResponseParseTest, reserved); - FRIEND_TEST(::HandshakeResponseParseTest, username); - FRIEND_TEST(::HandshakeResponseParseTest, auth_response); - FRIEND_TEST(::HandshakeResponseParseTest, database); - FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); - FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); - FRIEND_TEST(::HandshakeResponseParseTest, all); -#endif - }; - - class MYSQL_PROTOCOL_EXPORT Parser320 : public Parser { - public: - Parser320(HandshakeResponsePacket &packet) : packet_(packet) {} - - /** @brief Tests if handshake response packet DOES NOT have PROTOCOL_41 flag - * set - * - * This is a very simple method, it only checks that single flag and does - * nothing else (in particular, it doesn't perform any kind of validation) - */ - static bool is_protocol320(const HandshakeResponsePacket &packet); - - /** @brief Parses handshake response packet - * - * Currently not implemented - */ - void parse(Capabilities::Flags server_capabilities) override; - void debug_dump() const override; - - HandshakeResponsePacket &packet_; - Capabilities::Flags effective_capability_flags_; - }; - -#ifdef FRIEND_TEST - FRIEND_TEST(::HandshakeResponseParseTest, - server_does_not_support_PROTOCOL_41); - FRIEND_TEST(::HandshakeResponseParseTest, no_PROTOCOL_41); - FRIEND_TEST(::HandshakeResponseParseTest, bad_payload_length); - FRIEND_TEST(::HandshakeResponseParseTest, bad_seq_number); - FRIEND_TEST(::HandshakeResponseParseTest, max_packet_size); - FRIEND_TEST(::HandshakeResponseParseTest, character_set); - FRIEND_TEST(::HandshakeResponseParseTest, reserved); - FRIEND_TEST(::HandshakeResponseParseTest, username); - FRIEND_TEST(::HandshakeResponseParseTest, auth_response); - FRIEND_TEST(::HandshakeResponseParseTest, database); - FRIEND_TEST(::HandshakeResponseParseTest, auth_plugin); - FRIEND_TEST(::HandshakeResponseParseTest, connection_attrs); - FRIEND_TEST(::HandshakeResponseParseTest, all); -#endif - -}; // class HandshakeResponsePacket - -} // namespace mysql_protocol - -#endif // MYSQLROUTER_MYSQL_PROTOCOL_HANDSHAKE_PACKET_INCLUDED From b1ce61879cce9c1b3150e5c8f30e8e51e29858cb Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Thu, 21 Jan 2021 02:29:49 +0100 Subject: [PATCH 16/88] BUG#32368342: UPDATE COPYRIGHT HEADERS TO 2021 Update all copyright headers in 8.0 which were not already updated in 5.7, to 2021 and to the current copyright format. This patch was generated by a script. RB#25757 --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- mysqlrouter/partial_buffer_sequence.h | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index ae30f40..124b006 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index 1291991..73bafab 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index fec210d..17d918f 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 2a8f7ff..e99c8f7 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index e293c3f..1849026 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 1962166..664de2c 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 96265e3..5c2058d 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 282cc80..ce6cb8a 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 4ff6152..3799192 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 8dd02d7..fbecff7 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 5f8dd4b..401eb11 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index fa95805..7379ee2 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 4977b86..61e4f66 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/partial_buffer_sequence.h b/mysqlrouter/partial_buffer_sequence.h index 7cff68c..a5deccb 100644 --- a/mysqlrouter/partial_buffer_sequence.h +++ b/mysqlrouter/partial_buffer_sequence.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2019, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From b8310dfbfb2a2bfd7d8f489039afd86aa5bc5415 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 1 Mar 2021 08:35:29 +0100 Subject: [PATCH 17/88] Bug#32607646 session_track::State is a single-byte Issue ===== SessionTracker::State allows the server to announce that the last command changed the sessions state. It contains a boolean value "1". Our Codec<> treated that field as VarString, instead of a fixed-size-one-byte-string. Change ====== - treat SessionTracker::State as single byte RB: 26112 --- mysqlrouter/classic_protocol_codec_session_track.h | 4 ++-- mysqlrouter/classic_protocol_session_track.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 5c2058d..8f92c29 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -168,7 +168,7 @@ class Codec : public impl::EncodeBase> { template auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarString(v_.state())).result(); + return accu.step(wire::FixedInt<1>(v_.state())).result(); } public: @@ -196,7 +196,7 @@ class Codec const ConstBufferSequence &buffers, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffers, caps); - auto state_res = accu.template step(); + auto state_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 7379ee2..219fd63 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -102,15 +102,15 @@ inline bool operator==(const Schema &a, const Schema &b) { */ class State { public: - State(std::string state) : state_{std::move(state)} {} + constexpr State(int8_t state) : state_{std::move(state)} {} - std::string state() const noexcept { return state_; } + constexpr int8_t state() const noexcept { return state_; } private: - std::string state_; + int8_t state_; }; -inline bool operator==(const State &a, const State &b) { +constexpr inline bool operator==(const State &a, const State &b) { return (a.state() == b.state()); } From 2e79f17ad66bdfaa736c07f4267b4212ef4fa951 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 1 Mar 2021 08:27:24 +0100 Subject: [PATCH 18/88] Bug#32661958 typos in variable names Issue ===== the TransactionCharacteristics message contains a "characteristics" field which is exposed by the "statement()" method. Change ====== - renamed variable names to match the meaning of the data they contain. RB: 26113 --- mysqlrouter/classic_protocol_codec_session_track.h | 10 +++++----- mysqlrouter/classic_protocol_session_track.h | 11 ++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 8f92c29..68199ed 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -82,7 +82,7 @@ class Codec "buffers MUST be a const buffer sequence"); impl::DecodeBufferAccumulator accu(buffers, caps); - const auto try_type_res = accu.template step>(); + const auto trx_type_res = accu.template step>(); const auto read_unsafe_res = accu.template step>(); const auto read_trx_res = accu.template step>(); const auto write_unsafe_res = accu.template step>(); @@ -95,7 +95,7 @@ class Codec return std::make_pair( accu.result().value(), - value_type(try_type_res->value(), read_unsafe_res->value(), + value_type(trx_type_res->value(), read_unsafe_res->value(), read_trx_res->value(), write_unsafe_res->value(), write_trx_res->value(), stmt_unsafe_res->value(), resultset_res->value(), locked_tables_res->value())); @@ -116,7 +116,7 @@ class Codec Codec> { template auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarString(v_.statements())).result(); + return accu.step(wire::VarString(v_.trx_state())).result(); } public: @@ -146,12 +146,12 @@ class Codec "buffers MUST be a const buffer sequence"); impl::DecodeBufferAccumulator accu(buffers, caps); - const auto statements_res = accu.template step(); + const auto characteristics_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), - value_type(statements_res->value())); + value_type(characteristics_res->value())); } private: diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 219fd63..ddd5674 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -221,18 +221,19 @@ inline bool operator==(const TransactionState &a, const TransactionState &b) { */ class TransactionCharacteristics { public: - TransactionCharacteristics(std::string statements) - : statements_{std::move(statements)} {} + TransactionCharacteristics(std::string trx_state) + : trx_state_{std::move(trx_state)} {} - std::string statements() const { return statements_; } + // encoded VarString of TransactionState + std::string trx_state() const { return trx_state_; } private: - std::string statements_; + std::string trx_state_; }; inline bool operator==(const TransactionCharacteristics &a, const TransactionCharacteristics &b) { - return (a.statements() == b.statements()); + return (a.trx_state() == b.trx_state()); } } // namespace session_track From 268cac1cba9da6969c41ca035bc6c5cc0bf4cffd Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 1 Mar 2021 08:28:11 +0100 Subject: [PATCH 19/88] Bug#32661979 ColumnCount and ListFields message are missing Issue ===== The router's protocol codec is missing: - ColumnCount message - ListFields codec Clients like 'mysql' still use the deprecated COM_FIELD_LIST command which is different protocol flow than the COM_QUERY command. Change ====== - add client::ListFields - make ColumnCount its own message RB: 26114 --- mysqlrouter/classic_protocol_codec_message.h | 90 ++++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 53 ++++++++++++ 2 files changed, 143 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 664de2c..759af22 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -674,6 +674,46 @@ class Codec const value_type v_; }; +/** + * codec for ColumnCount message. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::VarInt(v_.count())).result(); + } + + public: + using value_type = message::server::ColumnCount; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr size_t max_size() noexcept { + return std::numeric_limits::max(); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto count_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(count_res->value())); + } + + private: + const value_type v_; +}; + /** * Codec of ColumnMeta. * @@ -1297,6 +1337,56 @@ class Codec const value_type v_; }; +/** + * codec for client's ListFields command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::NulTermString(v_.table_name())) + .step(wire::String(v_.wildcard())) + .result(); + } + + public: + using value_type = message::client::ListFields; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0x04; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto table_name_res = accu.template step(); + auto wildcard_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(table_name_res->value(), wildcard_res->value())); + } + + private: + const value_type v_; +}; + /** * codec for client's Prepared Statement command. */ diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 401eb11..3c2c3e2 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -286,6 +286,28 @@ inline bool operator==(const Error &a, const Error &b) { (a.sql_state() == b.sql_state()) && (a.message() == b.message()); } +/** + * ColumnCount message. + */ +class ColumnCount { + public: + /** + * construct an ColumnCount message. + * + * @param count column count + */ + constexpr ColumnCount(uint64_t count) : count_{count} {} + + constexpr uint64_t count() const noexcept { return count_; } + + private: + uint64_t count_; +}; + +constexpr inline bool operator==(const ColumnCount &a, const ColumnCount &b) { + return (a.count() == b.count()); +} + class ColumnMeta { public: ColumnMeta(std::string catalog, std::string schema, std::string table, @@ -536,6 +558,37 @@ inline bool operator==(const Query &a, const Query &b) { return a.statement() == b.statement(); } +class ListFields { + public: + /** + * list columns of a table. + * + * If 'wildcard' is empty the server will execute: + * + * SHOW COLUMNS FROM table_name + * + * Otherwise: + * + * SHOW COLUMNS FROM table_name LIKE wildcard + * + * @param table_name name of table to list + * @param wildcard wildcard + */ + ListFields(std::string table_name, std::string wildcard) + : table_name_{std::move(table_name)}, wildcard_{std::move(wildcard)} {} + + std::string table_name() const { return table_name_; } + std::string wildcard() const { return wildcard_; } + + private: + std::string table_name_; + std::string wildcard_; +}; + +inline bool operator==(const ListFields &a, const ListFields &b) { + return a.table_name() == b.table_name() && a.wildcard() == b.wildcard(); +} + class InitSchema { public: /** From e7fae13fcfe9198125c75e3aa0b9d3c3cd0c3338 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 1 Mar 2021 08:30:13 +0100 Subject: [PATCH 20/88] Bug#32662868 decoding Eof-session-track broken Issue ===== The EOF codec already supports the SessionTracker decoding, but doesn't handle the case where the SessionTracker part is announced, but completely empty. Change ====== - added tests for Eof + Session Tracker RB: 26118 --- mysqlrouter/classic_protocol_codec_message.h | 18 +++++++++++++----- mysqlrouter/classic_protocol_message.h | 5 +++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 759af22..9fd259e 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -412,7 +412,8 @@ class Codec stdx::expected message_res; stdx::expected session_changes_res; if (caps[capabilities::pos::session_track]) { - auto var_message_res = accu.template step(); + // if there is more data. + const auto var_message_res = accu.template try_step(); if (var_message_res) { // set the message from the var-string message_res = var_message_res.value(); @@ -477,9 +478,14 @@ class Codec } if (caps()[capabilities::pos::session_track]) { - accu.step(wire::VarString(v_.message())); - if (v_.status_flags()[status::pos::session_state_changed]) { - accu.step(wire::VarString(v_.session_changes())); + if (!v_.message().empty() || + v_.status_flags()[status::pos::session_state_changed]) { + // only write message and session-changes if both of them aren't + // empty. + accu.step(wire::VarString(v_.message())); + if (v_.status_flags()[status::pos::session_state_changed]) { + accu.step(wire::VarString(v_.session_changes())); + } } } else { accu.step(wire::String(v_.message())); @@ -551,7 +557,9 @@ class Codec stdx::expected message_res; stdx::expected session_state_info_res; if (caps[capabilities::pos::session_track]) { - const auto var_message_res = accu.template step(); + // when session-track is supported, the 'message' part is a VarString. + // But only if there is actually session-data and the message has data. + const auto var_message_res = accu.template try_step(); if (var_message_res) { // set the message from the var-string message_res = var_message_res.value(); diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 3c2c3e2..2638181 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -251,6 +251,11 @@ class Eof : public Ok { // 4.1-like constructor Eof(classic_protocol::status::value_type status_flags, uint16_t warning_count) : Ok(0, 0, status_flags, warning_count) {} + + Eof(classic_protocol::status::value_type status_flags, uint16_t warning_count, + std::string message, std::string session_changes) + : Ok(0, 0, status_flags, warning_count, std::move(message), + std::move(session_changes)) {} }; /** From fdb831ba54549bd24dc41a750138e781f720fea1 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 1 Mar 2021 08:32:39 +0100 Subject: [PATCH 21/88] Bug#32663921 mysql-4.0 client-greeting decode missing nul-terminator Issue ===== When a 3.23/4.0 client connects to the mock-server without specifying an initial schema, it closes the connection and reports: Error: missing nul-terminator The decoder for client::Greeting wrongly expects a nul-terminator. Issue ===== - use the shared-capabilities instead of the server-capabilities to check if the client is sending a schema-name - added tests to verify more combinations RB: 26119 --- mysqlrouter/classic_protocol_codec_message.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 9fd259e..c658100 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2197,7 +2197,8 @@ class Codec stdx::expected auth_method_data_res; stdx::expected schema_res; - if (caps[classic_protocol::capabilities::pos::connect_with_schema]) { + if (shared_capabilities + [classic_protocol::capabilities::pos::connect_with_schema]) { auto res = accu.template step(); if (!res) return stdx::make_unexpected(res.error()); From e577c91a499af390cce9001e57ace68ce9c763ee Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 1 Mar 2021 08:35:59 +0100 Subject: [PATCH 22/88] Bug#32667060 add session_track::Gtid codec Issue ===== Codec is missing for session_track::Gtid. Change ====== - added codec for session_track::Gtid - added tests for: - server::Ok with session_track::Gtid - session_track::Field with session_track::Gtid RB: 26123 --- .../classic_protocol_codec_session_track.h | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 68199ed..32174c8 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -315,6 +315,67 @@ class Codec const value_type v_; }; +/** + * codec for session_track::Gtid. + * + * format: + * + * - FixedInt spec (only 0 is in use for now) + * - VarString payload (payload accoring to spec). + * + * payload for spec 0: + * - GTID in human-readable form like 4dd0f9d5-3b00-11eb-ad70-003093140e4e:23929 + * + * part of session_track::Field + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(v_.spec())) + .step(wire::VarString(v_.gtid())) + .result(); + } + + public: + using value_type = session_track::Gtid; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + /** + * decode a session_track::Gtid from a buffer-sequence. + * + * @param buffers input buffser sequence + * @param caps protocol capabilities + * + * @retval std::pair on success, with bytes + * processed + * @retval codec_errc::not_enough_input not enough data to parse the whole + * message + */ + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto spec_res = accu.template step>(); + auto gtid_res = accu.template step(); + + if (!accu.result()) return accu.result().get_unexpected(); + + return std::make_pair(accu.result().value(), + value_type(spec_res->value(), gtid_res->value())); + } + + private: + const value_type v_; +}; + /** * codec for session-track's Field. * From 4ac7838053128cbefd1823c3773b10277f7144d4 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 23 Mar 2021 11:17:52 +0100 Subject: [PATCH 23/88] Bug#32667486 ensure cmd-byte is unique Issue ===== The Codec's cmd_byte() method uses a hardcoded number which uniquely identifies a command. Currently, it is up to the developer to ensure the number is unique and not shared with other commands. Change ====== - use an 'enum class' to ensure uniqueness of the cmd-bytes RB: 26124 --- mysqlrouter/classic_protocol_codec_message.h | 97 ++++++++++++++++---- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index c658100..6839476 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1187,6 +1187,41 @@ class CodecSimpleCommand } }; +enum class CommandByte { + Quit = 0x01, + InitSchema, + Query, + ListFields, + CreateDb, + DropDb, + Refresh, + Shutdown, + Statistics, + ProcessInfo, + Connect, + ProcessKill, + Debug, + Ping, + Time, + DelayedInsert, + ChangeUser, + BinlogDump, + TableDump, + ConnectOut, + RegisterSlave, + StmtPrepare, + StmtExecute, + StmtSendLongData, + StmtClose, + StmtReset, + SetOption, + StmtFetch, + Deamon, + BinlogDumpGtid, + ResetConnection, + Clone +}; + /** * codec for client's Quit command. */ @@ -1200,7 +1235,9 @@ class Codec constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} - constexpr static uint8_t cmd_byte() noexcept { return 0x01; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Quit); + } }; /** @@ -1216,7 +1253,9 @@ class Codec constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} - constexpr static uint8_t cmd_byte() noexcept { return 0x1f; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::ResetConnection); + } }; /** @@ -1232,7 +1271,9 @@ class Codec constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} - constexpr static uint8_t cmd_byte() noexcept { return 0x0e; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Ping); + } }; /** @@ -1248,7 +1289,9 @@ class Codec constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} - constexpr static uint8_t cmd_byte() noexcept { return 0x09; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Statistics); + } }; /** @@ -1273,7 +1316,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - constexpr static uint8_t cmd_byte() noexcept { return 0x02; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::InitSchema); + } template static stdx::expected, std::error_code> decode( @@ -1320,7 +1365,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x03; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Query); + } template static stdx::expected, std::error_code> decode( @@ -1368,7 +1415,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x04; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::ListFields); + } template static stdx::expected, std::error_code> decode( @@ -1417,7 +1466,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x16; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::StmtPrepare); + } template static stdx::expected, std::error_code> decode( @@ -1543,7 +1594,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x17; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::StmtExecute); + } /** * decode a sequence of buffers into a message::client::ExecuteStmt. @@ -1753,7 +1806,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x18; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::StmtSendLongData); + } template static stdx::expected, std::error_code> decode( @@ -1803,7 +1858,9 @@ class Codec constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x19; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::StmtClose); + } template static stdx::expected, std::error_code> decode( @@ -1850,7 +1907,9 @@ class Codec constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x1a; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::StmtReset); + } template static stdx::expected, std::error_code> decode( @@ -1876,7 +1935,7 @@ class Codec }; /** - * codec for client's Fetch Cursor command. + * codec for client's SetOption Cursor command. */ template <> class Codec @@ -1897,7 +1956,9 @@ class Codec constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x1b; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::SetOption); + } template static stdx::expected, std::error_code> decode( @@ -1945,7 +2006,9 @@ class Codec constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x1c; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::StmtFetch); + } template static stdx::expected, std::error_code> decode( @@ -2296,7 +2359,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - static constexpr uint8_t cmd_byte() noexcept { return 0x11; } + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::ChangeUser); + } template static stdx::expected, std::error_code> decode( From 417a152f251fdc8d6da421cf669fc53ffb2fda59 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:33:53 +0100 Subject: [PATCH 24/88] Bug#32667681 codec server::SendFileRequest Issue ===== The server may send a SendFileRequest after a server::Query to ask the client to send the payload of a file. The codec lib is current missing server::SendFileRequest. Change ====== - add a server::SendFileRequest POD - add a codec for server::SendFileRequest - add tests for the codec and POD RB: 26125 --- mysqlrouter/classic_protocol_codec_message.h | 54 ++++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 19 +++++++ 2 files changed, 73 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 6839476..2c7b60b 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -34,6 +34,7 @@ #include "mysqlrouter/classic_protocol_codec_base.h" #include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_codec_wire.h" +#include "mysqlrouter/classic_protocol_constants.h" #include "mysqlrouter/classic_protocol_message.h" #include "mysqlrouter/classic_protocol_wire.h" #include "mysqlrouter/partial_buffer_sequence.h" @@ -891,6 +892,59 @@ class Codec const value_type v_; }; +/** + * codec for server's SendFileRequest response. + * + * sent as response after client::Query + * + * layout: + * + * 0xfb + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::String(v_.filename())) + .result(); + } + + public: + using value_type = message::server::SendFileRequest; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + static constexpr uint8_t cmd_byte() noexcept { return 0xfb; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto filename_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(filename_res->value())); + } + + private: + const value_type v_; +}; + /** * codec for a Row from the server. */ diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 2638181..f23a83f 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -464,6 +464,25 @@ class StmtRow : public Row { std::vector types_; }; +class SendFileRequest { + public: + /** + * construct a SendFileRequest message. + * + * @param filename filename + */ + SendFileRequest(std::string filename) : filename_{std::move(filename)} {} + + std::string filename() const { return filename_; } + + private: + std::string filename_; +}; + +inline bool operator==(const SendFileRequest &a, const SendFileRequest &b) { + return a.filename() == b.filename(); +} + } // namespace server namespace client { From ff11cb9f5350110224bee80a00892f67e681953a Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:34:23 +0100 Subject: [PATCH 25/88] Bug#32670016 codec server::StmtPrepareOk Issue ===== Codec for the server's response to StmtPrepare is missing. Handle the cases: - full metadata - optional metadata Change ====== - added optional metadata support to pod type server::StmtPrepareOk - added codec for server::StmtPrepareOk RB: 26128 --- mysqlrouter/classic_protocol_codec_message.h | 83 ++++++++++++++++++++ mysqlrouter/classic_protocol_constants.h | 5 +- mysqlrouter/classic_protocol_message.h | 33 +++++--- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 2c7b60b..9da6f64 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -945,6 +945,89 @@ class Codec const value_type v_; }; +/** + * codec for server::StmtPrepareOk message. + * + * format: + * + * - FixedInt<1> == 0x00 [ok] + * - FixedInt<4> stmt-id + * - FixedInt<2> column-count + * - FixedInt<2> param-count + * - FixedInt<1> == 0x00 [filler] + * - FixedInt<2> warning-count + * + * If caps contains optional_resultset_metadata: + * + * - FixedInt<1> with_metadata + * + * sent as response after a client::StmtPrepare + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.statement_id())) + .step(wire::FixedInt<2>(v_.column_count())) + .step(wire::FixedInt<2>(v_.param_count())) + .step(wire::FixedInt<1>(0)) + .step(wire::FixedInt<2>(v_.warning_count())); + + if (caps()[capabilities::pos::optional_resultset_metadata]) { + accu.step(wire::FixedInt<1>(v_.with_metadata())); + } + + return accu.result(); + } + + public: + using value_type = message::server::StmtPrepareOk; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { return 0x00; } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + auto stmt_id_res = accu.template step>(); + auto column_count_res = accu.template step>(); + auto param_count_res = accu.template step>(); + auto filler_res = accu.template step>(); + auto warning_count_res = accu.template step>(); + + // by default, metadata isn't optional + int8_t with_metadata{1}; + if (caps[capabilities::pos::optional_resultset_metadata]) { + auto with_metadata_res = accu.template step>(); + + if (with_metadata_res) { + with_metadata = with_metadata_res->value(); + } + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(stmt_id_res->value(), column_count_res->value(), + param_count_res->value(), warning_count_res->value(), + with_metadata)); + } + + private: + const value_type v_; +}; + /** * codec for a Row from the server. */ diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 3799192..d35ce98 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -59,7 +59,7 @@ constexpr value_type client_auth_method_data_varint{21}; constexpr value_type expired_passwords{22}; constexpr value_type session_track{23}; constexpr value_type text_result_with_session_tracking{24}; -// 25: optional resultset metdata +constexpr value_type optional_resultset_metadata{25}; constexpr value_type compress_zstd{26}; // // 29 is an extension flag for >32 bit @@ -136,6 +136,9 @@ constexpr value_type text_result_with_session_tracking{ 1 << pos::text_result_with_session_tracking}; // version_added: 8.0 constexpr value_type compress_zstd{1 << pos::compress_zstd}; +// version_added: 8.0 +constexpr value_type optional_resultset_metadata{ + 1 << pos::optional_resultset_metadata}; } // namespace capabilities namespace status { diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index f23a83f..b30e6ec 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -420,28 +420,43 @@ class ResultSet { */ class StmtPrepareOk { public: - StmtPrepareOk(uint32_t stmt_id, uint16_t warning_count, - std::vector params, std::vector columns) + /** + * create a Ok message for a client::StmtPrepare. + * + * @param stmt_id id of the statement + * @param column_count number of columns the prepared stmt will return + * @param param_count number of parameters the prepared stmt contained + * @param warning_count number of warnings the prepared stmt created + * @param with_metadata 0 if no metadata shall be sent for "param_count" and + * "column_count". + */ + StmtPrepareOk(uint32_t stmt_id, uint16_t column_count, uint16_t param_count, + uint16_t warning_count, uint8_t with_metadata) : statement_id_{stmt_id}, warning_count_{warning_count}, - params_{std::move(params)}, - columns_{std::move(columns)} {} + param_count_{param_count}, + column_count_{column_count}, + with_metadata_{with_metadata} {} uint32_t statement_id() const noexcept { return statement_id_; } uint16_t warning_count() const noexcept { return warning_count_; } - std::vector params() const { return params_; } - std::vector columns() const { return columns_; } + + uint16_t column_count() const { return column_count_; } + uint16_t param_count() const { return param_count_; } + uint8_t with_metadata() const { return with_metadata_; } private: uint32_t statement_id_; uint16_t warning_count_; - std::vector params_; - std::vector columns_; + uint16_t param_count_; + uint16_t column_count_; + uint8_t with_metadata_{1}; }; inline bool operator==(const StmtPrepareOk &a, const StmtPrepareOk &b) { return (a.statement_id() == b.statement_id()) && - (a.columns() == b.columns()) && (a.params() == b.params()) && + (a.column_count() == b.column_count()) && + (a.param_count() == b.param_count()) && (a.warning_count() == b.warning_count()); } From da4671ba1c7d0de2462192561e925440593d2a7a Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:12:59 +0100 Subject: [PATCH 26/88] Bug#32670084 codec server::Statistics Issue ===== codec is missing a class for the response of the client::Statistics command. Change ====== - added POD type server::Statistics - added codec for server::Statistics - added tests RB: 26129 --- mysqlrouter/classic_protocol_codec_message.h | 37 ++++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 19 ++++++++++ 2 files changed, 56 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 9da6f64..d248a2b 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1288,6 +1288,43 @@ class Codec const value_type v_; }; +/** + * codec for server::Statistics message. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::String(v_.stats())).result(); + } + + public: + using value_type = message::server::Statistics; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto stats_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(stats_res->value())); + } + + private: + const value_type v_; +}; + /** * CRTP base for client-side commands that are encoded as a single byte. */ diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index b30e6ec..ee44683 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -498,6 +498,25 @@ inline bool operator==(const SendFileRequest &a, const SendFileRequest &b) { return a.filename() == b.filename(); } +class Statistics { + public: + /** + * construct a Statistics message. + * + * @param stats statistics + */ + Statistics(std::string stats) : stats_{std::move(stats)} {} + + std::string stats() const { return stats_; } + + private: + std::string stats_; +}; + +inline bool operator==(const Statistics &a, const Statistics &b) { + return a.stats() == b.stats(); +} + } // namespace server namespace client { From b89136d17b7dc811a456b57ba5ee2b25af1006ed Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:36:59 +0100 Subject: [PATCH 27/88] Bug#32670242 codec client::Reload Issue ===== codec is missing for client::Reload. Change ====== - added POD type for client::Reload - added codec for client::Reload - added tests RB: 26130 --- mysqlrouter/classic_protocol_codec_message.h | 48 ++++++++++++++++++++ mysqlrouter/classic_protocol_constants.h | 27 +++++++++++ mysqlrouter/classic_protocol_message.h | 19 ++++++++ 3 files changed, 94 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index d248a2b..84073ac 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1618,6 +1618,54 @@ class Codec const value_type v_; }; +/** + * codec for client's Reload command. + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<1>(v_.cmds().to_ulong())) + .result(); + } + + public: + using value_type = message::client::Reload; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Refresh); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto cmds_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type(cmds_res->value())); + } + + private: + const value_type v_; +}; + /** * codec for client's Prepared Statement command. */ diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index d35ce98..4bf3059 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -297,6 +297,33 @@ constexpr value_type on_update{1 << pos::on_update}; constexpr value_type numeric{1 << pos::numeric}; } // namespace column_def +namespace reload_cmds { +namespace pos { +using value_type = uint8_t; +constexpr value_type flush_privileges{0}; +constexpr value_type flush_logs{1}; +constexpr value_type flush_tables{2}; +constexpr value_type flush_hosts{3}; +constexpr value_type flush_status{4}; +constexpr value_type flush_threads{5}; +constexpr value_type reset_slave{6}; +constexpr value_type reset_master{7}; + +constexpr value_type _bitset_size{reset_master + 1}; +} // namespace pos +using value_type = std::bitset; + +constexpr value_type flush_privileges{1 << pos::flush_privileges}; +constexpr value_type flush_logs{1 << pos::flush_logs}; +constexpr value_type flush_tables{1 << pos::flush_tables}; +constexpr value_type flush_hosts{1 << pos::flush_hosts}; +constexpr value_type flush_status{1 << pos::flush_status}; +constexpr value_type flush_threads{1 << pos::flush_threads}; +constexpr value_type reset_slave{1 << pos::reset_slave}; +constexpr value_type reset_master{1 << pos::reset_master}; + +} // namespace reload_cmds + namespace collation { using value_type = uint8_t; constexpr value_type Latin1SwedishCi{0x08}; diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index ee44683..63cbc0b 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -728,6 +728,25 @@ constexpr bool operator==(const Statistics &, const Statistics &) { return true; } +class Reload { + public: + /** + * construct a Reload message. + * + * @param cmds what to reload + */ + Reload(classic_protocol::reload_cmds::value_type cmds) : cmds_{cmds} {} + + classic_protocol::reload_cmds::value_type cmds() const { return cmds_; } + + private: + classic_protocol::reload_cmds::value_type cmds_; +}; + +inline bool operator==(const Reload &a, const Reload &b) { + return a.cmds() == b.cmds(); +} + class StmtPrepare { public: /** From e2c8608d2f233fe190c8cf6d6413c83f0ebae9a7 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:37:11 +0100 Subject: [PATCH 28/88] Bug#32672117 codec client::Kill Issue ===== codec classes are missing the handling of client::Kill. Change ====== - added POD type for client::Kill - added codec for client::Kill - added tests RB: 26131 --- mysqlrouter/classic_protocol_codec_message.h | 54 ++++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 19 +++++++ 2 files changed, 73 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 84073ac..d575c6d 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1666,6 +1666,60 @@ class Codec const value_type v_; }; +/** + * codec for client's Kill command. + * + * format: + * + * - FixedInt<1> == 0x0c, ProcessKill + * - FixedInt<4> id + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.connection_id())) + .result(); + } + + public: + using value_type = message::client::Kill; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::ProcessKill); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + auto connection_id_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(connection_id_res->value())); + } + + private: + value_type v_; +}; + /** * codec for client's Prepared Statement command. */ diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 63cbc0b..e68e0eb 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -747,6 +747,25 @@ inline bool operator==(const Reload &a, const Reload &b) { return a.cmds() == b.cmds(); } +class Kill { + public: + /** + * construct a Kill message. + * + * @param connection_id payload + */ + constexpr Kill(uint32_t connection_id) : connection_id_{connection_id} {} + + constexpr uint32_t connection_id() const { return connection_id_; } + + private: + uint32_t connection_id_; +}; + +constexpr bool operator==(const Kill &a, const Kill &b) { + return a.connection_id() == b.connection_id(); +} + class StmtPrepare { public: /** From 2060611382c5ca2daf3bdfa6b771d04a202e078d Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:16:05 +0100 Subject: [PATCH 29/88] Bug#32672673 codec client::SendFile Issue ===== codec for the clients response to the server's SendFileRequest is missing. Change ====== - added POD type for client::SendFile - added codec for client::SendFile - added tests RB: 26134 --- mysqlrouter/classic_protocol_codec_message.h | 42 ++++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 19 +++++++++ 2 files changed, 61 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index d575c6d..1e1cb5b 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1566,6 +1566,48 @@ class Codec const value_type v_; }; +/** + * codec for client::SendFile message. + * + * sent by client as response to server::SendFileRequest + * + * format: + * + * - String payload + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::String(v_.payload())).result(); + } + + public: + using value_type = message::client::SendFile; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto payload_res = accu.template step(); + if (!accu.result()) return accu.result().get_unexpected(); + + return std::make_pair(accu.result().value(), + value_type(payload_res->value())); + } + + private: + const value_type v_; +}; + /** * codec for client's ListFields command. */ diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index e68e0eb..582ee17 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -766,6 +766,25 @@ constexpr bool operator==(const Kill &a, const Kill &b) { return a.connection_id() == b.connection_id(); } +class SendFile { + public: + /** + * construct a SendFile message. + * + * @param payload payload + */ + SendFile(std::string payload) : payload_{std::move(payload)} {} + + std::string payload() const { return payload_; } + + private: + std::string payload_; +}; + +inline bool operator==(const SendFile &a, const SendFile &b) { + return a.payload() == b.payload(); +} + class StmtPrepare { public: /** From fcbe8388095e538ab2e336aa716e8aeedeebad5d Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 22 Mar 2021 19:18:35 +0100 Subject: [PATCH 30/88] Bug#32672769 codec client::AuthMethodData Issue ===== The client's auth-handshake's data is currently encoded directly which means special handling must be done to pretty-print it like: std::cerr << auth_method_msg << ... Having a POD type for client::AuthMethodData which wraps a std::string allows add a pretty printer to client::AuthMethodData. Change ====== - added a POD type for client::AuthMethodData - added a codec for client::AuthMethodData - added tests RB: 26135 --- mysqlrouter/classic_protocol_codec_message.h | 43 ++++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 20 +++++++++ 2 files changed, 63 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 1e1cb5b..7a2ad1f 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2623,6 +2623,49 @@ class Codec const value_type v_; }; +/** + * codec for client::AuthMethodData message. + * + * format: + * + * - String auth_method_data + * + * sent after server::AuthMethodData or server::AuthMethodContinue + */ +template <> +class Codec + : public impl::EncodeBase> { + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::String(v_.auth_method_data())).result(); + } + + public: + using value_type = message::client::AuthMethodData; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto auth_method_data_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(auth_method_data_res->value())); + } + + private: + const value_type v_; +}; + /** * codec for client side change-user message. * diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 582ee17..4e3164b 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -995,6 +995,26 @@ class Ping {}; constexpr bool operator==(const Ping &, const Ping &) { return true; } +class AuthMethodData { + public: + /** + * send data for the current auth-method to server. + * + * @param auth_method_data data of the auth-method + */ + AuthMethodData(std::string auth_method_data) + : auth_method_data_{std::move(auth_method_data)} {} + + std::string auth_method_data() const { return auth_method_data_; } + + private: + std::string auth_method_data_; +}; + +inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { + return a.auth_method_data() == b.auth_method_data(); +} + } // namespace client } // namespace message } // namespace classic_protocol From cd39279ce219f089e6ceb2eae12935845a6d8faf Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 24 Jun 2021 15:08:58 +0200 Subject: [PATCH 31/88] Bug#33059724 missing cmd_byte() in server::AuthMethodData Issue ===== The server::AuthMethodData codec always starts with 0x01. Change ====== - removed packet_type() from server::AuthMethodData - added cmd_byte() = 0x01 RB: 26633 --- mysqlrouter/classic_protocol_codec_message.h | 16 +++++++++++----- mysqlrouter/classic_protocol_message.h | 17 +++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 7a2ad1f..8d81479 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -294,7 +294,7 @@ class Codec : public impl::EncodeBase> { template auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(v_.packet_type())) + return accu.step(wire::FixedInt<1>(cmd_byte())) .step(wire::String(v_.auth_method_data())) .result(); } @@ -308,19 +308,25 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + static constexpr uint8_t cmd_byte() noexcept { return 0x01; } + template static stdx::expected, std::error_code> decode( const ConstBufferSequence &buffers, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffers, caps); - auto packet_type_res = accu.template step>(); + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } auto auth_method_data_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - return std::make_pair( - accu.result().value(), - value_type(packet_type_res->value(), auth_method_data_res->value())); + return std::make_pair(accu.result().value(), + value_type(auth_method_data_res->value())); } private: diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 4e3164b..138eb56 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -155,28 +155,25 @@ inline bool operator==(const AuthMethodSwitch &a, const AuthMethodSwitch &b) { * - Error * - AuthMethodSwitch * - * like: + * like caching_sha2_password does: * - * - 0x01 (more auth data) - * - 0x03 (fast path) + * - 0x01 0x02 (send public key) + * - 0x01 0x03 (send full handshake) + * - 0x01 0x04 (fast path done) */ class AuthMethodData { public: - AuthMethodData(uint8_t packet_type, std::string auth_method_data) - : packet_type_{packet_type}, - auth_method_data_{std::move(auth_method_data)} {} + AuthMethodData(std::string auth_method_data) + : auth_method_data_{std::move(auth_method_data)} {} - uint8_t packet_type() const noexcept { return packet_type_; } std::string auth_method_data() const { return auth_method_data_; } private: - uint8_t packet_type_; std::string auth_method_data_; }; inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { - return (a.auth_method_data() == b.auth_method_data()) && - (a.packet_type() == b.packet_type()); + return a.auth_method_data() == b.auth_method_data(); } /** From d12e34d148ac4aee954ade08af1c0a1fc5ee6c6b Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 1 Sep 2021 18:31:50 +0200 Subject: [PATCH 32/88] Bug#33302907 remove C++11 support from stdx/type_traits.h Issue ===== mysql/harness/stdx/*.h contain several types and templates that look like their C++17 equivalents: - stdx::conjunction, stdx::disjunction, stdx::negation - stdx::void_t - stdx::in_place_t and stdx::in_place Change ====== - remove SUNPRO_CC specific defines from stdx::expected - replaced stdx::conjunction with std::conjunction - replaced stdx::disjunction with std::disjunction - replaced stdx::negation with std::negation - replaced stdx::in_place_t with std::in_place_t - replaced stdx::void_t with std::void_t - removed stdx::in_place_t - removed stdx::void_t RB: 26937 --- mysqlrouter/classic_protocol_codec_wire.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index ce6cb8a..f2e17d6 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -36,6 +36,7 @@ #include "mysql/harness/net_ts/buffer.h" #include "mysql/harness/stdx/expected.h" +#include "mysql/harness/stdx/type_traits.h" // endian #include "mysqlrouter/classic_protocol_codec_base.h" #include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_wire.h" From 20181c99b001e0f3ee6acf26dcf87f1bbfb739b5 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 6 Sep 2021 14:05:50 +0200 Subject: [PATCH 33/88] Bug#33318115 use static_assert() from C++17 Issue ===== C++11 introduced static_assert(EXPR, MSG) which required a MSG for each assertion. If no message was meant to be provided, "" would need to be passed: static_assert(1 == 1, ""); C++17 made the MSG part optional: static_assert(1 == 1); Change ====== - use static_assert(EXPR) instead of static_assert(EXPR, ""); RB: 26965 --- mysqlrouter/classic_protocol_codec_base.h | 8 ++++---- mysqlrouter/classic_protocol_codec_session_track.h | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 17d918f..413ba4b 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -52,10 +52,10 @@ namespace classic_protocol { // constexpr size_t bytes_per_bits(size_t bits) { return (bits + 7) / 8; } -static_assert(bytes_per_bits(0) == 0, ""); -static_assert(bytes_per_bits(1) == 1, ""); -static_assert(bytes_per_bits(8) == 1, ""); -static_assert(bytes_per_bits(9) == 2, ""); +static_assert(bytes_per_bits(0) == 0); +static_assert(bytes_per_bits(1) == 1); +static_assert(bytes_per_bits(8) == 1); +static_assert(bytes_per_bits(9) == 2); /** * Codec for a type. diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 32174c8..7e8a4e7 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -427,8 +427,7 @@ class Codec template static stdx::expected, std::error_code> decode( const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value, - ""); + static_assert(net::is_const_buffer_sequence::value); impl::DecodeBufferAccumulator accu(buffers, caps); auto type_res = accu.template step>(); From efec0cf4caf757732150c7d2b79b0e2132563510 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 15 Sep 2021 14:11:29 +0200 Subject: [PATCH 34/88] Bug#33357723 Oracle Analytic Cloud connection failure Issue ===== The mysql protocol implementation used by OAC sends a client greeting which announces: capabilities: - WITH_SCHEMA - PLUGIN_AUTH - no CONNECT_ATTRibutes which according to the spec should lead to: - caps - max-packet-size - collation - 23-bytes of 0 - username - auth-data - schema - auth-name But the payload that's sent stops right after the "schema" part: ...mysql\0 It is accepted like that by the server, but the router is strict according to the spec. Change ====== - accept client::Greeting messages which announce PLUGIN_AUTH, but don't send a plugin name - fixed error-msg if decoding client greeting failed. RB: 27018 --- mysqlrouter/classic_protocol_codec_message.h | 29 ++++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 8d81479..8256147 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2432,12 +2432,23 @@ class Codec accu.step(wire::NulTermString(v_.schema())); } - if (shared_caps[classic_protocol::capabilities::pos::plugin_auth]) { - accu.step(wire::NulTermString(v_.auth_method_name())); - } - - if (shared_caps + if (!shared_caps [classic_protocol::capabilities::pos::connect_attributes]) { + // special handling for off-spec client/server implimentations. + // + // 1. older clients may set ::plugin_auth, but + // ::connection_attributes which means nothing follows the + // "auth-method-name" field + // 2. auth-method-name is empty, it MAY be skipped. + if (shared_caps[classic_protocol::capabilities::pos::plugin_auth] && + !v_.auth_method_name().empty()) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + } else { + if (shared_caps[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + accu.step(wire::VarString(v_.attributes())); } } @@ -2556,7 +2567,13 @@ class Codec stdx::expected auth_method_res; if (shared_capabilities [classic_protocol::capabilities::pos::plugin_auth]) { - auth_method_res = accu.template step(); + if (net::buffer_size(buffers) == accu.result().value()) { + // even with plugin_auth set, the server is fine, if no + // auth_method_name is sent. + auth_method_res = wire::NulTermString{}; + } else { + auth_method_res = accu.template step(); + } } if (!auth_method_res) return stdx::make_unexpected(auth_method_res.error()); From 56b999e23bf8b88b307fa5587565e2a463fd6d3f Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 15 Sep 2021 14:11:29 +0200 Subject: [PATCH 35/88] Bug#33357723 Oracle Analytic Cloud connection failure Issue ===== The mysql protocol implementation used by OAC sends a client greeting which announces: capabilities: - WITH_SCHEMA - PLUGIN_AUTH - no CONNECT_ATTRibutes which according to the spec should lead to: - caps - max-packet-size - collation - 23-bytes of 0 - username - auth-data - schema - auth-name But the payload that's sent stops right after the "schema" part: ...mysql\0 It is accepted like that by the server, but the router is strict according to the spec. Change ====== - accept client::Greeting messages which announce PLUGIN_AUTH, but don't send a plugin name - fixed error-msg if decoding client greeting failed. RB: 27018 --- mysqlrouter/classic_protocol_codec_message.h | 29 ++++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 8d81479..8256147 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2432,12 +2432,23 @@ class Codec accu.step(wire::NulTermString(v_.schema())); } - if (shared_caps[classic_protocol::capabilities::pos::plugin_auth]) { - accu.step(wire::NulTermString(v_.auth_method_name())); - } - - if (shared_caps + if (!shared_caps [classic_protocol::capabilities::pos::connect_attributes]) { + // special handling for off-spec client/server implimentations. + // + // 1. older clients may set ::plugin_auth, but + // ::connection_attributes which means nothing follows the + // "auth-method-name" field + // 2. auth-method-name is empty, it MAY be skipped. + if (shared_caps[classic_protocol::capabilities::pos::plugin_auth] && + !v_.auth_method_name().empty()) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + } else { + if (shared_caps[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(wire::NulTermString(v_.auth_method_name())); + } + accu.step(wire::VarString(v_.attributes())); } } @@ -2556,7 +2567,13 @@ class Codec stdx::expected auth_method_res; if (shared_capabilities [classic_protocol::capabilities::pos::plugin_auth]) { - auth_method_res = accu.template step(); + if (net::buffer_size(buffers) == accu.result().value()) { + // even with plugin_auth set, the server is fine, if no + // auth_method_name is sent. + auth_method_res = wire::NulTermString{}; + } else { + auth_method_res = accu.template step(); + } } if (!auth_method_res) return stdx::make_unexpected(auth_method_res.error()); From 4fa9223bc3586b1f38c846b44c74e7757244134d Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 29 Sep 2021 17:32:36 +0200 Subject: [PATCH 36/88] Bug#33425108 fix doc-comment for StmtParamAppendData Issue ===== The doc-comment for StmtParamAppendData says: construct a ResetStmt message. Change ====== - reference the right message name. RB: 27087 --- mysqlrouter/classic_protocol_message.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 138eb56..fb563c7 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -807,7 +807,7 @@ inline bool operator==(const StmtPrepare &a, const StmtPrepare &b) { class StmtParamAppendData { public: /** - * construct a ResetStmt message. + * construct an append-data-to-parameter message. * * @param statement_id statement-id to close * @param param_id parameter-id to append data to From dccf05869116df1a162a6bf2ce0ace899903a309 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 17 Jan 2022 17:28:51 +0100 Subject: [PATCH 37/88] WL#12771 reuse connection [12/26] - classic::clone protocol Issue ===== The clone protocol is its own sub-protocol in the classic-protocol. Change ====== added the clone-protocol codec. RB: 27370 --- mysqlrouter/classic_protocol_clone.h | 87 +++++ mysqlrouter/classic_protocol_codec_clone.h | 386 +++++++++++++++++++ mysqlrouter/classic_protocol_codec_message.h | 22 +- mysqlrouter/classic_protocol_message.h | 10 +- 4 files changed, 503 insertions(+), 2 deletions(-) create mode 100644 mysqlrouter/classic_protocol_clone.h create mode 100644 mysqlrouter/classic_protocol_codec_clone.h diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h new file mode 100644 index 0000000..571399c --- /dev/null +++ b/mysqlrouter/classic_protocol_clone.h @@ -0,0 +1,87 @@ +/* + Copyright (c) 2021, 2022, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CLONE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CLONE_H_ + +#include // uint32_t +#include + +namespace classic_protocol::clone { + +struct Locator { + uint8_t storage_engine_type; + std::vector locator; +}; + +namespace client { +// negotiate clone protocol. +// +// response: server::Ok, server::Error +class Init { + public: + uint32_t protocol_version; + uint32_t ddl_timeout; + + std::vector locators; +}; + +class Attach : public Init {}; +class Reinit : public Init {}; + +// no content +class Execute {}; + +class Ack { + public: + uint32_t error_number; + + Locator locator; + + std::vector descriptor; +}; +// no content +class Exit {}; +} // namespace client + +namespace server { +class Locators { + public: + uint32_t protocol_version; + + std::vector locators; +}; +class DataDescriptor { + public: + uint8_t storage_engine_type; + + uint8_t locator_ndx; +}; +class Data {}; +class Complete {}; +class Error {}; +} // namespace server +} // namespace classic_protocol::clone + +#endif diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h new file mode 100644 index 0000000..88733f1 --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -0,0 +1,386 @@ +/* + Copyright (c) 2021, 2022, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_CLONE_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_CLONE_H_ + +#include "mysqlrouter/classic_protocol_clone.h" +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" + +namespace classic_protocol { +namespace clone::client { +enum class CommandByte { + Init = 0x01, + Attach, + Reinit, + Execute, + Ack, + Exit, +}; +} + +/** + * codec for clone::client::Init message. + * + * - Fixed<1> cmd_byte + * - Fixed<4> protocol version + * - Fixed<4> ddl_timeout + * - 0-or-more + * - 1 SE type + * - Fixed<4> locator_len + * - String locator + */ +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.protocol_version)) + .step(wire::FixedInt<4>(v_.ddl_timeout)) + .result(); + } + + public: + using value_type = clone::client::Init; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::client::CommandByte::Init); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto protocol_version_res = accu.template step>(); + auto ddl_timeout_res = accu.template step>(); + + // TODO(jkneschk): if there is more data, 1-or-more Locators + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +// Reinit decodes the same way as Init +// Ack decodes the same way as Init + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::client::Execute; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::client::CommandByte::Execute); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::client::Attach; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::client::CommandByte::Attach); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::client::Reinit; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::client::CommandByte::Reinit); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::client::Ack; + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::client::CommandByte::Ack); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::client::Exit; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::client::CommandByte::Exit); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +namespace clone::server { +enum class CommandByte { + Locators = 0x01, + DataDescriptor, + Data, + Plugin, + Config, + Collation, + PluginV2, // version: 0x0101 + ConfigV3, // version: 0x0102 + Complete = 99, + Error = 100, +}; +} + +// clone::string: +// Fixed<4> len +// String payload +// +// Plugin: +// - clone::string +// PluginV2: +// - key clone::string +// - value clone::string +// Collation: +// - clone::string +// Config +// - key clone::string +// - value clone::string + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::server::Complete; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::server::CommandByte::Complete); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())).result(); + } + + public: + using value_type = clone::server::Error; + using __base = impl::EncodeBase>; + + friend __base; + + constexpr Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(clone::server::CommandByte::Error); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), value_type()); + } + + private: + const value_type v_; +}; + +} // namespace classic_protocol +#endif diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 8256147..49df506 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -2820,6 +2820,26 @@ class Codec const value_type v_; }; +/** + * codec for client's Clone command. + * + * response: server::Ok or server::Error + */ +template <> +class Codec + : public CodecSimpleCommand, + message::client::Clone> { + public: + using value_type = message::client::Clone; + using __base = CodecSimpleCommand, value_type>; + + constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Clone); + } +}; + } // namespace classic_protocol #endif diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index fb563c7..2888cbc 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -1012,7 +1012,15 @@ inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { return a.auth_method_data() == b.auth_method_data(); } +// switch to Clone Protocol. +// +// response: server::Ok -> clone protocol +// response: server::Error +// +// no content +class Clone {}; } // namespace client + } // namespace message } // namespace classic_protocol From 11978820581d324384f638e529a7f3c02bb3371c Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 17 Jan 2022 17:28:56 +0100 Subject: [PATCH 38/88] WL#12771 reuse connection [14/26] - classic::binlog protocol Issue ===== The binlog protocol is its own sub-protocol in the classic protocol. Change ====== Added a codec for the COM_BINLOG_DUMP message. RB: 27370 --- mysqlrouter/classic_protocol_codec_message.h | 232 ++++++++++++++++++- mysqlrouter/classic_protocol_message.h | 124 +++++++++- 2 files changed, 354 insertions(+), 2 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 49df506..40c8f58 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1388,7 +1388,7 @@ enum class CommandByte { BinlogDump, TableDump, ConnectOut, - RegisterSlave, + RegisterReplica, StmtPrepare, StmtExecute, StmtSendLongData, @@ -2840,6 +2840,236 @@ class Codec } }; +/** + * codec for client side dump-binlog message. + */ +template <> +class Codec + : public impl::EncodeBase> { + public: + using value_type = message::client::BinlogDump; + + private: + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.position())) + .step(wire::FixedInt<2>(v_.flags().underlying_value())) + .step(wire::FixedInt<4>(v_.server_id())) + .step(wire::String(v_.filename())) + .result(); + } + + public: + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::BinlogDump); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + auto position_res = accu.template step>(); + auto flags_res = accu.template step>(); + auto server_id_res = accu.template step>(); + + auto filename_res = accu.template step(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + stdx::flags flags; + flags.underlying_value(flags_res->value()); + + return std::make_pair( + accu.result().value(), + value_type(flags, server_id_res->value(), filename_res->value(), + position_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client side register-replica message. + */ +template <> +class Codec + : public impl::EncodeBase> { + public: + using value_type = message::client::RegisterReplica; + + private: + template + auto accumulate_fields(Accumulator &&accu) const { + return accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<4>(v_.server_id())) + .step(wire::FixedInt<1>(v_.hostname().size())) + .step(wire::String(v_.hostname())) + .step(wire::FixedInt<1>(v_.username().size())) + .step(wire::String(v_.username())) + .step(wire::FixedInt<1>(v_.password().size())) + .step(wire::String(v_.password())) + .step(wire::FixedInt<2>(v_.port())) + .step(wire::FixedInt<4>(v_.replication_rank())) + .step(wire::FixedInt<4>(v_.master_id())) + .result(); + } + + public: + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::RegisterReplica); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + auto server_id_res = accu.template step>(); + auto hostname_len_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto hostname_res = + accu.template step(hostname_len_res->value()); + + auto username_len_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto username_res = + accu.template step(username_len_res->value()); + + auto password_len_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto password_res = + accu.template step(password_len_res->value()); + + auto port_res = accu.template step>(); + auto replication_rank_res = accu.template step>(); + auto master_id_res = accu.template step>(); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(server_id_res->value(), hostname_res->value(), + username_res->value(), password_res->value(), + port_res->value(), replication_rank_res->value(), + master_id_res->value())); + } + + private: + const value_type v_; +}; + +/** + * codec for client side dump-binlog-with-gtid message. + */ +template <> +class Codec + : public impl::EncodeBase> { + public: + using value_type = message::client::BinlogDumpGtid; + + private: + template + auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt<1>(cmd_byte())) + .step(wire::FixedInt<2>(v_.flags().underlying_value())) + .step(wire::FixedInt<4>(v_.server_id())) + .step(wire::FixedInt<4>(v_.filename().size())) + .step(wire::String(v_.filename())) + .step(wire::FixedInt<8>(v_.position())) + // + ; + + if (v_.flags() & value_type::Flags::through_gtid) { + accu.step(wire::FixedInt<4>(v_.sids().size())) + .step(wire::String(v_.sids())); + } + + return accu.result(); + } + + public: + using __base = impl::EncodeBase>; + + friend __base; + + Codec(value_type v, capabilities::value_type caps) + : __base(caps), v_{std::move(v)} {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::BinlogDumpGtid); + } + + template + static stdx::expected, std::error_code> decode( + const ConstBufferSequence &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); + + auto cmd_byte_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (cmd_byte_res->value() != cmd_byte()) { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + auto flags_res = accu.template step>(); + auto server_id_res = accu.template step>(); + auto filename_len_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto filename_res = + accu.template step(filename_len_res->value()); + auto position_res = accu.template step>(); + auto sids_len_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto sids_res = accu.template step(sids_len_res->value()); + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + stdx::flags flags; + flags.underlying_value(flags_res->value()); + + return std::make_pair( + accu.result().value(), + value_type(flags, server_id_res->value(), filename_res->value(), + position_res->value(), sids_res->value())); + } + + private: + const value_type v_; +}; + } // namespace classic_protocol #endif diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 2888cbc..ad6619d 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -30,6 +30,7 @@ #include #include "mysql/harness/stdx/expected.h" +#include "mysql/harness/stdx/flags.h" #include "mysqlrouter/classic_protocol_constants.h" namespace classic_protocol { @@ -1020,8 +1021,129 @@ inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { // no content class Clone {}; } // namespace client - } // namespace message } // namespace classic_protocol +namespace classic_protocol::message::client::impl { +class BinlogDump { + public: + // flags of message::client::BinlogDump + enum class Flags : uint16_t { + non_blocking = 1 << 0, + }; +}; +} // namespace classic_protocol::message::client::impl + +namespace stdx { +// enable flag-ops for BinlogDump::Flags +template <> +struct is_flags + : std::true_type {}; +} // namespace stdx + +namespace classic_protocol::message::client { +class BinlogDump { + public: + using Flags = typename impl::BinlogDump::Flags; + + BinlogDump(stdx::flags flags, uint32_t server_id, std::string filename, + uint32_t position) + : position_{position}, + flags_{flags}, + server_id_{server_id}, + filename_{std::move(filename)} {} + + [[nodiscard]] stdx::flags flags() const { return flags_; } + [[nodiscard]] uint32_t server_id() const { return server_id_; } + [[nodiscard]] std::string filename() const { return filename_; } + [[nodiscard]] uint64_t position() const { return position_; } + + private: + uint32_t position_; + stdx::flags flags_; + uint32_t server_id_; + std::string filename_; +}; +} // namespace classic_protocol::message::client + +namespace classic_protocol::message::client::impl { +class BinlogDumpGtid { + public: + // flags of message::client::BinlogDumpGtid + enum class Flags : uint16_t { + non_blocking = 1 << 0, + through_position = 1 << 1, + through_gtid = 1 << 2, + }; +}; +} // namespace classic_protocol::message::client::impl + +namespace stdx { +// enable flag-ops for BinlogDumpGtid::Flags +template <> +struct is_flags + : std::true_type {}; +} // namespace stdx + +namespace classic_protocol::message::client { + +class BinlogDumpGtid { + public: + using Flags = typename impl::BinlogDumpGtid::Flags; + + BinlogDumpGtid(stdx::flags flags, uint32_t server_id, + std::string filename, uint64_t position, std::string sids) + : flags_{flags}, + server_id_{server_id}, + filename_{std::move(filename)}, + position_{position}, + sids_{std::move(sids)} {} + + [[nodiscard]] stdx::flags flags() const { return flags_; } + [[nodiscard]] uint32_t server_id() const { return server_id_; } + [[nodiscard]] std::string filename() const { return filename_; } + [[nodiscard]] uint64_t position() const { return position_; } + [[nodiscard]] std::string sids() const { return sids_; } + + private: + stdx::flags flags_; + uint32_t server_id_; + std::string filename_; + uint64_t position_; + std::string sids_; +}; + +class RegisterReplica { + public: + RegisterReplica(uint32_t server_id, std::string hostname, + std::string username, std::string password, uint16_t port, + uint32_t replication_rank, uint32_t master_id) + : server_id_{server_id}, + hostname_{std::move(hostname)}, + username_{std::move(username)}, + password_{std::move(password)}, + port_{port}, + replication_rank_{replication_rank}, + master_id_{master_id} {} + + [[nodiscard]] uint32_t server_id() const { return server_id_; } + [[nodiscard]] std::string hostname() const { return hostname_; } + [[nodiscard]] std::string username() const { return username_; } + [[nodiscard]] std::string password() const { return password_; } + [[nodiscard]] uint16_t port() const { return port_; } + [[nodiscard]] uint32_t replication_rank() const { return replication_rank_; } + [[nodiscard]] uint32_t master_id() const { return master_id_; } + + private: + uint32_t server_id_; + std::string hostname_; + std::string username_; + std::string password_; + uint16_t port_; + uint32_t replication_rank_; + uint32_t master_id_; +}; + +} // namespace classic_protocol::message::client + #endif From 0cff43858237f1bd798e5efdfcedf32fe068990d Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 18 Jan 2022 14:55:18 +0100 Subject: [PATCH 39/88] Bug#33773243 stdx::expected is deprecated Issue ===== C++23's std::expected doesn't implement expected as that functionality is already provided by std::optional. See http://wg21.link/p0323 All uses if stdx::expected should be replaced by std::optional and support for expected should be removed. Change ====== - replaced all uses of stdx::expected by std::optional - removed support stdx::expected - removed support stdx::expected RB: 27602 --- mysqlrouter/classic_protocol_codec_message.h | 11 ++++++----- mysqlrouter/classic_protocol_message.h | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 40c8f58..ee8b107 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1079,7 +1079,7 @@ class Codec // field may other be a Null or a VarString auto null_res = accu.template try_step(); if (null_res) { - fields.emplace_back(stdx::unexpected()); + fields.emplace_back(std::nullopt); } else { auto field_res = accu.template step(); if (!field_res) return stdx::make_unexpected(field_res.error()); @@ -1281,7 +1281,7 @@ class Codec values.push_back(value_res->value()); } else { - values.emplace_back(stdx::make_unexpected()); + values.emplace_back(std::nullopt); } } @@ -2009,7 +2009,7 @@ class Codec if (!accu.result()) return stdx::make_unexpected(accu.result().error()); std::vector types; - std::vector> values; + std::vector> values; if (new_params_bound_res->value()) { const auto nullbits = std::move(nullbits_res->value()); @@ -2083,12 +2083,13 @@ class Codec } auto value_res = accu.template step(field_size_res.value()); - if (!accu.result()) + if (!accu.result()) { return stdx::make_unexpected(accu.result().error()); + } values.push_back(value_res->value()); } else { - values.emplace_back(stdx::make_unexpected()); + values.emplace_back(std::nullopt); } } } diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index ad6619d..57495ae 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -26,6 +26,7 @@ #define MYSQL_ROUTER_CLASSIC_PROTOCOL_MESSAGE_H_ #include // uint8_t +#include #include #include @@ -373,7 +374,7 @@ inline bool operator==(const ColumnMeta &a, const ColumnMeta &b) { */ class Row { public: - using value_type = stdx::expected; + using value_type = std::optional; using const_iterator = typename std::vector::const_iterator; Row(std::vector fields) : fields_{std::move(fields)} {} @@ -843,7 +844,7 @@ inline bool operator==(const StmtParamAppendData &a, */ class StmtExecute { public: - using value_type = stdx::expected; + using value_type = std::optional; /** * construct a ExecuteStmt message. From 53344298b2cbb6cc910cf197096e9070b1e581b6 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 1 Mar 2022 11:24:18 +0100 Subject: [PATCH 40/88] Bug#34105472 session-tracker trx-state codec misses length Issue ===== Codec is: - length-byte (0x08) - string-of-8-bytes but the codec doesn't encode/decode the length-byte right now. Change ====== - add field-type to codec classes - fix trx-state decoder Change-Id: Ib2af0b34f345444255d7f60055d1ca94e9ab79f7 --- .../classic_protocol_codec_session_track.h | 34 ++++++++++++++++--- mysqlrouter/classic_protocol_session_track.h | 15 ++++---- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 7e8a4e7..01f808b 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -27,7 +27,9 @@ // codecs for classic_protocol::session_track:: messages +#include "mysql/harness/stdx/expected.h" #include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_codec_wire.h" #include "mysqlrouter/classic_protocol_session_track.h" #include "mysqlrouter/classic_protocol_wire.h" @@ -44,7 +46,9 @@ class Codec : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(v_.trx_type())) + return accu + .step(wire::FixedInt<1>(0x08)) // length + .step(wire::FixedInt<1>(v_.trx_type())) .step(wire::FixedInt<1>(v_.read_unsafe())) .step(wire::FixedInt<1>(v_.read_trx())) .step(wire::FixedInt<1>(v_.write_unsafe())) @@ -64,6 +68,8 @@ class Codec constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + constexpr static uint8_t type_byte() { return 0x05; } + /** * decode a session_track::TransactionState from a buffer-sequence. * @@ -82,6 +88,14 @@ class Codec "buffers MUST be a const buffer sequence"); impl::DecodeBufferAccumulator accu(buffers, caps); + const auto payload_length_res = accu.template step(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + if (payload_length_res->value() != 0x08) { + // length of the payload that follows. + return stdx::make_unexpected(make_error_code(std::errc::bad_message)); + } + const auto trx_type_res = accu.template step>(); const auto read_unsafe_res = accu.template step>(); const auto read_trx_res = accu.template step>(); @@ -116,7 +130,7 @@ class Codec Codec> { template auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarString(v_.trx_state())).result(); + return accu.step(wire::VarString(v_.characteristics())).result(); } public: @@ -128,6 +142,8 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + constexpr static uint8_t type_byte() { return 0x04; } + /** * decode a session_track::TransactionCharacteristics from a buffer-sequence. * @@ -180,6 +196,8 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + constexpr static uint8_t type_byte() { return 0x02; } + /** * decode a session_track::State from a buffer-sequence. * @@ -230,6 +248,8 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + constexpr static uint8_t type_byte() { return 0x01; } + /** * decode a session_track::Schema from a buffer-sequence. * @@ -284,6 +304,8 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + constexpr static uint8_t type_byte() { return 0x00; } + /** * decode a session_track::SystemVariable from a buffer-sequence. * @@ -347,6 +369,8 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + constexpr static uint8_t type_byte() { return 0x03; } + /** * decode a session_track::Gtid from a buffer-sequence. * @@ -390,8 +414,8 @@ class Codec * - 0x01 session_track::Schema * - 0x02 session_track::StateChanged * - 0x03 session_track::Gtid - * - 0x04 session_track::TransactionState - * - 0x05 session_track::TransactionCharacteristics + * - 0x04 session_track::TransactionCharacteristics + * - 0x05 session_track::TransactionState */ template <> class Codec diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index ddd5674..4267219 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -217,23 +217,24 @@ inline bool operator==(const TransactionState &a, const TransactionState &b) { /** * TransactionCharacteristics changed. * + * resembles the SQL-text which started the transaction. + * * see: session_track_transaction_info */ class TransactionCharacteristics { public: - TransactionCharacteristics(std::string trx_state) - : trx_state_{std::move(trx_state)} {} + TransactionCharacteristics(std::string characteristics) + : characteristics_{std::move(characteristics)} {} - // encoded VarString of TransactionState - std::string trx_state() const { return trx_state_; } + std::string characteristics() const { return characteristics_; } private: - std::string trx_state_; + std::string characteristics_; }; inline bool operator==(const TransactionCharacteristics &a, const TransactionCharacteristics &b) { - return (a.trx_state() == b.trx_state()); + return (a.characteristics() == b.characteristics()); } } // namespace session_track From 9b2b9063571c4544408eb49094e44cee5375900b Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Thu, 28 Apr 2022 09:14:39 +0200 Subject: [PATCH 41/88] BUG#33764808: UPDATE COPYRIGHT HEADERS TO 2022 [8.0 patch] Update all copyright headers in 8.0 which were not already updated in 5.7, to 2022 and to the current copyright format. This patch was generated by a script. Approved-by: erlend.dahl@oracle.com Change-Id: If741cf56e6cdc40c53591a31a9b46c1ae4e1d987 --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- mysqlrouter/partial_buffer_sequence.h | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index 124b006..1ad670c 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index 73bafab..c1a4d22 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 413ba4b..b376d92 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index e99c8f7..5937ee3 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 1849026..01939c0 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index f2e17d6..339b342 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 4bf3059..286edc1 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index fbecff7..f8255c5 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 61e4f66..cd7b170 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/partial_buffer_sequence.h b/mysqlrouter/partial_buffer_sequence.h index a5deccb..329b1ad 100644 --- a/mysqlrouter/partial_buffer_sequence.h +++ b/mysqlrouter/partial_buffer_sequence.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2021, Oracle and/or its affiliates. + Copyright (c) 2019, 2022, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From 200921c0f63fc44921acbc83b9be121aab5a7d71 Mon Sep 17 00:00:00 2001 From: Miroslav Rajcic Date: Tue, 3 May 2022 09:38:32 +0200 Subject: [PATCH 42/88] Bug#34163323: Fix remaining spell check issues in code comments Description: ----------- This is a continuation of the Bug#34006439 to fix the remaining 2k spell check issues in MySQL source code. As in the previous bug, the focus is to fix only issues within code comments. Code variable names, output string spell check issues were ignored to avoid the impact on programs or tests. Approved by: Jon Stephens Approved by: Margaret Fisher Approved by: Edward Gilmore Approved by: Daniel Price Approved by: Stefan Hinz Change-Id: Ia7bf4e6358da891dcb6b00d64abea81d63e6ba2a --- mysqlrouter/classic_protocol_codec_message.h | 8 ++++---- mysqlrouter/classic_protocol_codec_session_track.h | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index ee8b107..a06fbf0 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -385,7 +385,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with bytes + * @retval std::pair on success, with bytes * processed * @retval codec_errc::invalid_input if preconditions aren't met * @retval codec_errc::not_enough_input not enough data to parse the whole @@ -455,7 +455,7 @@ class Codec * * - 0xef * - * If capabilties has text_result_with_session_tracking, it is followed by + * If capabilities has text_result_with_session_tracking, it is followed by * - [rest of Ok packet] * * otherwise, if capabilities has protocol_41 @@ -529,7 +529,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with bytes + * @retval std::pair on success, with bytes * processed * @retval codec_errc::invalid_input if preconditions aren't met * @retval codec_errc::not_enough_input not enough data to parse the whole @@ -2435,7 +2435,7 @@ class Codec if (!shared_caps [classic_protocol::capabilities::pos::connect_attributes]) { - // special handling for off-spec client/server implimentations. + // special handling for off-spec client/server implementations. // // 1. older clients may set ::plugin_auth, but // ::connection_attributes which means nothing follows the diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 01f808b..f205b09 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -76,7 +76,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with bytes + * @retval std::pair on success, with bytes * processed * @retval codec_errc::not_enough_input not enough data to parse the whole * message @@ -151,7 +151,7 @@ class Codec * @param caps protocol capabilities * * @retval std::pair on - * sucess, with bytes processed + * success, with bytes processed * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ @@ -204,7 +204,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with bytes + * @retval std::pair on success, with bytes * processed * @retval codec_errc::not_enough_input not enough data to parse the whole * message @@ -256,7 +256,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with bytes + * @retval std::pair on success, with bytes * processed * @retval codec_errc::not_enough_input not enough data to parse the whole * message @@ -312,7 +312,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with + * @retval std::pair on success, with * bytes processed * @retval codec_errc::not_enough_input not enough data to parse the whole * message @@ -343,7 +343,7 @@ class Codec * format: * * - FixedInt spec (only 0 is in use for now) - * - VarString payload (payload accoring to spec). + * - VarString payload (payload according to spec). * * payload for spec 0: * - GTID in human-readable form like 4dd0f9d5-3b00-11eb-ad70-003093140e4e:23929 @@ -443,7 +443,7 @@ class Codec * @param buffers input buffser sequence * @param caps protocol capabilities * - * @retval std::pair on sucess, with bytes + * @retval std::pair on success, with bytes * processed * @retval codec_errc::not_enough_input not enough data to parse the whole * message From d3ef7470935006dab61377051f021b3f35a042d5 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 1 Jul 2022 07:54:03 +0200 Subject: [PATCH 43/88] Bug#34376474 protocol constants miss query-attributes capability Issue ===== The router's protocol constants for the classic-protocol missing the capability bit for the "query_attributes". Change ====== - added query-attributes cap Change-Id: If671de6c74b671d57d84e93b2eb9a2289363c01b --- mysqlrouter/classic_protocol_constants.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 286edc1..ceda769 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -61,6 +61,7 @@ constexpr value_type session_track{23}; constexpr value_type text_result_with_session_tracking{24}; constexpr value_type optional_resultset_metadata{25}; constexpr value_type compress_zstd{26}; +constexpr value_type query_attributes{27}; // // 29 is an extension flag for >32 bit // 30 is client only @@ -139,6 +140,7 @@ constexpr value_type compress_zstd{1 << pos::compress_zstd}; // version_added: 8.0 constexpr value_type optional_resultset_metadata{ 1 << pos::optional_resultset_metadata}; +constexpr value_type query_attributes{1 << pos::query_attributes}; } // namespace capabilities namespace status { From 009bd68410c70469fdbb0e487849e810e2273b2d Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 12 Jul 2022 09:19:01 +0200 Subject: [PATCH 44/88] Bug#34376510 client::greeting misses settors Issue ===== The client::Greeting class represents a client-greeting message. It already provides getters for - username - auth-method-data - auth-method-name - schema but not setters Change ====== - added settors Change-Id: I26d3cf28037a203a224529c6d330d4f8912683ca --- mysqlrouter/classic_protocol_message.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 57495ae..81b1d53 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -556,10 +556,19 @@ class Greeting { } uint32_t max_packet_size() const noexcept { return max_packet_size_; } + void max_packet_size(uint32_t sz) noexcept { max_packet_size_ = sz; } + uint8_t collation() const noexcept { return collation_; } + void collation(uint8_t coll) noexcept { collation_ = coll; } + std::string username() const { return username_; } + void username(const std::string &v) { username_ = v; } + std::string auth_method_data() const { return auth_method_data_; } + void auth_method_data(const std::string &v) { auth_method_data_ = v; } + std::string schema() const { return schema_; } + void schema(const std::string &schema) { schema_ = schema; } /** * name of the auth-method that was explicitly set. @@ -570,6 +579,8 @@ class Greeting { */ std::string auth_method_name() const { return auth_method_name_; } + void auth_method_name(const std::string &name) { auth_method_name_ = name; } + // [key, value]* in Codec encoding std::string attributes() const { return attributes_; } From bec8133e594add5508c0441209174de3546d76bf Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 13 Sep 2022 15:14:06 +0200 Subject: [PATCH 45/88] Bug#34604207 wrongly named StmtSetOption classic_protocol::message::client::StmtSetOption implements the COM_SET_OPTION command of the classic protocol. It sets connection options at runtime, but the name suggests that refering to a prepared statement (::StmtPrepare, ::StmtExecute, ...) Change ====== - renamed StmtSetOption to SetOption - fixed doc-comments Change-Id: Ifda90087536806f1a56244c5b1125609ca03a069 --- mysqlrouter/classic_protocol_codec_message.h | 8 ++++---- mysqlrouter/classic_protocol_message.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index a06fbf0..c35793a 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2260,11 +2260,11 @@ class Codec }; /** - * codec for client's SetOption Cursor command. + * codec for client's SetOption command. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { return accu.step(wire::FixedInt<1>(cmd_byte())) @@ -2273,7 +2273,7 @@ class Codec } public: - using value_type = message::client::StmtSetOption; + using value_type = message::client::SetOption; using __base = impl::EncodeBase>; friend __base; diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 81b1d53..4645f5f 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -974,16 +974,16 @@ constexpr bool operator==(const StmtFetch &a, const StmtFetch &b) { } /** - * fetch rows from an executed statement. + * set options on the current connection. */ -class StmtSetOption { +class SetOption { public: /** - * construct a ResetStmt message. + * construct a SetOption message. * * @param option options to set */ - constexpr StmtSetOption(uint16_t option) : option_{option} {} + constexpr SetOption(uint16_t option) : option_{option} {} constexpr uint16_t option() const { return option_; } @@ -991,7 +991,7 @@ class StmtSetOption { uint16_t option_; }; -constexpr bool operator==(const StmtSetOption &a, const StmtSetOption &b) { +constexpr bool operator==(const SetOption &a, const SetOption &b) { return a.option() == b.option(); } From 244933b6c2d1f39d18491d4b514579d029d6ad89 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 28 Sep 2022 13:57:23 +0200 Subject: [PATCH 46/88] Bug #34278103 Compile MySQL with GCC 13 [noclose] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix more warnings from gcc 13: sql/changestreams/apply/commit_order_queue.cc:119:29: required from here sql/memory/unique_ptr.h:784:63: error: moving a temporary object prevents copy elision [-Werror=pessimizing-move] 784 | return std::move(memory::Unique_ptr{size}); | ^ sql/memory/unique_ptr.h:784:63: note: remove ‘std::move’ call unittest/gunit/memory/unique_ptr-t.cc:88:46: required from here sql/memory/unique_ptr.h:804:73: error: moving a temporary object prevents copy elision [-Werror=pessimizing-move] 804 | memory::Unique_ptr{std::forward(args)...}); | ^ sql/memory/unique_ptr.h:804:73: note: remove ‘std::move’ call building meb_innodb storage/innobase/include/mach0data.ic:106:8: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] 106 | b[0] = (byte)(n >> 16); router/include/mysql/harness/plugin.h:377:22: error: identifier ‘requires’ is a keyword in C++20 [-Werror=c++20-compat] 377 | const char *const *requires; router/src/harness/include/mysql/harness/net_ts/local.h:185:14: error: redundant move in return statement [-Werror=redundant-move] 185 | const auto fds = std::move(*res); router/src/harness/tests/plugins/example.cc:61:20: error: identifier ‘requires’ is a keyword in C++20 [-Werror=c++20-compat] 61 | static const char *requires[] = { router/src/mysql_protocol/tests/../include/mysqlrouter/classic_protocol_codec_message.h:1214:16: error: moving a temporary object prevents copy elision [-Werror=pessimizing-move] 1214 | const auto nullbits = std::move(nullbits_res->value()); router/src/plugin_info/src/plugin.h:59:16: error: identifier ‘requires’ is a keyword in C++20 [-Werror=c++20-compat] 59 | const char **requires; router/src/routing/src/classic_frame.cc:201:27: error: redundant move in return statement [-Werror=redundant-move] 201 | auto hdr = std::move(*hdr_res); router/src/router/src/router_app.cc:796:31: error: redundant move in return statement [-Werror=redundant-move] 796 | const auto err = std::move(res.error()); Change-Id: Idcc8a61c9f11031c7a6d94f705aa0ba304cda457 --- mysqlrouter/classic_protocol_codec_message.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index c35793a..80c4d38 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1211,7 +1211,7 @@ class Codec accu.template step(bytes_per_bits(types.size())); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - const auto nullbits = std::move(nullbits_res->value()); + const auto nullbits = nullbits_res->value(); std::vector values; @@ -2012,7 +2012,7 @@ class Codec std::vector> values; if (new_params_bound_res->value()) { - const auto nullbits = std::move(nullbits_res->value()); + const auto nullbits = nullbits_res->value(); types.reserve(param_count); values.reserve(param_count); From 328c0466c60f939388e622dc8bb201c74d2a7a70 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 28 Sep 2022 13:57:23 +0200 Subject: [PATCH 47/88] Bug #34278103 Compile MySQL with GCC 13 [noclose] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix more warnings from gcc 13: sql/changestreams/apply/commit_order_queue.cc:119:29: required from here sql/memory/unique_ptr.h:784:63: error: moving a temporary object prevents copy elision [-Werror=pessimizing-move] 784 | return std::move(memory::Unique_ptr{size}); | ^ sql/memory/unique_ptr.h:784:63: note: remove ‘std::move’ call unittest/gunit/memory/unique_ptr-t.cc:88:46: required from here sql/memory/unique_ptr.h:804:73: error: moving a temporary object prevents copy elision [-Werror=pessimizing-move] 804 | memory::Unique_ptr{std::forward(args)...}); | ^ sql/memory/unique_ptr.h:804:73: note: remove ‘std::move’ call building meb_innodb storage/innobase/include/mach0data.ic:106:8: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] 106 | b[0] = (byte)(n >> 16); router/include/mysql/harness/plugin.h:377:22: error: identifier ‘requires’ is a keyword in C++20 [-Werror=c++20-compat] 377 | const char *const *requires; router/src/harness/include/mysql/harness/net_ts/local.h:185:14: error: redundant move in return statement [-Werror=redundant-move] 185 | const auto fds = std::move(*res); router/src/harness/tests/plugins/example.cc:61:20: error: identifier ‘requires’ is a keyword in C++20 [-Werror=c++20-compat] 61 | static const char *requires[] = { router/src/mysql_protocol/tests/../include/mysqlrouter/classic_protocol_codec_message.h:1214:16: error: moving a temporary object prevents copy elision [-Werror=pessimizing-move] 1214 | const auto nullbits = std::move(nullbits_res->value()); router/src/plugin_info/src/plugin.h:59:16: error: identifier ‘requires’ is a keyword in C++20 [-Werror=c++20-compat] 59 | const char **requires; router/src/routing/src/classic_frame.cc:201:27: error: redundant move in return statement [-Werror=redundant-move] 201 | auto hdr = std::move(*hdr_res); router/src/router/src/router_app.cc:796:31: error: redundant move in return statement [-Werror=redundant-move] 796 | const auto err = std::move(res.error()); Change-Id: Idcc8a61c9f11031c7a6d94f705aa0ba304cda457 (cherry picked from commit 43eefb758891e98803565eea4c417cf54686fe05) --- mysqlrouter/classic_protocol_codec_message.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index c35793a..80c4d38 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1211,7 +1211,7 @@ class Codec accu.template step(bytes_per_bits(types.size())); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - const auto nullbits = std::move(nullbits_res->value()); + const auto nullbits = nullbits_res->value(); std::vector values; @@ -2012,7 +2012,7 @@ class Codec std::vector> values; if (new_params_bound_res->value()) { - const auto nullbits = std::move(nullbits_res->value()); + const auto nullbits = nullbits_res->value(); types.reserve(param_count); values.reserve(param_count); From 000f519478e269ed4c37f19d2686643b702eae18 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 9 Jan 2023 14:52:38 +0100 Subject: [PATCH 48/88] Bug#34969754 speedup message decoding The classic-protocol decoders currently allow decoding messages which are spread across multiple buffers (a ConstBufferSequence). While this is flexible, it isn't used and forces a buffer-copy even though only a single buffer is currently used. ConstBufferSequence is a sequence-of-net::const_buffer OR net::const_buffer itself. As the sequence part is not used, the decoders now just take a net::const_buffer everything works as before, just with less copying. Change ------ - replace ConstBufferSequence with net::const_buffer - remove PartialBufferSequence Change-Id: I95d20a28e534700a2c9b9fbc49221ee7666c8d85 --- mysqlrouter/classic_protocol_codec_base.h | 42 ++-- mysqlrouter/classic_protocol_codec_clone.h | 35 ++-- mysqlrouter/classic_protocol_codec_frame.h | 17 +- mysqlrouter/classic_protocol_codec_message.h | 185 +++++++----------- .../classic_protocol_codec_session_track.h | 60 +++--- mysqlrouter/classic_protocol_codec_wire.h | 115 +++++------ mysqlrouter/partial_buffer_sequence.h | 155 --------------- 7 files changed, 192 insertions(+), 417 deletions(-) delete mode 100644 mysqlrouter/partial_buffer_sequence.h diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index b376d92..60fbd13 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -35,7 +35,6 @@ #include "mysql/harness/stdx/bit.h" #include "mysql/harness/stdx/expected.h" #include "mysqlrouter/classic_protocol_constants.h" -#include "mysqlrouter/partial_buffer_sequence.h" namespace classic_protocol { @@ -129,7 +128,6 @@ namespace impl { * * - .step() */ -template class DecodeBufferAccumulator { public: using buffer_type = net::const_buffer; @@ -138,17 +136,13 @@ class DecodeBufferAccumulator { /** * construct a DecodeBufferAccumulator. * - * @param buffers a ConstBufferSequence + * @param buffer a net::const_buffer * @param caps classic-protocol capabilities * @param consumed bytes to skip from the buffers */ - DecodeBufferAccumulator(const ConstBufferSequence &buffers, + DecodeBufferAccumulator(const net::const_buffer &buffer, capabilities::value_type caps, size_t consumed = 0) - : buffers_{buffers}, caps_{caps} { - static_assert(net::is_const_buffer_sequence::value, - "buffers MUST be a const buffer sequence"); - buffers_.consume(consumed); - } + : buffer_(buffer), caps_(caps), consumed_(consumed) {} /** * decode a Type from the buffer sequence. @@ -185,11 +179,9 @@ class DecodeBufferAccumulator { * */ result_type result() const { - if (!res_) { - return res_; - } else { - return buffers_.total_consumed(); - } + if (!res_) return res_; + + return consumed_; } private: @@ -205,22 +197,24 @@ class DecodeBufferAccumulator { stdx::expected::value_type>, std::error_code> - decode_res = Codec::decode(buffers_.prepare(max_size), caps_); + decode_res = + Codec::decode(net::buffer(buffer_ + consumed_, max_size), caps_); if (decode_res) { - buffers_.consume(decode_res->first); + consumed_ += decode_res->first; return decode_res->second; - } else { - if (!Try) { - // capture the first failure - res_ = stdx::make_unexpected(decode_res.error()); - } - return stdx::make_unexpected(decode_res.error()); } + + if (!Try) { + // capture the first failure + res_ = stdx::make_unexpected(decode_res.error()); + } + return stdx::make_unexpected(decode_res.error()); } - PartialBufferSequence buffers_; + net::const_buffer buffer_; const capabilities::value_type caps_; + size_t consumed_; result_type res_; }; diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 88733f1..2bc52fd 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2022, Oracle and/or its affiliates. + Copyright (c) 2021, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -76,10 +76,9 @@ class Codec return static_cast(clone::client::CommandByte::Init); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -123,8 +122,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -159,8 +158,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -195,8 +194,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -231,8 +230,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -267,8 +266,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -333,8 +332,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -369,8 +368,8 @@ class Codec template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 01939c0..e3b5cee 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -63,10 +63,9 @@ class Codec : public impl::EncodeBase> { static constexpr size_t max_size() noexcept { return 4; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto payload_size_res = accu.template step>(); auto seq_id_res = accu.template step>(); @@ -107,10 +106,9 @@ class Codec static size_t max_size() noexcept { return 7; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto payload_size_res = accu.template step>(); auto seq_id_res = accu.template step>(); @@ -157,10 +155,9 @@ class Codec> constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffers, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffers, caps); auto header_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 80c4d38..193b9d7 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -37,7 +37,6 @@ #include "mysqlrouter/classic_protocol_constants.h" #include "mysqlrouter/classic_protocol_message.h" #include "mysqlrouter/classic_protocol_wire.h" -#include "mysqlrouter/partial_buffer_sequence.h" namespace classic_protocol { @@ -128,10 +127,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); // proto-version auto protocol_version_res = accu.template step>(); @@ -159,7 +157,7 @@ class Codec if (!accu.result()) return stdx::make_unexpected(accu.result().error()); // 3.21.x doesn't send more. - if (buffer_size(buffers) <= accu.result().value()) { + if (buffer_size(buffer) <= accu.result().value()) { return std::make_pair( accu.result().value(), value_type(protocol_version_res->value(), version_res->value(), @@ -255,10 +253,9 @@ class Codec static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); // proto-version auto cmd_byte_res = accu.template step>(); @@ -310,10 +307,9 @@ class Codec static constexpr uint8_t cmd_byte() noexcept { return 0x01; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -382,7 +378,7 @@ class Codec * precondition: * - input starts with cmd_byte() * - * @param buffers input buffser sequence + * @param buffer input buffser sequence * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -391,10 +387,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -526,7 +521,7 @@ class Codec * precondition: * - input starts with cmd_byte() * - * @param buffers input buffser sequence + * @param buffer input buffser sequence * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -535,10 +530,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); const auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -656,10 +650,9 @@ class Codec return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -713,10 +706,9 @@ class Codec return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto count_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -790,10 +782,9 @@ class Codec return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); if (!caps[capabilities::pos::protocol_41]) { // 3.2x protocol used up to 4.0.x @@ -928,10 +919,9 @@ class Codec static constexpr uint8_t cmd_byte() noexcept { return 0xfb; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -999,10 +989,9 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return 0x00; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); auto stmt_id_res = accu.template step>(); @@ -1066,14 +1055,13 @@ class Codec return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); std::vector fields; - const size_t buf_size = buffer_size(buffers); + const size_t buf_size = buffer_size(buffer); while (accu.result() && (accu.result().value() < buf_size)) { // field may other be a Null or a VarString @@ -1084,7 +1072,7 @@ class Codec auto field_res = accu.template step(); if (!field_res) return stdx::make_unexpected(field_res.error()); - fields.emplace_back(std::move(field_res->value())); + fields.emplace_back(field_res->value()); } } @@ -1193,11 +1181,10 @@ class Codec return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps, + const net::const_buffer &buffer, capabilities::value_type caps, std::vector types) { - impl::DecodeBufferAccumulator accu(buffers, caps); + impl::DecodeBufferAccumulator accu(buffer, caps); const auto row_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1314,10 +1301,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto stats_res = accu.template step(); @@ -1351,10 +1337,9 @@ class CodecSimpleCommand static constexpr size_t max_size() noexcept { return 1; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1500,10 +1485,9 @@ class Codec return static_cast(CommandByte::InitSchema); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1549,10 +1533,9 @@ class Codec return static_cast(CommandByte::Query); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1598,10 +1581,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto payload_res = accu.template step(); if (!accu.result()) return accu.result().get_unexpected(); @@ -1641,10 +1623,9 @@ class Codec return static_cast(CommandByte::ListFields); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1692,10 +1673,9 @@ class Codec return static_cast(CommandByte::Refresh); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1745,10 +1725,9 @@ class Codec return static_cast(CommandByte::ProcessKill); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1794,10 +1773,9 @@ class Codec return static_cast(CommandByte::StmtPrepare); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1925,7 +1903,7 @@ class Codec /** * decode a sequence of buffers into a message::client::ExecuteStmt. * - * @param buffers sequence of buffers + * @param buffer sequence of buffers * @param caps protocol capabilities * @param param_count_lookup callable that expects a 'uint32_t statement_id' * that returns and integer that's convertible to 'stdx::expected * }); * \endcode */ - template + template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps, + const net::const_buffer &buffer, capabilities::value_type caps, Func &¶m_count_lookup) { - impl::DecodeBufferAccumulator accu(buffers, caps); + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2135,10 +2113,9 @@ class Codec return static_cast(CommandByte::StmtSendLongData); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2187,10 +2164,9 @@ class Codec return static_cast(CommandByte::StmtClose); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2236,10 +2212,9 @@ class Codec return static_cast(CommandByte::StmtReset); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2285,10 +2260,9 @@ class Codec return static_cast(CommandByte::SetOption); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2335,10 +2309,9 @@ class Codec return static_cast(CommandByte::StmtFetch); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2478,10 +2451,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto capabilities_lo_res = accu.template step>(); if (!capabilities_lo_res) @@ -2568,7 +2540,7 @@ class Codec stdx::expected auth_method_res; if (shared_capabilities [classic_protocol::capabilities::pos::plugin_auth]) { - if (net::buffer_size(buffers) == accu.result().value()) { + if (net::buffer_size(buffer) == accu.result().value()) { // even with plugin_auth set, the server is fine, if no // auth_method_name is sent. auth_method_res = wire::NulTermString{}; @@ -2673,10 +2645,9 @@ class Codec Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto auth_method_data_res = accu.template step(); @@ -2748,10 +2719,9 @@ class Codec return static_cast(CommandByte::ChangeUser); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2788,7 +2758,7 @@ class Codec if (!accu.result()) return stdx::make_unexpected(accu.result().error()); // 3.23.x-4.0 don't send more. - if (buffer_size(buffers) <= accu.result().value()) { + if (buffer_size(buffer) <= accu.result().value()) { return std::make_pair( accu.result().value(), value_type(username_res->value(), auth_method_data_res->value(), @@ -2873,10 +2843,9 @@ class Codec return static_cast(CommandByte::BinlogDump); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2943,10 +2912,9 @@ class Codec return static_cast(CommandByte::RegisterReplica); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -3032,10 +3000,9 @@ class Codec return static_cast(CommandByte::BinlogDumpGtid); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index f205b09..3178996 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -73,7 +73,7 @@ class Codec /** * decode a session_track::TransactionState from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -81,12 +81,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value, - "buffers MUST be a const buffer sequence"); - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); const auto payload_length_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -147,7 +144,7 @@ class Codec /** * decode a session_track::TransactionCharacteristics from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on @@ -155,12 +152,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value, - "buffers MUST be a const buffer sequence"); - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); const auto characteristics_res = accu.template step(); @@ -201,7 +195,7 @@ class Codec /** * decode a session_track::State from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -209,10 +203,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto state_res = accu.template step>(); @@ -253,7 +246,7 @@ class Codec /** * decode a session_track::Schema from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -261,12 +254,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value, - "buffers MUST be a const buffer sequence"); - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto schema_res = accu.template step(); @@ -309,7 +299,7 @@ class Codec /** * decode a session_track::SystemVariable from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on success, with @@ -317,12 +307,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value, - "buffers MUST be a const buffer sequence"); - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto key_res = accu.template step(); auto value_res = accu.template step(); @@ -374,7 +361,7 @@ class Codec /** * decode a session_track::Gtid from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -382,10 +369,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto spec_res = accu.template step>(); auto gtid_res = accu.template step(); @@ -440,7 +426,7 @@ class Codec /** * decode a session_track::Field from a buffer-sequence. * - * @param buffers input buffser sequence + * @param buffer input buffer * @param caps protocol capabilities * * @retval std::pair on success, with bytes @@ -448,11 +434,9 @@ class Codec * @retval codec_errc::not_enough_input not enough data to parse the whole * message */ - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value); - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto type_res = accu.template step>(); auto data_res = accu.template step(); diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 339b342..664815e 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, @@ -40,7 +40,6 @@ #include "mysqlrouter/classic_protocol_codec_base.h" #include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_wire.h" -#include "mysqlrouter/partial_buffer_sequence.h" namespace classic_protocol { /** @@ -82,24 +81,28 @@ class Codec> { */ static constexpr size_t max_size() noexcept { return int_size; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { - typename value_type::value_type v{}; - - size_t copied = buffer_copy(net::buffer(&v, int_size), buffers); - - if (copied != int_size) { + const net::const_buffer &buffer, capabilities::value_type /* caps */) { + if (buffer.size() < int_size) { // not enough data in buffers. return stdx::make_unexpected( make_error_code(codec_errc::not_enough_input)); } + union { + std::array ar; + typename value_type::value_type value{}; + }; + + std::copy(static_cast(buffer.data()), + static_cast(buffer.data()) + int_size, + ar.data()); + if (stdx::endian::native == stdx::endian::big) { - v = stdx::byteswap(v); + value = stdx::byteswap(value); } - return std::make_pair(copied, value_type(v)); + return std::make_pair(int_size, value_type(value)); } private: @@ -161,10 +164,9 @@ class Codec : public impl::EncodeBase> { static constexpr size_t max_size() noexcept { return 9; } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); // length auto first_byte_res = accu.template step>(); @@ -211,12 +213,11 @@ class Codec : public Codec> { Codec(value_type, capabilities::value_type caps) : Codec>(nul_byte, caps) {} - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + const net::const_buffer &buffer, capabilities::value_type /* caps */) { uint8_t v; - size_t copied = buffer_copy(net::buffer(&v, 1), buffers); + size_t copied = buffer_copy(net::buffer(&v, 1), buffer); if (copied != 1) { return stdx::make_unexpected( @@ -239,8 +240,7 @@ class Codec { public: using value_type = size_t; - Codec(value_type v, capabilities::value_type caps) - : v_{std::move(v)}, caps_{caps} {} + Codec(value_type val, capabilities::value_type caps) : v_(val), caps_{caps} {} size_t size() const noexcept { return v_; } @@ -253,10 +253,9 @@ class Codec { return buffer_copy(buffer, net::buffer(std::vector(size()))); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { - size_t buf_size = buffer_size(buffers); + const net::const_buffer &buffer, capabilities::value_type /* caps */) { + size_t buf_size = buffer.size(); return std::make_pair(buf_size, buf_size); } @@ -292,10 +291,9 @@ class Codec { return buffer_copy(buffer, net::const_buffer(v_.value().data(), size())); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { - size_t buf_size = buffer_size(buffers); + const net::const_buffer &buffer, capabilities::value_type /* caps */) { + size_t buf_size = buffer_size(buffer); // MUST handle the empty case as &s.front() for .empty() std::string is // undefined and may trigger an assert()ion on glibc's implementation @@ -305,8 +303,7 @@ class Codec { std::string s; s.resize(buf_size); - size_t len = - buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffers); + size_t len = buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffer); return std::make_pair(len, value_type(s)); } @@ -333,12 +330,12 @@ class Codec : public impl::EncodeBase> { public: using value_type = wire::VarString; - using __base = impl::EncodeBase>; + using base_type = impl::EncodeBase>; - friend __base; + friend base_type; - Codec(value_type v, capabilities::value_type caps) - : __base(caps), v_{std::move(v)} {} + Codec(value_type val, capabilities::value_type caps) + : base_type(caps), v_{std::move(val)} {} static size_t max_size() noexcept { // we actually don't know what the size of the null-term string is ... @@ -346,10 +343,9 @@ class Codec : public impl::EncodeBase> { return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); // decode the length auto var_string_len_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -383,12 +379,12 @@ class Codec public: using value_type = wire::NulTermString; - using __base = impl::EncodeBase>; + using base_type = impl::EncodeBase>; - friend __base; + friend base_type; - Codec(value_type v, capabilities::value_type caps) - : __base(caps), v_{std::move(v)} {} + Codec(value_type val, capabilities::value_type caps) + : base_type(caps), v_{std::move(val)} {} static size_t max_size() noexcept { // we actually don't know what the size of the null-term string is ... @@ -396,38 +392,31 @@ class Codec return std::numeric_limits::max(); } - template static stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type /* caps */) { + const net::const_buffer &buffer, capabilities::value_type /* caps */) { // length of the string before the \0 size_t len{}; // we don't know where the \0 will be be, scan all buffers for the first // one. - const auto bufend = buffer_sequence_end(buffers); - for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend; - ++bufcur) { - const auto first = static_cast(bufcur->data()); - const auto last = first + bufcur->size(); - - const auto pos = std::find(first, last, '\0'); - if (pos != last) { - // \0 was found - len += std::distance(first, pos); - - // builds a string from the buffer-sequence's content - std::string s; - if (len > 0) { - // ensure we don't trigger undefined behaviour by using &s.front() if - // s.size() is 0 - s.resize(len); - buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffers, len); - } - - return std::make_pair(len + 1, value_type(s)); // consume the \0 too - } else { - len += buffer_size(*bufcur); + const auto *first = static_cast(buffer.data()); + const auto *last = first + buffer.size(); + + const auto *pos = std::find(first, last, '\0'); + if (pos != last) { + // \0 was found + len += std::distance(first, pos); + + // builds a string from the buffer-sequence's content + std::string s; + if (len > 0) { + // ensure we don't trigger undefined behaviour by using &s.front() if + // s.size() is 0 + s.resize(len); + buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffer, len); } + + return std::make_pair(len + 1, value_type(s)); // consume the \0 too } // no 0-term found diff --git a/mysqlrouter/partial_buffer_sequence.h b/mysqlrouter/partial_buffer_sequence.h deleted file mode 100644 index 329b1ad..0000000 --- a/mysqlrouter/partial_buffer_sequence.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_PARTIAL_BUFFER_SEQUENCE_H_ -#define MYSQL_ROUTER_CLASSIC_PROTOCOL_PARTIAL_BUFFER_SEQUENCE_H_ - -#include - -#include "mysql/harness/net_ts/buffer.h" - -namespace classic_protocol { - -/** - * partial buffer sequence. - * - * a sub-range of a buffer-sequence which returns a buffer-sequence itself. - * - * - consume() moves the position in the buffer-sequence forward. - * - prepare() returns a buffer-sequence from the current position up to n bytes - * (or end of sequence) - */ -template -class PartialBufferSequence { - public: - using buffer_sequence_type = BufferSequence; - using sequence_type = std::vector; - - PartialBufferSequence(const buffer_sequence_type &seq) - : seq_{seq}, - seq_cur_{net::buffer_sequence_begin(seq)}, - seq_end_{net::buffer_sequence_end(seq)} {} - - /** - * prepare a buffer-sequence for consumption. - * - * skips empty buffers. - */ - sequence_type prepare(size_t n) const noexcept { - sequence_type buf_seq; - - size_t pos = pos_; - - for (auto seq_cur = seq_cur_; n > 0 && seq_cur != seq_end_; ++seq_cur) { - // slice of the current buffer in the sequence - auto b = net::buffer(net::buffer(*seq_cur) + pos, n); - - // add a buffer to output if it is not empty - if (b.size() > 0) { - buf_seq.push_back(b); - n -= b.size(); - pos = 0; - } - } - - return buf_seq; - } - - /** - * consume n bytes of buffer-sequence. - * - * moves the position in the buffer-sequence forward. - */ - void consume(size_t n) noexcept { - pos_ += n; - consumed_ += n; - - // skip buffers that are already done or empty() - for (; seq_cur_ != seq_end_; ++seq_cur_) { - auto buf = *seq_cur_; - - if (buf.size() <= pos_) { - pos_ -= buf.size(); - } else { - break; - } - } - - // exit-condition: - // - // pos_ < seq_cur_->size() - } - - size_t total_consumed() const noexcept { return consumed_; } - - private: - // note: only captured to call decltype() on it get the return type of - // buffer_sequence_begin() and buffer_sequence_end(). If there is another - // way found to - const BufferSequence &seq_; - - // current pointer into the buffer-sequence - decltype(net::buffer_sequence_begin(seq_)) seq_cur_; - - // end of the buffer sequence - const decltype(net::buffer_sequence_begin(seq_)) seq_end_; - - // position into the first buffer - size_t pos_{}; - - // total consumed bytes - size_t consumed_{}; -}; - -/** - * partial buffer sequence. - * - * specialization for the common case where the BufferSequence is a single - * net::const_buffer. - * - * The partial sequence that's created by prepare() also creates a - * net::const_buffer which allows passing it to this specialization again. - */ -template <> -class PartialBufferSequence { - public: - using buffer_sequence_type = net::const_buffer; - using sequence_type = net::const_buffer; - - PartialBufferSequence(const buffer_sequence_type &seq) : seq_{seq} {} - - sequence_type prepare(size_t n) const noexcept { - return net::buffer(net::buffer(seq_) + pos_, n); - } - - void consume(size_t n) noexcept { pos_ += n; } - - size_t total_consumed() const noexcept { return pos_; } - - private: - const buffer_sequence_type &seq_; - size_t pos_{}; -}; -} // namespace classic_protocol -#endif From c6455bc1699516a953af8311d41af4d5787fd5d1 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 28 Dec 2022 09:12:07 +0100 Subject: [PATCH 49/88] Bug#34982979 setter for Ok and Eof Router can decode Ok and Eof packets from the server into server::Ok and server::Eof messages. Those classes provide getters for all the message fields, but not setters. Change ------ - added setters for all members of server::Ok and server::Eof - added unit-tests Change-Id: I7c44475054c8b0085cf2bf30a891f28ff077571d --- mysqlrouter/classic_protocol_message.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 4645f5f..2f3c900 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -202,15 +202,29 @@ class Ok { message_{std::move(message)}, session_changes_{std::move(session_changes)} {} + void status_flags(classic_protocol::status::value_type flags) { + status_flags_ = flags; + } + classic_protocol::status::value_type status_flags() const noexcept { return status_flags_; } + + void warning_count(uint16_t count) { warning_count_ = count; } uint16_t warning_count() const noexcept { return warning_count_; } + + void last_insert_id(uint64_t val) { last_insert_id_ = val; } uint64_t last_insert_id() const noexcept { return last_insert_id_; } + + void affected_rows(uint64_t val) { affected_rows_ = val; } uint64_t affected_rows() const noexcept { return affected_rows_; } + void message(const std::string &msg) { message_ = msg; } std::string message() const { return message_; } + void session_changes(const std::string &changes) { + session_changes_ = changes; + } /** * get session-changes. * From 282dcc31f8086298638cc85faa4b6885dbc34ff4 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Tue, 17 Jan 2023 10:33:54 +0100 Subject: [PATCH 50/88] Merged BUG#34750847 from 5.7 to 8.0. Change-Id: I85b8474fbfc09ef5daa52573d89229024e094682 --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_clone.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index 1ad670c..1a4ef38 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index 571399c..161fd71 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2022, Oracle and/or its affiliates. + Copyright (c) 2021, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index c1a4d22..555b80c 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 5937ee3..e746a83 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index ceda769..b76240c 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index f8255c5..c8eb14d 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 2f3c900..3489784 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 4267219..942d416 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index cd7b170..0951502 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2022, Oracle and/or its affiliates. + Copyright (c) 2019, 2023, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From aa3cbddc04b163e320a5e1b47ab5906a46658ba9 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 13 Jan 2023 10:31:03 +0100 Subject: [PATCH 51/88] Bug#34278103 Compile MySQL with GCC 13 [noclose] Fix warnings reported by gcc (GCC) 13.0.0 20230108 (experimental) Add a few missing #includes -Werror=dangling-reference -Werror=format-truncation= Warnings in router code also reported by gcc12. -Werror=maybe-uninitialized { new (&error_) error_type(e); } Change-Id: Ib769605ea766c0e5adf1219c57ae918c19f5b494 --- mysqlrouter/classic_protocol_message.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 3489784..779d4f4 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -276,6 +276,7 @@ class Eof : public Ok { */ class Error { public: + Error() = default; /** * construct an Error message. * @@ -294,7 +295,7 @@ class Error { std::string message() const { return message_; } private: - uint16_t error_code_; + uint16_t error_code_{0}; std::string message_; std::string sql_state_; }; From b291b6734e47b81f47973bd64bff7180298879d1 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 13 Jan 2023 10:31:03 +0100 Subject: [PATCH 52/88] Bug#34278103 Compile MySQL with GCC 13 [noclose] Fix warnings reported by gcc (GCC) 13.0.0 20230108 (experimental) Add a few missing #includes -Werror=dangling-reference -Werror=format-truncation= Warnings in router code also reported by gcc12. -Werror=maybe-uninitialized { new (&error_) error_type(e); } Change-Id: Ib769605ea766c0e5adf1219c57ae918c19f5b494 (cherry picked from commit dac356c5a0c3848f7a53328186e32bd648e8c39c) --- mysqlrouter/classic_protocol_message.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 3489784..779d4f4 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -276,6 +276,7 @@ class Eof : public Ok { */ class Error { public: + Error() = default; /** * construct an Error message. * @@ -294,7 +295,7 @@ class Error { std::string message() const { return message_; } private: - uint16_t error_code_; + uint16_t error_code_{0}; std::string message_; std::string sql_state_; }; From 90f97c457ec5b3998b251e444f4e5f90c02bb9d2 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 19 Jan 2023 11:42:56 +0100 Subject: [PATCH 53/88] Bug#34996516 wrong encoded buffers with gcc Building with GCC, a ClassicFrame::send_msg generated a mysql frame which was too short, but when building with clang it had the right size. In the GCC case, the correct length is generated when inlining is disabled, which hints at "undefined behaviour". Replacing the sequence res_ = encode(...); if (res_) { consumed_ += *res_; } with: auto res = encode(...); if (!res) { res_ = res; } else { consumed_ += *res; } fixes the problem. Microbenchmarking shows, that this is also faster. clang-14.0.0: ResetConnection_Encode 35 ns/iter -> 27 ns/iter Quit_Encode 35 ns/iter -> 27 ns/iter Ping_Encode 35 ns/iter -> 27 ns/iter Query_Encode 143 ns/iter -> 121 ns/iter Query_Decode 73 ns/iter -> 72 ns/iter Change ------ - EncodeBufferAccumulator::step remembers the result of the last step and continues if the it didn't fail. Instead of assigning res_ with each encode()'s return value, only set it when it failed. - accept buffers by value, as net::mutable_buffer and net::const_buffer are trivial Change-Id: If81ce6103d55f3a53437f7fb9bd89ebe59b3777b --- mysqlrouter/classic_protocol_codec_base.h | 27 +++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 60fbd13..d1962ac 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -234,7 +234,6 @@ class DecodeBufferAccumulator { */ class EncodeBufferAccumulator { public: - using buffer_type = net::mutable_buffer; using result_type = stdx::expected; /** @@ -244,9 +243,9 @@ class EncodeBufferAccumulator { * @param caps protocol capabilities * @param consumed bytes already used in the in buffer */ - EncodeBufferAccumulator(buffer_type buffer, capabilities::value_type caps, - size_t consumed = 0) - : buffer_{std::move(buffer)}, caps_{caps}, consumed_{consumed} {} + EncodeBufferAccumulator(net::mutable_buffer buffer, + capabilities::value_type caps, size_t consumed = 0) + : buffer_{buffer}, caps_{caps}, consumed_{consumed} {} /** * encode a T into the buffer and move position forward. @@ -257,9 +256,11 @@ class EncodeBufferAccumulator { EncodeBufferAccumulator &step(const T &v) { if (!res_) return *this; - res_ = Codec(v, caps_).encode(buffer_ + consumed_); - if (res_) { - consumed_ += res_.value(); + auto res = Codec(v, caps_).encode(buffer_ + consumed_); + if (!res) { // it failed. + res_ = res; + } else { + consumed_ += *res; } return *this; @@ -272,15 +273,13 @@ class EncodeBufferAccumulator { * failed. */ result_type result() const { - if (!res_) { - return res_; - } else { - return {consumed_}; - } + if (!res_) return res_; + + return {consumed_}; } private: - const buffer_type buffer_; + const net::mutable_buffer buffer_; const capabilities::value_type caps_; size_t consumed_{}; @@ -352,7 +351,7 @@ class EncodeBase { } stdx::expected encode( - const net::mutable_buffer &buffer) const { + net::mutable_buffer buffer) const { return static_cast(this)->accumulate_fields( EncodeBufferAccumulator(buffer, caps_)); } From 7f4acc46f507dbf9ee693251bc95d9346359b965 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 19 Jan 2023 11:33:23 +0100 Subject: [PATCH 54/88] Bug#34996521 remove more const-buffer-sequence support from codec A earlier patch already removed larger parts of the ConstBufferSequence support. This is the 2nd pass. net::buffer_copy() copys from one buffer-sequence to another, but that's not needed anymore. A plain std::copy() does the same, just faster as it is inlined. clang: ResetConnection_Encode 27 ns/iter -> 0 ns/iter Quit_Encode 27 ns/iter -> 0 ns/iter Ping_Encode 27 ns/iter -> 0 ns/iter Query_Encode 121 ns/iter -> 108 ns/iter Query_Decode 72 ns/iter -> 60 ns/iter Change ------ - fixed doc-comments - replaced ConstBufferSequence with plain net::const_buffer in ::decode() - replaced net::buffer_copy() with std::copy_n() Change-Id: I91248cc42339fc94ec4dfc56914f2d3023ed9e37 --- mysqlrouter/classic_protocol_codec_base.h | 15 ++- mysqlrouter/classic_protocol_codec_frame.h | 6 +- mysqlrouter/classic_protocol_codec_wire.h | 109 +++++++++++---------- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index d1962ac..342e627 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -105,26 +105,23 @@ stdx::expected encode(const T &v, } /** - * decode a message from a buffer sequence. + * decode a message from a buffer. * - * @param buffers buffer sequence to read from + * @param buffer buffer to read from * @param caps protocol capabilities * @returns number of bytes read from 'buffers' and a T on success, or * std::error_code on error */ -template +template stdx::expected, std::error_code> decode( - const ConstBufferSequence &buffers, capabilities::value_type caps) { - static_assert(net::is_const_buffer_sequence::value, - "buffers MUST be a const buffer sequence"); - - return Codec::decode(buffers, caps); + const net::const_buffer &buffer, capabilities::value_type caps) { + return Codec::decode(buffer, caps); } namespace impl { /** - * Generator of decoded Types of a buffer-sequence. + * Generator of decoded Types of a buffer. * * - .step() */ diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index e3b5cee..72a70b0 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -156,8 +156,8 @@ class Codec> : __base(caps), v_{std::move(v)} {} static stdx::expected, std::error_code> decode( - const net::const_buffer &buffers, capabilities::value_type caps) { - impl::DecodeBufferAccumulator accu(buffers, caps); + net::const_buffer buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); auto header_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -165,7 +165,7 @@ class Codec> constexpr const size_t header_size{Codec::max_size()}; // check the payload is at least what we expect. - if (net::buffer_size(buffers) < header_size + header_res->payload_size()) { + if (buffer.size() < header_size + header_res->payload_size()) { return stdx::make_unexpected( make_error_code(classic_protocol::codec_errc::not_enough_input)); } diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 664815e..9000888 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -66,14 +66,21 @@ class Codec> { * encode value_type into buffer. */ stdx::expected encode( - const net::mutable_buffer &buffer) const { - auto v = v_.value(); + net::mutable_buffer buffer) const { + if (buffer.size() < int_size) { + return stdx::make_unexpected(make_error_code(std::errc::no_buffer_space)); + } + + auto int_val = v_.value(); if (stdx::endian::native == stdx::endian::big) { - v = stdx::byteswap(v); + int_val = stdx::byteswap(int_val); } - return buffer_copy(buffer, net::const_buffer(&v, int_size)); + std::copy_n(reinterpret_cast(&int_val), int_size, + static_cast(buffer.data())); + + return int_size; } /** @@ -89,14 +96,10 @@ class Codec> { make_error_code(codec_errc::not_enough_input)); } - union { - std::array ar; - typename value_type::value_type value{}; - }; + typename value_type::value_type value{}; - std::copy(static_cast(buffer.data()), - static_cast(buffer.data()) + int_size, - ar.data()); + std::copy_n(static_cast(buffer.data()), int_size, + reinterpret_cast(&value)); if (stdx::endian::native == stdx::endian::big) { value = stdx::byteswap(value); @@ -215,18 +218,18 @@ class Codec : public Codec> { static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type /* caps */) { - uint8_t v; - - size_t copied = buffer_copy(net::buffer(&v, 1), buffer); - - if (copied != 1) { + if (buffer.size() < 1) { return stdx::make_unexpected( make_error_code(codec_errc::not_enough_input)); - } else if (v != nul_byte) { + } + + const uint8_t nul_val = *static_cast(buffer.data()); + + if (nul_val != nul_byte) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - return std::make_pair(copied, value_type()); + return std::make_pair(1, value_type()); } }; @@ -250,7 +253,17 @@ class Codec { stdx::expected encode( const net::mutable_buffer &buffer) const { - return buffer_copy(buffer, net::buffer(std::vector(size()))); + if (buffer.size() < size()) { + return stdx::make_unexpected(make_error_code(std::errc::no_buffer_space)); + } + + auto *first = static_cast(buffer.data()); + auto *last = first + size(); + + // fill with 0 + std::fill(first, last, 0); + + return size(); } static stdx::expected, std::error_code> decode( @@ -288,24 +301,26 @@ class Codec { stdx::expected encode( const net::mutable_buffer &buffer) const { - return buffer_copy(buffer, net::const_buffer(v_.value().data(), size())); + if (buffer.size() < size()) { + return stdx::make_unexpected(make_error_code(std::errc::no_buffer_space)); + } + + // in -> out + std::copy_n(reinterpret_cast(v_.value().data()), size(), + static_cast(buffer.data())); + + return size(); } static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type /* caps */) { - size_t buf_size = buffer_size(buffer); + const size_t buf_size = buffer_size(buffer); - // MUST handle the empty case as &s.front() for .empty() std::string is - // undefined and may trigger an assert()ion on glibc's implementation - if (0 == buf_size) { - return std::make_pair(buf_size, value_type(std::string())); - } - std::string s; - s.resize(buf_size); - - size_t len = buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffer); + if (0 == buf_size) return std::make_pair(buf_size, value_type()); - return std::make_pair(len, value_type(s)); + return std::make_pair( + buf_size, + value_type({static_cast(buffer.data()), buffer.size()})); } private: @@ -395,32 +410,26 @@ class Codec static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type /* caps */) { // length of the string before the \0 - size_t len{}; - // we don't know where the \0 will be be, scan all buffers for the first - // one. const auto *first = static_cast(buffer.data()); const auto *last = first + buffer.size(); const auto *pos = std::find(first, last, '\0'); - if (pos != last) { - // \0 was found - len += std::distance(first, pos); - - // builds a string from the buffer-sequence's content - std::string s; - if (len > 0) { - // ensure we don't trigger undefined behaviour by using &s.front() if - // s.size() is 0 - s.resize(len); - buffer_copy(net::mutable_buffer(&s.front(), s.size()), buffer, len); - } - - return std::make_pair(len + 1, value_type(s)); // consume the \0 too + if (pos == last) { + // no 0-term found + return stdx::make_unexpected( + make_error_code(codec_errc::missing_nul_term)); + } + + // \0 was found + size_t len = std::distance(first, pos); + if (len == 0) { + return std::make_pair(len + 1, value_type()); // consume the \0 too } - // no 0-term found - return stdx::make_unexpected(make_error_code(codec_errc::missing_nul_term)); + return std::make_pair(len + 1, + value_type({static_cast(buffer.data()), + len})); // consume the \0 too } private: From 6f5b5c8e64c782104d7c60fa4fdc74170c80dbf9 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 19 Jan 2023 11:40:55 +0100 Subject: [PATCH 55/88] Bug#34996528 simplify basic wire-types classic_protocol_wire.h contains a lot of duplication: - FixedInt<> and VarInt both share the same code, with only a different underlying type. Change ------ - inherit Int-types from BasicInt - clang-tidy reported 'redundant init of s_' -> String() = default Change-Id: I1679e6c1bab8f25d2e145150772f403f7986f75c --- mysqlrouter/classic_protocol_wire.h | 113 ++++++++++------------------ 1 file changed, 40 insertions(+), 73 deletions(-) diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 0951502..e4dc509 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -40,118 +40,85 @@ namespace wire { // - nul-terminated strings // - NULL -class VarInt { +template +class BasicInt { public: - using value_type = int64_t; + using value_type = U; - constexpr VarInt(value_type v) : v_{v} {} + constexpr BasicInt(value_type val) : val_{val} {} - constexpr value_type value() const noexcept { return v_; } + constexpr value_type value() const { return val_; } private: - value_type v_; + value_type val_; }; -constexpr bool operator==(const VarInt &a, const VarInt &b) { - return a.value() == b.value(); +template +constexpr bool operator==(const BasicInt &lhs, const BasicInt &rhs) { + return lhs.value() == rhs.value(); } -class String { +class VarInt : public BasicInt { public: - String() : s_{} {} - String(std::string s) : s_{std::move(s)} {} - - std::string value() const { return s_; } - - private: - std::string s_; + using BasicInt::BasicInt; }; -inline bool operator==(const String &a, const String &b) { - return a.value() == b.value(); -} +template +class FixedInt; -class NulTermString : public String { +template <> +class FixedInt<1> : public BasicInt { public: - using String::String; + using BasicInt::BasicInt; }; -class VarString : public String { +template <> +class FixedInt<2> : public BasicInt { public: - using String::String; + using BasicInt::BasicInt; }; -template -class FixedInt; - template <> -class FixedInt<1> { +class FixedInt<3> : public BasicInt { public: - using value_type = uint8_t; - - constexpr FixedInt(value_type v) : v_{std::move(v)} {} - - constexpr value_type value() const { return v_; } - - private: - value_type v_; + using BasicInt::BasicInt; }; -template -constexpr bool operator==(const FixedInt &a, const FixedInt &b) { - return a.value() == b.value(); -} - template <> -class FixedInt<2> { +class FixedInt<4> : public BasicInt { public: - using value_type = uint16_t; - - constexpr FixedInt(value_type v) : v_{std::move(v)} {} - - constexpr value_type value() const { return v_; } - - private: - value_type v_; + using BasicInt::BasicInt; }; template <> -class FixedInt<3> { +class FixedInt<8> : public BasicInt { public: - using value_type = uint32_t; - - constexpr FixedInt(value_type v) : v_{std::move(v)} {} - - constexpr value_type value() const { return v_; } - - private: - value_type v_; + using BasicInt::BasicInt; }; -template <> -class FixedInt<4> { +class String { public: - using value_type = uint32_t; - - constexpr FixedInt(value_type v) : v_{std::move(v)} {} + String() = default; + String(std::string str) : str_{std::move(str)} {} - constexpr value_type value() const { return v_; } + std::string value() const { return str_; } private: - value_type v_; + std::string str_; }; -template <> -class FixedInt<8> { - public: - using value_type = uint64_t; - - constexpr FixedInt(value_type v) : v_{std::move(v)} {} +inline bool operator==(const String &lhs, const String &rhs) { + return lhs.value() == rhs.value(); +} - constexpr value_type value() const { return v_; } +class NulTermString : public String { + public: + using String::String; +}; - private: - value_type v_; +class VarString : public String { + public: + using String::String; }; class Null {}; From 09c4a393be47346653857c5c8c1d51d1e692da08 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 20 Jan 2023 11:19:21 +0100 Subject: [PATCH 56/88] Bug#35006489 allow zero-copy classic codec Decoding and Encoding the classic protocol currently copies all the data fields. In case the recv-buffer outlives the use of the decoded data, the data-fields could point into the recv-buffer and borrow the data. It would avoid: std::string construction/destruction and data copies. With copying: Query_Encode 106 ns/iter Query_Decode 59 ns/iter With borrowed: Query_Encode_Borrowed 7 ns/iter -93% Query_Decode_Borrowed 9 ns/iter -85% Change ------ - moved POD classes from classic_protocol:: to classic_protocol::borrowable and added a template param to all POD classes that have a std::string - expose all types non-borrowed variants in the classic_protocol:: - expose all types borrowed variants in classic_protocol::borrowed:: - added micro-benchmark for borrowed variants Change-Id: Ibc723cebfee78cc679a42fa1c9242a13f223a722 --- mysqlrouter/classic_protocol_codec_message.h | 1362 ++++++++++------- .../classic_protocol_codec_session_track.h | 178 ++- mysqlrouter/classic_protocol_codec_wire.h | 91 +- mysqlrouter/classic_protocol_message.h | 794 ++++++---- mysqlrouter/classic_protocol_session_track.h | 102 +- mysqlrouter/classic_protocol_wire.h | 79 +- 6 files changed, 1586 insertions(+), 1020 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 193b9d7..0acd70b 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -74,16 +74,19 @@ namespace classic_protocol { * * NulTermString auth-method */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + if (v_.protocol_version() == 0x09) { - return accu.step(wire::FixedInt<1>(v_.protocol_version())) - .step(wire::NulTermString(v_.version())) - .step(wire::FixedInt<4>(v_.connection_id())) - .step(wire::NulTermString(v_.auth_method_data().substr(0, 8))) + return accu.step(bw::FixedInt<1>(v_.protocol_version())) + .step(bw::NulTermString(v_.version())) + .step(bw::FixedInt<4>(v_.connection_id())) + .step(bw::NulTermString(v_.auth_method_data().substr(0, 8))) .result(); } else { uint8_t auth_method_data_size{0}; @@ -91,26 +94,26 @@ class Codec auth_method_data_size = v_.auth_method_data().size(); } - accu.step(wire::FixedInt<1>(v_.protocol_version())) - .step(wire::NulTermString(v_.version())) - .step(wire::FixedInt<4>(v_.connection_id())) - .step(wire::NulTermString(v_.auth_method_data().substr(0, 8))) - .step(wire::FixedInt<2>(v_.capabilities().to_ulong() & 0xffff)); + accu.step(bw::FixedInt<1>(v_.protocol_version())) + .step(bw::NulTermString(v_.version())) + .step(bw::FixedInt<4>(v_.connection_id())) + .step(bw::NulTermString(v_.auth_method_data().substr(0, 8))) + .step(bw::FixedInt<2>(v_.capabilities().to_ulong() & 0xffff)); if ((v_.capabilities().to_ullong() >= (1 << 16)) || v_.status_flags().any() || (v_.collation() != 0)) { - accu.step(wire::FixedInt<1>(v_.collation())) - .step(wire::FixedInt<2>(v_.status_flags().to_ulong())) - .step(wire::FixedInt<2>((v_.capabilities().to_ulong() >> 16) & - 0xffff)) - .step(wire::FixedInt<1>(auth_method_data_size)) - .step(wire::String(std::string(10, '\0'))); + accu.step(bw::FixedInt<1>(v_.collation())) + .step(bw::FixedInt<2>(v_.status_flags().to_ulong())) + .step( + bw::FixedInt<2>((v_.capabilities().to_ulong() >> 16) & 0xffff)) + .step(bw::FixedInt<1>(auth_method_data_size)) + .step(bw::String(std::string(10, '\0'))); if (v_.capabilities() [classic_protocol::capabilities::pos::secure_connection]) { - accu.step(wire::String(v_.auth_method_data().substr(8))); + accu.step(bw::String(v_.auth_method_data().substr(8))); if (v_.capabilities() [classic_protocol::capabilities::pos::plugin_auth]) { - accu.step(wire::NulTermString(v_.auth_method_name())); + accu.step(bw::NulTermString(v_.auth_method_name())); } } } @@ -119,20 +122,22 @@ class Codec } public: - using value_type = message::server::Greeting; + using value_type = borrowable::message::server::Greeting; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); + namespace bw = borrowable::wire; + // proto-version - auto protocol_version_res = accu.template step>(); + auto protocol_version_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (protocol_version_res->value() != 0x09 && @@ -140,9 +145,10 @@ class Codec return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto version_res = accu.template step(); - auto connection_id_res = accu.template step>(); - auto auth_method_data_res = accu.template step(); + auto version_res = accu.template step>(); + auto connection_id_res = accu.template step>(); + auto auth_method_data_res = + accu.template step>(); if (protocol_version_res->value() == 0x09) { return std::make_pair( @@ -153,7 +159,7 @@ class Codec } else { // capabilities are split into two a lower-2-byte part and a // higher-2-byte - auto cap_lower_res = accu.template step>(); + auto cap_lower_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); // 3.21.x doesn't send more. @@ -167,9 +173,9 @@ class Codec } // if there's more data - auto collation_res = accu.template step>(); - auto status_flags_res = accu.template step>(); - auto cap_hi_res = accu.template step>(); + auto collation_res = accu.template step>(); + auto status_flags_res = accu.template step>(); + auto cap_hi_res = accu.template step>(); // before we use cap_hi|cap_low check they don't have an error if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -179,7 +185,7 @@ class Codec size_t auth_method_data_len{13}; if (capabilities[classic_protocol::capabilities::pos::plugin_auth]) { - auto auth_method_data_len_res = accu.template step>(); + auto auth_method_data_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); // should be 21, but least 8 @@ -194,17 +200,19 @@ class Codec accu.template step(10); // skip the filler - stdx::expected auth_method_data_2_res; - stdx::expected auth_method_res; + stdx::expected, std::error_code> + auth_method_data_2_res; + stdx::expected, std::error_code> + auth_method_res; if (capabilities [classic_protocol::capabilities::pos::secure_connection]) { // auth-method-data auth_method_data_2_res = - accu.template step(auth_method_data_len); + accu.template step>(auth_method_data_len); if (capabilities[classic_protocol::capabilities::pos::plugin_auth]) { // auth_method - auth_method_res = accu.template step(); + auth_method_res = accu.template step>(); } } @@ -228,27 +236,31 @@ class Codec /** * codec for server::AuthMethodSwitch message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())); - if (caps()[classic_protocol::capabilities::pos::plugin_auth]) { - accu.step(wire::NulTermString(v_.auth_method())) - .step(wire::String(v_.auth_method_data())); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())); + + if (this->caps()[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(bw::NulTermString(v_.auth_method())) + .step(bw::String(v_.auth_method_data())); } return accu.result(); } public: - using value_type = message::server::AuthMethodSwitch; + using value_type = borrowable::message::server::AuthMethodSwitch; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } @@ -257,8 +269,10 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); + namespace bw = borrowable::wire; + // proto-version - auto cmd_byte_res = accu.template step>(); + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { @@ -269,8 +283,8 @@ class Codec return std::make_pair(accu.result().value(), value_type()); } - auto auth_method_res = accu.template step(); - auto auth_method_data_res = accu.template step(); + auto auth_method_res = accu.template step>(); + auto auth_method_data_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -286,23 +300,26 @@ class Codec /** * codec for server::AuthMethodData message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::String(v_.auth_method_data())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::String(v_.auth_method_data())) .result(); } public: - using value_type = message::server::AuthMethodData; + using value_type = borrowable::message::server::AuthMethodData; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr uint8_t cmd_byte() noexcept { return 0x01; } @@ -311,13 +328,15 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto auth_method_data_res = accu.template step(); + auto auth_method_data_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -332,42 +351,45 @@ class Codec /** * codec for server-side Ok message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::VarInt(v_.affected_rows())) - .step(wire::VarInt(v_.last_insert_id())); - - if (caps()[capabilities::pos::protocol_41] || - caps()[capabilities::pos::transactions]) { - accu.step(wire::FixedInt<2>(v_.status_flags().to_ulong())); - if (caps()[capabilities::pos::protocol_41]) { - accu.step(wire::FixedInt<2>(v_.warning_count())); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::VarInt(v_.affected_rows())) + .step(bw::VarInt(v_.last_insert_id())); + + if (this->caps()[capabilities::pos::protocol_41] || + this->caps()[capabilities::pos::transactions]) { + accu.step(bw::FixedInt<2>(v_.status_flags().to_ulong())); + if (this->caps().test(capabilities::pos::protocol_41)) { + accu.step(bw::FixedInt<2>(v_.warning_count())); } } - if (caps()[capabilities::pos::session_track]) { - accu.step(wire::VarString(v_.message())); - if (v_.status_flags()[status::pos::session_state_changed]) { - accu.step(wire::VarString(v_.session_changes())); + if (this->caps().test(capabilities::pos::session_track)) { + accu.step(bw::VarString(v_.message())); + if (v_.status_flags().test(status::pos::session_state_changed)) { + accu.step(bw::VarString(v_.session_changes())); } } else { - accu.step(wire::String(v_.message())); + accu.step(bw::String(v_.message())); } return accu.result(); } public: - using value_type = message::server::Ok; + using value_type = borrowable::message::server::Ok; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr uint8_t cmd_byte() noexcept { return 0x00; } @@ -391,31 +413,35 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto affected_rows_res = accu.template step(); - auto last_insert_id_res = accu.template step(); + auto affected_rows_res = accu.template step(); + auto last_insert_id_res = accu.template step(); - stdx::expected, std::error_code> status_flags_res(0); - stdx::expected, std::error_code> warning_count_res(0); + stdx::expected, std::error_code> status_flags_res(0); + stdx::expected, std::error_code> warning_count_res(0); if (caps[capabilities::pos::protocol_41] || caps[capabilities::pos::transactions]) { - status_flags_res = accu.template step>(); + status_flags_res = accu.template step>(); if (caps[capabilities::pos::protocol_41]) { - warning_count_res = accu.template step>(); + warning_count_res = accu.template step>(); } } - stdx::expected message_res; - stdx::expected session_changes_res; + stdx::expected, std::error_code> message_res; + stdx::expected, std::error_code> + session_changes_res; if (caps[capabilities::pos::session_track]) { // if there is more data. - const auto var_message_res = accu.template try_step(); + const auto var_message_res = + accu.template try_step>(); if (var_message_res) { // set the message from the var-string message_res = var_message_res.value(); @@ -423,10 +449,10 @@ class Codec if (status_flags_res->value() & status::session_state_changed.to_ulong()) { - session_changes_res = accu.template step(); + session_changes_res = accu.template step>(); } } else { - message_res = accu.template step(); + message_res = accu.template step>(); } if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -460,53 +486,59 @@ class Codec * otherwise * - nothing */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; - if (caps()[capabilities::pos::text_result_with_session_tracking]) { - accu.step(wire::VarInt(v_.affected_rows())) - .step(wire::VarInt(v_.last_insert_id())); + accu.step(bw::FixedInt<1>(cmd_byte())); - if (caps()[capabilities::pos::protocol_41] || - caps()[capabilities::pos::transactions]) { - accu.step(wire::FixedInt<2>(v_.status_flags().to_ulong())); - if (caps()[capabilities::pos::protocol_41]) { - accu.step(wire::FixedInt<2>(v_.warning_count())); + auto shared_caps = this->caps(); + + if (shared_caps.test( + capabilities::pos::text_result_with_session_tracking)) { + accu.step(bw::VarInt(v_.affected_rows())) + .step(bw::VarInt(v_.last_insert_id())); + + if (shared_caps[capabilities::pos::protocol_41] || + shared_caps[capabilities::pos::transactions]) { + accu.step(bw::FixedInt<2>(v_.status_flags().to_ulong())); + if (shared_caps[capabilities::pos::protocol_41]) { + accu.step(bw::FixedInt<2>(v_.warning_count())); } } - if (caps()[capabilities::pos::session_track]) { + if (shared_caps[capabilities::pos::session_track]) { if (!v_.message().empty() || v_.status_flags()[status::pos::session_state_changed]) { // only write message and session-changes if both of them aren't // empty. - accu.step(wire::VarString(v_.message())); + accu.step(bw::VarString(v_.message())); if (v_.status_flags()[status::pos::session_state_changed]) { - accu.step(wire::VarString(v_.session_changes())); + accu.step(bw::VarString(v_.session_changes())); } } } else { - accu.step(wire::String(v_.message())); + accu.step(bw::String(v_.message())); } - } else if (caps()[capabilities::pos::protocol_41]) { - accu.step(wire::FixedInt<2>(v_.warning_count())) - .step(wire::FixedInt<2>(v_.status_flags().to_ulong())); + } else if (shared_caps[capabilities::pos::protocol_41]) { + accu.step(bw::FixedInt<2>(v_.warning_count())) + .step(bw::FixedInt<2>(v_.status_flags().to_ulong())); } return accu.result(); } public: - using value_type = message::server::Eof; + using value_type = borrowable::message::server::Eof; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } @@ -534,7 +566,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - const auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + const auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { @@ -542,25 +576,27 @@ class Codec } if (caps[capabilities::pos::text_result_with_session_tracking]) { - const auto affected_rows_res = accu.template step(); - const auto last_insert_id_res = accu.template step(); + const auto affected_rows_res = accu.template step(); + const auto last_insert_id_res = accu.template step(); - stdx::expected, std::error_code> status_flags_res(0); - stdx::expected, std::error_code> warning_count_res(0); + stdx::expected, std::error_code> status_flags_res(0); + stdx::expected, std::error_code> warning_count_res(0); if (caps[capabilities::pos::protocol_41] || caps[capabilities::pos::transactions]) { - status_flags_res = accu.template step>(); + status_flags_res = accu.template step>(); if (caps[capabilities::pos::protocol_41]) { - warning_count_res = accu.template step>(); + warning_count_res = accu.template step>(); } } - stdx::expected message_res; - stdx::expected session_state_info_res; + stdx::expected, std::error_code> message_res; + stdx::expected, std::error_code> + session_state_info_res; if (caps[capabilities::pos::session_track]) { // when session-track is supported, the 'message' part is a VarString. // But only if there is actually session-data and the message has data. - const auto var_message_res = accu.template try_step(); + const auto var_message_res = + accu.template try_step>(); if (var_message_res) { // set the message from the var-string message_res = var_message_res.value(); @@ -568,10 +604,11 @@ class Codec if (status_flags_res->value() & status::session_state_changed.to_ulong()) { - session_state_info_res = accu.template step(); + session_state_info_res = + accu.template step>(); } } else { - message_res = accu.template step(); + message_res = accu.template step>(); } if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -582,8 +619,8 @@ class Codec status_flags_res->value(), warning_count_res->value(), message_res->value(), session_state_info_res->value())); } else if (caps[capabilities::pos::protocol_41]) { - const auto warning_count_res = accu.template step>(); - const auto status_flags_res = accu.template step>(); + const auto warning_count_res = accu.template step>(); + const auto status_flags_res = accu.template step>(); return std::make_pair( accu.result().value(), @@ -621,27 +658,31 @@ class Codec * String<5> sql_state * String message */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<2>(v_.error_code())); - if (caps()[capabilities::pos::protocol_41]) { - accu.step(wire::FixedInt<1>('#')).step(wire::String(v_.sql_state())); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<2>(v_.error_code())); + if (this->caps()[capabilities::pos::protocol_41]) { + accu.step(bw::FixedInt<1>('#')) + .step(bw::String(v_.sql_state())); } - return accu.step(wire::String(v_.message())).result(); + return accu.step(bw::String(v_.message())).result(); } public: - using value_type = message::server::Error; + using value_type = borrowable::message::server::Error; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr uint8_t cmd_byte() { return 0xff; } @@ -654,7 +695,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { @@ -662,13 +705,13 @@ class Codec } // decode all fields, check result later before they are used. - auto error_code_res = accu.template step>(); - stdx::expected sql_state_res; + auto error_code_res = accu.template step>(); + stdx::expected, std::error_code> sql_state_res; if (caps[capabilities::pos::protocol_41]) { - auto sql_state_hash_res = accu.template step>(); - sql_state_res = accu.template step(5); + auto sql_state_hash_res = accu.template step>(); + sql_state_res = accu.template step>(5); } - auto message_res = accu.template step(); + auto message_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -686,20 +729,22 @@ class Codec * codec for ColumnCount message. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarInt(v_.count())).result(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::VarInt(v_.count())).result(); } public: - using value_type = message::server::ColumnCount; + using value_type = borrowable::message::server::ColumnCount; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr size_t max_size() noexcept { @@ -710,7 +755,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto count_res = accu.template step(); + namespace bw = borrowable::wire; + + auto count_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -727,50 +774,53 @@ class Codec * capabilities checked: * - protocol_41 */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template auto accumulate_fields(Accumulator &&accu) const { - if (!caps()[capabilities::pos::protocol_41]) { - accu.step(wire::VarString(v_.table())) - .step(wire::VarString(v_.name())) - .step(wire::VarInt(3)) - .step(wire::FixedInt<3>(v_.column_length())) - .step(wire::VarInt(1)) - .step(wire::FixedInt<1>(v_.type())); - - if (caps()[capabilities::pos::long_flag]) { - accu.step(wire::VarInt(3)) - .step(wire::FixedInt<2>(v_.flags().to_ulong())) - .step(wire::FixedInt<1>(v_.decimals())); + namespace bw = borrowable::wire; + + if (!this->caps()[capabilities::pos::protocol_41]) { + accu.step(bw::VarString(v_.table())) + .step(bw::VarString(v_.name())) + .step(bw::VarInt(3)) + .step(bw::FixedInt<3>(v_.column_length())) + .step(bw::VarInt(1)) + .step(bw::FixedInt<1>(v_.type())); + + if (this->caps()[capabilities::pos::long_flag]) { + accu.step(bw::VarInt(3)) + .step(bw::FixedInt<2>(v_.flags().to_ulong())) + .step(bw::FixedInt<1>(v_.decimals())); } else { - accu.step(wire::VarInt(2)) - .step(wire::FixedInt<1>(v_.flags().to_ulong())) - .step(wire::FixedInt<1>(v_.decimals())); + accu.step(bw::VarInt(2)) + .step(bw::FixedInt<1>(v_.flags().to_ulong())) + .step(bw::FixedInt<1>(v_.decimals())); } return accu.result(); } else { - return accu.step(wire::VarString(v_.catalog())) - .step(wire::VarString(v_.schema())) - .step(wire::VarString(v_.table())) - .step(wire::VarString(v_.orig_table())) - .step(wire::VarString(v_.name())) - .step(wire::VarString(v_.orig_name())) - .step(wire::VarInt(12)) - .step(wire::FixedInt<2>(v_.collation())) - .step(wire::FixedInt<4>(v_.column_length())) - .step(wire::FixedInt<1>(v_.type())) - .step(wire::FixedInt<2>(v_.flags().to_ulong())) - .step(wire::FixedInt<1>(v_.decimals())) - .step(wire::FixedInt<2>(0)) + return accu.step(bw::VarString(v_.catalog())) + .step(bw::VarString(v_.schema())) + .step(bw::VarString(v_.table())) + .step(bw::VarString(v_.orig_table())) + .step(bw::VarString(v_.name())) + .step(bw::VarString(v_.orig_name())) + .step(bw::VarInt(12)) + .step(bw::FixedInt<2>(v_.collation())) + .step(bw::FixedInt<4>(v_.column_length())) + .step(bw::FixedInt<1>(v_.type())) + .step(bw::FixedInt<2>(v_.flags().to_ulong())) + .step(bw::FixedInt<1>(v_.decimals())) + .step(bw::FixedInt<2>(0)) .result(); } } public: - using value_type = message::server::ColumnMeta; + using value_type = borrowable::message::server::ColumnMeta; using __base = impl::EncodeBase>; friend __base; @@ -786,16 +836,18 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); + namespace bw = borrowable::wire; + if (!caps[capabilities::pos::protocol_41]) { // 3.2x protocol used up to 4.0.x // bit-size of the 'flags' field const uint8_t flags_size = caps[capabilities::pos::long_flag] ? 2 : 1; - const auto table_res = accu.template step(); - const auto name_res = accu.template step(); + const auto table_res = accu.template step>(); + const auto name_res = accu.template step>(); - const auto column_length_len_res = accu.template step(); + const auto column_length_len_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (column_length_len_res->value() != 3) { @@ -803,8 +855,8 @@ class Codec make_error_code(codec_errc::invalid_input)); } - const auto column_length_res = accu.template step>(); - const auto type_len_res = accu.template step(); + const auto column_length_res = accu.template step>(); + const auto type_len_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (type_len_res->value() != 1) { @@ -812,9 +864,8 @@ class Codec make_error_code(codec_errc::invalid_input)); } - const auto type_res = accu.template step>(); - const auto flags_and_decimals_len_res = - accu.template step(); + const auto type_res = accu.template step>(); + const auto flags_and_decimals_len_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (flags_and_decimals_len_res->value() != flags_size + 1) { @@ -822,16 +873,16 @@ class Codec make_error_code(codec_errc::invalid_input)); } - stdx::expected, std::error_code> flags_and_decimals_res( + stdx::expected, std::error_code> flags_and_decimals_res( 0); if (flags_size == 2) { - flags_and_decimals_res = accu.template step>(); + flags_and_decimals_res = accu.template step>(); } else { const auto small_flags_and_decimals_res = - accu.template step>(); + accu.template step>(); if (small_flags_and_decimals_res) { flags_and_decimals_res = - wire::FixedInt<3>(small_flags_and_decimals_res->value()); + bw::FixedInt<3>(small_flags_and_decimals_res->value()); } } @@ -848,27 +899,27 @@ class Codec column_length_res->value(), type_res->value(), flags, decimals)); } else { - const auto catalog_res = accu.template step(); - const auto schema_res = accu.template step(); - const auto table_res = accu.template step(); - const auto orig_table_res = accu.template step(); - const auto name_res = accu.template step(); - const auto orig_name_res = accu.template step(); + const auto catalog_res = accu.template step>(); + const auto schema_res = accu.template step>(); + const auto table_res = accu.template step>(); + const auto orig_table_res = accu.template step>(); + const auto name_res = accu.template step>(); + const auto orig_name_res = accu.template step>(); /* next is a collection of fields which is wrapped inside a varstring of * 12-bytes size */ - const auto other_len_res = accu.template step(); + const auto other_len_res = accu.template step(); if (other_len_res->value() != 12) { return stdx::make_unexpected( make_error_code(codec_errc::invalid_input)); } - const auto collation_res = accu.template step>(); - const auto column_length_res = accu.template step>(); - const auto type_res = accu.template step>(); - const auto flags_res = accu.template step>(); - const auto decimals_res = accu.template step>(); + const auto collation_res = accu.template step>(); + const auto column_length_res = accu.template step>(); + const auto type_res = accu.template step>(); + const auto flags_res = accu.template step>(); + const auto decimals_res = accu.template step>(); accu.template step(2); // fillers @@ -898,23 +949,26 @@ class Codec * * 0xfb */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::String(v_.filename())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::String(v_.filename())) .result(); } public: - using value_type = message::server::SendFileRequest; + using value_type = borrowable::message::server::SendFileRequest; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static constexpr uint8_t cmd_byte() noexcept { return 0xfb; } @@ -923,14 +977,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto filename_res = accu.template step(); + auto filename_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -960,31 +1016,34 @@ class Codec * sent as response after a client::StmtPrepare */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase< + Codec> { template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.statement_id())) - .step(wire::FixedInt<2>(v_.column_count())) - .step(wire::FixedInt<2>(v_.param_count())) - .step(wire::FixedInt<1>(0)) - .step(wire::FixedInt<2>(v_.warning_count())); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.statement_id())) + .step(bw::FixedInt<2>(v_.column_count())) + .step(bw::FixedInt<2>(v_.param_count())) + .step(bw::FixedInt<1>(0)) + .step(bw::FixedInt<2>(v_.warning_count())); if (caps()[capabilities::pos::optional_resultset_metadata]) { - accu.step(wire::FixedInt<1>(v_.with_metadata())); + accu.step(bw::FixedInt<1>(v_.with_metadata())); } return accu.result(); } public: - using value_type = message::server::StmtPrepareOk; + using value_type = borrowable::message::server::StmtPrepareOk; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { return 0x00; } @@ -993,17 +1052,19 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); - auto stmt_id_res = accu.template step>(); - auto column_count_res = accu.template step>(); - auto param_count_res = accu.template step>(); - auto filler_res = accu.template step>(); - auto warning_count_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); + auto stmt_id_res = accu.template step>(); + auto column_count_res = accu.template step>(); + auto param_count_res = accu.template step>(); + auto filler_res = accu.template step>(); + auto warning_count_res = accu.template step>(); // by default, metadata isn't optional int8_t with_metadata{1}; if (caps[capabilities::pos::optional_resultset_metadata]) { - auto with_metadata_res = accu.template step>(); + auto with_metadata_res = accu.template step>(); if (with_metadata_res) { with_metadata = with_metadata_res->value(); @@ -1026,16 +1087,19 @@ class Codec /** * codec for a Row from the server. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + for (const auto &field : v_) { if (field) { - accu.step(wire::VarString(*field)); + accu.step(bw::VarString(*field)); } else { - accu.step(wire::Null()); + accu.step(bw::Null()); } } @@ -1043,7 +1107,7 @@ class Codec } public: - using value_type = message::server::Row; + using value_type = borrowable::message::server::Row; using __base = impl::EncodeBase>; friend __base; @@ -1059,17 +1123,19 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - std::vector fields; + namespace bw = borrowable::wire; + + std::vector fields; const size_t buf_size = buffer_size(buffer); while (accu.result() && (accu.result().value() < buf_size)) { // field may other be a Null or a VarString - auto null_res = accu.template try_step(); + auto null_res = accu.template try_step(); if (null_res) { fields.emplace_back(std::nullopt); } else { - auto field_res = accu.template step(); + auto field_res = accu.template step>(); if (!field_res) return stdx::make_unexpected(field_res.error()); fields.emplace_back(field_res->value()); @@ -1099,12 +1165,15 @@ class Codec * - size the NULL bitmap * - length of each field */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(0)); + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(0)); std::string nullbits; nullbits.resize(bytes_per_bits(v_.types().size())); @@ -1123,7 +1192,7 @@ class Codec } } - accu.step(wire::String(nullbits)); + accu.step(bw::String(nullbits)); size_t n{}; for (const auto &field : v_) { @@ -1142,13 +1211,13 @@ class Codec case field_type::Decimal: case field_type::NewDecimal: case field_type::Geometry: - accu.step(wire::VarInt(field->size())); + accu.step(bw::VarInt(field->size())); break; case field_type::Date: case field_type::DateTime: case field_type::Timestamp: case field_type::Time: - accu.step(wire::FixedInt<1>(field->size())); + accu.step(bw::FixedInt<1>(field->size())); break; case field_type::LongLong: case field_type::Double: @@ -1161,7 +1230,7 @@ class Codec // fixed size break; } - accu.step(wire::String(*field)); + accu.step(bw::String(*field)); } } @@ -1169,7 +1238,7 @@ class Codec } public: - using value_type = message::server::StmtRow; + using value_type = borrowable::message::server::StmtRow; using __base = impl::EncodeBase>; friend __base; @@ -1184,9 +1253,11 @@ class Codec static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps, std::vector types) { + namespace bw = borrowable::wire; + impl::DecodeBufferAccumulator accu(buffer, caps); - const auto row_byte_res = accu.template step>(); + const auto row_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); // first byte is 0x00 @@ -1195,12 +1266,12 @@ class Codec } const auto nullbits_res = - accu.template step(bytes_per_bits(types.size())); + accu.template step>(bytes_per_bits(types.size())); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); const auto nullbits = nullbits_res->value(); - std::vector values; + std::vector values; for (size_t n{}, bit_pos{2}, byte_pos{}; n < types.size(); ++n, ++bit_pos) { if (bit_pos > 7) { @@ -1226,7 +1297,7 @@ class Codec case field_type::Decimal: case field_type::NewDecimal: case field_type::Geometry: { - auto string_field_size_res = accu.template step(); + auto string_field_size_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1236,7 +1307,7 @@ class Codec case field_type::DateTime: case field_type::Timestamp: case field_type::Time: { - auto time_field_size_res = accu.template step>(); + auto time_field_size_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1263,7 +1334,7 @@ class Codec make_error_code(codec_errc::field_type_unknown)); } const auto value_res = - accu.template step(field_size_res.value()); + accu.template step>(field_size_res.value()); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); values.push_back(value_res->value()); @@ -1284,28 +1355,33 @@ class Codec /** * codec for server::Statistics message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::String(v_.stats())).result(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::String(v_.stats())).result(); } public: - using value_type = message::server::Statistics; + using value_type = borrowable::message::server::Statistics; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto stats_res = accu.template step(); + namespace bw = borrowable::wire; + + auto stats_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1325,7 +1401,9 @@ class CodecSimpleCommand : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(Base::cmd_byte())).result(); + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(Base::cmd_byte())).result(); } public: @@ -1341,7 +1419,9 @@ class CodecSimpleCommand const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != Base::cmd_byte()) { @@ -1391,11 +1471,11 @@ enum class CommandByte { * codec for client's Quit command. */ template <> -class Codec - : public CodecSimpleCommand, - message::client::Quit> { +class Codec + : public CodecSimpleCommand, + borrowable::message::client::Quit> { public: - using value_type = message::client::Quit; + using value_type = borrowable::message::client::Quit; using __base = CodecSimpleCommand, value_type>; constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} @@ -1409,11 +1489,12 @@ class Codec * codec for client's ResetConnection command. */ template <> -class Codec - : public CodecSimpleCommand, - message::client::ResetConnection> { +class Codec + : public CodecSimpleCommand< + Codec, + borrowable::message::client::ResetConnection> { public: - using value_type = message::client::ResetConnection; + using value_type = borrowable::message::client::ResetConnection; using __base = CodecSimpleCommand, value_type>; constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} @@ -1427,11 +1508,11 @@ class Codec * codec for client's Ping command. */ template <> -class Codec - : public CodecSimpleCommand, - message::client::Ping> { +class Codec + : public CodecSimpleCommand, + borrowable::message::client::Ping> { public: - using value_type = message::client::Ping; + using value_type = borrowable::message::client::Ping; using __base = CodecSimpleCommand, value_type>; constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} @@ -1445,11 +1526,11 @@ class Codec * codec for client's Statistics command. */ template <> -class Codec - : public CodecSimpleCommand, - message::client::Statistics> { +class Codec + : public CodecSimpleCommand, + borrowable::message::client::Statistics> { public: - using value_type = message::client::Statistics; + using value_type = borrowable::message::client::Statistics; using __base = CodecSimpleCommand, value_type>; constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} @@ -1462,23 +1543,26 @@ class Codec /** * codec for client's InitSchema command. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::String(v_.schema())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::String(v_.schema())) .result(); } public: - using value_type = message::client::InitSchema; + using value_type = borrowable::message::client::InitSchema; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -1489,14 +1573,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto schema_res = accu.template step(); + auto schema_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -1510,23 +1596,26 @@ class Codec /** * codec for client's Query command. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::String(v_.statement())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::String(v_.statement())) .result(); } public: - using value_type = message::client::Query; + using value_type = borrowable::message::client::Query; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -1537,14 +1626,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_res = accu.template step(); + auto statement_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -1564,28 +1655,33 @@ class Codec * * - String payload */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::String(v_.payload())).result(); + namespace bw = borrowable::wire; + + return accu.step(bw::String(v_.payload())).result(); } public: - using value_type = message::client::SendFile; + using value_type = borrowable::message::client::SendFile; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto payload_res = accu.template step(); + namespace bw = borrowable::wire; + + auto payload_res = accu.template step>(); if (!accu.result()) return accu.result().get_unexpected(); return std::make_pair(accu.result().value(), @@ -1599,24 +1695,27 @@ class Codec /** * codec for client's ListFields command. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::NulTermString(v_.table_name())) - .step(wire::String(v_.wildcard())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::NulTermString(v_.table_name())) + .step(bw::String(v_.wildcard())) .result(); } public: - using value_type = message::client::ListFields; + using value_type = borrowable::message::client::ListFields; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -1627,15 +1726,17 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto table_name_res = accu.template step(); - auto wildcard_res = accu.template step(); + auto table_name_res = accu.template step>(); + auto wildcard_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair( @@ -1651,22 +1752,24 @@ class Codec * codec for client's Reload command. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<1>(v_.cmds().to_ulong())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<1>(v_.cmds().to_ulong())) .result(); } public: - using value_type = message::client::Reload; + using value_type = borrowable::message::client::Reload; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -1677,14 +1780,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto cmds_res = accu.template step>(); + auto cmds_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(cmds_res->value())); @@ -1703,17 +1808,19 @@ class Codec * - FixedInt<4> id */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.connection_id())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.connection_id())) .result(); } public: - using value_type = message::client::Kill; + using value_type = borrowable::message::client::Kill; using __base = impl::EncodeBase>; friend __base; @@ -1729,14 +1836,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto connection_id_res = accu.template step>(); + auto connection_id_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -1750,23 +1859,26 @@ class Codec /** * codec for client's Prepared Statement command. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::String(v_.statement())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::String(v_.statement())) .result(); } public: - using value_type = message::client::StmtPrepare; + using value_type = borrowable::message::client::StmtPrepare; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -1777,14 +1889,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_res = accu.template step(); + auto statement_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -1798,15 +1912,18 @@ class Codec /** * codec for client's Execute Statement command. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.statement_id())) - .step(wire::FixedInt<1>(v_.flags().to_ullong())) - .step(wire::FixedInt<4>(v_.iteration_count())); + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.statement_id())) + .step(bw::FixedInt<1>(v_.flags().to_ullong())) + .step(bw::FixedInt<4>(v_.iteration_count())); // values.size() and types.size() MUST be the same if (!v_.values().empty()) { @@ -1832,13 +1949,13 @@ class Codec } } - accu.step(wire::String( - std::string(reinterpret_cast(nullbits.data()), - nullbits.size()))) - .step(wire::FixedInt<1>(v_.new_params_bound())); + accu.step(bw::String(typename bw::String::value_type( + reinterpret_cast(nullbits.data()), + nullbits.size()))) + .step(bw::FixedInt<1>(v_.new_params_bound())); if (v_.new_params_bound()) { for (const auto &t : v_.types()) { - accu.step(wire::FixedInt<2>(t)); + accu.step(bw::FixedInt<2>(t)); } size_t n{}; for (const auto &v : v_.values()) { @@ -1859,13 +1976,13 @@ class Codec case field_type::Decimal: case field_type::NewDecimal: case field_type::Geometry: - accu.step(wire::VarInt(v->size())); + accu.step(bw::VarInt(v->size())); break; case field_type::Date: case field_type::DateTime: case field_type::Timestamp: case field_type::Time: - accu.step(wire::FixedInt<1>(v->size())); + accu.step(bw::FixedInt<1>(v->size())); break; case field_type::LongLong: case field_type::Double: @@ -1878,7 +1995,7 @@ class Codec // fixed size break; } - accu.step(wire::String(v.value())); + accu.step(bw::String(v.value())); } } } @@ -1888,12 +2005,12 @@ class Codec } public: - using value_type = message::client::StmtExecute; + using value_type = borrowable::message::client::StmtExecute; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -1948,16 +2065,18 @@ class Codec Func &¶m_count_lookup) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_id_res = accu.template step>(); - auto flags_res = accu.template step>(); - auto iteration_count_res = accu.template step>(); + auto statement_id_res = accu.template step>(); + auto flags_res = accu.template step>(); + auto iteration_count_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1980,10 +2099,10 @@ class Codec } auto nullbits_res = - accu.template step(bytes_per_bits(param_count)); + accu.template step>(bytes_per_bits(param_count)); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - auto new_params_bound_res = accu.template step>(); + auto new_params_bound_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); std::vector types; @@ -1996,7 +2115,7 @@ class Codec values.reserve(param_count); for (size_t n{}; n < param_count; ++n) { - auto type_res = accu.template step>(); + auto type_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); types.push_back(type_res->value()); @@ -2025,7 +2144,7 @@ class Codec case field_type::Decimal: case field_type::NewDecimal: case field_type::Geometry: { - auto string_field_size_res = accu.template step(); + auto string_field_size_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2035,8 +2154,7 @@ class Codec case field_type::DateTime: case field_type::Timestamp: case field_type::Time: { - auto time_field_size_res = - accu.template step>(); + auto time_field_size_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2060,7 +2178,7 @@ class Codec break; } auto value_res = - accu.template step(field_size_res.value()); + accu.template step>(field_size_res.value()); if (!accu.result()) { return stdx::make_unexpected(accu.result().error()); } @@ -2088,25 +2206,28 @@ class Codec /** * codec for client's append data Statement command. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.statement_id())) - .step(wire::FixedInt<2>(v_.param_id())) - .step(wire::String(v_.data())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.statement_id())) + .step(bw::FixedInt<2>(v_.param_id())) + .step(bw::String(v_.data())) .result(); } public: - using value_type = message::client::StmtParamAppendData; + using value_type = borrowable::message::client::StmtParamAppendData; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -2117,16 +2238,18 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_id_res = accu.template step>(); - auto param_id_res = accu.template step>(); - auto data_res = accu.template step(); + auto statement_id_res = accu.template step>(); + auto param_id_res = accu.template step>(); + auto data_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -2142,17 +2265,19 @@ class Codec * codec for client's Close Statement command. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.statement_id())) + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.statement_id())) .result(); } public: - using value_type = message::client::StmtClose; + using value_type = borrowable::message::client::StmtClose; using __base = impl::EncodeBase>; friend __base; @@ -2168,14 +2293,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_id_res = accu.template step>(); + auto statement_id_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -2190,17 +2317,19 @@ class Codec * codec for client's Reset Statement command. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.statement_id())) + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.statement_id())) .result(); } public: - using value_type = message::client::StmtReset; + using value_type = borrowable::message::client::StmtReset; using __base = impl::EncodeBase>; friend __base; @@ -2216,14 +2345,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_id_res = accu.template step>(); + auto statement_id_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -2238,17 +2369,19 @@ class Codec * codec for client's SetOption command. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<2>(v_.option())) + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<2>(v_.option())) .result(); } public: - using value_type = message::client::SetOption; + using value_type = borrowable::message::client::SetOption; using __base = impl::EncodeBase>; friend __base; @@ -2264,14 +2397,16 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto option_res = accu.template step>(); + auto option_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair(accu.result().value(), @@ -2286,18 +2421,20 @@ class Codec * codec for client's Fetch Cursor command. */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.statement_id())) - .step(wire::FixedInt<4>(v_.row_count())) + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.statement_id())) + .step(bw::FixedInt<4>(v_.row_count())) .result(); } public: - using value_type = message::client::StmtFetch; + using value_type = borrowable::message::client::StmtFetch; using __base = impl::EncodeBase>; friend __base; @@ -2313,15 +2450,17 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto statement_id_res = accu.template step>(); - auto row_count_res = accu.template step>(); + auto statement_id_res = accu.template step>(); + auto row_count_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); return std::make_pair( @@ -2372,38 +2511,41 @@ class Codec * - plugin_auth * - connect_attributes */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - const auto shared_caps = v_.capabilities() & caps(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + const auto shared_caps = v_.capabilities() & this->caps(); if (shared_caps[classic_protocol::capabilities::pos::protocol_41]) { - accu.step(wire::FixedInt<4>(v_.capabilities().to_ulong())) - .step(wire::FixedInt<4>(v_.max_packet_size())) - .step(wire::FixedInt<1>(v_.collation())) - .step(wire::String(std::string(23, '\0'))); + accu.step(bw::FixedInt<4>(v_.capabilities().to_ulong())) + .step(bw::FixedInt<4>(v_.max_packet_size())) + .step(bw::FixedInt<1>(v_.collation())) + .step(bw::String(std::string(23, '\0'))); if (!(shared_caps[classic_protocol::capabilities::pos::ssl] && v_.username().empty())) { // the username is empty and SSL is set, this is a short SSL-greeting // packet - accu.step(wire::NulTermString(v_.username())); + accu.step(bw::NulTermString(v_.username())); if (shared_caps[classic_protocol::capabilities::pos:: client_auth_method_data_varint]) { - accu.step(wire::VarString(v_.auth_method_data())); + accu.step(bw::VarString(v_.auth_method_data())); } else if (shared_caps[classic_protocol::capabilities::pos:: secure_connection]) { - accu.step(wire::FixedInt<1>(v_.auth_method_data().size())) - .step(wire::String(v_.auth_method_data())); + accu.step(bw::FixedInt<1>(v_.auth_method_data().size())) + .step(bw::String(v_.auth_method_data())); } else { - accu.step(wire::NulTermString(v_.auth_method_data())); + accu.step(bw::NulTermString(v_.auth_method_data())); } if (shared_caps [classic_protocol::capabilities::pos::connect_with_schema]) { - accu.step(wire::NulTermString(v_.schema())); + accu.step(bw::NulTermString(v_.schema())); } if (!shared_caps @@ -2416,26 +2558,26 @@ class Codec // 2. auth-method-name is empty, it MAY be skipped. if (shared_caps[classic_protocol::capabilities::pos::plugin_auth] && !v_.auth_method_name().empty()) { - accu.step(wire::NulTermString(v_.auth_method_name())); + accu.step(bw::NulTermString(v_.auth_method_name())); } } else { if (shared_caps[classic_protocol::capabilities::pos::plugin_auth]) { - accu.step(wire::NulTermString(v_.auth_method_name())); + accu.step(bw::NulTermString(v_.auth_method_name())); } - accu.step(wire::VarString(v_.attributes())); + accu.step(bw::VarString(v_.attributes())); } } } else { - accu.step(wire::FixedInt<2>(v_.capabilities().to_ulong())) - .step(wire::FixedInt<3>(v_.max_packet_size())) - .step(wire::NulTermString(v_.username())); + accu.step(bw::FixedInt<2>(v_.capabilities().to_ulong())) + .step(bw::FixedInt<3>(v_.max_packet_size())) + .step(bw::NulTermString(v_.username())); if (shared_caps [classic_protocol::capabilities::pos::connect_with_schema]) { - accu.step(wire::NulTermString(v_.auth_method_data())) - .step(wire::String(v_.schema())); + accu.step(bw::NulTermString(v_.auth_method_data())) + .step(bw::String(v_.schema())); } else { - accu.step(wire::String(v_.auth_method_data())); + accu.step(bw::String(v_.auth_method_data())); } } @@ -2443,19 +2585,21 @@ class Codec } public: - using value_type = message::client::Greeting; + using value_type = borrowable::message::client::Greeting; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto capabilities_lo_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto capabilities_lo_res = accu.template step>(); if (!capabilities_lo_res) return stdx::make_unexpected(capabilities_lo_res.error()); @@ -2469,7 +2613,7 @@ class Codec if (shared_capabilities[classic_protocol::capabilities::pos::protocol_41]) { // if protocol_41 is set in the capabilities, we expected 2 more bytes // of capabilities - auto capabilities_hi_res = accu.template step>(); + auto capabilities_hi_res = accu.template step>(); if (!capabilities_hi_res) return stdx::make_unexpected(capabilities_hi_res.error()); @@ -2478,14 +2622,14 @@ class Codec shared_capabilities = caps & client_capabilities; - auto max_packet_size_res = accu.template step>(); - auto collation_res = accu.template step>(); + auto max_packet_size_res = accu.template step>(); + auto collation_res = accu.template step>(); - accu.template step(23); // skip 23 bytes + accu.template step>(23); // skip 23 bytes auto last_accu_res = accu.result(); - auto username_res = accu.template step(); + auto username_res = accu.template step>(); if (!accu.result()) { // if there isn't enough data for the nul-term-string, but we had the // 23-bytes ... @@ -2505,56 +2649,59 @@ class Codec // - varint length // - fixed-int-1 length // - null-term-string - stdx::expected auth_method_data_res; + stdx::expected, std::error_code> + auth_method_data_res; if (shared_capabilities[classic_protocol::capabilities::pos:: client_auth_method_data_varint]) { - auto res = accu.template step(); + auto res = accu.template step>(); if (!res) return stdx::make_unexpected(res.error()); - auth_method_data_res = wire::String(res->value()); + auth_method_data_res = bw::String(res->value()); } else if (shared_capabilities [classic_protocol::capabilities::pos::secure_connection]) { - auto auth_method_data_len_res = accu.template step>(); + auto auth_method_data_len_res = accu.template step>(); if (!auth_method_data_len_res) return stdx::make_unexpected(auth_method_data_len_res.error()); auto auth_method_data_len = auth_method_data_len_res->value(); - auto res = accu.template step(auth_method_data_len); + auto res = + accu.template step>(auth_method_data_len); if (!res) return stdx::make_unexpected(res.error()); - auth_method_data_res = wire::String(res->value()); + auth_method_data_res = bw::String(res->value()); } else { - auto res = accu.template step(); + auto res = accu.template step>(); if (!res) return stdx::make_unexpected(res.error()); - auth_method_data_res = wire::String(res->value()); + auth_method_data_res = bw::String(res->value()); } - stdx::expected schema_res; + stdx::expected, std::error_code> schema_res; if (shared_capabilities [classic_protocol::capabilities::pos::connect_with_schema]) { - schema_res = accu.template step(); + schema_res = accu.template step>(); } if (!schema_res) return stdx::make_unexpected(schema_res.error()); - stdx::expected auth_method_res; + stdx::expected, std::error_code> + auth_method_res; if (shared_capabilities [classic_protocol::capabilities::pos::plugin_auth]) { if (net::buffer_size(buffer) == accu.result().value()) { // even with plugin_auth set, the server is fine, if no // auth_method_name is sent. - auth_method_res = wire::NulTermString{}; + auth_method_res = bw::NulTermString{}; } else { - auth_method_res = accu.template step(); + auth_method_res = accu.template step>(); } } if (!auth_method_res) return stdx::make_unexpected(auth_method_res.error()); - stdx::expected attributes_res; + stdx::expected, std::error_code> attributes_res; if (shared_capabilities [classic_protocol::capabilities::pos::connect_attributes]) { - attributes_res = accu.template step(); + attributes_res = accu.template step>(); } if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2567,24 +2714,25 @@ class Codec auth_method_res->value(), attributes_res->value())); } else { - auto max_packet_size_res = accu.template step>(); + auto max_packet_size_res = accu.template step>(); - auto username_res = accu.template step(); + auto username_res = accu.template step>(); - stdx::expected auth_method_data_res; - stdx::expected schema_res; + stdx::expected, std::error_code> + auth_method_data_res; + stdx::expected, std::error_code> schema_res; if (shared_capabilities [classic_protocol::capabilities::pos::connect_with_schema]) { - auto res = accu.template step(); + auto res = accu.template step>(); if (!res) return stdx::make_unexpected(res.error()); // auth_method_data is a wire::String, move it over - auth_method_data_res = wire::String(res->value()); + auth_method_data_res = bw::String(res->value()); - schema_res = accu.template step(); + schema_res = accu.template step>(); } else { - auth_method_data_res = accu.template step(); + auth_method_data_res = accu.template step>(); } if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2628,28 +2776,33 @@ class Codec * * sent after server::AuthMethodData or server::AuthMethodContinue */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::String(v_.auth_method_data())).result(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::String(v_.auth_method_data())).result(); } public: - using value_type = message::client::AuthMethodData; + using value_type = borrowable::message::client::AuthMethodData; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto auth_method_data_res = accu.template step(); + namespace bw = borrowable::wire; + + auto auth_method_data_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2670,36 +2823,40 @@ class Codec * - plugin_auth * - connect_attributes */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::NulTermString(v_.username())); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::NulTermString(v_.username())); - if (caps()[classic_protocol::capabilities::pos::secure_connection]) { - accu.step(wire::FixedInt<1>(v_.auth_method_data().size())) - .step(wire::String(v_.auth_method_data())); + if (this->caps()[classic_protocol::capabilities::pos::secure_connection]) { + accu.step(bw::FixedInt<1>(v_.auth_method_data().size())) + .step(bw::String(v_.auth_method_data())); } else { - accu.step(wire::NulTermString(v_.auth_method_data())); + accu.step(bw::NulTermString(v_.auth_method_data())); } - accu.step(wire::NulTermString(v_.schema())); + accu.step(bw::NulTermString(v_.schema())); // 4.1 and later have a collation // // this could be checked via the protocol_41 capability, but that's not // what the server does if (v_.collation() != 0x00 || - caps()[classic_protocol::capabilities::pos::plugin_auth] || - caps()[classic_protocol::capabilities::pos::connect_attributes]) { - accu.step(wire::FixedInt<2>(v_.collation())); - if (caps()[classic_protocol::capabilities::pos::plugin_auth]) { - accu.step(wire::NulTermString(v_.auth_method_name())); + this->caps()[classic_protocol::capabilities::pos::plugin_auth] || + this->caps()[classic_protocol::capabilities::pos::connect_attributes]) { + accu.step(bw::FixedInt<2>(v_.collation())); + if (this->caps()[classic_protocol::capabilities::pos::plugin_auth]) { + accu.step(bw::NulTermString(v_.auth_method_name())); } - if (caps()[classic_protocol::capabilities::pos::connect_attributes]) { - accu.step(wire::VarString(v_.attributes())); + if (this->caps() + [classic_protocol::capabilities::pos::connect_attributes]) { + accu.step(bw::VarString(v_.attributes())); } } @@ -2707,12 +2864,12 @@ class Codec } public: - using value_type = message::client::ChangeUser; + using value_type = borrowable::message::client::ChangeUser; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -2723,37 +2880,39 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto username_res = accu.template step(); + auto username_res = accu.template step>(); // auth-method-data is either // // - fixed-int-1 length // - null-term-string - stdx::expected auth_method_data_res; + stdx::expected, std::error_code> auth_method_data_res; if (caps[classic_protocol::capabilities::pos::secure_connection]) { - auto auth_method_data_len_res = accu.template step>(); + auto auth_method_data_len_res = accu.template step>(); if (!auth_method_data_len_res) return stdx::make_unexpected(auth_method_data_len_res.error()); auto auth_method_data_len = auth_method_data_len_res->value(); - auto res = accu.template step(auth_method_data_len); + auto res = accu.template step>(auth_method_data_len); if (!res) return stdx::make_unexpected(res.error()); - auth_method_data_res = wire::String(res->value()); + auth_method_data_res = bw::String(res->value()); } else { - auto res = accu.template step(); + auto res = accu.template step>(); if (!res) return stdx::make_unexpected(res.error()); - auth_method_data_res = wire::String(res->value()); + auth_method_data_res = bw::String(res->value()); } - auto schema_res = accu.template step(); + auto schema_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2766,16 +2925,17 @@ class Codec } // added in 4.1 - auto collation_res = accu.template step>(); + auto collation_res = accu.template step>(); - stdx::expected auth_method_name_res; + stdx::expected, std::error_code> + auth_method_name_res; if (caps[classic_protocol::capabilities::pos::plugin_auth]) { - auth_method_name_res = accu.template step(); + auth_method_name_res = accu.template step>(); } - stdx::expected attributes_res; + stdx::expected, std::error_code> attributes_res; if (caps[classic_protocol::capabilities::pos::connect_attributes]) { - attributes_res = accu.template step(); + attributes_res = accu.template step>(); } if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2797,11 +2957,11 @@ class Codec * response: server::Ok or server::Error */ template <> -class Codec - : public CodecSimpleCommand, - message::client::Clone> { +class Codec + : public CodecSimpleCommand, + borrowable::message::client::Clone> { public: - using value_type = message::client::Clone; + using value_type = borrowable::message::client::Clone; using __base = CodecSimpleCommand, value_type>; constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} @@ -2814,20 +2974,23 @@ class Codec /** * codec for client side dump-binlog message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { public: - using value_type = message::client::BinlogDump; + using value_type = borrowable::message::client::BinlogDump; private: template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.position())) - .step(wire::FixedInt<2>(v_.flags().underlying_value())) - .step(wire::FixedInt<4>(v_.server_id())) - .step(wire::String(v_.filename())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.position())) + .step(bw::FixedInt<2>(v_.flags().underlying_value())) + .step(bw::FixedInt<4>(v_.server_id())) + .step(bw::String(v_.filename())) .result(); } @@ -2836,7 +2999,7 @@ class Codec friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -2847,21 +3010,23 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto position_res = accu.template step>(); - auto flags_res = accu.template step>(); - auto server_id_res = accu.template step>(); + auto position_res = accu.template step>(); + auto flags_res = accu.template step>(); + auto server_id_res = accu.template step>(); - auto filename_res = accu.template step(); + auto filename_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - stdx::flags flags; + stdx::flags flags; flags.underlying_value(flags_res->value()); return std::make_pair( @@ -2877,26 +3042,29 @@ class Codec /** * codec for client side register-replica message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { public: - using value_type = message::client::RegisterReplica; + using value_type = borrowable::message::client::RegisterReplica; private: template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<4>(v_.server_id())) - .step(wire::FixedInt<1>(v_.hostname().size())) - .step(wire::String(v_.hostname())) - .step(wire::FixedInt<1>(v_.username().size())) - .step(wire::String(v_.username())) - .step(wire::FixedInt<1>(v_.password().size())) - .step(wire::String(v_.password())) - .step(wire::FixedInt<2>(v_.port())) - .step(wire::FixedInt<4>(v_.replication_rank())) - .step(wire::FixedInt<4>(v_.master_id())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<4>(v_.server_id())) + .step(bw::FixedInt<1>(v_.hostname().size())) + .step(bw::String(v_.hostname())) + .step(bw::FixedInt<1>(v_.username().size())) + .step(bw::String(v_.username())) + .step(bw::FixedInt<1>(v_.password().size())) + .step(bw::String(v_.password())) + .step(bw::FixedInt<2>(v_.port())) + .step(bw::FixedInt<4>(v_.replication_rank())) + .step(bw::FixedInt<4>(v_.master_id())) .result(); } @@ -2905,7 +3073,7 @@ class Codec friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -2916,34 +3084,36 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto server_id_res = accu.template step>(); - auto hostname_len_res = accu.template step>(); + auto server_id_res = accu.template step>(); + auto hostname_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); auto hostname_res = - accu.template step(hostname_len_res->value()); + accu.template step>(hostname_len_res->value()); - auto username_len_res = accu.template step>(); + auto username_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); auto username_res = - accu.template step(username_len_res->value()); + accu.template step>(username_len_res->value()); - auto password_len_res = accu.template step>(); + auto password_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); auto password_res = - accu.template step(password_len_res->value()); + accu.template step>(password_len_res->value()); - auto port_res = accu.template step>(); - auto replication_rank_res = accu.template step>(); - auto master_id_res = accu.template step>(); + auto port_res = accu.template step>(); + auto replication_rank_res = accu.template step>(); + auto master_id_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2962,27 +3132,30 @@ class Codec /** * codec for client side dump-binlog-with-gtid message. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { public: - using value_type = message::client::BinlogDumpGtid; + using value_type = borrowable::message::client::BinlogDumpGtid; private: template - auto accumulate_fields(Accumulator &&accu) const { - accu.step(wire::FixedInt<1>(cmd_byte())) - .step(wire::FixedInt<2>(v_.flags().underlying_value())) - .step(wire::FixedInt<4>(v_.server_id())) - .step(wire::FixedInt<4>(v_.filename().size())) - .step(wire::String(v_.filename())) - .step(wire::FixedInt<8>(v_.position())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + accu.step(bw::FixedInt<1>(cmd_byte())) + .step(bw::FixedInt<2>(v_.flags().underlying_value())) + .step(bw::FixedInt<4>(v_.server_id())) + .step(bw::FixedInt<4>(v_.filename().size())) + .step(bw::String(v_.filename())) + .step(bw::FixedInt<8>(v_.position())) // ; if (v_.flags() & value_type::Flags::through_gtid) { - accu.step(wire::FixedInt<4>(v_.sids().size())) - .step(wire::String(v_.sids())); + accu.step(bw::FixedInt<4>(v_.sids().size())) + .step(bw::String(v_.sids())); } return accu.result(); @@ -2993,7 +3166,7 @@ class Codec friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t cmd_byte() noexcept { @@ -3004,28 +3177,31 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto cmd_byte_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto cmd_byte_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - auto flags_res = accu.template step>(); - auto server_id_res = accu.template step>(); - auto filename_len_res = accu.template step>(); + auto flags_res = accu.template step>(); + auto server_id_res = accu.template step>(); + auto filename_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); auto filename_res = - accu.template step(filename_len_res->value()); - auto position_res = accu.template step>(); - auto sids_len_res = accu.template step>(); + accu.template step>(filename_len_res->value()); + auto position_res = accu.template step>(); + auto sids_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - auto sids_res = accu.template step(sids_len_res->value()); + auto sids_res = + accu.template step>(sids_len_res->value()); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - stdx::flags flags; + stdx::flags flags; flags.underlying_value(flags_res->value()); return std::make_pair( diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 3178996..e47aeb3 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -42,25 +42,28 @@ namespace classic_protocol { * part of session_track::Field */ template <> -class Codec - : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase< + Codec> { template constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + return accu - .step(wire::FixedInt<1>(0x08)) // length - .step(wire::FixedInt<1>(v_.trx_type())) - .step(wire::FixedInt<1>(v_.read_unsafe())) - .step(wire::FixedInt<1>(v_.read_trx())) - .step(wire::FixedInt<1>(v_.write_unsafe())) - .step(wire::FixedInt<1>(v_.write_trx())) - .step(wire::FixedInt<1>(v_.stmt_unsafe())) - .step(wire::FixedInt<1>(v_.resultset())) - .step(wire::FixedInt<1>(v_.locked_tables())) + .step(bw::FixedInt<1>(0x08)) // length + .step(bw::FixedInt<1>(v_.trx_type())) + .step(bw::FixedInt<1>(v_.read_unsafe())) + .step(bw::FixedInt<1>(v_.read_trx())) + .step(bw::FixedInt<1>(v_.write_unsafe())) + .step(bw::FixedInt<1>(v_.write_trx())) + .step(bw::FixedInt<1>(v_.stmt_unsafe())) + .step(bw::FixedInt<1>(v_.resultset())) + .step(bw::FixedInt<1>(v_.locked_tables())) .result(); } public: - using value_type = session_track::TransactionState; + using value_type = borrowable::session_track::TransactionState; using __base = impl::EncodeBase>; friend __base; @@ -85,7 +88,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - const auto payload_length_res = accu.template step(); + namespace bw = borrowable::wire; + + const auto payload_length_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); if (payload_length_res->value() != 0x08) { @@ -93,14 +98,14 @@ class Codec return stdx::make_unexpected(make_error_code(std::errc::bad_message)); } - const auto trx_type_res = accu.template step>(); - const auto read_unsafe_res = accu.template step>(); - const auto read_trx_res = accu.template step>(); - const auto write_unsafe_res = accu.template step>(); - const auto write_trx_res = accu.template step>(); - const auto stmt_unsafe_res = accu.template step>(); - const auto resultset_res = accu.template step>(); - const auto locked_tables_res = accu.template step>(); + const auto trx_type_res = accu.template step>(); + const auto read_unsafe_res = accu.template step>(); + const auto read_trx_res = accu.template step>(); + const auto write_unsafe_res = accu.template step>(); + const auto write_trx_res = accu.template step>(); + const auto stmt_unsafe_res = accu.template step>(); + const auto resultset_res = accu.template step>(); + const auto locked_tables_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -121,22 +126,25 @@ class Codec * * part of session_track::Field */ -template <> -class Codec - : public impl::EncodeBase< - Codec> { +template +class Codec> + : public impl::EncodeBase>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarString(v_.characteristics())).result(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::VarString(v_.characteristics())).result(); } public: - using value_type = session_track::TransactionCharacteristics; + using value_type = + borrowable::session_track::TransactionCharacteristics; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t type_byte() { return 0x04; } @@ -156,7 +164,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - const auto characteristics_res = accu.template step(); + namespace bw = borrowable::wire; + + auto characteristics_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -177,8 +187,10 @@ template <> class Codec : public impl::EncodeBase> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(v_.state())).result(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(v_.state())).result(); } public: @@ -187,7 +199,7 @@ class Codec friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t type_byte() { return 0x02; } @@ -207,7 +219,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto state_res = accu.template step>(); + namespace bw = borrowable::wire; + + auto state_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -224,21 +238,24 @@ class Codec * * part of session_track::Field */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarString(v_.schema())).result(); + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::VarString(v_.schema())).result(); } public: - using value_type = session_track::Schema; + using value_type = borrowable::session_track::Schema; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t type_byte() { return 0x01; } @@ -258,7 +275,9 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto schema_res = accu.template step(); + namespace bw = borrowable::wire; + + auto schema_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -275,23 +294,26 @@ class Codec * * part of session_track::Field */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarString(v_.key())) - .step(wire::VarString(v_.value())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::VarString(v_.key())) + .step(bw::VarString(v_.value())) .result(); } public: - using value_type = session_track::SystemVariable; + using value_type = borrowable::session_track::SystemVariable; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t type_byte() { return 0x00; } @@ -311,8 +333,10 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto key_res = accu.template step(); - auto value_res = accu.template step(); + namespace bw = borrowable::wire; + + auto key_res = accu.template step>(); + auto value_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -337,23 +361,26 @@ class Codec * * part of session_track::Field */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(v_.spec())) - .step(wire::VarString(v_.gtid())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(v_.spec())) + .step(bw::VarString(v_.gtid())) .result(); } public: - using value_type = session_track::Gtid; + using value_type = borrowable::session_track::Gtid; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} constexpr static uint8_t type_byte() { return 0x03; } @@ -373,8 +400,10 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto spec_res = accu.template step>(); - auto gtid_res = accu.template step(); + namespace bw = borrowable::wire; + + auto spec_res = accu.template step>(); + auto gtid_res = accu.template step>(); if (!accu.result()) return accu.result().get_unexpected(); @@ -403,24 +432,27 @@ class Codec * - 0x04 session_track::TransactionCharacteristics * - 0x05 session_track::TransactionState */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::FixedInt<1>(v_.type())) - .step(wire::VarString(v_.data())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + namespace bw = borrowable::wire; + + return accu.step(bw::FixedInt<1>(v_.type())) + .step(bw::VarString(v_.data())) .result(); } public: - using value_type = session_track::Field; + using value_type = borrowable::session_track::Field; using __base = impl::EncodeBase>; friend __base; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} /** @@ -438,8 +470,10 @@ class Codec const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); - auto type_res = accu.template step>(); - auto data_res = accu.template step(); + namespace bw = borrowable::wire; + + auto type_res = accu.template step>(); + auto data_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 9000888..1423974 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -48,19 +48,18 @@ namespace classic_protocol { * classic proto uses 1, 2, 3, 4, 8 for IntSize */ template -class Codec> { +class Codec> { public: static constexpr size_t int_size{IntSize}; - using value_type = wire::FixedInt; + using value_type = borrowable::wire::FixedInt; - constexpr Codec(value_type v, capabilities::value_type /* unused */) - : v_{v} {} + constexpr Codec(value_type v, capabilities::value_type /* caps */) : v_{v} {} /** * size of the encoded object. */ - constexpr size_t size() const noexcept { return int_size; } + static constexpr size_t size() noexcept { return int_size; } /** * encode value_type into buffer. @@ -133,22 +132,23 @@ class Codec> { * [1 + 8 bytes read, only 4 bytes used] */ template <> -class Codec : public impl::EncodeBase> { +class Codec + : public impl::EncodeBase> { template constexpr auto accumulate_fields(Accumulator &&accu) const { if (v_.value() < 251) { - return accu.step(wire::FixedInt<1>(v_.value())).result(); + return accu.step(borrowable::wire::FixedInt<1>(v_.value())).result(); } else if (v_.value() < 1 << 16) { - return accu.step(wire::FixedInt<1>(varint_16)) - .step(wire::FixedInt<2>(v_.value())) + return accu.step(borrowable::wire::FixedInt<1>(varint_16)) + .step(borrowable::wire::FixedInt<2>(v_.value())) .result(); } else if (v_.value() < (1 << 24)) { - return accu.step(wire::FixedInt<1>(varint_24)) - .step(wire::FixedInt<3>(v_.value())) + return accu.step(borrowable::wire::FixedInt<1>(varint_24)) + .step(borrowable::wire::FixedInt<3>(v_.value())) .result(); } else { - return accu.step(wire::FixedInt<1>(varint_64)) - .step(wire::FixedInt<8>(v_.value())) + return accu.step(borrowable::wire::FixedInt<1>(varint_64)) + .step(borrowable::wire::FixedInt<8>(v_.value())) .result(); } } @@ -157,7 +157,7 @@ class Codec : public impl::EncodeBase> { static constexpr uint8_t varint_16{0xfc}; static constexpr uint8_t varint_24{0xfd}; static constexpr uint8_t varint_64{0xfe}; - using value_type = wire::VarInt; + using value_type = borrowable::wire::VarInt; using __base = impl::EncodeBase>; friend __base; @@ -172,7 +172,7 @@ class Codec : public impl::EncodeBase> { impl::DecodeBufferAccumulator accu(buffer, caps); // length - auto first_byte_res = accu.template step>(); + auto first_byte_res = accu.template step>(); if (!first_byte_res) return stdx::make_unexpected(first_byte_res.error()); auto first_byte = first_byte_res->value(); @@ -180,17 +180,17 @@ class Codec : public impl::EncodeBase> { if (first_byte < 251) { return std::make_pair(accu.result().value(), value_type(first_byte)); } else if (first_byte == varint_16) { - auto value_res = accu.template step>(); + auto value_res = accu.template step>(); if (!value_res) return stdx::make_unexpected(value_res.error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); } else if (first_byte == varint_24) { - auto value_res = accu.template step>(); + auto value_res = accu.template step>(); if (!value_res) return stdx::make_unexpected(value_res.error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); } else if (first_byte == varint_64) { - auto value_res = accu.template step>(); + auto value_res = accu.template step>(); if (!value_res) return stdx::make_unexpected(value_res.error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); @@ -207,14 +207,15 @@ class Codec : public impl::EncodeBase> { * codec for a NULL value in the Resultset. */ template <> -class Codec : public Codec> { +class Codec + : public Codec> { public: - using value_type = wire::Null; + using value_type = borrowable::wire::Null; static constexpr uint8_t nul_byte{0xfb}; Codec(value_type, capabilities::value_type caps) - : Codec>(nul_byte, caps) {} + : Codec>(nul_byte, caps) {} static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type /* caps */) { @@ -283,15 +284,15 @@ class Codec { * * limited by length or buffer.size() */ -template <> -class Codec { +template +class Codec> { public: - using value_type = wire::String; + using value_type = borrowable::wire::String; - Codec(value_type v, capabilities::value_type caps) + constexpr Codec(value_type v, capabilities::value_type caps) : v_{std::move(v)}, caps_{caps} {} - size_t size() const noexcept { return v_.value().size(); } + constexpr size_t size() const noexcept { return v_.value().size(); } static size_t max_size() noexcept { // we actually don't know what the size of the null-term string is ... until @@ -334,22 +335,23 @@ class Codec { * - varint of string length * - string of length */ -template <> -class Codec : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.step(wire::VarInt(v_.value().size())) - .step(wire::String(v_.value())) + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.step(borrowable::wire::VarInt(v_.value().size())) + .step(borrowable::wire::String(v_.value())) .result(); } public: - using value_type = wire::VarString; + using value_type = borrowable::wire::VarString; using base_type = impl::EncodeBase>; friend base_type; - Codec(value_type val, capabilities::value_type caps) + constexpr Codec(value_type val, capabilities::value_type caps) : base_type(caps), v_{std::move(val)} {} static size_t max_size() noexcept { @@ -362,12 +364,13 @@ class Codec : public impl::EncodeBase> { const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); // decode the length - auto var_string_len_res = accu.template step(); + auto var_string_len_res = accu.template step(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); // decode string of length auto var_string_res = - accu.template step(var_string_len_res->value()); + accu.template step>( + var_string_len_res->value()); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -382,23 +385,24 @@ class Codec : public impl::EncodeBase> { /** * codec for 0-terminated string. */ -template <> -class Codec - : public impl::EncodeBase> { +template +class Codec> + : public impl::EncodeBase< + Codec>> { template - auto accumulate_fields(Accumulator &&accu) const { - return accu.template step(v_) - .template step>(0) + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.template step>(v_) + .template step>(0) .result(); } public: - using value_type = wire::NulTermString; + using value_type = borrowable::wire::NulTermString; using base_type = impl::EncodeBase>; friend base_type; - Codec(value_type val, capabilities::value_type caps) + constexpr Codec(value_type val, capabilities::value_type caps) : base_type(caps), v_{std::move(val)} {} static size_t max_size() noexcept { @@ -435,6 +439,7 @@ class Codec private: const value_type v_; }; + } // namespace classic_protocol #endif diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 779d4f4..c168f1e 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -34,22 +34,63 @@ #include "mysql/harness/stdx/flags.h" #include "mysqlrouter/classic_protocol_constants.h" +namespace classic_protocol::message::client::impl { +class BinlogDump { + public: + // flags of message::client::BinlogDump + enum class Flags : uint16_t { + non_blocking = 1 << 0, + }; +}; +} // namespace classic_protocol::message::client::impl + +namespace stdx { +// enable flag-ops for BinlogDump::Flags +template <> +struct is_flags + : std::true_type {}; +} // namespace stdx + // +namespace classic_protocol::message::client::impl { +class BinlogDumpGtid { + public: + // flags of message::client::BinlogDumpGtid + enum class Flags : uint16_t { + non_blocking = 1 << 0, + through_position = 1 << 1, + through_gtid = 1 << 2, + }; +}; +} // namespace classic_protocol::message::client::impl + +namespace stdx { +// enable flag-ops for BinlogDumpGtid::Flags +template <> +struct is_flags + : std::true_type {}; +} // namespace stdx + namespace classic_protocol { +namespace borrowable { /** * AuthMethod of classic protocol. * * classic proto supports negotiating the auth-method via capabilities and * auth-method names. */ +template class AuthMethod { public: + using string_type = + std::conditional_t; + AuthMethod(classic_protocol::capabilities::value_type capabilities, - std::string auth_method_name) + string_type auth_method_name) : capabilities_{capabilities}, auth_method_name_{std::move(auth_method_name)} {} - std::string name() const { + constexpr string_type name() const { if (auth_method_name_.empty() && !capabilities_[classic_protocol::capabilities::pos::plugin_auth]) { if (capabilities_ @@ -65,19 +106,25 @@ class AuthMethod { private: const classic_protocol::capabilities::value_type capabilities_; - const std::string auth_method_name_; + const string_type auth_method_name_; }; namespace message { namespace server { + +template class Greeting { public: - Greeting(uint8_t protocol_version, std::string version, - uint32_t connection_id, std::string auth_method_data, - classic_protocol::capabilities::value_type capabilities, - uint8_t collation, classic_protocol::status::value_type status_flags, - std::string auth_method_name) + using string_type = + std::conditional_t; + + constexpr Greeting(uint8_t protocol_version, string_type version, + uint32_t connection_id, string_type auth_method_data, + classic_protocol::capabilities::value_type capabilities, + uint8_t collation, + classic_protocol::status::value_type status_flags, + string_type auth_method_name) : protocol_version_{protocol_version}, version_{std::move(version)}, connection_id_{connection_id}, @@ -87,10 +134,12 @@ class Greeting { status_flags_{status_flags}, auth_method_name_{std::move(auth_method_name)} {} - uint8_t protocol_version() const noexcept { return protocol_version_; } - std::string version() const { return version_; } - std::string auth_method_name() const { return auth_method_name_; } - std::string auth_method_data() const { return auth_method_data_; } + constexpr uint8_t protocol_version() const noexcept { + return protocol_version_; + } + constexpr string_type version() const { return version_; } + constexpr string_type auth_method_name() const { return auth_method_name_; } + constexpr string_type auth_method_data() const { return auth_method_data_; } classic_protocol::capabilities::value_type capabilities() const noexcept { return capabilities_; } @@ -99,24 +148,26 @@ class Greeting { capabilities_ = caps; } - uint8_t collation() const noexcept { return collation_; } - classic_protocol::status::value_type status_flags() const noexcept { + constexpr uint8_t collation() const noexcept { return collation_; } + constexpr classic_protocol::status::value_type status_flags() const noexcept { return status_flags_; } - uint32_t connection_id() const noexcept { return connection_id_; } + constexpr uint32_t connection_id() const noexcept { return connection_id_; } private: uint8_t protocol_version_; - std::string version_; + string_type version_; uint32_t connection_id_; - std::string auth_method_data_; + string_type auth_method_data_; classic_protocol::capabilities::value_type capabilities_; uint8_t collation_; classic_protocol::status::value_type status_flags_; - std::string auth_method_name_; + string_type auth_method_name_; }; -inline bool operator==(const Greeting &a, const Greeting &b) { +template +inline bool operator==(const Greeting &a, + const Greeting &b) { return (a.protocol_version() == b.protocol_version()) && (a.version() == b.version()) && (a.connection_id() == b.connection_id()) && @@ -127,23 +178,30 @@ inline bool operator==(const Greeting &a, const Greeting &b) { (a.auth_method_name() == b.auth_method_name()); } +template class AuthMethodSwitch { public: - AuthMethodSwitch() = default; + using string_type = + std::conditional_t; + + constexpr AuthMethodSwitch() = default; - AuthMethodSwitch(std::string auth_method, std::string auth_method_data) + constexpr AuthMethodSwitch(string_type auth_method, + string_type auth_method_data) : auth_method_{std::move(auth_method)}, auth_method_data_{std::move(auth_method_data)} {} - std::string auth_method() const { return auth_method_; } - std::string auth_method_data() const { return auth_method_data_; } + string_type auth_method() const { return auth_method_; } + string_type auth_method_data() const { return auth_method_data_; } private: - std::string auth_method_; - std::string auth_method_data_; + string_type auth_method_; + string_type auth_method_data_; }; -inline bool operator==(const AuthMethodSwitch &a, const AuthMethodSwitch &b) { +template +inline bool operator==(const AuthMethodSwitch &a, + const AuthMethodSwitch &b) { return (a.auth_method_data() == b.auth_method_data()) && (a.auth_method() == b.auth_method()); } @@ -163,18 +221,24 @@ inline bool operator==(const AuthMethodSwitch &a, const AuthMethodSwitch &b) { * - 0x01 0x03 (send full handshake) * - 0x01 0x04 (fast path done) */ +template class AuthMethodData { public: - AuthMethodData(std::string auth_method_data) + using string_type = + std::conditional_t; + + constexpr AuthMethodData(string_type auth_method_data) : auth_method_data_{std::move(auth_method_data)} {} - std::string auth_method_data() const { return auth_method_data_; } + constexpr string_type auth_method_data() const { return auth_method_data_; } private: - std::string auth_method_data_; + string_type auth_method_data_; }; -inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { +template +inline bool operator==(const AuthMethodData &a, + const AuthMethodData &b) { return a.auth_method_data() == b.auth_method_data(); } @@ -188,13 +252,18 @@ inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { * - optional message * - optional server-side tracked session_changes */ +template class Ok { public: - Ok() = default; + using string_type = + std::conditional_t; + + constexpr Ok() = default; - Ok(uint64_t affected_rows, uint64_t last_insert_id, - classic_protocol::status::value_type status_flags, uint16_t warning_count, - std::string message = "", std::string session_changes = "") + constexpr Ok(uint64_t affected_rows, uint64_t last_insert_id, + classic_protocol::status::value_type status_flags, + uint16_t warning_count, string_type message = "", + string_type session_changes = "") : status_flags_{status_flags}, warning_count_{warning_count}, last_insert_id_{last_insert_id}, @@ -202,27 +271,27 @@ class Ok { message_{std::move(message)}, session_changes_{std::move(session_changes)} {} - void status_flags(classic_protocol::status::value_type flags) { + constexpr void status_flags(classic_protocol::status::value_type flags) { status_flags_ = flags; } - classic_protocol::status::value_type status_flags() const noexcept { + constexpr classic_protocol::status::value_type status_flags() const noexcept { return status_flags_; } - void warning_count(uint16_t count) { warning_count_ = count; } - uint16_t warning_count() const noexcept { return warning_count_; } + constexpr void warning_count(uint16_t count) { warning_count_ = count; } + constexpr uint16_t warning_count() const noexcept { return warning_count_; } - void last_insert_id(uint64_t val) { last_insert_id_ = val; } - uint64_t last_insert_id() const noexcept { return last_insert_id_; } + constexpr void last_insert_id(uint64_t val) { last_insert_id_ = val; } + constexpr uint64_t last_insert_id() const noexcept { return last_insert_id_; } - void affected_rows(uint64_t val) { affected_rows_ = val; } - uint64_t affected_rows() const noexcept { return affected_rows_; } + constexpr void affected_rows(uint64_t val) { affected_rows_ = val; } + constexpr uint64_t affected_rows() const noexcept { return affected_rows_; } - void message(const std::string &msg) { message_ = msg; } - std::string message() const { return message_; } + constexpr void message(const string_type &msg) { message_ = msg; } + constexpr string_type message() const { return message_; } - void session_changes(const std::string &changes) { + constexpr void session_changes(const string_type &changes) { session_changes_ = changes; } /** @@ -230,7 +299,7 @@ class Ok { * * @returns encoded array of session_track::Field */ - std::string session_changes() const { return session_changes_; } + string_type session_changes() const { return session_changes_; } private: classic_protocol::status::value_type status_flags_{}; @@ -238,11 +307,12 @@ class Ok { uint64_t last_insert_id_{}; uint64_t affected_rows_{}; - std::string message_{}; - std::string session_changes_{}; + string_type message_{}; + string_type session_changes_{}; }; -inline bool operator==(const Ok &a, const Ok &b) { +template +inline bool operator==(const Ok &a, const Ok &b) { return (a.status_flags() == b.status_flags()) && (a.warning_count() == b.warning_count()) && (a.last_insert_id() == b.last_insert_id()) && @@ -254,29 +324,41 @@ inline bool operator==(const Ok &a, const Ok &b) { /** * End of Resultset message. */ -class Eof : public Ok { +template +class Eof : public Ok { public: - using Ok::Ok; + using string_type = + std::conditional_t; + + using base__ = Ok; + + using base__::base__; // 3.23-like constructor - Eof() : Ok(0, 0, 0, 0) {} + constexpr Eof() = default; // 4.1-like constructor - Eof(classic_protocol::status::value_type status_flags, uint16_t warning_count) - : Ok(0, 0, status_flags, warning_count) {} - - Eof(classic_protocol::status::value_type status_flags, uint16_t warning_count, - std::string message, std::string session_changes) - : Ok(0, 0, status_flags, warning_count, std::move(message), - std::move(session_changes)) {} + constexpr Eof(classic_protocol::status::value_type status_flags, + uint16_t warning_count) + : base__(0, 0, status_flags, warning_count) {} + + constexpr Eof(classic_protocol::status::value_type status_flags, + uint16_t warning_count, string_type message, + string_type session_changes) + : base__(0, 0, status_flags, warning_count, std::move(message), + std::move(session_changes)) {} }; /** * Error message. */ +template class Error { public: - Error() = default; + using string_type = + std::conditional_t; + + constexpr Error() = default; /** * construct an Error message. * @@ -284,23 +366,24 @@ class Error { * @param message error message * @param sql_state SQL state */ - Error(uint16_t error_code, std::string message, - std::string sql_state = "HY000") + constexpr Error(uint16_t error_code, string_type message, + string_type sql_state = "HY000") : error_code_{error_code}, message_{std::move(message)}, sql_state_{std::move(sql_state)} {} - uint16_t error_code() const noexcept { return error_code_; } - std::string sql_state() const { return sql_state_; } - std::string message() const { return message_; } + constexpr uint16_t error_code() const noexcept { return error_code_; } + constexpr string_type sql_state() const { return sql_state_; } + constexpr string_type message() const { return message_; } private: uint16_t error_code_{0}; - std::string message_; - std::string sql_state_; + string_type message_; + string_type sql_state_; }; -inline bool operator==(const Error &a, const Error &b) { +template +inline bool operator==(const Error &a, const Error &b) { return (a.error_code() == b.error_code()) && (a.sql_state() == b.sql_state()) && (a.message() == b.message()); } @@ -327,12 +410,18 @@ constexpr inline bool operator==(const ColumnCount &a, const ColumnCount &b) { return (a.count() == b.count()); } +template class ColumnMeta { public: - ColumnMeta(std::string catalog, std::string schema, std::string table, - std::string orig_table, std::string name, std::string orig_name, - uint16_t collation, uint32_t column_length, uint8_t type, - classic_protocol::column_def::value_type flags, uint8_t decimals) + using string_type = + std::conditional_t; + + constexpr ColumnMeta(string_type catalog, string_type schema, + string_type table, string_type orig_table, + string_type name, string_type orig_name, + uint16_t collation, uint32_t column_length, uint8_t type, + classic_protocol::column_def::value_type flags, + uint8_t decimals) : catalog_{std::move(catalog)}, schema_{std::move(schema)}, table_{std::move(table)}, @@ -345,25 +434,27 @@ class ColumnMeta { flags_{flags}, decimals_{decimals} {} - std::string catalog() const { return catalog_; } - std::string schema() const { return schema_; } - std::string table() const { return table_; } - std::string orig_table() const { return orig_table_; } - std::string name() const { return name_; } - std::string orig_name() const { return orig_name_; } - uint16_t collation() const { return collation_; } - uint32_t column_length() const { return column_length_; } - uint8_t type() const { return type_; } - classic_protocol::column_def::value_type flags() const { return flags_; } - uint8_t decimals() const { return decimals_; } + constexpr string_type catalog() const { return catalog_; } + constexpr string_type schema() const { return schema_; } + constexpr string_type table() const { return table_; } + constexpr string_type orig_table() const { return orig_table_; } + constexpr string_type name() const { return name_; } + constexpr string_type orig_name() const { return orig_name_; } + constexpr uint16_t collation() const { return collation_; } + constexpr uint32_t column_length() const { return column_length_; } + constexpr uint8_t type() const { return type_; } + constexpr classic_protocol::column_def::value_type flags() const { + return flags_; + } + constexpr uint8_t decimals() const { return decimals_; } private: - std::string catalog_; - std::string schema_; - std::string table_; - std::string orig_table_; - std::string name_; - std::string orig_name_; + string_type catalog_; + string_type schema_; + string_type table_; + string_type orig_table_; + string_type name_; + string_type orig_name_; uint16_t collation_; uint32_t column_length_; uint8_t type_; @@ -371,7 +462,9 @@ class ColumnMeta { uint8_t decimals_; }; -inline bool operator==(const ColumnMeta &a, const ColumnMeta &b) { +template +inline bool operator==(const ColumnMeta &a, + const ColumnMeta &b) { return (a.catalog() == b.catalog()) && (a.schema() == b.schema()) && (a.table() == b.table()) && (a.orig_table() == b.orig_table()) && (a.name() == b.name()) && (a.orig_name() == b.orig_name()) && @@ -387,9 +480,13 @@ inline bool operator==(const ColumnMeta &a, const ColumnMeta &b) { * * each Field in a row may either be NULL or a std::string. */ +template class Row { public: - using value_type = std::optional; + using string_type = + std::conditional_t; + + using value_type = std::optional; using const_iterator = typename std::vector::const_iterator; Row(std::vector fields) : fields_{std::move(fields)} {} @@ -401,7 +498,8 @@ class Row { std::vector fields_; }; -inline bool operator==(const Row &a, const Row &b) { +template +inline bool operator==(const Row &a, const Row &b) { auto a_iter = a.begin(); const auto a_end = a.end(); auto b_iter = b.begin(); @@ -414,17 +512,21 @@ inline bool operator==(const Row &a, const Row &b) { return true; } +template class ResultSet { public: - ResultSet(std::vector column_metas, std::vector rows) + ResultSet(std::vector> column_metas, + std::vector> rows) : column_metas_{std::move(column_metas)}, rows_{std::move(rows)} {} - std::vector column_metas() const { return column_metas_; } - std::vector rows() const { return rows_; } + std::vector> column_metas() const { + return column_metas_; + } + std::vector> rows() const { return rows_; } private: - std::vector column_metas_; - std::vector rows_; + std::vector> column_metas_; + std::vector> rows_; }; /** @@ -444,20 +546,21 @@ class StmtPrepareOk { * @param with_metadata 0 if no metadata shall be sent for "param_count" and * "column_count". */ - StmtPrepareOk(uint32_t stmt_id, uint16_t column_count, uint16_t param_count, - uint16_t warning_count, uint8_t with_metadata) + constexpr StmtPrepareOk(uint32_t stmt_id, uint16_t column_count, + uint16_t param_count, uint16_t warning_count, + uint8_t with_metadata) : statement_id_{stmt_id}, warning_count_{warning_count}, param_count_{param_count}, column_count_{column_count}, with_metadata_{with_metadata} {} - uint32_t statement_id() const noexcept { return statement_id_; } - uint16_t warning_count() const noexcept { return warning_count_; } + constexpr uint32_t statement_id() const noexcept { return statement_id_; } + constexpr uint16_t warning_count() const noexcept { return warning_count_; } - uint16_t column_count() const { return column_count_; } - uint16_t param_count() const { return param_count_; } - uint8_t with_metadata() const { return with_metadata_; } + constexpr uint16_t column_count() const { return column_count_; } + constexpr uint16_t param_count() const { return param_count_; } + constexpr uint8_t with_metadata() const { return with_metadata_; } private: uint32_t statement_id_; @@ -481,11 +584,16 @@ inline bool operator==(const StmtPrepareOk &a, const StmtPrepareOk &b) { * * needs 'types' to be able to encode a Field of the Row. */ -class StmtRow : public Row { +template +class StmtRow : public Row { public: + using base_ = Row; + + using value_type = typename base_::value_type; + StmtRow(std::vector types, std::vector fields) - : Row{std::move(fields)}, types_{std::move(types)} {} + : base_{std::move(fields)}, types_{std::move(types)} {} std::vector types() const { return types_; } @@ -493,41 +601,53 @@ class StmtRow : public Row { std::vector types_; }; +template class SendFileRequest { public: + using string_type = + std::conditional_t; /** * construct a SendFileRequest message. * * @param filename filename */ - SendFileRequest(std::string filename) : filename_{std::move(filename)} {} + constexpr SendFileRequest(string_type filename) + : filename_{std::move(filename)} {} - std::string filename() const { return filename_; } + constexpr string_type filename() const { return filename_; } private: - std::string filename_; + string_type filename_; }; -inline bool operator==(const SendFileRequest &a, const SendFileRequest &b) { +template +constexpr bool operator==(const SendFileRequest &a, + const SendFileRequest &b) { return a.filename() == b.filename(); } +template class Statistics { public: + using string_type = + std::conditional_t; + /** * construct a Statistics message. * * @param stats statistics */ - Statistics(std::string stats) : stats_{std::move(stats)} {} + constexpr Statistics(string_type stats) : stats_{std::move(stats)} {} - std::string stats() const { return stats_; } + constexpr string_type stats() const { return stats_; } private: - std::string stats_; + string_type stats_; }; -inline bool operator==(const Statistics &a, const Statistics &b) { +template +constexpr bool operator==(const Statistics &a, + const Statistics &b) { return a.stats() == b.stats(); } @@ -535,8 +655,12 @@ inline bool operator==(const Statistics &a, const Statistics &b) { namespace client { +template class Greeting { public: + using string_type = + std::conditional_t; + /** * construct a client::Greeting message. * @@ -549,10 +673,11 @@ class Greeting { * @param auth_method_name auth-method the data is for * @param attributes session-attributes */ - Greeting(classic_protocol::capabilities::value_type capabilities, - uint32_t max_packet_size, uint8_t collation, std::string username, - std::string auth_method_data, std::string schema, - std::string auth_method_name, std::string attributes) + constexpr Greeting(classic_protocol::capabilities::value_type capabilities, + uint32_t max_packet_size, uint8_t collation, + string_type username, string_type auth_method_data, + string_type schema, string_type auth_method_name, + string_type attributes) : capabilities_{capabilities}, max_packet_size_{max_packet_size}, collation_{collation}, @@ -560,30 +685,36 @@ class Greeting { auth_method_data_{std::move(auth_method_data)}, schema_{std::move(schema)}, auth_method_name_{std::move(auth_method_name)}, - attributes_{std::string(attributes)} {} + attributes_{std::move(attributes)} {} - classic_protocol::capabilities::value_type capabilities() const { + constexpr classic_protocol::capabilities::value_type capabilities() const { return capabilities_; } - void capabilities(classic_protocol::capabilities::value_type caps) { + constexpr void capabilities(classic_protocol::capabilities::value_type caps) { capabilities_ = caps; } - uint32_t max_packet_size() const noexcept { return max_packet_size_; } - void max_packet_size(uint32_t sz) noexcept { max_packet_size_ = sz; } + constexpr uint32_t max_packet_size() const noexcept { + return max_packet_size_; + } + constexpr void max_packet_size(uint32_t sz) noexcept { + max_packet_size_ = sz; + } - uint8_t collation() const noexcept { return collation_; } - void collation(uint8_t coll) noexcept { collation_ = coll; } + constexpr uint8_t collation() const noexcept { return collation_; } + constexpr void collation(uint8_t coll) noexcept { collation_ = coll; } - std::string username() const { return username_; } - void username(const std::string &v) { username_ = v; } + constexpr string_type username() const { return username_; } + constexpr void username(const string_type &v) { username_ = v; } - std::string auth_method_data() const { return auth_method_data_; } - void auth_method_data(const std::string &v) { auth_method_data_ = v; } + constexpr string_type auth_method_data() const { return auth_method_data_; } + constexpr void auth_method_data(const string_type &v) { + auth_method_data_ = v; + } - std::string schema() const { return schema_; } - void schema(const std::string &schema) { schema_ = schema; } + constexpr string_type schema() const { return schema_; } + constexpr void schema(const string_type &schema) { schema_ = schema; } /** * name of the auth-method that was explicitly set. @@ -592,27 +723,31 @@ class Greeting { * which may be announced though capability flags (like if * capabilities::plugin_auth wasn't set) */ - std::string auth_method_name() const { return auth_method_name_; } + constexpr string_type auth_method_name() const { return auth_method_name_; } - void auth_method_name(const std::string &name) { auth_method_name_ = name; } + constexpr void auth_method_name(const string_type &name) { + auth_method_name_ = name; + } // [key, value]* in Codec encoding - std::string attributes() const { return attributes_; } + constexpr string_type attributes() const { return attributes_; } - void attributes(const std::string &attrs) { attributes_ = attrs; } + constexpr void attributes(const string_type &attrs) { attributes_ = attrs; } private: classic_protocol::capabilities::value_type capabilities_; uint32_t max_packet_size_; uint8_t collation_; - std::string username_; - std::string auth_method_data_; - std::string schema_; - std::string auth_method_name_; - std::string attributes_; + string_type username_; + string_type auth_method_data_; + string_type schema_; + string_type auth_method_name_; + string_type attributes_; }; -inline bool operator==(const Greeting &a, const Greeting &b) { +template +constexpr bool operator==(const Greeting &a, + const Greeting &b) { return (a.capabilities() == b.capabilities()) && (a.max_packet_size() == b.max_packet_size()) && (a.collation() == b.collation()) && (a.username() == b.username()) && @@ -622,27 +757,36 @@ inline bool operator==(const Greeting &a, const Greeting &b) { (a.attributes() == b.attributes()); } +template class Query { public: + using string_type = + std::conditional_t; + /** * construct a Query message. * * @param statement statement to prepare */ - Query(std::string statement) : statement_{std::move(statement)} {} + constexpr Query(string_type statement) : statement_{std::move(statement)} {} - std::string statement() const { return statement_; } + constexpr string_type statement() const { return statement_; } private: - std::string statement_; + string_type statement_; }; -inline bool operator==(const Query &a, const Query &b) { +template +constexpr bool operator==(const Query &a, const Query &b) { return a.statement() == b.statement(); } +template class ListFields { public: + using string_type = + std::conditional_t; + /** * list columns of a table. * @@ -657,42 +801,53 @@ class ListFields { * @param table_name name of table to list * @param wildcard wildcard */ - ListFields(std::string table_name, std::string wildcard) + constexpr ListFields(string_type table_name, string_type wildcard) : table_name_{std::move(table_name)}, wildcard_{std::move(wildcard)} {} - std::string table_name() const { return table_name_; } - std::string wildcard() const { return wildcard_; } + constexpr string_type table_name() const { return table_name_; } + constexpr string_type wildcard() const { return wildcard_; } private: - std::string table_name_; - std::string wildcard_; + string_type table_name_; + string_type wildcard_; }; -inline bool operator==(const ListFields &a, const ListFields &b) { +template +constexpr bool operator==(const ListFields &a, + const ListFields &b) { return a.table_name() == b.table_name() && a.wildcard() == b.wildcard(); } +template class InitSchema { public: + using string_type = + std::conditional_t; + /** * construct a InitSchema message. * * @param schema schema to change to */ - InitSchema(std::string schema) : schema_{std::move(schema)} {} + constexpr InitSchema(string_type schema) : schema_{std::move(schema)} {} - std::string schema() const { return schema_; } + constexpr string_type schema() const { return schema_; } private: - std::string schema_; + string_type schema_; }; -inline bool operator==(const InitSchema &a, const InitSchema &b) { +template +constexpr bool operator==(const InitSchema &a, + const InitSchema &b) { return a.schema() == b.schema(); } +template class ChangeUser { public: + using string_type = + std::conditional_t; /** * construct a ChangeUser message. * @@ -703,9 +858,9 @@ class ChangeUser { * @param collation collation * @param attributes session-attributes */ - ChangeUser(std::string username, std::string auth_method_data, - std::string schema, uint16_t collation, - std::string auth_method_name, std::string attributes) + constexpr ChangeUser(string_type username, string_type auth_method_data, + string_type schema, uint16_t collation, + string_type auth_method_name, string_type attributes) : username_{std::move(username)}, auth_method_data_{std::move(auth_method_data)}, schema_{std::move(schema)}, @@ -713,25 +868,27 @@ class ChangeUser { auth_method_name_{std::move(auth_method_name)}, attributes_{std::move(attributes)} {} - uint8_t collation() const noexcept { return collation_; } - std::string username() const { return username_; } - std::string auth_method_data() const { return auth_method_data_; } - std::string schema() const { return schema_; } - std::string auth_method_name() const { return auth_method_name_; } + constexpr uint8_t collation() const noexcept { return collation_; } + constexpr string_type username() const { return username_; } + constexpr string_type auth_method_data() const { return auth_method_data_; } + constexpr string_type schema() const { return schema_; } + constexpr string_type auth_method_name() const { return auth_method_name_; } // [key, value]* in Codec encoding - std::string attributes() const { return attributes_; } + constexpr string_type attributes() const { return attributes_; } private: - std::string username_; - std::string auth_method_data_; - std::string schema_; + string_type username_; + string_type auth_method_data_; + string_type schema_; uint16_t collation_; - std::string auth_method_name_; - std::string attributes_; + string_type auth_method_name_; + string_type attributes_; }; -inline bool operator==(const ChangeUser &a, const ChangeUser &b) { +template +constexpr bool operator==(const ChangeUser &a, + const ChangeUser &b) { return (a.collation() == b.collation()) && (a.username() == b.username()) && (a.auth_method_data() == b.auth_method_data()) && (a.schema() == b.schema()) && @@ -760,9 +917,12 @@ class Reload { * * @param cmds what to reload */ - Reload(classic_protocol::reload_cmds::value_type cmds) : cmds_{cmds} {} + constexpr Reload(classic_protocol::reload_cmds::value_type cmds) + : cmds_{cmds} {} - classic_protocol::reload_cmds::value_type cmds() const { return cmds_; } + constexpr classic_protocol::reload_cmds::value_type cmds() const { + return cmds_; + } private: classic_protocol::reload_cmds::value_type cmds_; @@ -791,49 +951,65 @@ constexpr bool operator==(const Kill &a, const Kill &b) { return a.connection_id() == b.connection_id(); } +template class SendFile { public: + using string_type = + std::conditional_t; + /** * construct a SendFile message. * * @param payload payload */ - SendFile(std::string payload) : payload_{std::move(payload)} {} + constexpr SendFile(string_type payload) : payload_{std::move(payload)} {} - std::string payload() const { return payload_; } + constexpr string_type payload() const { return payload_; } private: - std::string payload_; + string_type payload_; }; -inline bool operator==(const SendFile &a, const SendFile &b) { +template +inline bool operator==(const SendFile &a, + const SendFile &b) { return a.payload() == b.payload(); } +template class StmtPrepare { public: + using string_type = + std::conditional_t; /** * construct a PrepareStmt message. * * @param statement statement to prepare */ - StmtPrepare(std::string statement) : statement_{std::move(statement)} {} + constexpr StmtPrepare(string_type statement) + : statement_{std::move(statement)} {} - std::string statement() const { return statement_; } + constexpr string_type statement() const { return statement_; } private: - std::string statement_; + string_type statement_; }; -inline bool operator==(const StmtPrepare &a, const StmtPrepare &b) { +template +inline bool operator==(const StmtPrepare &a, + const StmtPrepare &b) { return a.statement() == b.statement(); } /** * append data to a parameter of a prepared statement. */ +template class StmtParamAppendData { public: + using string_type = + std::conditional_t; + /** * construct an append-data-to-parameter message. * @@ -841,24 +1017,25 @@ class StmtParamAppendData { * @param param_id parameter-id to append data to * @param data data to append to param_id of statement_id */ - StmtParamAppendData(uint32_t statement_id, uint16_t param_id, - std::string data) + constexpr StmtParamAppendData(uint32_t statement_id, uint16_t param_id, + string_type data) : statement_id_{statement_id}, param_id_{param_id}, data_{std::move(data)} {} - uint32_t statement_id() const { return statement_id_; } - uint16_t param_id() const { return param_id_; } - std::string data() const { return data_; } + constexpr uint32_t statement_id() const { return statement_id_; } + constexpr uint16_t param_id() const { return param_id_; } + constexpr string_type data() const { return data_; } private: uint32_t statement_id_; uint16_t param_id_; - std::string data_; + string_type data_; }; -inline bool operator==(const StmtParamAppendData &a, - const StmtParamAppendData &b) { +template +inline bool operator==(const StmtParamAppendData &a, + const StmtParamAppendData &b) { return a.statement_id() == b.statement_id() && a.param_id() == b.param_id() && a.data() == b.data(); } @@ -868,8 +1045,12 @@ inline bool operator==(const StmtParamAppendData &a, * * 'values' raw bytes as encoded by the binary codec */ +template class StmtExecute { public: + using string_type = + std::conditional_t; + using value_type = std::optional; /** @@ -911,7 +1092,9 @@ class StmtExecute { std::vector values_; }; -inline bool operator==(const StmtExecute &a, const StmtExecute &b) { +template +inline bool operator==(const StmtExecute &a, + const StmtExecute &b) { return a.statement_id() == b.statement_id() && a.flags() == b.flags() && a.iteration_count() == b.iteration_count() && a.new_params_bound() == b.new_params_bound() && @@ -1020,23 +1203,29 @@ class Ping {}; constexpr bool operator==(const Ping &, const Ping &) { return true; } +template class AuthMethodData { public: + using string_type = + std::conditional_t; + /** * send data for the current auth-method to server. * * @param auth_method_data data of the auth-method */ - AuthMethodData(std::string auth_method_data) + constexpr AuthMethodData(string_type auth_method_data) : auth_method_data_{std::move(auth_method_data)} {} - std::string auth_method_data() const { return auth_method_data_; } + constexpr string_type auth_method_data() const { return auth_method_data_; } private: - std::string auth_method_data_; + string_type auth_method_data_; }; -inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { +template +constexpr bool operator==(const AuthMethodData &a, + const AuthMethodData &b) { return a.auth_method_data() == b.auth_method_data(); } @@ -1047,104 +1236,77 @@ inline bool operator==(const AuthMethodData &a, const AuthMethodData &b) { // // no content class Clone {}; -} // namespace client -} // namespace message -} // namespace classic_protocol -namespace classic_protocol::message::client::impl { +template class BinlogDump { public: - // flags of message::client::BinlogDump - enum class Flags : uint16_t { - non_blocking = 1 << 0, - }; -}; -} // namespace classic_protocol::message::client::impl - -namespace stdx { -// enable flag-ops for BinlogDump::Flags -template <> -struct is_flags - : std::true_type {}; -} // namespace stdx + using string_type = + std::conditional_t; -namespace classic_protocol::message::client { -class BinlogDump { - public: - using Flags = typename impl::BinlogDump::Flags; + using Flags = + typename classic_protocol::message::client::impl::BinlogDump::Flags; - BinlogDump(stdx::flags flags, uint32_t server_id, std::string filename, - uint32_t position) + constexpr BinlogDump(stdx::flags flags, uint32_t server_id, + string_type filename, uint32_t position) : position_{position}, flags_{flags}, server_id_{server_id}, filename_{std::move(filename)} {} - [[nodiscard]] stdx::flags flags() const { return flags_; } - [[nodiscard]] uint32_t server_id() const { return server_id_; } - [[nodiscard]] std::string filename() const { return filename_; } - [[nodiscard]] uint64_t position() const { return position_; } + [[nodiscard]] constexpr stdx::flags flags() const { return flags_; } + [[nodiscard]] constexpr uint32_t server_id() const { return server_id_; } + [[nodiscard]] constexpr string_type filename() const { return filename_; } + [[nodiscard]] constexpr uint64_t position() const { return position_; } private: uint32_t position_; stdx::flags flags_; uint32_t server_id_; - std::string filename_; + string_type filename_; }; -} // namespace classic_protocol::message::client -namespace classic_protocol::message::client::impl { +template class BinlogDumpGtid { public: - // flags of message::client::BinlogDumpGtid - enum class Flags : uint16_t { - non_blocking = 1 << 0, - through_position = 1 << 1, - through_gtid = 1 << 2, - }; -}; -} // namespace classic_protocol::message::client::impl + using string_type = + std::conditional_t; -namespace stdx { -// enable flag-ops for BinlogDumpGtid::Flags -template <> -struct is_flags - : std::true_type {}; -} // namespace stdx - -namespace classic_protocol::message::client { - -class BinlogDumpGtid { - public: - using Flags = typename impl::BinlogDumpGtid::Flags; + using Flags = + typename classic_protocol::message::client::impl::BinlogDumpGtid::Flags; - BinlogDumpGtid(stdx::flags flags, uint32_t server_id, - std::string filename, uint64_t position, std::string sids) + constexpr BinlogDumpGtid(stdx::flags flags, uint32_t server_id, + string_type filename, uint64_t position, + string_type sids) : flags_{flags}, server_id_{server_id}, filename_{std::move(filename)}, position_{position}, sids_{std::move(sids)} {} - [[nodiscard]] stdx::flags flags() const { return flags_; } - [[nodiscard]] uint32_t server_id() const { return server_id_; } - [[nodiscard]] std::string filename() const { return filename_; } - [[nodiscard]] uint64_t position() const { return position_; } - [[nodiscard]] std::string sids() const { return sids_; } + [[nodiscard]] constexpr stdx::flags flags() const { return flags_; } + [[nodiscard]] constexpr uint32_t server_id() const { return server_id_; } + [[nodiscard]] constexpr string_type filename() const { return filename_; } + [[nodiscard]] constexpr uint64_t position() const { return position_; } + [[nodiscard]] constexpr string_type sids() const { return sids_; } private: stdx::flags flags_; uint32_t server_id_; - std::string filename_; + string_type filename_; uint64_t position_; - std::string sids_; + string_type sids_; }; +template class RegisterReplica { public: - RegisterReplica(uint32_t server_id, std::string hostname, - std::string username, std::string password, uint16_t port, - uint32_t replication_rank, uint32_t master_id) + using string_type = + std::conditional_t; + + constexpr RegisterReplica(uint32_t server_id, string_type hostname, + string_type username, string_type password, + uint16_t port, uint32_t replication_rank, + uint32_t master_id) : server_id_{server_id}, hostname_{std::move(hostname)}, username_{std::move(username)}, @@ -1153,24 +1315,124 @@ class RegisterReplica { replication_rank_{replication_rank}, master_id_{master_id} {} - [[nodiscard]] uint32_t server_id() const { return server_id_; } - [[nodiscard]] std::string hostname() const { return hostname_; } - [[nodiscard]] std::string username() const { return username_; } - [[nodiscard]] std::string password() const { return password_; } - [[nodiscard]] uint16_t port() const { return port_; } - [[nodiscard]] uint32_t replication_rank() const { return replication_rank_; } - [[nodiscard]] uint32_t master_id() const { return master_id_; } + [[nodiscard]] constexpr uint32_t server_id() const { return server_id_; } + [[nodiscard]] constexpr string_type hostname() const { return hostname_; } + [[nodiscard]] constexpr string_type username() const { return username_; } + [[nodiscard]] constexpr string_type password() const { return password_; } + [[nodiscard]] constexpr uint16_t port() const { return port_; } + [[nodiscard]] constexpr uint32_t replication_rank() const { + return replication_rank_; + } + [[nodiscard]] constexpr uint32_t master_id() const { return master_id_; } private: uint32_t server_id_; - std::string hostname_; - std::string username_; - std::string password_; + string_type hostname_; + string_type username_; + string_type password_; uint16_t port_; uint32_t replication_rank_; uint32_t master_id_; }; -} // namespace classic_protocol::message::client +} // namespace client +} // namespace message +} // namespace borrowable + +namespace message { +namespace server { +using Ok = borrowable::message::server::Ok; +using Error = borrowable::message::server::Error; +using Eof = borrowable::message::server::Eof; +using Greeting = borrowable::message::server::Greeting; +using ColumnCount = borrowable::message::server::ColumnCount; +using ColumnMeta = borrowable::message::server::ColumnMeta; +using AuthMethodSwitch = borrowable::message::server::AuthMethodSwitch; +using AuthMethodData = borrowable::message::server::AuthMethodData; +using SendFileRequest = borrowable::message::server::SendFileRequest; +using Row = borrowable::message::server::Row; +using StmtRow = borrowable::message::server::StmtRow; +using StmtPrepareOk = borrowable::message::server::StmtPrepareOk; +using Statistics = borrowable::message::server::Statistics; +} // namespace server + +namespace client { +using Greeting = borrowable::message::client::Greeting; +using AuthMethodData = borrowable::message::client::AuthMethodData; +using InitSchema = borrowable::message::client::InitSchema; +using ListFields = borrowable::message::client::ListFields; +using Query = borrowable::message::client::Query; +using RegisterReplica = borrowable::message::client::RegisterReplica; +using Ping = borrowable::message::client::Ping; +using Kill = borrowable::message::client::Kill; +using ChangeUser = borrowable::message::client::ChangeUser; +using Reload = borrowable::message::client::Reload; +using ResetConnection = borrowable::message::client::ResetConnection; +using Quit = borrowable::message::client::Quit; +using StmtPrepare = borrowable::message::client::StmtPrepare; +using StmtExecute = borrowable::message::client::StmtExecute; +using StmtReset = borrowable::message::client::StmtReset; +using StmtClose = borrowable::message::client::StmtClose; +using StmtParamAppendData = + borrowable::message::client::StmtParamAppendData; +using SetOption = borrowable::message::client::SetOption; +using StmtFetch = borrowable::message::client::StmtFetch; +using Statistics = borrowable::message::client::Statistics; +using SendFile = borrowable::message::client::SendFile; +using Clone = borrowable::message::client::Clone; +using BinlogDump = borrowable::message::client::BinlogDump; +using BinlogDumpGtid = borrowable::message::client::BinlogDumpGtid; +} // namespace client +} // namespace message + +namespace borrowed { +namespace message { +namespace server { +using Ok = borrowable::message::server::Ok; +using Error = borrowable::message::server::Error; +using Eof = borrowable::message::server::Eof; +using Greeting = borrowable::message::server::Greeting; +using ColumnCount = borrowable::message::server::ColumnCount; +using ColumnMeta = borrowable::message::server::ColumnMeta; +using AuthMethodSwitch = borrowable::message::server::AuthMethodSwitch; +using AuthMethodData = borrowable::message::server::AuthMethodData; +using SendFileRequest = borrowable::message::server::SendFileRequest; +using Row = borrowable::message::server::Row; +using StmtRow = borrowable::message::server::StmtRow; +using StmtPrepareOk = borrowable::message::server::StmtPrepareOk; +using Statistics = borrowable::message::server::Statistics; +} // namespace server + +namespace client { +using Greeting = borrowable::message::client::Greeting; +using AuthMethodData = borrowable::message::client::AuthMethodData; +using Query = borrowable::message::client::Query; +using InitSchema = borrowable::message::client::InitSchema; +using ListFields = borrowable::message::client::ListFields; +using RegisterReplica = borrowable::message::client::RegisterReplica; +using Ping = borrowable::message::client::Ping; +using Kill = borrowable::message::client::Kill; +using ChangeUser = borrowable::message::client::ChangeUser; +using Reload = borrowable::message::client::Reload; +using ResetConnection = borrowable::message::client::ResetConnection; +using Quit = borrowable::message::client::Quit; +using StmtPrepare = borrowable::message::client::StmtPrepare; +using StmtExecute = borrowable::message::client::StmtExecute; +using StmtReset = borrowable::message::client::StmtReset; +using StmtClose = borrowable::message::client::StmtClose; +using StmtParamAppendData = + borrowable::message::client::StmtParamAppendData; +using SetOption = borrowable::message::client::SetOption; +using StmtFetch = borrowable::message::client::StmtFetch; +using Statistics = borrowable::message::client::Statistics; +using SendFile = borrowable::message::client::SendFile; +using Clone = borrowable::message::client::Clone; +using BinlogDump = borrowable::message::client::BinlogDump; +using BinlogDumpGtid = borrowable::message::client::BinlogDumpGtid; +} // namespace client +} // namespace message +} // namespace borrowed + +} // namespace classic_protocol #endif diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 942d416..a0d7444 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -31,6 +31,7 @@ namespace classic_protocol { +namespace borrowable { namespace session_track { /** @@ -38,19 +39,25 @@ namespace session_track { * * used in server::Ok and server::Eof */ +template class Field { public: - Field(uint8_t type, std::string data) : type_{type}, data_{std::move(data)} {} + using string_type = + std::conditional_t; - uint8_t type() const noexcept { return type_; } - std::string data() const noexcept { return data_; } + constexpr Field(uint8_t type, string_type data) + : type_{type}, data_{std::move(data)} {} + + constexpr uint8_t type() const noexcept { return type_; } + constexpr string_type data() const noexcept { return data_; } private: uint8_t type_; - std::string data_; + string_type data_; }; -inline bool operator==(const Field &a, const Field &b) { +template +inline bool operator==(const Field &a, const Field &b) { return (a.type() == b.type()) && (a.data() == b.data()); } @@ -59,20 +66,25 @@ inline bool operator==(const Field &a, const Field &b) { * * see: session_track_system_variable */ +template class SystemVariable { public: - SystemVariable(std::string key, std::string value) + using string_type = + std::conditional_t; + constexpr SystemVariable(string_type key, string_type value) : key_{std::move(key)}, value_{std::move(value)} {} - std::string key() const noexcept { return key_; } - std::string value() const noexcept { return value_; } + constexpr string_type key() const noexcept { return key_; } + constexpr string_type value() const noexcept { return value_; } private: - std::string key_; - std::string value_; + string_type key_; + string_type value_; }; -inline bool operator==(const SystemVariable &a, const SystemVariable &b) { +template +inline bool operator==(const SystemVariable &a, + const SystemVariable &b) { return (a.key() == b.key()) && (a.value() == b.value()); } @@ -81,17 +93,22 @@ inline bool operator==(const SystemVariable &a, const SystemVariable &b) { * * see: session_track_schema */ +template class Schema { public: - Schema(std::string schema) : schema_{std::move(schema)} {} + using string_type = + std::conditional_t; + + constexpr Schema(string_type schema) : schema_{std::move(schema)} {} - std::string schema() const noexcept { return schema_; } + constexpr string_type schema() const noexcept { return schema_; } private: - std::string schema_; + string_type schema_; }; -inline bool operator==(const Schema &a, const Schema &b) { +template +inline bool operator==(const Schema &a, const Schema &b) { return (a.schema() == b.schema()); } @@ -123,19 +140,24 @@ constexpr inline bool operator==(const State &a, const State &b) { * * see: session_track_gtid */ +template class Gtid { public: - Gtid(uint8_t spec, std::string gtid) : spec_{spec}, gtid_{std::move(gtid)} {} + using string_type = + std::conditional_t; + constexpr Gtid(uint8_t spec, string_type gtid) + : spec_{spec}, gtid_{std::move(gtid)} {} - uint8_t spec() const noexcept { return spec_; } - std::string gtid() const { return gtid_; } + constexpr uint8_t spec() const noexcept { return spec_; } + constexpr string_type gtid() const { return gtid_; } private: uint8_t spec_; - std::string gtid_; + string_type gtid_; }; -inline bool operator==(const Gtid &a, const Gtid &b) { +template +inline bool operator==(const Gtid &a, const Gtid &b) { return (a.spec() == b.spec()) && (a.gtid() == b.gtid()); } @@ -221,23 +243,53 @@ inline bool operator==(const TransactionState &a, const TransactionState &b) { * * see: session_track_transaction_info */ +template class TransactionCharacteristics { public: - TransactionCharacteristics(std::string characteristics) + using string_type = + std::conditional_t; + + constexpr TransactionCharacteristics(string_type characteristics) : characteristics_{std::move(characteristics)} {} - std::string characteristics() const { return characteristics_; } + constexpr string_type characteristics() const { return characteristics_; } private: - std::string characteristics_; + string_type characteristics_; }; -inline bool operator==(const TransactionCharacteristics &a, - const TransactionCharacteristics &b) { +template +inline bool operator==(const TransactionCharacteristics &a, + const TransactionCharacteristics &b) { return (a.characteristics() == b.characteristics()); } +} // namespace session_track +} // namespace borrowable +namespace borrowed { +namespace session_track { +using Field = borrowable::session_track::Field; +using TransactionCharacteristics = + borrowable::session_track::TransactionCharacteristics; +using TransactionState = borrowable::session_track::TransactionState; +using SystemVariable = borrowable::session_track::SystemVariable; +using Schema = borrowable::session_track::Schema; +using State = borrowable::session_track::State; +using Gtid = borrowable::session_track::Gtid; } // namespace session_track +} // namespace borrowed + +namespace session_track { +using Field = borrowable::session_track::Field; +using TransactionCharacteristics = + borrowable::session_track::TransactionCharacteristics; +using TransactionState = borrowable::session_track::TransactionState; +using SystemVariable = borrowable::session_track::SystemVariable; +using Schema = borrowable::session_track::Schema; +using State = borrowable::session_track::State; +using Gtid = borrowable::session_track::Gtid; +} // namespace session_track + } // namespace classic_protocol #endif diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index e4dc509..1578abf 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -30,7 +30,42 @@ namespace classic_protocol { +namespace borrowable { namespace wire { + +template +class String { + public: + using value_type = + std::conditional_t; + + constexpr String() = default; + constexpr String(value_type str) : str_{std::move(str)} {} + + constexpr value_type value() const { return str_; } + + private: + value_type str_; +}; + +template +inline bool operator==(const String &lhs, + const String &rhs) { + return lhs.value() == rhs.value(); +} + +template +class NulTermString : public String { + public: + using String::String; +}; + +template +class VarString : public String { + public: + using String::String; +}; + // basic POD types of the mysql classic-protocol's wire encoding: // // - fixed size integers @@ -96,34 +131,36 @@ class FixedInt<8> : public BasicInt { using BasicInt::BasicInt; }; -class String { - public: - String() = default; - String(std::string str) : str_{std::move(str)} {} - - std::string value() const { return str_; } +class Null {}; +} // namespace wire +} // namespace borrowable - private: - std::string str_; -}; +namespace borrowed { +namespace wire { -inline bool operator==(const String &lhs, const String &rhs) { - return lhs.value() == rhs.value(); -} +using String = borrowable::wire::String; +using NulTermString = borrowable::wire::NulTermString; +using VarString = borrowable::wire::VarString; +using Null = borrowable::wire::Null; +template +using FixedInt = borrowable::wire::FixedInt; +using VarInt = borrowable::wire::VarInt; +} // namespace wire +} // namespace borrowed -class NulTermString : public String { - public: - using String::String; -}; +namespace wire { -class VarString : public String { - public: - using String::String; -}; +using String = borrowable::wire::String; +using NulTermString = borrowable::wire::NulTermString; +using VarString = borrowable::wire::VarString; +using Null = borrowable::wire::Null; -class Null {}; +template +using FixedInt = borrowable::wire::FixedInt; +using VarInt = borrowable::wire::VarInt; } // namespace wire + } // namespace classic_protocol #endif From ac44d56c1596d83a44c5da15939192cb6fda082f Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 10 Feb 2023 13:29:27 +0100 Subject: [PATCH 57/88] Bug#35072489 message::server::Error has no setters classic_protocol::message::server::Error has no setter methods for its fields. Change ------ - added .message(msg), .error_code(code) and .sql_state(state) - added unit-tests Change-Id: I4fd83eaf2c95212a1b912bd97bae36f94f456735 --- mysqlrouter/classic_protocol_message.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index c168f1e..39ece6e 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -373,8 +373,11 @@ class Error { sql_state_{std::move(sql_state)} {} constexpr uint16_t error_code() const noexcept { return error_code_; } + constexpr void error_code(uint16_t code) { error_code_ = code; } constexpr string_type sql_state() const { return sql_state_; } + constexpr void sql_state(const string_type &state) { sql_state_ = state; } constexpr string_type message() const { return message_; } + constexpr void message(const string_type &msg) { message_ = msg; } private: uint16_t error_code_{0}; From 4022ddf680efce750e47d2241cc0b820cd4e2a80 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 16 Feb 2023 21:12:27 +0100 Subject: [PATCH 58/88] Bug#35097388 short Error packet does not fail with Bad Message wire::String() takes a string until the end of the buffer. To get string with a given length, the decoder's .step() can provided a 'size': .step(5); Decoding a Error-packet which a too short SQL-state suceeded, but should actually fail. Change ------ - fail decoding a step<>(sz) doesn't have enough data. Change-Id: I5f34595459d28c7ce9720f8809fb516435c58fe9 --- mysqlrouter/classic_protocol_codec_base.h | 72 +++++++++++++---------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 342e627..e536b4c 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -25,8 +25,9 @@ #ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_BASE_H_ #define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_BASE_H_ -#include // size_t -#include // uint8_t +#include // size_t +#include // uint8_t +#include #include // error_code #include #include // move @@ -34,6 +35,7 @@ #include "mysql/harness/net_ts/buffer.h" #include "mysql/harness/stdx/bit.h" #include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_constants.h" namespace classic_protocol { @@ -62,7 +64,6 @@ static_assert(bytes_per_bits(9) == 2); * requirements for T: * - size_t size() * - stdx::expected encode(net::mutable_buffer); - * - static size_t max_size(); * - static stdx::expected decode(buffer_sequence, * capabilities); */ @@ -144,14 +145,29 @@ class DecodeBufferAccumulator { /** * decode a Type from the buffer sequence. * - * if it succeeds, moves position in buffer-sequence forward and returns + * if it succeeds, moves position in buffer forward and returns * decoded Type, otherwise returns error and updates the global error-code in * result() + * + * 'sz' is unlimited, the whole rest of the current buffer + * is passed to the decoder. + * + * If not, a slice of size 'sz' is taken. If there isn't at least 'sz' bytes + * in the buffer, it fails. + * + * @param sz limits the size of the current buffer. */ template stdx::expected::value_type, std::error_code> step( - size_t max_size = classic_protocol::Codec::max_size()) { - return step_(max_size); + size_t sz = std::numeric_limits::max()) { + if (!res_) return stdx::make_unexpected(res_.error()); + + auto step_res = step_(sz); + + // capture the first failure + if (!step_res) res_ = stdx::make_unexpected(step_res.error()); + + return step_res; } /** @@ -163,8 +179,10 @@ class DecodeBufferAccumulator { */ template stdx::expected::value_type, std::error_code> try_step( - size_t max_size = classic_protocol::Codec::max_size()) { - return step_(max_size); + size_t sz = std::numeric_limits::max()) { + if (!res_) return stdx::make_unexpected(res_.error()); + + return step_(sz); } /** @@ -173,7 +191,6 @@ class DecodeBufferAccumulator { * if a step() failed, result is the error-code of the first failed step() * * @returns consumed bytes by all steps(), or error of first failed step() - * */ result_type result() const { if (!res_) return res_; @@ -182,31 +199,24 @@ class DecodeBufferAccumulator { } private: - /** - * execute a step. - * - * Note: 'Try' is used a compile time selector for step() and try_step() only. - */ - template + template stdx::expected::value_type, std::error_code> step_( - size_t max_size = std::numeric_limits::max()) { - if (!res_) return stdx::make_unexpected(res_.error()); - - stdx::expected::value_type>, - std::error_code> - decode_res = - Codec::decode(net::buffer(buffer_ + consumed_, max_size), caps_); - if (decode_res) { - consumed_ += decode_res->first; - - return decode_res->second; + size_t sz) { + auto buf = buffer_ + consumed_; + + if (sz != std::numeric_limits::max()) { + // not enough data. + if (buf.size() < sz) { + return stdx::make_unexpected( + make_error_code(codec_errc::not_enough_input)); + } } - if (!Try) { - // capture the first failure - res_ = stdx::make_unexpected(decode_res.error()); - } - return stdx::make_unexpected(decode_res.error()); + auto decode_res = Codec::decode(net::buffer(buf, sz), caps_); + if (!decode_res) return stdx::make_unexpected(decode_res.error()); + + consumed_ += decode_res->first; + return decode_res->second; } net::const_buffer buffer_; From 1cb829de1e231ab5a55acc847daab53b70f04bcb Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 16 Feb 2023 22:18:25 +0100 Subject: [PATCH 59/88] Bug#35097429 compile error with borrowed StmtExecute Codec::decode() fails to compile as construction of std::string from string-view fails. Change ------ - use std::string_view for strings if the message is borrowed. Change-Id: I55ed20bb0c84a9571e4caff183189178912cb10d --- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 0acd70b..8f55018 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2106,7 +2106,7 @@ class Codec> if (!accu.result()) return stdx::make_unexpected(accu.result().error()); std::vector types; - std::vector> values; + std::vector> values; if (new_params_bound_res->value()) { const auto nullbits = nullbits_res->value(); diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 39ece6e..3a6689c 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1054,7 +1054,7 @@ class StmtExecute { using string_type = std::conditional_t; - using value_type = std::optional; + using value_type = std::optional; /** * construct a ExecuteStmt message. From d85cabcbc14fb151a06640dfc051f29f19cd2184 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 17 Feb 2023 10:02:32 +0100 Subject: [PATCH 60/88] Bug#35097439 stmt-execute with new params bound = 0 encode The StmtExecute message has short variant which allows to skip the parameter-definitions. The decoding of new-params-bound == 0, and encoding it again leads to different messages. Change ------ - fail decoding if new_params_bound is != 1 Change-Id: I9bdbc2413c10c602439602012685ad68532c548b --- mysqlrouter/classic_protocol_codec_message.h | 291 ++++++++++--------- 1 file changed, 154 insertions(+), 137 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 8f55018..e4a9fae 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -31,6 +31,7 @@ #include // move #include "mysql/harness/stdx/expected.h" +#include "mysql/harness/stdx/ranges.h" #include "mysqlrouter/classic_protocol_codec_base.h" #include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_codec_wire.h" @@ -1329,10 +1330,13 @@ class Codec> case field_type::Tiny: field_size_res = 1; break; - default: - return stdx::make_unexpected( - make_error_code(codec_errc::field_type_unknown)); } + + if (!field_size_res) { + return stdx::make_unexpected( + make_error_code(codec_errc::field_type_unknown)); + } + const auto value_res = accu.template step>(field_size_res.value()); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -1926,81 +1930,80 @@ class Codec> .step(bw::FixedInt<4>(v_.iteration_count())); // values.size() and types.size() MUST be the same - if (!v_.values().empty()) { - // mark all that are NULL in the nullbits - // - // - one bit per parameter to send - // - if a parameter is NULL, the bit is set, and later no value is added. - std::vector nullbits(bytes_per_bits(v_.values().size())); - - { - size_t byte_pos{}, bit_pos{}; - for (auto const &v : v_.values()) { - if (bit_pos > 7) { - bit_pos = 0; - ++byte_pos; - } + if (v_.values().empty()) return accu.result(); - if (!v) { - nullbits[byte_pos] |= 1 << bit_pos; - } + // mark all that are NULL in the nullbits + // + // - one bit per parameter to send + // - if a parameter is NULL, the bit is set, and later no value is added. + std::vector nullbits(bytes_per_bits(v_.values().size())); - ++bit_pos; + { + size_t byte_pos{}, bit_pos{}; + for (auto const &v : v_.values()) { + if (bit_pos > 7) { + bit_pos = 0; + ++byte_pos; } - } - accu.step(bw::String(typename bw::String::value_type( - reinterpret_cast(nullbits.data()), - nullbits.size()))) - .step(bw::FixedInt<1>(v_.new_params_bound())); - if (v_.new_params_bound()) { - for (const auto &t : v_.types()) { - accu.step(bw::FixedInt<2>(t)); - } - size_t n{}; - for (const auto &v : v_.values()) { - // add all the values that aren't NULL - if (v.has_value()) { - // write length of the type is a variable length - switch (v_.types()[n++]) { - case field_type::Bit: - case field_type::Blob: - case field_type::Varchar: - case field_type::VarString: - case field_type::Set: - case field_type::String: - case field_type::Enum: - case field_type::TinyBlob: - case field_type::MediumBlob: - case field_type::LongBlob: - case field_type::Decimal: - case field_type::NewDecimal: - case field_type::Geometry: - accu.step(bw::VarInt(v->size())); - break; - case field_type::Date: - case field_type::DateTime: - case field_type::Timestamp: - case field_type::Time: - accu.step(bw::FixedInt<1>(v->size())); - break; - case field_type::LongLong: - case field_type::Double: - case field_type::Long: - case field_type::Int24: - case field_type::Float: - case field_type::Short: - case field_type::Year: - case field_type::Tiny: - // fixed size - break; - } - accu.step(bw::String(v.value())); - } + if (!v) { + nullbits[byte_pos] |= 1 << bit_pos; } + + ++bit_pos; + } + } + + accu + .step(bw::String(typename bw::String::value_type( + reinterpret_cast(nullbits.data()), nullbits.size()))) + .step(bw::FixedInt<1>(v_.new_params_bound())); + if (v_.new_params_bound()) { + for (const auto &t : v_.types()) { + accu.step(bw::FixedInt<2>(t)); } } + for (auto [n, v] : stdx::views::enumerate(v_.values())) { + // add all the values that aren't NULL + if (!v.has_value()) continue; + // write length of the type is a variable length + switch (v_.types()[n] & 0xff) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: + accu.step(bw::VarInt(v->size())); + break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: + accu.step(bw::FixedInt<1>(v->size())); + break; + case field_type::LongLong: + case field_type::Double: + case field_type::Long: + case field_type::Int24: + case field_type::Float: + case field_type::Short: + case field_type::Year: + case field_type::Tiny: + // fixed size + break; + } + accu.step(bw::String(v.value())); + } + return accu.result(); } @@ -2105,14 +2108,18 @@ class Codec> auto new_params_bound_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + auto new_params_bound = new_params_bound_res->value(); + if (new_params_bound != 1) { + // new-params-bound is required as long as the decoder doesn't know the + // old param-defs + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + std::vector types; std::vector> values; - if (new_params_bound_res->value()) { - const auto nullbits = nullbits_res->value(); - + if (new_params_bound == 1) { types.reserve(param_count); - values.reserve(param_count); for (size_t n{}; n < param_count; ++n) { auto type_res = accu.template step>(); @@ -2120,73 +2127,83 @@ class Codec> types.push_back(type_res->value()); } - for (size_t n{}, bit_pos{}, byte_pos{}; n < param_count; ++n, ++bit_pos) { - if (bit_pos > 7) { - bit_pos = 0; - ++byte_pos; + } + + const auto nullbits = nullbits_res->value(); + values.reserve(param_count); + + for (size_t n{}, bit_pos{}, byte_pos{}; n < param_count; ++n, ++bit_pos) { + if (bit_pos > 7) { + bit_pos = 0; + ++byte_pos; + } + + if (!(nullbits[byte_pos] & (1 << bit_pos))) { + stdx::expected field_size_res( + stdx::make_unexpected( + make_error_code(std::errc::invalid_argument))); + switch (types[n] & 0xff) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: { + auto string_field_size_res = accu.template step(); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + field_size_res = string_field_size_res->value(); + } break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: { + auto time_field_size_res = accu.template step>(); + if (!accu.result()) + return stdx::make_unexpected(accu.result().error()); + + field_size_res = time_field_size_res->value(); + } break; + case field_type::LongLong: + case field_type::Double: + field_size_res = 8; + break; + case field_type::Long: + case field_type::Int24: + case field_type::Float: + field_size_res = 4; + break; + case field_type::Short: + case field_type::Year: + field_size_res = 2; + break; + case field_type::Tiny: + field_size_res = 1; + break; } - if (!(nullbits[byte_pos] & (1 << bit_pos))) { - stdx::expected field_size_res( - stdx::make_unexpected( - make_error_code(std::errc::invalid_argument))); - switch (types[n]) { - case field_type::Bit: - case field_type::Blob: - case field_type::Varchar: - case field_type::VarString: - case field_type::Set: - case field_type::String: - case field_type::Enum: - case field_type::TinyBlob: - case field_type::MediumBlob: - case field_type::LongBlob: - case field_type::Decimal: - case field_type::NewDecimal: - case field_type::Geometry: { - auto string_field_size_res = accu.template step(); - if (!accu.result()) - return stdx::make_unexpected(accu.result().error()); - - field_size_res = string_field_size_res->value(); - } break; - case field_type::Date: - case field_type::DateTime: - case field_type::Timestamp: - case field_type::Time: { - auto time_field_size_res = accu.template step>(); - if (!accu.result()) - return stdx::make_unexpected(accu.result().error()); - - field_size_res = time_field_size_res->value(); - } break; - case field_type::LongLong: - case field_type::Double: - field_size_res = 8; - break; - case field_type::Long: - case field_type::Int24: - case field_type::Float: - field_size_res = 4; - break; - case field_type::Short: - case field_type::Year: - field_size_res = 2; - break; - case field_type::Tiny: - field_size_res = 1; - break; - } - auto value_res = - accu.template step>(field_size_res.value()); - if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); - } + if (!field_size_res) { + return stdx::make_unexpected( + make_error_code(codec_errc::field_type_unknown)); + } - values.push_back(value_res->value()); - } else { - values.emplace_back(std::nullopt); + auto value_res = + accu.template step>(field_size_res.value()); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); } + + values.push_back(value_res->value()); + } else { + values.emplace_back(std::nullopt); } } From 20dbdd77f9dd95e60537caffb85f08db204985a6 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 17 Feb 2023 13:04:36 +0100 Subject: [PATCH 61/88] Bug#35097470 BinlogDumpGtid decoder fails to decode without SIDs Decoding a BinlogDumpGtid message currently fails if it doesn't contain GTIDs which is announced by not setting the 'through-gtid' flag. Change ------ - don't expect GTIDs if the flag isn't set. Change-Id: I72b505aae4b6e2a5411b47a4cd9c5cc0e79d64fc --- mysqlrouter/classic_protocol_codec_message.h | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index e4a9fae..7dfa434 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -3166,9 +3166,7 @@ class Codec> .step(bw::FixedInt<4>(v_.server_id())) .step(bw::FixedInt<4>(v_.filename().size())) .step(bw::String(v_.filename())) - .step(bw::FixedInt<8>(v_.position())) - // - ; + .step(bw::FixedInt<8>(v_.position())); if (v_.flags() & value_type::Flags::through_gtid) { accu.step(bw::FixedInt<4>(v_.sids().size())) @@ -3179,12 +3177,12 @@ class Codec> } public: - using __base = impl::EncodeBase>; + using base_ = impl::EncodeBase>; - friend __base; + friend base_; - constexpr Codec(value_type v, capabilities::value_type caps) - : __base(caps), v_{std::move(v)} {} + constexpr Codec(value_type val, capabilities::value_type caps) + : base_(caps), v_{std::move(val)} {} constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::BinlogDumpGtid); @@ -3210,6 +3208,19 @@ class Codec> auto filename_res = accu.template step>(filename_len_res->value()); auto position_res = accu.template step>(); + + stdx::flags flags; + flags.underlying_value(flags_res->value()); + + if (!(flags & value_type::Flags::through_gtid)) { + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(flags, server_id_res->value(), filename_res->value(), + position_res->value(), {})); + } + auto sids_len_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -3218,9 +3229,6 @@ class Codec> if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - stdx::flags flags; - flags.underlying_value(flags_res->value()); - return std::make_pair( accu.result().value(), value_type(flags, server_id_res->value(), filename_res->value(), From 40ec967f4a47265df2138a3ed115f7f4e7b32759 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 21 Feb 2023 16:07:50 +0100 Subject: [PATCH 62/88] Bug#35115810 stmt-execute with new-params-bound = 0 [1/2] The COM_STMT_EXECUTE message is handled by the StmtExecute Codec which currently doesn't support messages with "new-params-bound = 0" which is used when a prepared statement is executed twice, with the same data-types, but possibly different values. To decode COM_STMT_EXECUTE packets, the number of parameter is needed which is sent in the COM_STMT_PREPARE's OK packet. Change ------ - track parameter definitions of prepared statements. - reset the parameter definitions and stmt-ids at COM_RESET_CONNECTION and COM_CHANGE_USER - change the StmtExecute's lookup function to return ParamDef instead of only a parameter-count. - fixed the StmtExecute .types() to remember the "flags" of a type, and not only the type. Change-Id: Idab11eca693299f3423fea936a32083500a2c88d --- mysqlrouter/classic_protocol_codec_message.h | 63 ++++++++++---------- mysqlrouter/classic_protocol_message.h | 21 +++++-- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 7dfa434..b4d1d4c 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1959,8 +1959,8 @@ class Codec> reinterpret_cast(nullbits.data()), nullbits.size()))) .step(bw::FixedInt<1>(v_.new_params_bound())); if (v_.new_params_bound()) { - for (const auto &t : v_.types()) { - accu.step(bw::FixedInt<2>(t)); + for (const auto ¶m_def : v_.types()) { + accu.step(bw::FixedInt<2>(param_def.type_and_flags)); } } @@ -1968,7 +1968,7 @@ class Codec> // add all the values that aren't NULL if (!v.has_value()) continue; // write length of the type is a variable length - switch (v_.types()[n] & 0xff) { + switch (v_.types()[n].type_and_flags & 0xff) { case field_type::Bit: case field_type::Blob: case field_type::Varchar: @@ -2000,8 +2000,10 @@ class Codec> case field_type::Tiny: // fixed size break; + default: + assert(false || "Unknown Type"); } - accu.step(bw::String(v.value())); + accu.step(borrowed::wire::String(v.value())); } return accu.result(); @@ -2013,48 +2015,49 @@ class Codec> friend __base; - constexpr Codec(value_type v, capabilities::value_type caps) - : __base(caps), v_{std::move(v)} {} + constexpr Codec(value_type val, capabilities::value_type caps) + : __base(caps), v_{std::move(val)} {} constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::StmtExecute); } /** - * decode a sequence of buffers into a message::client::ExecuteStmt. + * decode a buffer into a message::client::StmtExecute. * - * @param buffer sequence of buffers + * @param buffer a buffer * @param caps protocol capabilities - * @param param_count_lookup callable that expects a 'uint32_t statement_id' - * that returns and integer that's convertible to 'stdx::expected' representing the parameter count of the prepared - * statement + * @param metadata_lookup callable that expects a 'uint32_t statement_id' + * that returns a result that's convertible to + * 'stdx::expected, std::error_code>' representing the + * parameter-definitions of the prepared statement * - * decoding a ExecuteStmt message requires a parameter count of the prepared - * statement. The param_count_lookup function may be called to get the param - * count for the statement-id. + * decoding a StmtExecute message requires the parameter-definitions of the + * prepared statement. The metadata_lookup function may be called to get + * the parameter-definitions for the statement-id. * - * The function may return a param-count directly + * The function may return a parameter-definitions directly * * \code - * ExecuteStmt::decode( + * StmtExecute::decode( * buffers, * capabilities::protocol_41, - * [](uint32_t stmt_id) { return 1; }); + * [](uint32_t stmt_id) { return std::vector{}; }); * \endcode * - * ... or a stdx::expected if it wants to signal - * that a statement-id wasn't found + * ... or a stdx::expected, std::error_code> if it wants + * to signal that a statement-id wasn't found * * \code - * ExecuteStmt::decode( + * StmtExecute::decode( * buffers, * capabilities::protocol_41, - * [](uint32_t stmt_id) -> stdx::expected { + * [](uint32_t stmt_id) -> + * stdx::expected, std::error_code> { * bool found{true}; * * if (found) { - * return 1; + * return {}; * } else { * return stdx::make_unexpected(make_error_code( * codec_errc::statement_id_not_found)); @@ -2065,7 +2068,7 @@ class Codec> template static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps, - Func &¶m_count_lookup) { + Func &&metadata_lookup) { impl::DecodeBufferAccumulator accu(buffer, caps); namespace bw = borrowable::wire; @@ -2083,14 +2086,14 @@ class Codec> if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - stdx::expected param_count_res = - param_count_lookup(statement_id_res->value()); - if (!param_count_res) { + stdx::expected, std::error_code> + metadata_res = metadata_lookup(statement_id_res->value()); + if (!metadata_res) { return stdx::make_unexpected( make_error_code(codec_errc::statement_id_not_found)); } - const size_t param_count = param_count_res.value(); + const size_t param_count = metadata_res->size(); if (param_count == 0) { if (!accu.result()) return stdx::make_unexpected(accu.result().error()); @@ -2115,7 +2118,7 @@ class Codec> return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - std::vector types; + std::vector types; std::vector> values; if (new_params_bound == 1) { @@ -2142,7 +2145,7 @@ class Codec> stdx::expected field_size_res( stdx::make_unexpected( make_error_code(std::errc::invalid_argument))); - switch (types[n] & 0xff) { + switch (types[n].type_and_flags & 0xff) { case field_type::Bit: case field_type::Blob: case field_type::Varchar: diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 3a6689c..97b8baa 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1056,6 +1056,18 @@ class StmtExecute { using value_type = std::optional; + struct ParamDef { + ParamDef() = default; + + ParamDef(uint16_t type_and_flags_) : type_and_flags(type_and_flags_) {} + + friend bool operator==(const ParamDef &lhs, const ParamDef &rhs) { + return lhs.type_and_flags == rhs.type_and_flags; + } + + uint16_t type_and_flags{}; + }; + /** * construct a ExecuteStmt message. * @@ -1068,8 +1080,7 @@ class StmtExecute { */ StmtExecute(uint32_t statement_id, classic_protocol::cursor::value_type flags, uint32_t iteration_count, bool new_params_bound, - std::vector types, - std::vector values) + std::vector types, std::vector values) : statement_id_{statement_id}, flags_{flags}, iteration_count_{iteration_count}, @@ -1081,9 +1092,7 @@ class StmtExecute { classic_protocol::cursor::value_type flags() const noexcept { return flags_; } uint32_t iteration_count() const noexcept { return iteration_count_; } bool new_params_bound() const noexcept { return new_params_bound_; } - std::vector types() const { - return types_; - } + std::vector types() const { return types_; } std::vector values() const { return values_; } private: @@ -1091,7 +1100,7 @@ class StmtExecute { classic_protocol::cursor::value_type flags_; uint32_t iteration_count_; bool new_params_bound_; - std::vector types_; + std::vector types_; std::vector values_; }; From 2b4d4308a9dbc715b0e788d64777d2cf99a0f713 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 21 Feb 2023 21:12:44 +0100 Subject: [PATCH 63/88] Bug#35115810 stmt-execute with new-params-bound = 0 [2/2] The COM_STMT_EXECUTE message is handled by the StmtExecute Codec which currently doesn't support messages with "new-params-bound = 0" which is used when a prepared statement is executed twice, with the same data-types, but possibly different values. In the new-param-bound = 0 case, the types of the first execute of a statement must be remembered. Change ------ - handle new-param-bound = 0 properly Change-Id: Ic71a9fd00b0f56d8cfc2d707873c164fed7495fd --- mysqlrouter/classic_protocol_codec_base.h | 23 ++++++++++++++++++++ mysqlrouter/classic_protocol_codec_message.h | 14 +++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index e536b4c..1065e59 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -110,6 +110,7 @@ stdx::expected encode(const T &v, * * @param buffer buffer to read from * @param caps protocol capabilities + * @tparam T the message class * @returns number of bytes read from 'buffers' and a T on success, or * std::error_code on error */ @@ -119,6 +120,28 @@ stdx::expected, std::error_code> decode( return Codec::decode(buffer, caps); } +/** + * decode a message from a buffer. + * + * @param buffer buffer to read from + * @param caps protocol capabilities + * @param args arguments that shall be forwarded to T's decode() + * @tparam T the message class + * @tparam Args Types of the extra arguments to be forwarded to T's decode() + * function. + * @returns number of bytes read from 'buffers' and a T on success, or + * std::error_code on error + */ +template +stdx::expected, std::error_code> decode( + const net::const_buffer &buffer, capabilities::value_type caps, + // clang-format off + Args &&... args + // clang-format on +) { + return Codec::decode(buffer, caps, std::forward(args)...); +} + namespace impl { /** diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index b4d1d4c..8060582 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2111,17 +2111,13 @@ class Codec> auto new_params_bound_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - auto new_params_bound = new_params_bound_res->value(); - if (new_params_bound != 1) { - // new-params-bound is required as long as the decoder doesn't know the - // old param-defs - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); - } - std::vector types; std::vector> values; - if (new_params_bound == 1) { + auto new_params_bound = new_params_bound_res->value(); + if (new_params_bound == 0) { + types = *metadata_res; + } else if (new_params_bound == 1) { types.reserve(param_count); for (size_t n{}; n < param_count; ++n) { @@ -2130,6 +2126,8 @@ class Codec> types.push_back(type_res->value()); } + } else { + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } const auto nullbits = nullbits_res->value(); From a86c1b1f347cfcc7c60089a786de5b4104b4794b Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 8 Mar 2023 13:07:04 +0100 Subject: [PATCH 64/88] Bug#35163483 StmtPrepareOk::warning_count() is not settable StmtPrepareOk is similar to the Ok packet and also contains a warning count. The POD class has getters for all the fields, but misses a way to set the warning count. Change ------ - add setters Change-Id: Ic99c150ad44539913e3ee18741d6249f2a37df9d --- mysqlrouter/classic_protocol_message.h | 31 ++++++++++++++++---------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 97b8baa..b021e37 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -559,27 +559,34 @@ class StmtPrepareOk { with_metadata_{with_metadata} {} constexpr uint32_t statement_id() const noexcept { return statement_id_; } + constexpr void statement_id(uint32_t id) noexcept { statement_id_ = id; } constexpr uint16_t warning_count() const noexcept { return warning_count_; } - - constexpr uint16_t column_count() const { return column_count_; } - constexpr uint16_t param_count() const { return param_count_; } - constexpr uint8_t with_metadata() const { return with_metadata_; } + constexpr void warning_count(uint16_t cnt) noexcept { warning_count_ = cnt; } + + constexpr uint16_t column_count() const noexcept { return column_count_; } + constexpr void column_count(uint16_t cnt) noexcept { column_count_ = cnt; } + constexpr uint16_t param_count() const noexcept { return param_count_; } + constexpr void param_count(uint16_t cnt) noexcept { param_count_ = cnt; } + constexpr uint8_t with_metadata() const noexcept { return with_metadata_; } + constexpr void with_metadata(uint8_t with) noexcept { with_metadata_ = with; } + + friend constexpr bool operator==(const StmtPrepareOk &lhs, + const StmtPrepareOk &rhs) { + return (lhs.statement_id() == rhs.statement_id()) && + (lhs.column_count() == rhs.column_count()) && + (lhs.param_count() == rhs.param_count()) && + (lhs.warning_count() == rhs.warning_count()) && + (lhs.with_metadata() == rhs.with_metadata()); + } private: uint32_t statement_id_; uint16_t warning_count_; uint16_t param_count_; uint16_t column_count_; - uint8_t with_metadata_{1}; + uint8_t with_metadata_; }; -inline bool operator==(const StmtPrepareOk &a, const StmtPrepareOk &b) { - return (a.statement_id() == b.statement_id()) && - (a.column_count() == b.column_count()) && - (a.param_count() == b.param_count()) && - (a.warning_count() == b.warning_count()); -} - /** * StmtRow message. * From 0878bd6a53d489c49f8bcd4bb219116f0300eb62 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 27 Feb 2023 14:54:09 +0100 Subject: [PATCH 65/88] Bug#35120039 support query attributes in StmtExecute and Query codec Query attributes can be sent along a COM_STMT_EXECUTE and COM_QUERY. They are encoded as "binary protocol" in both packets (even if COM_QUERY uses the "text protocol"). Change ------ - added a codec for the binary protocol - added query-attribute codec to Query and StmtExecute - added unit-tests and fuzzers Change-Id: I3b057b48f3f11f08ebeefa1f31bd04da9c583063 --- mysqlrouter/classic_protocol_binary.h | 371 ++++++++++ mysqlrouter/classic_protocol_codec_binary.h | 711 +++++++++++++++++++ mysqlrouter/classic_protocol_codec_message.h | 456 +++++++++--- mysqlrouter/classic_protocol_constants.h | 5 +- mysqlrouter/classic_protocol_message.h | 28 +- 5 files changed, 1484 insertions(+), 87 deletions(-) create mode 100644 mysqlrouter/classic_protocol_binary.h create mode 100644 mysqlrouter/classic_protocol_codec_binary.h diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h new file mode 100644 index 0000000..dfda064 --- /dev/null +++ b/mysqlrouter/classic_protocol_binary.h @@ -0,0 +1,371 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_BINARY_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_BINARY_H_ + +#include // size_t +#include // uint8_t +#include // error_code +#include // move + +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" +#include "mysqlrouter/classic_protocol_wire.h" + +namespace classic_protocol { +namespace borrowable { +namespace binary { + +template +using string_type = std::conditional_t; + +/** + * base type of all binary scalar value types. + */ +template +class TypeBase { + public: + using value_type = T; + + constexpr TypeBase(value_type val) : v_(val) {} + + [[nodiscard]] constexpr value_type value() const { return v_; } + + friend bool operator==(const TypeBase &lhs, const TypeBase &rhs) { + return lhs.value() == rhs.value(); + } + + friend bool operator!=(const TypeBase &lhs, const TypeBase &rhs) { + return !(lhs == rhs); + } + + private: + value_type v_; +}; + +/** + * POD base-type for Datetime, Timestamp, Date. + */ +class DatetimeBase { + public: + DatetimeBase() = default; + + constexpr DatetimeBase(uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second, + uint32_t microsecond = 0) + : year_(year), + month_(month), + day_(day), + hour_(hour), + minute_(minute), + second_(second), + microsecond_(microsecond) {} + + constexpr DatetimeBase(uint16_t year, uint8_t month, uint8_t day) + : DatetimeBase(year, month, day, 0, 0, 0, 0) {} + + [[nodiscard]] constexpr uint16_t year() const { return year_; } + [[nodiscard]] constexpr uint8_t month() const { return month_; } + [[nodiscard]] constexpr uint8_t day() const { return day_; } + [[nodiscard]] constexpr uint8_t hour() const { return hour_; } + [[nodiscard]] constexpr uint8_t minute() const { return minute_; } + [[nodiscard]] constexpr uint8_t second() const { return second_; } + [[nodiscard]] constexpr uint32_t microsecond() const { return microsecond_; } + + friend bool operator==(const DatetimeBase &lhs, const DatetimeBase &rhs) { + return lhs.year() == rhs.year() && // + lhs.month() == rhs.month() && // + lhs.day() == rhs.day() && // + lhs.hour() == rhs.hour() && // + lhs.minute() == rhs.minute() && // + lhs.second() == rhs.second() && // + lhs.microsecond() == rhs.microsecond(); + } + + friend bool operator!=(const DatetimeBase &lhs, const DatetimeBase &rhs) { + return !(lhs == rhs); + } + + private: + uint16_t year_{}; + uint8_t month_{}; + uint8_t day_{}; + uint8_t hour_{}; + uint8_t minute_{}; + uint8_t second_{}; + uint32_t microsecond_{}; +}; + +class DateTime : public DatetimeBase { + public: + using DatetimeBase::DatetimeBase; +}; + +class Timestamp : public DatetimeBase { + public: + using DatetimeBase::DatetimeBase; +}; + +class Date : public DatetimeBase { + public: + using DatetimeBase::DatetimeBase; +}; + +class Time { + public: + constexpr Time() = default; + + constexpr Time(bool is_negative, uint32_t days, uint8_t hour, uint8_t minute, + uint8_t second, uint32_t microsecond = 0) + : is_negative_(is_negative), + days_(days), + hour_(hour), + minute_(minute), + second_(second), + microsecond_(microsecond) {} + + [[nodiscard]] constexpr bool is_negative() const { return is_negative_; } + [[nodiscard]] constexpr uint32_t days() const { return days_; } + [[nodiscard]] constexpr uint8_t hour() const { return hour_; } + [[nodiscard]] constexpr uint8_t minute() const { return minute_; } + [[nodiscard]] constexpr uint8_t second() const { return second_; } + [[nodiscard]] constexpr uint32_t microsecond() const { return microsecond_; } + + friend bool operator==(const Time &lhs, const Time &rhs) { + return lhs.days() == rhs.days() && // + lhs.hour() == rhs.hour() && // + lhs.minute() == rhs.minute() && // + lhs.second() == rhs.second() && // + lhs.microsecond() == rhs.microsecond() && // + lhs.is_negative() == rhs.is_negative(); + } + + friend bool operator!=(const Time &lhs, const Time &rhs) { + return !(lhs == rhs); + } + + private: + bool is_negative_{}; + uint32_t days_{}; + uint8_t hour_{}; + uint8_t minute_{}; + uint8_t second_{}; + uint32_t microsecond_{}; +}; + +class LongLong : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Long : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Int24 : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Short : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Year : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Tiny : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Double : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +class Float : public TypeBase { + public: + using TypeBase::TypeBase; +}; + +template +class String : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class VarString : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Varchar : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Json : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Blob : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class TinyBlob : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class MediumBlob : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class LongBlob : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Enum : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Set : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Decimal : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class NewDecimal : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Bit : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +template +class Geometry : public TypeBase> { + public: + using TypeBase>::TypeBase; +}; + +class Null {}; + +} // namespace binary +} // namespace borrowable + +namespace borrowed { +namespace binary { +using Tiny = borrowable::binary::Tiny; +using Short = borrowable::binary::Short; +using Int24 = borrowable::binary::Int24; +using Long = borrowable::binary::Long; +using LongLong = borrowable::binary::LongLong; +using Float = borrowable::binary::Float; +using Double = borrowable::binary::Double; +using Year = borrowable::binary::Year; +using Time = borrowable::binary::Time; +using Date = borrowable::binary::Date; +using DateTime = borrowable::binary::DateTime; +using Timestamp = borrowable::binary::Timestamp; +using String = borrowable::binary::String; +using VarString = borrowable::binary::VarString; +using Varchar = borrowable::binary::Varchar; +using TinyBlob = borrowable::binary::TinyBlob; +using Blob = borrowable::binary::Blob; +using MediumBlob = borrowable::binary::MediumBlob; +using LongBlob = borrowable::binary::LongBlob; +using Enum = borrowable::binary::Enum; +using Set = borrowable::binary::Set; +using Decimal = borrowable::binary::Decimal; +using NewDecimal = borrowable::binary::NewDecimal; +using Json = borrowable::binary::Json; +using Geometry = borrowable::binary::Geometry; +using Bit = borrowable::binary::Bit; +using Null = borrowable::binary::Null; +} // namespace binary +} // namespace borrowed + +namespace binary { +using Tiny = borrowable::binary::Tiny; +using Short = borrowable::binary::Short; +using Int24 = borrowable::binary::Int24; +using Long = borrowable::binary::Long; +using LongLong = borrowable::binary::LongLong; +using Float = borrowable::binary::Float; +using Double = borrowable::binary::Double; +using Year = borrowable::binary::Year; +using Time = borrowable::binary::Time; +using Date = borrowable::binary::Date; +using DateTime = borrowable::binary::DateTime; +using Timestamp = borrowable::binary::Timestamp; +using String = borrowable::binary::String; +using VarString = borrowable::binary::VarString; +using Varchar = borrowable::binary::Varchar; +using TinyBlob = borrowable::binary::TinyBlob; +using Blob = borrowable::binary::Blob; +using MediumBlob = borrowable::binary::MediumBlob; +using LongBlob = borrowable::binary::LongBlob; +using Enum = borrowable::binary::Enum; +using Set = borrowable::binary::Set; +using Decimal = borrowable::binary::Decimal; +using NewDecimal = borrowable::binary::NewDecimal; +using Json = borrowable::binary::Json; +using Geometry = borrowable::binary::Geometry; +using Bit = borrowable::binary::Bit; +using Null = borrowable::binary::Null; +} // namespace binary +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h new file mode 100644 index 0000000..a2717ce --- /dev/null +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -0,0 +1,711 @@ +/* + Copyright (c) 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_BINARY_H_ +#define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_BINARY_H_ + +#include // size_t +#include // uint8_t +#include // error_code +#include // move + +#include "mysql/harness/stdx/expected.h" +#include "mysqlrouter/classic_protocol_binary.h" +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_wire.h" +#include "mysqlrouter/classic_protocol_wire.h" + +namespace classic_protocol { + +namespace impl { +template +struct BinaryTypeBase; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Decimal; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Tiny; + static constexpr const uint8_t byte_size = 1; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Short; + static constexpr const uint8_t byte_size = 2; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Long; + static constexpr const uint8_t byte_size = 4; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Float; + static constexpr const uint8_t byte_size = 4; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Double; + static constexpr const uint8_t byte_size = 8; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Null; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Timestamp; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::LongLong; + static constexpr const uint8_t byte_size = 8; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Int24; + static constexpr const uint8_t byte_size = 4; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Date; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Time; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::DateTime; +}; + +template <> +struct BinaryTypeBase { + static constexpr const uint16_t binary_field_type = field_type::Year; + static constexpr const uint8_t byte_size = 2; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Varchar; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Bit; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Json; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::NewDecimal; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Enum; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Set; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::TinyBlob; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::MediumBlob; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::LongBlob; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Blob; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::String; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::VarString; +}; + +template +struct BinaryTypeBase> { + static constexpr const uint16_t binary_field_type = field_type::Geometry; +}; + +// codec base + +template +class FixedIntCodec : public impl::EncodeBase> { + static constexpr const int byte_size = BinaryTypeBase::byte_size; + + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + accu.step(wire::FixedInt(val_.value())); + + return accu.result(); + } + + public: + using value_type = T; + using base_ = impl::EncodeBase>; + + friend base_; + + constexpr FixedIntCodec(value_type val, capabilities::value_type caps) + : base_(caps), val_{std::move(val)} {} + + static constexpr uint16_t type() { + return BinaryTypeBase::binary_field_type; + } + + static stdx::expected, std::error_code> decode( + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); + + auto value_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(value_res->value())); + } + + private: + const value_type val_; +}; + +template +class FloatCodec : public impl::EncodeBase> { + static constexpr const int byte_size = BinaryTypeBase::byte_size; + + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + // reinterpret_cast<> isn't allowed in constexpr. + // + // Use a union as a workaround. + union overlapped_storage { + constexpr overlapped_storage(typename value_type::value_type val) + : val_(val) {} + + typename value_type::value_type val_; + + char addr_[sizeof(val_)]; + } overlapped(val_.value()); + + static_assert(sizeof(overlapped.val_) == byte_size); + + return accu + .step(borrowed::wire::String{ + std::string_view((&overlapped.addr_[0]), sizeof(overlapped.val_))}) + .result(); + } + + public: + using value_type = T; + using base_ = impl::EncodeBase>; + + friend base_; + + constexpr FloatCodec(value_type val, capabilities::value_type caps) + : base_(caps), val_{std::move(val)} {} + + static constexpr uint16_t type() { + return BinaryTypeBase::binary_field_type; + } + + static stdx::expected, std::error_code> decode( + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); + + auto value_res = accu.template step(byte_size); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + typename T::value_type val; + + memcpy(&val, value_res->value().data(), byte_size); + + return std::make_pair(accu.result().value(), value_type(val)); + } + + private: + const value_type val_; +}; + +template +class StringCodec : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + accu.step(borrowable::wire::String(val_.value())); + + return accu.result(); + } + + public: + using value_type = T; + using base_ = impl::EncodeBase>; + + friend base_; + + constexpr StringCodec(value_type val, capabilities::value_type caps) + : base_(caps), val_{std::move(val)} {} + + static constexpr uint16_t type() { + return BinaryTypeBase::binary_field_type; + } + + static stdx::expected, std::error_code> decode( + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); + + auto value_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair(accu.result().value(), + value_type(value_res->value())); + } + + private: + const value_type val_; +}; + +template +class DatetimeCodec : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + const bool has_ms = val_.microsecond(); + const bool has_time = + val_.hour() || val_.minute() || val_.second() || has_ms; + const bool has_date = val_.year() || val_.month() || val_.day() || has_time; + + if (has_date) { + accu.step(wire::FixedInt<2>(val_.year())) + .step(wire::FixedInt<1>(val_.month())) + .step(wire::FixedInt<1>(val_.day())); + if (has_time) { + accu.step(wire::FixedInt<1>(val_.hour())) + .step(wire::FixedInt<1>(val_.minute())) + .step(wire::FixedInt<1>(val_.second())); + if (has_ms) { + accu.step(wire::FixedInt<4>(val_.microsecond())); + } + } + } + return accu.result(); + } + + public: + using value_type = T; + using base_ = impl::EncodeBase>; + + friend base_; + + constexpr DatetimeCodec(value_type val, capabilities::value_type caps) + : base_(caps), val_{std::move(val)} {} + + static constexpr uint16_t type() { + return BinaryTypeBase::binary_field_type; + } + + static stdx::expected, std::error_code> decode( + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); + + auto year_res = accu.template try_step>(); + if (!year_res) { + return std::make_pair(accu.result().value(), value_type()); + } + + auto month_res = accu.template step>(); + auto day_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto hour_res = accu.template try_step>(); + if (!hour_res) { + return std::make_pair( + accu.result().value(), + value_type(year_res->value(), month_res->value(), day_res->value())); + } + + auto minute_res = accu.template step>(); + auto second_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto microsecond_res = accu.template try_step>(); + if (!microsecond_res) { + return std::make_pair( + accu.result().value(), + value_type(year_res->value(), month_res->value(), day_res->value(), + hour_res->value(), minute_res->value(), + second_res->value())); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(year_res->value(), month_res->value(), day_res->value(), + hour_res->value(), minute_res->value(), second_res->value(), + microsecond_res->value())); + } + + private: + value_type val_; +}; + +template +class TimeCodec : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + if (val_.days() || val_.hour() || val_.minute() || val_.second() || + val_.microsecond()) { + accu.step(wire::FixedInt<1>(val_.is_negative())) + .step(wire::FixedInt<4>(val_.days())) + .step(wire::FixedInt<1>(val_.hour())) + .step(wire::FixedInt<1>(val_.minute())) + .step(wire::FixedInt<1>(val_.second())); + if (val_.microsecond()) { + accu.step(wire::FixedInt<4>(val_.microsecond())); + } + } + return accu.result(); + } + + public: + using value_type = T; + using base_ = impl::EncodeBase>; + + friend base_; + + constexpr TimeCodec(value_type val, capabilities::value_type caps) + : base_(caps), val_{std::move(val)} {} + + static constexpr uint16_t type() { + return BinaryTypeBase::binary_field_type; + } + + static stdx::expected, std::error_code> decode( + const net::const_buffer &buffer, capabilities::value_type caps) { + impl::DecodeBufferAccumulator accu(buffer, caps); + + auto is_negative_res = accu.template try_step>(); + if (!is_negative_res) { + return std::make_pair(accu.result().value(), value_type()); + } + auto days_res = accu.template step>(); + auto hour_res = accu.template step>(); + auto minute_res = accu.template step>(); + auto second_res = accu.template step>(); + auto microsecond_res = accu.template try_step>(); + + if (!microsecond_res) { + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(is_negative_res->value(), days_res->value(), + hour_res->value(), minute_res->value(), + second_res->value())); + } + + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + return std::make_pair( + accu.result().value(), + value_type(is_negative_res->value(), days_res->value(), + hour_res->value(), minute_res->value(), second_res->value(), + microsecond_res->value())); + } + + private: + const value_type val_; +}; + +} // namespace impl + +template <> +class Codec + : public impl::EncodeBase> { + template + constexpr auto accumulate_fields(Accumulator &&accu) const { + return accu.result(); + } + + public: + using value_type = borrowable::binary::Null; + using base_ = impl::EncodeBase>; + + friend base_; + + constexpr Codec(value_type /* val */, capabilities::value_type caps) + : base_(caps) {} + + static constexpr uint16_t type() { + return impl::BinaryTypeBase::binary_field_type; + } + + static stdx::expected, std::error_code> decode( + const net::const_buffer & /* buffer */, + capabilities::value_type /* caps */) { + return std::make_pair(0, value_type()); + } +}; + +template <> +class Codec + : public impl::FixedIntCodec { + public: + using FixedIntCodec::FixedIntCodec; +}; + +template <> +class Codec + : public impl::FixedIntCodec { + public: + using FixedIntCodec::FixedIntCodec; +}; + +template <> +class Codec + : public impl::FixedIntCodec { + public: + using FixedIntCodec::FixedIntCodec; +}; + +template <> +class Codec + : public impl::FixedIntCodec { + public: + using FixedIntCodec::FixedIntCodec; +}; + +template <> +class Codec + : public impl::FixedIntCodec { + public: + using FixedIntCodec::FixedIntCodec; +}; + +template <> +class Codec + : public impl::FixedIntCodec { + public: + using FixedIntCodec::FixedIntCodec; +}; + +template <> +class Codec + : public impl::FloatCodec { + public: + using FloatCodec::FloatCodec; +}; + +template <> +class Codec + : public impl::FloatCodec { + public: + using FloatCodec::FloatCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec< + Borrowed, borrowable::binary::MediumBlob>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec< + Borrowed, borrowable::binary::NewDecimal>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template +class Codec> + : public impl::StringCodec> { + public: + using impl::StringCodec>::StringCodec; +}; + +template <> +class Codec + : public impl::DatetimeCodec { + public: + using DatetimeCodec::DatetimeCodec; +}; + +template <> +class Codec + : public impl::DatetimeCodec { + public: + using DatetimeCodec::DatetimeCodec; +}; + +template <> +class Codec + : public impl::DatetimeCodec { + public: + using DatetimeCodec::DatetimeCodec; +}; + +template <> +class Codec + : public impl::TimeCodec { + public: + using TimeCodec::TimeCodec; +}; + +} // namespace classic_protocol + +#endif diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 8060582..feabf47 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -25,6 +25,7 @@ #ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_MESSAGE_H_ #define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_MESSAGE_H_ +#include #include // size_t #include // uint8_t #include // error_code @@ -33,6 +34,7 @@ #include "mysql/harness/stdx/expected.h" #include "mysql/harness/stdx/ranges.h" #include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_binary.h" #include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_codec_wire.h" #include "mysqlrouter/classic_protocol_constants.h" @@ -1176,63 +1178,60 @@ class Codec> accu.step(bw::FixedInt<1>(0)); - std::string nullbits; - nullbits.resize(bytes_per_bits(v_.types().size())); - // null-bitmap starts with a 2-bit offset size_t bit_pos{2}; - size_t byte_pos{}; - for (const auto &field : v_) { - if (bit_pos > 7) { - bit_pos = 0; - ++byte_pos; - } + uint8_t null_bit_byte{}; - if (!field) { - nullbits[byte_pos] |= 1 << bit_pos; + for (auto const &field : v_) { + if (!field) null_bit_byte |= (1 << bit_pos); + + if (++bit_pos > 7) { + accu.step(bw::FixedInt<1>(null_bit_byte)); + + bit_pos = 0; + null_bit_byte = 0; } } - accu.step(bw::String(nullbits)); + if (bit_pos != 0) accu.step(bw::FixedInt<1>(null_bit_byte)); - size_t n{}; - for (const auto &field : v_) { - if (field) { - switch (v_.types()[n++]) { - case field_type::Bit: - case field_type::Blob: - case field_type::Varchar: - case field_type::VarString: - case field_type::Set: - case field_type::String: - case field_type::Enum: - case field_type::TinyBlob: - case field_type::MediumBlob: - case field_type::LongBlob: - case field_type::Decimal: - case field_type::NewDecimal: - case field_type::Geometry: - accu.step(bw::VarInt(field->size())); - break; - case field_type::Date: - case field_type::DateTime: - case field_type::Timestamp: - case field_type::Time: - accu.step(bw::FixedInt<1>(field->size())); - break; - case field_type::LongLong: - case field_type::Double: - case field_type::Long: - case field_type::Int24: - case field_type::Float: - case field_type::Short: - case field_type::Year: - case field_type::Tiny: - // fixed size - break; - } - accu.step(bw::String(*field)); + for (auto [n, field] : stdx::views::enumerate(v_)) { + if (!field) continue; + + switch (v_.types()[n]) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::NewDecimal: + case field_type::Geometry: + accu.step(bw::VarInt(field->size())); + break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: + accu.step(bw::FixedInt<1>(field->size())); + break; + case field_type::LongLong: + case field_type::Double: + case field_type::Long: + case field_type::Int24: + case field_type::Float: + case field_type::Short: + case field_type::Year: + case field_type::Tiny: + // fixed size + break; } + accu.step(borrowed::wire::String(*field)); } return accu.result(); @@ -1605,12 +1604,164 @@ class Codec> : public impl::EncodeBase< Codec>> { template - constexpr auto accumulate_fields(Accumulator &&accu) const { + auto accumulate_fields(Accumulator &&accu) const { namespace bw = borrowable::wire; - return accu.step(bw::FixedInt<1>(cmd_byte())) - .step(bw::String(v_.statement())) - .result(); + accu.step(bw::FixedInt<1>(cmd_byte())); + + auto caps = this->caps(); + + if (caps.test(capabilities::pos::query_attributes)) { + uint64_t param_count = v_.values().size(); + accu.step(bw::VarInt(param_count)); // param_count + accu.step(bw::VarInt(1)); // param_set_count: always 1 + + if (param_count > 0) { + // mark all that are NULL in the nullbits + // + // - one bit per parameter to send + // - if a parameter is NULL, the bit is set, and later no value is + // added. + + uint8_t null_bit_byte{}; + int bit_pos{}; + + for (auto const ¶m : v_.values()) { + if (!param.value) null_bit_byte |= 1 << bit_pos; + + if (++bit_pos > 7) { + accu.step(bw::FixedInt<1>(null_bit_byte)); + + bit_pos = 0; + null_bit_byte = 0; + } + } + + if (bit_pos != 0) accu.step(bw::FixedInt<1>(null_bit_byte)); + + accu.step(bw::FixedInt<1>(1)); // new_param_bind_flag: always 1 + + for (const auto ¶m : v_.values()) { + accu.step(bw::FixedInt<2>(param.type_and_flags)) + .step(bw::VarString(param.name)); + } + + for (const auto ¶m : v_.values()) { + if (!param.value) continue; + + auto type = param.type_and_flags & 0xff; + switch (type) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::Json: + case field_type::NewDecimal: + case field_type::Geometry: + accu.template step(param.value->size()); + break; + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: + assert(param.value->size() <= 255); + + accu.template step>(param.value->size()); + + break; + case field_type::LongLong: + case field_type::Double: + assert(param.value->size() == 8); + + break; + case field_type::Long: + case field_type::Int24: + case field_type::Float: + assert(param.value->size() == 4); + + break; + case field_type::Short: + case field_type::Year: + assert(param.value->size() == 2); + + break; + case field_type::Tiny: + assert(param.value->size() == 1); + + break; + default: + assert(true || "unknown field-type"); + break; + } + accu.template step>(*param.value); + } + } + } + + accu.step(bw::String(v_.statement())); + return accu.result(); + } + + template + static stdx::expected decode_field_size( + Accu &accu, uint8_t type) { + namespace bw = borrowable::wire; + + switch (type) { + case field_type::Bit: + case field_type::Blob: + case field_type::Varchar: + case field_type::VarString: + case field_type::Set: + case field_type::String: + case field_type::Enum: + case field_type::TinyBlob: + case field_type::MediumBlob: + case field_type::LongBlob: + case field_type::Decimal: + case field_type::Json: + case field_type::NewDecimal: + case field_type::Geometry: { + auto string_field_size_res = accu.template step(); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + + return string_field_size_res->value(); + } + case field_type::Date: + case field_type::DateTime: + case field_type::Timestamp: + case field_type::Time: { + auto time_field_size_res = accu.template step>(); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + + return time_field_size_res->value(); + } + case field_type::LongLong: + case field_type::Double: + return 8; + case field_type::Long: + case field_type::Int24: + case field_type::Float: + return 4; + case field_type::Short: + case field_type::Year: + return 2; + case field_type::Tiny: + return 1; + } + + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } public: @@ -1639,11 +1790,98 @@ class Codec> return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } + std::vector params; + if (caps.test(capabilities::pos::query_attributes)) { + // + auto param_count_res = accu.template step(); + if (!param_count_res) return param_count_res.get_unexpected(); + + // currently always 1. + auto param_set_count_res = accu.template step(); + if (!param_set_count_res) return param_set_count_res.get_unexpected(); + + if (param_set_count_res->value() != 1) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + + const auto param_count = param_count_res->value(); + if (param_count > 0) { + // bit-map + const auto nullbits_res = accu.template step>( + bytes_per_bits(param_count)); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + const auto nullbits = nullbits_res->value(); + + // always 1 + auto new_params_bound_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + + auto new_params_bound = new_params_bound_res->value(); + if (new_params_bound != 1) { + // Always 1, malformed packet error of not 1 + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + + // redundant, but protocol-docs says: + // + // 'if new-params-bind-flag == 1' + // + // therefore keep it. + if (new_params_bound == 1) { + for (long n{}; n < param_count; ++n) { + auto param_type_res = accu.template step>(); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + auto param_name_res = accu.template step>(); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + + params.emplace_back(param_type_res->value(), + param_name_res->value(), + std::optional()); + } + } + + for (long n{}, nullbit_pos{}, nullbyte_pos{}; n < param_count; + ++n, ++nullbit_pos) { + if (nullbit_pos > 7) { + ++nullbyte_pos; + nullbit_pos = 0; + } + + if (!(nullbits[nullbyte_pos] & (1 << nullbit_pos))) { + auto ¶m = params[n]; + + auto field_size_res = + decode_field_size(accu, param.type_and_flags & 0xff); + if (!field_size_res) { + return stdx::make_unexpected(field_size_res.error()); + } + + auto param_value_res = accu.template step>( + field_size_res.value()); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + + param.value = param_value_res->value(); + } + } + } + } + auto statement_res = accu.template step>(); + if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - return std::make_pair(accu.result().value(), - value_type(statement_res->value())); + return std::make_pair( + accu.result().value(), + value_type(statement_res->value(), std::move(params))); } private: @@ -1924,49 +2162,62 @@ class Codec> auto accumulate_fields(Accumulator &&accu) const { namespace bw = borrowable::wire; + auto caps = this->caps(); + accu.step(bw::FixedInt<1>(cmd_byte())) .step(bw::FixedInt<4>(v_.statement_id())) .step(bw::FixedInt<1>(v_.flags().to_ullong())) .step(bw::FixedInt<4>(v_.iteration_count())); - // values.size() and types.size() MUST be the same - if (v_.values().empty()) return accu.result(); + // num-params from the StmtPrepareOk + auto num_params = v_.values().size(); + + const bool supports_query_attributes = + caps.test(capabilities::pos::query_attributes); + + if (supports_query_attributes && + v_.flags().test(cursor::pos::param_count_available)) { + accu.step(bw::VarInt(num_params)); + } + + if (num_params == 0) return accu.result(); // mark all that are NULL in the nullbits // // - one bit per parameter to send // - if a parameter is NULL, the bit is set, and later no value is added. - std::vector nullbits(bytes_per_bits(v_.values().size())); - - { - size_t byte_pos{}, bit_pos{}; - for (auto const &v : v_.values()) { - if (bit_pos > 7) { - bit_pos = 0; - ++byte_pos; - } + uint8_t null_bit_byte{}; + int bit_pos{}; - if (!v) { - nullbits[byte_pos] |= 1 << bit_pos; - } + for (auto const ¶m : v_.values()) { + if (!param.has_value()) null_bit_byte |= 1 << bit_pos; - ++bit_pos; + if (++bit_pos > 7) { + accu.step(bw::FixedInt<1>(null_bit_byte)); + + bit_pos = 0; + null_bit_byte = 0; } } - accu - .step(bw::String(typename bw::String::value_type( - reinterpret_cast(nullbits.data()), nullbits.size()))) - .step(bw::FixedInt<1>(v_.new_params_bound())); + if (bit_pos != 0) accu.step(bw::FixedInt<1>(null_bit_byte)); + + accu.step(bw::FixedInt<1>(v_.new_params_bound())); + if (v_.new_params_bound()) { for (const auto ¶m_def : v_.types()) { accu.step(bw::FixedInt<2>(param_def.type_and_flags)); + + if (supports_query_attributes) { + accu.step(borrowed::wire::VarString(param_def.name)); + } } } for (auto [n, v] : stdx::views::enumerate(v_.values())) { // add all the values that aren't NULL if (!v.has_value()) continue; + // write length of the type is a variable length switch (v_.types()[n].type_and_flags & 0xff) { case field_type::Bit: @@ -2086,6 +2337,11 @@ class Codec> if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + const auto param_count_available{1 << cursor::pos::param_count_available}; + + const bool supports_query_attributes = + caps.test(capabilities::pos::query_attributes); + stdx::expected, std::error_code> metadata_res = metadata_lookup(statement_id_res->value()); if (!metadata_res) { @@ -2093,11 +2349,25 @@ class Codec> make_error_code(codec_errc::statement_id_not_found)); } - const size_t param_count = metadata_res->size(); + size_t param_count = metadata_res->size(); - if (param_count == 0) { - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (supports_query_attributes && + (flags_res->value() & param_count_available) != 0) { + auto param_count_res = accu.template step(); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + + if (static_cast(param_count_res->value()) < param_count) { + // can the param-count shrink? + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + param_count = param_count_res->value(); + } + + if (param_count == 0) { return std::make_pair( accu.result().value(), value_type(statement_id_res->value(), flags_res->value(), @@ -2112,27 +2382,48 @@ class Codec> if (!accu.result()) return stdx::make_unexpected(accu.result().error()); std::vector types; - std::vector> values; auto new_params_bound = new_params_bound_res->value(); if (new_params_bound == 0) { + // no new params, use the last known params. types = *metadata_res; } else if (new_params_bound == 1) { + // check that there is at least enough data for the types (a FixedInt<2>) + // before reserving memory. + if (param_count >= buffer.size() / 2) { + return stdx::make_unexpected( + make_error_code(codec_errc::invalid_input)); + } + types.reserve(param_count); for (size_t n{}; n < param_count; ++n) { auto type_res = accu.template step>(); if (!accu.result()) return stdx::make_unexpected(accu.result().error()); - types.push_back(type_res->value()); + if (supports_query_attributes) { + auto name_res = accu.template step>(); + if (!accu.result()) { + return stdx::make_unexpected(accu.result().error()); + } + types.emplace_back(type_res->value(), name_res->value()); + } else { + types.emplace_back(type_res->value()); + } } } else { return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); } - const auto nullbits = nullbits_res->value(); + if (param_count != types.size()) { + // param-count and available types doesn't match. + return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + } + + std::vector> values; values.reserve(param_count); + const auto nullbits = nullbits_res->value(); for (size_t n{}, bit_pos{}, byte_pos{}; n < param_count; ++n, ++bit_pos) { if (bit_pos > 7) { bit_pos = 0; @@ -2204,6 +2495,7 @@ class Codec> values.push_back(value_res->value()); } else { + // NULL values.emplace_back(std::nullopt); } } @@ -2624,8 +2916,8 @@ class Codec> auto client_capabilities = classic_protocol::capabilities::value_type( capabilities_lo_res->value()); - // decoding depends on the capabilities that both client and server have in - // common + // decoding depends on the capabilities that both client and server have + // in common auto shared_capabilities = caps & client_capabilities; if (shared_capabilities[classic_protocol::capabilities::pos::protocol_41]) { diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index b76240c..ac00ba7 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -140,6 +140,7 @@ constexpr value_type compress_zstd{1 << pos::compress_zstd}; // version_added: 8.0 constexpr value_type optional_resultset_metadata{ 1 << pos::optional_resultset_metadata}; +// version_added: 8.0 constexpr value_type query_attributes{1 << pos::query_attributes}; } // namespace capabilities @@ -212,8 +213,9 @@ constexpr value_type no_cursor{0}; constexpr value_type read_only{1}; constexpr value_type for_update{2}; constexpr value_type scrollable{3}; +constexpr value_type param_count_available{4}; -constexpr value_type _bitset_size{scrollable + 1}; +constexpr value_type _bitset_size{param_count_available + 1}; } // namespace pos using value_type = std::bitset; @@ -221,6 +223,7 @@ constexpr value_type no_cursor{1 << pos::no_cursor}; constexpr value_type read_only{1 << pos::read_only}; constexpr value_type for_update{1 << pos::for_update}; constexpr value_type scrollable{1 << pos::scrollable}; +constexpr value_type param_count_available{1 << pos::param_count_available}; } // namespace cursor namespace field_type { diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index b021e37..598bd82 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -773,17 +773,33 @@ class Query { using string_type = std::conditional_t; + struct Param { + uint16_t type_and_flags; + string_type name; + std::optional value; + + Param(uint16_t t, string_type n, std::optional v) + : type_and_flags(t), name(std::move(n)), value(std::move(v)) {} + }; + + Query(string_type statement) : statement_{std::move(statement)} {} + /** - * construct a Query message. + * construct a Query message with values. * - * @param statement statement to prepare + * @param statement statement to query + * @param values values for statement */ - constexpr Query(string_type statement) : statement_{std::move(statement)} {} + Query(string_type statement, std::vector values) + : statement_{std::move(statement)}, values_(std::move(values)) {} constexpr string_type statement() const { return statement_; } + std::vector values() const { return values_; } + private: string_type statement_; + std::vector values_; }; template @@ -1068,11 +1084,15 @@ class StmtExecute { ParamDef(uint16_t type_and_flags_) : type_and_flags(type_and_flags_) {} + ParamDef(uint16_t type_and_flags_, string_type name_) + : type_and_flags(type_and_flags_), name(std::move(name_)) {} + friend bool operator==(const ParamDef &lhs, const ParamDef &rhs) { - return lhs.type_and_flags == rhs.type_and_flags; + return lhs.type_and_flags == rhs.type_and_flags && lhs.name == rhs.name; } uint16_t type_and_flags{}; + string_type name; }; /** From f95765f22333427d166c9ac0e934d400247254e9 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 21 Apr 2023 20:41:46 +0200 Subject: [PATCH 66/88] Bug#35318781 parameter_count_available has the wrong value Decoding StmtExecute with the parameter-count-available flag set fails. This is only noticed if the tracer is enabled at build-time as the StmtExecute message isn't decoded by default. The routertest_integration_routing_direct fails due to this if the tracer is enabled. Change ------ - fixed values of the StmtExecute flags which were off-by-one Change-Id: Ib31d01150ef3e925218af7834dddb720be43c2fa --- mysqlrouter/classic_protocol_constants.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index ac00ba7..53e381b 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -209,17 +209,16 @@ constexpr value_type session_state_changed{1 << pos::session_state_changed}; namespace cursor { namespace pos { using value_type = uint8_t; -constexpr value_type no_cursor{0}; -constexpr value_type read_only{1}; -constexpr value_type for_update{2}; -constexpr value_type scrollable{3}; -constexpr value_type param_count_available{4}; +constexpr value_type read_only{0}; +constexpr value_type for_update{1}; +constexpr value_type scrollable{2}; +constexpr value_type param_count_available{3}; constexpr value_type _bitset_size{param_count_available + 1}; } // namespace pos using value_type = std::bitset; -constexpr value_type no_cursor{1 << pos::no_cursor}; +constexpr value_type no_cursor{0}; constexpr value_type read_only{1 << pos::read_only}; constexpr value_type for_update{1 << pos::for_update}; constexpr value_type scrollable{1 << pos::scrollable}; From 3676baedea786bcc34b7e55790498280b3155b84 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 4 Apr 2023 15:23:48 +0200 Subject: [PATCH 67/88] Bug#35318797 decode StmtExecute after StmtParamAppendData fails Sending StmtParamAppendData (aka COM_STMT_SEND_LONG_DATA) before a StmtExecute currently fails (which is only visibible if the tracer is enabled.) StmtParamAppendData allows to send data to a prepared-statement parameter before the statement is executed. Parameters of a statement which already filled with StmtParamAppendData do not send data in StmtExecute. This requires tracking the StmtParamAppendData when StmtExecute is called. Change ------ - track which parameters of a statement already got data via StmtParamAppendData Change-Id: I97ad0bc53da124b13fe00eef725852a0d39cb300 --- mysqlrouter/classic_protocol_codec_message.h | 18 ++++++++++++++---- mysqlrouter/classic_protocol_message.h | 11 +++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index feabf47..1f72371 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -2408,7 +2408,7 @@ class Codec> } types.emplace_back(type_res->value(), name_res->value()); } else { - types.emplace_back(type_res->value()); + types.emplace_back(type_res->value(), ""); } } } else { @@ -2430,7 +2430,15 @@ class Codec> ++byte_pos; } - if (!(nullbits[byte_pos] & (1 << bit_pos))) { + // if the data was sent via COM_STMT_SEND_LONG_DATA, there will be no data + // for + const auto param_already_sent = + n < metadata_res->size() ? (*metadata_res)[n].param_already_sent + : false; + + if (param_already_sent) { + values.emplace_back(""); // empty + } else if (!(nullbits[byte_pos] & (1 << bit_pos))) { stdx::expected field_size_res( stdx::make_unexpected( make_error_code(std::errc::invalid_argument))); @@ -2449,8 +2457,9 @@ class Codec> case field_type::NewDecimal: case field_type::Geometry: { auto string_field_size_res = accu.template step(); - if (!accu.result()) + if (!accu.result()) { return stdx::make_unexpected(accu.result().error()); + } field_size_res = string_field_size_res->value(); } break; @@ -2459,8 +2468,9 @@ class Codec> case field_type::Timestamp: case field_type::Time: { auto time_field_size_res = accu.template step>(); - if (!accu.result()) + if (!accu.result()) { return stdx::make_unexpected(accu.result().error()); + } field_size_res = time_field_size_res->value(); } break; diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 598bd82..548471c 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1084,15 +1084,22 @@ class StmtExecute { ParamDef(uint16_t type_and_flags_) : type_and_flags(type_and_flags_) {} - ParamDef(uint16_t type_and_flags_, string_type name_) - : type_and_flags(type_and_flags_), name(std::move(name_)) {} + ParamDef(uint16_t type_and_flags_, string_type name_, + bool param_already_sent_ = false) + : type_and_flags(type_and_flags_), + name(std::move(name_)), + param_already_sent(param_already_sent_) {} friend bool operator==(const ParamDef &lhs, const ParamDef &rhs) { return lhs.type_and_flags == rhs.type_and_flags && lhs.name == rhs.name; } uint16_t type_and_flags{}; + string_type name; + + // param already send via stmt_param_append_data + bool param_already_sent{false}; }; /** From 8f52abb6e801ddabc6f652063442f66861c8ec50 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 28 Apr 2023 09:26:26 +0200 Subject: [PATCH 68/88] Bug#35340772 server::Greeting misses setter The server::Greeting message is usually built by constructing via the decoder. It has getters for all fields, but only setters for some of them. Change ------ - add missing setters - add unit-test for all setters Change-Id: I36c665b70408cf567474bf61de8585f7934d1ee8 --- mysqlrouter/classic_protocol_message.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 598bd82..f0bbbc9 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -137,9 +137,16 @@ class Greeting { constexpr uint8_t protocol_version() const noexcept { return protocol_version_; } + void protocol_version(uint8_t val) { protocol_version_ = val; } + constexpr string_type version() const { return version_; } + void version(string_type val) { version_ = val; } + constexpr string_type auth_method_name() const { return auth_method_name_; } + void auth_method_name(string_type val) { auth_method_name_ = val; } constexpr string_type auth_method_data() const { return auth_method_data_; } + void auth_method_data(string_type val) { auth_method_data_ = val; } + classic_protocol::capabilities::value_type capabilities() const noexcept { return capabilities_; } @@ -149,10 +156,17 @@ class Greeting { } constexpr uint8_t collation() const noexcept { return collation_; } + void collation(uint8_t val) { collation_ = val; } + constexpr classic_protocol::status::value_type status_flags() const noexcept { return status_flags_; } + void status_flags(classic_protocol::status::value_type val) { + status_flags_ = val; + } + constexpr uint32_t connection_id() const noexcept { return connection_id_; } + void connection_id(uint32_t val) { connection_id_ = val; } private: uint8_t protocol_version_; From e28410f8dc330676b8c007427a718941281674a4 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Tue, 9 May 2023 09:48:08 +0200 Subject: [PATCH 69/88] Bug#35373628 mock-server can not set session-tracker from script The Router's mock-server sends static session trackers, just enough for the routertest_component_routing_sharing. Change ------ - allow set trackers as part of Ok/Result packets - allow set all valid trackers - added iterator over session-trackers to MysqlClient - added tests Change-Id: Ib1ae1986da35d8168e172a44736bf9f4a4131923 --- mysqlrouter/classic_protocol_session_track.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index a0d7444..e775287 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -29,6 +29,8 @@ #include +#include "mysql/harness/stdx/span.h" + namespace classic_protocol { namespace borrowable { @@ -205,6 +207,16 @@ class TransactionState { resultset_{resultset}, locked_tables_{locked_tables} {} + constexpr TransactionState(stdx::span val) + : trx_type_{val[0]}, + read_unsafe_{val[1]}, + read_trx_{val[2]}, + write_unsafe_{val[3]}, + write_trx_{val[4]}, + stmt_unsafe_{val[5]}, + resultset_{val[6]}, + locked_tables_{val[7]} {} + constexpr char trx_type() const noexcept { return trx_type_; } constexpr char read_unsafe() const noexcept { return read_unsafe_; } constexpr char read_trx() const noexcept { return read_trx_; } From cd29dc4c8e5774334a5aefdbc2b8bace35772e37 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 2 Jun 2023 13:53:10 +0200 Subject: [PATCH 70/88] Bug#35463262 mysql_dump_debug_info() fails through router When calling mysql_dump_debug_info() generate a dump of the debug info on the mysql-server, the command fails with an error from the router. Change ------ - forward COM_DEBUG to the server. Change-Id: Id8565a70d7ca4f39b081953ff99838b7d2ca0161 --- mysqlrouter/classic_protocol_codec_message.h | 18 ++++++++++++++++++ mysqlrouter/classic_protocol_message.h | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 1f72371..2521a4f 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1543,6 +1543,24 @@ class Codec } }; +/** + * codec for client's Debug command. + */ +template <> +class Codec + : public CodecSimpleCommand, + borrowable::message::client::Debug> { + public: + using value_type = borrowable::message::client::Debug; + using __base = CodecSimpleCommand, value_type>; + + constexpr Codec(value_type, capabilities::value_type caps) : __base(caps) {} + + constexpr static uint8_t cmd_byte() noexcept { + return static_cast(CommandByte::Debug); + } +}; + /** * codec for client's InitSchema command. */ diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 44dca6f..2e1f3ef 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1263,6 +1263,11 @@ class Ping {}; constexpr bool operator==(const Ping &, const Ping &) { return true; } +// no content +class Debug {}; + +constexpr bool operator==(const Debug &, const Debug &) { return true; } + template class AuthMethodData { public: @@ -1424,6 +1429,7 @@ using ListFields = borrowable::message::client::ListFields; using Query = borrowable::message::client::Query; using RegisterReplica = borrowable::message::client::RegisterReplica; using Ping = borrowable::message::client::Ping; +using Debug = borrowable::message::client::Debug; using Kill = borrowable::message::client::Kill; using ChangeUser = borrowable::message::client::ChangeUser; using Reload = borrowable::message::client::Reload; @@ -1471,6 +1477,7 @@ using InitSchema = borrowable::message::client::InitSchema; using ListFields = borrowable::message::client::ListFields; using RegisterReplica = borrowable::message::client::RegisterReplica; using Ping = borrowable::message::client::Ping; +using Debug = borrowable::message::client::Debug; using Kill = borrowable::message::client::Kill; using ChangeUser = borrowable::message::client::ChangeUser; using Reload = borrowable::message::client::Reload; From 29318c2bd453d0e6e47f92be22c69f4f4ece9403 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 7 Jun 2023 11:00:08 +0200 Subject: [PATCH 71/88] Bug#35472467 build failure with gcc-12 on EL8 build fails with: mysql_protocol/include/mysqlrouter/classic_protocol_codec_binary.h:277:11: error: memcpy was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] 277 | memcpy(&val, value_res->value().data(), byte_size); | ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Change ------ - include cstring Change-Id: I936946770ee38dd4358624902f095e9f6038d502 --- mysqlrouter/classic_protocol_codec_binary.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index a2717ce..98926b9 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -27,6 +27,7 @@ #include // size_t #include // uint8_t +#include // memcpy #include // error_code #include // move From 288b7c22c3b48a8806e4c3b6cb59f43c2640a2a5 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 18 May 2023 17:02:03 +0200 Subject: [PATCH 72/88] Bug#35468897 connection-sharing does not work with PHP [2/5] To determine if a message can be forwarded as is, the forwarder needs to know if the destination would encode the message in the same way as the source. Like, a COM_PING looks the same no matter what the negotiated capabilities look like. But a Eof packet depends on several capabilities. Change ------ Declare which protocol capabilities influence the protocol message's codec. Change-Id: I30b05c544ae45915e9c2c3fb5040d40e17362b73 --- mysqlrouter/classic_protocol_codec_message.h | 230 +++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 2521a4f..ce11f4d 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -268,6 +268,13 @@ class Codec> static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::plugin_auth; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -327,6 +334,13 @@ class Codec> static constexpr uint8_t cmd_byte() noexcept { return 0x01; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -397,6 +411,14 @@ class Codec> static constexpr uint8_t cmd_byte() noexcept { return 0x00; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::session_track | capabilities::transactions | + capabilities::protocol_41; + } + /** * decode a server::Ok message from a buffer-sequence. * @@ -546,6 +568,15 @@ class Codec> static constexpr uint8_t cmd_byte() noexcept { return 0xfe; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::text_result_with_session_tracking | + capabilities::session_track | capabilities::transactions | + capabilities::protocol_41; + } + /** * decode a server::Eof message from a buffer-sequence. * @@ -690,6 +721,13 @@ class Codec> static constexpr uint8_t cmd_byte() { return 0xff; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::protocol_41; + } + static constexpr size_t max_size() noexcept { return std::numeric_limits::max(); } @@ -976,6 +1014,13 @@ class Codec> static constexpr uint8_t cmd_byte() noexcept { return 0xfb; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -1051,6 +1096,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return 0x00; } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::optional_resultset_metadata; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -1486,6 +1538,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::Quit); } + + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } }; /** @@ -1505,6 +1564,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::ResetConnection); } + + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } }; /** @@ -1523,6 +1589,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::Ping); } + + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } }; /** @@ -1541,6 +1614,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::Statistics); } + + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } }; /** @@ -1559,6 +1639,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::Debug); } + + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } }; /** @@ -1590,6 +1677,13 @@ class Codec> return static_cast(CommandByte::InitSchema); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -1795,6 +1889,13 @@ class Codec> return static_cast(CommandByte::Query); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::query_attributes; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -1935,6 +2036,13 @@ class Codec> constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -1982,6 +2090,13 @@ class Codec> return static_cast(CommandByte::ListFields); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2036,6 +2151,13 @@ class Codec return static_cast(CommandByte::Refresh); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2092,6 +2214,13 @@ class Codec return static_cast(CommandByte::ProcessKill); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2145,6 +2274,13 @@ class Codec> return static_cast(CommandByte::StmtPrepare); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2291,6 +2427,13 @@ class Codec> return static_cast(CommandByte::StmtExecute); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::query_attributes; + } + /** * decode a buffer into a message::client::StmtExecute. * @@ -2572,6 +2715,13 @@ class Codec> return static_cast(CommandByte::StmtSendLongData); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2627,6 +2777,13 @@ class Codec return static_cast(CommandByte::StmtClose); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2679,6 +2836,13 @@ class Codec return static_cast(CommandByte::StmtReset); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2731,6 +2895,13 @@ class Codec return static_cast(CommandByte::SetOption); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2784,6 +2955,13 @@ class Codec return static_cast(CommandByte::StmtFetch); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -2931,6 +3109,15 @@ class Codec> constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::secure_connection | capabilities::protocol_41 | + capabilities::ssl | capabilities::client_auth_method_data_varint | + capabilities::connect_attributes | capabilities::connect_with_schema; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -3134,6 +3321,13 @@ class Codec> constexpr Codec(value_type v, capabilities::value_type caps) : __base(caps), v_{std::move(v)} {} + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -3214,6 +3408,14 @@ class Codec> return static_cast(CommandByte::ChangeUser); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return capabilities::secure_connection | capabilities::plugin_auth | + capabilities::connect_attributes; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -3307,6 +3509,13 @@ class Codec constexpr static uint8_t cmd_byte() noexcept { return static_cast(CommandByte::Clone); } + + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } }; /** @@ -3344,6 +3553,13 @@ class Codec> return static_cast(CommandByte::BinlogDump); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -3418,6 +3634,13 @@ class Codec> return static_cast(CommandByte::RegisterReplica); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); @@ -3509,6 +3732,13 @@ class Codec> return static_cast(CommandByte::BinlogDumpGtid); } + /** + * capabilities the codec depends on. + */ + static constexpr capabilities::value_type depends_on_capabilities() noexcept { + return {}; + } + static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps) { impl::DecodeBufferAccumulator accu(buffer, caps); From e891bdb86c664f9d76389af6c41f1317f96583dd Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 7 Jun 2023 11:00:08 +0200 Subject: [PATCH 73/88] Bug#35472467 build failure with gcc-12 on EL8 build fails with: mysql_protocol/include/mysqlrouter/classic_protocol_codec_binary.h:277:11: error: memcpy was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] 277 | memcpy(&val, value_res->value().data(), byte_size); | ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Change ------ - include cstring Change-Id: I936946770ee38dd4358624902f095e9f6038d502 --- mysqlrouter/classic_protocol_codec_binary.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index a2717ce..98926b9 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -27,6 +27,7 @@ #include // size_t #include // uint8_t +#include // memcpy #include // error_code #include // move From 565a68dedab9a49a63b00f87a8db492b142e4c26 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 15 Nov 2023 11:12:57 +0100 Subject: [PATCH 74/88] Bug#36018328 replace stdx::endian with std::endian Since the code-base is C++20, the "backported to C++17" stdx::endian can be replaced by C++20's std::endian. Change ====== - replace stdx::endian by std::endian Change-Id: Ie65673a1cf643e8da4335879b5ee863e75e1a626 --- mysqlrouter/classic_protocol_codec_wire.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 1423974..af411de 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -28,6 +28,7 @@ // codecs for classic_protocol::wire:: #include // find +#include // endian #include // size_t #include // uint8_t #include // error_code @@ -35,8 +36,8 @@ #include // move #include "mysql/harness/net_ts/buffer.h" +#include "mysql/harness/stdx/bit.h" // byteswap #include "mysql/harness/stdx/expected.h" -#include "mysql/harness/stdx/type_traits.h" // endian #include "mysqlrouter/classic_protocol_codec_base.h" #include "mysqlrouter/classic_protocol_codec_error.h" #include "mysqlrouter/classic_protocol_wire.h" @@ -72,7 +73,7 @@ class Codec> { auto int_val = v_.value(); - if (stdx::endian::native == stdx::endian::big) { + if (std::endian::native == std::endian::big) { int_val = stdx::byteswap(int_val); } @@ -100,7 +101,7 @@ class Codec> { std::copy_n(static_cast(buffer.data()), int_size, reinterpret_cast(&value)); - if (stdx::endian::native == stdx::endian::big) { + if (std::endian::native == std::endian::big) { value = stdx::byteswap(value); } From a95dbd18cf8e2f8a09330e0d08e69428c49715d2 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Wed, 15 Nov 2023 13:54:21 +0100 Subject: [PATCH 75/88] Bug#36018362 replace stdx::span with std::span Since the code-base is C++20, the "backported to C++17" stdx::span can be replaced by C++20's std::span. Change ====== - replaced stdx::span by std::span - removed mysql/harness/span.h Change-Id: I3dd1b02138b1243d752c1aa4963c89db9e160e29 --- mysqlrouter/classic_protocol_session_track.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index e775287..cea3092 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -27,10 +27,9 @@ // session_track as used by message::server::Ok and message::server::Eof +#include #include -#include "mysql/harness/stdx/span.h" - namespace classic_protocol { namespace borrowable { @@ -207,7 +206,7 @@ class TransactionState { resultset_{resultset}, locked_tables_{locked_tables} {} - constexpr TransactionState(stdx::span val) + constexpr TransactionState(std::span val) : trx_type_{val[0]}, read_unsafe_{val[1]}, read_trx_{val[2]}, From a1c4dcac4523325edcf153a6478d4a29fdefbc16 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 16 Nov 2023 12:02:16 +0100 Subject: [PATCH 76/88] Bug#36018398 remove std::expected::get_unexpected stdx::expected's get_unexpected was part of the early proposals, but has been drop from the C++23 spec. Change ====== - replace res.get_unexpected() by stdx::unexpected(res.error()) Change-Id: Idadf1c6aab5f02e1a001c6fc82aa48a0000c435a --- mysqlrouter/classic_protocol_codec_message.h | 7 ++++--- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index ce11f4d..9f18659 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1913,11 +1913,12 @@ class Codec> if (caps.test(capabilities::pos::query_attributes)) { // auto param_count_res = accu.template step(); - if (!param_count_res) return param_count_res.get_unexpected(); + if (!param_count_res) return stdx::unexpected(param_count_res.error()); // currently always 1. auto param_set_count_res = accu.template step(); - if (!param_set_count_res) return param_set_count_res.get_unexpected(); + if (!param_set_count_res) + return stdx::unexpected(param_set_count_res.error()); if (param_set_count_res->value() != 1) { return stdx::make_unexpected( @@ -2050,7 +2051,7 @@ class Codec> namespace bw = borrowable::wire; auto payload_res = accu.template step>(); - if (!accu.result()) return accu.result().get_unexpected(); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(payload_res->value())); diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index e47aeb3..e121095 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -405,7 +405,7 @@ class Codec> auto spec_res = accu.template step>(); auto gtid_res = accu.template step>(); - if (!accu.result()) return accu.result().get_unexpected(); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(spec_res->value(), gtid_res->value())); From f8ac5be7dba9bd9dcda163fa194d0375dcaf090c Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Thu, 16 Nov 2023 14:16:35 +0100 Subject: [PATCH 77/88] Bug#36018408 replace stdx::make_unexpected() by std::unexpected() make_unexpected() was removed from std::expected in favour of the deduction guides which were introduced in C++17. Change ====== - replace uses of stdx::make_unexpected() by stdx::unexpected() - remove stdx::make_unexpected() Change-Id: Ic56ee31087c0310decfb6cca0c09a5009364fb3a --- mysqlrouter/classic_protocol_codec_base.h | 11 +- mysqlrouter/classic_protocol_codec_binary.h | 16 +- mysqlrouter/classic_protocol_codec_clone.h | 18 +- mysqlrouter/classic_protocol_codec_frame.h | 10 +- mysqlrouter/classic_protocol_codec_message.h | 312 +++++++++--------- .../classic_protocol_codec_session_track.h | 16 +- mysqlrouter/classic_protocol_codec_wire.h | 31 +- 7 files changed, 198 insertions(+), 216 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 1065e59..132147e 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -183,12 +183,12 @@ class DecodeBufferAccumulator { template stdx::expected::value_type, std::error_code> step( size_t sz = std::numeric_limits::max()) { - if (!res_) return stdx::make_unexpected(res_.error()); + if (!res_) return stdx::unexpected(res_.error()); auto step_res = step_(sz); // capture the first failure - if (!step_res) res_ = stdx::make_unexpected(step_res.error()); + if (!step_res) res_ = stdx::unexpected(step_res.error()); return step_res; } @@ -203,7 +203,7 @@ class DecodeBufferAccumulator { template stdx::expected::value_type, std::error_code> try_step( size_t sz = std::numeric_limits::max()) { - if (!res_) return stdx::make_unexpected(res_.error()); + if (!res_) return stdx::unexpected(res_.error()); return step_(sz); } @@ -230,13 +230,12 @@ class DecodeBufferAccumulator { if (sz != std::numeric_limits::max()) { // not enough data. if (buf.size() < sz) { - return stdx::make_unexpected( - make_error_code(codec_errc::not_enough_input)); + return stdx::unexpected(make_error_code(codec_errc::not_enough_input)); } } auto decode_res = Codec::decode(net::buffer(buf, sz), caps_); - if (!decode_res) return stdx::make_unexpected(decode_res.error()); + if (!decode_res) return stdx::unexpected(decode_res.error()); consumed_ += decode_res->first; return decode_res->second; diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index 98926b9..807d65d 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -217,7 +217,7 @@ class FixedIntCodec : public impl::EncodeBase> { impl::DecodeBufferAccumulator accu(buffer, caps); auto value_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); @@ -271,7 +271,7 @@ class FloatCodec : public impl::EncodeBase> { impl::DecodeBufferAccumulator accu(buffer, caps); auto value_res = accu.template step(byte_size); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); typename T::value_type val; @@ -311,7 +311,7 @@ class StringCodec : public impl::EncodeBase> { impl::DecodeBufferAccumulator accu(buffer, caps); auto value_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); @@ -370,7 +370,7 @@ class DatetimeCodec : public impl::EncodeBase> { auto month_res = accu.template step>(); auto day_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto hour_res = accu.template try_step>(); if (!hour_res) { @@ -381,7 +381,7 @@ class DatetimeCodec : public impl::EncodeBase> { auto minute_res = accu.template step>(); auto second_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto microsecond_res = accu.template try_step>(); if (!microsecond_res) { @@ -392,7 +392,7 @@ class DatetimeCodec : public impl::EncodeBase> { second_res->value())); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -451,7 +451,7 @@ class TimeCodec : public impl::EncodeBase> { auto microsecond_res = accu.template try_step>(); if (!microsecond_res) { - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -460,7 +460,7 @@ class TimeCodec : public impl::EncodeBase> { second_res->value())); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 2bc52fd..cffc3cd 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -81,14 +81,14 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto protocol_version_res = accu.template step>(); auto ddl_timeout_res = accu.template step>(); // TODO(jkneschk): if there is more data, 1-or-more Locators - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -126,7 +126,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -162,7 +162,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -198,7 +198,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -234,7 +234,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -270,7 +270,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -336,7 +336,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } @@ -372,7 +372,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type()); } diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 72a70b0..3719c27 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -70,7 +70,7 @@ class Codec : public impl::EncodeBase> { auto payload_size_res = accu.template step>(); auto seq_id_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -114,7 +114,7 @@ class Codec auto seq_id_res = accu.template step>(); auto uncompressed_size_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -160,20 +160,20 @@ class Codec> impl::DecodeBufferAccumulator accu(buffer, caps); auto header_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); constexpr const size_t header_size{Codec::max_size()}; // check the payload is at least what we expect. if (buffer.size() < header_size + header_res->payload_size()) { - return stdx::make_unexpected( + return stdx::unexpected( make_error_code(classic_protocol::codec_errc::not_enough_input)); } auto payload_res = accu.template step(header_res->payload_size()); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 9f18659..60e8ffd 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -141,11 +141,11 @@ class Codec> // proto-version auto protocol_version_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (protocol_version_res->value() != 0x09 && protocol_version_res->value() != 0x0a) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto version_res = accu.template step>(); @@ -163,7 +163,7 @@ class Codec> // capabilities are split into two a lower-2-byte part and a // higher-2-byte auto cap_lower_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); // 3.21.x doesn't send more. if (buffer_size(buffer) <= accu.result().value()) { @@ -181,7 +181,7 @@ class Codec> auto cap_hi_res = accu.template step>(); // before we use cap_hi|cap_low check they don't have an error - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); classic_protocol::capabilities::value_type capabilities( cap_lower_res->value() | (cap_hi_res->value() << 16)); @@ -189,12 +189,11 @@ class Codec> size_t auth_method_data_len{13}; if (capabilities[classic_protocol::capabilities::pos::plugin_auth]) { auto auth_method_data_len_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); // should be 21, but least 8 if (auth_method_data_len_res->value() < 8) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auth_method_data_len = auth_method_data_len_res->value() - 8; } else { @@ -219,7 +218,7 @@ class Codec> } } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -283,10 +282,10 @@ class Codec> // proto-version auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } if (!caps[classic_protocol::capabilities::pos::plugin_auth]) { @@ -296,7 +295,7 @@ class Codec> auto auth_method_res = accu.template step>(); auto auth_method_data_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -348,14 +347,14 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto auth_method_data_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(auth_method_data_res->value())); @@ -441,10 +440,10 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto affected_rows_res = accu.template step(); @@ -480,7 +479,7 @@ class Codec> message_res = accu.template step>(); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -603,10 +602,10 @@ class Codec> namespace bw = borrowable::wire; const auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } if (caps[capabilities::pos::text_result_with_session_tracking]) { @@ -645,7 +644,7 @@ class Codec> message_res = accu.template step>(); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -739,10 +738,10 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } // decode all fields, check result later before they are used. @@ -754,7 +753,7 @@ class Codec> } auto message_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -799,7 +798,7 @@ class Codec namespace bw = borrowable::wire; auto count_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(count_res->value())); @@ -889,29 +888,26 @@ class Codec> const auto name_res = accu.template step>(); const auto column_length_len_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (column_length_len_res->value() != 3) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } const auto column_length_res = accu.template step>(); const auto type_len_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (type_len_res->value() != 1) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } const auto type_res = accu.template step>(); const auto flags_and_decimals_len_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (flags_and_decimals_len_res->value() != flags_size + 1) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } stdx::expected, std::error_code> flags_and_decimals_res( @@ -927,7 +923,7 @@ class Codec> } } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); const uint16_t flags = flags_and_decimals_res->value() & ((1 << (flags_size * 8)) - 1); @@ -952,8 +948,7 @@ class Codec> const auto other_len_res = accu.template step(); if (other_len_res->value() != 12) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } const auto collation_res = accu.template step>(); @@ -964,7 +959,7 @@ class Codec> accu.template step(2); // fillers - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -1028,14 +1023,14 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto filename_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(filename_res->value())); @@ -1126,7 +1121,7 @@ class Codec } } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -1191,13 +1186,13 @@ class Codec> fields.emplace_back(std::nullopt); } else { auto field_res = accu.template step>(); - if (!field_res) return stdx::make_unexpected(field_res.error()); + if (!field_res) return stdx::unexpected(field_res.error()); fields.emplace_back(field_res->value()); } } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(fields)); } @@ -1310,16 +1305,16 @@ class Codec> impl::DecodeBufferAccumulator accu(buffer, caps); const auto row_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); // first byte is 0x00 if (row_byte_res->value() != 0x00) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } const auto nullbits_res = accu.template step>(bytes_per_bits(types.size())); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); const auto nullbits = nullbits_res->value(); @@ -1333,8 +1328,7 @@ class Codec> if (!(nullbits[byte_pos] & (1 << bit_pos))) { stdx::expected field_size_res( - stdx::make_unexpected( - make_error_code(std::errc::invalid_argument))); + stdx::unexpected(make_error_code(std::errc::invalid_argument))); switch (types[n]) { case field_type::Bit: case field_type::Blob: @@ -1350,8 +1344,7 @@ class Codec> case field_type::NewDecimal: case field_type::Geometry: { auto string_field_size_res = accu.template step(); - if (!accu.result()) - return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); field_size_res = string_field_size_res->value(); } break; @@ -1360,8 +1353,7 @@ class Codec> case field_type::Timestamp: case field_type::Time: { auto time_field_size_res = accu.template step>(); - if (!accu.result()) - return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); field_size_res = time_field_size_res->value(); } break; @@ -1384,13 +1376,13 @@ class Codec> } if (!field_size_res) { - return stdx::make_unexpected( + return stdx::unexpected( make_error_code(codec_errc::field_type_unknown)); } const auto value_res = accu.template step>(field_size_res.value()); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); values.push_back(value_res->value()); } else { @@ -1398,7 +1390,7 @@ class Codec> } } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(types, values)); } @@ -1438,7 +1430,7 @@ class Codec> auto stats_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(stats_res->value())); @@ -1477,10 +1469,10 @@ class CodecSimpleCommand namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != Base::cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } return std::make_pair(accu.result().value(), ValueType()); @@ -1691,14 +1683,14 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto schema_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(schema_res->value())); @@ -1843,7 +1835,7 @@ class Codec> case field_type::Geometry: { auto string_field_size_res = accu.template step(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } return string_field_size_res->value(); @@ -1854,7 +1846,7 @@ class Codec> case field_type::Time: { auto time_field_size_res = accu.template step>(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } return time_field_size_res->value(); @@ -1873,7 +1865,7 @@ class Codec> return 1; } - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } public: @@ -1903,10 +1895,10 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } std::vector params; @@ -1921,8 +1913,7 @@ class Codec> return stdx::unexpected(param_set_count_res.error()); if (param_set_count_res->value() != 1) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } const auto param_count = param_count_res->value(); @@ -1930,19 +1921,18 @@ class Codec> // bit-map const auto nullbits_res = accu.template step>( bytes_per_bits(param_count)); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); const auto nullbits = nullbits_res->value(); // always 1 auto new_params_bound_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto new_params_bound = new_params_bound_res->value(); if (new_params_bound != 1) { // Always 1, malformed packet error of not 1 - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } // redundant, but protocol-docs says: @@ -1954,11 +1944,11 @@ class Codec> for (long n{}; n < param_count; ++n) { auto param_type_res = accu.template step>(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } auto param_name_res = accu.template step>(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } params.emplace_back(param_type_res->value(), @@ -1980,13 +1970,13 @@ class Codec> auto field_size_res = decode_field_size(accu, param.type_and_flags & 0xff); if (!field_size_res) { - return stdx::make_unexpected(field_size_res.error()); + return stdx::unexpected(field_size_res.error()); } auto param_value_res = accu.template step>( field_size_res.value()); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } param.value = param_value_res->value(); @@ -1997,7 +1987,7 @@ class Codec> auto statement_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -2105,15 +2095,15 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto table_name_res = accu.template step>(); auto wildcard_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -2166,14 +2156,14 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto cmds_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(cmds_res->value())); } @@ -2229,14 +2219,14 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto connection_id_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(connection_id_res->value())); @@ -2289,14 +2279,14 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto statement_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(statement_res->value())); @@ -2472,7 +2462,7 @@ class Codec> * if (found) { * return {}; * } else { - * return stdx::make_unexpected(make_error_code( + * return stdx::unexpected(make_error_code( * codec_errc::statement_id_not_found)); * } * }); @@ -2487,17 +2477,17 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto statement_id_res = accu.template step>(); auto flags_res = accu.template step>(); auto iteration_count_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); const auto param_count_available{1 << cursor::pos::param_count_available}; @@ -2507,7 +2497,7 @@ class Codec> stdx::expected, std::error_code> metadata_res = metadata_lookup(statement_id_res->value()); if (!metadata_res) { - return stdx::make_unexpected( + return stdx::unexpected( make_error_code(codec_errc::statement_id_not_found)); } @@ -2517,13 +2507,12 @@ class Codec> (flags_res->value() & param_count_available) != 0) { auto param_count_res = accu.template step(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } if (static_cast(param_count_res->value()) < param_count) { // can the param-count shrink? - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } param_count = param_count_res->value(); @@ -2538,10 +2527,10 @@ class Codec> auto nullbits_res = accu.template step>(bytes_per_bits(param_count)); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto new_params_bound_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); std::vector types; @@ -2553,20 +2542,19 @@ class Codec> // check that there is at least enough data for the types (a FixedInt<2>) // before reserving memory. if (param_count >= buffer.size() / 2) { - return stdx::make_unexpected( - make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } types.reserve(param_count); for (size_t n{}; n < param_count; ++n) { auto type_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (supports_query_attributes) { auto name_res = accu.template step>(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } types.emplace_back(type_res->value(), name_res->value()); } else { @@ -2574,12 +2562,12 @@ class Codec> } } } else { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } if (param_count != types.size()) { // param-count and available types doesn't match. - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } std::vector> values; @@ -2602,8 +2590,7 @@ class Codec> values.emplace_back(""); // empty } else if (!(nullbits[byte_pos] & (1 << bit_pos))) { stdx::expected field_size_res( - stdx::make_unexpected( - make_error_code(std::errc::invalid_argument))); + stdx::unexpected(make_error_code(std::errc::invalid_argument))); switch (types[n].type_and_flags & 0xff) { case field_type::Bit: case field_type::Blob: @@ -2620,7 +2607,7 @@ class Codec> case field_type::Geometry: { auto string_field_size_res = accu.template step(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } field_size_res = string_field_size_res->value(); @@ -2631,7 +2618,7 @@ class Codec> case field_type::Time: { auto time_field_size_res = accu.template step>(); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } field_size_res = time_field_size_res->value(); @@ -2655,14 +2642,14 @@ class Codec> } if (!field_size_res) { - return stdx::make_unexpected( + return stdx::unexpected( make_error_code(codec_errc::field_type_unknown)); } auto value_res = accu.template step>(field_size_res.value()); if (!accu.result()) { - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } values.push_back(value_res->value()); @@ -2672,7 +2659,7 @@ class Codec> } } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -2730,16 +2717,16 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto statement_id_res = accu.template step>(); auto param_id_res = accu.template step>(); auto data_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(statement_id_res->value(), @@ -2792,14 +2779,14 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto statement_id_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(statement_id_res->value())); @@ -2851,14 +2838,14 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto statement_id_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(statement_id_res->value())); @@ -2910,14 +2897,14 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto option_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(option_res->value())); @@ -2970,15 +2957,15 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto statement_id_res = accu.template step>(); auto row_count_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -3127,7 +3114,7 @@ class Codec> auto capabilities_lo_res = accu.template step>(); if (!capabilities_lo_res) - return stdx::make_unexpected(capabilities_lo_res.error()); + return stdx::unexpected(capabilities_lo_res.error()); auto client_capabilities = classic_protocol::capabilities::value_type( capabilities_lo_res->value()); @@ -3141,7 +3128,7 @@ class Codec> // of capabilities auto capabilities_hi_res = accu.template step>(); if (!capabilities_hi_res) - return stdx::make_unexpected(capabilities_hi_res.error()); + return stdx::unexpected(capabilities_hi_res.error()); client_capabilities |= classic_protocol::capabilities::value_type( capabilities_hi_res->value() << 16); @@ -3167,7 +3154,7 @@ class Codec> collation_res->value(), {}, {}, {}, {}, {})); } - return stdx::make_unexpected(accu.result().error()); + return stdx::unexpected(accu.result().error()); } // auth-method-data is either @@ -3180,24 +3167,24 @@ class Codec> if (shared_capabilities[classic_protocol::capabilities::pos:: client_auth_method_data_varint]) { auto res = accu.template step>(); - if (!res) return stdx::make_unexpected(res.error()); + if (!res) return stdx::unexpected(res.error()); auth_method_data_res = bw::String(res->value()); } else if (shared_capabilities [classic_protocol::capabilities::pos::secure_connection]) { auto auth_method_data_len_res = accu.template step>(); if (!auth_method_data_len_res) - return stdx::make_unexpected(auth_method_data_len_res.error()); + return stdx::unexpected(auth_method_data_len_res.error()); auto auth_method_data_len = auth_method_data_len_res->value(); auto res = accu.template step>(auth_method_data_len); - if (!res) return stdx::make_unexpected(res.error()); + if (!res) return stdx::unexpected(res.error()); auth_method_data_res = bw::String(res->value()); } else { auto res = accu.template step>(); - if (!res) return stdx::make_unexpected(res.error()); + if (!res) return stdx::unexpected(res.error()); auth_method_data_res = bw::String(res->value()); } @@ -3207,7 +3194,7 @@ class Codec> [classic_protocol::capabilities::pos::connect_with_schema]) { schema_res = accu.template step>(); } - if (!schema_res) return stdx::make_unexpected(schema_res.error()); + if (!schema_res) return stdx::unexpected(schema_res.error()); stdx::expected, std::error_code> auth_method_res; @@ -3221,8 +3208,7 @@ class Codec> auth_method_res = accu.template step>(); } } - if (!auth_method_res) - return stdx::make_unexpected(auth_method_res.error()); + if (!auth_method_res) return stdx::unexpected(auth_method_res.error()); stdx::expected, std::error_code> attributes_res; if (shared_capabilities @@ -3230,7 +3216,7 @@ class Codec> attributes_res = accu.template step>(); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -3251,7 +3237,7 @@ class Codec> if (shared_capabilities [classic_protocol::capabilities::pos::connect_with_schema]) { auto res = accu.template step>(); - if (!res) return stdx::make_unexpected(res.error()); + if (!res) return stdx::unexpected(res.error()); // auth_method_data is a wire::String, move it over auth_method_data_res = bw::String(res->value()); @@ -3261,7 +3247,7 @@ class Codec> auth_method_data_res = accu.template step>(); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); // idea: benchmark in-place constructor where all parameters are passed // down to the lowest level. @@ -3337,7 +3323,7 @@ class Codec> auto auth_method_data_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(auth_method_data_res->value())); @@ -3424,10 +3410,10 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto username_res = accu.template step>(); @@ -3439,23 +3425,23 @@ class Codec> if (caps[classic_protocol::capabilities::pos::secure_connection]) { auto auth_method_data_len_res = accu.template step>(); if (!auth_method_data_len_res) - return stdx::make_unexpected(auth_method_data_len_res.error()); + return stdx::unexpected(auth_method_data_len_res.error()); auto auth_method_data_len = auth_method_data_len_res->value(); auto res = accu.template step>(auth_method_data_len); - if (!res) return stdx::make_unexpected(res.error()); + if (!res) return stdx::unexpected(res.error()); auth_method_data_res = bw::String(res->value()); } else { auto res = accu.template step>(); - if (!res) return stdx::make_unexpected(res.error()); + if (!res) return stdx::unexpected(res.error()); auth_method_data_res = bw::String(res->value()); } auto schema_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); // 3.23.x-4.0 don't send more. if (buffer_size(buffer) <= accu.result().value()) { @@ -3479,7 +3465,7 @@ class Codec> attributes_res = accu.template step>(); } - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -3568,10 +3554,10 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto position_res = accu.template step>(); auto flags_res = accu.template step>(); @@ -3579,7 +3565,7 @@ class Codec> auto filename_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); stdx::flags flags; flags.underlying_value(flags_res->value()); @@ -3649,26 +3635,26 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto server_id_res = accu.template step>(); auto hostname_len_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto hostname_res = accu.template step>(hostname_len_res->value()); auto username_len_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto username_res = accu.template step>(username_len_res->value()); auto password_len_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto password_res = accu.template step>(password_len_res->value()); @@ -3677,7 +3663,7 @@ class Codec> auto replication_rank_res = accu.template step>(); auto master_id_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -3747,15 +3733,15 @@ class Codec> namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (cmd_byte_res->value() != cmd_byte()) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } auto flags_res = accu.template step>(); auto server_id_res = accu.template step>(); auto filename_len_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto filename_res = accu.template step>(filename_len_res->value()); @@ -3765,7 +3751,7 @@ class Codec> flags.underlying_value(flags_res->value()); if (!(flags & value_type::Flags::through_gtid)) { - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -3774,12 +3760,12 @@ class Codec> } auto sids_len_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); auto sids_res = accu.template step>(sids_len_res->value()); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index e121095..2eea09c 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -91,11 +91,11 @@ class Codec namespace bw = borrowable::wire; const auto payload_length_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); if (payload_length_res->value() != 0x08) { // length of the payload that follows. - return stdx::make_unexpected(make_error_code(std::errc::bad_message)); + return stdx::unexpected(make_error_code(std::errc::bad_message)); } const auto trx_type_res = accu.template step>(); @@ -107,7 +107,7 @@ class Codec const auto resultset_res = accu.template step>(); const auto locked_tables_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair( accu.result().value(), @@ -168,7 +168,7 @@ class Codec> auto characteristics_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(characteristics_res->value())); @@ -223,7 +223,7 @@ class Codec auto state_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(state_res->value())); @@ -279,7 +279,7 @@ class Codec> auto schema_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(schema_res->value())); @@ -338,7 +338,7 @@ class Codec> auto key_res = accu.template step>(); auto value_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(key_res->value(), value_res->value())); @@ -475,7 +475,7 @@ class Codec> auto type_res = accu.template step>(); auto data_res = accu.template step>(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(type_res->value(), data_res->value())); diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index af411de..13090a5 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -68,7 +68,7 @@ class Codec> { stdx::expected encode( net::mutable_buffer buffer) const { if (buffer.size() < int_size) { - return stdx::make_unexpected(make_error_code(std::errc::no_buffer_space)); + return stdx::unexpected(make_error_code(std::errc::no_buffer_space)); } auto int_val = v_.value(); @@ -92,8 +92,7 @@ class Codec> { const net::const_buffer &buffer, capabilities::value_type /* caps */) { if (buffer.size() < int_size) { // not enough data in buffers. - return stdx::make_unexpected( - make_error_code(codec_errc::not_enough_input)); + return stdx::unexpected(make_error_code(codec_errc::not_enough_input)); } typename value_type::value_type value{}; @@ -174,7 +173,7 @@ class Codec // length auto first_byte_res = accu.template step>(); - if (!first_byte_res) return stdx::make_unexpected(first_byte_res.error()); + if (!first_byte_res) return stdx::unexpected(first_byte_res.error()); auto first_byte = first_byte_res->value(); @@ -182,22 +181,22 @@ class Codec return std::make_pair(accu.result().value(), value_type(first_byte)); } else if (first_byte == varint_16) { auto value_res = accu.template step>(); - if (!value_res) return stdx::make_unexpected(value_res.error()); + if (!value_res) return stdx::unexpected(value_res.error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); } else if (first_byte == varint_24) { auto value_res = accu.template step>(); - if (!value_res) return stdx::make_unexpected(value_res.error()); + if (!value_res) return stdx::unexpected(value_res.error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); } else if (first_byte == varint_64) { auto value_res = accu.template step>(); - if (!value_res) return stdx::make_unexpected(value_res.error()); + if (!value_res) return stdx::unexpected(value_res.error()); return std::make_pair(accu.result().value(), value_type(value_res->value())); } - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } private: @@ -221,14 +220,13 @@ class Codec static stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type /* caps */) { if (buffer.size() < 1) { - return stdx::make_unexpected( - make_error_code(codec_errc::not_enough_input)); + return stdx::unexpected(make_error_code(codec_errc::not_enough_input)); } const uint8_t nul_val = *static_cast(buffer.data()); if (nul_val != nul_byte) { - return stdx::make_unexpected(make_error_code(codec_errc::invalid_input)); + return stdx::unexpected(make_error_code(codec_errc::invalid_input)); } return std::make_pair(1, value_type()); @@ -256,7 +254,7 @@ class Codec { stdx::expected encode( const net::mutable_buffer &buffer) const { if (buffer.size() < size()) { - return stdx::make_unexpected(make_error_code(std::errc::no_buffer_space)); + return stdx::unexpected(make_error_code(std::errc::no_buffer_space)); } auto *first = static_cast(buffer.data()); @@ -304,7 +302,7 @@ class Codec> { stdx::expected encode( const net::mutable_buffer &buffer) const { if (buffer.size() < size()) { - return stdx::make_unexpected(make_error_code(std::errc::no_buffer_space)); + return stdx::unexpected(make_error_code(std::errc::no_buffer_space)); } // in -> out @@ -366,14 +364,14 @@ class Codec> impl::DecodeBufferAccumulator accu(buffer, caps); // decode the length auto var_string_len_res = accu.template step(); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); // decode string of length auto var_string_res = accu.template step>( var_string_len_res->value()); - if (!accu.result()) return stdx::make_unexpected(accu.result().error()); + if (!accu.result()) return stdx::unexpected(accu.result().error()); return std::make_pair(accu.result().value(), value_type(var_string_res->value())); @@ -422,8 +420,7 @@ class Codec> const auto *pos = std::find(first, last, '\0'); if (pos == last) { // no 0-term found - return stdx::make_unexpected( - make_error_code(codec_errc::missing_nul_term)); + return stdx::unexpected(make_error_code(codec_errc::missing_nul_term)); } // \0 was found From 10e39e339fa434e6ec7b1f151d67c7ed7e278efe Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 20 Nov 2023 15:15:34 +0100 Subject: [PATCH 78/88] Bug#36031364 set-but-unused-variables warnings clang reports "set-but-unused-variables" after make stdx::expected trivially destructible. Change ====== - explicitly ignore the return-values Change-Id: Ia582da097b976db1c612dc78287709dd6e9ff47e --- mysqlrouter/classic_protocol_codec_clone.h | 25 +++++++++++++------- mysqlrouter/classic_protocol_codec_message.h | 5 ++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index cffc3cd..f30a1ff 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -25,6 +25,7 @@ #ifndef MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_CLONE_H_ #define MYSQL_ROUTER_CLASSIC_PROTOCOL_CODEC_CLONE_H_ +#include "mysql/harness/stdx/expected.h" #include "mysqlrouter/classic_protocol_clone.h" #include "mysqlrouter/classic_protocol_codec_base.h" #include "mysqlrouter/classic_protocol_codec_wire.h" @@ -39,7 +40,7 @@ enum class CommandByte { Ack, Exit, }; -} +} // namespace clone::client /** * codec for clone::client::Init message. @@ -81,10 +82,16 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); auto protocol_version_res = accu.template step>(); + if (!protocol_version_res) { + return stdx::unexpected(protocol_version_res.error()); + } auto ddl_timeout_res = accu.template step>(); + if (!ddl_timeout_res) { + return stdx::unexpected(ddl_timeout_res.error()); + } // TODO(jkneschk): if there is more data, 1-or-more Locators @@ -126,7 +133,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } @@ -162,7 +169,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } @@ -198,7 +205,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } @@ -234,7 +241,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } @@ -270,7 +277,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } @@ -336,7 +343,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } @@ -372,7 +379,7 @@ class Codec impl::DecodeBufferAccumulator accu(buffer, caps); auto cmd_byte_res = accu.template step>(); - if (!accu.result()) return stdx::unexpected(accu.result().error()); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); return std::make_pair(accu.result().value(), value_type()); } diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 60e8ffd..cbf6f02 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -749,6 +749,9 @@ class Codec> stdx::expected, std::error_code> sql_state_res; if (caps[capabilities::pos::protocol_41]) { auto sql_state_hash_res = accu.template step>(); + if (!sql_state_hash_res) { + return stdx::unexpected(sql_state_hash_res.error()); + } sql_state_res = accu.template step>(5); } auto message_res = accu.template step>(); @@ -1105,10 +1108,12 @@ class Codec namespace bw = borrowable::wire; auto cmd_byte_res = accu.template step>(); + if (!cmd_byte_res) return stdx::unexpected(cmd_byte_res.error()); auto stmt_id_res = accu.template step>(); auto column_count_res = accu.template step>(); auto param_count_res = accu.template step>(); auto filler_res = accu.template step>(); + if (!filler_res) return stdx::unexpected(filler_res.error()); auto warning_count_res = accu.template step>(); // by default, metadata isn't optional From 7a5e0eb5f64231006a107c882723c3129122b903 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Fri, 5 Jan 2024 12:29:39 +0100 Subject: [PATCH 79/88] BUG#36122452: Update copyright headers to 2024 [8.0 patch] Update all copyright headers in 8.0 to 2024 and to the current copyright format. This patch was generated by a script. Approved-by: erlend.dahl@oracle.com Change-Id: Ibd796c000b48576d390be22874a8efea4dd6ae40 --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_binary.h | 2 +- mysqlrouter/classic_protocol_clone.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_binary.h | 2 +- mysqlrouter/classic_protocol_codec_clone.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index 1a4ef38..d8779d8 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index dfda064..b9a375c 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, Oracle and/or its affiliates. + Copyright (c) 2023, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index 161fd71..9a3434f 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2023, Oracle and/or its affiliates. + Copyright (c) 2021, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index 555b80c..fe9c5d1 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 1065e59..eac65be 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index a2717ce..6edff36 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, Oracle and/or its affiliates. + Copyright (c) 2023, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 2bc52fd..40337b4 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2023, Oracle and/or its affiliates. + Copyright (c) 2021, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index e746a83..7180128 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 72a70b0..c88e4b3 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index feabf47..0d5b309 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index e47aeb3..f7abda6 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 1423974..40aea74 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 53e381b..9e65944 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index c8eb14d..42cbad4 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index f0bbbc9..3cfed44 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index a0d7444..047a9f9 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 1578abf..7c18052 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2023, Oracle and/or its affiliates. + Copyright (c) 2019, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From c3f873aad63ae3cff1429009170756505e5120f2 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 7 Feb 2024 09:51:11 +0100 Subject: [PATCH 80/88] GPL License Exception Update Approved by: Erlend Dahl --- mysqlrouter/classic_protocol.h | 5 +++-- mysqlrouter/classic_protocol_binary.h | 5 +++-- mysqlrouter/classic_protocol_clone.h | 5 +++-- mysqlrouter/classic_protocol_codec.h | 5 +++-- mysqlrouter/classic_protocol_codec_base.h | 5 +++-- mysqlrouter/classic_protocol_codec_binary.h | 5 +++-- mysqlrouter/classic_protocol_codec_clone.h | 5 +++-- mysqlrouter/classic_protocol_codec_error.h | 5 +++-- mysqlrouter/classic_protocol_codec_frame.h | 5 +++-- mysqlrouter/classic_protocol_codec_message.h | 5 +++-- mysqlrouter/classic_protocol_codec_session_track.h | 5 +++-- mysqlrouter/classic_protocol_codec_wire.h | 5 +++-- mysqlrouter/classic_protocol_constants.h | 5 +++-- mysqlrouter/classic_protocol_frame.h | 5 +++-- mysqlrouter/classic_protocol_message.h | 5 +++-- mysqlrouter/classic_protocol_session_track.h | 5 +++-- mysqlrouter/classic_protocol_wire.h | 5 +++-- 17 files changed, 51 insertions(+), 34 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index d8779d8..df8e46f 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index b9a375c..ae5d6a6 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index 9a3434f..c388815 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index fe9c5d1..accd4f6 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index eac65be..ecc4a69 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index 6edff36..99274aa 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 40337b4..32b5707 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 7180128..3128892 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index c88e4b3..7a3d277 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 0d5b309..2c42c23 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index f7abda6..b593f50 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 40aea74..73076aa 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 9e65944..41b885d 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 42cbad4..0befa5c 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 3cfed44..c9ad936 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 047a9f9..edda44e 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 7c18052..856cbf2 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of From fabf3512c0a91889b0802886bd41507016cbf9b1 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 7 Feb 2024 10:39:23 +0100 Subject: [PATCH 81/88] GPL License Exception Update Approved by: Erlend Dahl --- mysqlrouter/classic_protocol.h | 5 +++-- mysqlrouter/classic_protocol_binary.h | 5 +++-- mysqlrouter/classic_protocol_clone.h | 5 +++-- mysqlrouter/classic_protocol_codec.h | 5 +++-- mysqlrouter/classic_protocol_codec_base.h | 5 +++-- mysqlrouter/classic_protocol_codec_binary.h | 5 +++-- mysqlrouter/classic_protocol_codec_clone.h | 5 +++-- mysqlrouter/classic_protocol_codec_error.h | 5 +++-- mysqlrouter/classic_protocol_codec_frame.h | 5 +++-- mysqlrouter/classic_protocol_codec_message.h | 5 +++-- mysqlrouter/classic_protocol_codec_session_track.h | 5 +++-- mysqlrouter/classic_protocol_codec_wire.h | 5 +++-- mysqlrouter/classic_protocol_constants.h | 5 +++-- mysqlrouter/classic_protocol_frame.h | 5 +++-- mysqlrouter/classic_protocol_message.h | 5 +++-- mysqlrouter/classic_protocol_session_track.h | 5 +++-- mysqlrouter/classic_protocol_wire.h | 5 +++-- 17 files changed, 51 insertions(+), 34 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index d8779d8..df8e46f 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index b9a375c..ae5d6a6 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index 9a3434f..c388815 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index fe9c5d1..accd4f6 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index d9e1073..0c338d1 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index bfc7c91..bfd1e3a 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 8203c68..7cd7351 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 7180128..3128892 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index fb20c55..bc93679 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 7eb0cd7..048de2f 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index 9226b5d..8e0b633 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 795c385..7c4642f 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 9e65944..41b885d 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 42cbad4..0befa5c 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index b8c96a4..eaec86f 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index df626fe..9217c11 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 7c18052..856cbf2 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -5,12 +5,13 @@ it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. - This program is also distributed with certain software (including + This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. + separately licensed software that they have either included with + the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of From 528379e707b859e48729b98471f9886e4d95ece1 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Fri, 15 Nov 2024 09:32:25 +0100 Subject: [PATCH 82/88] Bug#37287684 removed clang-format directives The router/ sub-directory is contains several 'clang-format on/off' directives that aren't needed anymore with clang-format >= 15 Change ====== - removed clang-format on/off directives that aren't needed anymore Change-Id: Ie2e98329783079324c354372ce66154a40b4c7fe --- mysqlrouter/classic_protocol_codec_base.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 0c338d1..064e0f9 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -136,10 +136,7 @@ stdx::expected, std::error_code> decode( template stdx::expected, std::error_code> decode( const net::const_buffer &buffer, capabilities::value_type caps, - // clang-format off - Args &&... args - // clang-format on -) { + Args &&...args) { return Codec::decode(buffer, caps, std::forward(args)...); } From 13dad8e105b997a61b0ed8dbc252e8c56c8c7c65 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 16 Dec 2024 10:32:46 +0100 Subject: [PATCH 83/88] Bug#36374232 read-write splitting fails with connector-j When connector-j connects without "&trackSessionState=true" to the read-write splitting port, updates may fail with: java.sql.SQLException: Buffer length is less than expected payload length. Background ========== The Ok-message for UPDATEs (or other commands) from the server may contain a human-readable part like "Rows update: 1 ...". When the client connects to the router without the CLIENT_SESSION_TRACK capability, the Ok-message is wrongly encoded if it contains that human-readable part. Change ====== - encoded the human-readable part as "variable length string" instead of "string-to-the-end-of-packet" if CLIENT_SESSION_TRACK is not set. Change-Id: I6160d8e4d6e600c57972b2c95d389c8b97706a9f --- mysqlrouter/classic_protocol_codec_message.h | 35 ++++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 048de2f..d4dba0b 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -388,13 +388,16 @@ class Codec> } } - if (this->caps().test(capabilities::pos::session_track)) { + // if there is only empty "message" or "session-data", skip the rest. + const bool has_session_track_cap = + this->caps().test(capabilities::pos::session_track); + + if (!v_.message().empty() || has_session_track_cap) { accu.step(bw::VarString(v_.message())); - if (v_.status_flags().test(status::pos::session_state_changed)) { + if (has_session_track_cap && + v_.status_flags().test(status::pos::session_state_changed)) { accu.step(bw::VarString(v_.session_changes())); } - } else { - accu.step(bw::String(v_.message())); } return accu.result(); @@ -460,24 +463,20 @@ class Codec> } } - stdx::expected, std::error_code> message_res; stdx::expected, std::error_code> session_changes_res; - if (caps[capabilities::pos::session_track]) { - // if there is more data. - const auto var_message_res = - accu.template try_step>(); - if (var_message_res) { - // set the message from the var-string - message_res = var_message_res.value(); - } - - if (status_flags_res->value() & - status::session_state_changed.to_ulong()) { - session_changes_res = accu.template step>(); + // if there is more data. + auto message_res = accu.template try_step>(); + if (message_res) { + if (caps[capabilities::pos::session_track]) { + if (status_flags_res->value() & + status::session_state_changed.to_ulong()) { + session_changes_res = accu.template step>(); + } } } else { - message_res = accu.template step>(); + // no message field == empty message. + message_res = {}; } if (!accu.result()) return stdx::unexpected(accu.result().error()); From b3c6d04d3745bdc3c81c3e81f47e600cde3ca402 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Wed, 15 Jan 2025 13:12:42 +0100 Subject: [PATCH 84/88] BUG#37354028: Update copyright headers to 2025 [8.0 patch] Update all copyright headers in 8.0 to 2025 and to the current copyright format. This patch was generated by a script. Approved-by: Marc Alff --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_binary.h | 2 +- mysqlrouter/classic_protocol_clone.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_binary.h | 2 +- mysqlrouter/classic_protocol_codec_clone.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index df8e46f..d744277 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index ae5d6a6..c05b060 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2024, Oracle and/or its affiliates. + Copyright (c) 2023, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index c388815..b8c3616 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2024, Oracle and/or its affiliates. + Copyright (c) 2021, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index accd4f6..da8f911 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index ecc4a69..7034e9c 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index 99274aa..b6c08d9 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2024, Oracle and/or its affiliates. + Copyright (c) 2023, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 32b5707..84d6b88 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2024, Oracle and/or its affiliates. + Copyright (c) 2021, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 3128892..668bcec 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 7a3d277..17f10da 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 2c42c23..71f2309 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index b593f50..bb48aab 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 73076aa..c769203 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index 41b885d..c66bce6 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 0befa5c..6b8353e 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index c9ad936..3846c3f 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index edda44e..f73c592 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index 856cbf2..ba0fbe5 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2024, Oracle and/or its affiliates. + Copyright (c) 2019, 2025, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From 3dfe460ce6360e6e948db61c73b002400c3abf19 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Thu, 8 Jan 2026 21:09:34 +0100 Subject: [PATCH 85/88] Bug#38817886 Update copyright headers to 2026 [noclose] Patch for trunk, remaining community files Updated copyright headers to year 2026. Change-Id: I312cc5361e6269cb8acb6ff46bc33ff70388c7bd --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_binary.h | 2 +- mysqlrouter/classic_protocol_clone.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_binary.h | 2 +- mysqlrouter/classic_protocol_codec_clone.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index d744277..bb746da 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index c05b060..1a1ca7d 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2025, Oracle and/or its affiliates. + Copyright (c) 2023, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index b8c3616..f5f50d7 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2025, Oracle and/or its affiliates. + Copyright (c) 2021, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index da8f911..b5fb64f 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 855ef23..af70065 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index 1d1ce10..435f1e7 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2025, Oracle and/or its affiliates. + Copyright (c) 2023, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 548484e..cd3af89 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2025, Oracle and/or its affiliates. + Copyright (c) 2021, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 668bcec..c5c7469 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 32b86fc..bb36908 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index a329014..27789bd 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index a6f299a..4c530b0 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 106b4f1..caa0b71 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index c66bce6..67db243 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 6b8353e..ea1f981 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 718ac09..59b326b 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 14878c4..c516149 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index ba0fbe5..858d103 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From 95318ccc6bde48f7db257707d6aa5229eb58a4f2 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Mon, 12 Jan 2026 11:02:35 +0100 Subject: [PATCH 86/88] Bug#38817886 Update copyright headers to 2026 [noclose] Patch for 8.4 - fixed remaining code Updated copyright headers to year 2026. Change-Id: I5ff665e3cef5eb0a9782b42b8e373fc8ef55b2e7 --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_binary.h | 2 +- mysqlrouter/classic_protocol_clone.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_binary.h | 2 +- mysqlrouter/classic_protocol_codec_clone.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index d744277..bb746da 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index c05b060..1a1ca7d 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2025, Oracle and/or its affiliates. + Copyright (c) 2023, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index b8c3616..f5f50d7 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2025, Oracle and/or its affiliates. + Copyright (c) 2021, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index da8f911..b5fb64f 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 855ef23..af70065 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index 1d1ce10..435f1e7 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2025, Oracle and/or its affiliates. + Copyright (c) 2023, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 548484e..cd3af89 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2025, Oracle and/or its affiliates. + Copyright (c) 2021, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 668bcec..c5c7469 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 32b86fc..bb36908 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index a329014..27789bd 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index a6f299a..4c530b0 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index 106b4f1..caa0b71 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index c66bce6..67db243 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 6b8353e..ea1f981 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 718ac09..59b326b 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index 14878c4..c516149 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index ba0fbe5..858d103 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From a3a9357b32ae499131e20a8aa86fa8dbd2d721cd Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Mon, 19 Jan 2026 17:44:49 +0100 Subject: [PATCH 87/88] Bug#38817886 Update copyright headers to 2026 [noclose] Patch for 8.0 Updated copyright headers to year 2026. Change-Id: Ibcc3475bb4c58d246d239986b668194f9f536aa1 --- mysqlrouter/classic_protocol.h | 2 +- mysqlrouter/classic_protocol_binary.h | 2 +- mysqlrouter/classic_protocol_clone.h | 2 +- mysqlrouter/classic_protocol_codec.h | 2 +- mysqlrouter/classic_protocol_codec_base.h | 2 +- mysqlrouter/classic_protocol_codec_binary.h | 2 +- mysqlrouter/classic_protocol_codec_clone.h | 2 +- mysqlrouter/classic_protocol_codec_error.h | 2 +- mysqlrouter/classic_protocol_codec_frame.h | 2 +- mysqlrouter/classic_protocol_codec_message.h | 2 +- mysqlrouter/classic_protocol_codec_session_track.h | 2 +- mysqlrouter/classic_protocol_codec_wire.h | 2 +- mysqlrouter/classic_protocol_constants.h | 2 +- mysqlrouter/classic_protocol_frame.h | 2 +- mysqlrouter/classic_protocol_message.h | 2 +- mysqlrouter/classic_protocol_session_track.h | 2 +- mysqlrouter/classic_protocol_wire.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mysqlrouter/classic_protocol.h b/mysqlrouter/classic_protocol.h index d744277..bb746da 100644 --- a/mysqlrouter/classic_protocol.h +++ b/mysqlrouter/classic_protocol.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_binary.h b/mysqlrouter/classic_protocol_binary.h index c05b060..1a1ca7d 100644 --- a/mysqlrouter/classic_protocol_binary.h +++ b/mysqlrouter/classic_protocol_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2025, Oracle and/or its affiliates. + Copyright (c) 2023, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_clone.h b/mysqlrouter/classic_protocol_clone.h index b8c3616..f5f50d7 100644 --- a/mysqlrouter/classic_protocol_clone.h +++ b/mysqlrouter/classic_protocol_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2025, Oracle and/or its affiliates. + Copyright (c) 2021, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec.h b/mysqlrouter/classic_protocol_codec.h index da8f911..b5fb64f 100644 --- a/mysqlrouter/classic_protocol_codec.h +++ b/mysqlrouter/classic_protocol_codec.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_base.h b/mysqlrouter/classic_protocol_codec_base.h index 7034e9c..0b35baf 100644 --- a/mysqlrouter/classic_protocol_codec_base.h +++ b/mysqlrouter/classic_protocol_codec_base.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_binary.h b/mysqlrouter/classic_protocol_codec_binary.h index b6c08d9..19e4756 100644 --- a/mysqlrouter/classic_protocol_codec_binary.h +++ b/mysqlrouter/classic_protocol_codec_binary.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2023, 2025, Oracle and/or its affiliates. + Copyright (c) 2023, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_clone.h b/mysqlrouter/classic_protocol_codec_clone.h index 84d6b88..35d956c 100644 --- a/mysqlrouter/classic_protocol_codec_clone.h +++ b/mysqlrouter/classic_protocol_codec_clone.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2021, 2025, Oracle and/or its affiliates. + Copyright (c) 2021, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_error.h b/mysqlrouter/classic_protocol_codec_error.h index 668bcec..c5c7469 100644 --- a/mysqlrouter/classic_protocol_codec_error.h +++ b/mysqlrouter/classic_protocol_codec_error.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_frame.h b/mysqlrouter/classic_protocol_codec_frame.h index 17f10da..bf17218 100644 --- a/mysqlrouter/classic_protocol_codec_frame.h +++ b/mysqlrouter/classic_protocol_codec_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_message.h b/mysqlrouter/classic_protocol_codec_message.h index 71f2309..ae3cb48 100644 --- a/mysqlrouter/classic_protocol_codec_message.h +++ b/mysqlrouter/classic_protocol_codec_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_session_track.h b/mysqlrouter/classic_protocol_codec_session_track.h index bb48aab..1a94d0e 100644 --- a/mysqlrouter/classic_protocol_codec_session_track.h +++ b/mysqlrouter/classic_protocol_codec_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_codec_wire.h b/mysqlrouter/classic_protocol_codec_wire.h index c769203..ee4a65c 100644 --- a/mysqlrouter/classic_protocol_codec_wire.h +++ b/mysqlrouter/classic_protocol_codec_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_constants.h b/mysqlrouter/classic_protocol_constants.h index c66bce6..67db243 100644 --- a/mysqlrouter/classic_protocol_constants.h +++ b/mysqlrouter/classic_protocol_constants.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_frame.h b/mysqlrouter/classic_protocol_frame.h index 6b8353e..ea1f981 100644 --- a/mysqlrouter/classic_protocol_frame.h +++ b/mysqlrouter/classic_protocol_frame.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_message.h b/mysqlrouter/classic_protocol_message.h index 3846c3f..8069cfc 100644 --- a/mysqlrouter/classic_protocol_message.h +++ b/mysqlrouter/classic_protocol_message.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_session_track.h b/mysqlrouter/classic_protocol_session_track.h index f73c592..d9e2388 100644 --- a/mysqlrouter/classic_protocol_session_track.h +++ b/mysqlrouter/classic_protocol_session_track.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, diff --git a/mysqlrouter/classic_protocol_wire.h b/mysqlrouter/classic_protocol_wire.h index ba0fbe5..858d103 100644 --- a/mysqlrouter/classic_protocol_wire.h +++ b/mysqlrouter/classic_protocol_wire.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, 2025, Oracle and/or its affiliates. + Copyright (c) 2019, 2026, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, From 84bee8e66fb598a16035fadfba5893730dc87c05 Mon Sep 17 00:00:00 2001 From: Yura Sorokin Date: Sat, 27 Jun 2026 02:31:30 +0200 Subject: [PATCH 88/88] PBS-28 feature: Merge code from the MySQL Protocol Prototype onto the main source tree (#156) https://perconadev.atlassian.net/browse/PBS-28 Imported code from the MySQL Protocol Prototype server to the main code tree. At this iteration the code is added as a second binary 'minimysql_server' - top-level 'CMakeLists.txt' file updated correspondingly. To integrate headers from the 'extra/mysql_protocol/mysqlrouter' directory (those importer directly from the 'mysql-server' git repository) several auxiliary headers were created: * 'mysql/harness/net_ts/buffer.h' - creates aliases for various 'boost::asio' buffers. * 'mysql/harness/stdx/bit.h' - creates an alias for 'std::byteswap()'. * 'mysql/harness/stdx/expected.h' - creates an alias for 'std::expected'. * 'mysql/harness/stdx/type_traits.h' - creates aliases for 'std::is_scoped_enum' and 'std::type_identity'. * 'mysql/harness/stdx/ranges.h' - imported from 'mysql-server' with clang-format / clang-tidy fixes. This file could be just a set of aliases for 'std::ranges::enumerate_view' but unfortunately 'libstdc++' from 'clang-20' does not have this c++23 library feature implemented. A 'TODO' comment was created to change this when we switch to clang-23. * 'mysql/harness/stdx/flags.h' - imported from 'mysql-server' with clang-format / clang-tidy fixes. Added '.clang-format-ignore' file to ignore formatting of the headers located in the 'extra/mysql_protocol/mysqlrouter' directory as they were directly imported from the Oracle's 'mysql-server' repository and were formatted differently. Modified '.clang-tidy' directives to ignore warnings coming from the headers located in the 'extra/mysql_protocol/mysqlrouter' as they were directly imported from the Oracle's 'mysql-server' repository and still generate a number of warnings. GitHub Actions script modified to instruct `run-clang-tidy` to use this config file (with the required header filters). GitHib Actions scripts extended with one more step that shows clang-tidy configuration. --- .clang-format-ignore | 1 + .clang-tidy | 3 +- .github/workflows/cmake.yml | 6 +- CMakeLists.txt | 70 +- .../mysql/harness/net_ts/buffer.h | 40 + extra/mysql_protocol/mysql/harness/stdx/bit.h | 37 + .../mysql/harness/stdx/expected.h | 38 + .../mysql_protocol/mysql/harness/stdx/flags.h | 326 ++++++++ .../mysql/harness/stdx/ranges.h | 160 ++++ .../mysql/harness/stdx/type_traits.h | 41 + .../caching_sha2_password_authenticator.cpp | 155 ++++ .../caching_sha2_password_authenticator.hpp | 33 + src/minimysql/connection_context.cpp | 642 +++++++++++++++ src/minimysql/connection_context.hpp | 307 +++++++ src/minimysql/connection_context_fwd.hpp | 54 ++ src/minimysql_app.cpp | 772 ++++++++++++++++++ tests/gtid_set_test.cpp | 3 - 17 files changed, 2682 insertions(+), 6 deletions(-) create mode 100644 .clang-format-ignore create mode 100644 extra/mysql_protocol/mysql/harness/net_ts/buffer.h create mode 100644 extra/mysql_protocol/mysql/harness/stdx/bit.h create mode 100644 extra/mysql_protocol/mysql/harness/stdx/expected.h create mode 100644 extra/mysql_protocol/mysql/harness/stdx/flags.h create mode 100644 extra/mysql_protocol/mysql/harness/stdx/ranges.h create mode 100644 extra/mysql_protocol/mysql/harness/stdx/type_traits.h create mode 100644 src/minimysql/caching_sha2_password_authenticator.cpp create mode 100644 src/minimysql/caching_sha2_password_authenticator.hpp create mode 100644 src/minimysql/connection_context.cpp create mode 100644 src/minimysql/connection_context.hpp create mode 100644 src/minimysql/connection_context_fwd.hpp create mode 100644 src/minimysql_app.cpp diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000..bd86895 --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +extra/mysql_protocol/mysqlrouter/* diff --git a/.clang-tidy b/.clang-tidy index 09f2337..dca489a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -13,4 +13,5 @@ Checks: " -llvm-header-guard " WarningsAsErrors: '*' -HeaderFilterRegex: '' +HeaderFilterRegex: '.*' +ExcludeHeaderFilterRegex: 'mysqlrouter/.*' diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 5ed6678..01a2e24 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -263,10 +263,14 @@ jobs: if: matrix.config.run_clang_tidy run: clang-tidy-20 --version + - name: Dump Clang Tidy Config + if: matrix.config.run_clang_tidy + run: clang-tidy-20 --dump-config --config-file=${{github.workspace}}/src/.clang-tidy + - name: Clang Tidy if: matrix.config.run_clang_tidy # Run Clang Tidy - run: run-clang-tidy-20 -header-filter=.* -j=${{steps.cpu-cores.outputs.count}} -use-color -p=${{github.workspace}}/src-build-${{matrix.config.label}} + run: run-clang-tidy-20 -config-file=${{github.workspace}}/src/.clang-tidy -j=${{steps.cpu-cores.outputs.count}} -use-color -p=${{github.workspace}}/src-build-${{matrix.config.label}} - name: Application version working-directory: ${{github.workspace}}/src-build-${{matrix.config.label}} diff --git a/CMakeLists.txt b/CMakeLists.txt index d0f1c40..7eb6928 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ if(WITH_ASAN) target_link_options(binlog_server_compiler_flags INTERFACE "-fsanitize=address") endif() -find_package(Boost 1.90.0 EXACT REQUIRED COMPONENTS url json) +find_package(Boost 1.90.0 EXACT REQUIRED COMPONENTS url json asio) find_package(MySQL REQUIRED) @@ -536,6 +536,74 @@ set_target_properties(binlog_server PROPERTIES CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO ) +set(mysql_harness_source_files + extra/mysql_protocol/mysql/harness/net_ts/buffer.h + + extra/mysql_protocol/mysql/harness/stdx/bit.h + extra/mysql_protocol/mysql/harness/stdx/expected.h + extra/mysql_protocol/mysql/harness/stdx/flags.h + extra/mysql_protocol/mysql/harness/stdx/ranges.h + extra/mysql_protocol/mysql/harness/stdx/type_traits.h +) + +set(mysql_protocol_source_files + extra/mysql_protocol/mysqlrouter/classic_protocol.h + extra/mysql_protocol/mysqlrouter/classic_protocol_binary.h + extra/mysql_protocol/mysqlrouter/classic_protocol_clone.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_base.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_binary.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_clone.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_error.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_frame.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_message.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_session_track.h + extra/mysql_protocol/mysqlrouter/classic_protocol_codec_wire.h + extra/mysql_protocol/mysqlrouter/classic_protocol_constants.h + extra/mysql_protocol/mysqlrouter/classic_protocol_frame.h + extra/mysql_protocol/mysqlrouter/classic_protocol_message.h + extra/mysql_protocol/mysqlrouter/classic_protocol_session_track.h + extra/mysql_protocol/mysqlrouter/classic_protocol_wire.h) + +set(minimysql_source_files + src/minimysql/caching_sha2_password_authenticator.hpp + src/minimysql/caching_sha2_password_authenticator.cpp + src/minimysql/connection_context_fwd.hpp + src/minimysql/connection_context.hpp + src/minimysql/connection_context.cpp + + src/minimysql_app.cpp +) + +add_executable(minimysql_server ${minimysql_source_files} ${mysql_protocol_source_files} ${mysql_harness_source_files}) + +# GCC 14 may report a false-positive mismatched allocation/deallocation +# warning in boost::asio coroutine machinery (awaitable_frame) used in +# minimysql_app.cpp. +# Also, there are some false-positive null-dereference warnings in boost::asio +# io_context. +# Keep -Werror globally and suppress only this source file. +set_source_files_properties(src/minimysql_app.cpp PROPERTIES + COMPILE_OPTIONS + "$<$,$,14>>:-Wno-error=mismatched-new-delete;-Wno-error=null-dereference>" +) + +target_compile_definitions(minimysql_server PRIVATE BOOST_ASIO_NO_DEPRECATED) +target_link_libraries(minimysql_server + PRIVATE + binlog_server_compiler_flags + Boost::headers Boost::asio + OpenSSL::Crypto +) + +target_include_directories(minimysql_server PRIVATE "${PROJECT_SOURCE_DIR}/extra/mysql_protocol") + +# it is not possible to propagate CXX_EXTENSIONS and CXX_STANDARD_REQUIRED +# via interface library (binlog_server_compiler_flags) +set_target_properties(minimysql_server PROPERTIES + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO +) include(CTest) if(BUILD_TESTING) diff --git a/extra/mysql_protocol/mysql/harness/net_ts/buffer.h b/extra/mysql_protocol/mysql/harness/net_ts/buffer.h new file mode 100644 index 0000000..cbfb93e --- /dev/null +++ b/extra/mysql_protocol/mysql/harness/net_ts/buffer.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2019, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_HARNESS_NET_TS_BUFFER_H +#define MYSQL_HARNESS_NET_TS_BUFFER_H + +#include + +namespace net { + +using boost::asio::buffer; +using boost::asio::buffer_size; +using boost::asio::const_buffer; +using boost::asio::mutable_buffer; + +} // namespace net + +#endif // MYSQL_HARNESS_NET_TS_BUFFER_H diff --git a/extra/mysql_protocol/mysql/harness/stdx/bit.h b/extra/mysql_protocol/mysql/harness/stdx/bit.h new file mode 100644 index 0000000..dc116b1 --- /dev/null +++ b/extra/mysql_protocol/mysql/harness/stdx/bit.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2019, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_HARNESS_STDX_BIT_H +#define MYSQL_HARNESS_STDX_BIT_H + +#include + +namespace stdx { + +using std::byteswap; + +} // namespace stdx + +#endif // MYSQL_HARNESS_STDX_BIT_H diff --git a/extra/mysql_protocol/mysql/harness/stdx/expected.h b/extra/mysql_protocol/mysql/harness/stdx/expected.h new file mode 100644 index 0000000..c6ef7ef --- /dev/null +++ b/extra/mysql_protocol/mysql/harness/stdx/expected.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2019, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_HARNESS_STDX_EXPECTED_H +#define MYSQL_HARNESS_STDX_EXPECTED_H + +#include + +namespace stdx { + +using std::expected; +using std::unexpected; + +} // namespace stdx + +#endif // MYSQL_HARNESS_STDX_EXPECTED_H diff --git a/extra/mysql_protocol/mysql/harness/stdx/flags.h b/extra/mysql_protocol/mysql/harness/stdx/flags.h new file mode 100644 index 0000000..89e2d74 --- /dev/null +++ b/extra/mysql_protocol/mysql/harness/stdx/flags.h @@ -0,0 +1,326 @@ +/* + Copyright (c) 2021, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_HARNESS_STDX_FLAGS_H +#define MYSQL_HARNESS_STDX_FLAGS_H + +#include // popcount +#include +#include + +#include "mysql/harness/stdx/type_traits.h" // is_scoped_enum_v + +namespace stdx { + +// tags a class as flags-class +template +struct is_flags : public std::false_type {}; + +template inline constexpr bool is_flags_v = is_flags::value; + +/** + * a type-safe flags type. + * + * # abstract + * + * Using flags in the in C++ isn't very ergonimic: + * + * 1. using plain C-enum's isn't typesafe + * 2. using std::bitset requires bit-positions to set/reset/test them + * 3. scoped-enums (enum class) doesn't have operaters + * + * For ease of use it should be possible to have a flags-type which + * allows the type-safe operations: + * + * - flags = flags & flag + * - flags &= flag + * - flags = flags | flag + * - flags |= flag + * - flags = flags ^ flag + * - flags ^= flag + * - flags = ~flag + * + * - flag | flag -> flags + * - flag & flag -> flags + * - flag ^ flag -> flags + * + * # example + * + * @code{.cpp} + * // underlying scoped-enum + * enum class somebits { + * bit0 = 1 << 0, + * bit1 = 1 << 1, + * }; + * + * // activate stdx::flags support for the scoped-enum + * namespace stdx { + * template<> + * struct is_flags : std::true_type {} + * } + * + * { // default-construct + assignment + * stdx::flags someflags; + * + * someflags = somebits::bit0 | somebits::bit1; + * + * // get the underlying bitvalue. + * std::cerr << someflags.underlying_value() << "\n"; + * } + * + * { // direct initialization from enum + * stdx::flags someflags{somebits::bit0 | somebits::bit1}; + * } + * + * { // testing if bit set + * stdx::flags someflags{somebits::bit0 | somebits::bit1}; + * + * if (someflags & somebits::bit1) {} // true + * } + * + * { // setting raw value + * stdx::flags someflags; + * someflags.underlying_value(3); + * + * if (someflags & somebits::bit0) {} // true + * if (someflags & somebits::bit1) {} // true + * } + * @endcode + * + * @tparam E a scoped enum that is tagged with stdx::is_flags. + */ +template class flags { +public: + static_assert(stdx::is_scoped_enum_v, + "flags, E must be a scoped enum type"); + static_assert(is_flags_v, + "flags, E must be declared as flags-type via:" + "namespace stdx { template<> struct is_flags<...> : " + "std::true_type {};} "); + + //!< type of the wrapped enum. + using enum_type = std::decay_t; + //!< underlying integer type of the enum_type. + using underlying_type = std::underlying_type_t; + //!< implementation type of the underlying type. + using impl_type = std::make_unsigned_t; + using size_type = std::size_t; + + /** + * default constructor. + * + * initializes underlying_value to 0 (no-bits-set). + */ + constexpr flags() = default; + + constexpr flags(const flags &other) = default; //!< copy constructor. + constexpr flags(flags &&other) = default; //!< move constructor + constexpr flags &operator=(const flags &other) = default; //!< copy assignment + constexpr flags &operator=(flags &&other) = default; //!< move assignment + + constexpr ~flags() = default; + + /** + * converting constructor from enum_type. + */ + // NOLINTNEXTLINE(hicpp-explicit-conversions) + constexpr flags(enum_type value) : v_{static_cast(value)} {} + + /** + * check if any bit is set. + * + * @retval true at least one bit is set. + * @retval false no bit is set. + */ + // NOLINTNEXTLINE(hicpp-explicit-conversions) + constexpr operator bool() const noexcept { return v_ != 0; } + + /** + * check if no bit is set. + * + * @retval true no bit is set. + * @retval false at least one bit is set. + */ + constexpr bool operator!() const noexcept { return !v_; } + + /** + * negation of all bits. + * + * ~flags -> flags. + */ + constexpr flags operator~() const noexcept { + return flags{static_cast(~v_)}; + } + + /** + * bit-or. + * + * flags |= flags. + */ + constexpr flags &operator|=(const flags &other) noexcept { + v_ |= other.v_; + return *this; + } + + /** + * bit-and. + * + * flags &= flags. + */ + constexpr flags &operator&=(const flags &other) noexcept { + v_ &= other.v_; + return *this; + } + + /** + * bit-xor. + * + * flag ^= flag + */ + constexpr flags &operator^=(const flags &other) noexcept { + v_ ^= other.v_; + return *this; + } + + /** + * bit-and. + * + * flags & flags -> flags + */ + friend constexpr flags operator&(flags left, flags right) noexcept { + return flags{static_cast(left.v_ & right.v_)}; + } + + /** + * bit-or. + * + * flags | flags -> flags + */ + friend constexpr flags operator|(flags left, flags right) noexcept { + return flags{static_cast(left.v_ | right.v_)}; + } + + /** + * bit-xor. + * + * flags ^ flags -> flags + */ + friend constexpr flags operator^(flags left, flags right) noexcept { + return flags{static_cast(left.v_ ^ right.v_)}; + } + + /** + * set underlying value. + * + * @param value underlying value to set. + */ + constexpr void underlying_value(underlying_type value) noexcept { + v_ = value; + } + + /** + * get underlying value. + * + * @return underlying value. + */ + [[nodiscard]] constexpr underlying_type underlying_value() const noexcept { + return v_; + } + + /** + * count bits set to true. + * + * @sa size() + * + * @return bits set to true. + */ + [[nodiscard]] constexpr size_type count() const noexcept { + return std::popcount(v_); + } + + /** + * returns number of bits the flag-type can hold. + * + * @sa count() + * + * @returns number of bits the flag-type can hold. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return CHAR_BIT * sizeof(underlying_type); + } + + /** + * reset all flags to 0. + */ + constexpr void reset() { v_ = 0; } + +private: + constexpr explicit flags(impl_type value) noexcept : v_{value} {} + + impl_type v_{}; +}; + +} // namespace stdx + +/** + * bit-or. + * + * E | E -> flags; + * + * @return a flags of bit-or(e1, e2). + */ +template + requires stdx::is_flags_v +constexpr stdx::flags operator|(E left, E right) noexcept { + return stdx::flags(left) | right; +} + +/** + * bit-and. + * + * E & E -> flags; + * + * @return a flags of bit-and(e1, e2). + */ +template + requires stdx::is_flags_v +constexpr stdx::flags operator&(E left, E right) noexcept { + return stdx::flags(left) & right; +} + +/** + * bit-xor. + * + * E ^ E -> flags; + * + * @return a flags of bit-xor(e1, e2). + */ +template + requires stdx::is_flags_v +constexpr stdx::flags operator^(E left, E right) noexcept { + return stdx::flags(left) ^ right; +} + +#endif // MYSQL_HARNESS_STDX_FLAGS_H diff --git a/extra/mysql_protocol/mysql/harness/stdx/ranges.h b/extra/mysql_protocol/mysql/harness/stdx/ranges.h new file mode 100644 index 0000000..b01be3d --- /dev/null +++ b/extra/mysql_protocol/mysql/harness/stdx/ranges.h @@ -0,0 +1,160 @@ +/* + Copyright (c) 2022, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_HARNESS_STDX_RANGES_ENUMERATE_H +#define MYSQL_HARNESS_STDX_RANGES_ENUMERATE_H + +// +// From C++23's P2164 +// +// http://wg21.link/p2164 + +#include +#include +#include +#include // declval, forward + +namespace stdx::ranges { + +// TODO: change the content of this this file to "using +// std::ranges::views::enumerate" when switching to clang-23 + +/** + * enumerate_view over a range. + * + * @note only implements the const-iterator parts. + * + * @tparam V a range to enumerate + */ +template class enumerate_view { +private: + using Base = V; + + Base base_ = {}; + + template class iterator; + +public: + using value_type = std::iter_value_t>; + + constexpr enumerate_view() = default; + + constexpr explicit enumerate_view(V base) : base_(std::forward(base)) {} + + constexpr auto begin() const { return iterator{std::begin(base_), 0}; } + + constexpr auto end() const { return iterator{std::end(base_), 0}; } +}; + +template enumerate_view(R &&) -> enumerate_view; + +template template class enumerate_view::iterator { +private: + using Base = std::conditional_t; + +public: + using iterator_category = std::input_iterator_tag; + + using index_type = size_t; + using reference = + std::tuple>; + using value_type = std::tuple>; + + constexpr explicit iterator(std::ranges::iterator_t current, + index_type pos) + : pos_{pos}, current_{std::move(current)} {} + + constexpr bool operator!=(const iterator &other) const { + return current_ != other.current_; + } + + constexpr iterator &operator++() { + ++pos_; + ++current_; + + return *this; + } + + constexpr decltype(auto) operator*() const { + return reference{pos_, *current_}; + } + +private: + index_type pos_; + + std::ranges::iterator_t current_; +}; + +namespace views { +/* + * an iterator that wraps an iterable and returns a counter and the + * deref'ed wrapped iterable. + * + * @tparam T a iterable + * + * @code + * for (auto [ndx, vc]: enumerate(std::vector{1, 23, 42})) { + * std::cerr << "[" << ndx << "] " << v << "\n"; + * } + * + * // [0] 1 + * // [1] 23 + * // [2] 42 + * @endcode + * + * modelled after P2164 from C++23, but implemented for C++17 (aka without + * ranges and views) + */ +template ())), + class = decltype(std::end(std::declval()))> +constexpr auto enumerate(T &&iterable) { + return enumerate_view{std::forward(iterable)}; +} +} // namespace views + +/** + * Checks if a container contains a specified element. Implements + * https://en.cppreference.com/w/cpp/algorithm/ranges/contains + * + * @param container The container in which to search for the needle. + * @param needle The element to search for in the container. + * @return `true` if the needle is found in the container, `false` otherwise. + */ +template + requires std::convertible_to +bool contains(const Range &container, const T &needle) { + return std::find(container.begin(), container.end(), needle) != + container.end(); +} + +} // namespace stdx::ranges + +namespace stdx { + +namespace views = ranges::views; + +} // namespace stdx + +#endif // MYSQL_HARNESS_STDX_RANGES_ENUMERATE_H diff --git a/extra/mysql_protocol/mysql/harness/stdx/type_traits.h b/extra/mysql_protocol/mysql/harness/stdx/type_traits.h new file mode 100644 index 0000000..c32bcdd --- /dev/null +++ b/extra/mysql_protocol/mysql/harness/stdx/type_traits.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2019, 2026, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is designed to work with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have either included with + the program or referenced in the documentation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MYSQL_HARNESS_STDX_TYPE_TRAITS_H +#define MYSQL_HARNESS_STDX_TYPE_TRAITS_H + +#include + +namespace stdx { + +using std::is_scoped_enum; +using std::is_scoped_enum_v; + +using std::type_identity; +using std::type_identity_t; + +} // namespace stdx + +#endif // MYSQL_HARNESS_STDX_TYPE_TRAITS_H diff --git a/src/minimysql/caching_sha2_password_authenticator.cpp b/src/minimysql/caching_sha2_password_authenticator.cpp new file mode 100644 index 0000000..bc7ae81 --- /dev/null +++ b/src/minimysql/caching_sha2_password_authenticator.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2023-2026 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#include "minimysql/caching_sha2_password_authenticator.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +enum class digest_code_type : std::uint8_t { + sha256, +}; + +class digest_context { +public: + // no std::string_view for 'type' as we need it to be nul-terminated + explicit digest_context(digest_code_type digest_code) + : impl_{EVP_MD_CTX_new(), digest_context_deleter{}} { + if (!impl_) { + throw std::runtime_error{"failed to create digest context"}; + } + if (EVP_DigestInit_ex(impl_.get(), get_md_by_digest_code(digest_code), + nullptr) == 0) { + throw std::runtime_error{"failed to initialize digest context"}; + } + } + + ~digest_context() noexcept = default; + + digest_context(const digest_context &obj) = delete; + digest_context(digest_context &&obj) noexcept = delete; + + digest_context &operator=(const digest_context &obj) = delete; + digest_context &operator=(digest_context &&obj) noexcept = delete; + + [[nodiscard]] std::size_t get_size_in_bytes() const noexcept { + assert(impl_); + auto native_result{EVP_MD_CTX_size(impl_.get())}; + assert(native_result != -1); + return static_cast(native_result); + } + + void update(std::string_view data) { + assert(impl_); + if (EVP_DigestUpdate(impl_.get(), std::data(data), std::size(data)) == 0) { + throw std::runtime_error{"failed to update digest context"}; + } + } + std::string finalize() { + assert(impl_); + std::string result(get_size_in_bytes(), '\0'); + + unsigned int result_size = 0; + if (EVP_DigestFinal_ex( + impl_.get(), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(std::data(result)), + &result_size) == 0) { + throw std::runtime_error{"cannot finalize digest context"}; + } + assert(result_size == std::size(result)); + + impl_.reset(); + return result; + } + +private: + struct digest_context_deleter { + void operator()(EVP_MD_CTX *digest_context) const noexcept { + // null-ness is handled by EVP_MD_CTX_free + EVP_MD_CTX_free(digest_context); + } + }; + + using impl_ptr = std::unique_ptr; + impl_ptr impl_; + + [[nodiscard]] static const EVP_MD * + get_md_by_digest_code(digest_code_type digest_code) noexcept { + switch (digest_code) { + case digest_code_type::sha256: + return EVP_sha256(); + default: + // should never happen as we only construct digest_context with supported + // digest_code_type + return nullptr; + } + } +}; + +std::string calculate_digest(digest_code_type digest_code, + std::string_view data) { + digest_context ctx(digest_code); + ctx.update(data); + return ctx.finalize(); +} + +} // anonymous namespace + +namespace minimysql { + +std::string caching_sha2_password_authenticator::scramble( + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + std::string_view password, std::string_view salt) { + // this is how client calculates client_auth_data for caching_sha2_password + // plugin: SHA256(password) XOR SHA256(SHA256(SHA256(password)), + // server_auth_data) + + // server, provided that it knows original password and server_auth_data + // (salt), can verify client_auth_data by calculating the same way and + // comparing the result with client_auth_data + const auto digest_code{digest_code_type::sha256}; + + // calculating hashed password + auto result{calculate_digest(digest_code, password)}; + + // calculating double-hashed password + const auto double_hashed_password{calculate_digest(digest_code, result)}; + + // calculating salted triple-hashed password + digest_context ctx(digest_code); + ctx.update(double_hashed_password); + ctx.update(salt); + const auto salted_triple_hashed_password{ctx.finalize()}; + + assert(std::size(result) == std::size(salted_triple_hashed_password)); + std::ranges::transform(result, salted_triple_hashed_password, + std::begin(result), std::bit_xor{}); + return result; +} + +} // namespace minimysql diff --git a/src/minimysql/caching_sha2_password_authenticator.hpp b/src/minimysql/caching_sha2_password_authenticator.hpp new file mode 100644 index 0000000..b9f69bb --- /dev/null +++ b/src/minimysql/caching_sha2_password_authenticator.hpp @@ -0,0 +1,33 @@ +// Copyright (c) 2023-2026 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#ifndef MINIMYSQL_CACHING_SHA2_PASSWORD_AUTHENTICATOR_HPP +#define MINIMYSQL_CACHING_SHA2_PASSWORD_AUTHENTICATOR_HPP + +#include +#include + +namespace minimysql { + +class caching_sha2_password_authenticator { +public: + static constexpr std::string_view plugin_name{"caching_sha2_password"}; + + static std::string scramble(std::string_view password, std::string_view salt); +}; + +} // namespace minimysql + +#endif // MINIMYSQL_CACHING_SHA2_PASSWORD_AUTHENTICATOR_HPP diff --git a/src/minimysql/connection_context.cpp b/src/minimysql/connection_context.cpp new file mode 100644 index 0000000..29bdf39 --- /dev/null +++ b/src/minimysql/connection_context.cpp @@ -0,0 +1,642 @@ +// Copyright (c) 2023-2026 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#include "minimysql/connection_context.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +// code included from MySQL Router's classic protocol codec implementation +// has a number of conversion warnings, so we disable them before inclusion + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" + +#include "mysqlrouter/classic_protocol_codec_base.h" +#include "mysqlrouter/classic_protocol_codec_frame.h" +#include "mysqlrouter/classic_protocol_codec_message.h" +#include "mysqlrouter/classic_protocol_constants.h" +#include "mysqlrouter/classic_protocol_frame.h" +#include "mysqlrouter/classic_protocol_message.h" +#include "mysqlrouter/classic_protocol_wire.h" + +#include "mysql/harness/stdx/flags.h" + +#pragma GCC diagnostic pop + +#include "minimysql/caching_sha2_password_authenticator.hpp" + +namespace minimysql { + +namespace { + +template +classic_protocol::frame::Frame +decode_client_command_frame(const connection_buffer_type &payload, + const capability_bitset &shared_capabilities) { + auto buffer{boost::asio::buffer(payload)}; + using frame_type = classic_protocol::frame::Frame; + + auto decode_result{ + classic_protocol::decode(buffer, shared_capabilities)}; + if (!decode_result) { + throw boost::system::system_error{decode_result.error()}; + } + + return decode_result.value().second; +} + +} // namespace + +connection_context::connection_context( + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + std::string_view server_username, std::string_view server_password) + : server_username_(server_username), server_password_(server_password), + connection_id_(next_connection_id_++) { + static_assert(std::is_same_v, + "capability_bitset MUST be the same type as " + "classic_protocol::capabilities::value_type"); +} + +[[nodiscard]] bool connection_context::check_client_authentication() const { + return get_client_username() == get_server_username() && + get_client_auth_method_data() == + caching_sha2_password_authenticator::scramble( + get_server_password(), get_server_auth_method_data()); +} + +[[nodiscard]] bool +connection_context::check_shared_plugin_auth_supported() const { + // this method is not noexcept as it used std::bitset<>::test() which is + // not noexcept + return get_shared_capabilities().test( + classic_protocol::capabilities::pos::plugin_auth); +} + +[[nodiscard]] bool +connection_context::check_shared_text_result_with_session_tracking_supported() + const { + // this method is not noexcept as it used std::bitset<>::test() which is + // not noexcept + return get_shared_capabilities().test( + classic_protocol::capabilities::pos::text_result_with_session_tracking); +} + +[[nodiscard]] bool +connection_context::check_binlog_non_blocking_dump() const noexcept { + stdx::flags + binlog_dump_flags{}; + binlog_dump_flags.underlying_value(get_binlog_flags()); + return (binlog_dump_flags & + classic_protocol::message::client::BinlogDump::Flags::non_blocking); +} + +[[nodiscard]] std::size_t +connection_context::get_frame_header_length() noexcept { + return classic_protocol::Codec::max_size(); +} + +[[nodiscard]] std::size_t +connection_context::parse_frame_header(const connection_buffer_type &payload) { + + auto buffer{boost::asio::buffer(payload)}; + auto decode_result{ + classic_protocol::decode(buffer, {})}; + if (!decode_result) { + throw boost::system::system_error{decode_result.error()}; + } + + return decode_result.value().second.payload_size(); +} + +[[nodiscard]] std::string +connection_context::generate_encoded_server_greeting() { + std::string result_buffer{}; + + server_capabilities_ = get_default_server_capabilities(); + server_auth_method_ = std::string{default_server_auth_method}; + + // for historical reasons sever auth data must include a trailing '\0' byte + const std::string fixed_server_auth_data{generate_server_auth_method_data() + + '\0'}; + + const classic_protocol::message::server::Greeting server_greeting{ + default_server_protocol_version, + std::string{default_server_version}, + get_connection_id(), + fixed_server_auth_data, + get_server_capabilities(), + default_server_collation, + default_server_status_flags, + get_server_auth_method()}; + + using server_greeting_frame = classic_protocol::frame::Frame< + classic_protocol::message::server::Greeting>; + auto encode_result{classic_protocol::encode( + {generate_sequence_number(), server_greeting}, get_server_capabilities(), + boost::asio::dynamic_buffer(result_buffer))}; + + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } + return result_buffer; +} + +void connection_context::parse_client_greeting( + const connection_buffer_type &payload) { + auto buffer{boost::asio::buffer(payload)}; + using client_greeting_frame = classic_protocol::frame::Frame< + classic_protocol::message::client::Greeting>; + auto decode_result{classic_protocol::decode( + buffer, get_server_capabilities())}; + if (!decode_result) { + throw boost::system::system_error{decode_result.error()}; + } + + validate_and_update_sequence_number(decode_result.value().second.seq_id()); + + const auto &client_greeting{decode_result.value().second.payload()}; + + client_capabilities_ = client_greeting.capabilities(); + client_auth_method_ = client_greeting.auth_method_name(); + client_auth_method_data_ = client_greeting.auth_method_data(); + client_username_ = client_greeting.username(); + client_schema_ = client_greeting.schema(); + client_collation_ = client_greeting.collation(); + client_max_packet_size_ = client_greeting.max_packet_size(); + client_attributes_ = client_greeting.attributes(); +} + +[[nodiscard]] connection_buffer_type +connection_context::generate_encoded_fast_auth() { + std::string result_buffer{}; + + static constexpr std::string_view fast_auth_code{ + "\x03"}; // 0x03 means "fast auth success" in caching_sha2_password + // protocol + using auth_method_data_frame = classic_protocol::frame::Frame< + classic_protocol::message::server::AuthMethodData>; + auto encode_res = classic_protocol::encode( + {generate_sequence_number(), {std::string{fast_auth_code}}}, + get_shared_capabilities(), boost::asio::dynamic_buffer(result_buffer)); + + if (!encode_res) { + throw boost::system::system_error{encode_res.error()}; + } + return result_buffer; +} + +[[nodiscard]] connection_buffer_type connection_context::generate_encoded_ok() { + std::string result_buffer{}; + + const classic_protocol::message::server::Ok ok_message{}; + + using ok_frame = + classic_protocol::frame::Frame; + auto encode_result{classic_protocol::encode( + {generate_sequence_number(), ok_message}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffer))}; + + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } + return result_buffer; +} + +[[nodiscard]] connection_buffer_type +connection_context::generate_encoded_eof() { + std::string result_buffer{}; + + const classic_protocol::message::server::Eof eof_message{}; + + using eof_frame = + classic_protocol::frame::Frame; + auto encode_result{classic_protocol::encode( + {generate_sequence_number(), eof_message}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffer))}; + + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } + return result_buffer; +} + +[[nodiscard]] connection_buffer_type connection_context::generate_encoded_error( + std::uint16_t error_code, + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + std::string_view message, std::string_view sql_state) { + std::string result_buffer{}; + + classic_protocol::message::server::Error error_message{}; + error_message.error_code(error_code); + error_message.message(std::string{message}); + error_message.sql_state(std::string{sql_state}); + + using error_frame = + classic_protocol::frame::Frame; + auto encode_result{classic_protocol::encode( + {generate_sequence_number(), error_message}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffer))}; + + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } + return result_buffer; +} + +[[nodiscard]] connection_buffer_type +connection_context::generate_encoded_access_denied() { + return generate_encoded_error( + ER_ACCESS_DENIED_ERROR, + "Access Denied for user '" + get_client_username() + "'@'%'", "28000"); +} +[[nodiscard]] connection_buffer_type +connection_context::generate_encoded_unknown_command() { + return generate_encoded_error(ER_UNKNOWN_COM_ERROR, "Unknown command", + "HY000"); +} +[[nodiscard]] connection_buffer_type +connection_context::generate_encoded_syntax_error() { + // 00000 Success + // 01000 Warning + // 02000 No data + // 23000 Integrity constraint violation + // 28000 Invalid authorization + // 42000 Syntax error or access rule violation + // HY000 General error (MySQL‑specific catch‑all) + return generate_encoded_error(ER_PARSE_ERROR, "Syntax error", "42000"); +} + +[[nodiscard]] connection_buffer_type +connection_context::generate_encoded_binlog_event(std::string_view event_data) { + std::string result_buffer{}; + result_buffer.reserve(get_frame_header_length() + std::size(event_data) + 1U); + + auto encode_result{classic_protocol::encode( + {std::size(event_data) + 1U, generate_sequence_number()}, + get_shared_capabilities(), boost::asio::dynamic_buffer(result_buffer))}; + + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } + result_buffer += + '\0'; // the first byte in the payload must be '\0' to indicate OK + result_buffer += event_data; + return result_buffer; +} + +void connection_context::parse_client_command( + const connection_buffer_type &payload) { + if (payload.size() <= get_frame_header_length()) { + throw boost::system::system_error{ + make_error_code(std::errc::protocol_error)}; + } + + const auto command_byte_index{get_frame_header_length()}; + const auto command_byte{ + static_cast(payload[command_byte_index])}; + + switch (command_byte) { + case classic_protocol::Codec< + classic_protocol::message::client::Query>::cmd_byte(): { + auto frame = + decode_client_command_frame( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::query; + client_statement_ = frame.payload().statement(); + break; + } + case classic_protocol::Codec< + classic_protocol::message::client::Quit>::cmd_byte(): { + auto frame = + decode_client_command_frame( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::quit; + break; + } + case classic_protocol::Codec< + classic_protocol::message::client::ResetConnection>::cmd_byte(): { + auto frame = decode_client_command_frame< + classic_protocol::message::client::ResetConnection>( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::reset_connection; + break; + } + case classic_protocol::Codec< + classic_protocol::message::client::ChangeUser>::cmd_byte(): { + auto frame = decode_client_command_frame< + classic_protocol::message::client::ChangeUser>( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::change_user; + break; + } + case classic_protocol::Codec< + classic_protocol::message::client::Ping>::cmd_byte(): { + auto frame = + decode_client_command_frame( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::ping; + break; + } + case classic_protocol::Codec< + classic_protocol::message::client::BinlogDump>::cmd_byte(): { + auto frame = decode_client_command_frame< + classic_protocol::message::client::BinlogDump>( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::binlog_dump; + const auto &binlog_dump_payload{frame.payload()}; + binlog_flags_ = binlog_dump_payload.flags().underlying_value(); + binlog_server_id_ = binlog_dump_payload.server_id(); + binlog_filename_ = binlog_dump_payload.filename(); + binlog_position_ = binlog_dump_payload.position(); + break; + } + case classic_protocol::Codec< + classic_protocol::message::client::BinlogDumpGtid>::cmd_byte(): { + auto frame = decode_client_command_frame< + classic_protocol::message::client::BinlogDumpGtid>( + payload, get_shared_capabilities()); + validate_and_update_sequence_number(frame.seq_id()); + + client_command_ = client_command_type::binlog_dump_gtid; + break; + } + default: + client_command_ = client_command_type::unknown; + } +} + +std::atomic_uint32_t connection_context::next_connection_id_{0U}; + +[[nodiscard]] capability_bitset +connection_context::get_default_server_capabilities() noexcept { + return classic_protocol::capabilities::long_password | + classic_protocol::capabilities::found_rows | + classic_protocol::capabilities::long_flag | + classic_protocol::capabilities::connect_with_schema | + classic_protocol::capabilities::no_schema | + // compress (not yet) + classic_protocol::capabilities::odbc | + classic_protocol::capabilities::local_files | + // ignore_space (client only) + classic_protocol::capabilities::protocol_41 | + // interactive (client-only) + // ssl (below) + // ignore sigpipe (client-only) + classic_protocol::capabilities::transactions | + classic_protocol::capabilities::secure_connection | + // multi_statements (not yet) + classic_protocol::capabilities::multi_results | + classic_protocol::capabilities::ps_multi_results | + classic_protocol::capabilities::plugin_auth | + classic_protocol::capabilities::connect_attributes | + classic_protocol::capabilities::client_auth_method_data_varint + // disable expired_passwords + // disable session_track + // disable text_result_with_session_tracking + // optional_resultset_metadata (not yet) + // compress_zstd (not yet) + ; +} + +[[nodiscard]] const std::string & +connection_context::generate_server_auth_method_data() { + // TODO: generate random auth method data (for caching_sha2_password, it must + // be 20 random bytes) + server_auth_method_data_ = "01234567890123456789"; + return server_auth_method_data_; +} + +[[nodiscard]] std::uint8_t connection_context::generate_sequence_number() { + return sequence_number_++; +} + +void connection_context::validate_and_update_sequence_number( + std::uint8_t sequence_number) { + if (sequence_number != sequence_number_) { + throw boost::system::system_error{ + make_error_code(std::errc::protocol_error)}; + } + ++sequence_number_; +} + +void connection_context::encode_resultset_number_of_columns_internal( + connection_buffer_container &result_buffers, + std::size_t number_of_columns) { + const classic_protocol::wire::VarInt column_count{ + static_cast(number_of_columns)}; + using column_count_frame = + classic_protocol::frame::Frame; + const auto encode_result = classic_protocol::encode( + {generate_sequence_number(), column_count}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffers.emplace_back())); + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } +} + +struct connection_context::datatype_properties_type { + classic_protocol::collation::value_type collation; + std::uint32_t column_length; + classic_protocol::field_type::value_type field_type; + classic_protocol::column_def::value_type column_flags; + std::uint8_t decimals; +}; + +template struct standard_type_translator { + static constexpr connection_context::datatype_properties_type translate() { + classic_protocol::collation::value_type collation{}; + if constexpr (std::is_integral_v) { + collation = classic_protocol::collation::Binary; + } else if constexpr (std::is_same_v) { + collation = classic_protocol::collation::Latin1SwedishCi; + } + + std::uint32_t column_length{}; + if constexpr (std::is_integral_v) { + column_length = std::numeric_limits::digits10 + 1; + } else if constexpr (std::is_same_v) { + // just some default for string types, can be adjusted as needed + constexpr std::size_t default_varchar_column_length{255U}; + column_length = default_varchar_column_length; + } + + classic_protocol::field_type::value_type field_type{}; + if constexpr (std::is_integral_v) { + field_type = classic_protocol::field_type::LongLong; + } else if constexpr (std::is_same_v) { + field_type = classic_protocol::field_type::VarString; + } + + classic_protocol::column_def::value_type column_flags{}; + if constexpr (std::is_integral_v) { + column_flags |= classic_protocol::column_def::numeric; + if constexpr (std::is_unsigned_v) { + column_flags |= classic_protocol::column_def::is_unsigned; + } + } else if constexpr (std::is_same_v) { + // do nothing for string types + } + column_flags |= classic_protocol::column_def::not_null; + + std::uint8_t decimals{}; + if constexpr (std::is_integral_v) { + // for integral types it should be 0 + decimals = 0U; + } else if constexpr (std::is_same_v) { + constexpr std::uint8_t magic_varchar_decimals{31U}; + decimals = magic_varchar_decimals; + } + return {.collation = collation, + .column_length = column_length, + .field_type = field_type, + .column_flags = column_flags, + .decimals = decimals}; + } +}; + +template struct standard_type_translator> { + static constexpr connection_context::datatype_properties_type + translate() noexcept { + auto result{standard_type_translator::translate()}; + // for optional types we can just use the same translation as for the + // underlying type, but without the 'not_null' flag + result.column_flags &= ~classic_protocol::column_def::not_null; + return result; + } +}; + +template +static constexpr connection_context::datatype_properties_type + datatype_properties{standard_type_translator::translate()}; + +template +const connection_context::datatype_properties_type & +connection_context::get_datatype_properties() noexcept { + return datatype_properties; +} + +template const connection_context::datatype_properties_type & +connection_context::get_datatype_properties() noexcept; +template const connection_context::datatype_properties_type & +connection_context::get_datatype_properties< + std::optional>() noexcept; +template const connection_context::datatype_properties_type & +connection_context::get_datatype_properties() noexcept; +template const connection_context::datatype_properties_type & +connection_context::get_datatype_properties< + std::optional>() noexcept; + +void connection_context::encode_resultset_column_definition_internal( + connection_buffer_container &result_buffers, std::string_view db_name, + std::string_view table_name, const column_name_pair &column_name, + const datatype_properties_type &datatype_properties) { + // 'binary' collation for integer types + classic_protocol::message::server::ColumnMeta column_definition{ + "def" /* catalog */, + std::string{db_name} /* schema */, + std::string{table_name} /* table */, + std::string{table_name} /* orig_table */, + std::string{column_name.first} /* name */, + std::string{column_name.second} /* orig_name */, + datatype_properties.collation /* collation */, + datatype_properties.column_length /* column_length */, + datatype_properties.field_type /* type */, + datatype_properties.column_flags /* flags */, + datatype_properties.decimals /* decimals */ + }; + using column_definition_frame = classic_protocol::frame::Frame< + classic_protocol::message::server::ColumnMeta>; + const auto encode_result = classic_protocol::encode( + {generate_sequence_number(), std::move(column_definition)}, + get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffers.emplace_back())); + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } +} + +void connection_context::encode_resultset_intermediate_eof_internal( + connection_buffer_container &result_buffers) { + const auto encode_result = classic_protocol::encode< + classic_protocol::frame::Frame>( + {generate_sequence_number(), {}}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffers.emplace_back())); + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } +} + +void connection_context::encode_resultset_textualized_row_internal( + connection_buffer_container &result_buffers, + const optional_string_collection &row_values) { + static_assert( + std::is_same_v); + using row_frame = + classic_protocol::frame::Frame; + const auto encode_result = classic_protocol::encode( + {generate_sequence_number(), {row_values}}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffers.emplace_back())); + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } +} + +void connection_context::encode_resultset_eof_internal( + connection_buffer_container &result_buffers) { + classic_protocol::message::server::Eof eof_packet{}; + const classic_protocol::status::value_type eof_status_flags{ + classic_protocol::status::autocommit}; + eof_packet.status_flags(eof_status_flags); + using eof_frame = + classic_protocol::frame::Frame; + const auto encode_result = classic_protocol::encode( + {generate_sequence_number(), eof_packet}, get_shared_capabilities(), + boost::asio::dynamic_buffer(result_buffers.emplace_back())); + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } +} + +} // namespace minimysql diff --git a/src/minimysql/connection_context.hpp b/src/minimysql/connection_context.hpp new file mode 100644 index 0000000..cdf43eb --- /dev/null +++ b/src/minimysql/connection_context.hpp @@ -0,0 +1,307 @@ +// Copyright (c) 2023-2026 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#ifndef MINIMYSQL_CONNECTION_CONTEXT_HPP +#define MINIMYSQL_CONNECTION_CONTEXT_HPP + +#include "minimysql/connection_context_fwd.hpp" // IWYU pragma: export + +#include +#include +#include +#include +#include +#include + +namespace minimysql { + +class connection_context { +public: + struct datatype_properties_type; + + // Constants for the greetings packet + static constexpr std::uint8_t default_server_protocol_version{10U}; + static constexpr std::string_view default_server_version{"9.7.0-pbs"}; + static constexpr std::uint16_t default_server_status_flags{0U}; + static constexpr std::uint8_t default_server_collation{0U}; + static constexpr std::string_view default_server_auth_method{ + "caching_sha2_password"}; + + connection_context(std::string_view server_username, + std::string_view server_password); + + [[nodiscard]] const std::string &get_server_username() const noexcept { + return server_username_; + } + [[nodiscard]] const std::string &get_server_password() const noexcept { + return server_password_; + } + [[nodiscard]] bool check_client_authentication() const; + + [[nodiscard]] std::uint32_t get_connection_id() const noexcept { + return connection_id_; + } + [[nodiscard]] std::uint8_t get_sequence_number() const noexcept { + return sequence_number_; + } + + [[nodiscard]] const capability_bitset & + get_server_capabilities() const noexcept { + return server_capabilities_; + } + [[nodiscard]] const capability_bitset & + get_client_capabilities() const noexcept { + return client_capabilities_; + } + [[nodiscard]] capability_bitset get_shared_capabilities() const noexcept { + return client_capabilities_ & server_capabilities_; + } + [[nodiscard]] bool check_shared_plugin_auth_supported() const; + [[nodiscard]] bool + check_shared_text_result_with_session_tracking_supported() const; + + [[nodiscard]] const std::string &get_server_auth_method() const noexcept { + return server_auth_method_; + } + [[nodiscard]] const std::string & + get_server_auth_method_data() const noexcept { + return server_auth_method_data_; + } + + [[nodiscard]] const std::string &get_client_auth_method() const noexcept { + return client_auth_method_; + } + [[nodiscard]] const std::string & + get_client_auth_method_data() const noexcept { + return client_auth_method_data_; + } + + [[nodiscard]] const std::string &get_client_username() const noexcept { + return client_username_; + } + [[nodiscard]] const std::string &get_client_schema() const noexcept { + return client_schema_; + } + [[nodiscard]] std::uint8_t get_client_collation() const noexcept { + return client_collation_; + } + [[nodiscard]] std::uint32_t get_client_max_packet_size() const noexcept { + return client_max_packet_size_; + } + [[nodiscard]] const std::string &get_client_attributes() const noexcept { + return client_attributes_; + } + + [[nodiscard]] client_command_type get_client_mysql_command() const noexcept { + return client_command_; + } + [[nodiscard]] const std::string &get_client_statement() const noexcept { + return client_statement_; + } + + [[nodiscard]] std::uint16_t get_binlog_flags() const noexcept { + return binlog_flags_; + } + [[nodiscard]] bool check_binlog_non_blocking_dump() const noexcept; + [[nodiscard]] std::uint32_t get_binlog_server_id() const noexcept { + return binlog_server_id_; + } + [[nodiscard]] const std::string &get_binlog_filename() const noexcept { + return binlog_filename_; + } + [[nodiscard]] std::uint64_t get_binlog_position() const noexcept { + return binlog_position_; + } + + [[nodiscard]] static std::size_t get_frame_header_length() noexcept; + [[nodiscard]] static std::size_t + parse_frame_header(const connection_buffer_type &payload); + + [[nodiscard]] connection_buffer_type generate_encoded_server_greeting(); + void parse_client_greeting(const connection_buffer_type &payload); + + [[nodiscard]] connection_buffer_type generate_encoded_fast_auth(); + [[nodiscard]] connection_buffer_type generate_encoded_ok(); + [[nodiscard]] connection_buffer_type generate_encoded_eof(); + [[nodiscard]] connection_buffer_type + generate_encoded_error(std::uint16_t error_code, std::string_view message, + std::string_view sql_state); + [[nodiscard]] connection_buffer_type generate_encoded_access_denied(); + [[nodiscard]] connection_buffer_type generate_encoded_unknown_command(); + [[nodiscard]] connection_buffer_type generate_encoded_syntax_error(); + + [[nodiscard]] connection_buffer_type + generate_encoded_binlog_event(std::string_view event_data); + + void enter_command_loop_iteration() noexcept { + sequence_number_ = 0U; + client_command_ = client_command_type::unknown; + client_statement_.clear(); + } + void parse_client_command(const connection_buffer_type &payload); + + template + [[nodiscard]] connection_buffer_container encode_resultset( + const std::vector> &rows, + std::span column_names) { + static constexpr std::size_t number_of_columns{ + std::tuple_size_v>}; + const std::size_t number_of_rows{rows.size()}; + + const bool text_result_with_session_tracking_supported{ + check_shared_text_result_with_session_tracking_supported()}; + connection_buffer_container result_buffers{}; + // 1 for column count, 1 per column + (0 or 1) for intermediate EOF, 1 per + // row + 1 for EOF + result_buffers.reserve( + 1UZ + number_of_columns + + (text_result_with_session_tracking_supported ? 1UZ : 0UZ) + + number_of_rows + 1UZ); + + // part 1: column count + encode_resultset_number_of_columns_internal(result_buffers, + number_of_columns); + + // part 2: column definition(s) + encode_resultset_column_definitions_internal( + result_buffers, "db", "tbl", column_names); + + // part 2a: intermediate eof packet that is needed only when text resultset + // with session tracking is NOT supported by the client. + if (!text_result_with_session_tracking_supported) { + encode_resultset_intermediate_eof_internal(result_buffers); + } + + // part 3: row data + for (const auto &row : rows) { + encode_resultset_row_internal(result_buffers, row); + } + + // part 4: EOF + encode_resultset_eof_internal(result_buffers); + + return result_buffers; + } + +private: + static std::atomic_uint32_t next_connection_id_; + + std::string server_username_; + std::string server_password_; + + std::uint32_t connection_id_; + std::uint8_t sequence_number_{0U}; + + capability_bitset server_capabilities_{}; + capability_bitset client_capabilities_{}; + + std::string server_auth_method_{}; + std::string server_auth_method_data_{}; + + std::string client_auth_method_{}; + std::string client_auth_method_data_{}; + + std::string client_username_{}; + std::string client_schema_{}; + std::uint8_t client_collation_{0U}; + std::uint32_t client_max_packet_size_{0U}; + std::string client_attributes_{}; + + client_command_type client_command_{client_command_type::unknown}; + std::string client_statement_{}; + + std::uint16_t binlog_flags_{}; + std::uint32_t binlog_server_id_{}; + std::string binlog_filename_{}; + std::uint64_t binlog_position_{}; + + [[nodiscard]] static capability_bitset + get_default_server_capabilities() noexcept; + [[nodiscard]] const std::string &generate_server_auth_method_data(); + [[nodiscard]] std::uint8_t generate_sequence_number(); + void validate_and_update_sequence_number(std::uint8_t sequence_number); + + void encode_resultset_number_of_columns_internal( + connection_buffer_container &result_buffers, + std::size_t number_of_columns); + + template + static const datatype_properties_type &get_datatype_properties() noexcept; + + void encode_resultset_column_definition_internal( + connection_buffer_container &result_buffers, std::string_view db_name, + std::string_view table_name, const column_name_pair &column_name, + const datatype_properties_type &datatype_properties); + template + void encode_resultset_column_definitions_internal( + connection_buffer_container &result_buffers, + [[maybe_unused]] std::string_view db_name, + [[maybe_unused]] std::string_view table_name, + [[maybe_unused]] std::span + column_names) { + std::size_t column_index{0UZ}; + (encode_resultset_column_definition_internal( + result_buffers, db_name, table_name, column_names[column_index++], + get_datatype_properties()), + ...); + } + void encode_resultset_intermediate_eof_internal( + connection_buffer_container &result_buffers); + + using optional_string = std::optional; + using optional_string_collection = std::vector; + void encode_resultset_textualized_row_internal( + connection_buffer_container &result_buffers, + const optional_string_collection &row_values); + + template + static optional_string textualize_value(const Type &value) { + if constexpr (std::is_integral_v) { + return std::to_string(value); + } else if constexpr (std::is_same_v) { + return value; + } + } + template + static optional_string textualize_value(const std::optional &value) { + if (!value.has_value()) { + return {}; + } + return textualize_value(*value); + } + + template + void + encode_resultset_row_internal(connection_buffer_container &result_buffers, + const std::tuple &row) { + optional_string_collection textualized_row_values{}; + textualized_row_values.reserve(sizeof...(TypePack)); + const auto tuple_traversal_lambda{[&textualized_row_values, + &row]( + std::index_sequence) { + (textualized_row_values.emplace_back(textualize_value(std::get(row))), + ...); + }}; + tuple_traversal_lambda(std::index_sequence_for{}); + encode_resultset_textualized_row_internal(result_buffers, + textualized_row_values); + } + void + encode_resultset_eof_internal(connection_buffer_container &result_buffers); +}; + +} // namespace minimysql + +#endif // MINIMYSQL_CONNECTION_CONTEXT_HPP diff --git a/src/minimysql/connection_context_fwd.hpp b/src/minimysql/connection_context_fwd.hpp new file mode 100644 index 0000000..dfa57c7 --- /dev/null +++ b/src/minimysql/connection_context_fwd.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2023-2026 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#ifndef MINIMYSQL_CONNECTION_CONTEXT_FWD_HPP +#define MINIMYSQL_CONNECTION_CONTEXT_FWD_HPP + +#include +#include +#include +#include +#include + +#include + +namespace minimysql { + +using connection_buffer_type = std::string; +using connection_buffer_container = std::vector; + +inline constexpr auto number_of_capability_bits{32UZ}; +using capability_bitset = std::bitset; + +// first is column name, second is original column name +using column_name_pair = std::pair; + +// clang-format off +BOOST_DEFINE_FIXED_ENUM_CLASS(client_command_type, std::uint8_t, + query, + quit, + reset_connection, + change_user, + ping, + binlog_dump, + binlog_dump_gtid, + unknown) +// clang-format on + +class connection_context; + +} // namespace minimysql + +#endif // MINIMYSQL_CONNECTION_CONTEXT_FWD_HPP diff --git a/src/minimysql_app.cpp b/src/minimysql_app.cpp new file mode 100644 index 0000000..bc9d868 --- /dev/null +++ b/src/minimysql_app.cpp @@ -0,0 +1,772 @@ +// Copyright (c) 2023-20264 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +// Include What You Use pragma is needed here because the 'co_spawn()' +// function that is used in this file is located in the 'impl' subdirectory +// of the 'asio' headers ('boost/asio/impl/co_spawn.hpp') and should not be +// included directly, but the 'boost/asio/co_spawn.hpp' header is a public +// one that includes the 'impl' header +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include "minimysql/connection_context.hpp" + +class scope_tracer { +public: + explicit scope_tracer(std::string_view name) : name_(name) { + std::cout << "entering " << name_ << '\n'; + } + scope_tracer(const scope_tracer &) = delete; + scope_tracer &operator=(const scope_tracer &) = delete; + scope_tracer(scope_tracer &&) = delete; + scope_tracer &operator=(scope_tracer &&) = delete; + + ~scope_tracer() { std::cout << "leaving " << name_ << '\n'; } + +private: + std::string name_; +}; + +class hardcoded_event_data { +public: + using event_data_type = std::string; + static constexpr auto number_of_predefined_events{10UZ}; + using event_data_container = + std::array; + + hardcoded_event_data() + : events_{hex_to_bin(hardcoded_event_data::rotate_artificial), + hex_to_bin(hardcoded_event_data::fde), + hex_to_bin(hardcoded_event_data::previous_gtids), + hex_to_bin(hardcoded_event_data::first_gtid_log), + hex_to_bin(hardcoded_event_data::first_query), + hex_to_bin(hardcoded_event_data::second_gtid_log), + hex_to_bin(hardcoded_event_data::second_query), + hex_to_bin(hardcoded_event_data::second_table_map), + hex_to_bin(hardcoded_event_data::second_write_rows), + hex_to_bin(hardcoded_event_data::second_xid)} {} + + [[nodiscard]] const event_data_container &get_events() const noexcept { + return events_; + } + +private: + static constexpr std::string_view rotate_artificial{ + "00 00 00 00 04 01 00 00 00 28 00 00 00 00 00 00" + "00 20 00 04 00 00 00 00 00 00 00 62 69 6e 6c 6f" + "67 2e 30 30 30 30 30 31 "}; + static constexpr std::string_view fde{ + "1a b2 1c 6a 0f 01 00 00 00 7b 00 00 00 7f 00 00" + "00 00 00 04 00 38 2e 34 2e 37 2d 37 00 00 00 00" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + "00 00 00 00 00 00 00 1a b2 1c 6a 13 00 0d 00 08" + "00 00 00 00 04 00 04 00 00 00 63 00 04 1a 08 00" + "00 00 00 00 00 02 00 00 00 0a 0a 0a 2a 2a 00 12" + "34 00 0a 28 00 00 01 ee c6 67 82 "}; + static constexpr std::string_view previous_gtids{ + "1a b2 1c 6a 23 01 00 00 00 1f 00 00 00 9e 00 00" + "00 80 00 00 00 00 00 00 00 00 00 09 5d aa 16 "}; + static constexpr std::string_view first_gtid_log{ + "52 b2 1c 6a 21 01 00 00 00 4d 00 00 00 eb 00 00" + "00 00 00 01 b1 14 55 0c 5d 3d 11 f1 a7 3a 00 15" + "5d f1 f4 92 01 00 00 00 00 00 00 00 02 00 00 00" + "00 00 00 00 00 01 00 00 00 00 00 00 00 9c 7e f6" + "5f 24 53 06 cb 17 3a 01 00 80 dc f9 5c "}; + static constexpr std::string_view first_query{ + "52 b2 1c 6a 02 01 00 00 00 7e 00 00 00 69 01 00" + "00 00 00 09 00 00 00 00 00 00 00 04 00 00 2f 00" + "00 00 00 00 00 01 20 00 a0 45 00 00 00 00 06 03" + "73 74 64 04 ff 00 ff 00 ff 00 0c 01 74 65 73 74" + "00 11 06 00 00 00 00 00 00 00 12 ff 00 13 00 74" + "65 73 74 00 43 52 45 41 54 45 20 54 41 42 4c 45" + "20 74 31 28 69 64 20 53 45 52 49 41 4c 20 50 52" + "49 4d 41 52 59 20 4b 45 59 29 d9 1d 2b 2b "}; + static constexpr std::string_view second_gtid_log{ + "57 b2 1c 6a 21 01 00 00 00 4f 00 00 00 b8 01 00" + "00 00 00 00 b1 14 55 0c 5d 3d 11 f1 a7 3a 00 15" + "5d f1 f4 92 02 00 00 00 00 00 00 00 02 01 00 00" + "00 00 00 00 00 02 00 00 00 00 00 00 00 8a 35 4f" + "60 24 53 06 fc 15 01 17 3a 01 00 24 35 52 71 "}; + static constexpr std::string_view second_query{ + "57 b2 1c 6a 02 01 00 00 00 4b 00 00 00 03 02 00" + "00 08 00 09 00 00 00 00 00 00 00 04 00 00 1d 00" + "00 00 00 00 00 01 20 00 a0 45 00 00 00 00 06 03" + "73 74 64 04 ff 00 ff 00 ff 00 12 ff 00 74 65 73" + "74 00 42 45 47 49 4e c8 4c ce 80 "}; + static constexpr std::string_view second_table_map{ + "57 b2 1c 6a 13 01 00 00 00 30 00 00 00 33 02 00" + "00 00 00 73 00 00 00 00 00 01 00 04 74 65 73 74" + "00 02 74 31 00 01 08 00 00 01 01 80 21 d6 bc 8c"}; + static constexpr std::string_view second_write_rows{ + "57 b2 1c 6a 1e 01 00 00 00 2c 00 00 00 5f 02 00" + "00 00 00 73 00 00 00 00 00 01 00 02 00 01 ff 00" + "01 00 00 00 00 00 00 00 82 65 a7 86 "}; + static constexpr std::string_view second_xid{ + "57 b2 1c 6a 10 01 00 00 00 1f 00 00 00 7e 02 00" + "00 00 00 07 00 00 00 00 00 00 00 e3 36 06 8f "}; + + static std::string hex_to_bin(std::string_view hex) { + std::string filtered_hex{hex}; + std::erase(filtered_hex, ' '); + return boost::algorithm::unhex(filtered_hex); + } + + event_data_container events_; +}; + +namespace { + +constexpr std::uint16_t listening_port{3307}; +constexpr std::size_t expected_packet_size{4096}; +constexpr std::chrono::seconds session_authentication_timeout{10}; +constexpr std::chrono::seconds session_command_timeout{120}; +constexpr std::size_t max_payload_size{ + 16UZ * 1024UZ * 1024UZ}; // 16 MiB - MySQL's maximum packet size + +constexpr std::string_view default_username{"rpl"}; +constexpr std::string_view default_password{"password"}; + +void print_server_greeting( + const boost::asio::ip::tcp::endpoint &remote_endpoint, + const minimysql::connection_context &context) { + std::cout + << "generated server greeting for " << remote_endpoint << '\n' + << "[sequence_number " + << static_cast(context.get_sequence_number() - 1U) << "]\n" + << " protocol_version: " + << static_cast( + minimysql::connection_context::default_server_protocol_version) + << '\n' + << " server_version : " + << minimysql::connection_context::default_server_version << '\n' + << " connection_id : " << context.get_connection_id() << '\n' + << " collation : " + << static_cast( + minimysql::connection_context::default_server_collation) + << '\n' + << " status flags : " + << minimysql::connection_context::default_server_status_flags << '\n' + << " auth_method : " << context.get_server_auth_method() << '\n' + << " auth_method_data: " + << std::size(context.get_server_auth_method_data()) << " byte(s)\n"; +} + +void print_client_greeting( + const boost::asio::ip::tcp::endpoint &remote_endpoint, + const minimysql::connection_context &context) { + std::cout << "parsed client greeting from " << remote_endpoint << '\n' + << "[sequence_number " + << static_cast(context.get_sequence_number() - 1U) + << "]\n" + << " auth_method : " << context.get_client_auth_method() + << '\n' + << " auth_method_data: " + << std::size(context.get_client_auth_method_data()) << " byte(s)\n" + << " username : " << context.get_client_username() << '\n' + << " schema : " << context.get_client_schema() << '\n' + << " collation : " + << static_cast(context.get_client_collation()) + << '\n' + << " max_packet_size : " << context.get_client_max_packet_size() + << '\n'; +} +void print_generic(const boost::asio::ip::tcp::endpoint &remote_endpoint, + const minimysql::connection_context &context, + std::string_view label) { + std::cout << "generated " << label << " packet for " << remote_endpoint + << '\n' + << "[sequence_number " + << static_cast(context.get_sequence_number() - 1U) + << "]\n"; +} +void print_error(const boost::asio::ip::tcp::endpoint &remote_endpoint, + const minimysql::connection_context &context, + std::string_view label) { + std::cout << "generated error packet (" << label << ") for " + << remote_endpoint << '\n' + << "[sequence_number " + << static_cast(context.get_sequence_number() - 1U) + << "]\n"; +} +void print_client_command(const boost::asio::ip::tcp::endpoint &remote_endpoint, + const minimysql::connection_context &context) { + std::cout << "parsed client command from " << remote_endpoint << '\n' + << "[sequence_number " + << static_cast(context.get_sequence_number() - 1U) + << "]\n" + << " code : " + << boost::describe::enum_to_string( + context.get_client_mysql_command(), "unknown") + << '\n'; + if (context.get_client_mysql_command() == + minimysql::client_command_type::query) { + std::cout << " statement : " << context.get_client_statement() + << '\n'; + } + if (context.get_client_mysql_command() == + minimysql::client_command_type::binlog_dump) { + std::cout << " binlog blocking mode : " + << (context.check_binlog_non_blocking_dump() ? "non-" : "") + << "blocking\n"; + std::cout << " binlog server id : " << context.get_binlog_server_id() + << '\n'; + std::cout << " binlog file name : " << context.get_binlog_filename() + << '\n'; + std::cout << " binlog position : " << context.get_binlog_position() + << '\n'; + } +} + +// a helper function to log exceptions with context information +void handle_exception(std::string_view context) { + try { + throw; + } catch (const boost::system::system_error &e) { + bool logged_specific_error{false}; + const auto exception_error_code{e.code()}; + if (exception_error_code.category() == + boost::asio::error::get_system_category()) { + switch (exception_error_code.value()) { + case boost::asio::error::timed_out: + std::cout << context << ": " << e.what() << '\n'; + logged_specific_error = true; + break; + case boost::asio::error::operation_aborted: + std::cout << context << ": " << "operation aborted" << '\n'; + logged_specific_error = true; + break; + case boost::asio::error::eof: + case boost::asio::error::connection_reset: + std::cout << context << ": " << "connection closed by peer" << '\n'; + logged_specific_error = true; + break; + default: + // for other system errors we log the error code and message and rethrow + break; + } + } + + if (!logged_specific_error) { + std::cerr << "system error caught in " << context << ": " << e.code() + << '\n' + << e.what() << '\n'; + } + } catch (const std::exception &e) { + std::cerr << "exception caught in " << context << ": " << e.what() << '\n'; + } catch (...) { + std::cerr << "unknown exception caught in " << context << '\n'; + } +} + +// a helper coroutine for reading MySQL frame with a timeout - returns a tuple +// of (error_code, bytes_transferred) + +// as this coroutine is always used with co_await, it is absolutely safe to +// pass arguments by reference here +boost::asio::awaitable async_read_mysql_frame( + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + boost::asio::ip::tcp::socket &socket, + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + minimysql::connection_buffer_type &payload, + std::chrono::steady_clock::duration timeout) { + using namespace boost::asio::experimental::awaitable_operators; + + minimysql::connection_buffer_type local_payload{}; + auto payload_buffer{boost::asio::dynamic_buffer(local_payload)}; + + boost::asio::steady_timer read_timer{socket.get_executor(), timeout}; + // timed_read_result is a variant of 2 results (one form async read, one from + // timer) + auto timed_read_result{co_await ( + boost::asio::async_read( + socket, payload_buffer, + boost::asio::transfer_exactly( + minimysql::connection_context::get_frame_header_length()), + boost::asio::as_tuple(boost::asio::use_awaitable)) || + read_timer.async_wait( + boost::asio::as_tuple(boost::asio::use_awaitable)))}; + + // if timer finished first, we consider it a timeout error + if (timed_read_result.index() != 0UZ) { + throw boost::system::system_error{boost::asio::error::timed_out, + "frame header read timeout"}; + } + + // extracting the result of async_read for header + const auto &header_read_result{std::get<0UZ>(timed_read_result)}; + + // extracting the error code from the header_read_result and throwing if there + // was an error + const auto header_read_error_code{std::get<0UZ>(header_read_result)}; + if (header_read_error_code) { + throw boost::system::system_error{header_read_error_code, + "frame header read error"}; + } + + assert(std::size(local_payload) == + minimysql::connection_context::get_frame_header_length()); + assert(std::get<1UZ>(header_read_result) == + minimysql::connection_context::get_frame_header_length()); + + // checking the payload size from the header and throwing if it is larger than + // our maximum allowed size + auto payload_size{ + minimysql::connection_context::parse_frame_header(local_payload)}; + if (payload_size == max_payload_size) { + throw boost::system::system_error{boost::asio::error::message_size, + "frame payload size too large"}; + } + + // it is ok to reuse the same timer for reading the payload - calling + // expires_after() cancels any previously set timeout + read_timer.expires_after(timeout); + // reusing timed_read result for reading the payload + timed_read_result = co_await ( + boost::asio::async_read( + socket, payload_buffer, boost::asio::transfer_exactly(payload_size), + boost::asio::as_tuple(boost::asio::use_awaitable)) || + read_timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable))); + + // if timer finished first, we consider it a timeout error + if (timed_read_result.index() != 0UZ) { + throw boost::system::system_error{boost::asio::error::timed_out, + "frame payload read timeout"}; + } + + // extracting the result of async_read for payload + const auto &payload_read_result{std::get<0UZ>(timed_read_result)}; + // extracting the error code from payload_read_result and throwing if there + // was an error + const auto payload_read_error_code{std::get<0UZ>(payload_read_result)}; + if (payload_read_error_code) { + throw boost::system::system_error{payload_read_error_code, + "frame payload read error"}; + } + assert(std::size(local_payload) == + minimysql::connection_context::get_frame_header_length() + + payload_size); + assert(std::get<1UZ>(payload_read_result) == payload_size); + + payload.swap(local_payload); +} + +// a helper coroutine for writing with a timeout - +// throws on error + +// as this coroutine is always used with co_await, it is absolutely safe to +// pass arguments by reference here +boost::asio::awaitable async_write_mysql_frame( + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + boost::asio::ip::tcp::socket &socket, + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + const minimysql::connection_buffer_type &payload, + std::chrono::steady_clock::duration timeout) { + using namespace boost::asio::experimental::awaitable_operators; + + boost::asio::steady_timer write_timer{socket.get_executor(), timeout}; + // timed_write_result is a variant of 2 results (one form async write, one + // from timer) + auto timed_write_result{ + co_await (boost::asio::async_write( + socket, boost::asio::buffer(payload), + boost::asio::as_tuple(boost::asio::use_awaitable)) || + write_timer.async_wait( + boost::asio::as_tuple(boost::asio::use_awaitable)))}; + + // if timer finished first, we consider it a timeout error + if (timed_write_result.index() != 0UZ) { + throw boost::system::system_error{boost::asio::error::timed_out, + "frame write timeout"}; + } + + // extracting the result of async_write + const auto &write_result{std::get<0UZ>(timed_write_result)}; + // extracting the error code from async_write result and throwing if there was + // an error + const auto write_error_code{std::get<0UZ>(write_result)}; + if (write_error_code) { + throw boost::system::system_error{write_error_code, "frame write error"}; + } + assert(std::get<1UZ>(write_result) == std::size(payload)); +} + +// a helper coroutine for writing a collection of frames with a timeout - +// throws on error + +// as this coroutine is always used with co_await, it is absolutely safe to +// pass arguments by reference here +boost::asio::awaitable async_write_mysql_frames( + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + boost::asio::ip::tcp::socket &socket, + // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) + const minimysql::connection_buffer_container &payloads, + std::chrono::steady_clock::duration timeout) { + for (const auto &payload : payloads) { + co_await async_write_mysql_frame(socket, payload, timeout); + } +} + +// MySQL session handling coroutine - writes server greeting, then receives and +// parses client greeting +boost::asio::awaitable session(boost::asio::ip::tcp::socket socket) { + boost::system::error_code session_ec; + const auto remote_endpoint{socket.remote_endpoint(session_ec)}; + + const scope_tracer tracer("session " + + boost::lexical_cast(remote_endpoint)); + + try { + minimysql::connection_buffer_type data; + data.reserve(expected_packet_size); + + minimysql::connection_context context{default_username, default_password}; + + // creating and sending server greeting packet: + // protocol_version: 10 + // server_version: "9.7.0-pbs", + // connection_id: (maintained by connection_context, starts with + // 1 and is incremented for each new connection) auth_method_data: 20 + // random bytes generated by connection_context server_capabilities: + // collation: 0 (not set explicitly, client will assume the default one, + // most probably 255 utf8mb4_0900_ai_ci) status_flags: 0 auth_method: + // "caching_sha2_password" + + const auto server_greeting{context.generate_encoded_server_greeting()}; + print_server_greeting(remote_endpoint, context); + co_await async_write_mysql_frame(socket, server_greeting, + session_authentication_timeout); + std::cout << "sent server greeting (" << std::size(server_greeting) + << " bytes to " << remote_endpoint << ")\n"; + + // receiving and parsing client greeting packet: + // capabilities + // max_packet_size + // collation + // username + // auth_method_data + // schema + // auth_method_name + // attributes + + co_await async_read_mysql_frame(socket, data, + session_authentication_timeout); + std::cout << "received client greeting (" << std::size(data) + << " bytes from " << remote_endpoint << ")\n"; + context.parse_client_greeting(data); + print_client_greeting(remote_endpoint, context); + + if (!context.check_shared_plugin_auth_supported()) { + std::cout << "client does not support plugin authentication\n"; + const auto access_denied{context.generate_encoded_access_denied()}; + print_error(remote_endpoint, context, "plugin auth required"); + co_await async_write_mysql_frame(socket, access_denied, + session_authentication_timeout); + std::cout << "sent server access denied (" << std::size(access_denied) + << " bytes to " << remote_endpoint << ")\n"; + co_return; + } + + if (context.get_client_auth_method() != context.get_server_auth_method()) { + std::cout << "client requested " << context.get_client_auth_method() + << " authentication that does not match the one associated " + "with the user account (" + << context.get_server_auth_method() << ")\n"; + // TODO: send SwitchAuthentication packet + co_return; + } + if (!context.check_client_authentication()) { + std::cout << "client authentication failed for " + << context.get_client_username() << '\n'; + const auto access_denied{context.generate_encoded_access_denied()}; + print_error(remote_endpoint, context, "auth failure"); + co_await async_write_mysql_frame(socket, access_denied, + session_authentication_timeout); + std::cout << "sent server access denied (" << std::size(access_denied) + << " bytes to " << remote_endpoint << ")\n"; + co_return; + } + + std::cout << "client authentication succeeded for " + << context.get_client_username() << '\n'; + + // sending fast auth success + const auto fast_auth_success{context.generate_encoded_fast_auth()}; + print_generic(remote_endpoint, context, "auth method data (fast auth)"); + co_await async_write_mysql_frame(socket, fast_auth_success, + session_authentication_timeout); + std::cout << "sent server fast auth success (" + << std::size(fast_auth_success) << " bytes to " << remote_endpoint + << ")\n"; + + // sending server ok after successful authentication + const auto auth_ok{context.generate_encoded_ok()}; + print_generic(remote_endpoint, context, "ok (auth)"); + co_await async_write_mysql_frame(socket, auth_ok, + session_authentication_timeout); + std::cout << "sent server ok after authentication (" << std::size(auth_ok) + << " bytes to " << remote_endpoint << ")\n"; + + // defining known queries container + using query_handler_type = + std::function; + using query_container = std::unordered_map; + + const auto set_checksum_query_handler = + [](minimysql::connection_context &ctx) { + minimysql::connection_buffer_container resultset; + resultset.emplace_back(ctx.generate_encoded_ok()); + return resultset; + }; + query_container known_queries{ + {"select * from tbl", + [](minimysql::connection_context &ctx) { + using row_type = + std::tuple, + std::string, std::optional>; + using row_collection_type = std::vector; + const row_collection_type rows{{1, 100, "Alice", "Cooper"}, + {2, {}, "Bob", {}}}; + const std::array column_names{ + minimysql::column_name_pair{"id", "id"}, + minimysql::column_name_pair{"optional_id", "optional_id"}, + minimysql::column_name_pair{"name", "name"}, + minimysql::column_name_pair{"optional_name", "optional_name"}}; + return ctx.encode_resultset(rows, column_names); + }}, + {"select @@version_comment limit 1", + [](minimysql::connection_context &ctx) { + using version_comment_record = std::tuple; + using version_comment_record_collection = + std::vector; + const version_comment_record_collection records{ + {"Percona Binlog Server - GPL"}}; + const std::array column_names{ + minimysql::column_name_pair{"@@version_comment", ""}}; + return ctx.encode_resultset(records, column_names); + }}, + {"SELECT VERSION()", + [](minimysql::connection_context &ctx) { + using version_record = std::tuple; + using version_record_collection = std::vector; + const version_record_collection records{{"9.7.0"}}; + const std::array column_names{ + minimysql::column_name_pair{"VERSION()", ""}}; + return ctx.encode_resultset(records, column_names); + }}, + {"SET @source_binlog_checksum = 'NONE', @master_binlog_checksum = " + "'NONE'", + set_checksum_query_handler}, + {"SET @master_binlog_checksum = 'NONE', @source_binlog_checksum = " + "'NONE'", + set_checksum_query_handler}}; + + // starting command loop + bool terminated{false}; + while (!terminated) { + context.enter_command_loop_iteration(); + co_await async_read_mysql_frame(socket, data, session_command_timeout); + std::cout << "received client command (" << std::size(data) + << " bytes from " << remote_endpoint << ")\n"; + context.parse_client_command(data); + print_client_command(remote_endpoint, context); + + switch (context.get_client_mysql_command()) { + case minimysql::client_command_type::query: { + const auto known_query_it{ + known_queries.find(context.get_client_statement())}; + if (known_query_it != std::end(known_queries)) { + const auto resultset{known_query_it->second(context)}; + print_generic(remote_endpoint, context, "resultset"); + co_await async_write_mysql_frames(socket, resultset, + session_command_timeout); + std::cout << "sent server resultset (" << std::size(resultset) + << " frames to " << remote_endpoint << ")\n"; + } else { + // return 'syntax error' for every other query + const auto syntax_error = context.generate_encoded_syntax_error(); + print_error(remote_endpoint, context, "syntax error"); + co_await async_write_mysql_frame(socket, syntax_error, + session_command_timeout); + std::cout << "sent server syntax error (" << std::size(syntax_error) + << " bytes to " << remote_endpoint << ")\n"; + } + } break; + case minimysql::client_command_type::ping: { + const auto ok_after_ping{context.generate_encoded_ok()}; + print_generic(remote_endpoint, context, "ok (ping success)"); + co_await async_write_mysql_frame(socket, ok_after_ping, + session_command_timeout); + std::cout << "sent server ok after ping (" << std::size(ok_after_ping) + << " bytes to " << remote_endpoint << ")\n"; + } break; + case minimysql::client_command_type::binlog_dump: { + const hardcoded_event_data event_block; + for (const auto &event_data : event_block.get_events()) { + const auto event{context.generate_encoded_binlog_event(event_data)}; + print_generic(remote_endpoint, context, "binlog event"); + co_await async_write_mysql_frame(socket, event, + session_command_timeout); + std::cout << "sent server binlog event (" << std::size(event) + << " bytes to " << remote_endpoint << ")\n"; + // co_await async_read_mysql_frame(socket, data, + // session_command_timeout); std::cout << "received binlog event reply + // command (" << std::size(data) << " bytes from " << remote_endpoint + // << ")\n"; + } + const auto eof = context.generate_encoded_eof(); + print_generic(remote_endpoint, context, "binlog eof"); + co_await async_write_mysql_frame(socket, eof, session_command_timeout); + std::cout << "sent server eof (" << std::size(eof) << " bytes to " + << remote_endpoint << ")\n"; + terminated = true; + } break; + case minimysql::client_command_type::quit: { + // TODO: read EOF from the socket to make sure the client has closed the + // connection instead of just closing it from our side + terminated = true; + } break; + default: { + const auto unknown_command_error = + context.generate_encoded_unknown_command(); + print_error(remote_endpoint, context, "unknown command"); + co_await async_write_mysql_frame(socket, unknown_command_error, + session_command_timeout); + std::cout << "sent server unknown command (" + << std::size(unknown_command_error) << " bytes to " + << remote_endpoint << ")\n"; + } + } + } + } catch (...) { + const std::string context{ + "session " + boost::lexical_cast(remote_endpoint)}; + handle_exception(context); + } +} + +// listener coroutine - accepts incoming connections and spawns a session +// coroutine for each accepted connection + +// it is safe to pass the acceptor by reference here, as the instance of the +// acceptor object is guaranteed to outlive the listener coroutine, as it is +// created in main() and destroyed after the io_context.run() call returns +boost::asio::awaitable +// NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) +listener(boost::asio::ip::tcp::acceptor &acceptor) { + const scope_tracer tracer("listener"); + + auto executor = acceptor.get_executor(); + + try { + for (;;) { + boost::system::error_code listener_ec; + boost::asio::ip::tcp::socket socket = co_await acceptor.async_accept( + boost::asio::redirect_error(boost::asio::use_awaitable, listener_ec)); + if (listener_ec) { + if (listener_ec != boost::asio::error::operation_aborted && + listener_ec != boost::asio::error::bad_descriptor) { + throw boost::system::system_error{listener_ec}; + } + std::cout << "listener stopped\n"; + break; + } + + const auto remote_endpoint{socket.remote_endpoint(listener_ec)}; + std::cout << "accepted connection from " << remote_endpoint << '\n'; + + // NOLINTNEXTLINE(misc-include-cleaner) + boost::asio::co_spawn(executor, session(std::move(socket)), + boost::asio::detached); + } + } catch (...) { + handle_exception("listener"); + } +} + +} // anonymous namespace + +int main(int /* argc */, char * /* argv */[]) { + int res{EXIT_FAILURE}; + try { + std::cout << "starting mini-mysql-server" << '\n'; + boost::asio::io_context ctx; + const boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), + listening_port); + boost::asio::ip::tcp::acceptor acceptor(ctx, endpoint); + + boost::asio::signal_set signals(ctx, SIGINT, SIGTERM); + signals.async_wait([&](auto, auto) { + std::cout << "received termination signal\n"; + // stop accepting new clients; existing sessions keep running + boost::system::error_code close_ec; + acceptor.close(close_ec); + }); + + // NOLINTNEXTLINE(misc-include-cleaner) + boost::asio::co_spawn(ctx, listener(acceptor), boost::asio::detached); + const auto ctx_run_result{ctx.run()}; + std::cout << "ctx.run() returned " << ctx_run_result << '\n'; + res = EXIT_SUCCESS; + std::cout << "stopping mini-mysql-server\n"; + } catch (...) { + handle_exception("main"); + } + + return res; +} diff --git a/tests/gtid_set_test.cpp b/tests/gtid_set_test.cpp index 16a1b70..d3b170e 100644 --- a/tests/gtid_set_test.cpp +++ b/tests/gtid_set_test.cpp @@ -712,9 +712,6 @@ BOOST_AUTO_TEST_CASE(GtidSetWhitespaces) { // looks like a misconfiguration in clang-tidy-19 that doesn't know that // std::erase() for std::string is located in the system header - // TODO: re-check this when switching to clang-20 - - // NOLINTNEXTLINE(misc-include-cleaner) std::erase(gtids_str, ' '); BOOST_CHECK(gtids_str.find(' ') == std::string::npos); const auto restored_gtids{