Skip to content

Commit 86fb949

Browse files
PS-10243 feature: Add data structures for GTID set operations (part 2) (#77)
https://perconadev.atlassian.net/browse/PS-10243 'binsrv::gtids' namespace extended with the following classes / typedefs: * 'tag_storage' - an alias for the 'util::bounded_string_storage<32>', used in GTID_TAGGED_LOG event body * 'gno_t' - an unsigned integer used to represent the 'gno' part of 'GTID'. * 'tag' class, which is used to represent GTID tag, implemented via 'tag_storage' but performs character validation. * 'gtid' class, which is used to represent GTID, incapsulates 'uuid', optional 'tag', and 'gno'. * 'gtid_set' class, which is used to represent a set of GTIDs, can combine elements and other sets, knows how to generate its string representation. Added unit tests based on boost::test framework for the GTID classes: * 'tag_test' * 'gtid_test' * 'gtid_set_test' Main CMakeLists.txt file extended with standard 'BUILD_TESTING' option, imported from 'include(CTest)'. 'BUILD_TESTING' CMake option is now enabled by default in all CMake presets. Temporarily disabled running 'ctest' in GitHub Actions in clang-19 ASan configuration because of the known "-stdlib=libc++ -fsanitize=address" alloc-dealloc-mismatch issue (llvm/llvm-project#59432), which is expected to be fixed in clang-20.
1 parent bfee64e commit 86fb949

21 files changed

Lines changed: 1189 additions & 36 deletions

.github/workflows/cmake.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,35 +64,41 @@ jobs:
6464
- {
6565
name: "GCC 14 Debug",
6666
label: "debug_gcc14",
67-
run_mtr: true
67+
run_mtr: true,
68+
run_ctest: true
6869
}
6970
- {
7071
name: "GCC 14 RelWithDebInfo",
7172
label: "release_gcc14",
72-
run_mtr: true
73+
run_mtr: true,
74+
run_ctest: true
7375
}
7476
- {
7577
name: "GCC 14 ASan",
7678
label: "asan_gcc14",
7779
run_mtr: true,
78-
mtr_options: "--sanitize"
80+
mtr_options: "--sanitize",
81+
run_ctest: true
7982
}
8083
- {
8184
name: "Clang 19 Debug",
8285
label: "debug_clang19",
83-
run_clang_tidy: true
86+
run_clang_tidy: true,
87+
run_ctest: true
8488
}
8589
- {
8690
name: "Clang 19 RelWithDebInfo",
8791
label: "release_clang19",
88-
run_clang_tidy: true
92+
run_clang_tidy: true,
93+
run_ctest: true
8994
}
9095
- {
9196
name: "Clang 19 ASan",
9297
label: "asan_clang19"
9398
# TODO: re-enable running MTR under this "Clang XX ASan"
9499
# run_mtr: true,
95-
# mtr_options: "--sanitize"
100+
# mtr_options: "--sanitize",
101+
# run_ctest: true
96102
# when "-stdlib=libc++ -fsanitize=address" alloc-dealloc-mismatch issue is fixed
97103
# (https://github.com/llvm/llvm-project/issues/59432)
98104
# or CI is upgraded to Clang 20
@@ -309,6 +315,7 @@ jobs:
309315
--suite=binlog_streaming ${{matrix.config.mtr_options}}
310316
311317
- name: CTest
318+
if: matrix.config.run_ctest
312319
working-directory: ${{github.workspace}}/src-build-${{matrix.config.label}}
313320
# Execute tests defined by the CMake configuration.
314321
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail

CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,16 @@ set(source_files
190190
src/binsrv/event/unknown_post_header.cpp
191191

192192
# gtid data structure files
193-
src/binsrv/gtid/common_types.hpp
193+
src/binsrv/gtids/common_types.hpp
194+
src/binsrv/gtids/gtid_fwd.hpp
195+
src/binsrv/gtids/gtid.hpp
196+
src/binsrv/gtids/gtid.cpp
197+
src/binsrv/gtids/gtid_set_fwd.hpp
198+
src/binsrv/gtids/gtid_set.hpp
199+
src/binsrv/gtids/gtid_set.cpp
200+
src/binsrv/gtids/tag_fwd.hpp
201+
src/binsrv/gtids/tag.hpp
202+
src/binsrv/gtids/tag.cpp
194203

195204
# binlog files
196205
src/binsrv/basic_logger_fwd.hpp
@@ -348,3 +357,8 @@ set_target_properties(binlog_server PROPERTIES
348357
CXX_STANDARD_REQUIRED YES
349358
CXX_EXTENSIONS NO
350359
)
360+
361+
include(CTest)
362+
if(BUILD_TESTING)
363+
add_subdirectory(tests)
364+
endif()

CMakePresets.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"cacheVariables": {
1616
"CMAKE_PREFIX_PATH": "${sourceParentDir}/aws-sdk-cpp-install-${presetName};${sourceParentDir}/boost-install-${presetName}",
1717
"CPP_STANDARD": "20",
18-
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
18+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
19+
"BUILD_TESTING": "ON"
1920
}
2021
},
2122

src/binsrv/event/gtid_log_post_header.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
#include "binsrv/event/gtid_log_flag_type.hpp"
3232

33-
#include "binsrv/gtid/common_types.hpp"
33+
#include "binsrv/gtids/common_types.hpp"
3434

3535
#include "util/byte_span.hpp"
3636
#include "util/byte_span_extractors.hpp"
@@ -127,8 +127,8 @@ gtid_log_post_header::get_flags() const noexcept {
127127
return to_string(get_flags());
128128
}
129129

130-
[[nodiscard]] gtid::uuid gtid_log_post_header::get_uuid() const noexcept {
131-
gtid::uuid result;
130+
[[nodiscard]] gtids::uuid gtid_log_post_header::get_uuid() const noexcept {
131+
gtids::uuid result;
132132
const auto &uuid_raw{get_uuid_raw()};
133133
static_assert(std::tuple_size_v<decltype(uuid_)> ==
134134
boost::uuids::uuid::static_size());

src/binsrv/event/gtid_log_post_header.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
#include "binsrv/event/gtid_log_flag_type_fwd.hpp"
2525

26-
#include "binsrv/gtid/common_types.hpp"
26+
#include "binsrv/gtids/common_types.hpp"
2727

2828
#include "util/byte_span_fwd.hpp"
2929

@@ -43,10 +43,10 @@ class [[nodiscard]] gtid_log_post_header {
4343
[[nodiscard]] gtid_log_flag_set get_flags() const noexcept;
4444
[[nodiscard]] std::string get_readable_flags() const;
4545

46-
[[nodiscard]] const gtid::uuid_storage &get_uuid_raw() const noexcept {
46+
[[nodiscard]] const gtids::uuid_storage &get_uuid_raw() const noexcept {
4747
return uuid_;
4848
}
49-
[[nodiscard]] gtid::uuid get_uuid() const noexcept;
49+
[[nodiscard]] gtids::uuid get_uuid() const noexcept;
5050
[[nodiscard]] std::string get_readable_uuid() const;
5151

5252
[[nodiscard]] std::int64_t get_gno_raw() const noexcept { return gno_; }
@@ -67,7 +67,7 @@ class [[nodiscard]] gtid_log_post_header {
6767
// the members are deliberately reordered for better packing
6868
std::uint8_t flags_{}; // 0
6969
std::uint8_t logical_ts_code_{}; // 3
70-
gtid::uuid_storage uuid_{}; // 1
70+
gtids::uuid_storage uuid_{}; // 1
7171
std::int64_t gno_{}; // 2
7272
std::int64_t last_committed_{}; // 4
7373
std::int64_t sequence_number_{}; // 5

src/binsrv/event/gtid_tagged_log_body_impl.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
#include "binsrv/event/code_type.hpp"
4141
#include "binsrv/event/gtid_log_flag_type.hpp"
4242

43-
#include "binsrv/gtid/common_types.hpp"
43+
#include "binsrv/gtids/common_types.hpp"
4444

4545
#include "util/bounded_string_storage.hpp"
4646
#include "util/byte_span.hpp"
@@ -152,9 +152,9 @@ generic_body_impl<code_type::gtid_tagged_log>::get_readable_flags() const {
152152
return to_string(get_flags());
153153
}
154154

155-
[[nodiscard]] gtid::uuid
155+
[[nodiscard]] gtids::uuid
156156
generic_body_impl<code_type::gtid_tagged_log>::get_uuid() const noexcept {
157-
gtid::uuid result;
157+
gtids::uuid result;
158158
const auto &uuid_raw{get_uuid_raw()};
159159
static_assert(std::tuple_size_v<decltype(uuid_)> ==
160160
boost::uuids::uuid::static_size());
@@ -298,8 +298,8 @@ void generic_body_impl<code_type::gtid_tagged_log>::process_field_data(
298298
std::size_t extracted_tag_length{};
299299
varlen_int_extractor(remainder, extracted_tag_length, "tag length");
300300
tag_.resize(extracted_tag_length);
301-
const std::span<tag_storage::value_type> tag_subrange{std::data(tag_),
302-
extracted_tag_length};
301+
const std::span<gtids::tag_storage::value_type> tag_subrange{
302+
std::data(tag_), extracted_tag_length};
303303
if (!util::extract_byte_span_from_byte_span_checked(remainder,
304304
tag_subrange)) {
305305
util::exception_location().raise<std::invalid_argument>(

src/binsrv/event/gtid_tagged_log_body_impl.hpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@
2323

2424
#include "binsrv/event/gtid_log_flag_type_fwd.hpp"
2525

26-
#include "binsrv/gtid/common_types.hpp"
26+
#include "binsrv/gtids/common_types.hpp"
2727

28-
#include "util/bounded_string_storage_fwd.hpp"
2928
#include "util/byte_span_fwd.hpp"
3029
#include "util/semantic_version_fwd.hpp"
3130

@@ -35,24 +34,23 @@ template <> class [[nodiscard]] generic_body_impl<code_type::gtid_tagged_log> {
3534
public:
3635
// https://github.com/mysql/mysql-server/blob/mysql-8.4.6/libs/mysql/binlog/event/control_events.h#L1111
3736

38-
static constexpr std::size_t tag_length{32U};
39-
using tag_storage = util::bounded_string_storage<tag_length>;
40-
4137
explicit generic_body_impl(util::const_byte_span portion);
4238

4339
[[nodiscard]] std::uint8_t get_flags_raw() const noexcept { return flags_; }
4440
[[nodiscard]] gtid_log_flag_set get_flags() const noexcept;
4541
[[nodiscard]] std::string get_readable_flags() const;
4642

47-
[[nodiscard]] const gtid::uuid_storage &get_uuid_raw() const noexcept {
43+
[[nodiscard]] const gtids::uuid_storage &get_uuid_raw() const noexcept {
4844
return uuid_;
4945
}
50-
[[nodiscard]] gtid::uuid get_uuid() const noexcept;
46+
[[nodiscard]] gtids::uuid get_uuid() const noexcept;
5147
[[nodiscard]] std::string get_readable_uuid() const;
5248

5349
[[nodiscard]] std::int64_t get_gno_raw() const noexcept { return gno_; }
5450

55-
[[nodiscard]] const tag_storage &get_tag_raw() const noexcept { return tag_; }
51+
[[nodiscard]] const gtids::tag_storage &get_tag_raw() const noexcept {
52+
return tag_;
53+
}
5654
[[nodiscard]] std::string_view get_tag() const noexcept;
5755

5856
[[nodiscard]] std::int64_t get_last_committed_raw() const noexcept {
@@ -132,9 +130,9 @@ template <> class [[nodiscard]] generic_body_impl<code_type::gtid_tagged_log> {
132130

133131
// the members are deliberately reordered for better packing
134132
std::uint8_t flags_{}; // 0
135-
gtid::uuid_storage uuid_{}; // 1
133+
gtids::uuid_storage uuid_{}; // 1
136134
std::int64_t gno_{}; // 2
137-
tag_storage tag_{}; // 3
135+
gtids::tag_storage tag_{}; // 3
138136
std::int64_t last_committed_{}; // 4
139137
std::int64_t sequence_number_{}; // 5
140138
std::uint64_t immediate_commit_timestamp_{unset_commit_timestamp}; // 6
Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,34 @@
1313
// along with this program; if not, write to the Free Software
1414
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1515

16-
#ifndef BINSRV_GTID_COMMON_TYPES_HPP
17-
#define BINSRV_GTID_COMMON_TYPES_HPP
16+
#ifndef BINSRV_GTIDS_COMMON_TYPES_HPP
17+
#define BINSRV_GTIDS_COMMON_TYPES_HPP
1818

1919
#include <array>
2020
#include <cstddef>
2121
#include <cstdint>
2222
#include <limits>
23+
#include <type_traits>
2324

2425
#include <boost/uuid/uuid.hpp>
2526

26-
namespace binsrv::gtid {
27+
#include "util/bounded_string_storage_fwd.hpp"
28+
29+
namespace binsrv::gtids {
2730

28-
using uuid = boost::uuids::uuid;
2931
inline constexpr std::size_t uuid_length{boost::uuids::uuid::static_size()};
3032
using uuid_storage = std::array<std::byte, uuid_length>;
3133

32-
} // namespace binsrv::gtid
34+
using uuid = boost::uuids::uuid;
35+
36+
inline constexpr std::size_t tag_max_length{32U};
37+
using tag_storage = util::bounded_string_storage<tag_max_length>;
38+
39+
using gno_t = std::uint64_t;
40+
inline constexpr gno_t min_gno{1ULL};
41+
inline constexpr gno_t max_gno{
42+
std::numeric_limits<std::make_signed_t<gno_t>>::max()};
43+
44+
} // namespace binsrv::gtids
3345

34-
#endif // BINSRV_GTID_COMMON_TYPES_HPP
46+
#endif // BINSRV_GTIDS_COMMON_TYPES_HPP

src/binsrv/gtids/gtid.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2023-2024 Percona and/or its affiliates.
2+
//
3+
// This program is free software; you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License, version 2.0,
5+
// as published by the Free Software Foundation.
6+
//
7+
// This program is distributed in the hope that it will be useful,
8+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
// GNU General Public License, version 2.0, for more details.
11+
//
12+
// You should have received a copy of the GNU General Public License
13+
// along with this program; if not, write to the Free Software
14+
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
15+
16+
#include "binsrv/gtids/gtid.hpp"
17+
18+
#include <ostream>
19+
#include <stdexcept>
20+
21+
#include <boost/uuid/uuid_io.hpp>
22+
23+
#include "binsrv/gtids/common_types.hpp"
24+
25+
#include "util/exception_location_helpers.hpp"
26+
27+
namespace binsrv::gtids {
28+
29+
void gtid::validate_components() {
30+
if (uuid_.is_nil()) {
31+
util::exception_location().raise<std::invalid_argument>(
32+
"uuid must not be nil");
33+
}
34+
if (gno_ < min_gno || gno_ > max_gno) {
35+
util::exception_location().raise<std::invalid_argument>(
36+
"gno is out of range");
37+
}
38+
}
39+
40+
std::ostream &operator<<(std::ostream &output, const gtid &obj) {
41+
output << obj.get_uuid();
42+
if (obj.has_tag()) {
43+
output << gtid::tag_separator << obj.get_tag();
44+
}
45+
output << gtid::gno_separator << obj.get_gno();
46+
return output;
47+
}
48+
49+
} // namespace binsrv::gtids

src/binsrv/gtids/gtid.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) 2023-2024 Percona and/or its affiliates.
2+
//
3+
// This program is free software; you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License, version 2.0,
5+
// as published by the Free Software Foundation.
6+
//
7+
// This program is distributed in the hope that it will be useful,
8+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
// GNU General Public License, version 2.0, for more details.
11+
//
12+
// You should have received a copy of the GNU General Public License
13+
// along with this program; if not, write to the Free Software
14+
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
15+
16+
#ifndef BINSRV_GTIDS_GTID_HPP
17+
#define BINSRV_GTIDS_GTID_HPP
18+
19+
#include "binsrv/gtids/gtid_fwd.hpp" // IWYU pragma: export
20+
21+
#include "binsrv/gtids/common_types.hpp"
22+
#include "binsrv/gtids/tag.hpp"
23+
24+
namespace binsrv::gtids {
25+
26+
class gtid {
27+
public:
28+
static constexpr char tag_separator{':'};
29+
static constexpr char gno_separator{':'};
30+
31+
gtid() = default;
32+
33+
// NOLINTNEXTLINE(modernize-pass-by-value)
34+
gtid(const uuid &uuid_component, const tag &tag_component,
35+
gno_t gno_component)
36+
: uuid_{uuid_component}, tag_{tag_component}, gno_{gno_component} {
37+
validate_components();
38+
}
39+
40+
gtid(const uuid &uuid_component, gno_t gno_component)
41+
: uuid_{uuid_component}, tag_{}, gno_{gno_component} {
42+
validate_components();
43+
}
44+
45+
[[nodiscard]] bool is_empty() const noexcept { return gno_ == 0ULL; }
46+
47+
[[nodiscard]] const uuid &get_uuid() const noexcept { return uuid_; }
48+
49+
[[nodiscard]] bool has_tag() const noexcept { return !tag_.is_empty(); }
50+
[[nodiscard]] const tag &get_tag() const noexcept { return tag_; }
51+
52+
[[nodiscard]] gno_t get_gno() const noexcept { return gno_; }
53+
54+
[[nodiscard]] friend bool operator==(const gtid &first,
55+
const gtid &second) noexcept = default;
56+
57+
private:
58+
uuid uuid_{};
59+
tag tag_{};
60+
gno_t gno_{};
61+
62+
void validate_components();
63+
};
64+
65+
} // namespace binsrv::gtids
66+
67+
#endif // BINSRV_GTIDS_GTID_HPP

0 commit comments

Comments
 (0)