/* 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, 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, 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 "mysql/gtid/gtidset.h" #include #include namespace mysql::gtid { std::size_t Gno_interval::count() const { return m_next_gno_after_end - m_start; } Gno_interval::Gno_interval(gno_t start, gno_t end) : m_start(start), m_next_gno_after_end(end + 1) {} Gno_interval::Gno_interval(const Gno_interval &other) : m_start(other.get_start()), m_next_gno_after_end(other.get_end() + 1) {} Gno_interval &Gno_interval::operator=(const Gno_interval &other) { m_start = other.get_start(); m_next_gno_after_end = other.get_end() + 1; return *this; } bool Gno_interval::operator==(const Gno_interval &other) const { return ((other.get_end() + 1) == m_next_gno_after_end) && other.get_start() == m_start; } bool Gno_interval::operator<(const Gno_interval &other) const { if (m_start < other.get_start()) return true; if (m_start > other.get_start()) return false; if ((m_next_gno_after_end - 1) < other.get_end()) return true; return false; } bool Gno_interval::intersects(const Gno_interval &other) const { bool other_starts_in_this_interval = (other.get_start() >= m_start && other.get_start() < m_next_gno_after_end); bool this_starts_in_other_interval = (m_start >= other.get_start() && m_start <= other.get_end()); return other_starts_in_this_interval || this_starts_in_other_interval; } bool Gno_interval::contiguous(const Gno_interval &other) const { if (other.get_start() == m_next_gno_after_end) return true; if (other.get_end() + 1 == m_start) return true; return false; } bool Gno_interval::intersects_or_contiguous(const Gno_interval &other) const { return intersects(other) || contiguous(other); } gno_t Gno_interval::get_start() const { return m_start; } gno_t Gno_interval::get_end() const { return m_next_gno_after_end - 1; } bool Gno_interval::add(const Gno_interval &other) { if (intersects_or_contiguous(other)) { m_next_gno_after_end = std::max(other.get_end() + 1, m_next_gno_after_end); m_start = std::min(other.get_start(), m_start); return false; } return true; } std::string Gno_interval::to_string() const { std::stringstream ss; if (m_start == get_end()) ss << m_start; else ss << m_start << SEPARATOR_GNO_START_END << get_end(); return ss.str(); } bool Gno_interval::is_valid() const { return m_start < m_next_gno_after_end && m_start > 0; } Gtid_set::~Gtid_set() = default; Gtid_set &Gtid_set::operator=(const Gtid_set &other) { reset(); add(other); return *this; } bool Gtid_set::operator==(const Gtid_set &other) const { const auto &other_set = other.get_gtid_set(); const auto &this_set = m_gtid_set; if (other_set.size() != this_set.size()) return false; for (auto const &[other_uuid, other_tag_map] : other_set) { auto tag_map_it = this_set.find(other_uuid); // uuid does not exist in this set if (tag_map_it == this_set.end()) { return false; } const auto &this_tag_map = tag_map_it->second; // the number of tags if (this_tag_map.size() != other_tag_map.size()) { return false; } for (auto const &[other_tag, other_intervals] : other_tag_map) { auto it = this_tag_map.find(other_tag); // tsid does not exist in this set if (it == this_tag_map.end()) { return false; } // tsid exists, lets check if the set of intervals match const auto &this_intervals = it->second; if (this_intervals.size() != other_intervals.size()) return false; // check each interval for this tsid for (const auto &interval : this_intervals) { if (other_intervals.find(interval) == other_intervals.end()) return false; } } } return true; } const Gtid_set::Tsid_interval_map &Gtid_set::get_gtid_set() const { return m_gtid_set; } bool Gtid_set::do_add(const Tsid &tsid, const Gno_interval &interval) { return this->do_add(tsid.get_uuid(), tsid.get_tag(), interval); } bool Gtid_set::do_add(const Uuid &uuid, const Tag &tag, const Gno_interval &interval) { const auto &sid = uuid; auto gtid_set_it = m_gtid_set.find(sid); if (gtid_set_it == m_gtid_set.end()) { auto ret = m_gtid_set.insert(std::make_pair(sid, Tag_interval_map())); gtid_set_it = ret.first; } auto it = gtid_set_it->second.find(tag); if (it == gtid_set_it->second.end()) { std::set intervals; intervals.insert(interval); gtid_set_it->second.insert(std::make_pair(tag, intervals)); return false; } auto &intervals = it->second; /* * Iterate over the list of intervals and whenever we find a contiguous * interval remove it, merge the intervals and move on to the next interval. * * Eventually at the end, we will have the interval adjusted to merge. */ auto current = interval; for (auto iterator = intervals.begin(); iterator != intervals.end();) { auto &next = *iterator; if (next.intersects_or_contiguous(current)) { current.add(next); /* returns the next element after the one erased */ iterator = intervals.erase(iterator); } else { ++iterator; } } intervals.insert(current); return false; } bool Gtid_set::add(const Tsid &tsid, const Gno_interval &interval) { return do_add(tsid, interval); } bool Gtid_set::add(const Gtid_set &other) { for (auto const &[uuid, tag_map] : other.m_gtid_set) { for (auto const &[tag, intervals] : tag_map) { for (auto &interval : intervals) { if (do_add(uuid, tag, interval)) return true; } } } return false; } bool Gtid_set::add(const Gtid >id) { return do_add(gtid.get_tsid(), Gno_interval{gtid.get_gno(), gtid.get_gno()}); } std::string Gtid_set::to_string() const { if (m_gtid_set.empty()) { return empty_gtid_set_str; } std::stringstream ss; for (auto const &[uuid, tag_map] : m_gtid_set) { ss << uuid.to_string(); ss << Gtid::separator_gtid; assert(!tag_map.empty()); for (auto const &[tag, intervals] : tag_map) { if (tag.is_empty() == false) { ss << tag.to_string() << Gtid::separator_gtid; } assert(!intervals.empty()); for (auto &interval : intervals) { ss << interval.to_string() << separator_interval; } ss.seekp(-1, std::ios_base::end); ss << Gtid::separator_gtid; } ss.seekp(-1, std::ios_base::end); ss << separator_uuid_set; } return std::string(ss.str(), 0, ss.str().size() - 1); } bool Gtid_set::contains(const Gtid >id) const { auto tag_map_it = m_gtid_set.find(gtid.get_uuid()); if (tag_map_it == m_gtid_set.end()) return false; auto it = tag_map_it->second.find(gtid.get_tag()); if (it == tag_map_it->second.end()) return false; for (const auto &interval : it->second) { if (gtid.get_gno() >= interval.get_start() && gtid.get_gno() <= interval.get_end()) return true; } return false; } std::size_t Gtid_set::get_num_tsids() const { std::size_t tsid_num = 0; for (auto const &[uuid, tag_map] : m_gtid_set) { tsid_num += tag_map.size(); } return tsid_num; } bool Gtid_set::is_empty() const { return m_gtid_set.empty(); } std::size_t Gtid_set::count() const { std::size_t count{0}; for (auto const &[uuid, tag_map] : m_gtid_set) { for (auto const &[tag, intervals] : tag_map) { for (auto &interval : intervals) { count += interval.count(); } } } return count; } void Gtid_set::reset() { m_gtid_set.clear(); } Gtid_format Gtid_set::get_gtid_set_format() const { for (auto const &[uuid, tag_map] : m_gtid_set) { for (auto const &[tag, intervals] : tag_map) { if (tag.is_defined()) { return Gtid_format::tagged; } } } return Gtid_format::untagged; } } // namespace mysql::gtid