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 
6 #include "rocksdb/configurable.h"
7 
8 #include "logging/logging.h"
9 #include "options/configurable_helper.h"
10 #include "options/options_helper.h"
11 #include "rocksdb/customizable.h"
12 #include "rocksdb/status.h"
13 #include "rocksdb/utilities/object_registry.h"
14 #include "rocksdb/utilities/options_type.h"
15 #include "util/coding.h"
16 #include "util/string_util.h"
17 
18 namespace ROCKSDB_NAMESPACE {
19 
RegisterOptions(const std::string & name,void * opt_ptr,const std::unordered_map<std::string,OptionTypeInfo> * type_map)20 void Configurable::RegisterOptions(
21     const std::string& name, void* opt_ptr,
22     const std::unordered_map<std::string, OptionTypeInfo>* type_map) {
23   RegisteredOptions opts;
24   opts.name = name;
25 #ifndef ROCKSDB_LITE
26   opts.type_map = type_map;
27 #else
28   (void)type_map;
29 #endif  // ROCKSDB_LITE
30   opts.opt_ptr = opt_ptr;
31   options_.emplace_back(opts);
32 }
33 
34 //*************************************************************************
35 //
36 //       Methods for Initializing and Validating Configurable Objects
37 //
38 //*************************************************************************
39 
PrepareOptions(const ConfigOptions & opts)40 Status Configurable::PrepareOptions(const ConfigOptions& opts) {
41   // We ignore the invoke_prepare_options here intentionally,
42   // as if you are here, you must have called PrepareOptions explicitly.
43   Status status = Status::OK();
44 #ifndef ROCKSDB_LITE
45   for (auto opt_iter : options_) {
46     for (auto map_iter : *(opt_iter.type_map)) {
47       auto& opt_info = map_iter.second;
48       if (!opt_info.IsDeprecated() && !opt_info.IsAlias() &&
49           opt_info.IsConfigurable()) {
50         if (!opt_info.IsEnabled(OptionTypeFlags::kDontPrepare)) {
51           Configurable* config =
52               opt_info.AsRawPointer<Configurable>(opt_iter.opt_ptr);
53           if (config != nullptr) {
54             status = config->PrepareOptions(opts);
55             if (!status.ok()) {
56               return status;
57             }
58           } else if (!opt_info.CanBeNull()) {
59             status =
60                 Status::NotFound("Missing configurable object", map_iter.first);
61           }
62         }
63       }
64     }
65   }
66 #else
67   (void)opts;
68 #endif  // ROCKSDB_LITE
69   return status;
70 }
71 
ValidateOptions(const DBOptions & db_opts,const ColumnFamilyOptions & cf_opts) const72 Status Configurable::ValidateOptions(const DBOptions& db_opts,
73                                      const ColumnFamilyOptions& cf_opts) const {
74   Status status;
75 #ifndef ROCKSDB_LITE
76   for (auto opt_iter : options_) {
77     for (auto map_iter : *(opt_iter.type_map)) {
78       auto& opt_info = map_iter.second;
79       if (!opt_info.IsDeprecated() && !opt_info.IsAlias()) {
80         if (opt_info.IsConfigurable()) {
81           const Configurable* config =
82               opt_info.AsRawPointer<Configurable>(opt_iter.opt_ptr);
83           if (config != nullptr) {
84             status = config->ValidateOptions(db_opts, cf_opts);
85           } else if (!opt_info.CanBeNull()) {
86             status =
87                 Status::NotFound("Missing configurable object", map_iter.first);
88           }
89           if (!status.ok()) {
90             return status;
91           }
92         }
93       }
94     }
95   }
96 #else
97   (void)db_opts;
98   (void)cf_opts;
99 #endif  // ROCKSDB_LITE
100   return status;
101 }
102 
103 /*********************************************************************************/
104 /*                                                                               */
105 /*       Methods for Retrieving Options from Configurables */
106 /*                                                                               */
107 /*********************************************************************************/
108 
GetOptionsPtr(const std::string & name) const109 const void* Configurable::GetOptionsPtr(const std::string& name) const {
110   for (auto o : options_) {
111     if (o.name == name) {
112       return o.opt_ptr;
113     }
114   }
115   return nullptr;
116 }
117 
GetOptionName(const std::string & opt_name) const118 std::string Configurable::GetOptionName(const std::string& opt_name) const {
119   return opt_name;
120 }
121 
122 #ifndef ROCKSDB_LITE
FindOption(const std::vector<Configurable::RegisteredOptions> & options,const std::string & short_name,std::string * opt_name,void ** opt_ptr)123 const OptionTypeInfo* ConfigurableHelper::FindOption(
124     const std::vector<Configurable::RegisteredOptions>& options,
125     const std::string& short_name, std::string* opt_name, void** opt_ptr) {
126   for (auto iter : options) {
127     const auto opt_info =
128         OptionTypeInfo::Find(short_name, *(iter.type_map), opt_name);
129     if (opt_info != nullptr) {
130       *opt_ptr = iter.opt_ptr;
131       return opt_info;
132     }
133   }
134   return nullptr;
135 }
136 #endif  // ROCKSDB_LITE
137 
138 //*************************************************************************
139 //
140 //       Methods for Configuring Options from Strings/Name-Value Pairs/Maps
141 //
142 //*************************************************************************
143 
ConfigureFromMap(const ConfigOptions & config_options,const std::unordered_map<std::string,std::string> & opts_map)144 Status Configurable::ConfigureFromMap(
145     const ConfigOptions& config_options,
146     const std::unordered_map<std::string, std::string>& opts_map) {
147   Status s = ConfigureFromMap(config_options, opts_map, nullptr);
148   return s;
149 }
150 
ConfigureFromMap(const ConfigOptions & config_options,const std::unordered_map<std::string,std::string> & opts_map,std::unordered_map<std::string,std::string> * unused)151 Status Configurable::ConfigureFromMap(
152     const ConfigOptions& config_options,
153     const std::unordered_map<std::string, std::string>& opts_map,
154     std::unordered_map<std::string, std::string>* unused) {
155   return ConfigureOptions(config_options, opts_map, unused);
156 }
157 
ConfigureOptions(const ConfigOptions & config_options,const std::unordered_map<std::string,std::string> & opts_map,std::unordered_map<std::string,std::string> * unused)158 Status Configurable::ConfigureOptions(
159     const ConfigOptions& config_options,
160     const std::unordered_map<std::string, std::string>& opts_map,
161     std::unordered_map<std::string, std::string>* unused) {
162   std::string curr_opts;
163   Status s;
164   if (!opts_map.empty()) {
165     // There are options in the map.
166     // Save the current configuration in curr_opts and then configure the
167     // options, but do not prepare them now.  We will do all the prepare when
168     // the configuration is complete.
169     ConfigOptions copy = config_options;
170     copy.invoke_prepare_options = false;
171 #ifndef ROCKSDB_LITE
172     if (!config_options.ignore_unknown_options) {
173       // If we are not ignoring unused, get the defaults in case we need to
174       // reset
175       copy.depth = ConfigOptions::kDepthDetailed;
176       copy.delimiter = "; ";
177       GetOptionString(copy, &curr_opts).PermitUncheckedError();
178     }
179 #endif  // ROCKSDB_LITE
180 
181     s = ConfigurableHelper::ConfigureOptions(copy, *this, opts_map, unused);
182   }
183   if (config_options.invoke_prepare_options && s.ok()) {
184     s = PrepareOptions(config_options);
185   }
186 #ifndef ROCKSDB_LITE
187   if (!s.ok() && !curr_opts.empty()) {
188     ConfigOptions reset = config_options;
189     reset.ignore_unknown_options = true;
190     reset.invoke_prepare_options = true;
191     reset.ignore_unsupported_options = true;
192     // There are some options to reset from this current error
193     ConfigureFromString(reset, curr_opts).PermitUncheckedError();
194   }
195 #endif  // ROCKSDB_LITE
196   return s;
197 }
198 
ParseStringOptions(const ConfigOptions &,const std::string &)199 Status Configurable::ParseStringOptions(const ConfigOptions& /*config_options*/,
200                                         const std::string& /*opts_str*/) {
201   return Status::OK();
202 }
203 
ConfigureFromString(const ConfigOptions & config_options,const std::string & opts_str)204 Status Configurable::ConfigureFromString(const ConfigOptions& config_options,
205                                          const std::string& opts_str) {
206   Status s;
207   if (!opts_str.empty()) {
208 #ifndef ROCKSDB_LITE
209     if (opts_str.find(';') != std::string::npos ||
210         opts_str.find('=') != std::string::npos) {
211       std::unordered_map<std::string, std::string> opt_map;
212       s = StringToMap(opts_str, &opt_map);
213       if (s.ok()) {
214         s = ConfigureFromMap(config_options, opt_map, nullptr);
215       }
216     } else {
217 #endif  // ROCKSDB_LITE
218       s = ParseStringOptions(config_options, opts_str);
219       if (s.ok() && config_options.invoke_prepare_options) {
220         s = PrepareOptions(config_options);
221       }
222 #ifndef ROCKSDB_LITE
223     }
224 #endif  // ROCKSDB_LITE
225   } else if (config_options.invoke_prepare_options) {
226     s = PrepareOptions(config_options);
227   } else {
228     s = Status::OK();
229   }
230   return s;
231 }
232 
233 #ifndef ROCKSDB_LITE
234 /**
235  * Sets the value of the named property to the input value, returning OK on
236  * succcess.
237  */
ConfigureOption(const ConfigOptions & config_options,const std::string & name,const std::string & value)238 Status Configurable::ConfigureOption(const ConfigOptions& config_options,
239                                      const std::string& name,
240                                      const std::string& value) {
241   return ConfigurableHelper::ConfigureSingleOption(config_options, *this, name,
242                                                    value);
243 }
244 
245 /**
246  * Looks for the named option amongst the options for this type and sets
247  * the value for it to be the input value.
248  * If the name was found, found_option will be set to true and the resulting
249  * status should be returned.
250  */
251 
ParseOption(const ConfigOptions & config_options,const OptionTypeInfo & opt_info,const std::string & opt_name,const std::string & opt_value,void * opt_ptr)252 Status Configurable::ParseOption(const ConfigOptions& config_options,
253                                  const OptionTypeInfo& opt_info,
254                                  const std::string& opt_name,
255                                  const std::string& opt_value, void* opt_ptr) {
256   if (opt_info.IsMutable()) {
257     if (config_options.mutable_options_only) {
258       // This option is mutable. Treat all of its children as mutable as well
259       ConfigOptions copy = config_options;
260       copy.mutable_options_only = false;
261       return opt_info.Parse(copy, opt_name, opt_value, opt_ptr);
262     } else {
263       return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr);
264     }
265   } else if (config_options.mutable_options_only) {
266     return Status::InvalidArgument("Option not changeable: " + opt_name);
267   } else {
268     return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr);
269   }
270 }
271 
272 #endif  // ROCKSDB_LITE
273 
ConfigureOptions(const ConfigOptions & config_options,Configurable & configurable,const std::unordered_map<std::string,std::string> & opts_map,std::unordered_map<std::string,std::string> * unused)274 Status ConfigurableHelper::ConfigureOptions(
275     const ConfigOptions& config_options, Configurable& configurable,
276     const std::unordered_map<std::string, std::string>& opts_map,
277     std::unordered_map<std::string, std::string>* unused) {
278   std::unordered_map<std::string, std::string> remaining = opts_map;
279   Status s = Status::OK();
280   if (!opts_map.empty()) {
281 #ifndef ROCKSDB_LITE
282     for (const auto& iter : configurable.options_) {
283       s = ConfigureSomeOptions(config_options, configurable, *(iter.type_map),
284                                &remaining, iter.opt_ptr);
285       if (remaining.empty()) {  // Are there more options left?
286         break;
287       } else if (!s.ok()) {
288         break;
289       }
290     }
291 #else
292     (void)configurable;
293     if (!config_options.ignore_unknown_options) {
294       s = Status::NotSupported("ConfigureFromMap not supported in LITE mode");
295     }
296 #endif  // ROCKSDB_LITE
297   }
298   if (unused != nullptr && !remaining.empty()) {
299     unused->insert(remaining.begin(), remaining.end());
300   }
301   if (config_options.ignore_unknown_options) {
302     s = Status::OK();
303   } else if (s.ok() && unused == nullptr && !remaining.empty()) {
304     s = Status::NotFound("Could not find option: ", remaining.begin()->first);
305   }
306   return s;
307 }
308 
309 #ifndef ROCKSDB_LITE
310 /**
311  * Updates the object with the named-value property values, returning OK on
312  * succcess. Any properties that were found are removed from the options list;
313  * upon return only options that were not found in this opt_map remain.
314 
315  * Returns:
316  * -  OK if ignore_unknown_options is set
317  * - InvalidArgument, if any option was invalid
318  * - NotSupported, if any option is unsupported and ignore_unsupported_options
319  is OFF
320  * - OK, if no option was invalid or not supported (or ignored)
321  */
ConfigureSomeOptions(const ConfigOptions & config_options,Configurable & configurable,const std::unordered_map<std::string,OptionTypeInfo> & type_map,std::unordered_map<std::string,std::string> * options,void * opt_ptr)322 Status ConfigurableHelper::ConfigureSomeOptions(
323     const ConfigOptions& config_options, Configurable& configurable,
324     const std::unordered_map<std::string, OptionTypeInfo>& type_map,
325     std::unordered_map<std::string, std::string>* options, void* opt_ptr) {
326   Status result = Status::OK();  // The last non-OK result (if any)
327   Status notsup = Status::OK();  // The last NotSupported result (if any)
328   std::string elem_name;
329   int found = 1;
330   std::unordered_set<std::string> unsupported;
331   // While there are unused properties and we processed at least one,
332   // go through the remaining unused properties and attempt to configure them.
333   while (found > 0 && !options->empty()) {
334     found = 0;
335     notsup = Status::OK();
336     for (auto it = options->begin(); it != options->end();) {
337       const std::string& opt_name = configurable.GetOptionName(it->first);
338       const std::string& opt_value = it->second;
339       const auto opt_info =
340           OptionTypeInfo::Find(opt_name, type_map, &elem_name);
341       if (opt_info == nullptr) {  // Did not find the option.  Skip it
342         ++it;
343       } else {
344         Status s = ConfigureOption(config_options, configurable, *opt_info,
345                                    opt_name, elem_name, opt_value, opt_ptr);
346         if (s.IsNotFound()) {
347           ++it;
348         } else if (s.IsNotSupported()) {
349           notsup = s;
350           unsupported.insert(it->first);
351           ++it;  // Skip it for now
352         } else {
353           found++;
354           it = options->erase(it);
355           if (!s.ok()) {
356             result = s;
357           }
358         }
359       }
360     }  // End for all remaining options
361   }    // End while found one or options remain
362 
363   // Now that we have been through the list, remove any unsupported
364   for (auto u : unsupported) {
365     auto it = options->find(u);
366     if (it != options->end()) {
367       options->erase(it);
368     }
369   }
370   if (config_options.ignore_unknown_options) {
371     if (!result.ok()) result.PermitUncheckedError();
372     if (!notsup.ok()) notsup.PermitUncheckedError();
373     return Status::OK();
374   } else if (!result.ok()) {
375     if (!notsup.ok()) notsup.PermitUncheckedError();
376     return result;
377   } else if (config_options.ignore_unsupported_options) {
378     if (!notsup.ok()) notsup.PermitUncheckedError();
379     return Status::OK();
380   } else {
381     return notsup;
382   }
383 }
384 
ConfigureSingleOption(const ConfigOptions & config_options,Configurable & configurable,const std::string & name,const std::string & value)385 Status ConfigurableHelper::ConfigureSingleOption(
386     const ConfigOptions& config_options, Configurable& configurable,
387     const std::string& name, const std::string& value) {
388   const std::string& opt_name = configurable.GetOptionName(name);
389   std::string elem_name;
390   void* opt_ptr = nullptr;
391   const auto opt_info =
392       FindOption(configurable.options_, opt_name, &elem_name, &opt_ptr);
393   if (opt_info == nullptr) {
394     return Status::NotFound("Could not find option: ", name);
395   } else {
396     return ConfigureOption(config_options, configurable, *opt_info, opt_name,
397                            elem_name, value, opt_ptr);
398   }
399 }
ConfigureCustomizableOption(const ConfigOptions & config_options,Configurable & configurable,const OptionTypeInfo & opt_info,const std::string & opt_name,const std::string & name,const std::string & value,void * opt_ptr)400 Status ConfigurableHelper::ConfigureCustomizableOption(
401     const ConfigOptions& config_options, Configurable& configurable,
402     const OptionTypeInfo& opt_info, const std::string& opt_name,
403     const std::string& name, const std::string& value, void* opt_ptr) {
404   Customizable* custom = opt_info.AsRawPointer<Customizable>(opt_ptr);
405   ConfigOptions copy = config_options;
406   if (opt_info.IsMutable()) {
407     // This option is mutable. Pass that property on to any subsequent calls
408     copy.mutable_options_only = false;
409   }
410 
411   if (opt_info.IsMutable() || !config_options.mutable_options_only) {
412     // Either the option is mutable, or we are processing all of the options
413     if (opt_name == name || name == OptionTypeInfo::kIdPropName() ||
414         EndsWith(opt_name, OptionTypeInfo::kIdPropSuffix())) {
415       return configurable.ParseOption(copy, opt_info, name, value, opt_ptr);
416     } else if (value.empty()) {
417       return Status::OK();
418     } else if (custom == nullptr || !StartsWith(name, custom->GetId() + ".")) {
419       return configurable.ParseOption(copy, opt_info, name, value, opt_ptr);
420     } else if (value.find("=") != std::string::npos) {
421       return custom->ConfigureFromString(copy, value);
422     } else {
423       return custom->ConfigureOption(copy, name, value);
424     }
425   } else {
426     // We are processing immutable options, which means that we cannot change
427     // the Customizable object itself, but could change its mutable properties.
428     // Check to make sure that nothing is trying to change the Customizable
429     if (custom == nullptr) {
430       // We do not have a Customizable to configure.  This is OK if the
431       // value is empty (nothing being configured) but an error otherwise
432       if (value.empty()) {
433         return Status::OK();
434       } else {
435         return Status::InvalidArgument("Option not changeable: " + opt_name);
436       }
437     } else if (EndsWith(opt_name, OptionTypeInfo::kIdPropSuffix()) ||
438                name == OptionTypeInfo::kIdPropName()) {
439       // We have a property of the form "id=value" or "table.id=value"
440       // This is OK if we ID/value matches the current customizable object
441       if (custom->GetId() == value) {
442         return Status::OK();
443       } else {
444         return Status::InvalidArgument("Option not changeable: " + opt_name);
445       }
446     } else if (opt_name == name) {
447       // The properties are of one of forms:
448       //    name = { id = id; prop1 = value1; ... }
449       //    name = { prop1=value1; prop2=value2; ... }
450       //    name = ID
451       // Convert the value to a map and extract the ID
452       // If the ID does not match that of the current customizable, return an
453       // error. Otherwise, update the current customizable via the properties
454       // map
455       std::unordered_map<std::string, std::string> props;
456       std::string id;
457       Status s =
458           Configurable::GetOptionsMap(value, custom->GetId(), &id, &props);
459       if (!s.ok()) {
460         return s;
461       } else if (custom->GetId() != id) {
462         return Status::InvalidArgument("Option not changeable: " + opt_name);
463       } else if (props.empty()) {
464         return Status::OK();
465       } else {
466         return custom->ConfigureFromMap(copy, props);
467       }
468     } else {
469       // Attempting to configure one of the properties of the customizable
470       // Let it through
471       return custom->ConfigureOption(copy, name, value);
472     }
473   }
474 }
475 
ConfigureOption(const ConfigOptions & config_options,Configurable & configurable,const OptionTypeInfo & opt_info,const std::string & opt_name,const std::string & name,const std::string & value,void * opt_ptr)476 Status ConfigurableHelper::ConfigureOption(
477     const ConfigOptions& config_options, Configurable& configurable,
478     const OptionTypeInfo& opt_info, const std::string& opt_name,
479     const std::string& name, const std::string& value, void* opt_ptr) {
480   if (opt_info.IsCustomizable()) {
481     return ConfigureCustomizableOption(config_options, configurable, opt_info,
482                                        opt_name, name, value, opt_ptr);
483   } else if (opt_name == name) {
484     return configurable.ParseOption(config_options, opt_info, opt_name, value,
485                                     opt_ptr);
486   } else if (opt_info.IsStruct() || opt_info.IsConfigurable()) {
487     return configurable.ParseOption(config_options, opt_info, name, value,
488                                     opt_ptr);
489   } else {
490     return Status::NotFound("Could not find option: ", name);
491   }
492 }
493 #endif  // ROCKSDB_LITE
494 
495 //*******************************************************************************
496 //
497 //       Methods for Converting Options into strings
498 //
499 //*******************************************************************************
500 
GetOptionString(const ConfigOptions & config_options,std::string * result) const501 Status Configurable::GetOptionString(const ConfigOptions& config_options,
502                                      std::string* result) const {
503   assert(result);
504   result->clear();
505 #ifndef ROCKSDB_LITE
506   return ConfigurableHelper::SerializeOptions(config_options, *this, "",
507                                               result);
508 #else
509   (void)config_options;
510   return Status::NotSupported("GetOptionString not supported in LITE mode");
511 #endif  // ROCKSDB_LITE
512 }
513 
514 #ifndef ROCKSDB_LITE
ToString(const ConfigOptions & config_options,const std::string & prefix) const515 std::string Configurable::ToString(const ConfigOptions& config_options,
516                                    const std::string& prefix) const {
517   std::string result = SerializeOptions(config_options, prefix);
518   if (result.empty() || result.find('=') == std::string::npos) {
519     return result;
520   } else {
521     return "{" + result + "}";
522   }
523 }
524 
SerializeOptions(const ConfigOptions & config_options,const std::string & header) const525 std::string Configurable::SerializeOptions(const ConfigOptions& config_options,
526                                            const std::string& header) const {
527   std::string result;
528   Status s = ConfigurableHelper::SerializeOptions(config_options, *this, header,
529                                                   &result);
530   assert(s.ok());
531   return result;
532 }
533 
GetOption(const ConfigOptions & config_options,const std::string & name,std::string * value) const534 Status Configurable::GetOption(const ConfigOptions& config_options,
535                                const std::string& name,
536                                std::string* value) const {
537   return ConfigurableHelper::GetOption(config_options, *this,
538                                        GetOptionName(name), value);
539 }
540 
GetOption(const ConfigOptions & config_options,const Configurable & configurable,const std::string & short_name,std::string * value)541 Status ConfigurableHelper::GetOption(const ConfigOptions& config_options,
542                                      const Configurable& configurable,
543                                      const std::string& short_name,
544                                      std::string* value) {
545   // Look for option directly
546   assert(value);
547   value->clear();
548 
549   std::string opt_name;
550   void* opt_ptr = nullptr;
551   const auto opt_info =
552       FindOption(configurable.options_, short_name, &opt_name, &opt_ptr);
553   if (opt_info != nullptr) {
554     ConfigOptions embedded = config_options;
555     embedded.delimiter = ";";
556     if (short_name == opt_name) {
557       return opt_info->Serialize(embedded, opt_name, opt_ptr, value);
558     } else if (opt_info->IsStruct()) {
559       return opt_info->Serialize(embedded, opt_name, opt_ptr, value);
560     } else if (opt_info->IsConfigurable()) {
561       auto const* config = opt_info->AsRawPointer<Configurable>(opt_ptr);
562       if (config != nullptr) {
563         return config->GetOption(embedded, opt_name, value);
564       }
565     }
566   }
567   return Status::NotFound("Cannot find option: ", short_name);
568 }
569 
SerializeOptions(const ConfigOptions & config_options,const Configurable & configurable,const std::string & prefix,std::string * result)570 Status ConfigurableHelper::SerializeOptions(const ConfigOptions& config_options,
571                                             const Configurable& configurable,
572                                             const std::string& prefix,
573                                             std::string* result) {
574   assert(result);
575   for (auto const& opt_iter : configurable.options_) {
576     for (const auto& map_iter : *(opt_iter.type_map)) {
577       const auto& opt_name = map_iter.first;
578       const auto& opt_info = map_iter.second;
579       if (opt_info.ShouldSerialize()) {
580         std::string value;
581         Status s;
582         if (!config_options.mutable_options_only) {
583           s = opt_info.Serialize(config_options, prefix + opt_name,
584                                  opt_iter.opt_ptr, &value);
585         } else if (opt_info.IsMutable()) {
586           ConfigOptions copy = config_options;
587           copy.mutable_options_only = false;
588           s = opt_info.Serialize(copy, prefix + opt_name, opt_iter.opt_ptr,
589                                  &value);
590         } else if (opt_info.IsConfigurable()) {
591           // If it is a Configurable and we are either printing all of the
592           // details or not printing only the name, this option should be
593           // included in the list
594           if (config_options.IsDetailed() ||
595               !opt_info.IsEnabled(OptionTypeFlags::kStringNameOnly)) {
596             s = opt_info.Serialize(config_options, prefix + opt_name,
597                                    opt_iter.opt_ptr, &value);
598           }
599         }
600         if (!s.ok()) {
601           return s;
602         } else if (!value.empty()) {
603           // <prefix><opt_name>=<value><delimiter>
604           result->append(prefix + opt_name + "=" + value +
605                          config_options.delimiter);
606         }
607       }
608     }
609   }
610   return Status::OK();
611 }
612 #endif  // ROCKSDB_LITE
613 
614 //********************************************************************************
615 //
616 // Methods for listing the options from Configurables
617 //
618 //********************************************************************************
619 #ifndef ROCKSDB_LITE
GetOptionNames(const ConfigOptions & config_options,std::unordered_set<std::string> * result) const620 Status Configurable::GetOptionNames(
621     const ConfigOptions& config_options,
622     std::unordered_set<std::string>* result) const {
623   assert(result);
624   return ConfigurableHelper::ListOptions(config_options, *this, "", result);
625 }
626 
ListOptions(const ConfigOptions & config_options,const Configurable & configurable,const std::string & prefix,std::unordered_set<std::string> * result)627 Status ConfigurableHelper::ListOptions(
628     const ConfigOptions& config_options, const Configurable& configurable,
629     const std::string& prefix, std::unordered_set<std::string>* result) {
630   Status status;
631   for (auto const& opt_iter : configurable.options_) {
632     for (const auto& map_iter : *(opt_iter.type_map)) {
633       const auto& opt_name = map_iter.first;
634       const auto& opt_info = map_iter.second;
635       // If the option is no longer used in rocksdb and marked as deprecated,
636       // we skip it in the serialization.
637       if (!opt_info.IsDeprecated() && !opt_info.IsAlias()) {
638         if (!config_options.mutable_options_only) {
639           result->emplace(prefix + opt_name);
640         } else if (opt_info.IsMutable()) {
641           result->emplace(prefix + opt_name);
642         }
643       }
644     }
645   }
646   return status;
647 }
648 #endif  // ROCKSDB_LITE
649 
650 //*******************************************************************************
651 //
652 //       Methods for Comparing Configurables
653 //
654 //*******************************************************************************
655 
AreEquivalent(const ConfigOptions & config_options,const Configurable * other,std::string * name) const656 bool Configurable::AreEquivalent(const ConfigOptions& config_options,
657                                  const Configurable* other,
658                                  std::string* name) const {
659   assert(name);
660   name->clear();
661   if (this == other || config_options.IsCheckDisabled()) {
662     return true;
663   } else if (other != nullptr) {
664 #ifndef ROCKSDB_LITE
665     return ConfigurableHelper::AreEquivalent(config_options, *this, *other,
666                                              name);
667 #else
668     return true;
669 #endif  // ROCKSDB_LITE
670   } else {
671     return false;
672   }
673 }
674 
675 #ifndef ROCKSDB_LITE
OptionsAreEqual(const ConfigOptions & config_options,const OptionTypeInfo & opt_info,const std::string & opt_name,const void * const this_ptr,const void * const that_ptr,std::string * mismatch) const676 bool Configurable::OptionsAreEqual(const ConfigOptions& config_options,
677                                    const OptionTypeInfo& opt_info,
678                                    const std::string& opt_name,
679                                    const void* const this_ptr,
680                                    const void* const that_ptr,
681                                    std::string* mismatch) const {
682   if (opt_info.AreEqual(config_options, opt_name, this_ptr, that_ptr,
683                         mismatch)) {
684     return true;
685   } else if (opt_info.AreEqualByName(config_options, opt_name, this_ptr,
686                                      that_ptr)) {
687     *mismatch = "";
688     return true;
689   } else {
690     return false;
691   }
692 }
693 
AreEquivalent(const ConfigOptions & config_options,const Configurable & this_one,const Configurable & that_one,std::string * mismatch)694 bool ConfigurableHelper::AreEquivalent(const ConfigOptions& config_options,
695                                        const Configurable& this_one,
696                                        const Configurable& that_one,
697                                        std::string* mismatch) {
698   assert(mismatch != nullptr);
699   for (auto const& o : this_one.options_) {
700     const auto this_offset = this_one.GetOptionsPtr(o.name);
701     const auto that_offset = that_one.GetOptionsPtr(o.name);
702     if (this_offset != that_offset) {
703       if (this_offset == nullptr || that_offset == nullptr) {
704         return false;
705       } else {
706         for (const auto& map_iter : *(o.type_map)) {
707           const auto& opt_info = map_iter.second;
708           if (config_options.IsCheckEnabled(opt_info.GetSanityLevel())) {
709             if (!config_options.mutable_options_only) {
710               if (!this_one.OptionsAreEqual(config_options, opt_info,
711                                             map_iter.first, this_offset,
712                                             that_offset, mismatch)) {
713                 return false;
714               }
715             } else if (opt_info.IsMutable()) {
716               ConfigOptions copy = config_options;
717               copy.mutable_options_only = false;
718               if (!this_one.OptionsAreEqual(copy, opt_info, map_iter.first,
719                                             this_offset, that_offset,
720                                             mismatch)) {
721                 return false;
722               }
723             }
724           }
725         }
726       }
727     }
728   }
729   return true;
730 }
731 #endif  // ROCKSDB_LITE
732 
GetOptionsMap(const std::string & value,const std::string & default_id,std::string * id,std::unordered_map<std::string,std::string> * props)733 Status Configurable::GetOptionsMap(
734     const std::string& value, const std::string& default_id, std::string* id,
735     std::unordered_map<std::string, std::string>* props) {
736   assert(id);
737   assert(props);
738   Status status;
739   if (value.empty() || value == kNullptrString) {
740     *id = default_id;
741   } else if (value.find('=') == std::string::npos) {
742     *id = value;
743 #ifndef ROCKSDB_LITE
744   } else {
745     status = StringToMap(value, props);
746     if (!status.ok()) {       // There was an error creating the map.
747       *id = value;            // Treat the value as id
748       props->clear();         // Clear the properties
749       status = Status::OK();  // and ignore the error
750     } else {
751       auto iter = props->find(OptionTypeInfo::kIdPropName());
752       if (iter != props->end()) {
753         *id = iter->second;
754         props->erase(iter);
755         if (*id == kNullptrString) {
756           id->clear();
757         }
758       } else if (!default_id.empty()) {
759         *id = default_id;
760       } else {           // No id property and no default
761         *id = value;     // Treat the value as id
762         props->clear();  // Clear the properties
763       }
764     }
765 #else
766   } else {
767     *id = value;
768     props->clear();
769 #endif
770   }
771   return status;
772 }
773 }  // namespace ROCKSDB_NAMESPACE
774