]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/options/options_helper.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / rocksdb / options / options_helper.cc
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).
5 #include "options/options_helper.h"
6
7 #include <cassert>
8 #include <cctype>
9 #include <cstdlib>
10 #include <set>
11 #include <unordered_set>
12 #include <vector>
13
14 #include "options/cf_options.h"
15 #include "options/db_options.h"
16 #include "rocksdb/cache.h"
17 #include "rocksdb/compaction_filter.h"
18 #include "rocksdb/convenience.h"
19 #include "rocksdb/filter_policy.h"
20 #include "rocksdb/flush_block_policy.h"
21 #include "rocksdb/memtablerep.h"
22 #include "rocksdb/merge_operator.h"
23 #include "rocksdb/options.h"
24 #include "rocksdb/rate_limiter.h"
25 #include "rocksdb/slice_transform.h"
26 #include "rocksdb/table.h"
27 #include "rocksdb/utilities/object_registry.h"
28 #include "rocksdb/utilities/options_type.h"
29 #include "util/string_util.h"
30
31 namespace ROCKSDB_NAMESPACE {
32 ConfigOptions::ConfigOptions()
33 #ifndef ROCKSDB_LITE
34 : registry(ObjectRegistry::NewInstance())
35 #endif
36 {
37 env = Env::Default();
38 }
39
40 ConfigOptions::ConfigOptions(const DBOptions& db_opts) : env(db_opts.env) {
41 #ifndef ROCKSDB_LITE
42 registry = ObjectRegistry::NewInstance();
43 #endif
44 }
45
46 Status ValidateOptions(const DBOptions& db_opts,
47 const ColumnFamilyOptions& cf_opts) {
48 Status s;
49 #ifndef ROCKSDB_LITE
50 auto db_cfg = DBOptionsAsConfigurable(db_opts);
51 auto cf_cfg = CFOptionsAsConfigurable(cf_opts);
52 s = db_cfg->ValidateOptions(db_opts, cf_opts);
53 if (s.ok()) s = cf_cfg->ValidateOptions(db_opts, cf_opts);
54 #else
55 s = cf_opts.table_factory->ValidateOptions(db_opts, cf_opts);
56 #endif
57 return s;
58 }
59
60 DBOptions BuildDBOptions(const ImmutableDBOptions& immutable_db_options,
61 const MutableDBOptions& mutable_db_options) {
62 DBOptions options;
63
64 options.create_if_missing = immutable_db_options.create_if_missing;
65 options.create_missing_column_families =
66 immutable_db_options.create_missing_column_families;
67 options.error_if_exists = immutable_db_options.error_if_exists;
68 options.paranoid_checks = immutable_db_options.paranoid_checks;
69 options.flush_verify_memtable_count =
70 immutable_db_options.flush_verify_memtable_count;
71 options.track_and_verify_wals_in_manifest =
72 immutable_db_options.track_and_verify_wals_in_manifest;
73 options.verify_sst_unique_id_in_manifest =
74 immutable_db_options.verify_sst_unique_id_in_manifest;
75 options.env = immutable_db_options.env;
76 options.rate_limiter = immutable_db_options.rate_limiter;
77 options.sst_file_manager = immutable_db_options.sst_file_manager;
78 options.info_log = immutable_db_options.info_log;
79 options.info_log_level = immutable_db_options.info_log_level;
80 options.max_open_files = mutable_db_options.max_open_files;
81 options.max_file_opening_threads =
82 immutable_db_options.max_file_opening_threads;
83 options.max_total_wal_size = mutable_db_options.max_total_wal_size;
84 options.statistics = immutable_db_options.statistics;
85 options.use_fsync = immutable_db_options.use_fsync;
86 options.db_paths = immutable_db_options.db_paths;
87 options.db_log_dir = immutable_db_options.db_log_dir;
88 options.wal_dir = immutable_db_options.wal_dir;
89 options.delete_obsolete_files_period_micros =
90 mutable_db_options.delete_obsolete_files_period_micros;
91 options.max_background_jobs = mutable_db_options.max_background_jobs;
92 options.max_background_compactions =
93 mutable_db_options.max_background_compactions;
94 options.bytes_per_sync = mutable_db_options.bytes_per_sync;
95 options.wal_bytes_per_sync = mutable_db_options.wal_bytes_per_sync;
96 options.strict_bytes_per_sync = mutable_db_options.strict_bytes_per_sync;
97 options.max_subcompactions = mutable_db_options.max_subcompactions;
98 options.max_background_flushes = mutable_db_options.max_background_flushes;
99 options.max_log_file_size = immutable_db_options.max_log_file_size;
100 options.log_file_time_to_roll = immutable_db_options.log_file_time_to_roll;
101 options.keep_log_file_num = immutable_db_options.keep_log_file_num;
102 options.recycle_log_file_num = immutable_db_options.recycle_log_file_num;
103 options.max_manifest_file_size = immutable_db_options.max_manifest_file_size;
104 options.table_cache_numshardbits =
105 immutable_db_options.table_cache_numshardbits;
106 options.WAL_ttl_seconds = immutable_db_options.WAL_ttl_seconds;
107 options.WAL_size_limit_MB = immutable_db_options.WAL_size_limit_MB;
108 options.manifest_preallocation_size =
109 immutable_db_options.manifest_preallocation_size;
110 options.allow_mmap_reads = immutable_db_options.allow_mmap_reads;
111 options.allow_mmap_writes = immutable_db_options.allow_mmap_writes;
112 options.use_direct_reads = immutable_db_options.use_direct_reads;
113 options.use_direct_io_for_flush_and_compaction =
114 immutable_db_options.use_direct_io_for_flush_and_compaction;
115 options.allow_fallocate = immutable_db_options.allow_fallocate;
116 options.is_fd_close_on_exec = immutable_db_options.is_fd_close_on_exec;
117 options.stats_dump_period_sec = mutable_db_options.stats_dump_period_sec;
118 options.stats_persist_period_sec =
119 mutable_db_options.stats_persist_period_sec;
120 options.persist_stats_to_disk = immutable_db_options.persist_stats_to_disk;
121 options.stats_history_buffer_size =
122 mutable_db_options.stats_history_buffer_size;
123 options.advise_random_on_open = immutable_db_options.advise_random_on_open;
124 options.db_write_buffer_size = immutable_db_options.db_write_buffer_size;
125 options.write_buffer_manager = immutable_db_options.write_buffer_manager;
126 options.access_hint_on_compaction_start =
127 immutable_db_options.access_hint_on_compaction_start;
128 options.compaction_readahead_size =
129 mutable_db_options.compaction_readahead_size;
130 options.random_access_max_buffer_size =
131 immutable_db_options.random_access_max_buffer_size;
132 options.writable_file_max_buffer_size =
133 mutable_db_options.writable_file_max_buffer_size;
134 options.use_adaptive_mutex = immutable_db_options.use_adaptive_mutex;
135 options.listeners = immutable_db_options.listeners;
136 options.enable_thread_tracking = immutable_db_options.enable_thread_tracking;
137 options.delayed_write_rate = mutable_db_options.delayed_write_rate;
138 options.enable_pipelined_write = immutable_db_options.enable_pipelined_write;
139 options.unordered_write = immutable_db_options.unordered_write;
140 options.allow_concurrent_memtable_write =
141 immutable_db_options.allow_concurrent_memtable_write;
142 options.enable_write_thread_adaptive_yield =
143 immutable_db_options.enable_write_thread_adaptive_yield;
144 options.max_write_batch_group_size_bytes =
145 immutable_db_options.max_write_batch_group_size_bytes;
146 options.write_thread_max_yield_usec =
147 immutable_db_options.write_thread_max_yield_usec;
148 options.write_thread_slow_yield_usec =
149 immutable_db_options.write_thread_slow_yield_usec;
150 options.skip_stats_update_on_db_open =
151 immutable_db_options.skip_stats_update_on_db_open;
152 options.skip_checking_sst_file_sizes_on_db_open =
153 immutable_db_options.skip_checking_sst_file_sizes_on_db_open;
154 options.wal_recovery_mode = immutable_db_options.wal_recovery_mode;
155 options.allow_2pc = immutable_db_options.allow_2pc;
156 options.row_cache = immutable_db_options.row_cache;
157 #ifndef ROCKSDB_LITE
158 options.wal_filter = immutable_db_options.wal_filter;
159 #endif // ROCKSDB_LITE
160 options.fail_if_options_file_error =
161 immutable_db_options.fail_if_options_file_error;
162 options.dump_malloc_stats = immutable_db_options.dump_malloc_stats;
163 options.avoid_flush_during_recovery =
164 immutable_db_options.avoid_flush_during_recovery;
165 options.avoid_flush_during_shutdown =
166 mutable_db_options.avoid_flush_during_shutdown;
167 options.allow_ingest_behind = immutable_db_options.allow_ingest_behind;
168 options.two_write_queues = immutable_db_options.two_write_queues;
169 options.manual_wal_flush = immutable_db_options.manual_wal_flush;
170 options.wal_compression = immutable_db_options.wal_compression;
171 options.atomic_flush = immutable_db_options.atomic_flush;
172 options.avoid_unnecessary_blocking_io =
173 immutable_db_options.avoid_unnecessary_blocking_io;
174 options.log_readahead_size = immutable_db_options.log_readahead_size;
175 options.file_checksum_gen_factory =
176 immutable_db_options.file_checksum_gen_factory;
177 options.best_efforts_recovery = immutable_db_options.best_efforts_recovery;
178 options.max_bgerror_resume_count =
179 immutable_db_options.max_bgerror_resume_count;
180 options.bgerror_resume_retry_interval =
181 immutable_db_options.bgerror_resume_retry_interval;
182 options.db_host_id = immutable_db_options.db_host_id;
183 options.allow_data_in_errors = immutable_db_options.allow_data_in_errors;
184 options.checksum_handoff_file_types =
185 immutable_db_options.checksum_handoff_file_types;
186 options.lowest_used_cache_tier = immutable_db_options.lowest_used_cache_tier;
187 options.enforce_single_del_contracts =
188 immutable_db_options.enforce_single_del_contracts;
189 return options;
190 }
191
192 ColumnFamilyOptions BuildColumnFamilyOptions(
193 const ColumnFamilyOptions& options,
194 const MutableCFOptions& mutable_cf_options) {
195 ColumnFamilyOptions cf_opts(options);
196 UpdateColumnFamilyOptions(mutable_cf_options, &cf_opts);
197 // TODO(yhchiang): find some way to handle the following derived options
198 // * max_file_size
199 return cf_opts;
200 }
201
202 void UpdateColumnFamilyOptions(const MutableCFOptions& moptions,
203 ColumnFamilyOptions* cf_opts) {
204 // Memtable related options
205 cf_opts->write_buffer_size = moptions.write_buffer_size;
206 cf_opts->max_write_buffer_number = moptions.max_write_buffer_number;
207 cf_opts->arena_block_size = moptions.arena_block_size;
208 cf_opts->memtable_prefix_bloom_size_ratio =
209 moptions.memtable_prefix_bloom_size_ratio;
210 cf_opts->memtable_whole_key_filtering = moptions.memtable_whole_key_filtering;
211 cf_opts->memtable_huge_page_size = moptions.memtable_huge_page_size;
212 cf_opts->max_successive_merges = moptions.max_successive_merges;
213 cf_opts->inplace_update_num_locks = moptions.inplace_update_num_locks;
214 cf_opts->prefix_extractor = moptions.prefix_extractor;
215 cf_opts->experimental_mempurge_threshold =
216 moptions.experimental_mempurge_threshold;
217 cf_opts->memtable_protection_bytes_per_key =
218 moptions.memtable_protection_bytes_per_key;
219
220 // Compaction related options
221 cf_opts->disable_auto_compactions = moptions.disable_auto_compactions;
222 cf_opts->soft_pending_compaction_bytes_limit =
223 moptions.soft_pending_compaction_bytes_limit;
224 cf_opts->hard_pending_compaction_bytes_limit =
225 moptions.hard_pending_compaction_bytes_limit;
226 cf_opts->level0_file_num_compaction_trigger =
227 moptions.level0_file_num_compaction_trigger;
228 cf_opts->level0_slowdown_writes_trigger =
229 moptions.level0_slowdown_writes_trigger;
230 cf_opts->level0_stop_writes_trigger = moptions.level0_stop_writes_trigger;
231 cf_opts->max_compaction_bytes = moptions.max_compaction_bytes;
232 cf_opts->ignore_max_compaction_bytes_for_input =
233 moptions.ignore_max_compaction_bytes_for_input;
234 cf_opts->target_file_size_base = moptions.target_file_size_base;
235 cf_opts->target_file_size_multiplier = moptions.target_file_size_multiplier;
236 cf_opts->max_bytes_for_level_base = moptions.max_bytes_for_level_base;
237 cf_opts->max_bytes_for_level_multiplier =
238 moptions.max_bytes_for_level_multiplier;
239 cf_opts->ttl = moptions.ttl;
240 cf_opts->periodic_compaction_seconds = moptions.periodic_compaction_seconds;
241
242 cf_opts->max_bytes_for_level_multiplier_additional.clear();
243 for (auto value : moptions.max_bytes_for_level_multiplier_additional) {
244 cf_opts->max_bytes_for_level_multiplier_additional.emplace_back(value);
245 }
246
247 cf_opts->compaction_options_fifo = moptions.compaction_options_fifo;
248 cf_opts->compaction_options_universal = moptions.compaction_options_universal;
249
250 // Blob file related options
251 cf_opts->enable_blob_files = moptions.enable_blob_files;
252 cf_opts->min_blob_size = moptions.min_blob_size;
253 cf_opts->blob_file_size = moptions.blob_file_size;
254 cf_opts->blob_compression_type = moptions.blob_compression_type;
255 cf_opts->enable_blob_garbage_collection =
256 moptions.enable_blob_garbage_collection;
257 cf_opts->blob_garbage_collection_age_cutoff =
258 moptions.blob_garbage_collection_age_cutoff;
259 cf_opts->blob_garbage_collection_force_threshold =
260 moptions.blob_garbage_collection_force_threshold;
261 cf_opts->blob_compaction_readahead_size =
262 moptions.blob_compaction_readahead_size;
263 cf_opts->blob_file_starting_level = moptions.blob_file_starting_level;
264 cf_opts->prepopulate_blob_cache = moptions.prepopulate_blob_cache;
265
266 // Misc options
267 cf_opts->max_sequential_skip_in_iterations =
268 moptions.max_sequential_skip_in_iterations;
269 cf_opts->check_flush_compaction_key_order =
270 moptions.check_flush_compaction_key_order;
271 cf_opts->paranoid_file_checks = moptions.paranoid_file_checks;
272 cf_opts->report_bg_io_stats = moptions.report_bg_io_stats;
273 cf_opts->compression = moptions.compression;
274 cf_opts->compression_opts = moptions.compression_opts;
275 cf_opts->bottommost_compression = moptions.bottommost_compression;
276 cf_opts->bottommost_compression_opts = moptions.bottommost_compression_opts;
277 cf_opts->sample_for_compression = moptions.sample_for_compression;
278 cf_opts->compression_per_level = moptions.compression_per_level;
279 cf_opts->last_level_temperature = moptions.last_level_temperature;
280 cf_opts->bottommost_temperature = moptions.last_level_temperature;
281 }
282
283 void UpdateColumnFamilyOptions(const ImmutableCFOptions& ioptions,
284 ColumnFamilyOptions* cf_opts) {
285 cf_opts->compaction_style = ioptions.compaction_style;
286 cf_opts->compaction_pri = ioptions.compaction_pri;
287 cf_opts->comparator = ioptions.user_comparator;
288 cf_opts->merge_operator = ioptions.merge_operator;
289 cf_opts->compaction_filter = ioptions.compaction_filter;
290 cf_opts->compaction_filter_factory = ioptions.compaction_filter_factory;
291 cf_opts->min_write_buffer_number_to_merge =
292 ioptions.min_write_buffer_number_to_merge;
293 cf_opts->max_write_buffer_number_to_maintain =
294 ioptions.max_write_buffer_number_to_maintain;
295 cf_opts->max_write_buffer_size_to_maintain =
296 ioptions.max_write_buffer_size_to_maintain;
297 cf_opts->inplace_update_support = ioptions.inplace_update_support;
298 cf_opts->inplace_callback = ioptions.inplace_callback;
299 cf_opts->memtable_factory = ioptions.memtable_factory;
300 cf_opts->table_factory = ioptions.table_factory;
301 cf_opts->table_properties_collector_factories =
302 ioptions.table_properties_collector_factories;
303 cf_opts->bloom_locality = ioptions.bloom_locality;
304 cf_opts->level_compaction_dynamic_level_bytes =
305 ioptions.level_compaction_dynamic_level_bytes;
306 cf_opts->level_compaction_dynamic_file_size =
307 ioptions.level_compaction_dynamic_file_size;
308 cf_opts->num_levels = ioptions.num_levels;
309 cf_opts->optimize_filters_for_hits = ioptions.optimize_filters_for_hits;
310 cf_opts->force_consistency_checks = ioptions.force_consistency_checks;
311 cf_opts->memtable_insert_with_hint_prefix_extractor =
312 ioptions.memtable_insert_with_hint_prefix_extractor;
313 cf_opts->cf_paths = ioptions.cf_paths;
314 cf_opts->compaction_thread_limiter = ioptions.compaction_thread_limiter;
315 cf_opts->sst_partitioner_factory = ioptions.sst_partitioner_factory;
316 cf_opts->blob_cache = ioptions.blob_cache;
317 cf_opts->preclude_last_level_data_seconds =
318 ioptions.preclude_last_level_data_seconds;
319 cf_opts->preserve_internal_time_seconds =
320 ioptions.preserve_internal_time_seconds;
321
322 // TODO(yhchiang): find some way to handle the following derived options
323 // * max_file_size
324 }
325
326 std::map<CompactionStyle, std::string>
327 OptionsHelper::compaction_style_to_string = {
328 {kCompactionStyleLevel, "kCompactionStyleLevel"},
329 {kCompactionStyleUniversal, "kCompactionStyleUniversal"},
330 {kCompactionStyleFIFO, "kCompactionStyleFIFO"},
331 {kCompactionStyleNone, "kCompactionStyleNone"}};
332
333 std::map<CompactionPri, std::string> OptionsHelper::compaction_pri_to_string = {
334 {kByCompensatedSize, "kByCompensatedSize"},
335 {kOldestLargestSeqFirst, "kOldestLargestSeqFirst"},
336 {kOldestSmallestSeqFirst, "kOldestSmallestSeqFirst"},
337 {kMinOverlappingRatio, "kMinOverlappingRatio"},
338 {kRoundRobin, "kRoundRobin"}};
339
340 std::map<CompactionStopStyle, std::string>
341 OptionsHelper::compaction_stop_style_to_string = {
342 {kCompactionStopStyleSimilarSize, "kCompactionStopStyleSimilarSize"},
343 {kCompactionStopStyleTotalSize, "kCompactionStopStyleTotalSize"}};
344
345 std::map<Temperature, std::string> OptionsHelper::temperature_to_string = {
346 {Temperature::kUnknown, "kUnknown"},
347 {Temperature::kHot, "kHot"},
348 {Temperature::kWarm, "kWarm"},
349 {Temperature::kCold, "kCold"}};
350
351 std::unordered_map<std::string, ChecksumType>
352 OptionsHelper::checksum_type_string_map = {{"kNoChecksum", kNoChecksum},
353 {"kCRC32c", kCRC32c},
354 {"kxxHash", kxxHash},
355 {"kxxHash64", kxxHash64},
356 {"kXXH3", kXXH3}};
357
358 std::unordered_map<std::string, CompressionType>
359 OptionsHelper::compression_type_string_map = {
360 {"kNoCompression", kNoCompression},
361 {"kSnappyCompression", kSnappyCompression},
362 {"kZlibCompression", kZlibCompression},
363 {"kBZip2Compression", kBZip2Compression},
364 {"kLZ4Compression", kLZ4Compression},
365 {"kLZ4HCCompression", kLZ4HCCompression},
366 {"kXpressCompression", kXpressCompression},
367 {"kZSTD", kZSTD},
368 {"kZSTDNotFinalCompression", kZSTDNotFinalCompression},
369 {"kDisableCompressionOption", kDisableCompressionOption}};
370
371 std::vector<CompressionType> GetSupportedCompressions() {
372 // std::set internally to deduplicate potential name aliases
373 std::set<CompressionType> supported_compressions;
374 for (const auto& comp_to_name : OptionsHelper::compression_type_string_map) {
375 CompressionType t = comp_to_name.second;
376 if (t != kDisableCompressionOption && CompressionTypeSupported(t)) {
377 supported_compressions.insert(t);
378 }
379 }
380 return std::vector<CompressionType>(supported_compressions.begin(),
381 supported_compressions.end());
382 }
383
384 std::vector<CompressionType> GetSupportedDictCompressions() {
385 std::set<CompressionType> dict_compression_types;
386 for (const auto& comp_to_name : OptionsHelper::compression_type_string_map) {
387 CompressionType t = comp_to_name.second;
388 if (t != kDisableCompressionOption && DictCompressionTypeSupported(t)) {
389 dict_compression_types.insert(t);
390 }
391 }
392 return std::vector<CompressionType>(dict_compression_types.begin(),
393 dict_compression_types.end());
394 }
395
396 std::vector<ChecksumType> GetSupportedChecksums() {
397 std::set<ChecksumType> checksum_types;
398 for (const auto& e : OptionsHelper::checksum_type_string_map) {
399 checksum_types.insert(e.second);
400 }
401 return std::vector<ChecksumType>(checksum_types.begin(),
402 checksum_types.end());
403 }
404
405 #ifndef ROCKSDB_LITE
406 static bool ParseOptionHelper(void* opt_address, const OptionType& opt_type,
407 const std::string& value) {
408 switch (opt_type) {
409 case OptionType::kBoolean:
410 *static_cast<bool*>(opt_address) = ParseBoolean("", value);
411 break;
412 case OptionType::kInt:
413 *static_cast<int*>(opt_address) = ParseInt(value);
414 break;
415 case OptionType::kInt32T:
416 *static_cast<int32_t*>(opt_address) = ParseInt32(value);
417 break;
418 case OptionType::kInt64T:
419 PutUnaligned(static_cast<int64_t*>(opt_address), ParseInt64(value));
420 break;
421 case OptionType::kUInt:
422 *static_cast<unsigned int*>(opt_address) = ParseUint32(value);
423 break;
424 case OptionType::kUInt8T:
425 *static_cast<uint8_t*>(opt_address) = ParseUint8(value);
426 break;
427 case OptionType::kUInt32T:
428 *static_cast<uint32_t*>(opt_address) = ParseUint32(value);
429 break;
430 case OptionType::kUInt64T:
431 PutUnaligned(static_cast<uint64_t*>(opt_address), ParseUint64(value));
432 break;
433 case OptionType::kSizeT:
434 PutUnaligned(static_cast<size_t*>(opt_address), ParseSizeT(value));
435 break;
436 case OptionType::kString:
437 *static_cast<std::string*>(opt_address) = value;
438 break;
439 case OptionType::kDouble:
440 *static_cast<double*>(opt_address) = ParseDouble(value);
441 break;
442 case OptionType::kCompactionStyle:
443 return ParseEnum<CompactionStyle>(
444 compaction_style_string_map, value,
445 static_cast<CompactionStyle*>(opt_address));
446 case OptionType::kCompactionPri:
447 return ParseEnum<CompactionPri>(compaction_pri_string_map, value,
448 static_cast<CompactionPri*>(opt_address));
449 case OptionType::kCompressionType:
450 return ParseEnum<CompressionType>(
451 compression_type_string_map, value,
452 static_cast<CompressionType*>(opt_address));
453 case OptionType::kChecksumType:
454 return ParseEnum<ChecksumType>(checksum_type_string_map, value,
455 static_cast<ChecksumType*>(opt_address));
456 case OptionType::kEncodingType:
457 return ParseEnum<EncodingType>(encoding_type_string_map, value,
458 static_cast<EncodingType*>(opt_address));
459 case OptionType::kCompactionStopStyle:
460 return ParseEnum<CompactionStopStyle>(
461 compaction_stop_style_string_map, value,
462 static_cast<CompactionStopStyle*>(opt_address));
463 case OptionType::kEncodedString: {
464 std::string* output_addr = static_cast<std::string*>(opt_address);
465 (Slice(value)).DecodeHex(output_addr);
466 break;
467 }
468 case OptionType::kTemperature: {
469 return ParseEnum<Temperature>(temperature_string_map, value,
470 static_cast<Temperature*>(opt_address));
471 }
472 default:
473 return false;
474 }
475 return true;
476 }
477
478 bool SerializeSingleOptionHelper(const void* opt_address,
479 const OptionType opt_type,
480 std::string* value) {
481 assert(value);
482 switch (opt_type) {
483 case OptionType::kBoolean:
484 *value = *(static_cast<const bool*>(opt_address)) ? "true" : "false";
485 break;
486 case OptionType::kInt:
487 *value = std::to_string(*(static_cast<const int*>(opt_address)));
488 break;
489 case OptionType::kInt32T:
490 *value = std::to_string(*(static_cast<const int32_t*>(opt_address)));
491 break;
492 case OptionType::kInt64T:
493 {
494 int64_t v;
495 GetUnaligned(static_cast<const int64_t*>(opt_address), &v);
496 *value = std::to_string(v);
497 }
498 break;
499 case OptionType::kUInt:
500 *value = std::to_string(*(static_cast<const unsigned int*>(opt_address)));
501 break;
502 case OptionType::kUInt8T:
503 *value = std::to_string(*(static_cast<const uint8_t*>(opt_address)));
504 break;
505 case OptionType::kUInt32T:
506 *value = std::to_string(*(static_cast<const uint32_t*>(opt_address)));
507 break;
508 case OptionType::kUInt64T:
509 {
510 uint64_t v;
511 GetUnaligned(static_cast<const uint64_t*>(opt_address), &v);
512 *value = std::to_string(v);
513 }
514 break;
515 case OptionType::kSizeT:
516 {
517 size_t v;
518 GetUnaligned(static_cast<const size_t*>(opt_address), &v);
519 *value = std::to_string(v);
520 }
521 break;
522 case OptionType::kDouble:
523 *value = std::to_string(*(static_cast<const double*>(opt_address)));
524 break;
525 case OptionType::kString:
526 *value =
527 EscapeOptionString(*(static_cast<const std::string*>(opt_address)));
528 break;
529 case OptionType::kCompactionStyle:
530 return SerializeEnum<CompactionStyle>(
531 compaction_style_string_map,
532 *(static_cast<const CompactionStyle*>(opt_address)), value);
533 case OptionType::kCompactionPri:
534 return SerializeEnum<CompactionPri>(
535 compaction_pri_string_map,
536 *(static_cast<const CompactionPri*>(opt_address)), value);
537 case OptionType::kCompressionType:
538 return SerializeEnum<CompressionType>(
539 compression_type_string_map,
540 *(static_cast<const CompressionType*>(opt_address)), value);
541 break;
542 case OptionType::kChecksumType:
543 return SerializeEnum<ChecksumType>(
544 checksum_type_string_map,
545 *static_cast<const ChecksumType*>(opt_address), value);
546 case OptionType::kEncodingType:
547 return SerializeEnum<EncodingType>(
548 encoding_type_string_map,
549 *static_cast<const EncodingType*>(opt_address), value);
550 case OptionType::kCompactionStopStyle:
551 return SerializeEnum<CompactionStopStyle>(
552 compaction_stop_style_string_map,
553 *static_cast<const CompactionStopStyle*>(opt_address), value);
554 case OptionType::kEncodedString: {
555 const auto* ptr = static_cast<const std::string*>(opt_address);
556 *value = (Slice(*ptr)).ToString(true);
557 break;
558 }
559 case OptionType::kTemperature: {
560 return SerializeEnum<Temperature>(
561 temperature_string_map, *static_cast<const Temperature*>(opt_address),
562 value);
563 }
564 default:
565 return false;
566 }
567 return true;
568 }
569
570 template <typename T>
571 Status ConfigureFromMap(
572 const ConfigOptions& config_options,
573 const std::unordered_map<std::string, std::string>& opt_map,
574 const std::string& option_name, Configurable* config, T* new_opts) {
575 Status s = config->ConfigureFromMap(config_options, opt_map);
576 if (s.ok()) {
577 *new_opts = *(config->GetOptions<T>(option_name));
578 }
579 return s;
580 }
581
582
583 Status StringToMap(const std::string& opts_str,
584 std::unordered_map<std::string, std::string>* opts_map) {
585 assert(opts_map);
586 // Example:
587 // opts_str = "write_buffer_size=1024;max_write_buffer_number=2;"
588 // "nested_opt={opt1=1;opt2=2};max_bytes_for_level_base=100"
589 size_t pos = 0;
590 std::string opts = trim(opts_str);
591 // If the input string starts and ends with "{...}", strip off the brackets
592 while (opts.size() > 2 && opts[0] == '{' && opts[opts.size() - 1] == '}') {
593 opts = trim(opts.substr(1, opts.size() - 2));
594 }
595
596 while (pos < opts.size()) {
597 size_t eq_pos = opts.find_first_of("={};", pos);
598 if (eq_pos == std::string::npos) {
599 return Status::InvalidArgument("Mismatched key value pair, '=' expected");
600 } else if (opts[eq_pos] != '=') {
601 return Status::InvalidArgument("Unexpected char in key");
602 }
603
604 std::string key = trim(opts.substr(pos, eq_pos - pos));
605 if (key.empty()) {
606 return Status::InvalidArgument("Empty key found");
607 }
608
609 std::string value;
610 Status s = OptionTypeInfo::NextToken(opts, ';', eq_pos + 1, &pos, &value);
611 if (!s.ok()) {
612 return s;
613 } else {
614 (*opts_map)[key] = value;
615 if (pos == std::string::npos) {
616 break;
617 } else {
618 pos++;
619 }
620 }
621 }
622
623 return Status::OK();
624 }
625
626
627 Status GetStringFromDBOptions(std::string* opt_string,
628 const DBOptions& db_options,
629 const std::string& delimiter) {
630 ConfigOptions config_options(db_options);
631 config_options.delimiter = delimiter;
632 return GetStringFromDBOptions(config_options, db_options, opt_string);
633 }
634
635 Status GetStringFromDBOptions(const ConfigOptions& config_options,
636 const DBOptions& db_options,
637 std::string* opt_string) {
638 assert(opt_string);
639 opt_string->clear();
640 auto config = DBOptionsAsConfigurable(db_options);
641 return config->GetOptionString(config_options, opt_string);
642 }
643
644
645 Status GetStringFromColumnFamilyOptions(std::string* opt_string,
646 const ColumnFamilyOptions& cf_options,
647 const std::string& delimiter) {
648 ConfigOptions config_options;
649 config_options.delimiter = delimiter;
650 return GetStringFromColumnFamilyOptions(config_options, cf_options,
651 opt_string);
652 }
653
654 Status GetStringFromColumnFamilyOptions(const ConfigOptions& config_options,
655 const ColumnFamilyOptions& cf_options,
656 std::string* opt_string) {
657 const auto config = CFOptionsAsConfigurable(cf_options);
658 return config->GetOptionString(config_options, opt_string);
659 }
660
661 Status GetStringFromCompressionType(std::string* compression_str,
662 CompressionType compression_type) {
663 bool ok = SerializeEnum<CompressionType>(compression_type_string_map,
664 compression_type, compression_str);
665 if (ok) {
666 return Status::OK();
667 } else {
668 return Status::InvalidArgument("Invalid compression types");
669 }
670 }
671
672 Status GetColumnFamilyOptionsFromMap(
673 const ColumnFamilyOptions& base_options,
674 const std::unordered_map<std::string, std::string>& opts_map,
675 ColumnFamilyOptions* new_options, bool input_strings_escaped,
676 bool ignore_unknown_options) {
677 ConfigOptions config_options;
678 config_options.ignore_unknown_options = ignore_unknown_options;
679 config_options.input_strings_escaped = input_strings_escaped;
680 return GetColumnFamilyOptionsFromMap(config_options, base_options, opts_map,
681 new_options);
682 }
683
684 Status GetColumnFamilyOptionsFromMap(
685 const ConfigOptions& config_options,
686 const ColumnFamilyOptions& base_options,
687 const std::unordered_map<std::string, std::string>& opts_map,
688 ColumnFamilyOptions* new_options) {
689 assert(new_options);
690
691 *new_options = base_options;
692
693 const auto config = CFOptionsAsConfigurable(base_options);
694 Status s = ConfigureFromMap<ColumnFamilyOptions>(
695 config_options, opts_map, OptionsHelper::kCFOptionsName, config.get(),
696 new_options);
697 // Translate any errors (NotFound, NotSupported, to InvalidArgument
698 if (s.ok() || s.IsInvalidArgument()) {
699 return s;
700 } else {
701 return Status::InvalidArgument(s.getState());
702 }
703 }
704
705 Status GetColumnFamilyOptionsFromString(
706 const ColumnFamilyOptions& base_options,
707 const std::string& opts_str,
708 ColumnFamilyOptions* new_options) {
709 ConfigOptions config_options;
710 config_options.input_strings_escaped = false;
711 config_options.ignore_unknown_options = false;
712 return GetColumnFamilyOptionsFromString(config_options, base_options,
713 opts_str, new_options);
714 }
715
716 Status GetColumnFamilyOptionsFromString(const ConfigOptions& config_options,
717 const ColumnFamilyOptions& base_options,
718 const std::string& opts_str,
719 ColumnFamilyOptions* new_options) {
720 std::unordered_map<std::string, std::string> opts_map;
721 Status s = StringToMap(opts_str, &opts_map);
722 if (!s.ok()) {
723 *new_options = base_options;
724 return s;
725 }
726 return GetColumnFamilyOptionsFromMap(config_options, base_options, opts_map,
727 new_options);
728 }
729
730 Status GetDBOptionsFromMap(
731 const DBOptions& base_options,
732 const std::unordered_map<std::string, std::string>& opts_map,
733 DBOptions* new_options, bool input_strings_escaped,
734 bool ignore_unknown_options) {
735 ConfigOptions config_options(base_options);
736 config_options.input_strings_escaped = input_strings_escaped;
737 config_options.ignore_unknown_options = ignore_unknown_options;
738 return GetDBOptionsFromMap(config_options, base_options, opts_map,
739 new_options);
740 }
741
742 Status GetDBOptionsFromMap(
743 const ConfigOptions& config_options, const DBOptions& base_options,
744 const std::unordered_map<std::string, std::string>& opts_map,
745 DBOptions* new_options) {
746 assert(new_options);
747 *new_options = base_options;
748 auto config = DBOptionsAsConfigurable(base_options);
749 Status s = ConfigureFromMap<DBOptions>(config_options, opts_map,
750 OptionsHelper::kDBOptionsName,
751 config.get(), new_options);
752 // Translate any errors (NotFound, NotSupported, to InvalidArgument
753 if (s.ok() || s.IsInvalidArgument()) {
754 return s;
755 } else {
756 return Status::InvalidArgument(s.getState());
757 }
758 }
759
760 Status GetDBOptionsFromString(const DBOptions& base_options,
761 const std::string& opts_str,
762 DBOptions* new_options) {
763 ConfigOptions config_options(base_options);
764 config_options.input_strings_escaped = false;
765 config_options.ignore_unknown_options = false;
766
767 return GetDBOptionsFromString(config_options, base_options, opts_str,
768 new_options);
769 }
770
771 Status GetDBOptionsFromString(const ConfigOptions& config_options,
772 const DBOptions& base_options,
773 const std::string& opts_str,
774 DBOptions* new_options) {
775 std::unordered_map<std::string, std::string> opts_map;
776 Status s = StringToMap(opts_str, &opts_map);
777 if (!s.ok()) {
778 *new_options = base_options;
779 return s;
780 }
781 return GetDBOptionsFromMap(config_options, base_options, opts_map,
782 new_options);
783 }
784
785 Status GetOptionsFromString(const Options& base_options,
786 const std::string& opts_str, Options* new_options) {
787 ConfigOptions config_options(base_options);
788 config_options.input_strings_escaped = false;
789 config_options.ignore_unknown_options = false;
790
791 return GetOptionsFromString(config_options, base_options, opts_str,
792 new_options);
793 }
794
795 Status GetOptionsFromString(const ConfigOptions& config_options,
796 const Options& base_options,
797 const std::string& opts_str, Options* new_options) {
798 ColumnFamilyOptions new_cf_options;
799 std::unordered_map<std::string, std::string> unused_opts;
800 std::unordered_map<std::string, std::string> opts_map;
801
802 assert(new_options);
803 *new_options = base_options;
804 Status s = StringToMap(opts_str, &opts_map);
805 if (!s.ok()) {
806 return s;
807 }
808 auto config = DBOptionsAsConfigurable(base_options);
809 s = config->ConfigureFromMap(config_options, opts_map, &unused_opts);
810
811 if (s.ok()) {
812 DBOptions* new_db_options =
813 config->GetOptions<DBOptions>(OptionsHelper::kDBOptionsName);
814 if (!unused_opts.empty()) {
815 s = GetColumnFamilyOptionsFromMap(config_options, base_options,
816 unused_opts, &new_cf_options);
817 if (s.ok()) {
818 *new_options = Options(*new_db_options, new_cf_options);
819 }
820 } else {
821 *new_options = Options(*new_db_options, base_options);
822 }
823 }
824 // Translate any errors (NotFound, NotSupported, to InvalidArgument
825 if (s.ok() || s.IsInvalidArgument()) {
826 return s;
827 } else {
828 return Status::InvalidArgument(s.getState());
829 }
830 }
831
832 std::unordered_map<std::string, EncodingType>
833 OptionsHelper::encoding_type_string_map = {{"kPlain", kPlain},
834 {"kPrefix", kPrefix}};
835
836 std::unordered_map<std::string, CompactionStyle>
837 OptionsHelper::compaction_style_string_map = {
838 {"kCompactionStyleLevel", kCompactionStyleLevel},
839 {"kCompactionStyleUniversal", kCompactionStyleUniversal},
840 {"kCompactionStyleFIFO", kCompactionStyleFIFO},
841 {"kCompactionStyleNone", kCompactionStyleNone}};
842
843 std::unordered_map<std::string, CompactionPri>
844 OptionsHelper::compaction_pri_string_map = {
845 {"kByCompensatedSize", kByCompensatedSize},
846 {"kOldestLargestSeqFirst", kOldestLargestSeqFirst},
847 {"kOldestSmallestSeqFirst", kOldestSmallestSeqFirst},
848 {"kMinOverlappingRatio", kMinOverlappingRatio},
849 {"kRoundRobin", kRoundRobin}};
850
851 std::unordered_map<std::string, CompactionStopStyle>
852 OptionsHelper::compaction_stop_style_string_map = {
853 {"kCompactionStopStyleSimilarSize", kCompactionStopStyleSimilarSize},
854 {"kCompactionStopStyleTotalSize", kCompactionStopStyleTotalSize}};
855
856 std::unordered_map<std::string, Temperature>
857 OptionsHelper::temperature_string_map = {
858 {"kUnknown", Temperature::kUnknown},
859 {"kHot", Temperature::kHot},
860 {"kWarm", Temperature::kWarm},
861 {"kCold", Temperature::kCold}};
862
863 std::unordered_map<std::string, PrepopulateBlobCache>
864 OptionsHelper::prepopulate_blob_cache_string_map = {
865 {"kDisable", PrepopulateBlobCache::kDisable},
866 {"kFlushOnly", PrepopulateBlobCache::kFlushOnly}};
867
868 Status OptionTypeInfo::NextToken(const std::string& opts, char delimiter,
869 size_t pos, size_t* end, std::string* token) {
870 while (pos < opts.size() && isspace(opts[pos])) {
871 ++pos;
872 }
873 // Empty value at the end
874 if (pos >= opts.size()) {
875 *token = "";
876 *end = std::string::npos;
877 return Status::OK();
878 } else if (opts[pos] == '{') {
879 int count = 1;
880 size_t brace_pos = pos + 1;
881 while (brace_pos < opts.size()) {
882 if (opts[brace_pos] == '{') {
883 ++count;
884 } else if (opts[brace_pos] == '}') {
885 --count;
886 if (count == 0) {
887 break;
888 }
889 }
890 ++brace_pos;
891 }
892 // found the matching closing brace
893 if (count == 0) {
894 *token = trim(opts.substr(pos + 1, brace_pos - pos - 1));
895 // skip all whitespace and move to the next delimiter
896 // brace_pos points to the next position after the matching '}'
897 pos = brace_pos + 1;
898 while (pos < opts.size() && isspace(opts[pos])) {
899 ++pos;
900 }
901 if (pos < opts.size() && opts[pos] != delimiter) {
902 return Status::InvalidArgument("Unexpected chars after nested options");
903 }
904 *end = pos;
905 } else {
906 return Status::InvalidArgument(
907 "Mismatched curly braces for nested options");
908 }
909 } else {
910 *end = opts.find(delimiter, pos);
911 if (*end == std::string::npos) {
912 // It either ends with a trailing semi-colon or the last key-value pair
913 *token = trim(opts.substr(pos));
914 } else {
915 *token = trim(opts.substr(pos, *end - pos));
916 }
917 }
918 return Status::OK();
919 }
920
921 Status OptionTypeInfo::Parse(const ConfigOptions& config_options,
922 const std::string& opt_name,
923 const std::string& value, void* opt_ptr) const {
924 if (IsDeprecated()) {
925 return Status::OK();
926 }
927 try {
928 const std::string& opt_value = config_options.input_strings_escaped
929 ? UnescapeOptionString(value)
930 : value;
931
932 if (opt_ptr == nullptr) {
933 return Status::NotFound("Could not find option", opt_name);
934 } else if (parse_func_ != nullptr) {
935 ConfigOptions copy = config_options;
936 copy.invoke_prepare_options = false;
937 void* opt_addr = GetOffset(opt_ptr);
938 return parse_func_(copy, opt_name, opt_value, opt_addr);
939 } else if (ParseOptionHelper(GetOffset(opt_ptr), type_, opt_value)) {
940 return Status::OK();
941 } else if (IsConfigurable()) {
942 // The option is <config>.<name>
943 Configurable* config = AsRawPointer<Configurable>(opt_ptr);
944 if (opt_value.empty()) {
945 return Status::OK();
946 } else if (config == nullptr) {
947 return Status::NotFound("Could not find configurable: ", opt_name);
948 } else {
949 ConfigOptions copy = config_options;
950 copy.ignore_unknown_options = false;
951 copy.invoke_prepare_options = false;
952 if (opt_value.find("=") != std::string::npos) {
953 return config->ConfigureFromString(copy, opt_value);
954 } else {
955 return config->ConfigureOption(copy, opt_name, opt_value);
956 }
957 }
958 } else if (IsByName()) {
959 return Status::NotSupported("Deserializing the option " + opt_name +
960 " is not supported");
961 } else {
962 return Status::InvalidArgument("Error parsing:", opt_name);
963 }
964 } catch (std::exception& e) {
965 return Status::InvalidArgument("Error parsing " + opt_name + ":" +
966 std::string(e.what()));
967 }
968 }
969
970 Status OptionTypeInfo::ParseType(
971 const ConfigOptions& config_options, const std::string& opts_str,
972 const std::unordered_map<std::string, OptionTypeInfo>& type_map,
973 void* opt_addr, std::unordered_map<std::string, std::string>* unused) {
974 std::unordered_map<std::string, std::string> opts_map;
975 Status status = StringToMap(opts_str, &opts_map);
976 if (!status.ok()) {
977 return status;
978 } else {
979 return ParseType(config_options, opts_map, type_map, opt_addr, unused);
980 }
981 }
982
983 Status OptionTypeInfo::ParseType(
984 const ConfigOptions& config_options,
985 const std::unordered_map<std::string, std::string>& opts_map,
986 const std::unordered_map<std::string, OptionTypeInfo>& type_map,
987 void* opt_addr, std::unordered_map<std::string, std::string>* unused) {
988 for (const auto& opts_iter : opts_map) {
989 std::string opt_name;
990 const auto* opt_info = Find(opts_iter.first, type_map, &opt_name);
991 if (opt_info != nullptr) {
992 Status status =
993 opt_info->Parse(config_options, opt_name, opts_iter.second, opt_addr);
994 if (!status.ok()) {
995 return status;
996 }
997 } else if (unused != nullptr) {
998 (*unused)[opts_iter.first] = opts_iter.second;
999 } else if (!config_options.ignore_unknown_options) {
1000 return Status::NotFound("Unrecognized option", opts_iter.first);
1001 }
1002 }
1003 return Status::OK();
1004 }
1005
1006 Status OptionTypeInfo::ParseStruct(
1007 const ConfigOptions& config_options, const std::string& struct_name,
1008 const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
1009 const std::string& opt_name, const std::string& opt_value, void* opt_addr) {
1010 assert(struct_map);
1011 Status status;
1012 if (opt_name == struct_name || EndsWith(opt_name, "." + struct_name)) {
1013 // This option represents the entire struct
1014 std::unordered_map<std::string, std::string> unused;
1015 status =
1016 ParseType(config_options, opt_value, *struct_map, opt_addr, &unused);
1017 if (status.ok() && !unused.empty()) {
1018 status = Status::InvalidArgument(
1019 "Unrecognized option", struct_name + "." + unused.begin()->first);
1020 }
1021 } else if (StartsWith(opt_name, struct_name + ".")) {
1022 // This option represents a nested field in the struct (e.g, struct.field)
1023 std::string elem_name;
1024 const auto opt_info =
1025 Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
1026 if (opt_info != nullptr) {
1027 status = opt_info->Parse(config_options, elem_name, opt_value, opt_addr);
1028 } else {
1029 status = Status::InvalidArgument("Unrecognized option", opt_name);
1030 }
1031 } else {
1032 // This option represents a field in the struct (e.g. field)
1033 std::string elem_name;
1034 const auto opt_info = Find(opt_name, *struct_map, &elem_name);
1035 if (opt_info != nullptr) {
1036 status = opt_info->Parse(config_options, elem_name, opt_value, opt_addr);
1037 } else {
1038 status = Status::InvalidArgument("Unrecognized option",
1039 struct_name + "." + opt_name);
1040 }
1041 }
1042 return status;
1043 }
1044
1045 Status OptionTypeInfo::Serialize(const ConfigOptions& config_options,
1046 const std::string& opt_name,
1047 const void* const opt_ptr,
1048 std::string* opt_value) const {
1049 // If the option is no longer used in rocksdb and marked as deprecated,
1050 // we skip it in the serialization.
1051 if (opt_ptr == nullptr || IsDeprecated()) {
1052 return Status::OK();
1053 } else if (IsEnabled(OptionTypeFlags::kDontSerialize)) {
1054 return Status::NotSupported("Cannot serialize option: ", opt_name);
1055 } else if (serialize_func_ != nullptr) {
1056 const void* opt_addr = GetOffset(opt_ptr);
1057 return serialize_func_(config_options, opt_name, opt_addr, opt_value);
1058 } else if (IsCustomizable()) {
1059 const Customizable* custom = AsRawPointer<Customizable>(opt_ptr);
1060 opt_value->clear();
1061 if (custom == nullptr) {
1062 // We do not have a custom object to serialize.
1063 // If the option is not mutable and we are doing only mutable options,
1064 // we return an empty string (which will cause the option not to be
1065 // printed). Otherwise, we return the "nullptr" string, which will result
1066 // in "option=nullptr" being printed.
1067 if (IsMutable() || !config_options.mutable_options_only) {
1068 *opt_value = kNullptrString;
1069 } else {
1070 *opt_value = "";
1071 }
1072 } else if (IsEnabled(OptionTypeFlags::kStringNameOnly) &&
1073 !config_options.IsDetailed()) {
1074 if (!config_options.mutable_options_only || IsMutable()) {
1075 *opt_value = custom->GetId();
1076 }
1077 } else {
1078 ConfigOptions embedded = config_options;
1079 embedded.delimiter = ";";
1080 // If this option is mutable, everything inside it should be considered
1081 // mutable
1082 if (IsMutable()) {
1083 embedded.mutable_options_only = false;
1084 }
1085 std::string value = custom->ToString(embedded);
1086 if (!embedded.mutable_options_only ||
1087 value.find("=") != std::string::npos) {
1088 *opt_value = value;
1089 } else {
1090 *opt_value = "";
1091 }
1092 }
1093 return Status::OK();
1094 } else if (IsConfigurable()) {
1095 const Configurable* config = AsRawPointer<Configurable>(opt_ptr);
1096 if (config != nullptr) {
1097 ConfigOptions embedded = config_options;
1098 embedded.delimiter = ";";
1099 *opt_value = config->ToString(embedded);
1100 }
1101 return Status::OK();
1102 } else if (config_options.mutable_options_only && !IsMutable()) {
1103 return Status::OK();
1104 } else if (SerializeSingleOptionHelper(GetOffset(opt_ptr), type_,
1105 opt_value)) {
1106 return Status::OK();
1107 } else {
1108 return Status::InvalidArgument("Cannot serialize option: ", opt_name);
1109 }
1110 }
1111
1112 Status OptionTypeInfo::SerializeType(
1113 const ConfigOptions& config_options,
1114 const std::unordered_map<std::string, OptionTypeInfo>& type_map,
1115 const void* opt_addr, std::string* result) {
1116 Status status;
1117 for (const auto& iter : type_map) {
1118 std::string single;
1119 const auto& opt_info = iter.second;
1120 if (opt_info.ShouldSerialize()) {
1121 status =
1122 opt_info.Serialize(config_options, iter.first, opt_addr, &single);
1123 if (!status.ok()) {
1124 return status;
1125 } else {
1126 result->append(iter.first + "=" + single + config_options.delimiter);
1127 }
1128 }
1129 }
1130 return status;
1131 }
1132
1133 Status OptionTypeInfo::SerializeStruct(
1134 const ConfigOptions& config_options, const std::string& struct_name,
1135 const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
1136 const std::string& opt_name, const void* opt_addr, std::string* value) {
1137 assert(struct_map);
1138 Status status;
1139 if (EndsWith(opt_name, struct_name)) {
1140 // We are going to write the struct as "{ prop1=value1; prop2=value2;}.
1141 // Set the delimiter to ";" so that the everything will be on one line.
1142 ConfigOptions embedded = config_options;
1143 embedded.delimiter = ";";
1144
1145 // This option represents the entire struct
1146 std::string result;
1147 status = SerializeType(embedded, *struct_map, opt_addr, &result);
1148 if (!status.ok()) {
1149 return status;
1150 } else {
1151 *value = "{" + result + "}";
1152 }
1153 } else if (StartsWith(opt_name, struct_name + ".")) {
1154 // This option represents a nested field in the struct (e.g, struct.field)
1155 std::string elem_name;
1156 const auto opt_info =
1157 Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
1158 if (opt_info != nullptr) {
1159 status = opt_info->Serialize(config_options, elem_name, opt_addr, value);
1160 } else {
1161 status = Status::InvalidArgument("Unrecognized option", opt_name);
1162 }
1163 } else {
1164 // This option represents a field in the struct (e.g. field)
1165 std::string elem_name;
1166 const auto opt_info = Find(opt_name, *struct_map, &elem_name);
1167 if (opt_info == nullptr) {
1168 status = Status::InvalidArgument("Unrecognized option", opt_name);
1169 } else if (opt_info->ShouldSerialize()) {
1170 status = opt_info->Serialize(config_options, opt_name + "." + elem_name,
1171 opt_addr, value);
1172 }
1173 }
1174 return status;
1175 }
1176
1177 template <typename T>
1178 bool IsOptionEqual(const void* offset1, const void* offset2) {
1179 return (*static_cast<const T*>(offset1) == *static_cast<const T*>(offset2));
1180 }
1181
1182 static bool AreEqualDoubles(const double a, const double b) {
1183 return (fabs(a - b) < 0.00001);
1184 }
1185
1186 static bool AreOptionsEqual(OptionType type, const void* this_offset,
1187 const void* that_offset) {
1188 switch (type) {
1189 case OptionType::kBoolean:
1190 return IsOptionEqual<bool>(this_offset, that_offset);
1191 case OptionType::kInt:
1192 return IsOptionEqual<int>(this_offset, that_offset);
1193 case OptionType::kUInt:
1194 return IsOptionEqual<unsigned int>(this_offset, that_offset);
1195 case OptionType::kInt32T:
1196 return IsOptionEqual<int32_t>(this_offset, that_offset);
1197 case OptionType::kInt64T: {
1198 int64_t v1, v2;
1199 GetUnaligned(static_cast<const int64_t*>(this_offset), &v1);
1200 GetUnaligned(static_cast<const int64_t*>(that_offset), &v2);
1201 return (v1 == v2);
1202 }
1203 case OptionType::kUInt8T:
1204 return IsOptionEqual<uint8_t>(this_offset, that_offset);
1205 case OptionType::kUInt32T:
1206 return IsOptionEqual<uint32_t>(this_offset, that_offset);
1207 case OptionType::kUInt64T: {
1208 uint64_t v1, v2;
1209 GetUnaligned(static_cast<const uint64_t*>(this_offset), &v1);
1210 GetUnaligned(static_cast<const uint64_t*>(that_offset), &v2);
1211 return (v1 == v2);
1212 }
1213 case OptionType::kSizeT: {
1214 size_t v1, v2;
1215 GetUnaligned(static_cast<const size_t*>(this_offset), &v1);
1216 GetUnaligned(static_cast<const size_t*>(that_offset), &v2);
1217 return (v1 == v2);
1218 }
1219 case OptionType::kString:
1220 return IsOptionEqual<std::string>(this_offset, that_offset);
1221 case OptionType::kDouble:
1222 return AreEqualDoubles(*static_cast<const double*>(this_offset),
1223 *static_cast<const double*>(that_offset));
1224 case OptionType::kCompactionStyle:
1225 return IsOptionEqual<CompactionStyle>(this_offset, that_offset);
1226 case OptionType::kCompactionStopStyle:
1227 return IsOptionEqual<CompactionStopStyle>(this_offset, that_offset);
1228 case OptionType::kCompactionPri:
1229 return IsOptionEqual<CompactionPri>(this_offset, that_offset);
1230 case OptionType::kCompressionType:
1231 return IsOptionEqual<CompressionType>(this_offset, that_offset);
1232 case OptionType::kChecksumType:
1233 return IsOptionEqual<ChecksumType>(this_offset, that_offset);
1234 case OptionType::kEncodingType:
1235 return IsOptionEqual<EncodingType>(this_offset, that_offset);
1236 case OptionType::kEncodedString:
1237 return IsOptionEqual<std::string>(this_offset, that_offset);
1238 case OptionType::kTemperature:
1239 return IsOptionEqual<Temperature>(this_offset, that_offset);
1240 default:
1241 return false;
1242 } // End switch
1243 }
1244
1245 bool OptionTypeInfo::AreEqual(const ConfigOptions& config_options,
1246 const std::string& opt_name,
1247 const void* const this_ptr,
1248 const void* const that_ptr,
1249 std::string* mismatch) const {
1250 auto level = GetSanityLevel();
1251 if (!config_options.IsCheckEnabled(level)) {
1252 return true; // If the sanity level is not being checked, skip it
1253 }
1254 if (this_ptr == nullptr || that_ptr == nullptr) {
1255 if (this_ptr == that_ptr) {
1256 return true;
1257 }
1258 } else if (equals_func_ != nullptr) {
1259 const void* this_addr = GetOffset(this_ptr);
1260 const void* that_addr = GetOffset(that_ptr);
1261 if (equals_func_(config_options, opt_name, this_addr, that_addr,
1262 mismatch)) {
1263 return true;
1264 }
1265 } else {
1266 const void* this_addr = GetOffset(this_ptr);
1267 const void* that_addr = GetOffset(that_ptr);
1268 if (AreOptionsEqual(type_, this_addr, that_addr)) {
1269 return true;
1270 } else if (IsConfigurable()) {
1271 const auto* this_config = AsRawPointer<Configurable>(this_ptr);
1272 const auto* that_config = AsRawPointer<Configurable>(that_ptr);
1273 if (this_config == that_config) {
1274 return true;
1275 } else if (this_config != nullptr && that_config != nullptr) {
1276 std::string bad_name;
1277 bool matches;
1278 if (level < config_options.sanity_level) {
1279 ConfigOptions copy = config_options;
1280 copy.sanity_level = level;
1281 matches = this_config->AreEquivalent(copy, that_config, &bad_name);
1282 } else {
1283 matches = this_config->AreEquivalent(config_options, that_config,
1284 &bad_name);
1285 }
1286 if (!matches) {
1287 *mismatch = opt_name + "." + bad_name;
1288 }
1289 return matches;
1290 }
1291 }
1292 }
1293 if (mismatch->empty()) {
1294 *mismatch = opt_name;
1295 }
1296 return false;
1297 }
1298
1299 bool OptionTypeInfo::TypesAreEqual(
1300 const ConfigOptions& config_options,
1301 const std::unordered_map<std::string, OptionTypeInfo>& type_map,
1302 const void* this_addr, const void* that_addr, std::string* mismatch) {
1303 for (const auto& iter : type_map) {
1304 const auto& opt_info = iter.second;
1305 if (!opt_info.AreEqual(config_options, iter.first, this_addr, that_addr,
1306 mismatch)) {
1307 return false;
1308 }
1309 }
1310 return true;
1311 }
1312
1313 bool OptionTypeInfo::StructsAreEqual(
1314 const ConfigOptions& config_options, const std::string& struct_name,
1315 const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
1316 const std::string& opt_name, const void* this_addr, const void* that_addr,
1317 std::string* mismatch) {
1318 assert(struct_map);
1319 bool matches = true;
1320 std::string result;
1321 if (EndsWith(opt_name, struct_name)) {
1322 // This option represents the entire struct
1323 matches = TypesAreEqual(config_options, *struct_map, this_addr, that_addr,
1324 &result);
1325 if (!matches) {
1326 *mismatch = struct_name + "." + result;
1327 return false;
1328 }
1329 } else if (StartsWith(opt_name, struct_name + ".")) {
1330 // This option represents a nested field in the struct (e.g, struct.field)
1331 std::string elem_name;
1332 const auto opt_info =
1333 Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
1334 assert(opt_info);
1335 if (opt_info == nullptr) {
1336 *mismatch = opt_name;
1337 matches = false;
1338 } else if (!opt_info->AreEqual(config_options, elem_name, this_addr,
1339 that_addr, &result)) {
1340 matches = false;
1341 *mismatch = struct_name + "." + result;
1342 }
1343 } else {
1344 // This option represents a field in the struct (e.g. field)
1345 std::string elem_name;
1346 const auto opt_info = Find(opt_name, *struct_map, &elem_name);
1347 assert(opt_info);
1348 if (opt_info == nullptr) {
1349 *mismatch = struct_name + "." + opt_name;
1350 matches = false;
1351 } else if (!opt_info->AreEqual(config_options, elem_name, this_addr,
1352 that_addr, &result)) {
1353 matches = false;
1354 *mismatch = struct_name + "." + result;
1355 }
1356 }
1357 return matches;
1358 }
1359
1360 bool MatchesOptionsTypeFromMap(
1361 const ConfigOptions& config_options,
1362 const std::unordered_map<std::string, OptionTypeInfo>& type_map,
1363 const void* const this_ptr, const void* const that_ptr,
1364 std::string* mismatch) {
1365 for (auto& pair : type_map) {
1366 // We skip checking deprecated variables as they might
1367 // contain random values since they might not be initialized
1368 if (config_options.IsCheckEnabled(pair.second.GetSanityLevel())) {
1369 if (!pair.second.AreEqual(config_options, pair.first, this_ptr, that_ptr,
1370 mismatch) &&
1371 !pair.second.AreEqualByName(config_options, pair.first, this_ptr,
1372 that_ptr)) {
1373 return false;
1374 }
1375 }
1376 }
1377 return true;
1378 }
1379
1380 bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
1381 const std::string& opt_name,
1382 const void* const this_ptr,
1383 const void* const that_ptr) const {
1384 if (IsByName()) {
1385 std::string that_value;
1386 if (Serialize(config_options, opt_name, that_ptr, &that_value).ok()) {
1387 return AreEqualByName(config_options, opt_name, this_ptr, that_value);
1388 }
1389 }
1390 return false;
1391 }
1392
1393 bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
1394 const std::string& opt_name,
1395 const void* const opt_ptr,
1396 const std::string& that_value) const {
1397 std::string this_value;
1398 if (!IsByName()) {
1399 return false;
1400 } else if (!Serialize(config_options, opt_name, opt_ptr, &this_value).ok()) {
1401 return false;
1402 } else if (IsEnabled(OptionVerificationType::kByNameAllowFromNull)) {
1403 if (that_value == kNullptrString) {
1404 return true;
1405 }
1406 } else if (IsEnabled(OptionVerificationType::kByNameAllowNull)) {
1407 if (that_value == kNullptrString) {
1408 return true;
1409 }
1410 }
1411 return (this_value == that_value);
1412 }
1413
1414 Status OptionTypeInfo::Prepare(const ConfigOptions& config_options,
1415 const std::string& name, void* opt_ptr) const {
1416 if (ShouldPrepare()) {
1417 if (prepare_func_ != nullptr) {
1418 void* opt_addr = GetOffset(opt_ptr);
1419 return prepare_func_(config_options, name, opt_addr);
1420 } else if (IsConfigurable()) {
1421 Configurable* config = AsRawPointer<Configurable>(opt_ptr);
1422 if (config != nullptr) {
1423 return config->PrepareOptions(config_options);
1424 } else if (!CanBeNull()) {
1425 return Status::NotFound("Missing configurable object", name);
1426 }
1427 }
1428 }
1429 return Status::OK();
1430 }
1431
1432 Status OptionTypeInfo::Validate(const DBOptions& db_opts,
1433 const ColumnFamilyOptions& cf_opts,
1434 const std::string& name,
1435 const void* opt_ptr) const {
1436 if (ShouldValidate()) {
1437 if (validate_func_ != nullptr) {
1438 const void* opt_addr = GetOffset(opt_ptr);
1439 return validate_func_(db_opts, cf_opts, name, opt_addr);
1440 } else if (IsConfigurable()) {
1441 const Configurable* config = AsRawPointer<Configurable>(opt_ptr);
1442 if (config != nullptr) {
1443 return config->ValidateOptions(db_opts, cf_opts);
1444 } else if (!CanBeNull()) {
1445 return Status::NotFound("Missing configurable object", name);
1446 }
1447 }
1448 }
1449 return Status::OK();
1450 }
1451
1452 const OptionTypeInfo* OptionTypeInfo::Find(
1453 const std::string& opt_name,
1454 const std::unordered_map<std::string, OptionTypeInfo>& opt_map,
1455 std::string* elem_name) {
1456 const auto iter = opt_map.find(opt_name); // Look up the value in the map
1457 if (iter != opt_map.end()) { // Found the option in the map
1458 *elem_name = opt_name; // Return the name
1459 return &(iter->second); // Return the contents of the iterator
1460 } else {
1461 auto idx = opt_name.find("."); // Look for a separator
1462 if (idx > 0 && idx != std::string::npos) { // We found a separator
1463 auto siter =
1464 opt_map.find(opt_name.substr(0, idx)); // Look for the short name
1465 if (siter != opt_map.end()) { // We found the short name
1466 if (siter->second.IsStruct() || // If the object is a struct
1467 siter->second.IsConfigurable()) { // or a Configurable
1468 *elem_name = opt_name.substr(idx + 1); // Return the rest
1469 return &(siter->second); // Return the contents of the iterator
1470 }
1471 }
1472 }
1473 }
1474 return nullptr;
1475 }
1476 #endif // !ROCKSDB_LITE
1477
1478 } // namespace ROCKSDB_NAMESPACE