YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/rocksdb/util/options_parser.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
// This source code is licensed under the BSD-style license found in the
3
// LICENSE file in the root directory of this source tree. An additional grant
4
// of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
21
#include "yb/rocksdb/util/options_parser.h"
22
23
#include <math.h>
24
25
#include <map>
26
#include <string>
27
#include <utility>
28
#include <vector>
29
30
#include "yb/rocksdb/convenience.h"
31
#include "yb/rocksdb/db.h"
32
#include "yb/rocksdb/util/options_helper.h"
33
#include "yb/rocksdb/util/sync_point.h"
34
35
#include "yb/util/status_log.h"
36
#include "yb/util/string_util.h"
37
#include "yb/util/version_info.h"
38
39
namespace rocksdb {
40
41
static const std::string option_file_header =
42
    "# This is a RocksDB option file.\n"
43
    "#\n"
44
    "# For detailed file format spec, please refer to the example file\n"
45
    "# in examples/rocksdb_option_file_example.ini\n"
46
    "#\n"
47
    "\n";
48
49
Status PersistRocksDBOptions(const DBOptions& db_opt,
50
                             const std::vector<std::string>& cf_names,
51
                             const std::vector<ColumnFamilyOptions>& cf_opts,
52
                             const std::string& file_name, Env* env,
53
                             const IncludeHeader include_header,
54
1.27M
                             const IncludeFileVersion include_file_version) {
55
1.27M
  TEST_SYNC_POINT("PersistRocksDBOptions:start");
56
1.27M
  if (cf_names.size() != cf_opts.size()) {
57
0
    return STATUS(InvalidArgument,
58
0
        "cf_names.size() and cf_opts.size() must be the same");
59
0
  }
60
1.27M
  std::unique_ptr<WritableFile> writable;
61
62
1.27M
  Status s = env->NewWritableFile(file_name, &writable, EnvOptions());
63
1.27M
  if (!s.ok()) {
64
0
    return s;
65
0
  }
66
67
  // Header section
68
1.27M
  
if (1.27M
include_header1.27M
) {
69
1.27M
    RETURN_NOT_OK(writable->Append(option_file_header));
70
1.27M
  }
71
72
  // Versions section
73
1.27M
  RETURN_NOT_OK(writable->Append(
74
1.27M
      "[" + opt_section_titles[kOptionSectionVersion] + "]\n"
75
1.27M
      "  yugabyte_version=" + yb::VersionInfo::GetShortVersionString() + "\n"));
76
1.27M
  
if (1.27M
include_file_version1.27M
) {
77
1.27M
    RETURN_NOT_OK(writable->Append("  options_file_version=" +
78
1.27M
                                   ToString(ROCKSDB_OPTION_FILE_MAJOR) + "." +
79
1.27M
                                   ToString(ROCKSDB_OPTION_FILE_MINOR) + "\n"));
80
1.27M
  }
81
82
  // DBOptions section
83
1.27M
  std::string options_file_content;
84
1.27M
  RETURN_NOT_OK(writable->Append("\n[" + opt_section_titles[kOptionSectionDBOptions] + "]\n  "));
85
1.27M
  s = GetStringFromDBOptions(&options_file_content, db_opt, "\n  ");
86
1.27M
  if (!s.ok()) {
87
0
    WARN_NOT_OK(writable->Close(), "Failed to close writable");
88
0
    return s;
89
0
  }
90
1.27M
  RETURN_NOT_OK(writable->Append(options_file_content));
91
92
2.56M
  
for (size_t i = 0; 1.27M
i < cf_opts.size();
++i1.28M
) {
93
    // CFOptions section
94
1.28M
    RETURN_NOT_OK(writable->Append("\n[" + opt_section_titles[kOptionSectionCFOptions] +
95
1.28M
                                   " \"" + EscapeOptionString(cf_names[i]) + "\"]\n  "));
96
1.28M
    s = GetStringFromColumnFamilyOptions(&options_file_content, cf_opts[i], "\n  ");
97
1.28M
    if (!s.ok()) {
98
0
      WARN_NOT_OK(writable->Close(), "Failed to close writable");
99
0
      return s;
100
0
    }
101
1.28M
    RETURN_NOT_OK(writable->Append(options_file_content));
102
103
    // TableOptions section
104
1.28M
    auto* tf = cf_opts[i].table_factory.get();
105
1.28M
    if (tf != nullptr) {
106
1.28M
      RETURN_NOT_OK(writable->Append(
107
1.28M
          "[" + opt_section_titles[kOptionSectionTableOptions] +
108
1.28M
          tf->Name() + " \"" + EscapeOptionString(cf_names[i]) +
109
1.28M
          "\"]\n  "));
110
1.28M
      s = GetStringFromTableFactory(&options_file_content, tf, "\n  ");
111
1.28M
      if (!s.ok()) {
112
0
        return s;
113
0
      }
114
1.28M
      RETURN_NOT_OK(writable->Append(options_file_content));
115
1.28M
    }
116
1.28M
  }
117
118
1.27M
  RETURN_NOT_OK(writable->Flush());
119
1.27M
  if (!db_opt.disableDataSync) {
120
14.6k
    RETURN_NOT_OK(writable->Fsync());
121
14.6k
  }
122
1.27M
  RETURN_NOT_OK(writable->Close());
123
124
1.27M
  return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
125
1.27M
      db_opt, cf_names, cf_opts, file_name, env);
126
1.27M
}
127
128
1.27M
RocksDBOptionsParser::RocksDBOptionsParser() { Reset(); }
129
130
2.55M
void RocksDBOptionsParser::Reset() {
131
2.55M
  db_opt_ = DBOptions();
132
2.55M
  db_opt_map_.clear();
133
2.55M
  cf_names_.clear();
134
2.55M
  cf_opts_.clear();
135
2.55M
  cf_opt_maps_.clear();
136
2.55M
  has_version_section_ = false;
137
2.55M
  has_db_options_ = false;
138
2.55M
  has_default_cf_options_ = false;
139
10.2M
  for (int i = 0; i < 3; 
++i7.67M
) {
140
7.67M
    db_version[i] = 0;
141
7.67M
    opt_file_version[i] = 0;
142
7.67M
  }
143
2.55M
}
144
145
160M
bool RocksDBOptionsParser::IsSection(const std::string& line) {
146
160M
  if (line.size() < 2) {
147
0
    return false;
148
0
  }
149
160M
  if (line[0] != '[' || 
line[line.size() - 1] != ']'5.13M
) {
150
155M
    return false;
151
155M
  }
152
4.95M
  return true;
153
160M
}
154
155
Status RocksDBOptionsParser::ParseSection(OptionSection* section,
156
                                          std::string* title,
157
                                          std::string* argument,
158
                                          const std::string& line,
159
5.12M
                                          const int line_num) {
160
5.12M
  *section = kOptionSectionUnknown;
161
  // A section is of the form [<SectionName> "<SectionArg>"], where
162
  // "<SectionArg>" is optional.
163
5.12M
  size_t arg_start_pos = line.find("\"");
164
5.12M
  size_t arg_end_pos = line.rfind("\"");
165
  // The following if-then check tries to identify whether the input
166
  // section has the optional section argument.
167
5.12M
  if (arg_start_pos != std::string::npos && 
arg_start_pos != arg_end_pos2.57M
) {
168
2.57M
    *title = TrimAndRemoveComment(line.substr(1, arg_start_pos - 1), true);
169
2.57M
    *argument = UnescapeOptionString(
170
2.57M
        line.substr(arg_start_pos + 1, arg_end_pos - arg_start_pos - 1));
171
2.57M
  } else {
172
2.55M
    *title = TrimAndRemoveComment(line.substr(1, line.size() - 2), true);
173
2.55M
    *argument = "";
174
2.55M
  }
175
12.8M
  for (int i = 0; 
i < kOptionSectionUnknown12.8M
;
++i7.70M
) {
176
12.8M
    if (title->find(opt_section_titles[i]) == 0) {
177
5.13M
      if (i == kOptionSectionVersion || 
i == kOptionSectionDBOptions3.85M
||
178
5.13M
          
i == kOptionSectionCFOptions2.57M
) {
179
3.84M
        if (title->size() == opt_section_titles[i].size()) {
180
          // if true, then it indicats equal
181
3.84M
          *section = static_cast<OptionSection>(i);
182
3.84M
          return CheckSection(*section, *argument, line_num);
183
3.84M
        }
184
3.84M
      } else 
if (1.28M
i == kOptionSectionTableOptions1.28M
) {
185
        // This type of sections has a sufffix at the end of the
186
        // section title
187
1.28M
        if (title->size() > opt_section_titles[i].size()) {
188
1.28M
          *section = static_cast<OptionSection>(i);
189
1.28M
          return CheckSection(*section, *argument, line_num);
190
1.28M
        }
191
1.28M
      }
192
5.13M
    }
193
12.8M
  }
194
18.4E
  return STATUS(InvalidArgument, std::string("Unknown section ") + line);
195
5.12M
}
196
197
Status RocksDBOptionsParser::InvalidArgument(const int line_num,
198
4
                                             const std::string& message) {
199
4
  return STATUS(InvalidArgument,
200
4
      "[RocksDBOptionsParser Error] ",
201
4
      message + " (at line " + ToString(line_num) + ")");
202
4
}
203
204
Status RocksDBOptionsParser::ParseStatement(std::string* name,
205
                                            std::string* value,
206
                                            const std::string& line,
207
156M
                                            const int line_num) {
208
156M
  size_t eq_pos = line.find("=");
209
156M
  if (eq_pos == std::string::npos) {
210
0
    return InvalidArgument(line_num, "A valid statement must have a '='.");
211
0
  }
212
213
156M
  *name = TrimAndRemoveComment(line.substr(0, eq_pos), true);
214
156M
  *value =
215
156M
      TrimAndRemoveComment(line.substr(eq_pos + 1, line.size() - eq_pos - 1));
216
156M
  if (name->empty()) {
217
0
    return InvalidArgument(line_num,
218
0
                           "A valid statement must have a variable name.");
219
0
  }
220
156M
  return Status::OK();
221
156M
}
222
223
namespace {
224
bool ReadOneLine(std::istringstream* iss, SequentialFile* seq_file,
225
172M
                 std::string* output, bool* has_data, Status* result) {
226
172M
  const int kBufferSize = 4096;
227
172M
  uint8_t buffer[kBufferSize + 1];
228
172M
  Slice input_slice;
229
230
172M
  std::string line;
231
172M
  bool has_complete_line = false;
232
345M
  while (!has_complete_line) {
233
174M
    if (std::getline(*iss, line)) {
234
173M
      has_complete_line = !iss->eof();
235
173M
    } else {
236
687k
      has_complete_line = false;
237
687k
    }
238
174M
    if (!has_complete_line) {
239
      // if we're not sure whether we have a complete line,
240
      // further read from the file.
241
3.83M
      if (*has_data) {
242
2.55M
        *result = seq_file->Read(kBufferSize, &input_slice, buffer);
243
2.55M
      }
244
3.83M
      if (input_slice.size() == 0) {
245
        // meaning we have read all the data
246
1.27M
        *has_data = false;
247
1.27M
        break;
248
2.55M
      } else {
249
2.55M
        iss->str(line + input_slice.ToString());
250
        // reset the internal state of iss so that we can keep reading it.
251
2.55M
        iss->clear();
252
2.55M
        *has_data = (input_slice.size() == kBufferSize);
253
2.55M
        continue;
254
2.55M
      }
255
3.83M
    }
256
174M
  }
257
172M
  *output = line;
258
172M
  return *has_data || 
has_complete_line14.4M
;
259
172M
}
260
}  // namespace
261
262
1.27M
Status RocksDBOptionsParser::Parse(const std::string& file_name, Env* env) {
263
1.27M
  Reset();
264
265
1.27M
  std::unique_ptr<SequentialFile> seq_file;
266
1.27M
  Status s = env->NewSequentialFile(file_name, &seq_file, EnvOptions());
267
1.27M
  if (!s.ok()) {
268
0
    return s;
269
0
  }
270
271
1.27M
  OptionSection section = kOptionSectionUnknown;
272
1.27M
  std::string title;
273
1.27M
  std::string argument;
274
1.27M
  std::unordered_map<std::string, std::string> opt_map;
275
1.27M
  std::istringstream iss;
276
1.27M
  std::string line;
277
1.27M
  bool has_data = true;
278
  // we only support single-lined statement.
279
1.27M
  for (int line_num = 1;
280
172M
       ReadOneLine(&iss, seq_file.get(), &line, &has_data, &s); 
++line_num171M
) {
281
171M
    if (!s.ok()) {
282
0
      return s;
283
0
    }
284
171M
    line = TrimAndRemoveComment(line);
285
171M
    if (line.empty()) {
286
10.2M
      continue;
287
10.2M
    }
288
161M
    if (IsSection(line)) {
289
5.13M
      s = EndSection(section, title, argument, opt_map);
290
5.13M
      opt_map.clear();
291
5.13M
      if (!s.ok()) {
292
12
        return s;
293
12
      }
294
5.13M
      s = ParseSection(&section, &title, &argument, line, line_num);
295
5.13M
      if (!s.ok()) {
296
4
        return s;
297
4
      }
298
156M
    } else {
299
156M
      std::string name;
300
156M
      std::string value;
301
156M
      s = ParseStatement(&name, &value, line, line_num);
302
156M
      if (!s.ok()) {
303
0
        return s;
304
0
      }
305
156M
      opt_map.insert({name, value});
306
156M
    }
307
161M
  }
308
309
1.27M
  s = EndSection(section, title, argument, opt_map);
310
1.27M
  opt_map.clear();
311
1.27M
  if (!s.ok()) {
312
0
    return s;
313
0
  }
314
1.27M
  return ValidityCheck();
315
1.27M
}
316
317
Status RocksDBOptionsParser::CheckSection(const OptionSection section,
318
                                          const std::string& section_arg,
319
5.12M
                                          const int line_num) {
320
5.12M
  if (section == kOptionSectionDBOptions) {
321
1.27M
    if (has_db_options_) {
322
1
      return InvalidArgument(
323
1
          line_num,
324
1
          "More than one DBOption section found in the option config file");
325
1
    }
326
1.27M
    has_db_options_ = true;
327
3.85M
  } else if (section == kOptionSectionCFOptions) {
328
1.28M
    bool is_default_cf = (section_arg == kDefaultColumnFamilyName);
329
1.28M
    if (cf_opts_.size() == 0 && 
!is_default_cf1.27M
) {
330
2
      return InvalidArgument(
331
2
          line_num,
332
2
          "Default column family must be the first CFOptions section "
333
2
          "in the option config file");
334
1.28M
    } else if (cf_opts_.size() != 0 && 
is_default_cf6.19k
) {
335
0
      return InvalidArgument(
336
0
          line_num,
337
0
          "Default column family must be the first CFOptions section "
338
0
          "in the optio/n config file");
339
1.28M
    } else if (GetCFOptions(section_arg) != nullptr) {
340
1
      return InvalidArgument(
341
1
          line_num,
342
1
          "Two identical column families found in option config file");
343
1
    }
344
1.28M
    has_default_cf_options_ |= is_default_cf;
345
2.56M
  } else if (section == kOptionSectionTableOptions) {
346
1.28M
    if (GetCFOptions(section_arg) == nullptr) {
347
0
      return InvalidArgument(
348
0
          line_num, std::string(
349
0
                        "Does not find a matched column family name in "
350
0
                        "TableOptions section.  Column Family Name:") +
351
0
                        section_arg);
352
0
    }
353
1.28M
  } else 
if (1.27M
section == kOptionSectionVersion1.27M
) {
354
1.27M
    if (has_version_section_) {
355
0
      return InvalidArgument(
356
0
          line_num,
357
0
          "More than one Version section found in the option config file.");
358
0
    }
359
1.27M
    has_version_section_ = true;
360
1.27M
  }
361
5.12M
  return Status::OK();
362
5.12M
}
363
364
Status RocksDBOptionsParser::ParseVersionNumber(const std::string& ver_name,
365
                                                const std::string& ver_string,
366
                                                const int max_count,
367
1.27M
                                                int* version) {
368
1.27M
  int version_index = 0;
369
1.27M
  int current_number = 0;
370
1.27M
  int current_digit_count = 0;
371
1.27M
  bool has_dot = false;
372
3.83M
  for (int i = 0; i < max_count; 
++i2.55M
) {
373
2.55M
    version[i] = 0;
374
2.55M
  }
375
1.27M
  const int kBufferSize = 200;
376
1.27M
  char buffer[kBufferSize];
377
5.11M
  for (size_t i = 0; i < ver_string.size(); 
++i3.83M
) {
378
3.83M
    if (ver_string[i] == '.') {
379
1.27M
      if (version_index >= max_count - 1) {
380
6
        snprintf(buffer, sizeof(buffer) - 1,
381
6
                 "A valid %s can only contains at most %d dots.",
382
6
                 ver_name.c_str(), max_count - 1);
383
6
        return STATUS(InvalidArgument, buffer);
384
6
      }
385
1.27M
      if (current_digit_count == 0) {
386
2
        snprintf(buffer, sizeof(buffer) - 1,
387
2
                 "A valid %s must have at least one digit before each dot.",
388
2
                 ver_name.c_str());
389
2
        return STATUS(InvalidArgument, buffer);
390
2
      }
391
1.27M
      version[version_index++] = current_number;
392
1.27M
      current_number = 0;
393
1.27M
      current_digit_count = 0;
394
1.27M
      has_dot = true;
395
2.55M
    } else 
if (2.55M
isdigit(ver_string[i])2.55M
) {
396
2.55M
      current_number = current_number * 10 + (ver_string[i] - '0');
397
2.55M
      current_digit_count++;
398
18.4E
    } else {
399
18.4E
      snprintf(buffer, sizeof(buffer) - 1,
400
18.4E
               "A valid %s can only contains dots and numbers.",
401
18.4E
               ver_name.c_str());
402
18.4E
      return STATUS(InvalidArgument, buffer);
403
18.4E
    }
404
3.83M
  }
405
1.28M
  version[version_index] = current_number;
406
1.28M
  if (has_dot && 
current_digit_count == 01.27M
) {
407
1
    snprintf(buffer, sizeof(buffer) - 1,
408
1
             "A valid %s must have at least one digit after each dot.",
409
1
             ver_name.c_str());
410
1
    return STATUS(InvalidArgument, buffer);
411
1
  }
412
1.28M
  return Status::OK();
413
1.28M
}
414
415
Status RocksDBOptionsParser::EndSection(
416
    const OptionSection section, const std::string& section_title,
417
    const std::string& section_arg,
418
6.40M
    const std::unordered_map<std::string, std::string>& opt_map) {
419
6.40M
  Status s;
420
6.40M
  if (section == kOptionSectionDBOptions) {
421
1.27M
    s = GetDBOptionsFromMap(DBOptions(), opt_map, &db_opt_, true);
422
1.27M
    if (!s.ok()) {
423
0
      return s;
424
0
    }
425
1.27M
    db_opt_map_ = opt_map;
426
5.13M
  } else if (section == kOptionSectionCFOptions) {
427
    // This condition should be ensured earlier in ParseSection
428
    // so we make an assertion here.
429
1.28M
    assert(GetCFOptions(section_arg) == nullptr);
430
0
    cf_names_.emplace_back(section_arg);
431
1.28M
    cf_opts_.emplace_back();
432
1.28M
    s = GetColumnFamilyOptionsFromMap(ColumnFamilyOptions(), opt_map,
433
1.28M
                                      &cf_opts_.back(), true);
434
1.28M
    if (!s.ok()) {
435
0
      return s;
436
0
    }
437
    // keep the parsed string.
438
1.28M
    cf_opt_maps_.emplace_back(opt_map);
439
3.84M
  } else if (section == kOptionSectionTableOptions) {
440
1.28M
    assert(GetCFOptions(section_arg) != nullptr);
441
0
    auto* cf_opt = GetCFOptionsImpl(section_arg);
442
1.28M
    if (cf_opt == nullptr) {
443
0
      return STATUS(InvalidArgument,
444
0
          "The specified column family must be defined before the "
445
0
          "TableOptions section:",
446
0
          section_arg);
447
0
    }
448
    // Ignore error as table factory deserialization is optional
449
1.28M
    s = GetTableFactoryFromMap(
450
1.28M
        section_title.substr(
451
1.28M
            opt_section_titles[kOptionSectionTableOptions].size()),
452
1.28M
        opt_map, &(cf_opt->table_factory));
453
1.28M
    if (!s.ok()) {
454
0
      return s;
455
0
    }
456
2.55M
  } else if (section == kOptionSectionVersion) {
457
2.55M
    for (const auto& pair : opt_map) {
458
2.55M
      if (pair.first == "rocksdb_version") {
459
13
        s = ParseVersionNumber(pair.first, pair.second, 3, db_version);
460
13
        if (!s.ok()) {
461
0
          return s;
462
0
        }
463
2.55M
      } else if (pair.first == "options_file_version") {
464
1.27M
        s = ParseVersionNumber(pair.first, pair.second, 2, opt_file_version);
465
1.27M
        if (!s.ok()) {
466
12
          return s;
467
12
        }
468
1.27M
        if (opt_file_version[0] < 1) {
469
0
          return STATUS(InvalidArgument,
470
0
              "A valid options_file_version must be at least 1.");
471
0
        }
472
1.27M
      }
473
2.55M
    }
474
1.27M
  }
475
6.40M
  return Status::OK();
476
6.40M
}
477
478
1.27M
Status RocksDBOptionsParser::ValidityCheck() {
479
1.27M
  if (!has_db_options_) {
480
1
    return STATUS(Corruption,
481
1
        "A RocksDB Option file must have a single DBOptions section");
482
1
  }
483
1.27M
  if (!has_default_cf_options_) {
484
0
    return STATUS(Corruption,
485
0
        "A RocksDB Option file must have a single CFOptions:default section");
486
0
  }
487
488
1.27M
  return Status::OK();
489
1.27M
}
490
491
std::string RocksDBOptionsParser::TrimAndRemoveComment(const std::string& line,
492
484M
                                                       bool trim_only) {
493
484M
  size_t start = 0;
494
484M
  size_t end = line.size();
495
496
  // we only support "#" style comment
497
484M
  if (!trim_only) {
498
326M
    size_t search_pos = 0;
499
326M
    while (
search_pos < line.size()325M
) {
500
321M
      size_t comment_pos = line.find('#', search_pos);
501
321M
      if (comment_pos == std::string::npos) {
502
314M
        break;
503
314M
      }
504
6.39M
      
if (6.22M
comment_pos == 06.22M
||
line[comment_pos - 1] != '\\'140
) {
505
6.39M
        end = comment_pos;
506
6.39M
        break;
507
6.39M
      }
508
18.4E
      search_pos = comment_pos + 1;
509
18.4E
    }
510
326M
  }
511
512
801M
  while (start < end && 
isspace(line[start]) != 0786M
) {
513
316M
    ++start;
514
316M
  }
515
516
  // start < end implies end > 0.
517
487M
  while (start < end && 
isspace(line[end - 1]) != 0474M
) {
518
2.57M
    --end;
519
2.57M
  }
520
521
484M
  if (start < end) {
522
473M
    return line.substr(start, end - start);
523
473M
  }
524
525
10.7M
  return "";
526
484M
}
527
528
namespace {
529
1.28M
bool AreEqualDoubles(const double a, const double b) {
530
1.28M
  return (fabs(a - b) < 0.00001);
531
1.28M
}
532
533
bool AreEqualOptions(
534
    const char* opt1, const char* opt2, const OptionTypeInfo& type_info,
535
    const std::string& opt_name,
536
153M
    const std::unordered_map<std::string, std::string>* opt_map) {
537
153M
  const char* offset1 = opt1 + type_info.offset;
538
153M
  const char* offset2 = opt2 + type_info.offset;
539
153M
  static const std::string kNullptrString = "nullptr";
540
153M
  switch (type_info.type) {
541
44.8M
    case OptionType::kBoolean:
542
44.8M
      return (*reinterpret_cast<const bool*>(offset1) ==
543
44.8M
              *reinterpret_cast<const bool*>(offset2));
544
28.2M
    case OptionType::kInt:
545
28.2M
      return (*reinterpret_cast<const int*>(offset1) ==
546
28.2M
              *reinterpret_cast<const int*>(offset2));
547
1.27M
    case OptionType::kUInt:
548
1.27M
      return (*reinterpret_cast<const unsigned int*>(offset1) ==
549
1.27M
              *reinterpret_cast<const unsigned int*>(offset2));
550
7.70M
    case OptionType::kUInt32T:
551
7.70M
      return (*reinterpret_cast<const uint32_t*>(offset1) ==
552
7.70M
              *reinterpret_cast<const uint32_t*>(offset2));
553
23.0M
    case OptionType::kUInt64T:
554
23.0M
      return (*reinterpret_cast<const uint64_t*>(offset1) ==
555
23.0M
              *reinterpret_cast<const uint64_t*>(offset2));
556
23.0M
    case OptionType::kSizeT:
557
23.0M
      return (*reinterpret_cast<const size_t*>(offset1) ==
558
23.0M
              *reinterpret_cast<const size_t*>(offset2));
559
2.55M
    case OptionType::kString:
560
2.55M
      return (*reinterpret_cast<const std::string*>(offset1) ==
561
2.55M
              *reinterpret_cast<const std::string*>(offset2));
562
1.28M
    case OptionType::kDouble:
563
1.28M
      return AreEqualDoubles(*reinterpret_cast<const double*>(offset1),
564
1.28M
                             *reinterpret_cast<const double*>(offset2));
565
1.28M
    case OptionType::kCompactionStyle:
566
1.28M
      return (*reinterpret_cast<const CompactionStyle*>(offset1) ==
567
1.28M
              *reinterpret_cast<const CompactionStyle*>(offset2));
568
1.28M
    case OptionType::kCompressionType:
569
1.28M
      return (*reinterpret_cast<const CompressionType*>(offset1) ==
570
1.28M
              *reinterpret_cast<const CompressionType*>(offset2));
571
1.28M
    case OptionType::kVectorCompressionType: {
572
1.28M
      const auto* vec1 =
573
1.28M
          reinterpret_cast<const std::vector<CompressionType>*>(offset1);
574
1.28M
      const auto* vec2 =
575
1.28M
          reinterpret_cast<const std::vector<CompressionType>*>(offset2);
576
1.28M
      return (*vec1 == *vec2);
577
0
    }
578
1.28M
    case OptionType::kChecksumType:
579
1.28M
      return (*reinterpret_cast<const ChecksumType*>(offset1) ==
580
1.28M
              *reinterpret_cast<const ChecksumType*>(offset2));
581
1.28M
    case OptionType::kBlockBasedTableIndexType:
582
1.28M
      return (
583
1.28M
          *reinterpret_cast<const IndexType*>(offset1) ==
584
1.28M
          *reinterpret_cast<const IndexType*>(offset2));
585
1.27M
    case OptionType::kWALRecoveryMode:
586
1.27M
      return (*reinterpret_cast<const WALRecoveryMode*>(offset1) ==
587
1.27M
              *reinterpret_cast<const WALRecoveryMode*>(offset2));
588
1.27M
    case OptionType::kAccessHint:
589
1.27M
      return (*reinterpret_cast<const DBOptions::AccessHint*>(offset1) ==
590
1.27M
              *reinterpret_cast<const DBOptions::AccessHint*>(offset2));
591
1.27M
    case OptionType::kInfoLogLevel:
592
1.27M
      return (*reinterpret_cast<const InfoLogLevel*>(offset1) ==
593
1.27M
              *reinterpret_cast<const InfoLogLevel*>(offset2));
594
11.5M
    default:
595
11.5M
      if (type_info.verification == OptionVerificationType::kByName ||
596
11.5M
          
type_info.verification == OptionVerificationType::kByNameAllowNull1.28M
) {
597
11.5M
        std::string value1;
598
11.5M
        bool result =
599
11.5M
            SerializeSingleOptionHelper(offset1, type_info.type, &value1);
600
11.5M
        if (result == false) {
601
0
          return false;
602
0
        }
603
11.5M
        if (opt_map == nullptr) {
604
2.56M
          return true;
605
2.56M
        }
606
8.99M
        auto iter = opt_map->find(opt_name);
607
8.99M
        if (iter == opt_map->end()) {
608
0
          return true;
609
8.99M
        } else {
610
8.99M
          if (type_info.verification ==
611
8.99M
              OptionVerificationType::kByNameAllowNull) {
612
1.28M
            if (iter->second == kNullptrString || 
value1 == kNullptrString3.08k
) {
613
1.28M
              return true;
614
1.28M
            }
615
1.28M
          }
616
7.71M
          return (value1 == iter->second);
617
8.99M
        }
618
8.99M
      }
619
18.4E
      return false;
620
153M
  }
621
153M
}
622
623
}  // namespace
624
625
Status RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
626
    const DBOptions& db_opt, const std::vector<std::string>& cf_names,
