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 "file/read_write_util.h"
17 #include "file/writable_file_writer.h"
18 #include "options/options_helper.h"
19 #include "rocksdb/convenience.h"
20 #include "rocksdb/db.h"
21 #include "test_util/sync_point.h"
22 #include "util/cast_util.h"
23 #include "util/string_util.h"
25 #include "port/port.h"
27 namespace ROCKSDB_NAMESPACE
{
29 static const std::string option_file_header
=
30 "# This is a RocksDB option file.\n"
32 "# For detailed file format spec, please refer to the example file\n"
33 "# in examples/rocksdb_option_file_example.ini\n"
37 Status
PersistRocksDBOptions(const DBOptions
& db_opt
,
38 const std::vector
<std::string
>& cf_names
,
39 const std::vector
<ColumnFamilyOptions
>& cf_opts
,
40 const std::string
& file_name
, FileSystem
* fs
) {
41 TEST_SYNC_POINT("PersistRocksDBOptions:start");
42 if (cf_names
.size() != cf_opts
.size()) {
43 return Status::InvalidArgument(
44 "cf_names.size() and cf_opts.size() must be the same");
46 std::unique_ptr
<FSWritableFile
> wf
;
49 fs
->NewWritableFile(file_name
, FileOptions(), &wf
, nullptr);
53 std::unique_ptr
<WritableFileWriter
> writable
;
54 writable
.reset(new WritableFileWriter(std::move(wf
), file_name
, EnvOptions(),
55 nullptr /* statistics */));
57 std::string options_file_content
;
59 writable
->Append(option_file_header
+ "[" +
60 opt_section_titles
[kOptionSectionVersion
] +
63 ToString(ROCKSDB_MAJOR
) + "." + ToString(ROCKSDB_MINOR
) +
64 "." + ToString(ROCKSDB_PATCH
) + "\n");
65 writable
->Append(" options_file_version=" +
66 ToString(ROCKSDB_OPTION_FILE_MAJOR
) + "." +
67 ToString(ROCKSDB_OPTION_FILE_MINOR
) + "\n");
68 writable
->Append("\n[" + opt_section_titles
[kOptionSectionDBOptions
] +
71 s
= GetStringFromDBOptions(&options_file_content
, db_opt
, "\n ");
76 writable
->Append(options_file_content
+ "\n");
78 for (size_t i
= 0; i
< cf_opts
.size(); ++i
) {
80 writable
->Append("\n[" + opt_section_titles
[kOptionSectionCFOptions
] +
81 " \"" + EscapeOptionString(cf_names
[i
]) + "\"]\n ");
82 s
= GetStringFromColumnFamilyOptions(&options_file_content
, cf_opts
[i
],
88 writable
->Append(options_file_content
+ "\n");
89 // TableOptions section
90 auto* tf
= cf_opts
[i
].table_factory
.get();
92 writable
->Append("[" + opt_section_titles
[kOptionSectionTableOptions
] +
93 tf
->Name() + " \"" + EscapeOptionString(cf_names
[i
]) +
95 options_file_content
.clear();
96 s
= tf
->GetOptionString(&options_file_content
, "\n ");
100 writable
->Append(options_file_content
+ "\n");
103 writable
->Sync(true /* use_fsync */);
106 return RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
107 db_opt
, cf_names
, cf_opts
, file_name
, fs
);
110 RocksDBOptionsParser::RocksDBOptionsParser() { Reset(); }
112 void RocksDBOptionsParser::Reset() {
113 db_opt_
= DBOptions();
117 cf_opt_maps_
.clear();
118 has_version_section_
= false;
119 has_db_options_
= false;
120 has_default_cf_options_
= false;
121 for (int i
= 0; i
< 3; ++i
) {
123 opt_file_version
[i
] = 0;
127 bool RocksDBOptionsParser::IsSection(const std::string
& line
) {
128 if (line
.size() < 2) {
131 if (line
[0] != '[' || line
[line
.size() - 1] != ']') {
137 Status
RocksDBOptionsParser::ParseSection(OptionSection
* section
,
139 std::string
* argument
,
140 const std::string
& line
,
141 const int line_num
) {
142 *section
= kOptionSectionUnknown
;
143 // A section is of the form [<SectionName> "<SectionArg>"], where
144 // "<SectionArg>" is optional.
145 size_t arg_start_pos
= line
.find("\"");
146 size_t arg_end_pos
= line
.rfind("\"");
147 // The following if-then check tries to identify whether the input
148 // section has the optional section argument.
149 if (arg_start_pos
!= std::string::npos
&& arg_start_pos
!= arg_end_pos
) {
150 *title
= TrimAndRemoveComment(line
.substr(1, arg_start_pos
- 1), true);
151 *argument
= UnescapeOptionString(
152 line
.substr(arg_start_pos
+ 1, arg_end_pos
- arg_start_pos
- 1));
154 *title
= TrimAndRemoveComment(line
.substr(1, line
.size() - 2), true);
157 for (int i
= 0; i
< kOptionSectionUnknown
; ++i
) {
158 if (title
->find(opt_section_titles
[i
]) == 0) {
159 if (i
== kOptionSectionVersion
|| i
== kOptionSectionDBOptions
||
160 i
== kOptionSectionCFOptions
) {
161 if (title
->size() == opt_section_titles
[i
].size()) {
162 // if true, then it indicats equal
163 *section
= static_cast<OptionSection
>(i
);
164 return CheckSection(*section
, *argument
, line_num
);
166 } else if (i
== kOptionSectionTableOptions
) {
167 // This type of sections has a sufffix at the end of the
169 if (title
->size() > opt_section_titles
[i
].size()) {
170 *section
= static_cast<OptionSection
>(i
);
171 return CheckSection(*section
, *argument
, line_num
);
176 return Status::InvalidArgument(std::string("Unknown section ") + line
);
179 Status
RocksDBOptionsParser::InvalidArgument(const int line_num
,
180 const std::string
& message
) {
181 return Status::InvalidArgument(
182 "[RocksDBOptionsParser Error] ",
183 message
+ " (at line " + ToString(line_num
) + ")");
186 Status
RocksDBOptionsParser::ParseStatement(std::string
* name
,
188 const std::string
& line
,
189 const int line_num
) {
190 size_t eq_pos
= line
.find("=");
191 if (eq_pos
== std::string::npos
) {
192 return InvalidArgument(line_num
, "A valid statement must have a '='.");
195 *name
= TrimAndRemoveComment(line
.substr(0, eq_pos
), true);
197 TrimAndRemoveComment(line
.substr(eq_pos
+ 1, line
.size() - eq_pos
- 1));
199 return InvalidArgument(line_num
,
200 "A valid statement must have a variable name.");
205 Status
RocksDBOptionsParser::Parse(const std::string
& file_name
, FileSystem
* fs
,
206 bool ignore_unknown_options
,
207 size_t file_readahead_size
) {
210 std::unique_ptr
<FSSequentialFile
> seq_file
;
211 Status s
= fs
->NewSequentialFile(file_name
, FileOptions(), &seq_file
,
217 SequentialFileReader
sf_reader(std::move(seq_file
), file_name
,
218 file_readahead_size
);
220 OptionSection section
= kOptionSectionUnknown
;
222 std::string argument
;
223 std::unordered_map
<std::string
, std::string
> opt_map
;
224 std::istringstream iss
;
226 bool has_data
= true;
227 // we only support single-lined statement.
228 for (int line_num
= 1; ReadOneLine(&iss
, &sf_reader
, &line
, &has_data
, &s
);
233 line
= TrimAndRemoveComment(line
);
237 if (IsSection(line
)) {
238 s
= EndSection(section
, title
, argument
, opt_map
, ignore_unknown_options
);
244 // If the option file is not generated by a higher minor version,
245 // there shouldn't be any unknown option.
246 if (ignore_unknown_options
&& section
== kOptionSectionVersion
) {
247 if (db_version
[0] < ROCKSDB_MAJOR
|| (db_version
[0] == ROCKSDB_MAJOR
&&
248 db_version
[1] <= ROCKSDB_MINOR
)) {
249 ignore_unknown_options
= false;
253 s
= ParseSection(§ion
, &title
, &argument
, line
, line_num
);
260 s
= ParseStatement(&name
, &value
, line
, line_num
);
264 opt_map
.insert({name
, value
});
268 s
= EndSection(section
, title
, argument
, opt_map
, ignore_unknown_options
);
273 return ValidityCheck();
276 Status
RocksDBOptionsParser::CheckSection(const OptionSection section
,
277 const std::string
& section_arg
,
278 const int line_num
) {
279 if (section
== kOptionSectionDBOptions
) {
280 if (has_db_options_
) {
281 return InvalidArgument(
283 "More than one DBOption section found in the option config file");
285 has_db_options_
= true;
286 } else if (section
== kOptionSectionCFOptions
) {
287 bool is_default_cf
= (section_arg
== kDefaultColumnFamilyName
);
288 if (cf_opts_
.size() == 0 && !is_default_cf
) {
289 return InvalidArgument(
291 "Default column family must be the first CFOptions section "
292 "in the option config file");
293 } else if (cf_opts_
.size() != 0 && is_default_cf
) {
294 return InvalidArgument(
296 "Default column family must be the first CFOptions section "
297 "in the optio/n config file");
298 } else if (GetCFOptions(section_arg
) != nullptr) {
299 return InvalidArgument(
301 "Two identical column families found in option config file");
303 has_default_cf_options_
|= is_default_cf
;
304 } else if (section
== kOptionSectionTableOptions
) {
305 if (GetCFOptions(section_arg
) == nullptr) {
306 return InvalidArgument(
307 line_num
, std::string(
308 "Does not find a matched column family name in "
309 "TableOptions section. Column Family Name:") +
312 } else if (section
== kOptionSectionVersion
) {
313 if (has_version_section_
) {
314 return InvalidArgument(
316 "More than one Version section found in the option config file.");
318 has_version_section_
= true;
323 Status
RocksDBOptionsParser::ParseVersionNumber(const std::string
& ver_name
,
324 const std::string
& ver_string
,
327 int version_index
= 0;
328 int current_number
= 0;
329 int current_digit_count
= 0;
330 bool has_dot
= false;
331 for (int i
= 0; i
< max_count
; ++i
) {
334 constexpr int kBufferSize
= 200;
335 char buffer
[kBufferSize
];
336 for (size_t i
= 0; i
< ver_string
.size(); ++i
) {
337 if (ver_string
[i
] == '.') {
338 if (version_index
>= max_count
- 1) {
339 snprintf(buffer
, sizeof(buffer
) - 1,
340 "A valid %s can only contains at most %d dots.",
341 ver_name
.c_str(), max_count
- 1);
342 return Status::InvalidArgument(buffer
);
344 if (current_digit_count
== 0) {
345 snprintf(buffer
, sizeof(buffer
) - 1,
346 "A valid %s must have at least one digit before each dot.",
348 return Status::InvalidArgument(buffer
);
350 version
[version_index
++] = current_number
;
352 current_digit_count
= 0;
354 } else if (isdigit(ver_string
[i
])) {
355 current_number
= current_number
* 10 + (ver_string
[i
] - '0');
356 current_digit_count
++;
358 snprintf(buffer
, sizeof(buffer
) - 1,
359 "A valid %s can only contains dots and numbers.",
361 return Status::InvalidArgument(buffer
);
364 version
[version_index
] = current_number
;
365 if (has_dot
&& current_digit_count
== 0) {
366 snprintf(buffer
, sizeof(buffer
) - 1,
367 "A valid %s must have at least one digit after each dot.",
369 return Status::InvalidArgument(buffer
);
374 Status
RocksDBOptionsParser::EndSection(
375 const OptionSection section
, const std::string
& section_title
,
376 const std::string
& section_arg
,
377 const std::unordered_map
<std::string
, std::string
>& opt_map
,
378 bool ignore_unknown_options
) {
380 if (section
== kOptionSectionDBOptions
) {
381 s
= GetDBOptionsFromMap(DBOptions(), opt_map
, &db_opt_
, true,
382 ignore_unknown_options
);
386 db_opt_map_
= opt_map
;
387 } else if (section
== kOptionSectionCFOptions
) {
388 // This condition should be ensured earlier in ParseSection
389 // so we make an assertion here.
390 assert(GetCFOptions(section_arg
) == nullptr);
391 cf_names_
.emplace_back(section_arg
);
392 cf_opts_
.emplace_back();
393 s
= GetColumnFamilyOptionsFromMap(ColumnFamilyOptions(), opt_map
,
394 &cf_opts_
.back(), true,
395 ignore_unknown_options
);
399 // keep the parsed string.
400 cf_opt_maps_
.emplace_back(opt_map
);
401 } else if (section
== kOptionSectionTableOptions
) {
402 assert(GetCFOptions(section_arg
) != nullptr);
403 auto* cf_opt
= GetCFOptionsImpl(section_arg
);
404 if (cf_opt
== nullptr) {
405 return Status::InvalidArgument(
406 "The specified column family must be defined before the "
407 "TableOptions section:",
410 // Ignore error as table factory deserialization is optional
411 s
= GetTableFactoryFromMap(
412 section_title
.substr(
413 opt_section_titles
[kOptionSectionTableOptions
].size()),
414 opt_map
, &(cf_opt
->table_factory
), ignore_unknown_options
);
418 } else if (section
== kOptionSectionVersion
) {
419 for (const auto pair
: opt_map
) {
420 if (pair
.first
== "rocksdb_version") {
421 s
= ParseVersionNumber(pair
.first
, pair
.second
, 3, db_version
);
425 } else if (pair
.first
== "options_file_version") {
426 s
= ParseVersionNumber(pair
.first
, pair
.second
, 2, opt_file_version
);
430 if (opt_file_version
[0] < 1) {
431 return Status::InvalidArgument(
432 "A valid options_file_version must be at least 1.");
440 Status
RocksDBOptionsParser::ValidityCheck() {
441 if (!has_db_options_
) {
442 return Status::Corruption(
443 "A RocksDB Option file must have a single DBOptions section");
445 if (!has_default_cf_options_
) {
446 return Status::Corruption(
447 "A RocksDB Option file must have a single CFOptions:default section");
453 std::string
RocksDBOptionsParser::TrimAndRemoveComment(const std::string
& line
,
456 size_t end
= line
.size();
458 // we only support "#" style comment
460 size_t search_pos
= 0;
461 while (search_pos
< line
.size()) {
462 size_t comment_pos
= line
.find('#', search_pos
);
463 if (comment_pos
== std::string::npos
) {
466 if (comment_pos
== 0 || line
[comment_pos
- 1] != '\\') {
470 search_pos
= comment_pos
+ 1;
474 while (start
< end
&& isspace(line
[start
]) != 0) {
478 // start < end implies end > 0.
479 while (start
< end
&& isspace(line
[end
- 1]) != 0) {
484 return line
.substr(start
, end
- start
);
491 bool AreEqualDoubles(const double a
, const double b
) {
492 return (fabs(a
- b
) < 0.00001);
496 bool AreEqualOptions(
497 const char* opt1
, const char* opt2
, const OptionTypeInfo
& type_info
,
498 const std::string
& opt_name
,
499 const std::unordered_map
<std::string
, std::string
>* opt_map
) {
500 const char* offset1
= opt1
+ type_info
.offset
;
501 const char* offset2
= opt2
+ type_info
.offset
;
503 switch (type_info
.type
) {
504 case OptionType::kBoolean
:
505 return (*reinterpret_cast<const bool*>(offset1
) ==
506 *reinterpret_cast<const bool*>(offset2
));
507 case OptionType::kInt
:
508 return (*reinterpret_cast<const int*>(offset1
) ==
509 *reinterpret_cast<const int*>(offset2
));
510 case OptionType::kInt32T
:
511 return (*reinterpret_cast<const int32_t*>(offset1
) ==
512 *reinterpret_cast<const int32_t*>(offset2
));
513 case OptionType::kInt64T
:
516 GetUnaligned(reinterpret_cast<const int64_t*>(offset1
), &v1
);
517 GetUnaligned(reinterpret_cast<const int64_t*>(offset2
), &v2
);
520 case OptionType::kVectorInt
:
521 return (*reinterpret_cast<const std::vector
<int>*>(offset1
) ==
522 *reinterpret_cast<const std::vector
<int>*>(offset2
));
523 case OptionType::kUInt
:
524 return (*reinterpret_cast<const unsigned int*>(offset1
) ==
525 *reinterpret_cast<const unsigned int*>(offset2
));
526 case OptionType::kUInt32T
:
527 return (*reinterpret_cast<const uint32_t*>(offset1
) ==
528 *reinterpret_cast<const uint32_t*>(offset2
));
529 case OptionType::kUInt64T
:
532 GetUnaligned(reinterpret_cast<const uint64_t*>(offset1
), &v1
);
533 GetUnaligned(reinterpret_cast<const uint64_t*>(offset2
), &v2
);
536 case OptionType::kSizeT
:
539 GetUnaligned(reinterpret_cast<const size_t*>(offset1
), &v1
);
540 GetUnaligned(reinterpret_cast<const size_t*>(offset2
), &v2
);
543 case OptionType::kString
:
544 return (*reinterpret_cast<const std::string
*>(offset1
) ==
545 *reinterpret_cast<const std::string
*>(offset2
));
546 case OptionType::kDouble
:
547 return AreEqualDoubles(*reinterpret_cast<const double*>(offset1
),
548 *reinterpret_cast<const double*>(offset2
));
549 case OptionType::kCompactionStyle
:
550 return (*reinterpret_cast<const CompactionStyle
*>(offset1
) ==
551 *reinterpret_cast<const CompactionStyle
*>(offset2
));
552 case OptionType::kCompactionPri
:
553 return (*reinterpret_cast<const CompactionPri
*>(offset1
) ==
554 *reinterpret_cast<const CompactionPri
*>(offset2
));
555 case OptionType::kCompressionType
:
556 return (*reinterpret_cast<const CompressionType
*>(offset1
) ==
557 *reinterpret_cast<const CompressionType
*>(offset2
));
558 case OptionType::kVectorCompressionType
: {
560 reinterpret_cast<const std::vector
<CompressionType
>*>(offset1
);
562 reinterpret_cast<const std::vector
<CompressionType
>*>(offset2
);
563 return (*vec1
== *vec2
);
565 case OptionType::kChecksumType
:
566 return (*reinterpret_cast<const ChecksumType
*>(offset1
) ==
567 *reinterpret_cast<const ChecksumType
*>(offset2
));
568 case OptionType::kBlockBasedTableIndexType
:
570 *reinterpret_cast<const BlockBasedTableOptions::IndexType
*>(
572 *reinterpret_cast<const BlockBasedTableOptions::IndexType
*>(offset2
));
573 case OptionType::kBlockBasedTableDataBlockIndexType
:
575 *reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType
*>(
577 *reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType
*>(
579 case OptionType::kBlockBasedTableIndexShorteningMode
:
581 *reinterpret_cast<const BlockBasedTableOptions::IndexShorteningMode
*>(
583 *reinterpret_cast<const BlockBasedTableOptions::IndexShorteningMode
*>(
585 case OptionType::kWALRecoveryMode
:
586 return (*reinterpret_cast<const WALRecoveryMode
*>(offset1
) ==
587 *reinterpret_cast<const WALRecoveryMode
*>(offset2
));
588 case OptionType::kAccessHint
:
589 return (*reinterpret_cast<const DBOptions::AccessHint
*>(offset1
) ==
590 *reinterpret_cast<const DBOptions::AccessHint
*>(offset2
));
591 case OptionType::kInfoLogLevel
:
592 return (*reinterpret_cast<const InfoLogLevel
*>(offset1
) ==
593 *reinterpret_cast<const InfoLogLevel
*>(offset2
));
594 case OptionType::kCompactionOptionsFIFO
: {
595 CompactionOptionsFIFO lhs
=
596 *reinterpret_cast<const CompactionOptionsFIFO
*>(offset1
);
597 CompactionOptionsFIFO rhs
=
598 *reinterpret_cast<const CompactionOptionsFIFO
*>(offset2
);
599 if (lhs
.max_table_files_size
== rhs
.max_table_files_size
&&
600 lhs
.allow_compaction
== rhs
.allow_compaction
) {
605 case OptionType::kCompactionOptionsUniversal
: {
606 CompactionOptionsUniversal lhs
=
607 *reinterpret_cast<const CompactionOptionsUniversal
*>(offset1
);
608 CompactionOptionsUniversal rhs
=
609 *reinterpret_cast<const CompactionOptionsUniversal
*>(offset2
);
610 if (lhs
.size_ratio
== rhs
.size_ratio
&&
611 lhs
.min_merge_width
== rhs
.min_merge_width
&&
612 lhs
.max_merge_width
== rhs
.max_merge_width
&&
613 lhs
.max_size_amplification_percent
==
614 rhs
.max_size_amplification_percent
&&
615 lhs
.compression_size_percent
== rhs
.compression_size_percent
&&
616 lhs
.stop_style
== rhs
.stop_style
&&
617 lhs
.allow_trivial_move
== rhs
.allow_trivial_move
) {
623 if (type_info
.verification
== OptionVerificationType::kByName
||
624 type_info
.verification
==
625 OptionVerificationType::kByNameAllowFromNull
||
626 type_info
.verification
== OptionVerificationType::kByNameAllowNull
) {
629 SerializeSingleOptionHelper(offset1
, type_info
.type
, &value1
);
630 if (result
== false) {
633 if (opt_map
== nullptr) {
636 auto iter
= opt_map
->find(opt_name
);
637 if (iter
== opt_map
->end()) {
640 if (type_info
.verification
==
641 OptionVerificationType::kByNameAllowNull
) {
642 if (iter
->second
== kNullptrString
|| value1
== kNullptrString
) {
645 } else if (type_info
.verification
==
646 OptionVerificationType::kByNameAllowFromNull
) {
647 if (iter
->second
== kNullptrString
) {
651 return (value1
== iter
->second
);
658 Status
RocksDBOptionsParser::VerifyRocksDBOptionsFromFile(
659 const DBOptions
& db_opt
, const std::vector
<std::string
>& cf_names
,
660 const std::vector
<ColumnFamilyOptions
>& cf_opts
,
661 const std::string
& file_name
, FileSystem
* fs
,
662 OptionsSanityCheckLevel sanity_check_level
, bool ignore_unknown_options
) {
663 // We infer option file readhead size from log readahead size.
664 // If it is not given, use 512KB.
665 size_t file_readahead_size
= db_opt
.log_readahead_size
;
666 if (file_readahead_size
== 0) {
667 const size_t kDefaultOptionFileReadAheadSize
= 512 * 1024;
668 file_readahead_size
= kDefaultOptionFileReadAheadSize
;
671 RocksDBOptionsParser parser
;
673 parser
.Parse(file_name
, fs
, ignore_unknown_options
, file_readahead_size
);
679 s
= VerifyDBOptions(db_opt
, *parser
.db_opt(), parser
.db_opt_map(),
685 // Verify ColumnFamily Name
686 if (cf_names
.size() != parser
.cf_names()->size()) {
687 if (sanity_check_level
>= kSanityLevelLooselyCompatible
) {
688 return Status::InvalidArgument(
689 "[RocksDBOptionParser Error] The persisted options does not have "
690 "the same number of column family names as the db instance.");
691 } else if (cf_opts
.size() > parser
.cf_opts()->size()) {
692 return Status::InvalidArgument(
693 "[RocksDBOptionsParser Error]",
694 "The persisted options file has less number of column family "
695 "names than that of the specified one.");
698 for (size_t i
= 0; i
< cf_names
.size(); ++i
) {
699 if (cf_names
[i
] != parser
.cf_names()->at(i
)) {
700 return Status::InvalidArgument(
701 "[RocksDBOptionParser Error] The persisted options and the db"
702 "instance does not have the same name for column family ",
707 // Verify Column Family Options
708 if (cf_opts
.size() != parser
.cf_opts()->size()) {
709 if (sanity_check_level
>= kSanityLevelLooselyCompatible
) {
710 return Status::InvalidArgument(
711 "[RocksDBOptionsParser Error]",
712 "The persisted options does not have the same number of "
713 "column families as the db instance.");
714 } else if (cf_opts
.size() > parser
.cf_opts()->size()) {
715 return Status::InvalidArgument(
716 "[RocksDBOptionsParser Error]",
717 "The persisted options file has less number of column families "
718 "than that of the specified number.");
721 for (size_t i
= 0; i
< cf_opts
.size(); ++i
) {
722 s
= VerifyCFOptions(cf_opts
[i
], parser
.cf_opts()->at(i
),
723 &(parser
.cf_opt_maps()->at(i
)), sanity_check_level
);
727 s
= VerifyTableFactory(cf_opts
[i
].table_factory
.get(),
728 parser
.cf_opts()->at(i
).table_factory
.get(),
738 Status
RocksDBOptionsParser::VerifyDBOptions(
739 const DBOptions
& base_opt
, const DBOptions
& persisted_opt
,
740 const std::unordered_map
<std::string
, std::string
>* /*opt_map*/,
741 OptionsSanityCheckLevel sanity_check_level
) {
742 for (auto pair
: db_options_type_info
) {
743 if (pair
.second
.verification
== OptionVerificationType::kDeprecated
) {
744 // We skip checking deprecated variables as they might
745 // contain random values since they might not be initialized
748 if (DBOptionSanityCheckLevel(pair
.first
) <= sanity_check_level
) {
749 if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt
),
750 reinterpret_cast<const char*>(&persisted_opt
),
751 pair
.second
, pair
.first
, nullptr)) {
752 constexpr size_t kBufferSize
= 2048;
753 char buffer
[kBufferSize
];
754 std::string base_value
;
755 std::string persisted_value
;
756 SerializeSingleOptionHelper(
757 reinterpret_cast<const char*>(&base_opt
) + pair
.second
.offset
,
758 pair
.second
.type
, &base_value
);
759 SerializeSingleOptionHelper(
760 reinterpret_cast<const char*>(&persisted_opt
) + pair
.second
.offset
,
761 pair
.second
.type
, &persisted_value
);
762 snprintf(buffer
, sizeof(buffer
),
763 "[RocksDBOptionsParser]: "
764 "failed the verification on DBOptions::%s --- "
765 "The specified one is %s while the persisted one is %s.\n",
766 pair
.first
.c_str(), base_value
.c_str(),
767 persisted_value
.c_str());
768 return Status::InvalidArgument(Slice(buffer
, strlen(buffer
)));
775 Status
RocksDBOptionsParser::VerifyCFOptions(
776 const ColumnFamilyOptions
& base_opt
,
777 const ColumnFamilyOptions
& persisted_opt
,
778 const std::unordered_map
<std::string
, std::string
>* persisted_opt_map
,
779 OptionsSanityCheckLevel sanity_check_level
) {
780 for (auto& pair
: cf_options_type_info
) {
781 if (pair
.second
.verification
== OptionVerificationType::kDeprecated
) {
782 // We skip checking deprecated variables as they might
783 // contain random values since they might not be initialized
786 if (CFOptionSanityCheckLevel(pair
.first
) <= sanity_check_level
) {
787 if (!AreEqualOptions(reinterpret_cast<const char*>(&base_opt
),
788 reinterpret_cast<const char*>(&persisted_opt
),
789 pair
.second
, pair
.first
, persisted_opt_map
)) {
790 constexpr size_t kBufferSize
= 2048;
791 char buffer
[kBufferSize
];
792 std::string base_value
;
793 std::string persisted_value
;
794 SerializeSingleOptionHelper(
795 reinterpret_cast<const char*>(&base_opt
) + pair
.second
.offset
,
796 pair
.second
.type
, &base_value
);
797 SerializeSingleOptionHelper(
798 reinterpret_cast<const char*>(&persisted_opt
) + pair
.second
.offset
,
799 pair
.second
.type
, &persisted_value
);
800 snprintf(buffer
, sizeof(buffer
),
801 "[RocksDBOptionsParser]: "
802 "failed the verification on ColumnFamilyOptions::%s --- "
803 "The specified one is %s while the persisted one is %s.\n",
804 pair
.first
.c_str(), base_value
.c_str(),
805 persisted_value
.c_str());
806 return Status::InvalidArgument(Slice(buffer
, sizeof(buffer
)));
813 Status
RocksDBOptionsParser::VerifyTableFactory(
814 const TableFactory
* base_tf
, const TableFactory
* file_tf
,
815 OptionsSanityCheckLevel sanity_check_level
) {
816 if (base_tf
&& file_tf
) {
817 if (sanity_check_level
> kSanityLevelNone
&&
818 std::string(base_tf
->Name()) != std::string(file_tf
->Name())) {
819 return Status::Corruption(
820 "[RocksDBOptionsParser]: "
821 "failed the verification on TableFactory->Name()");
823 if (base_tf
->Name() == BlockBasedTableFactory::kName
) {
824 return VerifyBlockBasedTableFactory(
825 static_cast_with_check
<const BlockBasedTableFactory
,
826 const TableFactory
>(base_tf
),
827 static_cast_with_check
<const BlockBasedTableFactory
,
828 const TableFactory
>(file_tf
),
831 // TODO(yhchiang): add checks for other table factory types
833 // TODO(yhchiang): further support sanity check here
837 } // namespace ROCKSDB_NAMESPACE
839 #endif // !ROCKSDB_LITE