]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/options/options_parser.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / options / options_parser.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
2// This source code is licensed under both the GPLv2 (found in the
3// COPYING file in the root directory) and Apache 2.0 License
4// (found in the LICENSE.Apache file in the root directory).
7c673cae
FG
5
6#ifndef ROCKSDB_LITE
7
8#include "options/options_parser.h"
9
10#include <cmath>
11#include <map>
12#include <string>
13#include <utility>
14#include <vector>
15
f67539c2
TL
16#include "file/read_write_util.h"
17#include "file/writable_file_writer.h"
20effc67
TL
18#include "options/cf_options.h"
19#include "options/db_options.h"
7c673cae 20#include "options/options_helper.h"
20effc67 21#include "port/port.h"
7c673cae
FG
22#include "rocksdb/convenience.h"
23#include "rocksdb/db.h"
20effc67 24#include "rocksdb/utilities/options_type.h"
f67539c2 25#include "test_util/sync_point.h"
11fdf7f2 26#include "util/cast_util.h"
7c673cae 27#include "util/string_util.h"
7c673cae 28
f67539c2 29namespace ROCKSDB_NAMESPACE {
7c673cae
FG
30
31static const std::string option_file_header =
32 "# This is a RocksDB option file.\n"
33 "#\n"
34 "# For detailed file format spec, please refer to the example file\n"
35 "# in examples/rocksdb_option_file_example.ini\n"
36 "#\n"
37 "\n";
38
39Status PersistRocksDBOptions(const DBOptions& db_opt,
40 const std::vector<std::string>& cf_names,
41 const std::vector<ColumnFamilyOptions>& cf_opts,
f67539c2 42 const std::string& file_name, FileSystem* fs) {
20effc67
TL
43 ConfigOptions
44 config_options; // Use default for escaped(true) and check (exact)
45 config_options.delimiter = "\n ";
46 // Do not invoke PrepareOptions when we are doing validation.
47 config_options.invoke_prepare_options = false;
48 // If a readahead size was set in the input options, use it
49 if (db_opt.log_readahead_size > 0) {
50 config_options.file_readahead_size = db_opt.log_readahead_size;
51 }
52 return PersistRocksDBOptions(config_options, db_opt, cf_names, cf_opts,
53 file_name, fs);
54}
55
56Status PersistRocksDBOptions(const ConfigOptions& config_options_in,
57 const DBOptions& db_opt,
58 const std::vector<std::string>& cf_names,
59 const std::vector<ColumnFamilyOptions>& cf_opts,
60 const std::string& file_name, FileSystem* fs) {
61 ConfigOptions config_options = config_options_in;
62 config_options.delimiter = "\n "; // Override the default to nl
63
7c673cae
FG
64 TEST_SYNC_POINT("PersistRocksDBOptions:start");
65 if (cf_names.size() != cf_opts.size()) {
66 return Status::InvalidArgument(
67 "cf_names.size() and cf_opts.size() must be the same");
68 }
f67539c2 69 std::unique_ptr<FSWritableFile> wf;
7c673cae 70
f67539c2
TL
71 Status s =
72 fs->NewWritableFile(file_name, FileOptions(), &wf, nullptr);
7c673cae
FG
73 if (!s.ok()) {
74 return s;
75 }
494da23a 76 std::unique_ptr<WritableFileWriter> writable;
11fdf7f2
TL
77 writable.reset(new WritableFileWriter(std::move(wf), file_name, EnvOptions(),
78 nullptr /* statistics */));
79
7c673cae
FG
80 std::string options_file_content;
81
20effc67
TL
82 s = writable->Append(option_file_header + "[" +
83 opt_section_titles[kOptionSectionVersion] +
84 "]\n"
85 " rocksdb_version=" +
86 ToString(ROCKSDB_MAJOR) + "." + ToString(ROCKSDB_MINOR) +
87 "." + ToString(ROCKSDB_PATCH) + "\n");
88 if (s.ok()) {
89 s = writable->Append(
90 " options_file_version=" + ToString(ROCKSDB_OPTION_FILE_MAJOR) + "." +
91 ToString(ROCKSDB_OPTION_FILE_MINOR) + "\n");
92 }
93 if (s.ok()) {
94 s = writable->Append("\n[" + opt_section_titles[kOptionSectionDBOptions] +
95 "]\n ");
7c673cae 96 }
7c673cae 97
20effc67
TL
98 if (s.ok()) {
99 s = GetStringFromDBOptions(config_options, db_opt, &options_file_content);
100 }
101 if (s.ok()) {
102 s = writable->Append(options_file_content + "\n");
103 }
104
105 for (size_t i = 0; s.ok() && i < cf_opts.size(); ++i) {
7c673cae 106 // CFOptions section
20effc67
TL
107 s = writable->Append("\n[" + opt_section_titles[kOptionSectionCFOptions] +
108 " \"" + EscapeOptionString(cf_names[i]) + "\"]\n ");
109 if (s.ok()) {
110 s = GetStringFromColumnFamilyOptions(config_options, cf_opts[i],
111 &options_file_content);
112 }
113 if (s.ok()) {
114 s = writable->Append(options_file_content + "\n");
7c673cae 115 }
7c673cae
FG
116 // TableOptions section
117 auto* tf = cf_opts[i].table_factory.get();
118 if (tf != nullptr) {
20effc67
TL
119 if (s.ok()) {
120 s = writable->Append(
121 "[" + opt_section_titles[kOptionSectionTableOptions] + tf->Name() +
122 " \"" + EscapeOptionString(cf_names[i]) + "\"]\n ");
123 }
124 if (s.ok()) {
125 options_file_content.clear();
126 s = tf->GetOptionString(config_options, &options_file_content);
127 }
128 if (s.ok()) {
129 s = writable->Append(options_file_content + "\n");
7c673cae 130 }
7c673cae
FG
131 }
132 }
20effc67
TL
133 if (s.ok()) {
134 s = writable->Sync(true /* use_fsync */);
135 }
136 if (s.ok()) {
137 s = writable->Close();
138 }
139 if (s.ok()) {
140 return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
141 config_options, db_opt, cf_names, cf_opts, file_name, fs);
142 }
143 return s;
7c673cae
FG
144}
145
146RocksDBOptionsParser::RocksDBOptionsParser() { Reset(); }
147
148void RocksDBOptionsParser::Reset() {
149 db_opt_ = DBOptions();
150 db_opt_map_.clear();
151 cf_names_.clear();
152 cf_opts_.clear();
153 cf_opt_maps_.clear();
154 has_version_section_ = false;
155 has_db_options_ = false;
156 has_default_cf_options_ = false;
157 for (int i = 0; i < 3; ++i) {
158 db_version[i] = 0;
159 opt_file_version[i] = 0;
160 }
161}
162
163bool RocksDBOptionsParser::IsSection(const std::string& line) {
164 if (line.size() < 2) {
165 return false;
166 }
167 if (line[0] != '[' || line[line.size() - 1] != ']') {
168 return false;
169 }
170 return true;
171}
172
173Status RocksDBOptionsParser::ParseSection(OptionSection* section,
174 std::string* title,
175 std::string* argument,
176 const std::string& line,
177 const int line_num) {
178 *section = kOptionSectionUnknown;
179 // A section is of the form [<SectionName> "<SectionArg>"], where
180 // "<SectionArg>" is optional.
181 size_t arg_start_pos = line.find("\"");
182 size_t arg_end_pos = line.rfind("\"");
183 // The following if-then check tries to identify whether the input
184 // section has the optional section argument.
185 if (arg_start_pos != std::string::npos && arg_start_pos != arg_end_pos) {
186 *title = TrimAndRemoveComment(line.substr(1, arg_start_pos - 1), true);
187 *argument = UnescapeOptionString(
188 line.substr(arg_start_pos + 1, arg_end_pos - arg_start_pos - 1));
189 } else {
190 *title = TrimAndRemoveComment(line.substr(1, line.size() - 2), true);
191 *argument = "";
192 }
193 for (int i = 0; i < kOptionSectionUnknown; ++i) {
194 if (title->find(opt_section_titles[i]) == 0) {
195 if (i == kOptionSectionVersion || i == kOptionSectionDBOptions ||
196 i == kOptionSectionCFOptions) {
197 if (title->size() == opt_section_titles[i].size()) {
198 // if true, then it indicats equal
199 *section = static_cast<OptionSection>(i);
200 return CheckSection(*section, *argument, line_num);
201 }
202 } else if (i == kOptionSectionTableOptions) {
203 // This type of sections has a sufffix at the end of the
204 // section title
205 if (title->size() > opt_section_titles[i].size()) {
206 *section = static_cast<OptionSection>(i);
207 return CheckSection(*section, *argument, line_num);
208 }
209 }
210 }
211 }
212 return Status::InvalidArgument(std::string("Unknown section ") + line);
213}
214
215Status RocksDBOptionsParser::InvalidArgument(const int line_num,
216 const std::string& message) {
217 return Status::InvalidArgument(
218 "[RocksDBOptionsParser Error] ",
219 message + " (at line " + ToString(line_num) + ")");
220}
221
222Status RocksDBOptionsParser::ParseStatement(std::string* name,
223 std::string* value,
224 const std::string& line,
225 const int line_num) {
226 size_t eq_pos = line.find("=");
227 if (eq_pos == std::string::npos) {
228 return InvalidArgument(line_num, "A valid statement must have a '='.");
229 }
230
231 *name = TrimAndRemoveComment(line.substr(0, eq_pos), true);
232 *value =
233 TrimAndRemoveComment(line.substr(eq_pos + 1, line.size() - eq_pos - 1));
234 if (name->empty()) {
235 return InvalidArgument(line_num,
236 "A valid statement must have a variable name.");
237 }
238 return Status::OK();
239}
240
f67539c2
TL
241Status RocksDBOptionsParser::Parse(const std::string& file_name, FileSystem* fs,
242 bool ignore_unknown_options,
243 size_t file_readahead_size) {
20effc67
TL
244 ConfigOptions
245 config_options; // Use default for escaped(true) and check (exact)
246 config_options.ignore_unknown_options = ignore_unknown_options;
247 if (file_readahead_size > 0) {
248 config_options.file_readahead_size = file_readahead_size;
249 }
250 return Parse(config_options, file_name, fs);
251}
252
253Status RocksDBOptionsParser::Parse(const ConfigOptions& config_options_in,
254 const std::string& file_name,
255 FileSystem* fs) {
7c673cae 256 Reset();
20effc67 257 ConfigOptions config_options = config_options_in;
7c673cae 258
f67539c2
TL
259 std::unique_ptr<FSSequentialFile> seq_file;
260 Status s = fs->NewSequentialFile(file_name, FileOptions(), &seq_file,
261 nullptr);
7c673cae
FG
262 if (!s.ok()) {
263 return s;
264 }
f67539c2 265 SequentialFileReader sf_reader(std::move(seq_file), file_name,
20effc67 266 config_options.file_readahead_size);
f67539c2 267
7c673cae
FG
268 OptionSection section = kOptionSectionUnknown;
269 std::string title;
270 std::string argument;
271 std::unordered_map<std::string, std::string> opt_map;
272 std::istringstream iss;
273 std::string line;
274 bool has_data = true;
275 // we only support single-lined statement.
f67539c2
TL
276 for (int line_num = 1; ReadOneLine(&iss, &sf_reader, &line, &has_data, &s);
277 ++line_num) {
7c673cae
FG
278 if (!s.ok()) {
279 return s;
280 }
281 line = TrimAndRemoveComment(line);
282 if (line.empty()) {
283 continue;
284 }
285 if (IsSection(line)) {
20effc67 286 s = EndSection(config_options, section, title, argument, opt_map);
7c673cae
FG
287 opt_map.clear();
288 if (!s.ok()) {
289 return s;
290 }
11fdf7f2
TL
291
292 // If the option file is not generated by a higher minor version,
293 // there shouldn't be any unknown option.
20effc67
TL
294 if (config_options.ignore_unknown_options &&
295 section == kOptionSectionVersion) {
11fdf7f2
TL
296 if (db_version[0] < ROCKSDB_MAJOR || (db_version[0] == ROCKSDB_MAJOR &&
297 db_version[1] <= ROCKSDB_MINOR)) {
20effc67 298 config_options.ignore_unknown_options = false;
11fdf7f2
TL
299 }
300 }
301
7c673cae
FG
302 s = ParseSection(&section, &title, &argument, line, line_num);
303 if (!s.ok()) {
304 return s;
305 }
306 } else {
307 std::string name;
308 std::string value;
309 s = ParseStatement(&name, &value, line, line_num);
310 if (!s.ok()) {
311 return s;
312 }
313 opt_map.insert({name, value});
314 }
315 }
316
20effc67 317 s = EndSection(config_options, section, title, argument, opt_map);
7c673cae
FG
318 opt_map.clear();
319 if (!s.ok()) {
320 return s;
321 }
322 return ValidityCheck();
323}
324
325Status RocksDBOptionsParser::CheckSection(const OptionSection section,
326 const std::string& section_arg,
327 const int line_num) {
328 if (section == kOptionSectionDBOptions) {
329 if (has_db_options_) {
330 return InvalidArgument(
331 line_num,
332 "More than one DBOption section found in the option config file");
333 }
334 has_db_options_ = true;
335 } else if (section == kOptionSectionCFOptions) {
336 bool is_default_cf = (section_arg == kDefaultColumnFamilyName);
337 if (cf_opts_.size() == 0 && !is_default_cf) {
338 return InvalidArgument(
339 line_num,
340 "Default column family must be the first CFOptions section "
341 "in the option config file");
342 } else if (cf_opts_.size() != 0 && is_default_cf) {
343 return InvalidArgument(
344 line_num,
345 "Default column family must be the first CFOptions section "
346 "in the optio/n config file");
347 } else if (GetCFOptions(section_arg) != nullptr) {
348 return InvalidArgument(
349 line_num,
350 "Two identical column families found in option config file");
351 }
352 has_default_cf_options_ |= is_default_cf;
353 } else if (section == kOptionSectionTableOptions) {
354 if (GetCFOptions(section_arg) == nullptr) {
355 return InvalidArgument(
356 line_num, std::string(
357 "Does not find a matched column family name in "
358 "TableOptions section. Column Family Name:") +
359 section_arg);
360 }
361 } else if (section == kOptionSectionVersion) {
362 if (has_version_section_) {
363 return InvalidArgument(
364 line_num,
365 "More than one Version section found in the option config file.");
366 }
367 has_version_section_ = true;
368 }
369 return Status::OK();
370}
371
372Status RocksDBOptionsParser::ParseVersionNumber(const std::string& ver_name,
373 const std::string& ver_string,
374 const int max_count,
375 int* version) {
376 int version_index = 0;
377 int current_number = 0;
378 int current_digit_count = 0;
379 bool has_dot = false;
380 for (int i = 0; i < max_count; ++i) {
381 version[i] = 0;
382 }
f67539c2 383 constexpr int kBufferSize = 200;
7c673cae
FG
384 char buffer[kBufferSize];
385 for (size_t i = 0; i < ver_string.size(); ++i) {
386 if (ver_string[i] == '.') {
387 if (version_index >= max_count - 1) {
388 snprintf(buffer, sizeof(buffer) - 1,
389 "A valid %s can only contains at most %d dots.",
390 ver_name.c_str(), max_count - 1);
391 return Status::InvalidArgument(buffer);
392 }
393 if (current_digit_count == 0) {
394 snprintf(buffer, sizeof(buffer) - 1,
395 "A valid %s must have at least one digit before each dot.",
396 ver_name.c_str());
397 return Status::InvalidArgument(buffer);
398 }
399 version[version_index++] = current_number;
400 current_number = 0;
401 current_digit_count = 0;
402 has_dot = true;
403 } else if (isdigit(ver_string[i])) {
404 current_number = current_number * 10 + (ver_string[i] - '0');
405 current_digit_count++;
406 } else {
407 snprintf(buffer, sizeof(buffer) - 1,
408 "A valid %s can only contains dots and numbers.",
409 ver_name.c_str());
410 return Status::InvalidArgument(buffer);
411 }
412 }
413 version[version_index] = current_number;
414 if (has_dot && current_digit_count == 0) {
415 snprintf(buffer, sizeof(buffer) - 1,
416 "A valid %s must have at least one digit after each dot.",
417 ver_name.c_str());
418 return Status::InvalidArgument(buffer);
419 }
420 return Status::OK();
421}
422
423Status RocksDBOptionsParser::EndSection(
20effc67
TL
424 const ConfigOptions& config_options, const OptionSection section,
425 const std::string& section_title, const std::string& section_arg,
426 const std::unordered_map<std::string, std::string>& opt_map) {
7c673cae
FG
427 Status s;
428 if (section == kOptionSectionDBOptions) {
20effc67 429 s = GetDBOptionsFromMap(config_options, DBOptions(), opt_map, &db_opt_);
7c673cae
FG
430 if (!s.ok()) {
431 return s;
432 }
433 db_opt_map_ = opt_map;
434 } else if (section == kOptionSectionCFOptions) {
435 // This condition should be ensured earlier in ParseSection
436 // so we make an assertion here.
437 assert(GetCFOptions(section_arg) == nullptr);
438 cf_names_.emplace_back(section_arg);
439 cf_opts_.emplace_back();
20effc67
TL
440 s = GetColumnFamilyOptionsFromMap(config_options, ColumnFamilyOptions(),
441 opt_map, &cf_opts_.back());
7c673cae
FG
442 if (!s.ok()) {
443 return s;
444 }
445 // keep the parsed string.
446 cf_opt_maps_.emplace_back(opt_map);
447 } else if (section == kOptionSectionTableOptions) {
448 assert(GetCFOptions(section_arg) != nullptr);
449 auto* cf_opt = GetCFOptionsImpl(section_arg);
450 if (cf_opt == nullptr) {
451 return Status::InvalidArgument(
452 "The specified column family must be defined before the "
453 "TableOptions section:",
454 section_arg);
455 }
456 // Ignore error as table factory deserialization is optional
20effc67
TL
457 s = TableFactory::CreateFromString(
458 config_options,
7c673cae
FG
459 section_title.substr(
460 opt_section_titles[kOptionSectionTableOptions].size()),
20effc67
TL
461 &(cf_opt->table_factory));
462 if (s.ok()) {
463 s = cf_opt->table_factory->ConfigureFromMap(config_options, opt_map);
464 // Translate any errors (NotFound, NotSupported, to InvalidArgument
465 if (s.ok() || s.IsInvalidArgument()) {
466 return s;
467 } else {
468 return Status::InvalidArgument(s.getState());
469 }
470 } else {
471 // Return OK for not supported table factories as TableFactory
472 // Deserialization is optional.
473 cf_opt->table_factory.reset();
474 return Status::OK();
7c673cae
FG
475 }
476 } else if (section == kOptionSectionVersion) {
20effc67 477 for (const auto& pair : opt_map) {
7c673cae
FG
478 if (pair.first == "rocksdb_version") {
479 s = ParseVersionNumber(pair.first, pair.second, 3, db_version);
480 if (!s.ok()) {
481 return s;
482 }
483 } else if (pair.first == "options_file_version") {
484 s = ParseVersionNumber(pair.first, pair.second, 2, opt_file_version);
485 if (!s.ok()) {
486 return s;
487 }
488 if (opt_file_version[0] < 1) {
489 return Status::InvalidArgument(
490 "A valid options_file_version must be at least 1.");
491 }
492 }
493 }
494 }
20effc67 495 return s;
7c673cae
FG
496}
497
498Status RocksDBOptionsParser::ValidityCheck() {
499 if (!has_db_options_) {
500 return Status::Corruption(
501 "A RocksDB Option file must have a single DBOptions section");
502 }
503 if (!has_default_cf_options_) {
504 return Status::Corruption(
505 "A RocksDB Option file must have a single CFOptions:default section");
506 }
507
508 return Status::OK();
509}
510
511std::string RocksDBOptionsParser::TrimAndRemoveComment(const std::string& line,
512 bool trim_only) {
513 size_t start = 0;
514 size_t end = line.size();
515
516 // we only support "#" style comment
517 if (!trim_only) {
518 size_t search_pos = 0;
519 while (search_pos < line.size()) {
520 size_t comment_pos = line.find('#', search_pos);
521 if (comment_pos == std::string::npos) {
522 break;
523 }
524 if (comment_pos == 0 || line[comment_pos - 1] != '\\') {
525 end = comment_pos;
526 break;
527 }
528 search_pos = comment_pos + 1;
529 }
530 }
531
532 while (start < end && isspace(line[start]) != 0) {
533 ++start;
534 }
535
536 // start < end implies end > 0.
537 while (start < end && isspace(line[end - 1]) != 0) {
538 --end;
539 }
540
541 if (start < end) {
542 return line.substr(start, end - start);
543 }
544
545 return "";
546}
547
7c673cae 548Status RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
20effc67
TL
549 const ConfigOptions& config_options_in, const DBOptions& db_opt,
550 const std::vector<std::string>& cf_names,
7c673cae 551 const std::vector<ColumnFamilyOptions>& cf_opts,
20effc67 552 const std::string& file_name, FileSystem* fs) {
7c673cae 553 RocksDBOptionsParser parser;
20effc67
TL
554 ConfigOptions config_options = config_options_in;
555 config_options.invoke_prepare_options =
556 false; // No need to do a prepare for verify
557 Status s = parser.Parse(config_options, file_name, fs);
7c673cae
FG
558 if (!s.ok()) {
559 return s;
560 }
561
562 // Verify DBOptions
20effc67
TL
563 s = VerifyDBOptions(config_options, db_opt, *parser.db_opt(),
564 parser.db_opt_map());
7c673cae
FG
565 if (!s.ok()) {
566 return s;
567 }
568
569 // Verify ColumnFamily Name
570 if (cf_names.size() != parser.cf_names()->size()) {
20effc67
TL
571 if (config_options.sanity_level >=
572 ConfigOptions::kSanityLevelLooselyCompatible) {
7c673cae
FG
573 return Status::InvalidArgument(
574 "[RocksDBOptionParser Error] The persisted options does not have "
575 "the same number of column family names as the db instance.");
576 } else if (cf_opts.size() > parser.cf_opts()->size()) {
577 return Status::InvalidArgument(
578 "[RocksDBOptionsParser Error]",
579 "The persisted options file has less number of column family "
580 "names than that of the specified one.");
581 }
582 }
583 for (size_t i = 0; i < cf_names.size(); ++i) {
584 if (cf_names[i] != parser.cf_names()->at(i)) {
585 return Status::InvalidArgument(
586 "[RocksDBOptionParser Error] The persisted options and the db"
587 "instance does not have the same name for column family ",
588 ToString(i));
589 }
590 }
591
592 // Verify Column Family Options
593 if (cf_opts.size() != parser.cf_opts()->size()) {
20effc67
TL
594 if (config_options.sanity_level >=
595 ConfigOptions::kSanityLevelLooselyCompatible) {
7c673cae
FG
596 return Status::InvalidArgument(
597 "[RocksDBOptionsParser Error]",
598 "The persisted options does not have the same number of "
599 "column families as the db instance.");
600 } else if (cf_opts.size() > parser.cf_opts()->size()) {
601 return Status::InvalidArgument(
602 "[RocksDBOptionsParser Error]",
603 "The persisted options file has less number of column families "
604 "than that of the specified number.");
605 }
606 }
607 for (size_t i = 0; i < cf_opts.size(); ++i) {
20effc67
TL
608 s = VerifyCFOptions(config_options, cf_opts[i], parser.cf_opts()->at(i),
609 &(parser.cf_opt_maps()->at(i)));
7c673cae
FG
610 if (!s.ok()) {
611 return s;
612 }
20effc67
TL
613 s = VerifyTableFactory(config_options, cf_opts[i].table_factory.get(),
614 parser.cf_opts()->at(i).table_factory.get());
7c673cae
FG
615 if (!s.ok()) {
616 return s;
617 }
618 }
619
620 return Status::OK();
621}
622
623Status RocksDBOptionsParser::VerifyDBOptions(
20effc67
TL
624 const ConfigOptions& config_options, const DBOptions& base_opt,
625 const DBOptions& file_opt,
626 const std::unordered_map<std::string, std::string>* /*opt_map*/) {
627 auto base_config = DBOptionsAsConfigurable(base_opt);
628 auto file_config = DBOptionsAsConfigurable(file_opt);
629 std::string mismatch;
630 if (!base_config->AreEquivalent(config_options, file_config.get(),
631 &mismatch)) {
632 const size_t kBufferSize = 2048;
633 char buffer[kBufferSize];
634 std::string base_value;
635 std::string file_value;
636 int offset = snprintf(buffer, sizeof(buffer),
637 "[RocksDBOptionsParser]: "
638 "failed the verification on DBOptions::%s -- ",
639 mismatch.c_str());
640 Status s = base_config->GetOption(config_options, mismatch, &base_value);
641 if (s.ok()) {
642 s = file_config->GetOption(config_options, mismatch, &file_value);
7c673cae 643 }
20effc67
TL
644 assert(offset >= 0);
645 assert(static_cast<size_t>(offset) < sizeof(buffer));
646 if (s.ok()) {
647 snprintf(buffer + offset, sizeof(buffer) - static_cast<size_t>(offset),
648 "-- The specified one is %s while the persisted one is %s.\n",
649 base_value.c_str(), file_value.c_str());
650 } else {
651 snprintf(buffer + offset, sizeof(buffer) - static_cast<size_t>(offset),
652 "-- Unable to re-serialize an option: %s.\n",
653 s.ToString().c_str());
7c673cae 654 }
20effc67 655 return Status::InvalidArgument(Slice(buffer, strlen(buffer)));
7c673cae
FG
656 }
657 return Status::OK();
658}
659
660Status RocksDBOptionsParser::VerifyCFOptions(
20effc67
TL
661 const ConfigOptions& config_options, const ColumnFamilyOptions& base_opt,
662 const ColumnFamilyOptions& file_opt,
663 const std::unordered_map<std::string, std::string>* opt_map) {
664 auto base_config = CFOptionsAsConfigurable(base_opt, opt_map);
665 auto file_config = CFOptionsAsConfigurable(file_opt, opt_map);
666 std::string mismatch;
667 if (!base_config->AreEquivalent(config_options, file_config.get(),
668 &mismatch)) {
669 std::string base_value;
670 std::string file_value;
671 // The options do not match
672 const size_t kBufferSize = 2048;
673 char buffer[kBufferSize];
674 Status s = base_config->GetOption(config_options, mismatch, &base_value);
675 if (s.ok()) {
676 s = file_config->GetOption(config_options, mismatch, &file_value);
7c673cae 677 }
20effc67
TL
678 int offset = snprintf(buffer, sizeof(buffer),
679 "[RocksDBOptionsParser]: "
680 "failed the verification on ColumnFamilyOptions::%s",
681 mismatch.c_str());
682 assert(offset >= 0);
683 assert(static_cast<size_t>(offset) < sizeof(buffer));
684 if (s.ok()) {
685 snprintf(buffer + offset, sizeof(buffer) - static_cast<size_t>(offset),
686 "--- The specified one is %s while the persisted one is %s.\n",
687 base_value.c_str(), file_value.c_str());
688 } else {
689 snprintf(buffer + offset, sizeof(buffer) - static_cast<size_t>(offset),
690 "--- Unable to re-serialize an option: %s.\n",
691 s.ToString().c_str());
7c673cae 692 }
20effc67
TL
693 return Status::InvalidArgument(Slice(buffer, sizeof(buffer)));
694 } // For each option
7c673cae
FG
695 return Status::OK();
696}
697
7c673cae 698Status RocksDBOptionsParser::VerifyTableFactory(
20effc67
TL
699 const ConfigOptions& config_options, const TableFactory* base_tf,
700 const TableFactory* file_tf) {
701 std::string mismatch;
7c673cae 702 if (base_tf && file_tf) {
20effc67 703 if (config_options.sanity_level > ConfigOptions::kSanityLevelNone &&
11fdf7f2 704 std::string(base_tf->Name()) != std::string(file_tf->Name())) {
7c673cae
FG
705 return Status::Corruption(
706 "[RocksDBOptionsParser]: "
707 "failed the verification on TableFactory->Name()");
20effc67
TL
708 } else if (!base_tf->AreEquivalent(config_options, file_tf, &mismatch)) {
709 return Status::Corruption(std::string("[RocksDBOptionsParser]:"
710 "failed the verification on ") +
711 base_tf->Name() + "::",
712 mismatch);
7c673cae 713 }
7c673cae
FG
714 } else {
715 // TODO(yhchiang): further support sanity check here
716 }
717 return Status::OK();
718}
f67539c2 719} // namespace ROCKSDB_NAMESPACE
7c673cae
FG
720
721#endif // !ROCKSDB_LITE