diff --git a/include/wsrep/client_state.hpp b/include/wsrep/client_state.hpp index 7b0409a9..c293a77f 100644 --- a/include/wsrep/client_state.hpp +++ b/include/wsrep/client_state.hpp @@ -307,6 +307,10 @@ namespace wsrep /** * Append a key into transaction write set. + * + * @param key Key to be appended + * + * @return Zero on success, non-zero on failure. */ int append_key(const wsrep::key& key) { @@ -315,6 +319,27 @@ namespace wsrep return transaction_.append_key(key); } + /** + * Append keys in key_array into transaction write set. + * + * @param keys Array of keys to be appended + * + * @return Zero in case of success, non-zero on failure. + */ + int append_keys(const wsrep::key_array& keys) + { + assert(mode_ == m_local || mode_ == m_toi); + assert(state_ == s_exec); + for (auto i(keys.begin()); i != keys.end(); ++i) + { + if (transaction_.append_key(*i)) + { + return 1; + } + } + return 0; + } + /** * Append data into transaction write set. */ diff --git a/include/wsrep/compiler.hpp b/include/wsrep/compiler.hpp index 21f74570..e1241cb8 100644 --- a/include/wsrep/compiler.hpp +++ b/include/wsrep/compiler.hpp @@ -20,7 +20,12 @@ /** @file compiler.hpp * - * Compiler specific options. + * Compiler specific macro definitions. + * + * WSREP_OVERRIDE - Set to "override" if the compiler supports it, otherwise + * left empty. + * WSREP_CONSTEXPR_OR_INLINE - Set to "constexpr" if the compiler supports it, + * otherwise "inline". */ #define WSREP_UNUSED __attribute__((unused)) diff --git a/include/wsrep/gtid.hpp b/include/wsrep/gtid.hpp index 043dc483..9d5c2951 100644 --- a/include/wsrep/gtid.hpp +++ b/include/wsrep/gtid.hpp @@ -22,9 +22,16 @@ #include "id.hpp" #include "seqno.hpp" +#include "compiler.hpp" #include +/** + * Minimum number of bytes guaratneed to store GTID string representation, + * terminating '\0' not included (36 + 1 + 20). + */ +#define WSREP_LIB_GTID_C_STR_LEN 57 + namespace wsrep { class gtid @@ -61,6 +68,18 @@ namespace wsrep wsrep::seqno seqno_; }; + /** + * Scan a GTID from C string. + * + * @param buf Buffer containing the string + * @param len Length of buffer + * @param[out] gtid Gtid to be printed to + * + * @return Number of bytes scanned, negative value on error. + */ + ssize_t scan_from_c_str(const char* buf, size_t buf_len, + wsrep::gtid& gtid); + /** * Print a GTID into character buffer. * @param buf Pointer to the beginning of the buffer @@ -68,16 +87,16 @@ namespace wsrep * * @return Number of characters printed or negative value for error */ - ssize_t gtid_print_to_c_str(const wsrep::gtid&, char* buf, size_t buf_len); + ssize_t print_to_c_str(const wsrep::gtid&, char* buf, size_t buf_len); + /** - * Return minimum number of bytes guaranteed to store GTID string - * representation, terminating '\0' not included (36 + 1 + 20) + * Overload for ostream operator<<. */ - static inline size_t gtid_c_str_len() - { - return 57; - } std::ostream& operator<<(std::ostream&, const wsrep::gtid&); + + /** + * Overload for istream operator>>. + */ std::istream& operator>>(std::istream&, wsrep::gtid&); } diff --git a/include/wsrep/id.hpp b/include/wsrep/id.hpp index be8c9292..fc1e82b2 100644 --- a/include/wsrep/id.hpp +++ b/include/wsrep/id.hpp @@ -42,10 +42,11 @@ namespace wsrep class id { public: + typedef struct native_type { unsigned char buf[16]; } native_type; /** * Default constructor. Constructs an empty identifier. */ - id() : data_() { std::memset(data_, 0, sizeof(data_)); } + id() : data_() { std::memset(data_.buf, 0, sizeof(data_.buf)); } /** * Construct from string. The input string may contain either @@ -62,24 +63,24 @@ namespace wsrep { throw wsrep::runtime_error("Too long identifier"); } - std::memset(data_, 0, sizeof(data_)); - std::memcpy(data_, data, size); + std::memset(data_.buf, 0, sizeof(data_.buf)); + std::memcpy(data_.buf, data, size); } bool operator<(const id& other) const { - return (std::memcmp(data_, other.data_, sizeof(data_)) < 0); + return (std::memcmp(data_.buf, other.data_.buf, sizeof(data_.buf)) < 0); } bool operator==(const id& other) const { - return (std::memcmp(data_, other.data_, sizeof(data_)) == 0); + return (std::memcmp(data_.buf, other.data_.buf, sizeof(data_.buf)) == 0); } bool operator!=(const id& other) const { return !(*this == other); } - const void* data() const { return data_; } + const void* data() const { return data_.buf; } size_t size() const { return sizeof(data_); } @@ -94,7 +95,7 @@ namespace wsrep } private: static const wsrep::id undefined_; - unsigned char data_[16]; + native_type data_; }; std::ostream& operator<<(std::ostream&, const wsrep::id& id); diff --git a/include/wsrep/provider.hpp b/include/wsrep/provider.hpp index 8d57283e..24572f95 100644 --- a/include/wsrep/provider.hpp +++ b/include/wsrep/provider.hpp @@ -25,6 +25,7 @@ #include "buffer.hpp" #include "client_id.hpp" #include "transaction_id.hpp" +#include "compiler.hpp" #include #include @@ -33,6 +34,12 @@ #include #include +/** + * Empty provider magic. If none provider is passed to make_provider(), + * a dummy provider is loaded. + */ +#define WSREP_LIB_PROVIDER_NONE "none" + namespace wsrep { class server_state; diff --git a/include/wsrep/seqno.hpp b/include/wsrep/seqno.hpp index 458bad68..27563375 100644 --- a/include/wsrep/seqno.hpp +++ b/include/wsrep/seqno.hpp @@ -20,8 +20,6 @@ #ifndef WSREP_SEQNO_HPP #define WSREP_SEQNO_HPP -#include "exception.hpp" - #include namespace wsrep @@ -33,6 +31,8 @@ namespace wsrep class seqno { public: + typedef long long native_type; + seqno() : seqno_(-1) { } @@ -77,8 +77,9 @@ namespace wsrep return (*this + seqno(other)); } static seqno undefined() { return seqno(-1); } + private: - long long seqno_; + native_type seqno_; }; std::ostream& operator<<(std::ostream& os, wsrep::seqno seqno); diff --git a/include/wsrep/server_state.hpp b/include/wsrep/server_state.hpp index fd204fbd..110f13ae 100644 --- a/include/wsrep/server_state.hpp +++ b/include/wsrep/server_state.hpp @@ -95,6 +95,13 @@ #include #include +/** + * Magic string to tell provider to engage into trivial (empty) + * state transfer. No data will be passed, but the node shall be + * considered joined. + */ +#define WSREP_LIB_SST_TRIVIAL "trivial" + namespace wsrep { // Forward declarations @@ -179,7 +186,6 @@ namespace wsrep rm_sync }; - virtual ~server_state(); wsrep::encryption_service* encryption_service() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16fe5bf7..38939abd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,5 +15,6 @@ add_library(wsrep-lib server_state.cpp thread.cpp transaction.cpp + uuid.cpp wsrep_provider_v26.cpp) target_link_libraries(wsrep-lib wsrep_api_v26 pthread dl) diff --git a/src/gtid.cpp b/src/gtid.cpp index 5fa90f6b..3a655249 100644 --- a/src/gtid.cpp +++ b/src/gtid.cpp @@ -36,11 +36,47 @@ std::istream& wsrep::operator>>(std::istream& is, wsrep::gtid& gtid) std::getline(is, id_str, ':'); long long seq; is >> seq; - gtid = wsrep::gtid(wsrep::id(id_str), wsrep::seqno(seq)); + if (!is) + { + is.clear(std::ios_base::failbit); + return is; + } + try + { + // wsrep::id constructor will throw if it cannot parse the + // id_str. + gtid = wsrep::gtid(wsrep::id(id_str), wsrep::seqno(seq)); + } + catch (const wsrep::runtime_error& e) + { + // Formatting or extraction error. Clear the istream state and + // set failibit. + is.clear(std::ios_base::failbit); + } return is; } -ssize_t wsrep::gtid_print_to_c_str( +ssize_t wsrep::scan_from_c_str( + const char* buf, size_t buf_len, wsrep::gtid& gtid) +{ + std::istringstream is(std::string(buf, buf_len)); + is >> gtid; + // Whole string was consumed without failures + if (is && is.eof()) + { + return buf_len; + } + // Some failure occurred + if (!is) + { + return -EINVAL; + } + // The string was not consumed completely, return current position + // of the istream. + return is.tellg(); +} + +ssize_t wsrep::print_to_c_str( const wsrep::gtid& gtid, char* buf, size_t buf_len) { std::ostringstream os; diff --git a/src/id.cpp b/src/id.cpp index 6c355e7f..e08dca0a 100644 --- a/src/id.cpp +++ b/src/id.cpp @@ -18,7 +18,7 @@ */ #include "wsrep/id.hpp" -#include +#include "uuid.hpp" #include #include @@ -29,15 +29,17 @@ const wsrep::id wsrep::id::undefined_ = wsrep::id(); wsrep::id::id(const std::string& str) : data_() { - wsrep_uuid_t wsrep_uuid; - if (wsrep_uuid_scan(str.c_str(), str.size(), &wsrep_uuid) == - WSREP_UUID_STR_LEN) + wsrep::uuid_t wsrep_uuid; + + if (str.size() == WSREP_LIB_UUID_STR_LEN && + wsrep::uuid_scan(str.c_str(), str.size(), &wsrep_uuid) == + WSREP_LIB_UUID_STR_LEN) { - std::memcpy(data_, wsrep_uuid.data, sizeof(data_)); + std::memcpy(data_.buf, wsrep_uuid.data, sizeof(data_.buf)); } else if (str.size() <= 16) { - std::memcpy(data_, str.c_str(), str.size()); + std::memcpy(data_.buf, str.c_str(), str.size()); } else { @@ -58,14 +60,14 @@ std::ostream& wsrep::operator<<(std::ostream& os, const wsrep::id& id) } else { - char uuid_str[WSREP_UUID_STR_LEN + 1]; - wsrep_uuid_t uuid; + char uuid_str[WSREP_LIB_UUID_STR_LEN + 1]; + wsrep::uuid_t uuid; std::memcpy(uuid.data, ptr, sizeof(uuid.data)); - if (wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str)) < 0) + if (wsrep::uuid_print(&uuid, uuid_str, sizeof(uuid_str)) < 0) { throw wsrep::runtime_error("Could not print uuid"); } - uuid_str[WSREP_UUID_STR_LEN] = '\0'; + uuid_str[WSREP_LIB_UUID_STR_LEN] = '\0'; return (os << uuid_str); } } diff --git a/src/uuid.cpp b/src/uuid.cpp new file mode 100644 index 00000000..e7d5fb29 --- /dev/null +++ b/src/uuid.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +#include "uuid.hpp" + +#include +#include +#include +#include + +int wsrep::uuid_scan (const char* str, size_t str_len, wsrep::uuid_t* uuid) +{ + unsigned int uuid_len = 0; + unsigned int uuid_offt = 0; + + while (uuid_len + 1 < str_len) { + /* We are skipping potential '-' after uuid_offt == 4, 6, 8, 10 + * which means + * (uuid_offt >> 1) == 2, 3, 4, 5, + * which in turn means + * (uuid_offt >> 1) - 2 <= 3 + * since it is always >= 0, because uuid_offt is unsigned */ + if (((uuid_offt >> 1) - 2) <= 3 && str[uuid_len] == '-') { + // skip dashes after 4th, 6th, 8th and 10th positions + uuid_len += 1; + continue; + } + + if (isxdigit(str[uuid_len]) && isxdigit(str[uuid_len + 1])) { + // got hex digit, scan another byte to uuid, increment uuid_offt + sscanf (str + uuid_len, "%2hhx", uuid->data + uuid_offt); + uuid_len += 2; + uuid_offt += 1; + if (sizeof (uuid->data) == uuid_offt) + return uuid_len; + } + else { + break; + } + } + + *uuid = wsrep::uuid_initializer; + return -EINVAL; +} + +int wsrep::uuid_print (const wsrep::uuid_t* uuid, char* str, size_t str_len) +{ + if (str_len > WSREP_LIB_UUID_STR_LEN) { + const unsigned char* u = uuid->data; + return snprintf(str, str_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + u[ 0], u[ 1], u[ 2], u[ 3], u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], u[12], u[13], u[14], u[15]); + } + else { + return -EMSGSIZE; + } +} diff --git a/src/uuid.hpp b/src/uuid.hpp new file mode 100644 index 00000000..72812a76 --- /dev/null +++ b/src/uuid.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +/** @file uuid.hpp + * + * Helper methods to parse and print UUIDs, intended to use + * internally in wsrep-lib. + * + * The implementation is copied from wsrep-API v26. + */ + +#ifndef WSREP_UUID_HPP +#define WSREP_UUID_HPP + +#include "wsrep/compiler.hpp" + +#include + +/** + * Length of UUID string representation, not including terminating '\0'. + */ +#define WSREP_LIB_UUID_STR_LEN 36 + +namespace wsrep +{ + /** + * UUID type. + */ + typedef union uuid_ + { + unsigned char data[16]; + size_t alignment; + } uuid_t; + + static const wsrep::uuid_t uuid_initializer = {{0, }}; + + /** + * Read UUID from string. + * + * @param str String to read from + * @param str_len Length of string + * @param[out] UUID to read to + * + * @return Number of bytes read or negative error code in case + * of error. + */ + int uuid_scan(const char* str, size_t str_len, wsrep::uuid_t* uuid); + + /** + * Write UUID to string. The caller must allocate at least + * WSREP_LIB_UUID_STR_LEN + 1 space for the output str parameter. + * + * @param uuid UUID to print + * @param str[out] Output buffer + * @param str_len Size of output buffer + * + * @return Number of chars printerd, negative error code in case of + * error. + */ + int uuid_print(const wsrep::uuid_t* uuid, char* str, size_t str_len); +} + +#endif // WSREP_UUID_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc715a4e..1385c1fa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(wsrep-lib_test mock_high_priority_service.cpp mock_storage_service.cpp test_utils.cpp + gtid_test.cpp id_test.cpp server_context_test.cpp transaction_test.cpp diff --git a/test/gtid_test.cpp b/test/gtid_test.cpp new file mode 100644 index 00000000..4f4d644b --- /dev/null +++ b/test/gtid_test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +#include "wsrep/gtid.hpp" +#include + +BOOST_AUTO_TEST_CASE(gtid_test_scan_from_string_uuid) +{ + std::string gtid_str("6a20d44a-6e17-11e8-b1e2-9061aec0cdad:123456"); + wsrep::gtid gtid; + ssize_t ret(wsrep::scan_from_c_str( + gtid_str.c_str(), + gtid_str.size(), gtid)); + BOOST_REQUIRE_MESSAGE(ret == ssize_t(gtid_str.size()), + "Expected " << gtid_str.size() << " got " << ret); + BOOST_REQUIRE(gtid.seqno().get() == 123456); +} + +BOOST_AUTO_TEST_CASE(gtid_test_scan_from_string_uuid_too_long) +{ + std::string gtid_str("6a20d44a-6e17-11e8-b1e2-9061aec0cdadx:123456"); + wsrep::gtid gtid; + ssize_t ret(wsrep::scan_from_c_str( + gtid_str.c_str(), + gtid_str.size(), gtid)); + BOOST_REQUIRE_MESSAGE(ret == -EINVAL, + "Expected " << -EINVAL << " got " << ret); +} + +BOOST_AUTO_TEST_CASE(gtid_test_scan_from_string_seqno_out_of_range) +{ + std::string gtid_str("6a20d44a-6e17-11e8-b1e2-9061aec0cdad:9223372036854775808"); + wsrep::gtid gtid; + ssize_t ret(wsrep::scan_from_c_str( + gtid_str.c_str(), + gtid_str.size(), gtid)); + BOOST_REQUIRE_MESSAGE(ret == -EINVAL, + "Expected " << -EINVAL << " got " << ret); + + gtid_str = "6a20d44a-6e17-11e8-b1e2-9061aec0cdad:-9223372036854775809"; + ret = wsrep::scan_from_c_str( + gtid_str.c_str(), + gtid_str.size(), gtid); + BOOST_REQUIRE_MESSAGE(ret == -EINVAL, + "Expected " << -EINVAL << " got " << ret); +}