627
    const std::vector<ColumnFamilyOptions>& cf_opts,
628
    const std::string& file_name, Env* env,
629
1.27M
    OptionsSanityCheckLevel sanity_check_level) {
630
1.27M
  RocksDBOptionsParser parser;
631
1.27M
  std::unique_ptr<SequentialFile> seq_file;
632
1.27M
  Status s = parser.Parse(file_name, env);
633
1.27M
  if (!s.ok()) {
634
0
    return s;
635
0
  }
636
637
  // Verify DBOptions
638
1.27M
  s = VerifyDBOptions(db_opt, *parser.db_opt(), parser.db_opt_map(),
639
1.27M
                      sanity_check_level);
640
1.27M
  if (!s.ok()) {
641
1
    return s;
642
1
  }
643
644
  // Verify ColumnFamily Name
645
1.27M
  if (cf_names.size() != parser.cf_names()->size()) {
646
0
    if (sanity_check_level >= kSanityLevelLooselyCompatible) {
647
0
      return STATUS(InvalidArgument,
648
0
          "[RocksDBOptionParser Error] The persisted options does not have "
649
0
          "the same number of column family names as the db instance.");
650
0
    } else if (cf_opts.size() > parser.cf_opts()->size()) {
651
0
      return STATUS(InvalidArgument,
652
0
          "[RocksDBOptionsParser Error]",
653
0
          "The persisted options file has less number of column family "
654
0
          "names than that of the specified one.");
655
0
    }
656
0
  }
657
2.56M
  
for (size_t i = 0; 1.27M
i < cf_names.size();
++i1.28M
) {
658
1.28M
    if (cf_names[i] != parser.cf_names()->at(i)) {
659
0
      return STATUS(InvalidArgument,
660
0
          "[RocksDBOptionParser Error] The persisted options and the db"
661
0
          "instance does not have the same name for column family ",
662
0
          ToString(i));
663
0
    }
664
1.28M
  }
665
666
  // Verify Column Family Options
667
1.27M
  if (cf_opts.size() != parser.cf_opts()->size()) {
668
0
    if (sanity_check_level >= kSanityLevelLooselyCompatible) {
669
0
      return STATUS(InvalidArgument,
670
0
          "[RocksDBOptionsParser Error]",
671
0
          "The persisted options does not have the same number of "
672
0
          "column families as the db instance.");
673
0
    } else if (cf_opts.size() > parser.cf_opts()->size()) {
674
0
      return STATUS(InvalidArgument,
675
0
          "[RocksDBOptionsParser Error]",
676
0
          "The persisted options file has less number of column families "
677
0
          "than that of the specified number.");
678
0
    }
679
0
  }
680
2.56M
  
for (size_t i = 0; 1.27M
i < cf_opts.size();
++i1.28M
) {
681
1.28M
    s = VerifyCFOptions(cf_opts[i], parser.cf_opts()->at(i),
682
1.28M
                        &(parser.cf_opt_maps()->at(i)), sanity_check_level);
683
1.28M
    if (!s.ok()) {
684
25
      return s;
685
25
    }
686
1.28M
    s = VerifyTableFactory(cf_opts[i].table_factory.get(),
687
1.28M
                           parser.cf_opts()->at(i).table_factory.get(),
688
1.28M
                           sanity_check_level);
689
1.28M
    if (!s.ok()) {
690
56
      return s;
691
56
    }
692
1.28M
  }
693
694
1.27M
  return Status::OK();
695
1.27M
}
696
697
Status RocksDBOptionsParser::VerifyDBOptions(
698
    const DBOptions& base_opt, const DBOptions& persisted_opt,
699
    const std::unordered_map<std::string, std::string>* opt_map,
700
1.27M
    OptionsSanityCheckLevel sanity_check_level) {
701
72.7M
  for (auto pair : db_options_type_info) {
702
72.7M
    if (pair.second.verification == OptionVerificationType::kDeprecated) {
703
      // We skip checking deprecated variables as they might
704
      // contain random values since they might not be initialized
705
0
      continue;
706
0
    }
707
72.8M
    
if (72.7M
DBOptionSanityCheckLevel(pair.first) <= sanity_check_level72.7M
) {
708
72.8M
      if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt),
709
72.8M
                           reinterpret_cast<const char*>(&persisted_opt),
710
72.8M
                           pair.second, pair.first, nullptr)) {
711
2
        const size_t kBufferSize = 2048;
712
2
        char buffer[kBufferSize];
713
2
        std::string base_value;
714
2
        std::string persisted_value;
715
2
        SerializeSingleOptionHelper(
716
2
            reinterpret_cast<const char*>(&base_opt) + pair.second.offset,
717
2
            pair.second.type, &base_value);
718
2
        SerializeSingleOptionHelper(
719
2
            reinterpret_cast<const char*>(&persisted_opt) + pair.second.offset,
720
2
            pair.second.type, &persisted_value);
721
2
        snprintf(buffer, sizeof(buffer),
722
2
                 "[RocksDBOptionsParser]: "
723
2
                 "failed the verification on DBOptions::%s --- "
724
2
                 "The specified one is %s while the persisted one is %s.\n",
725
2
                 pair.first.c_str(), base_value.c_str(),
726
2
                 persisted_value.c_str());
727
2
        return STATUS(InvalidArgument, Slice(buffer, strlen(buffer)));
728
2
      }
