1 /*
2 Copyright (c) 2016, Facebook, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
16
17 #include <my_global.h>
18
19 /* This C++ file's header */
20 #include "./rdb_utils.h"
21
22 /* C++ standard header files */
23 #include <array>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27
28 /* C standard header files */
29 #include <ctype.h>
30
31 /* MyRocks header files */
32 #include "./ha_rocksdb.h"
33
34 /*
35 Both innobase/include/ut0counter.h and rocksdb/port/port_posix.h define
36 CACHE_LINE_SIZE.
37 */
38 #ifdef CACHE_LINE_SIZE
39 # undef CACHE_LINE_SIZE
40 #endif
41
42 /* RocksDB header files */
43 #include "util/compression.h"
44
45 namespace myrocks {
46
47 /*
48 Skip past any spaces in the input
49 */
rdb_skip_spaces(const struct charset_info_st * const cs,const char * str)50 const char *rdb_skip_spaces(const struct charset_info_st *const cs,
51 const char *str) {
52 while (my_isspace(cs, *str)) {
53 str++;
54 }
55
56 return str;
57 }
58
59 /*
60 Compare (ignoring case) to see if str2 is the next data in str1.
61 Note that str1 can be longer but we only compare up to the number
62 of characters in str2.
63 */
rdb_compare_strings_ic(const char * const str1,const char * const str2)64 bool rdb_compare_strings_ic(const char *const str1, const char *const str2) {
65 // Scan through the strings
66 size_t ii;
67 for (ii = 0; str2[ii]; ii++) {
68 if (toupper(static_cast<int>(str1[ii])) !=
69 toupper(static_cast<int>(str2[ii]))) {
70 return false;
71 }
72 }
73
74 return true;
75 }
76
77 /*
78 Scan through an input string looking for pattern, ignoring case
79 and skipping all data enclosed in quotes.
80 */
rdb_find_in_string(const char * str,const char * pattern,bool * const succeeded)81 const char *rdb_find_in_string(const char *str, const char *pattern,
82 bool *const succeeded) {
83 char quote = '\0';
84 bool escape = false;
85
86 *succeeded = false;
87
88 for (; *str; str++) {
89 /* If we found a our starting quote character */
90 if (*str == quote) {
91 /* If it was escaped ignore it */
92 if (escape) {
93 escape = false;
94 }
95 /* Otherwise we are now outside of the quoted string */
96 else {
97 quote = '\0';
98 }
99 }
100 /* Else if we are currently inside a quoted string? */
101 else if (quote != '\0') {
102 /* If so, check for the escape character */
103 escape = !escape && *str == '\\';
104 }
105 /* Else if we found a quote we are starting a quoted string */
106 else if (*str == '"' || *str == '\'' || *str == '`') {
107 quote = *str;
108 }
109 /* Else we are outside of a quoted string - look for our pattern */
110 else {
111 if (rdb_compare_strings_ic(str, pattern)) {
112 *succeeded = true;
113 return str;
114 }
115 }
116 }
117
118 // Return the character after the found pattern or the null terminateor
119 // if the pattern wasn't found.
120 return str;
121 }
122
123 /*
124 See if the next valid token matches the specified string
125 */
rdb_check_next_token(const struct charset_info_st * const cs,const char * str,const char * const pattern,bool * const succeeded)126 const char *rdb_check_next_token(const struct charset_info_st *const cs,
127 const char *str, const char *const pattern,
128 bool *const succeeded) {
129 // Move past any spaces
130 str = rdb_skip_spaces(cs, str);
131
132 // See if the next characters match the pattern
133 if (rdb_compare_strings_ic(str, pattern)) {
134 *succeeded = true;
135 return str + strlen(pattern);
136 }
137
138 *succeeded = false;
139 return str;
140 }
141
142 /*
143 Parse id
144 */
rdb_parse_id(const struct charset_info_st * const cs,const char * str,std::string * const id)145 const char *rdb_parse_id(const struct charset_info_st *const cs,
146 const char *str, std::string *const id) {
147 // Move past any spaces
148 str = rdb_skip_spaces(cs, str);
149
150 if (*str == '\0') {
151 return str;
152 }
153
154 char quote = '\0';
155 if (*str == '`' || *str == '"') {
156 quote = *str++;
157 }
158
159 size_t len = 0;
160 const char *start = str;
161
162 if (quote != '\0') {
163 for (;;) {
164 if (*str == '\0') {
165 return str;
166 }
167
168 if (*str == quote) {
169 str++;
170 if (*str != quote) {
171 break;
172 }
173 }
174
175 str++;
176 len++;
177 }
178 } else {
179 while (!my_isspace(cs, *str) && *str != '(' && *str != ')' && *str != '.' &&
180 *str != ',' && *str != '\0') {
181 str++;
182 len++;
183 }
184 }
185
186 // If the user requested the id create it and return it
187 if (id != nullptr) {
188 *id = std::string("");
189 id->reserve(len);
190 while (len--) {
191 *id += *start;
192 if (*start++ == quote) {
193 start++;
194 }
195 }
196 }
197
198 return str;
199 }
200
201 /*
202 Skip id
203 */
rdb_skip_id(const struct charset_info_st * const cs,const char * str)204 const char *rdb_skip_id(const struct charset_info_st *const cs,
205 const char *str) {
206 return rdb_parse_id(cs, str, nullptr);
207 }
208
209 /*
210 Parses a given string into tokens (if any) separated by a specific delimiter.
211 */
parse_into_tokens(const std::string & s,const char delim)212 const std::vector<std::string> parse_into_tokens(const std::string &s,
213 const char delim) {
214 std::vector<std::string> tokens;
215 std::string t;
216 std::stringstream ss(s);
217
218 while (getline(ss, t, delim)) {
219 tokens.push_back(t);
220 }
221
222 return tokens;
223 }
224
225 static const std::size_t rdb_hex_bytes_per_char = 2;
226 static const std::array<char, 16> rdb_hexdigit = {{'0', '1', '2', '3', '4', '5',
227 '6', '7', '8', '9', 'a', 'b',
228 'c', 'd', 'e', 'f'}};
229
230 /*
231 Convert data into a hex string with optional maximum length.
232 If the data is larger than the maximum length trancate it and append "..".
233 */
rdb_hexdump(const char * data,const std::size_t data_len,const std::size_t maxsize)234 std::string rdb_hexdump(const char *data, const std::size_t data_len,
235 const std::size_t maxsize) {
236 // Count the elements in the string
237 std::size_t elems = data_len;
238 // Calculate the amount of output needed
239 std::size_t len = elems * rdb_hex_bytes_per_char;
240 std::string str;
241
242 if (maxsize != 0 && len > maxsize) {
243 // If the amount of output is too large adjust the settings
244 // and leave room for the ".." at the end
245 elems = (maxsize - 2) / rdb_hex_bytes_per_char;
246 len = elems * rdb_hex_bytes_per_char + 2;
247 }
248
249 // Reserve sufficient space to avoid reallocations
250 str.reserve(len);
251
252 // Loop through the input data and build the output string
253 for (std::size_t ii = 0; ii < elems; ii++, data++) {
254 uint8_t ch = (uint8_t)*data;
255 str += rdb_hexdigit[ch >> 4];
256 str += rdb_hexdigit[ch & 0x0F];
257 }
258
259 // If we can't fit it all add the ".."
260 if (elems != data_len) {
261 str += "..";
262 }
263
264 return str;
265 }
266
267 /*
268 Attempt to access the database subdirectory to see if it exists
269 */
rdb_database_exists(const std::string & db_name)270 bool rdb_database_exists(const std::string &db_name) {
271 const std::string dir =
272 std::string(mysql_real_data_home) + FN_DIRSEP + db_name;
273 struct st_my_dir *const dir_info =
274 my_dir(dir.c_str(), MYF(MY_DONT_SORT | MY_WANT_STAT));
275 if (dir_info == nullptr) {
276 return false;
277 }
278
279 my_dirend(dir_info);
280 return true;
281 }
282
rdb_log_status_error(const rocksdb::Status & s,const char * msg)283 void rdb_log_status_error(const rocksdb::Status &s, const char *msg) {
284 if (msg == nullptr) {
285 // NO_LINT_DEBUG
286 sql_print_error("RocksDB: status error, code: %d, error message: %s",
287 s.code(), s.ToString().c_str());
288 return;
289 }
290
291 // NO_LINT_DEBUG
292 sql_print_error("RocksDB: %s, Status Code: %d, Status: %s", msg, s.code(),
293 s.ToString().c_str());
294 }
295
296 /*
297 @brief
298 Return a comma-separated string with compiled-in compression types.
299 Not thread-safe.
300 */
get_rocksdb_supported_compression_types()301 const char *get_rocksdb_supported_compression_types()
302 {
303 static std::string compression_methods_buf;
304 static bool inited=false;
305 if (!inited)
306 {
307 inited= true;
308 std::vector<rocksdb::CompressionType> known_types=
309 {
310 rocksdb::kSnappyCompression,
311 rocksdb::kZlibCompression,
312 rocksdb::kBZip2Compression,
313 rocksdb::kLZ4Compression,
314 rocksdb::kLZ4HCCompression,
315 rocksdb::kXpressCompression,
316 rocksdb::kZSTDNotFinalCompression
317 };
318
319 for (auto typ : known_types)
320 {
321 if (CompressionTypeSupported(typ))
322 {
323 if (compression_methods_buf.size())
324 compression_methods_buf.append(",");
325 compression_methods_buf.append(CompressionTypeToString(typ));
326 }
327 }
328 }
329 return compression_methods_buf.c_str();
330 }
331
rdb_check_rocksdb_corruption()332 bool rdb_check_rocksdb_corruption() {
333 return !my_access(myrocks::rdb_corruption_marker_file_name().c_str(), F_OK);
334 }
335
rdb_persist_corruption_marker()336 void rdb_persist_corruption_marker() {
337 const std::string &fileName(myrocks::rdb_corruption_marker_file_name());
338 /* O_SYNC is not supported on windows */
339 int fd = my_open(fileName.c_str(), O_CREAT | IF_WIN(0, O_SYNC), MYF(MY_WME));
340 if (fd < 0) {
341 // NO_LINT_DEBUG
342 sql_print_error(
343 "RocksDB: Can't create file %s to mark rocksdb as "
344 "corrupted.",
345 fileName.c_str());
346 } else {
347 // NO_LINT_DEBUG
348 sql_print_information(
349 "RocksDB: Creating the file %s to abort mysqld "
350 "restarts. Remove this file from the data directory "
351 "after fixing the corruption to recover. ",
352 fileName.c_str());
353 }
354
355 #ifdef _WIN32
356 /* A replacement for O_SYNC flag above */
357 if (fd >= 0)
358 my_sync(fd, MYF(0));
359 #endif
360
361 int ret = my_close(fd, MYF(MY_WME));
362 if (ret) {
363 // NO_LINT_DEBUG
364 sql_print_error("RocksDB: Error (%d) closing the file %s", ret,
365 fileName.c_str());
366 }
367 }
368
369 } // namespace myrocks
370