diff options
Diffstat (limited to 'storage/rocksdb/rdb_utils.cc')
-rw-r--r-- | storage/rocksdb/rdb_utils.cc | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/storage/rocksdb/rdb_utils.cc b/storage/rocksdb/rdb_utils.cc new file mode 100644 index 00000000..ad6fdf58 --- /dev/null +++ b/storage/rocksdb/rdb_utils.cc @@ -0,0 +1,369 @@ +/* + Copyright (c) 2016, Facebook, Inc. + + This program 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; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include <my_global.h> + +/* This C++ file's header */ +#include "./rdb_utils.h" + +/* C++ standard header files */ +#include <array> +#include <sstream> +#include <string> +#include <vector> + +/* C standard header files */ +#include <ctype.h> + +/* MyRocks header files */ +#include "./ha_rocksdb.h" + +/* + Both innobase/include/ut0counter.h and rocksdb/port/port_posix.h define + CACHE_LINE_SIZE. +*/ +#ifdef CACHE_LINE_SIZE +# undef CACHE_LINE_SIZE +#endif + +/* RocksDB header files */ +#include "util/compression.h" + +namespace myrocks { + +/* + Skip past any spaces in the input +*/ +const char *rdb_skip_spaces(const struct charset_info_st *const cs, + const char *str) { + while (my_isspace(cs, *str)) { + str++; + } + + return str; +} + +/* + Compare (ignoring case) to see if str2 is the next data in str1. + Note that str1 can be longer but we only compare up to the number + of characters in str2. +*/ +bool rdb_compare_strings_ic(const char *const str1, const char *const str2) { + // Scan through the strings + size_t ii; + for (ii = 0; str2[ii]; ii++) { + if (toupper(static_cast<int>(str1[ii])) != + toupper(static_cast<int>(str2[ii]))) { + return false; + } + } + + return true; +} + +/* + Scan through an input string looking for pattern, ignoring case + and skipping all data enclosed in quotes. +*/ +const char *rdb_find_in_string(const char *str, const char *pattern, + bool *const succeeded) { + char quote = '\0'; + bool escape = false; + + *succeeded = false; + + for (; *str; str++) { + /* If we found a our starting quote character */ + if (*str == quote) { + /* If it was escaped ignore it */ + if (escape) { + escape = false; + } + /* Otherwise we are now outside of the quoted string */ + else { + quote = '\0'; + } + } + /* Else if we are currently inside a quoted string? */ + else if (quote != '\0') { + /* If so, check for the escape character */ + escape = !escape && *str == '\\'; + } + /* Else if we found a quote we are starting a quoted string */ + else if (*str == '"' || *str == '\'' || *str == '`') { + quote = *str; + } + /* Else we are outside of a quoted string - look for our pattern */ + else { + if (rdb_compare_strings_ic(str, pattern)) { + *succeeded = true; + return str; + } + } + } + + // Return the character after the found pattern or the null terminateor + // if the pattern wasn't found. + return str; +} + +/* + See if the next valid token matches the specified string +*/ +const char *rdb_check_next_token(const struct charset_info_st *const cs, + const char *str, const char *const pattern, + bool *const succeeded) { + // Move past any spaces + str = rdb_skip_spaces(cs, str); + + // See if the next characters match the pattern + if (rdb_compare_strings_ic(str, pattern)) { + *succeeded = true; + return str + strlen(pattern); + } + + *succeeded = false; + return str; +} + +/* + Parse id +*/ +const char *rdb_parse_id(const struct charset_info_st *const cs, + const char *str, std::string *const id) { + // Move past any spaces + str = rdb_skip_spaces(cs, str); + + if (*str == '\0') { + return str; + } + + char quote = '\0'; + if (*str == '`' || *str == '"') { + quote = *str++; + } + + size_t len = 0; + const char *start = str; + + if (quote != '\0') { + for (;;) { + if (*str == '\0') { + return str; + } + + if (*str == quote) { + str++; + if (*str != quote) { + break; + } + } + + str++; + len++; + } + } else { + while (!my_isspace(cs, *str) && *str != '(' && *str != ')' && *str != '.' && + *str != ',' && *str != '\0') { + str++; + len++; + } + } + + // If the user requested the id create it and return it + if (id != nullptr) { + *id = std::string(""); + id->reserve(len); + while (len--) { + *id += *start; + if (*start++ == quote) { + start++; + } + } + } + + return str; +} + +/* + Skip id +*/ +const char *rdb_skip_id(const struct charset_info_st *const cs, + const char *str) { + return rdb_parse_id(cs, str, nullptr); +} + +/* + Parses a given string into tokens (if any) separated by a specific delimiter. +*/ +const std::vector<std::string> parse_into_tokens(const std::string &s, + const char delim) { + std::vector<std::string> tokens; + std::string t; + std::stringstream ss(s); + + while (getline(ss, t, delim)) { + tokens.push_back(t); + } + + return tokens; +} + +static const std::size_t rdb_hex_bytes_per_char = 2; +static const std::array<char, 16> rdb_hexdigit = {{'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f'}}; + +/* + Convert data into a hex string with optional maximum length. + If the data is larger than the maximum length trancate it and append "..". +*/ +std::string rdb_hexdump(const char *data, const std::size_t data_len, + const std::size_t maxsize) { + // Count the elements in the string + std::size_t elems = data_len; + // Calculate the amount of output needed + std::size_t len = elems * rdb_hex_bytes_per_char; + std::string str; + + if (maxsize != 0 && len > maxsize) { + // If the amount of output is too large adjust the settings + // and leave room for the ".." at the end + elems = (maxsize - 2) / rdb_hex_bytes_per_char; + len = elems * rdb_hex_bytes_per_char + 2; + } + + // Reserve sufficient space to avoid reallocations + str.reserve(len); + + // Loop through the input data and build the output string + for (std::size_t ii = 0; ii < elems; ii++, data++) { + uint8_t ch = (uint8_t)*data; + str += rdb_hexdigit[ch >> 4]; + str += rdb_hexdigit[ch & 0x0F]; + } + + // If we can't fit it all add the ".." + if (elems != data_len) { + str += ".."; + } + + return str; +} + +/* + Attempt to access the database subdirectory to see if it exists +*/ +bool rdb_database_exists(const std::string &db_name) { + const std::string dir = + std::string(mysql_real_data_home) + FN_DIRSEP + db_name; + struct st_my_dir *const dir_info = + my_dir(dir.c_str(), MYF(MY_DONT_SORT | MY_WANT_STAT)); + if (dir_info == nullptr) { + return false; + } + + my_dirend(dir_info); + return true; +} + +void rdb_log_status_error(const rocksdb::Status &s, const char *msg) { + if (msg == nullptr) { + // NO_LINT_DEBUG + sql_print_error("RocksDB: status error, code: %d, error message: %s", + s.code(), s.ToString().c_str()); + return; + } + + // NO_LINT_DEBUG + sql_print_error("RocksDB: %s, Status Code: %d, Status: %s", msg, s.code(), + s.ToString().c_str()); +} + +/* + @brief + Return a comma-separated string with compiled-in compression types. + Not thread-safe. +*/ +const char *get_rocksdb_supported_compression_types() +{ + static std::string compression_methods_buf; + static bool inited=false; + if (!inited) + { + inited= true; + std::vector<rocksdb::CompressionType> known_types= + { + rocksdb::kSnappyCompression, + rocksdb::kZlibCompression, + rocksdb::kBZip2Compression, + rocksdb::kLZ4Compression, + rocksdb::kLZ4HCCompression, + rocksdb::kXpressCompression, + rocksdb::kZSTDNotFinalCompression + }; + + for (auto typ : known_types) + { + if (CompressionTypeSupported(typ)) + { + if (compression_methods_buf.size()) + compression_methods_buf.append(","); + compression_methods_buf.append(CompressionTypeToString(typ)); + } + } + } + return compression_methods_buf.c_str(); +} + +bool rdb_check_rocksdb_corruption() { + return !my_access(myrocks::rdb_corruption_marker_file_name().c_str(), F_OK); +} + +void rdb_persist_corruption_marker() { + const std::string &fileName(myrocks::rdb_corruption_marker_file_name()); + /* O_SYNC is not supported on windows */ + int fd = my_open(fileName.c_str(), O_CREAT | IF_WIN(0, O_SYNC), MYF(MY_WME)); + if (fd < 0) { + // NO_LINT_DEBUG + sql_print_error( + "RocksDB: Can't create file %s to mark rocksdb as " + "corrupted.", + fileName.c_str()); + } else { + // NO_LINT_DEBUG + sql_print_information( + "RocksDB: Creating the file %s to abort server " + "restarts. Remove this file from the data directory " + "after fixing the corruption to recover. ", + fileName.c_str()); + } + +#ifdef _WIN32 + /* A replacement for O_SYNC flag above */ + if (fd >= 0) + my_sync(fd, MYF(0)); +#endif + + int ret = my_close(fd, MYF(MY_WME)); + if (ret) { + // NO_LINT_DEBUG + sql_print_error("RocksDB: Error (%d) closing the file %s", ret, + fileName.c_str()); + } +} + +} // namespace myrocks |