729
72.8M
    }
730
72.7M
  }
731
1.27M
  return Status::OK();
732
1.27M
}
733
734
Status RocksDBOptionsParser::VerifyCFOptions(
735
    const ColumnFamilyOptions& base_opt,
736
    const ColumnFamilyOptions& persisted_opt,
737
    const std::unordered_map<std::string, std::string>* persisted_opt_map,
738
1.28M
    OptionsSanityCheckLevel sanity_check_level) {
739
62.9M
  for (auto& pair : cf_options_type_info) {
740
62.9M
    if (pair.second.verification == OptionVerificationType::kDeprecated) {
741
      // We skip checking deprecated variables as they might
742
      // contain random values since they might not be initialized
743
3.85M
      continue;
744
3.85M
    }
745
59.0M
    
if (59.0M
CFOptionSanityCheckLevel(pair.first) <= sanity_check_level59.0M
) {
746
59.0M
      if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt),
747
59.0M
                           reinterpret_cast<const char*>(&persisted_opt),
748
59.0M
                           pair.second, pair.first, persisted_opt_map)) {
749
60
        const size_t kBufferSize = 2048;
750
60
        char buffer[kBufferSize];
751
60
        std::string base_value;
752
60
        std::string persisted_value;
753
60
        SerializeSingleOptionHelper(
754
60
            reinterpret_cast<const char*>(&base_opt) + pair.second.offset,
755
60
            pair.second.type, &base_value);
756
60
        SerializeSingleOptionHelper(
757
60
            reinterpret_cast<const char*>(&persisted_opt) + pair.second.offset,
758
60
            pair.second.type, &persisted_value);
759
60
        snprintf(buffer, sizeof(buffer),
760
60
                 "[RocksDBOptionsParser]: "
761
60
                 "failed the verification on ColumnFamilyOptions::%s --- "
762
60
                 "The specified one is %s while the persisted one is %s.\n",
763
60
                 pair.first.c_str(), base_value.c_str(),
764
60
                 persisted_value.c_str());
765
60
        return STATUS(InvalidArgument, Slice(buffer, sizeof(buffer)));
766
60
      }
