From af6f71cfbe5822e4ff3a865059ee22af910ec6b7 Mon Sep 17 00:00:00 2001 From: Anefu Date: Wed, 30 Oct 2024 18:28:40 +0100 Subject: [PATCH 1/3] feat: first pass at integrating support for ClickHouse --- include/boost/mysql/connection.hpp | 2 +- .../boost/mysql/impl/internal/auth/auth.ipp | 16 +++++---- .../impl/internal/protocol/db_flavor.hpp | 3 +- .../internal/protocol/deserialization.hpp | 34 +++++++++++++------ .../mysql/impl/internal/sansio/handshake.hpp | 2 +- test/unit/test/protocol/deserialization.cpp | 6 ++-- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/include/boost/mysql/connection.hpp b/include/boost/mysql/connection.hpp index c59d9e7a2..d1701ec85 100644 --- a/include/boost/mysql/connection.hpp +++ b/include/boost/mysql/connection.hpp @@ -241,7 +241,7 @@ class connection * If the final handler has an associated immediate executor, and the operation * completes immediately, the final handler is dispatched to it. * Otherwise, the final handler is called as if it was submitted using `asio::post`, - * and is never be called inline from within this function. + * and is never to be called inline from within this function. */ template < typename EndpointType, diff --git a/include/boost/mysql/impl/internal/auth/auth.ipp b/include/boost/mysql/impl/internal/auth/auth.ipp index 2ac250b79..de408ba07 100644 --- a/include/boost/mysql/impl/internal/auth/auth.ipp +++ b/include/boost/mysql/impl/internal/auth/auth.ipp @@ -174,13 +174,17 @@ struct authentication_plugin BOOST_INLINE_CONSTEXPR authentication_plugin all_authentication_plugins[] = { { - make_string_view("mysql_native_password"), - &mnp_compute_response, - }, + make_string_view("mysql_native_password"), + &mnp_compute_response, + }, { - make_string_view("caching_sha2_password"), - &csha2p_compute_response, - }, + make_string_view("caching_sha2_password"), + &csha2p_compute_response, + }, + { + make_string_view("double_sha1_password"), + &mnp_compute_response, + }, }; inline const authentication_plugin* find_plugin(string_view name) diff --git a/include/boost/mysql/impl/internal/protocol/db_flavor.hpp b/include/boost/mysql/impl/internal/protocol/db_flavor.hpp index b33be25a7..2def18dae 100644 --- a/include/boost/mysql/impl/internal/protocol/db_flavor.hpp +++ b/include/boost/mysql/impl/internal/protocol/db_flavor.hpp @@ -15,7 +15,8 @@ namespace detail { enum class db_flavor { mysql, - mariadb + mariadb, + clickhouse }; } // namespace detail diff --git a/include/boost/mysql/impl/internal/protocol/deserialization.hpp b/include/boost/mysql/impl/internal/protocol/deserialization.hpp index 5505415d7..6109445b6 100644 --- a/include/boost/mysql/impl/internal/protocol/deserialization.hpp +++ b/include/boost/mysql/impl/internal/protocol/deserialization.hpp @@ -47,7 +47,7 @@ namespace mysql { namespace detail { // OK packets (views because strings are non-owning) -inline error_code deserialize_ok_packet(span msg, ok_view& output); // for testing +inline error_code deserialize_ok_packet(span msg, ok_view& output, db_flavor flavor); // for testing // Error packets (exposed for testing) struct err_view @@ -269,7 +269,8 @@ BOOST_INLINE_CONSTEXPR std::uint8_t ok_packet_header = 0x00; // OK packets boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( span msg, - ok_view& output + ok_view& output, + db_flavor flavor ) { struct ok_packet @@ -280,7 +281,8 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( int2 status_flags; // server_status_flags int2 warnings; // CLIENT_SESSION_TRACK: not implemented - string_lenenc info; + using info_type = boost::variant2::variant; + info_type info; } pack{}; deserialization_context ctx(msg); @@ -290,7 +292,13 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( if (ctx.enough_size(1)) // message is optional, may be omitted { - err = pack.info.deserialize(ctx); + if (flavor == db_flavor::clickhouse) { + pack.info = string_eof{}; + } else{ + pack.info = string_lenenc{}; + } + err = boost::variant2::visit( [&]( auto& info ){ return info.deserialize(ctx); }, pack.info ); + // err = pack.info.deserialize(ctx); if (err != deserialize_errc::ok) return to_error_code(err); } @@ -300,7 +308,7 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( pack.last_insert_id.value, pack.status_flags.value, pack.warnings.value, - pack.info.value, + boost::variant2::visit( [&]( auto& info ){ return info.value; }, pack.info ), }; return ctx.check_extra_bytes(); @@ -473,7 +481,7 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_response( { // Verify that the ok_packet is correct ok_view ok{}; - err = deserialize_ok_packet(ctx.to_span(), ok); + err = deserialize_ok_packet(ctx.to_span(), ok, flavor); if (err) return err; backslash_escapes = ok.backslash_escapes(); @@ -574,7 +582,7 @@ boost::mysql::detail::execute_response boost::mysql::detail::deserialize_execute if (msg_type.value == ok_packet_header) { ok_view ok{}; - err = deserialize_ok_packet(ctx.to_span(), ok); + err = deserialize_ok_packet(ctx.to_span(), ok, flavor); if (err) return err; return ok; @@ -630,7 +638,7 @@ boost::mysql::detail::row_message boost::mysql::detail::deserialize_row_message( { // end of resultset => this is a ok_packet, not a row ok_view ok{}; - auto err = deserialize_ok_packet(ctx.to_span(), ok); + auto err = deserialize_ok_packet(ctx.to_span(), ok, flavor); if (err) return err; return ok; @@ -762,7 +770,13 @@ inline capabilities compose_capabilities(string_fixed<2> low, string_fixed<2> hi inline db_flavor parse_db_version(string_view version_string) { - return version_string.find("MariaDB") != string_view::npos ? db_flavor::mariadb : db_flavor::mysql; + if (version_string.find("MariaDB") != std::string_view::npos) { + return db_flavor::mariadb; + } else if (version_string.find("ClickHouse") != std::string_view::npos) { + return db_flavor::clickhouse; + } else { + return db_flavor::mysql; + } } } // namespace detail @@ -940,7 +954,7 @@ boost::mysql::detail::handhake_server_response boost::mysql::detail::deserialize if (msg_type.value == ok_packet_header) { ok_view ok{}; - err = deserialize_ok_packet(ctx.to_span(), ok); + err = deserialize_ok_packet(ctx.to_span(), ok, flavor); if (err) return err; return ok; diff --git a/include/boost/mysql/impl/internal/sansio/handshake.hpp b/include/boost/mysql/impl/internal/sansio/handshake.hpp index 3fdb090f0..6d58e8269 100644 --- a/include/boost/mysql/impl/internal/sansio/handshake.hpp +++ b/include/boost/mysql/impl/internal/sansio/handshake.hpp @@ -185,7 +185,7 @@ class handshake_algo error_code process_ok(connection_state_data& st) { ok_view res{}; - auto ec = deserialize_ok_packet(st.reader.message(), res); + auto ec = deserialize_ok_packet(st.reader.message(), res, st.flavor); if (ec) return ec; on_success(st, res); diff --git a/test/unit/test/protocol/deserialization.cpp b/test/unit/test/protocol/deserialization.cpp index ebedc49b7..9b4c71113 100644 --- a/test/unit/test/protocol/deserialization.cpp +++ b/test/unit/test/protocol/deserialization.cpp @@ -116,7 +116,8 @@ BOOST_AUTO_TEST_CASE(ok_view_success) BOOST_TEST_CONTEXT(tc.name) { ok_view actual{}; - error_code err = deserialize_ok_packet(tc.serialized, actual); + db_flavor flavor{db_flavor::mysql}; + error_code err = deserialize_ok_packet(tc.serialized, actual, flavor); // No error BOOST_TEST(err == error_code()); @@ -153,7 +154,8 @@ BOOST_AUTO_TEST_CASE(ok_view_error) BOOST_TEST_CONTEXT(tc.name) { ok_view value{}; - error_code err = deserialize_ok_packet(tc.serialized, value); + db_flavor flavor{db_flavor::mysql}; + error_code err = deserialize_ok_packet(tc.serialized, value, flavor); BOOST_TEST(err == tc.expected_err); } } From f1df4194d97a2c4edf01fed03422cb0d018d0242 Mon Sep 17 00:00:00 2001 From: Anefu Date: Wed, 30 Oct 2024 20:03:05 +0100 Subject: [PATCH 2/3] replace boost-variant type for ok_packet.info --- .../internal/protocol/deserialization.hpp | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/include/boost/mysql/impl/internal/protocol/deserialization.hpp b/include/boost/mysql/impl/internal/protocol/deserialization.hpp index 6109445b6..be6f48b34 100644 --- a/include/boost/mysql/impl/internal/protocol/deserialization.hpp +++ b/include/boost/mysql/impl/internal/protocol/deserialization.hpp @@ -71,6 +71,12 @@ BOOST_ATTRIBUTE_NODISCARD inline error_code process_error_packet( // Applicable for commands like ping and reset connection. // If the response is an OK packet, sets backslash_escapes according to the // OK packet's server status flags +BOOST_ATTRIBUTE_NODISCARD inline deserialize_errc get_info_value( + deserialization_context& ctx, + string_view& output, + db_flavor flavor +); + BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_ok_response( span message, db_flavor flavor, @@ -267,6 +273,28 @@ BOOST_INLINE_CONSTEXPR std::uint8_t ok_packet_header = 0x00; // // OK packets +boost::mysql::detail::deserialize_errc boost::mysql::detail::get_info_value( + deserialization_context& ctx, + string_view& output, + db_flavor flavor +) +{ + boost::mysql::detail::deserialize_errc err; + if (flavor == db_flavor::clickhouse) + { + string_eof info; + err = info.deserialize(ctx); + output = info.value; + + } else + { + string_lenenc info; + err = info.deserialize(ctx); + output = info.value; + } + return err; +} + boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( span msg, ok_view& output, @@ -280,11 +308,10 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( int_lenenc last_insert_id; int2 status_flags; // server_status_flags int2 warnings; - // CLIENT_SESSION_TRACK: not implemented - using info_type = boost::variant2::variant; - info_type info; } pack{}; + string_view info_value; + deserialization_context ctx(msg); auto err = ctx.deserialize(pack.affected_rows, pack.last_insert_id, pack.status_flags, pack.warnings); if (err != deserialize_errc::ok) @@ -292,13 +319,8 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( if (ctx.enough_size(1)) // message is optional, may be omitted { - if (flavor == db_flavor::clickhouse) { - pack.info = string_eof{}; - } else{ - pack.info = string_lenenc{}; - } - err = boost::variant2::visit( [&]( auto& info ){ return info.deserialize(ctx); }, pack.info ); - // err = pack.info.deserialize(ctx); + // CLIENT_SESSION_TRACK: not implemented + err = get_info_value(ctx, info_value, flavor); if (err != deserialize_errc::ok) return to_error_code(err); } @@ -308,7 +330,7 @@ boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet( pack.last_insert_id.value, pack.status_flags.value, pack.warnings.value, - boost::variant2::visit( [&]( auto& info ){ return info.value; }, pack.info ), + info_value, }; return ctx.check_extra_bytes(); From 8694749d0cabf51e0894df1fa0ed9f473a046ca1 Mon Sep 17 00:00:00 2001 From: Anefu Date: Wed, 30 Oct 2024 20:10:50 +0100 Subject: [PATCH 3/3] update parse_ok_packet in fuzz ok_packet test --- test/fuzzing/fuzz_ok_packet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/fuzzing/fuzz_ok_packet.cpp b/test/fuzzing/fuzz_ok_packet.cpp index 168fac353..1b0674eab 100644 --- a/test/fuzzing/fuzz_ok_packet.cpp +++ b/test/fuzzing/fuzz_ok_packet.cpp @@ -14,7 +14,8 @@ using namespace boost::mysql::detail; static bool parse_ok_packet(const uint8_t* data, size_t size) noexcept { ok_view msg{}; - auto ec = deserialize_ok_packet({data, size}, msg); + db_flavor flavor{db::mysql}; + auto ec = deserialize_ok_packet({data, size}, msg, flavor); return !ec.failed() && msg.is_out_params(); }