ClickHouse/src/Core/Settings.cpp
2022-07-18 13:19:41 +00:00

199 lines
7.4 KiB
C++

#include "Settings.h"
#include <Core/SettingsChangesHistory.h>
#include <Poco/Util/AbstractConfiguration.h>
#include <Columns/ColumnArray.h>
#include <Columns/ColumnMap.h>
#include <Common/typeid_cast.h>
#include <cstring>
#include <boost/program_options/options_description.hpp>
namespace DB
{
namespace ErrorCodes
{
extern const int THERE_IS_NO_PROFILE;
extern const int NO_ELEMENTS_IN_CONFIG;
extern const int UNKNOWN_ELEMENT_IN_CONFIG;
}
IMPLEMENT_SETTINGS_TRAITS(SettingsTraits, LIST_OF_SETTINGS)
/** Set the settings from the profile (in the server configuration, many settings can be listed in one profile).
* The profile can also be set using the `set` functions, like the `profile` setting.
*/
void Settings::setProfile(const String & profile_name, const Poco::Util::AbstractConfiguration & config)
{
String elem = "profiles." + profile_name;
if (!config.has(elem))
throw Exception("There is no profile '" + profile_name + "' in configuration file.", ErrorCodes::THERE_IS_NO_PROFILE);
Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(elem, config_keys);
for (const std::string & key : config_keys)
{
if (key == "constraints")
continue;
if (key == "profile" || key.starts_with("profile[")) /// Inheritance of profiles from the current one.
setProfile(config.getString(elem + "." + key), config);
else
set(key, config.getString(elem + "." + key));
}
}
void Settings::loadSettingsFromConfig(const String & path, const Poco::Util::AbstractConfiguration & config)
{
if (!config.has(path))
throw Exception("There is no path '" + path + "' in configuration file.", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(path, config_keys);
for (const std::string & key : config_keys)
{
set(key, config.getString(path + "." + key));
}
}
void Settings::dumpToMapColumn(IColumn * column, bool changed_only)
{
/// Convert ptr and make simple check
auto * column_map = column ? &typeid_cast<ColumnMap &>(*column) : nullptr;
if (!column_map)
return;
auto & offsets = column_map->getNestedColumn().getOffsets();
auto & tuple_column = column_map->getNestedData();
auto & key_column = tuple_column.getColumn(0);
auto & value_column = tuple_column.getColumn(1);
size_t size = 0;
for (const auto & setting : all(changed_only ? SKIP_UNCHANGED : SKIP_NONE))
{
auto name = setting.getName();
key_column.insertData(name.data(), name.size());
value_column.insert(setting.getValueString());
size++;
}
offsets.push_back(offsets.back() + size);
}
void Settings::addProgramOptions(boost::program_options::options_description & options)
{
for (const auto & field : all())
{
addProgramOption(options, field);
}
}
void Settings::addProgramOptionsAsMultitokens(boost::program_options::options_description & options)
{
for (const auto & field : all())
{
addProgramOptionAsMultitoken(options, field);
}
}
void Settings::addProgramOption(boost::program_options::options_description & options, const SettingFieldRef & field)
{
const std::string_view name = field.getName();
auto on_program_option = boost::function1<void, const std::string &>([this, name](const std::string & value) { set(name, value); });
options.add(boost::shared_ptr<boost::program_options::option_description>(new boost::program_options::option_description(
name.data(), boost::program_options::value<std::string>()->composing()->notifier(on_program_option), field.getDescription())));
}
void Settings::addProgramOptionAsMultitoken(boost::program_options::options_description & options, const SettingFieldRef & field)
{
const std::string_view name = field.getName();
auto on_program_option = boost::function1<void, const Strings &>([this, name](const Strings & values) { set(name, values.back()); });
options.add(boost::shared_ptr<boost::program_options::option_description>(new boost::program_options::option_description(
name.data(), boost::program_options::value<Strings>()->multitoken()->composing()->notifier(on_program_option), field.getDescription())));
}
void Settings::checkNoSettingNamesAtTopLevel(const Poco::Util::AbstractConfiguration & config, const String & config_path)
{
if (config.getBool("skip_check_for_incorrect_settings", false))
return;
Settings settings;
for (auto setting : settings.all())
{
const auto & name = setting.getName();
if (config.has(name) && !setting.isObsolete())
{
throw Exception(fmt::format("A setting '{}' appeared at top level in config {}."
" But it is user-level setting that should be located in users.xml inside <profiles> section for specific profile."
" You can add it to <profiles><default> if you want to change default value of this setting."
" You can also disable the check - specify <skip_check_for_incorrect_settings>1</skip_check_for_incorrect_settings>"
" in the main configuration file.",
name, config_path),
ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG);
}
}
}
std::vector<String> Settings::getAllRegisteredNames() const
{
std::vector<String> all_settings;
for (const auto & setting_field : all())
{
all_settings.push_back(setting_field.getName());
}
return all_settings;
}
void Settings::set(std::string_view name, const Field & value)
{
BaseSettings::set(name, value);
if (name == "compatibility")
applyCompatibilitySetting();
/// If we change setting that was changed by compatibility setting before
/// we should remove it from settings_changed_by_compatibility_setting,
/// otherwise the next time we will change compatibility setting
/// this setting will be changed too (and we don't want it).
else if (settings_changed_by_compatibility_setting.contains(name))
settings_changed_by_compatibility_setting.erase(name);
}
void Settings::applyCompatibilitySetting()
{
/// First, revert all changes applied by previous compatibility setting
for (const auto & setting_name : settings_changed_by_compatibility_setting)
resetToDefault(setting_name);
settings_changed_by_compatibility_setting.clear();
String compatibility = getString("compatibility");
/// If setting value is empty, we don't need to change settings
if (compatibility.empty())
return;
ClickHouseVersion version(compatibility);
/// Iterate through ClickHouse version in descending order and apply reversed
/// changes for each version that is higher that version from compatibility setting
for (auto it = settings_changes_history.rbegin(); it != settings_changes_history.rend(); ++it)
{
if (version >= it->first)
break;
/// Apply reversed changes from this version.
for (const auto & change : it->second)
{
/// If this setting was changed manually, we don't change it
if (isChanged(change.name) && !settings_changed_by_compatibility_setting.contains(change.name))
continue;
BaseSettings::set(change.name, change.previous_value);
settings_changed_by_compatibility_setting.insert(change.name);
}
}
}
IMPLEMENT_SETTINGS_TRAITS(FormatFactorySettingsTraits, FORMAT_FACTORY_SETTINGS)
}