Skip to content

Commit

Permalink
Added more consistency checks into reader_context (#28)
Browse files Browse the repository at this point in the history
When 'format description' event is encountered, we expect it to have
a recognized binlog version (default_binlog_version == 4).
Also, the length of the common header specified in FDE should match
with what we use during parsing (default_common_header_length == 19).
We also check that the values from the post header length array in FDE
are the same as what we use for parsing
(generic_post_header<xxx>::size_in_bytes).
  • Loading branch information
percona-ysorokin authored Dec 20, 2023
1 parent a385fee commit e2d8a8d
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 11 deletions.
89 changes: 84 additions & 5 deletions src/binsrv/event/reader_context.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
#include "binsrv/event/reader_context.hpp"

#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>

#include "binsrv/event/checksum_algorithm_type.hpp"
#include "binsrv/event/code_type.hpp"
#include "binsrv/event/event.hpp"
#include "binsrv/event/protocol_traits.hpp"

#include "util/conversion_helpers.hpp"
#include "util/exception_location_helpers.hpp"

namespace binsrv::event {

Expand All @@ -19,11 +34,63 @@ void reader_context::process_event(const event &current_event) {
current_event.get_post_header<code_type::format_description>()};
const auto &body{current_event.get_body<code_type::format_description>()};

// TODO: check if binlog_version == default_binlog_version
// TODO: check if common_header_length == default_common_header_length
// TODO: check if post_header_lengths for known events has expected
// generic_post_header_impl<code_type::xxx>::size_in_bytes
// (at least for 'format_description' and 'rotate')
// check if FDE has expected binlog version number
if (post_header.get_binlog_version_raw() != default_binlog_version) {
util::exception_location().raise<std::logic_error>(
"unexpected binlog version number in format description event");
}

// check if FDE has expected common header size
if (post_header.get_common_header_length() !=
default_common_header_length) {
util::exception_location().raise<std::logic_error>(
"unexpected common header length in format description event");
}

// check if the values from the post_header_lengths array are the same as
// generic_post_header_impl<code_type::xxx>::size_in_bytes for known events

// here we use a trick with immediately invoked lambda to initialize a
// constexpr array which would have expected post header lengths for all
// event codes based on generic_post_header<xxx>::size_in_bytes

// we ignore the very first element in the code_type enum
// (code_type::unknown) since the post header length for this value is
// simply not included into FDE post header

// therefore, the size of the array is default_number_of_events - 1
using length_container =
std::array<std::size_t, default_number_of_events - 1U>;
static constexpr length_container expected_post_header_lengths{
[]<std::size_t... IndexPack>(
std::index_sequence<IndexPack...>) -> length_container {
return {generic_post_header<util::index_to_enum<code_type>(
IndexPack + 1U)>::size_in_bytes...};
}(std::make_index_sequence<default_number_of_events - 1U>{})};

static_assert(
std::tuple_size_v<std::remove_cvref_t<
decltype(post_header.get_post_header_lengths_raw())>> ==
std::tuple_size_v<decltype(expected_post_header_lengths)>,
"mismatch in size between expected_header_lengths and "
"post_header.get_post_header_lengths_raw()");
const auto length_mismatch_result{std::ranges::mismatch(
post_header.get_post_header_lengths_raw(), expected_post_header_lengths,
[](post_header_length_container::value_type real,
std::size_t expected) {
return expected == unknown_post_header::size_in_bytes ||
static_cast<std::size_t>(real) == expected;
})};
if (length_mismatch_result.in2 != std::end(expected_post_header_lengths)) {
const auto offset{static_cast<std::size_t>(
std::distance(std::begin(expected_post_header_lengths),
length_mismatch_result.in2))};
const std::string label{
to_string_view(util::index_to_enum<code_type>(offset + 1U))};
util::exception_location().raise<std::logic_error>(
"mismatch in expected post header length in FDE for '" + label +
"' event");
}

fde_processed_ = true;
post_header_lengths_ = post_header.get_post_header_lengths_raw();
Expand All @@ -35,4 +102,16 @@ void reader_context::process_event(const event &current_event) {
// calculated one
}

[[nodiscard]] checksum_algorithm_type
reader_context::get_current_checksum_algorithm() const noexcept {
assert(has_fde_processed());
return checksum_algorithm_;
}

[[nodiscard]] std::size_t
reader_context::get_current_post_header_length(code_type code) const noexcept {
assert(has_fde_processed());
return get_post_header_length_for_code(post_header_lengths_, code);
}

} // namespace binsrv::event
8 changes: 2 additions & 6 deletions src/binsrv/event/reader_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,9 @@ class [[nodiscard]] reader_context {
return fde_processed_;
}
[[nodiscard]] checksum_algorithm_type
get_current_checksum_algorithm() const noexcept {
return checksum_algorithm_;
}
get_current_checksum_algorithm() const noexcept;
[[nodiscard]] std::size_t
get_current_post_header_length(code_type code) const noexcept {
return get_post_header_length_for_code(post_header_lengths_, code);
}
get_current_post_header_length(code_type code) const noexcept;

private:
bool fde_processed_{false};
Expand Down
4 changes: 4 additions & 0 deletions src/binsrv/event/unknown_post_header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@

#include "binsrv/event/unknown_post_header_fwd.hpp" // IWYU pragma: export

#include <cstddef>

#include "util/byte_span_fwd.hpp"

namespace binsrv::event {

class [[nodiscard]] unknown_post_header {
public:
static constexpr std::size_t size_in_bytes{~static_cast<std::size_t>(0U)};

// this class will be used as the first type of the event post header
// variant, so it needs to be default constructible
unknown_post_header() noexcept = default;
Expand Down

0 comments on commit e2d8a8d

Please sign in to comment.