#include #include #include #include #include "proxysql_gtid.h" // Initializes a trxid interval from a range. TrxId_Interval::TrxId_Interval(const trxid_t _start, const trxid_t _end) { start = _start; end = _end; if (start > end) { std::swap(start, end); } } TrxId_Interval::TrxId_Interval(const trxid_t trxid) : TrxId_Interval(trxid, trxid) { } // Initializes a trxid interval from a C string buffer, in [trxid]{-[trxid]} format. TrxId_Interval::TrxId_Interval(const char *s) { start = 0; end = 0; if (s == nullptr) { return; } trxid_t _start = 0, _end = 0; if (sscanf(s, "%ld-%ld", &_start, &_end) == 2) { start = _start; end = _end; } else if (sscanf(s, "%ld", &_start) == 1) { start = _start; end = _start; } if (start > end) { std::swap(start, end); } } // Initializes a trxid interval from a string, in [trxid]{-[trxid]} format. TrxId_Interval::TrxId_Interval(const std::string& s) : TrxId_Interval(s.c_str()) { } // Checks if another trxid interval is contained in this one, const bool TrxId_Interval::contains(const TrxId_Interval& other) { return (other.start >= start && other.end <= end); } // Checks if a given trxid is contained in this interval. const bool TrxId_Interval::contains(trxid_t trxid) { return (trxid >= start && trxid <= end); } // Yields a string representation for a trxid interval. const std::string TrxId_Interval::to_string(void) { if (start == end) { return std::to_string(start); } return std::to_string(start) + "-" + std::to_string(end); } // Attempts to append a new interval to this interval's end. Returns true if the append succeded, false otherwise. const bool TrxId_Interval::append(const TrxId_Interval& other) { if (other.start >= start && other.end >= end && other.start <= (end+1)) { // other overlaps interval at end end = other.end; return true; } return false; } // Attempts to merge two trxid intervals. Returns true if the intervals were merged (and potentially modified), false otherwise. const bool TrxId_Interval::merge(const TrxId_Interval& other) { if (other.start >= start && other.end <= end) { // other is contained by interval return true; } if (other.start <= start && other.end >= end) { // other contains whole of existing interval start = other.start; end = other.end; return true; } if (other.start <= start && other.end >= (start-1)) { // other overlaps interval at start start = other.start; return true; } if (other.end >= end && other.start <= (end+1)) { // other overlaps interval at end end = other.end; return true; } return false; } // Compares two trxid intervals, by strict weak ordering. const int TrxId_Interval::cmp(const TrxId_Interval& other) { if (start < other.start) { return -1; } if (start > other.start) { return 1; } if (end < other.end) { return -1; } if (end > other.end) { return 1; } return 0; } const bool TrxId_Interval::operator<(const TrxId_Interval& other) { return cmp(other) == -1; } const bool TrxId_Interval::operator==(const TrxId_Interval& other) { return cmp(other) == 0; } const bool TrxId_Interval::operator!=(const TrxId_Interval& other) { return cmp(other) != 0; } // Initializes a GTID set. GTID_Set::GTID_Set() {} // Creates a copy of this GTID set. GTID_Set GTID_Set::copy() { GTID_Set cp; cp.map = map; return cp; } // Clears all GTID set entries. void GTID_Set::clear() { map.clear(); } // Adds a new trxid interval for a given UUID. Returns true if the set was modified, false otherwise. bool GTID_Set::add(const std::string& uuid, const TrxId_Interval& iv) { auto it = map.find(uuid); if (it == map.end()) { // new UUID entry map[uuid].emplace_back(iv); return true; } if (!it->second.empty()) { auto& last = it->second.back(); if (last.contains(iv)) { return false; } if (last.append(iv)) { return true; } } // insert/merge trxid interval... auto pos = it->second.begin(); for (; pos != it->second.end(); ++pos) { if (pos->contains(iv)) { // trxid interval is already present, nothing to do return false; } if (pos->merge(iv)) break; } if (pos == it->second.end()) { it->second.emplace_back(iv); } // ...and merge overlapping trxid ranges, if any it->second.sort(); auto a = it->second.begin(); while (a != it->second.end()) { auto b = std::next(a); if (b == it->second.end()) { break; } if (a->merge(*b)) { it->second.erase(b); continue; } a++; } return true; } // Adds a single trxid for a given UUID. Returns true if the set was modified, false otherwise. bool GTID_Set::add(const std::string& uuid, const trxid_t& trxid) { return add(uuid, TrxId_Interval(trxid)); } // Adds a new trxid range for a given UUID. Returns true if the set was modified, false otherwise. bool GTID_Set::add(const std::string& uuid, const trxid_t& start, const trxid_t& end) { return add(uuid, TrxId_Interval(start, end)); } // Adds a new trxid range for a given UUID, as a C string buffer. Returns true if the set was modified, false otherwise. bool GTID_Set::add(const std::string& uuid, const char *s) { return add(uuid, TrxId_Interval(s)); } // Adds a new trxid range for a given UUID, as a string. Returns true if the set was modified, false otherwise. bool GTID_Set::add(const std::string& uuid, const std::string& s) { return add(uuid, TrxId_Interval(s)); } // Evaluates whether a trxid is present in any of the intervals for a given UUID. const bool GTID_Set::has_gtid(const std::string& uuid, const trxid_t trxid) { auto it = map.find(uuid); if (it == map.end()) { return false; } for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { if (itr->contains(trxid)) { return true; } } return false; } // Yields a string representation for a GTID set. const std::string GTID_Set::to_string(void) { std::stringstream out; bool first_uuid = true; for (auto it=map.begin(); it!=map.end(); ++it) { if (!first_uuid) { out << ","; } std::string uuid = it->first; uuid.insert(8,"-"); uuid.insert(13,"-"); uuid.insert(18,"-"); uuid.insert(23,"-"); out << uuid; for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { out << ":" << itr->to_string(); } first_uuid = false; } return out.str(); }