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