#pragma once #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; } /// NOLINT 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 SettingFieldUInt32 = SettingFieldNumber; using SettingFieldInt32 = SettingFieldNumber; using SettingFieldFloat = SettingFieldNumber; using SettingFieldDouble = SettingFieldNumber; using SettingFieldBool = SettingFieldNumber; /** Wraps any SettingField to support special value 'auto' that can be checked with `is_auto` flag. * Note about serialization: * The new versions with `SettingsWriteFormat::STRINGS_WITH_FLAGS` serialize values as a string. * In legacy SettingsWriteFormat mode, functions `read/writeBinary` would serialize values as a binary, and 'is_auto' would be ignored. * It's possible to upgrade settings from regular type to wrapped ones and keep compatibility with old versions, * but when serializing 'auto' old version will see binary representation of the default value. */ template struct SettingAutoWrapper { constexpr static auto keyword = "auto"; static bool isAuto(const Field & f) { return f.getType() == Field::Types::String && f.safeGet() == keyword; } static bool isAuto(const String & str) { return str == keyword; } using Type = typename Base::Type; Base base; bool is_auto = false; bool changed = false; explicit SettingAutoWrapper() : is_auto(true) {} explicit SettingAutoWrapper(Type val) : is_auto(false) { base = Base(val); } explicit SettingAutoWrapper(const Field & f) : is_auto(isAuto(f)) { if (!is_auto) base = Base(f); } SettingAutoWrapper & operator=(const Field & f) { changed = true; if (is_auto = isAuto(f); !is_auto) base = f; return *this; } explicit operator Field() const { return is_auto ? Field(keyword) : Field(base); } String toString() const { return is_auto ? keyword : base.toString(); } void parseFromString(const String & str) { changed = true; if (is_auto = isAuto(str); !is_auto) base.parseFromString(str); } void writeBinary(WriteBuffer & out) const { if (is_auto) Base().writeBinary(out); /// serialize default value else base.writeBinary(out); } /* * That it is fine to reset `is_auto` here and to use default value in case `is_auto` * because settings will be serialized only if changed. * If they were changed they were requested to use explicit value instead of `auto`. * And so interactions between client-server, and server-server (distributed queries), should be OK. */ void readBinary(ReadBuffer & in) { changed = true; is_auto = false; base.readBinary(in); } Type valueOr(Type default_value) const { return is_auto ? default_value : base.value; } }; using SettingFieldUInt64Auto = SettingAutoWrapper; using SettingFieldInt64Auto = SettingAutoWrapper; using SettingFieldFloatAuto = SettingAutoWrapper; using SettingFieldUInt32Auto = SettingAutoWrapper; using SettingFieldInt32Auto = SettingAutoWrapper; using SettingFieldDoubleAuto = SettingAutoWrapper; /* Similar to SettingFieldUInt64Auto with small differences to behave like regular UInt64, supported to compatibility. * When setting to 'auto' it becomes equal to the number of processor cores without taking into account SMT. * A value of 0 is also treated as 'auto', so 'auto' is parsed and serialized 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; } /// NOLINT 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 : uint8_t { 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; } /// NOLINT template > operator std::chrono::duration() const { return std::chrono::duration_cast>(std::chrono::microseconds(value.totalMicroseconds())); } /// NOLINT explicit operator UInt64() const { return value.totalMicroseconds() / microseconds_per_unit; } explicit operator Field() const; 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(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 =(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; } /// NOLINT 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 SettingFieldMap { public: Map value; bool changed = false; explicit SettingFieldMap(const Map & map = {}) : value(map) {} explicit SettingFieldMap(Map && map) : value(std::move(map)) {} explicit SettingFieldMap(const Field & f); SettingFieldMap & operator =(const Map & map) { value = map; changed = true; return *this; } SettingFieldMap & operator =(const Field & f); operator const Map &() const { return value; } /// NOLINT explicit operator Field() const { return value; } String toString() const; void parseFromString(const String & str); void writeBinary(WriteBuffer & out) const; void readBinary(ReadBuffer & in); }; #undef NORETURN 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; } /// NOLINT 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; } /// NOLINT 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; } /// NOLINT 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(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)); } // Mostly like SettingFieldEnum, but can have multiple enum values (or none) set at once. template struct SettingFieldMultiEnum { using EnumType = Enum; using ValueType = std::vector; ValueType value; bool changed = false; explicit SettingFieldMultiEnum(ValueType v = ValueType{}) : value{v} {} explicit SettingFieldMultiEnum(EnumType e) : value{e} {} explicit SettingFieldMultiEnum(const Field & f) : value(parseValueFromString(f.safeGet())) {} operator ValueType() const { return value; } /// NOLINT explicit operator Field() const { return toString(); } operator MultiEnum() const /// NOLINT { MultiEnum res; for (const auto & v : value) res.set(v); return res; } SettingFieldMultiEnum & operator= (ValueType x) { changed = true; value = x; return *this; } SettingFieldMultiEnum & operator= (const Field & x) { parseFromString(x.safeGet()); return *this; } String toString() const { static const String separator = ","; String result; for (const auto & v : value) { result += Traits::toString(v); result += separator; } if (!result.empty()) 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; std::unordered_set values_set; //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(); auto value = Traits::fromString(str_view.substr(value_start, value_end - value_start)); /// Deduplicate values auto [_, inserted] = values_set.emplace(value); if (inserted) result.push_back(value); 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)); } /// Setting field for specifying user-defined timezone. It is basically a string, but it needs validation. struct SettingFieldTimezone { String value; bool changed = false; explicit SettingFieldTimezone(std::string_view str = {}) { validateTimezone(std::string(str)); value = str; } explicit SettingFieldTimezone(const String & str) { validateTimezone(str); value = str; } explicit SettingFieldTimezone(String && str) { validateTimezone(str); value = std::move(str); } explicit SettingFieldTimezone(const char * str) { validateTimezone(str); value = str; } explicit SettingFieldTimezone(const Field & f) { const String & str = f.safeGet(); validateTimezone(str); value = str; } SettingFieldTimezone & operator =(std::string_view str) { validateTimezone(std::string(str)); value = str; changed = true; return *this; } SettingFieldTimezone & operator =(const String & str) { *this = std::string_view{str}; return *this; } SettingFieldTimezone & operator =(String && str) { validateTimezone(str); value = std::move(str); changed = true; return *this; } SettingFieldTimezone & operator =(const char * str) { *this = std::string_view{str}; return *this; } SettingFieldTimezone & operator =(const Field & f) { *this = f.safeGet(); return *this; } operator const String &() const { return value; } /// NOLINT 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); private: void validateTimezone(const std::string & tz_str); }; /// 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); }; struct SettingFieldNonZeroUInt64 : public SettingFieldUInt64 { public: explicit SettingFieldNonZeroUInt64(UInt64 x = 1); explicit SettingFieldNonZeroUInt64(const Field & f); SettingFieldNonZeroUInt64 & operator=(UInt64 x); SettingFieldNonZeroUInt64 & operator=(const Field & f); void parseFromString(const String & str); private: void checkValueNonZero() const; }; }