1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
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).
8 #include "options/options_parser.h"
16 #include "options/options_helper.h"
17 #include "rocksdb/convenience.h"
18 #include "rocksdb/db.h"
19 #include "util/cast_util.h"
20 #include "util/file_reader_writer.h"
21 #include "util/string_util.h"
22 #include "util/sync_point.h"
24 #include "port/port.h"
28 static const std::string option_file_header
=
29 "# This is a RocksDB option file.\n"
31 "# For detailed file format spec, please refer to the example file\n"
32 "# in examples/rocksdb_option_file_example.ini\n"
36 Status
PersistRocksDBOptions(const DBOptions
& db_opt
,
37 const std::vector
<std::string
>& cf_names
,
38 const std::vector
<ColumnFamilyOptions
>& cf_opts
,
39 const std::string
& file_name
, Env
* env
) {
40 TEST_SYNC_POINT("PersistRocksDBOptions:start");
41 if (cf_names
.size() != cf_opts
.size()) {
42 return Status::InvalidArgument(
43 "cf_names.size() and cf_opts.size() must be the same");
45 std::unique_ptr
<WritableFile
> wf
;
47 Status s
= env
->NewWritableFile(file_name
, &wf
, EnvOptions());
51 std::unique_ptr
<WritableFileWriter
> writable
;
52 writable
.reset(new WritableFileWriter(std::move(wf
), file_name
, EnvOptions(),
53 nullptr /* statistics */));
55 std::string options_file_content
;
57 writable
->Append(option_file_header
+ "[" +
58 opt_section_titles
[kOptionSectionVersion
] +
61 ToString(ROCKSDB_MAJOR
) + "." + ToString(ROCKSDB_MINOR
) +
62 "." + ToString(ROCKSDB_PATCH
) + "\n");
63 writable
->Append(" options_file_version=" +
64 ToString(ROCKSDB_OPTION_FILE_MAJOR
) + "." +
65 ToString(ROCKSDB_OPTION_FILE_MINOR
) + "\n");
66 writable
->Append("\n[" + opt_section_titles
[kOptionSectionDBOptions
] +
69 s
= GetStringFromDBOptions(&options_file_content
, db_opt
, "\n ");
74 writable
->Append(options_file_content
+ "\n");
76 for (size_t i
= 0; i
< cf_opts
.size(); ++i
) {
78 writable
->Append("\n[" + opt_section_titles
[kOptionSectionCFOptions
] +
79 " \"" + EscapeOptionString(cf_names
[i
]) + "\"]\n ");
80 s
= GetStringFromColumnFamilyOptions(&options_file_content
, cf_opts
[i
],
86 writable
->Append(options_file_content
+ "\n");
87 // TableOptions section
88 auto* tf
= cf_opts
[i
].table_factory
.get();
90 writable
->Append("[" + opt_section_titles
[kOptionSectionTableOptions
] +
91 tf
->Name() + " \"" + EscapeOptionString(cf_names
[i
]) +
93 options_file_content
.clear();
94 s
= tf
->GetOptionString(&options_file_content
, "\n ");
98 writable
->Append(options_file_content
+ "\n");
101 writable
->Sync(true /* use_fsync */);
104 return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
105 db_opt
, cf_names
, cf_opts
, file_name
, env
);
108 RocksDBOptionsParser::RocksDBOptionsParser() { Reset(); }
110 void RocksDBOptionsParser::Reset() {
111 db_opt_
= DBOptions();
115 cf_opt_maps_
.clear();
116 has_version_section_
= false;
117 has_db_options_
= false;
118 has_default_cf_options_
= false;
119 for (int i
= 0; i
< 3; ++i
) {
121 opt_file_version
[i
] = 0;
125 bool RocksDBOptionsParser::IsSection(const std::string
& line
) {
126 if (line
.size() < 2) {
129 if (line
[0] != '[' || line
[line
.size() - 1] != ']') {
135 Status
RocksDBOptionsParser::ParseSection(OptionSection
* section
,
137 std::string
* argument
,
138 const std::string
& line
,
139 const int line_num
) {
140 *section
= kOptionSectionUnknown
;
141 // A section is of the form [<SectionName> "<SectionArg>"], where
142 // "<SectionArg>" is optional.
143 size_t arg_start_pos
= line
.find("\"");
144 size_t arg_end_pos
= line
.rfind("\"");
145 // The following if-then check tries to identify whether the input
146 // section has the optional section argument.
147 if (arg_start_pos
!= std::string::npos
&& arg_start_pos
!= arg_end_pos
) {
148 *title
= TrimAndRemoveComment(line
.substr(1, arg_start_pos
- 1), true);
149 *argument
= UnescapeOptionString(
150 line
.substr(arg_start_pos
+ 1, arg_end_pos
- arg_start_pos
- 1));
152 *title
= TrimAndRemoveComment(line
.substr(1, line
.size() - 2), true);
155 for (int i
= 0; i
< kOptionSectionUnknown
; ++i
) {
156 if (title
->find(opt_section_titles
[i
]) == 0) {
157 if (i
== kOptionSectionVersion
|| i
== kOptionSectionDBOptions
||
158 i
== kOptionSectionCFOptions
) {
159 if (title
->size() == opt_section_titles
[i
].size()) {
160 // if true, then it indicats equal
161 *section
= static_cast<OptionSection
>(i
);
162 return CheckSection(*section
, *argument
, line_num
);
164 } else if (i
== kOptionSectionTableOptions
) {
165 // This type of sections has a sufffix at the end of the
167 if (title
->size() > opt_section_titles
[i
].size()) {
168 *section
= static_cast<OptionSection
>(i
);
169 return CheckSection(*section
, *argument
, line_num
);
174 return Status::InvalidArgument(std::string("Unknown section ") + line
);
177 Status
RocksDBOptionsParser::InvalidArgument(const int line_num
,
178 const std::string
& message
) {
179 return Status::InvalidArgument(
180 "[RocksDBOptionsParser Error] ",
181 message
+ " (at line " + ToString(line_num
) + ")");
184 Status
RocksDBOptionsParser::ParseStatement(std::string
* name
,
186 const std::string
& line
,
187 const int line_num
) {
188 size_t eq_pos
= line
.find("=");
189 if (eq_pos
== std::string::npos
) {
190 return InvalidArgument(line_num
, "A valid statement must have a '='.");
193 *name
= TrimAndRemoveComment(line
.substr(0, eq_pos
), true);
195 TrimAndRemoveComment(line
.substr(eq_pos
+ 1, line
.size() - eq_pos
- 1));
197 return InvalidArgument(line_num
,
198 "A valid statement must have a variable name.");
203 Status
RocksDBOptionsParser::Parse(const std::string
& file_name
, Env
* env
,
204 bool ignore_unknown_options
) {
207 std::unique_ptr
<SequentialFile
> seq_file
;
208 Status s
= env
->NewSequentialFile(file_name
, &seq_file
, EnvOptions());
213 OptionSection section
= kOptionSectionUnknown
;
215 std::string argument
;
216 std::unordered_map
<std::string
, std::string
> opt_map
;
217 std::istringstream iss
;
219 bool has_data
= true;
220 // we only support single-lined statement.
221 for (int line_num
= 1;
222 ReadOneLine(&iss
, seq_file
.get(), &line
, &has_data
, &s
); ++line_num
) {
226 line
= TrimAndRemoveComment(line
);
230 if (IsSection(line
)) {
231 s
= EndSection(section
, title
, argument
, opt_map
, ignore_unknown_options
);
237 // If the option file is not generated by a higher minor version,
238 // there shouldn't be any unknown option.
239 if (ignore_unknown_options
&& section
== kOptionSectionVersion
) {
240 if (db_version
[0] < ROCKSDB_MAJOR
|| (db_version
[0] == ROCKSDB_MAJOR
&&
241 db_version
[1] <= ROCKSDB_MINOR
)) {
242 ignore_unknown_options
= false;
246 s
= ParseSection(§ion
, &title
, &argument
, line
, line_num
);
253 s
= ParseStatement(&name
, &value
, line
, line_num
);
257 opt_map
.insert({name
, value
});
261 s
= EndSection(section
, title
, argument
, opt_map
, ignore_unknown_options
);
266 return ValidityCheck();
269 Status
RocksDBOptionsParser::CheckSection(const OptionSection section
,
270 const std::string
& section_arg
,
271 const int line_num
) {
272 if (section
== kOptionSectionDBOptions
) {
273 if (has_db_options_
) {
274 return InvalidArgument(
276 "More than one DBOption section found in the option config file");
278 has_db_options_
= true;
279 } else if (section
== kOptionSectionCFOptions
) {
280 bool is_default_cf
= (section_arg
== kDefaultColumnFamilyName
);
281 if (cf_opts_
.size() == 0 && !is_default_cf
) {
282 return InvalidArgument(
284 "Default column family must be the first CFOptions section "
285 "in the option config file");
286 } else if (cf_opts_
.size() != 0 && is_default_cf
) {
287 return InvalidArgument(
289 "Default column family must be the first CFOptions section "
290 "in the optio/n config file");
291 } else if (GetCFOptions(section_arg
) != nullptr) {
292 return InvalidArgument(
294 "Two identical column families found in option config file");
296 has_default_cf_options_
|= is_default_cf
;
297 } else if (section
== kOptionSectionTableOptions
) {
298 if (GetCFOptions(section_arg
) == nullptr) {
299 return InvalidArgument(
300 line_num
, std::string(
301 "Does not find a matched column family name in "
302 "TableOptions section. Column Family Name:") +
305 } else if (section
== kOptionSectionVersion
) {
306 if (has_version_section_
) {
307 return InvalidArgument(
309 "More than one Version section found in the option config file.");
311 has_version_section_
= true;
316 Status
RocksDBOptionsParser::ParseVersionNumber(const std::string
& ver_name
,
317 const std::string
& ver_string
,
320 int version_index
= 0;
321 int current_number
= 0;
322 int current_digit_count
= 0;
323 bool has_dot
= false;
324 for (int i
= 0; i
< max_count
; ++i
) {
327 const int kBufferSize
= 200;
328 char buffer
[kBufferSize
];
329 for (size_t i
= 0; i
< ver_string
.size(); ++i
) {
330 if (ver_string
[i
] == '.') {
331 if (version_index
>= max_count
- 1) {
332 snprintf(buffer
, sizeof(buffer
) - 1,
333 "A valid %s can only contains at most %d dots.",
334 ver_name
.c_str(), max_count
- 1);
335 return Status::InvalidArgument(buffer
);
337 if (current_digit_count
== 0) {
338 snprintf(buffer
, sizeof(buffer
) - 1,
339 "A valid %s must have at least one digit before each dot.",
341 return Status::InvalidArgument(buffer
);
343 version
[version_index
++] = current_number
;
345 current_digit_count
= 0;
347 } else if (isdigit(ver_string
[i
])) {
348 current_number
= current_number
* 10 + (ver_string
[i
] - '0');
349 current_digit_count
++;
351 snprintf(buffer
, sizeof(buffer
) - 1,
352 "A valid %s can only contains dots and numbers.",
354 return Status::InvalidArgument(buffer
);
357 version
[version_index
] = current_number
;
358 if (has_dot
&& current_digit_count
== 0) {
359 snprintf(buffer
, sizeof(buffer
) - 1,
360 "A valid %s must have at least one digit after each dot.",
362 return Status::InvalidArgument(buffer
);
367 Status
RocksDBOptionsParser::EndSection(
368 const OptionSection section
, const std::string
& section_title
,
369 const std::string
& section_arg
,
370 const std::unordered_map
<std::string
, std::string
>& opt_map
,
371 bool ignore_unknown_options
) {
373 if (section
== kOptionSectionDBOptions
) {
374 s
= GetDBOptionsFromMap(DBOptions(), opt_map
, &db_opt_
, true,
375 ignore_unknown_options
);
379 db_opt_map_
= opt_map
;
380 } else if (section
== kOptionSectionCFOptions
) {
381 // This condition should be ensured earlier in ParseSection
382 // so we make an assertion here.
383 assert(GetCFOptions(section_arg
) == nullptr);
384 cf_names_
.emplace_back(section_arg
);
385 cf_opts_
.emplace_back();
386 s
= GetColumnFamilyOptionsFromMap(ColumnFamilyOptions(), opt_map
,
387 &cf_opts_
.back(), true,
388 ignore_unknown_options
);
392 // keep the parsed string.
393 cf_opt_maps_
.emplace_back(opt_map
);
394 } else if (section
== kOptionSectionTableOptions
) {
395 assert(GetCFOptions(section_arg
) != nullptr);
396 auto* cf_opt
= GetCFOptionsImpl(section_arg
);
397 if (cf_opt
== nullptr) {
398 return Status::InvalidArgument(
399 "The specified column family must be defined before the "
400 "TableOptions section:",
403 // Ignore error as table factory deserialization is optional
404 s
= GetTableFactoryFromMap(
405 section_title
.substr(
406 opt_section_titles
[kOptionSectionTableOptions
].size()),
407 opt_map
, &(cf_opt
->table_factory
), ignore_unknown_options
);
411 } else if (section
== kOptionSectionVersion
) {
412 for (const auto pair
: opt_map
) {
413 if (pair
.first
== "rocksdb_version") {
414 s
= ParseVersionNumber(pair
.first
, pair
.second
, 3, db_version
);
418 } else if (pair
.first
== "options_file_version") {
419 s
= ParseVersionNumber(pair
.first
, pair
.second
, 2, opt_file_version
);
423 if (opt_file_version
[0] < 1) {
424 return Status::InvalidArgument(
425 "A valid options_file_version must be at least 1.");
433 Status
RocksDBOptionsParser::ValidityCheck() {
434 if (!has_db_options_
) {
435 return Status::Corruption(
436 "A RocksDB Option file must have a single DBOptions section");
438 if (!has_default_cf_options_
) {
439 return Status::Corruption(
440 "A RocksDB Option file must have a single CFOptions:default section");
446 std::string
RocksDBOptionsParser::TrimAndRemoveComment(const std::string
& line
,
449 size_t end
= line
.size();
451 // we only support "#" style comment
453 size_t search_pos
= 0;
454 while (search_pos
< line
.size()) {
455 size_t comment_pos
= line
.find('#', search_pos
);
456 if (comment_pos
== std::string::npos
) {
459 if (comment_pos
== 0 || line
[comment_pos
- 1] != '\\') {
463 search_pos
= comment_pos
+ 1;
467 while (start
< end
&& isspace(line
[start
]) != 0) {
471 // start < end implies end > 0.
472 while (start
< end
&& isspace(line
[end
- 1]) != 0) {
477 return line
.substr(start
, end
- start
);
484 bool AreEqualDoubles(const double a
, const double b
) {
485 return (fabs(a
- b
) < 0.00001);
489 bool AreEqualOptions(
490 const char* opt1
, const char* opt2
, const OptionTypeInfo
& type_info
,
491 const std::string
& opt_name
,
492 const std::unordered_map
<std::string
, std::string
>* opt_map
) {
493 const char* offset1
= opt1
+ type_info
.offset
;
494 const char* offset2
= opt2
+ type_info
.offset
;
496 switch (type_info
.type
) {
497 case OptionType::kBoolean
:
498 return (*reinterpret_cast<const bool*>(offset1
) ==
499 *reinterpret_cast<const bool*>(offset2
));
500 case OptionType::kInt
:
501 return (*reinterpret_cast<const int*>(offset1
) ==
502 *reinterpret_cast<const int*>(offset2
));
503 case OptionType::kInt32T
:
504 return (*reinterpret_cast<const int32_t*>(offset1
) ==
505 *reinterpret_cast<const int32_t*>(offset2
));
506 case OptionType::kInt64T
:
509 GetUnaligned(reinterpret_cast<const int64_t*>(offset1
), &v1
);
510 GetUnaligned(reinterpret_cast<const int64_t*>(offset2
), &v2
);
513 case OptionType::kVectorInt
:
514 return (*reinterpret_cast<const std::vector
<int>*>(offset1
) ==
515 *reinterpret_cast<const std::vector
<int>*>(offset2
));
516 case OptionType::kUInt
:
517 return (*reinterpret_cast<const unsigned int*>(offset1
) ==
518 *reinterpret_cast<const unsigned int*>(offset2
));
519 case OptionType::kUInt32T
:
520 return (*reinterpret_cast<const uint32_t*>(offset1
) ==
521 *reinterpret_cast<const uint32_t*>(offset2
));
522 case OptionType::kUInt64T
:
525 GetUnaligned(reinterpret_cast<const uint64_t*>(offset1
), &v1
);
526 GetUnaligned(reinterpret_cast<const uint64_t*>(offset2
), &v2
);
529 case OptionType::kSizeT
:
532 GetUnaligned(reinterpret_cast<const size_t*>(offset1
), &v1
);
533 GetUnaligned(reinterpret_cast<const size_t*>(offset2
), &v2
);
536 case OptionType::kString
:
537 return (*reinterpret_cast<const std::string
*>(offset1
) ==
538 *reinterpret_cast<const std::string
*>(offset2
));
539 case OptionType::kDouble
:
540 return AreEqualDoubles(*reinterpret_cast<const double*>(offset1
),
541 *reinterpret_cast<const double*>(offset2
));
542 case OptionType::kCompactionStyle
:
543 return (*reinterpret_cast<const CompactionStyle
*>(offset1
) ==
544 *reinterpret_cast<const CompactionStyle
*>(offset2
));
545 case OptionType::kCompactionPri
:
546 return (*reinterpret_cast<const CompactionPri
*>(offset1
) ==
547 *reinterpret_cast<const CompactionPri
*>(offset2
));
548 case OptionType::kCompressionType
:
549 return (*reinterpret_cast<const CompressionType
*>(offset1
) ==
550 *reinterpret_cast<const CompressionType
*>(offset2
));
551 case OptionType::kVectorCompressionType
: {
553 reinterpret_cast<const std::vector
<CompressionType
>*>(offset1
);
555 reinterpret_cast<const std::vector
<CompressionType
>*>(offset2
);
556 return (*vec1
== *vec2
);
558 case OptionType::kChecksumType
:
559 return (*reinterpret_cast<const ChecksumType
*>(offset1
) ==
560 *reinterpret_cast<const ChecksumType
*>(offset2
));
561 case OptionType::kBlockBasedTableIndexType
:
563 *reinterpret_cast<const BlockBasedTableOptions::IndexType
*>(
565 *reinterpret_cast<const BlockBasedTableOptions::IndexType
*>(offset2
));
566 case OptionType::kBlockBasedTableDataBlockIndexType
:
568 *reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType
*>(
570 *reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType
*>(
572 case OptionType::kWALRecoveryMode
:
573 return (*reinterpret_cast<const WALRecoveryMode
*>(offset1
) ==
574 *reinterpret_cast<const WALRecoveryMode
*>(offset2
));
575 case OptionType::kAccessHint
:
576 return (*reinterpret_cast<const DBOptions::AccessHint
*>(offset1
) ==
577 *reinterpret_cast<const DBOptions::AccessHint
*>(offset2
));
578 case OptionType::kInfoLogLevel
:
579 return (*reinterpret_cast<const InfoLogLevel
*>(offset1
) ==
580 *reinterpret_cast<const InfoLogLevel
*>(offset2
));
581 case OptionType::kCompactionOptionsFIFO
: {
582 CompactionOptionsFIFO lhs
=
583 *reinterpret_cast<const CompactionOptionsFIFO
*>(offset1
);
584 CompactionOptionsFIFO rhs
=
585 *reinterpret_cast<const CompactionOptionsFIFO
*>(offset2
);
586 if (lhs
.max_table_files_size
== rhs
.max_table_files_size
&&
587 lhs
.allow_compaction
== rhs
.allow_compaction
) {
592 case OptionType::kCompactionOptionsUniversal
: {
593 CompactionOptionsUniversal lhs
=
594 *reinterpret_cast<const CompactionOptionsUniversal
*>(offset1
);
595 CompactionOptionsUniversal rhs
=
596 *reinterpret_cast<const CompactionOptionsUniversal
*>(offset2
);
597 if (lhs
.size_ratio
== rhs
.size_ratio
&&
598 lhs
.min_merge_width
== rhs
.min_merge_width
&&
599 lhs
.max_merge_width
== rhs
.max_merge_width
&&
600 lhs
.max_size_amplification_percent
==
601 rhs
.max_size_amplification_percent
&&
602 lhs
.compression_size_percent
== rhs
.compression_size_percent
&&
603 lhs
.stop_style
== rhs
.stop_style
&&
604 lhs
.allow_trivial_move
== rhs
.allow_trivial_move
) {
610 if (type_info
.verification
== OptionVerificationType::kByName
||
611 type_info
.verification
==
612 OptionVerificationType::kByNameAllowFromNull
||
613 type_info
.verification
== OptionVerificationType::kByNameAllowNull
) {
616 SerializeSingleOptionHelper(offset1
, type_info
.type
, &value1
);
617 if (result
== false) {
620 if (opt_map
== nullptr) {
623 auto iter
= opt_map
->find(opt_name
);
624 if (iter
== opt_map
->end()) {
627 if (type_info
.verification
==
628 OptionVerificationType::kByNameAllowNull
) {
629 if (iter
->second
== kNullptrString
|| value1
== kNullptrString
) {
632 } else if (type_info
.verification
==
633 OptionVerificationType::kByNameAllowFromNull
) {
634 if (iter
->second
== kNullptrString
) {
638 return (value1
== iter
->second
);
645 Status
RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
646 const DBOptions
& db_opt
, const std::vector
<std::string
>& cf_names
,
647 const std::vector
<ColumnFamilyOptions
>& cf_opts
,
648 const std::string
& file_name
, Env
* env
,
649 OptionsSanityCheckLevel sanity_check_level
, bool ignore_unknown_options
) {
650 RocksDBOptionsParser parser
;
651 std::unique_ptr
<SequentialFile
> seq_file
;
652 Status s
= parser
.Parse(file_name
, env
, ignore_unknown_options
);
658 s
= VerifyDBOptions(db_opt
, *parser
.db_opt(), parser
.db_opt_map(),
664 // Verify ColumnFamily Name
665 if (cf_names
.size() != parser
.cf_names()->size()) {
666 if (sanity_check_level
>= kSanityLevelLooselyCompatible
) {
667 return Status::InvalidArgument(
668 "[RocksDBOptionParser Error] The persisted options does not have "
669 "the same number of column family names as the db instance.");
670 } else if (cf_opts
.size() > parser
.cf_opts()->size()) {
671 return Status::InvalidArgument(
672 "[RocksDBOptionsParser Error]",
673 "The persisted options file has less number of column family "
674 "names than that of the specified one.");
677 for (size_t i
= 0; i
< cf_names
.size(); ++i
) {
678 if (cf_names
[i
] != parser
.cf_names()->at(i
)) {
679 return Status::InvalidArgument(
680 "[RocksDBOptionParser Error] The persisted options and the db"
681 "instance does not have the same name for column family ",
686 // Verify Column Family Options
687 if (cf_opts
.size() != parser
.cf_opts()->size()) {
688 if (sanity_check_level
>= kSanityLevelLooselyCompatible
) {
689 return Status::InvalidArgument(
690 "[RocksDBOptionsParser Error]",
691 "The persisted options does not have the same number of "
692 "column families as the db instance.");
693 } else if (cf_opts
.size() > parser
.cf_opts()->size()) {
694 return Status::InvalidArgument(
695 "[RocksDBOptionsParser Error]",
696 "The persisted options file has less number of column families "
697 "than that of the specified number.");
700 for (size_t i
= 0; i
< cf_opts
.size(); ++i
) {
701 s
= VerifyCFOptions(cf_opts
[i
], parser
.cf_opts()->at(i
),
702 &(parser
.cf_opt_maps()->at(i
)), sanity_check_level
);
706 s
= VerifyTableFactory(cf_opts
[i
].table_factory
.get(),
707 parser
.cf_opts()->at(i
).table_factory
.get(),
717 Status
RocksDBOptionsParser::VerifyDBOptions(
718 const DBOptions
& base_opt
, const DBOptions
& persisted_opt
,
719 const std::unordered_map
<std::string
, std::string
>* /*opt_map*/,
720 OptionsSanityCheckLevel sanity_check_level
) {
721 for (auto pair
: db_options_type_info
) {
722 if (pair
.second
.verification
== OptionVerificationType::kDeprecated
) {
723 // We skip checking deprecated variables as they might
724 // contain random values since they might not be initialized
727 if (DBOptionSanityCheckLevel(pair
.first
) <= sanity_check_level
) {
728 if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt
),
729 reinterpret_cast<const char*>(&persisted_opt
),
730 pair
.second
, pair
.first
, nullptr)) {
731 const size_t kBufferSize
= 2048;
732 char buffer
[kBufferSize
];
733 std::string base_value
;
734 std::string persisted_value
;
735 SerializeSingleOptionHelper(
736 reinterpret_cast<const char*>(&base_opt
) + pair
.second
.offset
,
737 pair
.second
.type
, &base_value
);
738 SerializeSingleOptionHelper(
739 reinterpret_cast<const char*>(&persisted_opt
) + pair
.second
.offset
,
740 pair
.second
.type
, &persisted_value
);
741 snprintf(buffer
, sizeof(buffer
),
742 "[RocksDBOptionsParser]: "
743 "failed the verification on DBOptions::%s --- "
744 "The specified one is %s while the persisted one is %s.\n",
745 pair
.first
.c_str(), base_value
.c_str(),
746 persisted_value
.c_str());
747 return Status::InvalidArgument(Slice(buffer
, strlen(buffer
)));
754 Status
RocksDBOptionsParser::VerifyCFOptions(
755 const ColumnFamilyOptions
& base_opt
,
756 const ColumnFamilyOptions
& persisted_opt
,
757 const std::unordered_map
<std::string
, std::string
>* persisted_opt_map
,
758 OptionsSanityCheckLevel sanity_check_level
) {
759 for (auto& pair
: cf_options_type_info
) {
760 if (pair
.second
.verification
== OptionVerificationType::kDeprecated
) {
761 // We skip checking deprecated variables as they might
762 // contain random values since they might not be initialized
765 if (CFOptionSanityCheckLevel(pair
.first
) <= sanity_check_level
) {
766 if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt
),
767 reinterpret_cast<const char*>(&persisted_opt
),
768 pair
.second
, pair
.first
, persisted_opt_map
)) {
769 const size_t kBufferSize
= 2048;
770 char buffer
[kBufferSize
];
771 std::string base_value
;
772 std::string persisted_value
;
773 SerializeSingleOptionHelper(
774 reinterpret_cast<const char*>(&base_opt
) + pair
.second
.offset
,
775 pair
.second
.type
, &base_value
);
776 SerializeSingleOptionHelper(
777 reinterpret_cast<const char*>(&persisted_opt
) + pair
.second
.offset
,
778 pair
.second
.type
, &persisted_value
);
779 snprintf(buffer
, sizeof(buffer
),
780 "[RocksDBOptionsParser]: "
781 "failed the verification on ColumnFamilyOptions::%s --- "
782 "The specified one is %s while the persisted one is %s.\n",
783 pair
.first
.c_str(), base_value
.c_str(),
784 persisted_value
.c_str());
785 return Status::InvalidArgument(Slice(buffer
, sizeof(buffer
)));
792 Status
RocksDBOptionsParser::VerifyTableFactory(
793 const TableFactory
* base_tf
, const TableFactory
* file_tf
,
794 OptionsSanityCheckLevel sanity_check_level
) {
795 if (base_tf
&& file_tf
) {
796 if (sanity_check_level
> kSanityLevelNone
&&
797 std::string(base_tf
->Name()) != std::string(file_tf
->Name())) {
798 return Status::Corruption(
799 "[RocksDBOptionsParser]: "
800 "failed the verification on TableFactory->Name()");
802 if (base_tf
->Name() == BlockBasedTableFactory::kName
) {
803 return VerifyBlockBasedTableFactory(
804 static_cast_with_check
<const BlockBasedTableFactory
,
805 const TableFactory
>(base_tf
),
806 static_cast_with_check
<const BlockBasedTableFactory
,
807 const TableFactory
>(file_tf
),
810 // TODO(yhchiang): add checks for other table factory types
812 // TODO(yhchiang): further support sanity check here
816 } // namespace rocksdb
818 #endif // !ROCKSDB_LITE