767
59.0M
    }
768
59.0M
  }
769
1.28M
  return Status::OK();
770
1.28M
}
771
772
Status RocksDBOptionsParser::VerifyBlockBasedTableFactory(
773
    const BlockBasedTableFactory* base_tf,
774
    const BlockBasedTableFactory* file_tf,
775
1.28M
    OptionsSanityCheckLevel sanity_check_level) {
776
1.28M
  if ((base_tf != nullptr) != (file_tf != nullptr) &&
777
1.28M
      
sanity_check_level > kSanityLevelNone2
) {
778
0
    return STATUS(Corruption,
779
0
        "[RocksDBOptionsParser]: Inconsistent TableFactory class type");
780
0
  }
781
1.28M
  if (base_tf == nullptr || 
file_tf == nullptr1.28M
) {
782
1.56k
    return Status::OK();
783
1.56k
  }
784
785
1.28M
  const auto& base_opt = base_tf->table_options();
786
1.28M
  const auto& file_opt = file_tf->table_options();
787
788
21.8M
  for (auto& pair : block_based_table_type_info) {
789
21.8M
    if (pair.second.verification == OptionVerificationType::kDeprecated) {
790
      // We skip checking deprecated variables as they might
791
      // contain random values since they might not be initialized
792
0
      continue;
793
0
    }
794
21.8M
    
if (21.8M
BBTOptionSanityCheckLevel(pair.first) <= sanity_check_level21.8M
) {
795
21.8M
      if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt),
796
21.8M
                           reinterpret_cast<const char*>(&file_opt),
797
21.8M
                           pair.second, pair.first, nullptr)) {
798
0
        return STATUS(Corruption,
799
0
            "[RocksDBOptionsParser]: "
800
0
            "failed the verification on BlockBasedTableOptions::",
801
0
            pair.first);
802
0
      }
