#pragma once #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int BAD_ARGUMENTS; } class ReadBuffer; class WriteBuffer; /** One setting for any type. * Stores a value within itself, as well as a flag - whether the value was changed. * This is done so that you can send to the remote servers only changed settings (or explicitly specified in the config) values. * That is, if the configuration was not specified in the config and was not dynamically changed, it is not sent to the remote server, * and the remote server will use its default value. */ template struct SettingFieldNumber { using Type = T; Type value; bool changed = false; explicit SettingFieldNumber(Type x = 0) : value(x) {} explicit SettingFieldNumber(const Field & f); SettingFieldNumber & operator=(Type x) { value = x; changed = true; return *this; } SettingFieldNumber & operator=(const Field & f); operator Type() const { return value; } explicit operator Field() const { return value; } String toString() const; void parseFromString(const String & str); void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; using SettingFieldUInt64 = SettingFieldNumber; using SettingFieldInt64 = SettingFieldNumber; using SettingFieldFloat = SettingFieldNumber; using SettingFieldBool = SettingFieldNumber; /** Unlike SettingFieldUInt64, supports the value of 'auto' - the number of processor cores without taking into account SMT. * A value of 0 is also treated as auto. * When serializing, `auto` is written in the same way as 0. */ struct SettingFieldMaxThreads { bool is_auto; UInt64 value; bool changed = false; explicit SettingFieldMaxThreads(UInt64 x = 0) : is_auto(!x), value(is_auto ? getAuto() : x) {} explicit SettingFieldMaxThreads(const Field & f); SettingFieldMaxThreads & operator=(UInt64 x) { is_auto = !x; value = is_auto ? getAuto() : x; changed = true; return *this; } SettingFieldMaxThreads & operator=(const Field & f); operator UInt64() const { return value; } explicit operator Field() const { return value; } /// Writes "auto()" instead of simple "" if `is_auto==true`. String toString() const; void parseFromString(const String & str); void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); private: static UInt64 getAuto(); }; enum class SettingFieldTimespanUnit { Millisecond, Second }; template struct SettingFieldTimespan { using Unit = SettingFieldTimespanUnit; static constexpr Unit unit = unit_; static constexpr UInt64 microseconds_per_unit = (unit == SettingFieldTimespanUnit::Millisecond) ? 1000 : 1000000; Poco::Timespan value; bool changed = false; explicit SettingFieldTimespan(Poco::Timespan x = {}) : value(x) {} template > explicit SettingFieldTimespan(const std::chrono::duration & x) : SettingFieldTimespan(Poco::Timespan{static_cast(std::chrono::duration_cast(x).count())}) {} explicit SettingFieldTimespan(UInt64 x) : SettingFieldTimespan(Poco::Timespan{static_cast(x * microseconds_per_unit)}) {} explicit SettingFieldTimespan(const Field & f); SettingFieldTimespan & operator =(Poco::Timespan x) { value = x; changed = true; return *this; } template > SettingFieldTimespan & operator =(const std::chrono::duration & x) { *this = Poco::Timespan{static_cast(std::chrono::duration_cast(x).count())}; return *this; } SettingFieldTimespan & operator =(UInt64 x) { *this = Poco::Timespan{static_cast(x * microseconds_per_unit)}; return *this; } SettingFieldTimespan & operator =(const Field & f); operator Poco::Timespan() const { return value; } template > operator std::chrono::duration() const { return std::chrono::duration_cast>(std::chrono::microseconds(value.totalMicroseconds())); } explicit operator UInt64() const { return value.totalMicroseconds() / microseconds_per_unit; } explicit operator Field() const { return operator UInt64(); } Poco::Timespan::TimeDiff totalMicroseconds() const { return value.totalMicroseconds(); } Poco::Timespan::TimeDiff totalMilliseconds() const { return value.totalMilliseconds(); } Poco::Timespan::TimeDiff totalSeconds() const { return value.totalSeconds(); } String toString() const; void parseFromString(const String & str); void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; using SettingFieldSeconds = SettingFieldTimespan; using SettingFieldMilliseconds = SettingFieldTimespan; struct SettingFieldString { String value; bool changed = false; explicit SettingFieldString(const std::string_view & str = {}) : value(str) {} explicit SettingFieldString(const String & str) : SettingFieldString(std::string_view{str}) {} explicit SettingFieldString(String && str) : value(std::move(str)) {} explicit SettingFieldString(const char * str) : SettingFieldString(std::string_view{str}) {} explicit SettingFieldString(const Field & f) : SettingFieldString(f.safeGet()) {} SettingFieldString & operator =(const std::string_view & str) { value = str; changed = true; return *this; } SettingFieldString & operator =(const String & str) { *this = std::string_view{str}; return *this; } SettingFieldString & operator =(String && str) { value = std::move(str); changed = true; return *this; } SettingFieldString & operator =(const char * str) { *this = std::string_view{str}; return *this; } SettingFieldString & operator =(const Field & f) { *this = f.safeGet(); return *this; } operator const String &() const { return value; } explicit operator Field() const { return value; } const String & toString() const { return value; } void parseFromString(const String & str) { *this = str; } void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; struct SettingFieldChar { public: char value; bool changed = false; explicit SettingFieldChar(char c = '\0') : value(c) {} explicit SettingFieldChar(const Field & f); SettingFieldChar & operator =(char c) { value = c; changed = true; return *this; } SettingFieldChar & operator =(const Field & f); operator char() const { return value; } explicit operator Field() const { return toString(); } String toString() const { return String(&value, 1); } void parseFromString(const String & str); void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; struct SettingFieldURI { Poco::URI value; bool changed = false; explicit SettingFieldURI(const Poco::URI & uri = {}) : value(uri) {} explicit SettingFieldURI(const String & str) : SettingFieldURI(Poco::URI{str}) {} explicit SettingFieldURI(const char * str) : SettingFieldURI(Poco::URI{str}) {} explicit SettingFieldURI(const Field & f) : SettingFieldURI(f.safeGet()) {} SettingFieldURI & operator =(const Poco::URI & x) { value = x; changed = true; return *this; } SettingFieldURI & operator =(const String & str) { *this = Poco::URI{str}; return *this; } SettingFieldURI & operator =(const char * str) { *this = Poco::URI{str}; return *this; } SettingFieldURI & operator =(const Field & f) { *this = f.safeGet(); return *this; } operator const Poco::URI &() const { return value; } explicit operator String() const { return toString(); } explicit operator Field() const { return toString(); } String toString() const { return value.toString(); } void parseFromString(const String & str) { *this = str; } void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; /** Template class to define enum-based settings. * Example of usage: * * mysettings.h: * enum Gender { Male, Female }; * DECLARE_SETTING_ENUM(SettingFieldGender, Gender) * * mysettings.cpp: * IMPLEMENT_SETTING_ENUM(SettingFieldGender, ErrorCodes::BAD_ARGUMENTS, * {{"Male", Gender::Male}, {"Female", Gender::Female}}) */ template struct SettingFieldEnum { using EnumType = EnumT; EnumType value; bool changed = false; explicit SettingFieldEnum(EnumType x = EnumType{0}) : value(x) {} explicit SettingFieldEnum(const Field & f) : SettingFieldEnum(Traits::fromString(f.safeGet())) {} SettingFieldEnum & operator =(EnumType x) { value = x; changed = true; return *this; } SettingFieldEnum & operator =(const Field & f) { *this = Traits::fromString(f.safeGet()); return *this; } operator EnumType() const { return value; } explicit operator Field() const { return toString(); } String toString() const { return Traits::toString(value); } void parseFromString(const String & str) { *this = Traits::fromString(str); } void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; struct SettingFieldEnumHelpers { static void writeBinary(const std::string_view & str, WriteBuffer & out); static String readBinary(ReadBuffer & in); }; template void SettingFieldEnum::writeBinary(WriteBuffer & out) const { SettingFieldEnumHelpers::writeBinary(toString(), out); } template void SettingFieldEnum::readBinary(ReadBuffer & in) { *this = Traits::fromString(SettingFieldEnumHelpers::readBinary(in)); } #define DECLARE_SETTING_ENUM(ENUM_TYPE) \ DECLARE_SETTING_ENUM_WITH_RENAME(ENUM_TYPE, ENUM_TYPE) #define IMPLEMENT_SETTING_ENUM(ENUM_TYPE, ERROR_CODE_FOR_UNEXPECTED_NAME, ...) \ IMPLEMENT_SETTING_ENUM_WITH_RENAME(ENUM_TYPE, ERROR_CODE_FOR_UNEXPECTED_NAME, __VA_ARGS__) #define DECLARE_SETTING_ENUM_WITH_RENAME(NEW_NAME, ENUM_TYPE) \ struct SettingField##NEW_NAME##Traits \ { \ using EnumType = ENUM_TYPE; \ static const String & toString(EnumType value); \ static EnumType fromString(const std::string_view & str); \ }; \ \ using SettingField##NEW_NAME = SettingFieldEnum; #define IMPLEMENT_SETTING_ENUM_WITH_RENAME(NEW_NAME, ERROR_CODE_FOR_UNEXPECTED_NAME, ...) \ const String & SettingField##NEW_NAME##Traits::toString(typename SettingField##NEW_NAME::EnumType value) \ { \ static const std::unordered_map map = [] { \ std::unordered_map res; \ constexpr std::pair pairs[] = __VA_ARGS__; \ for (const auto & [name, val] : pairs) \ res.emplace(val, name); \ return res; \ }(); \ auto it = map.find(value); \ if (it != map.end()) \ return it->second; \ throw Exception( \ "Unexpected value of " #NEW_NAME ":" + std::to_string(std::underlying_type::type(value)), \ ERROR_CODE_FOR_UNEXPECTED_NAME); \ } \ \ typename SettingField##NEW_NAME::EnumType SettingField##NEW_NAME##Traits::fromString(const std::string_view & str) \ { \ static const std::unordered_map map = [] { \ std::unordered_map res; \ constexpr std::pair pairs[] = __VA_ARGS__; \ for (const auto & [name, val] : pairs) \ res.emplace(name, val); \ return res; \ }(); \ auto it = map.find(str); \ if (it != map.end()) \ return it->second; \ String msg = "Unexpected value of " #NEW_NAME ": '" + String{str} + "'. Must be one of ["; \ bool need_comma = false; \ for (auto & name : map | boost::adaptors::map_keys) \ { \ if (std::exchange(need_comma, true)) \ msg += ", "; \ msg += "'" + String{name} + "'"; \ } \ msg += "]"; \ throw Exception(msg, ERROR_CODE_FOR_UNEXPECTED_NAME); \ } // Mostly like SettingFieldEnum, but can have multiple enum values (or none) set at once. template struct SettingFieldMultiEnum { using EnumType = Enum; using ValueType = MultiEnum; using StorageType = typename ValueType::StorageType; ValueType value; bool changed = false; explicit SettingFieldMultiEnum(ValueType v = ValueType{}) : value{v} {} explicit SettingFieldMultiEnum(EnumType e) : value{e} {} explicit SettingFieldMultiEnum(StorageType s) : value(s) {} explicit SettingFieldMultiEnum(const Field & f) : value(parseValueFromString(f.safeGet())) {} operator ValueType() const { return value; } explicit operator StorageType() const { return value.getValue(); } explicit operator Field() const { return toString(); } SettingFieldMultiEnum & operator= (StorageType x) { changed = x != value.getValue(); value.setValue(x); return *this; } SettingFieldMultiEnum & operator= (ValueType x) { changed = !(x == value); value = x; return *this; } SettingFieldMultiEnum & operator= (const Field & x) { parseFromString(x.safeGet()); return *this; } String toString() const { static const String separator = ","; String result; for (StorageType i = 0; i < Traits::getEnumSize(); ++i) { const auto v = static_cast(i); if (value.isSet(v)) { result += Traits::toString(v); result += separator; } } if (result.size() > 0) result.erase(result.size() - separator.size()); return result; } void parseFromString(const String & str) { *this = parseValueFromString(str); } void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); private: static ValueType parseValueFromString(const std::string_view str) { static const String separators=", "; ValueType result; //to avoid allocating memory on substr() const std::string_view str_view{str}; auto value_start = str_view.find_first_not_of(separators); while (value_start != std::string::npos) { auto value_end = str_view.find_first_of(separators, value_start + 1); if (value_end == std::string::npos) value_end = str_view.size(); result.set(Traits::fromString(str_view.substr(value_start, value_end - value_start))); value_start = str_view.find_first_not_of(separators, value_end); } return result; } }; template void SettingFieldMultiEnum::writeBinary(WriteBuffer & out) const { SettingFieldEnumHelpers::writeBinary(toString(), out); } template void SettingFieldMultiEnum::readBinary(ReadBuffer & in) { parseFromString(SettingFieldEnumHelpers::readBinary(in)); } #define DECLARE_SETTING_MULTI_ENUM(ENUM_TYPE) \ DECLARE_SETTING_MULTI_ENUM_WITH_RENAME(ENUM_TYPE, ENUM_TYPE) #define DECLARE_SETTING_MULTI_ENUM_WITH_RENAME(ENUM_TYPE, NEW_NAME) \ struct SettingField##NEW_NAME##Traits \ { \ using EnumType = ENUM_TYPE; \ static size_t getEnumSize(); \ static const String & toString(EnumType value); \ static EnumType fromString(const std::string_view & str); \ }; \ \ using SettingField##NEW_NAME = SettingFieldMultiEnum; #define IMPLEMENT_SETTING_MULTI_ENUM(ENUM_TYPE, ERROR_CODE_FOR_UNEXPECTED_NAME, ...) \ IMPLEMENT_SETTING_MULTI_ENUM_WITH_RENAME(ENUM_TYPE, ERROR_CODE_FOR_UNEXPECTED_NAME, __VA_ARGS__) #define IMPLEMENT_SETTING_MULTI_ENUM_WITH_RENAME(NEW_NAME, ERROR_CODE_FOR_UNEXPECTED_NAME, ...) \ IMPLEMENT_SETTING_ENUM_WITH_RENAME(NEW_NAME, ERROR_CODE_FOR_UNEXPECTED_NAME, __VA_ARGS__)\ size_t SettingField##NEW_NAME##Traits::getEnumSize() {\ return std::initializer_list> __VA_ARGS__ .size();\ } /// Can keep a value of any type. Used for user-defined settings. struct SettingFieldCustom { Field value; bool changed = false; explicit SettingFieldCustom(const Field & f = {}) : value(f) {} SettingFieldCustom & operator =(const Field & f) { value = f; changed = true; return *this; } explicit operator Field() const { return value; } String toString() const; void parseFromString(const String & str); void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; }