2020-09-14 23:59:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
|
|
|
|
#include "rocksdb/configurable.h"
|
|
|
|
|
|
|
|
#include "logging/logging.h"
|
|
|
|
#include "options/configurable_helper.h"
|
|
|
|
#include "options/options_helper.h"
|
2020-11-11 23:09:14 +00:00
|
|
|
#include "rocksdb/customizable.h"
|
2020-09-14 23:59:00 +00:00
|
|
|
#include "rocksdb/status.h"
|
|
|
|
#include "rocksdb/utilities/object_registry.h"
|
|
|
|
#include "rocksdb/utilities/options_type.h"
|
|
|
|
#include "util/coding.h"
|
|
|
|
#include "util/string_util.h"
|
|
|
|
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
|
2021-04-26 10:12:35 +00:00
|
|
|
void Configurable::RegisterOptions(
|
|
|
|
const std::string& name, void* opt_ptr,
|
2020-09-14 23:59:00 +00:00
|
|
|
const std::unordered_map<std::string, OptionTypeInfo>* type_map) {
|
2021-04-26 10:12:35 +00:00
|
|
|
RegisteredOptions opts;
|
2020-09-14 23:59:00 +00:00
|
|
|
opts.name = name;
|
|
|
|
opts.type_map = type_map;
|
|
|
|
opts.opt_ptr = opt_ptr;
|
2021-04-26 10:12:35 +00:00
|
|
|
options_.emplace_back(opts);
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
|
|
|
// Methods for Initializing and Validating Configurable Objects
|
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
Status Configurable::PrepareOptions(const ConfigOptions& opts) {
|
2021-07-16 14:57:47 +00:00
|
|
|
// We ignore the invoke_prepare_options here intentionally,
|
|
|
|
// as if you are here, you must have called PrepareOptions explicitly.
|
2020-09-14 23:59:00 +00:00
|
|
|
Status status = Status::OK();
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& opt_iter : options_) {
|
2021-11-30 21:22:27 +00:00
|
|
|
if (opt_iter.type_map != nullptr) {
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& map_iter : *(opt_iter.type_map)) {
|
2021-11-30 21:22:27 +00:00
|
|
|
auto& opt_info = map_iter.second;
|
2022-05-13 11:57:08 +00:00
|
|
|
if (opt_info.ShouldPrepare()) {
|
|
|
|
status = opt_info.Prepare(opts, map_iter.first, opt_iter.opt_ptr);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 14:57:47 +00:00
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Configurable::ValidateOptions(const DBOptions& db_opts,
|
|
|
|
const ColumnFamilyOptions& cf_opts) const {
|
|
|
|
Status status;
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& opt_iter : options_) {
|
2021-11-30 21:22:27 +00:00
|
|
|
if (opt_iter.type_map != nullptr) {
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& map_iter : *(opt_iter.type_map)) {
|
2021-11-30 21:22:27 +00:00
|
|
|
auto& opt_info = map_iter.second;
|
2022-05-13 11:57:08 +00:00
|
|
|
if (opt_info.ShouldValidate()) {
|
|
|
|
status = opt_info.Validate(db_opts, cf_opts, map_iter.first,
|
|
|
|
opt_iter.opt_ptr);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* Methods for Retrieving Options from Configurables */
|
|
|
|
/* */
|
|
|
|
/*********************************************************************************/
|
|
|
|
|
|
|
|
const void* Configurable::GetOptionsPtr(const std::string& name) const {
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& o : options_) {
|
2020-09-14 23:59:00 +00:00
|
|
|
if (o.name == name) {
|
|
|
|
return o.opt_ptr;
|
|
|
|
}
|
|
|
|
}
|
2020-11-11 23:09:14 +00:00
|
|
|
return nullptr;
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string Configurable::GetOptionName(const std::string& opt_name) const {
|
|
|
|
return opt_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
const OptionTypeInfo* ConfigurableHelper::FindOption(
|
|
|
|
const std::vector<Configurable::RegisteredOptions>& options,
|
|
|
|
const std::string& short_name, std::string* opt_name, void** opt_ptr) {
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& iter : options) {
|
2021-11-30 21:22:27 +00:00
|
|
|
if (iter.type_map != nullptr) {
|
|
|
|
const auto opt_info =
|
|
|
|
OptionTypeInfo::Find(short_name, *(iter.type_map), opt_name);
|
|
|
|
if (opt_info != nullptr) {
|
|
|
|
*opt_ptr = iter.opt_ptr;
|
|
|
|
return opt_info;
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
|
|
|
// Methods for Configuring Options from Strings/Name-Value Pairs/Maps
|
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
Status Configurable::ConfigureFromMap(
|
|
|
|
const ConfigOptions& config_options,
|
|
|
|
const std::unordered_map<std::string, std::string>& opts_map) {
|
|
|
|
Status s = ConfigureFromMap(config_options, opts_map, nullptr);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Configurable::ConfigureFromMap(
|
|
|
|
const ConfigOptions& config_options,
|
|
|
|
const std::unordered_map<std::string, std::string>& opts_map,
|
|
|
|
std::unordered_map<std::string, std::string>* unused) {
|
|
|
|
return ConfigureOptions(config_options, opts_map, unused);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Configurable::ConfigureOptions(
|
|
|
|
const ConfigOptions& config_options,
|
|
|
|
const std::unordered_map<std::string, std::string>& opts_map,
|
|
|
|
std::unordered_map<std::string, std::string>* unused) {
|
|
|
|
std::string curr_opts;
|
2021-06-30 21:08:19 +00:00
|
|
|
Status s;
|
|
|
|
if (!opts_map.empty()) {
|
|
|
|
// There are options in the map.
|
|
|
|
// Save the current configuration in curr_opts and then configure the
|
|
|
|
// options, but do not prepare them now. We will do all the prepare when
|
|
|
|
// the configuration is complete.
|
2021-02-19 18:25:39 +00:00
|
|
|
ConfigOptions copy = config_options;
|
2021-06-30 21:08:19 +00:00
|
|
|
copy.invoke_prepare_options = false;
|
|
|
|
if (!config_options.ignore_unknown_options) {
|
|
|
|
// If we are not ignoring unused, get the defaults in case we need to
|
|
|
|
// reset
|
|
|
|
copy.depth = ConfigOptions::kDepthDetailed;
|
|
|
|
copy.delimiter = "; ";
|
|
|
|
GetOptionString(copy, &curr_opts).PermitUncheckedError();
|
|
|
|
}
|
|
|
|
|
|
|
|
s = ConfigurableHelper::ConfigureOptions(copy, *this, opts_map, unused);
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
if (config_options.invoke_prepare_options && s.ok()) {
|
|
|
|
s = PrepareOptions(config_options);
|
|
|
|
}
|
|
|
|
if (!s.ok() && !curr_opts.empty()) {
|
|
|
|
ConfigOptions reset = config_options;
|
|
|
|
reset.ignore_unknown_options = true;
|
|
|
|
reset.invoke_prepare_options = true;
|
2021-06-30 21:08:19 +00:00
|
|
|
reset.ignore_unsupported_options = true;
|
2020-09-14 23:59:00 +00:00
|
|
|
// There are some options to reset from this current error
|
|
|
|
ConfigureFromString(reset, curr_opts).PermitUncheckedError();
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Configurable::ParseStringOptions(const ConfigOptions& /*config_options*/,
|
|
|
|
const std::string& /*opts_str*/) {
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Configurable::ConfigureFromString(const ConfigOptions& config_options,
|
|
|
|
const std::string& opts_str) {
|
|
|
|
Status s;
|
|
|
|
if (!opts_str.empty()) {
|
|
|
|
if (opts_str.find(';') != std::string::npos ||
|
|
|
|
opts_str.find('=') != std::string::npos) {
|
|
|
|
std::unordered_map<std::string, std::string> opt_map;
|
|
|
|
s = StringToMap(opts_str, &opt_map);
|
|
|
|
if (s.ok()) {
|
|
|
|
s = ConfigureFromMap(config_options, opt_map, nullptr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s = ParseStringOptions(config_options, opts_str);
|
|
|
|
if (s.ok() && config_options.invoke_prepare_options) {
|
|
|
|
s = PrepareOptions(config_options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (config_options.invoke_prepare_options) {
|
|
|
|
s = PrepareOptions(config_options);
|
|
|
|
} else {
|
|
|
|
s = Status::OK();
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the value of the named property to the input value, returning OK on
|
|
|
|
* succcess.
|
|
|
|
*/
|
|
|
|
Status Configurable::ConfigureOption(const ConfigOptions& config_options,
|
|
|
|
const std::string& name,
|
|
|
|
const std::string& value) {
|
2021-02-19 18:25:39 +00:00
|
|
|
return ConfigurableHelper::ConfigureSingleOption(config_options, *this, name,
|
|
|
|
value);
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Looks for the named option amongst the options for this type and sets
|
|
|
|
* the value for it to be the input value.
|
|
|
|
* If the name was found, found_option will be set to true and the resulting
|
|
|
|
* status should be returned.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Status Configurable::ParseOption(const ConfigOptions& config_options,
|
|
|
|
const OptionTypeInfo& opt_info,
|
|
|
|
const std::string& opt_name,
|
|
|
|
const std::string& opt_value, void* opt_ptr) {
|
2021-02-19 18:25:39 +00:00
|
|
|
if (opt_info.IsMutable()) {
|
|
|
|
if (config_options.mutable_options_only) {
|
|
|
|
// This option is mutable. Treat all of its children as mutable as well
|
|
|
|
ConfigOptions copy = config_options;
|
|
|
|
copy.mutable_options_only = false;
|
|
|
|
return opt_info.Parse(copy, opt_name, opt_value, opt_ptr);
|
|
|
|
} else {
|
|
|
|
return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr);
|
|
|
|
}
|
|
|
|
} else if (config_options.mutable_options_only) {
|
2020-09-14 23:59:00 +00:00
|
|
|
return Status::InvalidArgument("Option not changeable: " + opt_name);
|
|
|
|
} else {
|
|
|
|
return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Status ConfigurableHelper::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) {
|
|
|
|
std::unordered_map<std::string, std::string> remaining = opts_map;
|
|
|
|
Status s = Status::OK();
|
|
|
|
if (!opts_map.empty()) {
|
|
|
|
for (const auto& iter : configurable.options_) {
|
2021-11-30 21:22:27 +00:00
|
|
|
if (iter.type_map != nullptr) {
|
|
|
|
s = ConfigureSomeOptions(config_options, configurable, *(iter.type_map),
|
|
|
|
&remaining, iter.opt_ptr);
|
|
|
|
if (remaining.empty()) { // Are there more options left?
|
|
|
|
break;
|
|
|
|
} else if (!s.ok()) {
|
|
|
|
break;
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (unused != nullptr && !remaining.empty()) {
|
|
|
|
unused->insert(remaining.begin(), remaining.end());
|
|
|
|
}
|
|
|
|
if (config_options.ignore_unknown_options) {
|
|
|
|
s = Status::OK();
|
|
|
|
} else if (s.ok() && unused == nullptr && !remaining.empty()) {
|
|
|
|
s = Status::NotFound("Could not find option: ", remaining.begin()->first);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the object with the named-value property values, returning OK on
|
|
|
|
* succcess. Any properties that were found are removed from the options list;
|
|
|
|
* upon return only options that were not found in this opt_map remain.
|
|
|
|
|
|
|
|
* Returns:
|
|
|
|
* - OK if ignore_unknown_options is set
|
|
|
|
* - InvalidArgument, if any option was invalid
|
|
|
|
* - NotSupported, if any option is unsupported and ignore_unsupported_options
|
|
|
|
is OFF
|
|
|
|
* - OK, if no option was invalid or not supported (or ignored)
|
|
|
|
*/
|
|
|
|
Status ConfigurableHelper::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) {
|
|
|
|
Status result = Status::OK(); // The last non-OK result (if any)
|
|
|
|
Status notsup = Status::OK(); // The last NotSupported result (if any)
|
|
|
|
std::string elem_name;
|
|
|
|
int found = 1;
|
|
|
|
std::unordered_set<std::string> unsupported;
|
|
|
|
// While there are unused properties and we processed at least one,
|
|
|
|
// go through the remaining unused properties and attempt to configure them.
|
|
|
|
while (found > 0 && !options->empty()) {
|
|
|
|
found = 0;
|
|
|
|
notsup = Status::OK();
|
|
|
|
for (auto it = options->begin(); it != options->end();) {
|
|
|
|
const std::string& opt_name = configurable.GetOptionName(it->first);
|
|
|
|
const std::string& opt_value = it->second;
|
|
|
|
const auto opt_info =
|
|
|
|
OptionTypeInfo::Find(opt_name, type_map, &elem_name);
|
|
|
|
if (opt_info == nullptr) { // Did not find the option. Skip it
|
|
|
|
++it;
|
|
|
|
} else {
|
|
|
|
Status s = ConfigureOption(config_options, configurable, *opt_info,
|
|
|
|
opt_name, elem_name, opt_value, opt_ptr);
|
|
|
|
if (s.IsNotFound()) {
|
|
|
|
++it;
|
|
|
|
} else if (s.IsNotSupported()) {
|
|
|
|
notsup = s;
|
|
|
|
unsupported.insert(it->first);
|
|
|
|
++it; // Skip it for now
|
|
|
|
} else {
|
|
|
|
found++;
|
|
|
|
it = options->erase(it);
|
|
|
|
if (!s.ok()) {
|
|
|
|
result = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // End for all remaining options
|
|
|
|
} // End while found one or options remain
|
|
|
|
|
|
|
|
// Now that we have been through the list, remove any unsupported
|
2023-12-01 19:10:30 +00:00
|
|
|
for (const auto& u : unsupported) {
|
2020-09-14 23:59:00 +00:00
|
|
|
auto it = options->find(u);
|
|
|
|
if (it != options->end()) {
|
|
|
|
options->erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (config_options.ignore_unknown_options) {
|
2023-12-01 19:10:30 +00:00
|
|
|
if (!result.ok()) {
|
|
|
|
result.PermitUncheckedError();
|
|
|
|
}
|
|
|
|
if (!notsup.ok()) {
|
|
|
|
notsup.PermitUncheckedError();
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
return Status::OK();
|
|
|
|
} else if (!result.ok()) {
|
2023-12-01 19:10:30 +00:00
|
|
|
if (!notsup.ok()) {
|
|
|
|
notsup.PermitUncheckedError();
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
return result;
|
|
|
|
} else if (config_options.ignore_unsupported_options) {
|
2023-12-01 19:10:30 +00:00
|
|
|
if (!notsup.ok()) {
|
|
|
|
notsup.PermitUncheckedError();
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
return Status::OK();
|
|
|
|
} else {
|
|
|
|
return notsup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ConfigurableHelper::ConfigureSingleOption(
|
|
|
|
const ConfigOptions& config_options, Configurable& configurable,
|
|
|
|
const std::string& name, const std::string& value) {
|
2021-02-19 18:25:39 +00:00
|
|
|
const std::string& opt_name = configurable.GetOptionName(name);
|
|
|
|
std::string elem_name;
|
2020-09-14 23:59:00 +00:00
|
|
|
void* opt_ptr = nullptr;
|
|
|
|
const auto opt_info =
|
2021-02-19 18:25:39 +00:00
|
|
|
FindOption(configurable.options_, opt_name, &elem_name, &opt_ptr);
|
2020-09-14 23:59:00 +00:00
|
|
|
if (opt_info == nullptr) {
|
|
|
|
return Status::NotFound("Could not find option: ", name);
|
|
|
|
} else {
|
2021-02-19 18:25:39 +00:00
|
|
|
return ConfigureOption(config_options, configurable, *opt_info, opt_name,
|
|
|
|
elem_name, value, opt_ptr);
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-19 18:25:39 +00:00
|
|
|
Status ConfigurableHelper::ConfigureCustomizableOption(
|
2020-09-14 23:59:00 +00:00
|
|
|
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) {
|
2021-02-19 18:25:39 +00:00
|
|
|
Customizable* custom = opt_info.AsRawPointer<Customizable>(opt_ptr);
|
|
|
|
ConfigOptions copy = config_options;
|
|
|
|
if (opt_info.IsMutable()) {
|
|
|
|
// This option is mutable. Pass that property on to any subsequent calls
|
|
|
|
copy.mutable_options_only = false;
|
|
|
|
}
|
2020-11-11 23:09:14 +00:00
|
|
|
|
2021-02-19 18:25:39 +00:00
|
|
|
if (opt_info.IsMutable() || !config_options.mutable_options_only) {
|
|
|
|
// Either the option is mutable, or we are processing all of the options
|
2021-08-19 17:09:30 +00:00
|
|
|
if (opt_name == name || name == OptionTypeInfo::kIdPropName() ||
|
|
|
|
EndsWith(opt_name, OptionTypeInfo::kIdPropSuffix())) {
|
2021-06-29 16:07:10 +00:00
|
|
|
return configurable.ParseOption(copy, opt_info, name, value, opt_ptr);
|
2021-02-19 18:25:39 +00:00
|
|
|
} else if (value.empty()) {
|
2020-11-11 23:09:14 +00:00
|
|
|
return Status::OK();
|
|
|
|
} else if (custom == nullptr || !StartsWith(name, custom->GetId() + ".")) {
|
2021-02-19 18:25:39 +00:00
|
|
|
return configurable.ParseOption(copy, opt_info, name, value, opt_ptr);
|
2023-12-01 19:10:30 +00:00
|
|
|
} else if (value.find('=') != std::string::npos) {
|
2021-02-19 18:25:39 +00:00
|
|
|
return custom->ConfigureFromString(copy, value);
|
|
|
|
} else {
|
|
|
|
return custom->ConfigureOption(copy, name, value);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We are processing immutable options, which means that we cannot change
|
|
|
|
// the Customizable object itself, but could change its mutable properties.
|
|
|
|
// Check to make sure that nothing is trying to change the Customizable
|
|
|
|
if (custom == nullptr) {
|
|
|
|
// We do not have a Customizable to configure. This is OK if the
|
|
|
|
// value is empty (nothing being configured) but an error otherwise
|
|
|
|
if (value.empty()) {
|
|
|
|
return Status::OK();
|
|
|
|
} else {
|
|
|
|
return Status::InvalidArgument("Option not changeable: " + opt_name);
|
|
|
|
}
|
2021-08-19 17:09:30 +00:00
|
|
|
} else if (EndsWith(opt_name, OptionTypeInfo::kIdPropSuffix()) ||
|
|
|
|
name == OptionTypeInfo::kIdPropName()) {
|
2021-02-19 18:25:39 +00:00
|
|
|
// We have a property of the form "id=value" or "table.id=value"
|
|
|
|
// This is OK if we ID/value matches the current customizable object
|
|
|
|
if (custom->GetId() == value) {
|
|
|
|
return Status::OK();
|
|
|
|
} else {
|
|
|
|
return Status::InvalidArgument("Option not changeable: " + opt_name);
|
|
|
|
}
|
|
|
|
} else if (opt_name == name) {
|
|
|
|
// The properties are of one of forms:
|
|
|
|
// name = { id = id; prop1 = value1; ... }
|
|
|
|
// name = { prop1=value1; prop2=value2; ... }
|
|
|
|
// name = ID
|
|
|
|
// Convert the value to a map and extract the ID
|
|
|
|
// If the ID does not match that of the current customizable, return an
|
|
|
|
// error. Otherwise, update the current customizable via the properties
|
|
|
|
// map
|
|
|
|
std::unordered_map<std::string, std::string> props;
|
|
|
|
std::string id;
|
2021-08-19 17:09:30 +00:00
|
|
|
Status s =
|
|
|
|
Configurable::GetOptionsMap(value, custom->GetId(), &id, &props);
|
2021-02-19 18:25:39 +00:00
|
|
|
if (!s.ok()) {
|
|
|
|
return s;
|
|
|
|
} else if (custom->GetId() != id) {
|
|
|
|
return Status::InvalidArgument("Option not changeable: " + opt_name);
|
|
|
|
} else if (props.empty()) {
|
|
|
|
return Status::OK();
|
|
|
|
} else {
|
|
|
|
return custom->ConfigureFromMap(copy, props);
|
|
|
|
}
|
2020-11-11 23:09:14 +00:00
|
|
|
} else {
|
2021-02-19 18:25:39 +00:00
|
|
|
// Attempting to configure one of the properties of the customizable
|
|
|
|
// Let it through
|
|
|
|
return custom->ConfigureOption(copy, name, value);
|
2020-11-11 23:09:14 +00:00
|
|
|
}
|
2021-02-19 18:25:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ConfigurableHelper::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) {
|
|
|
|
if (opt_info.IsCustomizable()) {
|
|
|
|
return ConfigureCustomizableOption(config_options, configurable, opt_info,
|
|
|
|
opt_name, name, value, opt_ptr);
|
|
|
|
} else if (opt_name == name) {
|
|
|
|
return configurable.ParseOption(config_options, opt_info, opt_name, value,
|
|
|
|
opt_ptr);
|
2020-09-14 23:59:00 +00:00
|
|
|
} else if (opt_info.IsStruct() || opt_info.IsConfigurable()) {
|
|
|
|
return configurable.ParseOption(config_options, opt_info, name, value,
|
|
|
|
opt_ptr);
|
|
|
|
} else {
|
|
|
|
return Status::NotFound("Could not find option: ", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//*******************************************************************************
|
|
|
|
//
|
|
|
|
// Methods for Converting Options into strings
|
|
|
|
//
|
|
|
|
//*******************************************************************************
|
|
|
|
|
|
|
|
Status Configurable::GetOptionString(const ConfigOptions& config_options,
|
|
|
|
std::string* result) const {
|
|
|
|
assert(result);
|
|
|
|
result->clear();
|
|
|
|
return ConfigurableHelper::SerializeOptions(config_options, *this, "",
|
|
|
|
result);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Configurable::ToString(const ConfigOptions& config_options,
|
|
|
|
const std::string& prefix) const {
|
|
|
|
std::string result = SerializeOptions(config_options, prefix);
|
|
|
|
if (result.empty() || result.find('=') == std::string::npos) {
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return "{" + result + "}";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Configurable::SerializeOptions(const ConfigOptions& config_options,
|
|
|
|
const std::string& header) const {
|
|
|
|
std::string result;
|
|
|
|
Status s = ConfigurableHelper::SerializeOptions(config_options, *this, header,
|
|
|
|
&result);
|
|
|
|
assert(s.ok());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status Configurable::GetOption(const ConfigOptions& config_options,
|
|
|
|
const std::string& name,
|
|
|
|
std::string* value) const {
|
|
|
|
return ConfigurableHelper::GetOption(config_options, *this,
|
|
|
|
GetOptionName(name), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ConfigurableHelper::GetOption(const ConfigOptions& config_options,
|
|
|
|
const Configurable& configurable,
|
|
|
|
const std::string& short_name,
|
|
|
|
std::string* value) {
|
|
|
|
// Look for option directly
|
|
|
|
assert(value);
|
|
|
|
value->clear();
|
|
|
|
|
|
|
|
std::string opt_name;
|
|
|
|
void* opt_ptr = nullptr;
|
|
|
|
const auto opt_info =
|
|
|
|
FindOption(configurable.options_, short_name, &opt_name, &opt_ptr);
|
|
|
|
if (opt_info != nullptr) {
|
|
|
|
ConfigOptions embedded = config_options;
|
|
|
|
embedded.delimiter = ";";
|
|
|
|
if (short_name == opt_name) {
|
|
|
|
return opt_info->Serialize(embedded, opt_name, opt_ptr, value);
|
|
|
|
} else if (opt_info->IsStruct()) {
|
|
|
|
return opt_info->Serialize(embedded, opt_name, opt_ptr, value);
|
|
|
|
} else if (opt_info->IsConfigurable()) {
|
|
|
|
auto const* config = opt_info->AsRawPointer<Configurable>(opt_ptr);
|
|
|
|
if (config != nullptr) {
|
|
|
|
return config->GetOption(embedded, opt_name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Status::NotFound("Cannot find option: ", short_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ConfigurableHelper::SerializeOptions(const ConfigOptions& config_options,
|
|
|
|
const Configurable& configurable,
|
|
|
|
const std::string& prefix,
|
|
|
|
std::string* result) {
|
|
|
|
assert(result);
|
|
|
|
for (auto const& opt_iter : configurable.options_) {
|
2021-11-30 21:22:27 +00:00
|
|
|
if (opt_iter.type_map != nullptr) {
|
|
|
|
for (const auto& map_iter : *(opt_iter.type_map)) {
|
|
|
|
const auto& opt_name = map_iter.first;
|
|
|
|
const auto& opt_info = map_iter.second;
|
|
|
|
if (opt_info.ShouldSerialize()) {
|
|
|
|
std::string value;
|
|
|
|
Status s;
|
|
|
|
if (!config_options.mutable_options_only) {
|
2021-02-19 18:25:39 +00:00
|
|
|
s = opt_info.Serialize(config_options, prefix + opt_name,
|
|
|
|
opt_iter.opt_ptr, &value);
|
2021-11-30 21:22:27 +00:00
|
|
|
} else if (opt_info.IsMutable()) {
|
|
|
|
ConfigOptions copy = config_options;
|
|
|
|
copy.mutable_options_only = false;
|
|
|
|
s = opt_info.Serialize(copy, prefix + opt_name, opt_iter.opt_ptr,
|
|
|
|
&value);
|
|
|
|
} else if (opt_info.IsConfigurable()) {
|
|
|
|
// If it is a Configurable and we are either printing all of the
|
|
|
|
// details or not printing only the name, this option should be
|
|
|
|
// included in the list
|
|
|
|
if (config_options.IsDetailed() ||
|
|
|
|
!opt_info.IsEnabled(OptionTypeFlags::kStringNameOnly)) {
|
|
|
|
s = opt_info.Serialize(config_options, prefix + opt_name,
|
|
|
|
opt_iter.opt_ptr, &value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!s.ok()) {
|
|
|
|
return s;
|
|
|
|
} else if (!value.empty()) {
|
|
|
|
// <prefix><opt_name>=<value><delimiter>
|
|
|
|
result->append(prefix + opt_name + "=" + value +
|
|
|
|
config_options.delimiter);
|
2021-02-19 18:25:39 +00:00
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
//********************************************************************************
|
|
|
|
//
|
|
|
|
// Methods for listing the options from Configurables
|
|
|
|
//
|
|
|
|
//********************************************************************************
|
|
|
|
Status Configurable::GetOptionNames(
|
|
|
|
const ConfigOptions& config_options,
|
|
|
|
std::unordered_set<std::string>* result) const {
|
|
|
|
assert(result);
|
|
|
|
return ConfigurableHelper::ListOptions(config_options, *this, "", result);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status ConfigurableHelper::ListOptions(
|
2021-02-19 18:25:39 +00:00
|
|
|
const ConfigOptions& config_options, const Configurable& configurable,
|
2020-09-14 23:59:00 +00:00
|
|
|
const std::string& prefix, std::unordered_set<std::string>* result) {
|
|
|
|
Status status;
|
|
|
|
for (auto const& opt_iter : configurable.options_) {
|
2021-11-30 21:22:27 +00:00
|
|
|
if (opt_iter.type_map != nullptr) {
|
|
|
|
for (const auto& map_iter : *(opt_iter.type_map)) {
|
|
|
|
const auto& opt_name = map_iter.first;
|
|
|
|
const auto& opt_info = map_iter.second;
|
|
|
|
// If the option is no longer used in rocksdb and marked as deprecated,
|
|
|
|
// we skip it in the serialization.
|
|
|
|
if (!opt_info.IsDeprecated() && !opt_info.IsAlias()) {
|
|
|
|
if (!config_options.mutable_options_only) {
|
|
|
|
result->emplace(prefix + opt_name);
|
|
|
|
} else if (opt_info.IsMutable()) {
|
|
|
|
result->emplace(prefix + opt_name);
|
|
|
|
}
|
2021-02-19 18:25:39 +00:00
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
//*******************************************************************************
|
|
|
|
//
|
|
|
|
// Methods for Comparing Configurables
|
|
|
|
//
|
|
|
|
//*******************************************************************************
|
|
|
|
|
|
|
|
bool Configurable::AreEquivalent(const ConfigOptions& config_options,
|
|
|
|
const Configurable* other,
|
|
|
|
std::string* name) const {
|
|
|
|
assert(name);
|
|
|
|
name->clear();
|
|
|
|
if (this == other || config_options.IsCheckDisabled()) {
|
|
|
|
return true;
|
|
|
|
} else if (other != nullptr) {
|
|
|
|
return ConfigurableHelper::AreEquivalent(config_options, *this, *other,
|
|
|
|
name);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Configurable::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) const {
|
|
|
|
if (opt_info.AreEqual(config_options, opt_name, this_ptr, that_ptr,
|
|
|
|
mismatch)) {
|
|
|
|
return true;
|
|
|
|
} else if (opt_info.AreEqualByName(config_options, opt_name, this_ptr,
|
|
|
|
that_ptr)) {
|
|
|
|
*mismatch = "";
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConfigurableHelper::AreEquivalent(const ConfigOptions& config_options,
|
|
|
|
const Configurable& this_one,
|
|
|
|
const Configurable& that_one,
|
|
|
|
std::string* mismatch) {
|
|
|
|
assert(mismatch != nullptr);
|
|
|
|
for (auto const& o : this_one.options_) {
|
|
|
|
const auto this_offset = this_one.GetOptionsPtr(o.name);
|
|
|
|
const auto that_offset = that_one.GetOptionsPtr(o.name);
|
|
|
|
if (this_offset != that_offset) {
|
|
|
|
if (this_offset == nullptr || that_offset == nullptr) {
|
|
|
|
return false;
|
2021-11-30 21:22:27 +00:00
|
|
|
} else if (o.type_map != nullptr) {
|
2020-09-14 23:59:00 +00:00
|
|
|
for (const auto& map_iter : *(o.type_map)) {
|
2021-02-19 18:25:39 +00:00
|
|
|
const auto& opt_info = map_iter.second;
|
|
|
|
if (config_options.IsCheckEnabled(opt_info.GetSanityLevel())) {
|
|
|
|
if (!config_options.mutable_options_only) {
|
|
|
|
if (!this_one.OptionsAreEqual(config_options, opt_info,
|
|
|
|
map_iter.first, this_offset,
|
|
|
|
that_offset, mismatch)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (opt_info.IsMutable()) {
|
|
|
|
ConfigOptions copy = config_options;
|
|
|
|
copy.mutable_options_only = false;
|
|
|
|
if (!this_one.OptionsAreEqual(copy, opt_info, map_iter.first,
|
|
|
|
this_offset, that_offset,
|
|
|
|
mismatch)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2020-11-11 23:09:14 +00:00
|
|
|
|
2021-08-19 17:09:30 +00:00
|
|
|
Status Configurable::GetOptionsMap(
|
2020-11-11 23:09:14 +00:00
|
|
|
const std::string& value, const std::string& default_id, std::string* id,
|
|
|
|
std::unordered_map<std::string, std::string>* props) {
|
|
|
|
assert(id);
|
|
|
|
assert(props);
|
|
|
|
Status status;
|
|
|
|
if (value.empty() || value == kNullptrString) {
|
|
|
|
*id = default_id;
|
|
|
|
} else if (value.find('=') == std::string::npos) {
|
|
|
|
*id = value;
|
|
|
|
} else {
|
|
|
|
status = StringToMap(value, props);
|
2021-07-16 22:04:29 +00:00
|
|
|
if (!status.ok()) { // There was an error creating the map.
|
|
|
|
*id = value; // Treat the value as id
|
|
|
|
props->clear(); // Clear the properties
|
|
|
|
status = Status::OK(); // and ignore the error
|
|
|
|
} else {
|
2021-08-19 17:09:30 +00:00
|
|
|
auto iter = props->find(OptionTypeInfo::kIdPropName());
|
2020-11-11 23:09:14 +00:00
|
|
|
if (iter != props->end()) {
|
|
|
|
*id = iter->second;
|
|
|
|
props->erase(iter);
|
2021-08-19 17:09:30 +00:00
|
|
|
if (*id == kNullptrString) {
|
|
|
|
id->clear();
|
|
|
|
}
|
2021-07-16 22:04:29 +00:00
|
|
|
} else if (!default_id.empty()) {
|
2020-11-11 23:09:14 +00:00
|
|
|
*id = default_id;
|
2021-07-16 22:04:29 +00:00
|
|
|
} else { // No id property and no default
|
|
|
|
*id = value; // Treat the value as id
|
|
|
|
props->clear(); // Clear the properties
|
2020-11-11 23:09:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
2020-09-14 23:59:00 +00:00
|
|
|
} // namespace ROCKSDB_NAMESPACE
|