803
21.8M
    }
804
21.8M
  }
805
1.28M
  return Status::OK();
806
1.28M
}
807
808
Status RocksDBOptionsParser::VerifyTableFactory(
809
    const TableFactory* base_tf, const TableFactory* file_tf,
810
1.28M
    OptionsSanityCheckLevel sanity_check_level) {
811
1.28M
  if (
base_tf1.28M
&& file_tf) {
812
1.28M
    if (sanity_check_level > kSanityLevelNone &&
813
1.28M
        
base_tf->Name() != file_tf->Name()1.28M
) {
814
56
      return STATUS(Corruption,
815
56
          "[RocksDBOptionsParser]: "
816
56
          "failed the verification on TableFactory->Name()");
817
56
    }
818
1.28M
    auto s = VerifyBlockBasedTableFactory(
819
1.28M
        dynamic_cast<const BlockBasedTableFactory*>(base_tf),
820
1.28M
        dynamic_cast<const BlockBasedTableFactory*>(file_tf),
821
1.28M
        sanity_check_level);
822
1.28M
    if (!s.ok()) {
823
0
      return s;
824
0
    }
825
    // TODO(yhchiang): add checks for other table factory types
826
18.4E
  } else {
827
    // TODO(yhchiang): further support sanity check here
828
18.4E
  }
829
1.28M
  return Status::OK();
830
1.28M
}
831
}  // namespace rocksdb