2014-02-17 23:56:45 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <Poco/Timespan.h>
|
2018-03-11 00:15:26 +00:00
|
|
|
#include <DataStreams/SizeLimits.h>
|
2018-06-10 19:22:49 +00:00
|
|
|
#include <Formats/FormatSettings.h>
|
2019-04-18 23:29:32 +00:00
|
|
|
#include <common/StringRef.h>
|
|
|
|
#include <Common/SettingsChanges.h>
|
2018-12-21 13:25:39 +00:00
|
|
|
#include <Compression/CompressionInfo.h>
|
2018-06-05 19:46:49 +00:00
|
|
|
#include <Core/Types.h>
|
2019-04-18 23:29:32 +00:00
|
|
|
#include <ext/singleton.h>
|
|
|
|
#include <unordered_map>
|
2015-10-12 07:05:54 +00:00
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
class Field;
|
|
|
|
class ReadBuffer;
|
|
|
|
class WriteBuffer;
|
2016-01-11 21:46:36 +00:00
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
|
2017-06-02 21:37:28 +00:00
|
|
|
/** 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.
|
2014-02-17 23:56:45 +00:00
|
|
|
*/
|
|
|
|
|
2016-02-12 02:26:04 +00:00
|
|
|
template <typename IntType>
|
|
|
|
struct SettingInt
|
2014-02-17 23:56:45 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
IntType value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingInt(IntType x = 0) : value(x) {}
|
|
|
|
|
|
|
|
operator IntType() const { return value; }
|
|
|
|
SettingInt & operator= (IntType x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
/// Serialize to a test string.
|
|
|
|
String toString() const;
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
/// Converts to a field.
|
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(IntType x);
|
|
|
|
|
|
|
|
/// Read from SQL literal.
|
|
|
|
void set(const Field & x);
|
|
|
|
|
|
|
|
/// Read from text string.
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
/// Serialize to binary stream suitable for transfer over network.
|
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
/// Read from binary stream.
|
2019-04-18 23:29:32 +00:00
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
2016-02-12 02:26:04 +00:00
|
|
|
using SettingUInt64 = SettingInt<UInt64>;
|
|
|
|
using SettingInt64 = SettingInt<Int64>;
|
|
|
|
using SettingBool = SettingUInt64;
|
2014-02-17 23:56:45 +00:00
|
|
|
|
|
|
|
|
2017-06-02 21:37:28 +00:00
|
|
|
/** Unlike SettingUInt64, 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.
|
2015-02-22 10:50:36 +00:00
|
|
|
*/
|
|
|
|
struct SettingMaxThreads
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
UInt64 value;
|
|
|
|
bool is_auto;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingMaxThreads(UInt64 x = 0) : value(x ? x : getAutoValue()), is_auto(x == 0) {}
|
|
|
|
|
|
|
|
operator UInt64() const { return value; }
|
|
|
|
SettingMaxThreads & operator= (UInt64 x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(UInt64 x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void setAuto();
|
|
|
|
UInt64 getAutoValue() const;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-06-02 21:37:28 +00:00
|
|
|
/// Executed once for all time. Executed from one thread.
|
2018-06-05 19:46:49 +00:00
|
|
|
UInt64 getAutoValueImpl() const;
|
2015-02-22 10:50:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
struct SettingSeconds
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
Poco::Timespan value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingSeconds(UInt64 seconds = 0) : value(seconds, 0) {}
|
|
|
|
|
|
|
|
operator Poco::Timespan() const { return value; }
|
2017-09-08 02:37:02 +00:00
|
|
|
SettingSeconds & operator= (const Poco::Timespan & x) { set(x); return *this; }
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
Poco::Timespan::TimeDiff totalSeconds() const { return value.totalSeconds(); }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(const Poco::Timespan & x);
|
|
|
|
|
|
|
|
void set(UInt64 x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct SettingMilliseconds
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
Poco::Timespan value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingMilliseconds(UInt64 milliseconds = 0) : value(milliseconds * 1000) {}
|
|
|
|
|
|
|
|
operator Poco::Timespan() const { return value; }
|
2017-09-08 02:29:47 +00:00
|
|
|
SettingMilliseconds & operator= (const Poco::Timespan & x) { set(x); return *this; }
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
Poco::Timespan::TimeDiff totalMilliseconds() const { return value.totalMilliseconds(); }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(const Poco::Timespan & x);
|
|
|
|
void set(UInt64 x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
2019-04-18 23:29:32 +00:00
|
|
|
|
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct SettingFloat
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
float value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingFloat(float x = 0) : value(x) {}
|
|
|
|
|
|
|
|
operator float() const { return value; }
|
|
|
|
SettingFloat & operator= (float x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(float x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-02-19 13:27:59 +00:00
|
|
|
/// TODO: X macro
|
2014-02-17 23:56:45 +00:00
|
|
|
enum class LoadBalancing
|
|
|
|
{
|
2017-06-02 21:37:28 +00:00
|
|
|
/// among replicas with a minimum number of errors selected randomly
|
2017-04-01 07:20:54 +00:00
|
|
|
RANDOM = 0,
|
2017-06-02 21:37:28 +00:00
|
|
|
/// a replica is selected among the replicas with the minimum number of errors
|
|
|
|
/// with the minimum number of distinguished characters in the replica name and local hostname
|
2017-04-01 07:20:54 +00:00
|
|
|
NEAREST_HOSTNAME,
|
2017-06-02 21:37:28 +00:00
|
|
|
/// replicas are walked through strictly in order; the number of errors does not matter
|
2017-04-01 07:20:54 +00:00
|
|
|
IN_ORDER,
|
2019-04-15 21:54:18 +00:00
|
|
|
/// if first replica one has higher number of errors,
|
|
|
|
/// pick a random one from replicas with minimum number of errors
|
|
|
|
FIRST_OR_RANDOM,
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct SettingLoadBalancing
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
LoadBalancing value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingLoadBalancing(LoadBalancing x) : value(x) {}
|
|
|
|
|
|
|
|
operator LoadBalancing() const { return value; }
|
|
|
|
SettingLoadBalancing & operator= (LoadBalancing x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
static LoadBalancing getLoadBalancing(const String & s);
|
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(LoadBalancing x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-08-29 15:15:42 +00:00
|
|
|
enum class JoinStrictness
|
|
|
|
{
|
|
|
|
Unspecified = 0, /// Query JOIN without strictness will throw Exception.
|
|
|
|
ALL, /// Query JOIN without strictness -> ALL JOIN ...
|
|
|
|
ANY, /// Query JOIN without strictness -> ANY JOIN ...
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct SettingJoinStrictness
|
|
|
|
{
|
|
|
|
JoinStrictness value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingJoinStrictness(JoinStrictness x) : value(x) {}
|
|
|
|
|
|
|
|
operator JoinStrictness() const { return value; }
|
|
|
|
SettingJoinStrictness & operator= (JoinStrictness x) { set(x); return *this; }
|
|
|
|
|
|
|
|
static JoinStrictness getJoinStrictness(const String & s);
|
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-08-29 15:15:42 +00:00
|
|
|
|
|
|
|
void set(JoinStrictness x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2018-08-29 15:15:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-06-02 21:37:28 +00:00
|
|
|
/// Which rows should be included in TOTALS.
|
2014-02-18 09:02:35 +00:00
|
|
|
enum class TotalsMode
|
|
|
|
{
|
2017-06-02 21:37:28 +00:00
|
|
|
BEFORE_HAVING = 0, /// Count HAVING for all read rows;
|
|
|
|
/// including those not in max_rows_to_group_by
|
|
|
|
/// and have not passed HAVING after grouping.
|
|
|
|
AFTER_HAVING_INCLUSIVE = 1, /// Count on all rows except those that have not passed HAVING;
|
|
|
|
/// that is, to include in TOTALS all the rows that did not pass max_rows_to_group_by.
|
|
|
|
AFTER_HAVING_EXCLUSIVE = 2, /// Include only the rows that passed and max_rows_to_group_by, and HAVING.
|
|
|
|
AFTER_HAVING_AUTO = 3, /// Automatically select between INCLUSIVE and EXCLUSIVE,
|
2014-02-18 09:02:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct SettingTotalsMode
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
TotalsMode value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingTotalsMode(TotalsMode x) : value(x) {}
|
|
|
|
|
|
|
|
operator TotalsMode() const { return value; }
|
|
|
|
SettingTotalsMode & operator= (TotalsMode x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
static TotalsMode getTotalsMode(const String & s);
|
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(TotalsMode x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-18 09:02:35 +00:00
|
|
|
};
|
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
|
|
|
|
template <bool enable_mode_any>
|
|
|
|
struct SettingOverflowMode
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
OverflowMode value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingOverflowMode(OverflowMode x = OverflowMode::THROW) : value(x) {}
|
|
|
|
|
|
|
|
operator OverflowMode() const { return value; }
|
|
|
|
SettingOverflowMode & operator= (OverflowMode x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
static OverflowMode getOverflowModeForGroupBy(const String & s);
|
|
|
|
static OverflowMode getOverflowMode(const String & s);
|
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(OverflowMode x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2014-02-17 23:56:45 +00:00
|
|
|
};
|
|
|
|
|
2017-06-02 21:37:28 +00:00
|
|
|
/// The setting for executing distributed subqueries inside IN or JOIN sections.
|
2015-09-18 13:36:10 +00:00
|
|
|
enum class DistributedProductMode
|
|
|
|
{
|
2017-06-02 21:37:28 +00:00
|
|
|
DENY = 0, /// Disable
|
|
|
|
LOCAL, /// Convert to local query
|
|
|
|
GLOBAL, /// Convert to global query
|
|
|
|
ALLOW /// Enable
|
2015-09-18 13:36:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct SettingDistributedProductMode
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
DistributedProductMode value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingDistributedProductMode(DistributedProductMode x) : value(x) {}
|
|
|
|
|
|
|
|
operator DistributedProductMode() const { return value; }
|
|
|
|
SettingDistributedProductMode & operator= (DistributedProductMode x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
static DistributedProductMode getDistributedProductMode(const String & s);
|
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(DistributedProductMode x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
2019-04-18 23:29:32 +00:00
|
|
|
|
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2015-09-18 13:36:10 +00:00
|
|
|
};
|
|
|
|
|
2016-07-10 15:52:35 +00:00
|
|
|
|
|
|
|
struct SettingString
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
String value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingString(const String & x = String{}) : value(x) {}
|
|
|
|
|
|
|
|
operator String() const { return value; }
|
|
|
|
SettingString & operator= (const String & x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(const String & x);
|
|
|
|
void set(const Field & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2016-07-10 15:52:35 +00:00
|
|
|
};
|
|
|
|
|
2018-04-27 20:12:26 +00:00
|
|
|
|
2018-04-21 23:38:01 +00:00
|
|
|
struct SettingChar
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
char value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingChar(char x = '\0') : value(x) {}
|
|
|
|
|
|
|
|
operator char() const { return value; }
|
|
|
|
SettingChar & operator= (char x) { set(x); return *this; }
|
|
|
|
|
2018-06-05 19:46:49 +00:00
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-05 19:46:49 +00:00
|
|
|
|
|
|
|
void set(char x);
|
|
|
|
void set(const String & x);
|
|
|
|
void set(const Field & x);
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2018-04-21 23:38:01 +00:00
|
|
|
};
|
2018-06-05 19:46:49 +00:00
|
|
|
|
2018-06-08 03:15:33 +00:00
|
|
|
|
|
|
|
struct SettingDateTimeInputFormat
|
|
|
|
{
|
|
|
|
using Value = FormatSettings::DateTimeInputFormat;
|
|
|
|
|
|
|
|
Value value;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
SettingDateTimeInputFormat(Value x) : value(x) {}
|
|
|
|
|
|
|
|
operator Value() const { return value; }
|
|
|
|
SettingDateTimeInputFormat & operator= (Value x) { set(x); return *this; }
|
|
|
|
|
|
|
|
static Value getValue(const String & s);
|
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2018-06-08 03:15:33 +00:00
|
|
|
|
|
|
|
void set(Value x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
2019-04-18 23:29:32 +00:00
|
|
|
|
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2018-06-08 03:15:33 +00:00
|
|
|
};
|
|
|
|
|
2018-12-18 21:18:54 +00:00
|
|
|
|
2019-02-19 13:27:59 +00:00
|
|
|
enum class LogsLevel
|
|
|
|
{
|
|
|
|
none = 0, /// Disable
|
|
|
|
error,
|
|
|
|
warning,
|
|
|
|
information,
|
|
|
|
debug,
|
|
|
|
trace,
|
|
|
|
};
|
|
|
|
|
2018-12-18 21:18:54 +00:00
|
|
|
class SettingLogsLevel
|
|
|
|
{
|
|
|
|
public:
|
2019-02-19 13:27:59 +00:00
|
|
|
using Value = LogsLevel;
|
2018-12-18 21:18:54 +00:00
|
|
|
|
2019-02-19 13:27:59 +00:00
|
|
|
Value value;
|
2018-12-18 21:18:54 +00:00
|
|
|
bool changed = false;
|
|
|
|
|
2019-02-19 13:27:59 +00:00
|
|
|
SettingLogsLevel(Value x) : value(x) {}
|
|
|
|
|
|
|
|
operator Value() const { return value; }
|
|
|
|
SettingLogsLevel & operator= (Value x) { set(x); return *this; }
|
|
|
|
|
|
|
|
static Value getValue(const String & s);
|
2018-12-18 21:18:54 +00:00
|
|
|
|
|
|
|
String toString() const;
|
2019-04-18 23:29:32 +00:00
|
|
|
Field toField() const;
|
2019-02-19 13:27:59 +00:00
|
|
|
|
|
|
|
void set(Value x);
|
|
|
|
void set(const Field & x);
|
|
|
|
void set(const String & x);
|
2019-04-18 23:29:32 +00:00
|
|
|
|
|
|
|
void serialize(WriteBuffer & buf) const;
|
|
|
|
void deserialize(ReadBuffer & buf);
|
2018-12-18 21:18:54 +00:00
|
|
|
};
|
|
|
|
|
2019-04-18 23:29:32 +00:00
|
|
|
|
|
|
|
namespace details
|
|
|
|
{
|
|
|
|
struct SettingsCollectionUtils
|
|
|
|
{
|
|
|
|
static void serializeName(const StringRef & name, WriteBuffer & buf);
|
|
|
|
static String deserializeName(ReadBuffer & buf);
|
|
|
|
static void throwNameNotFound(const StringRef & name);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Template class to define collections of settings.
|
|
|
|
* Example of usage:
|
|
|
|
*
|
|
|
|
* mysettings.h:
|
|
|
|
* struct MySettings : public SettingsCollection<MySettings>
|
|
|
|
* {
|
|
|
|
* # define APPLY_FOR_MYSETTINGS(M) \
|
|
|
|
* M(SettingUInt64, a, 100, "Description of a") \
|
|
|
|
* M(SettingFloat, f, 3.11, "Description of f") \
|
|
|
|
* M(SettingString, s, "default", "Description of s")
|
|
|
|
*
|
|
|
|
* DECLARE_SETTINGS_COLLECTION(MySettings, APPLY_FOR_MYSETTINGS)
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* mysettings.cpp:
|
|
|
|
* IMPLEMENT_SETTINGS_COLLECTION(MySettings, APPLY_FOR_MYSETTINGS)
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
class SettingsCollection
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
using Type = T;
|
|
|
|
using GetStringFunction = String (*)(const Type &);
|
|
|
|
using GetFieldFunction = Field (*)(const Type &);
|
|
|
|
using SetStringFunction = void (*)(Type &, const String &);
|
|
|
|
using SetFieldFunction = void (*)(Type &, const Field &);
|
|
|
|
using SerializeFunction = void (*)(const Type &, WriteBuffer & buf);
|
|
|
|
using DeserializeFunction = void (*)(Type &, ReadBuffer & buf);
|
|
|
|
|
|
|
|
struct MemberInfo
|
|
|
|
{
|
|
|
|
size_t offset_of_changed;
|
|
|
|
StringRef name;
|
|
|
|
StringRef description;
|
|
|
|
GetStringFunction get_string;
|
|
|
|
GetFieldFunction get_field;
|
|
|
|
SetStringFunction set_string;
|
|
|
|
SetFieldFunction set_field;
|
|
|
|
SerializeFunction serialize;
|
|
|
|
DeserializeFunction deserialize;
|
|
|
|
bool isChanged(const Type & collection) const { return *reinterpret_cast<const bool*>(reinterpret_cast<const UInt8*>(&collection) + offset_of_changed); }
|
|
|
|
};
|
|
|
|
|
|
|
|
using MemberInfos = std::vector<MemberInfo>;
|
|
|
|
|
|
|
|
class Layout
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
size_t size() const { return infos.size(); }
|
|
|
|
const MemberInfo & operator[](size_t index) const { return infos[index]; }
|
|
|
|
|
|
|
|
static constexpr size_t npos = static_cast<size_t>(-1);
|
|
|
|
|
|
|
|
size_t find(const StringRef & name) const
|
|
|
|
{
|
|
|
|
auto map_it = by_name_map.find(name);
|
|
|
|
if (map_it == by_name_map.end())
|
|
|
|
return npos;
|
|
|
|
else
|
|
|
|
return map_it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t findStrict(const StringRef & name) const
|
|
|
|
{
|
|
|
|
auto map_it = by_name_map.find(name);
|
|
|
|
if (map_it == by_name_map.end())
|
|
|
|
details::SettingsCollectionUtils::throwNameNotFound(name);
|
|
|
|
return map_it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addMemberInfo(MemberInfo && info)
|
|
|
|
{
|
|
|
|
size_t index = infos.size();
|
|
|
|
infos.emplace_back(info);
|
|
|
|
by_name_map.emplace(infos.back().name, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
MemberInfos infos;
|
|
|
|
std::unordered_map<StringRef, size_t> by_name_map;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const Layout & getLayout();
|
|
|
|
Type & castThis() { return static_cast<Type &>(*this); }
|
|
|
|
const Type & castThis() const { return static_cast<const Type &>(*this); }
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// Provides access to a setting.
|
|
|
|
class reference
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
const StringRef & getName() const { return member.name; }
|
|
|
|
const StringRef & getDescription() const { return member.description; }
|
|
|
|
bool isChanged() const { return member.isChanged(collection); }
|
|
|
|
Field getValue() const { return member.get_field(collection); }
|
|
|
|
String getValueAsString() const { return member.get_string(collection); }
|
|
|
|
void setValue(const Field & value) { member.set_field(collection, value); }
|
|
|
|
void setValue(const String & value) { member.set_string(collection, value); }
|
|
|
|
|
|
|
|
reference(Type & collection_, const MemberInfo & member_) : collection(collection_), member(member_) {}
|
|
|
|
private:
|
|
|
|
Type & collection;
|
|
|
|
const MemberInfo & member;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Provides read-only access to a setting.
|
|
|
|
class const_reference
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
const StringRef & getName() const { return member.name; }
|
|
|
|
const StringRef & getDescription() const { return member.description; }
|
|
|
|
bool isChanged() const { return member.isChanged(collection); }
|
|
|
|
Field getValue() const { return member.get_field(collection); }
|
|
|
|
String getValueAsString() const { return member.get_string(collection); }
|
|
|
|
|
|
|
|
const_reference(const Type & collection_, const MemberInfo & member_) : collection(collection_), member(member_) {}
|
|
|
|
private:
|
|
|
|
const Type & collection;
|
|
|
|
const MemberInfo & member;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Returns number of settings.
|
|
|
|
size_t size() const { return getLayout().size(); }
|
|
|
|
|
|
|
|
/// Returns a setting by its zero-based index.
|
|
|
|
reference operator [](size_t index) { return reference(castThis(), getLayout()[index]); }
|
|
|
|
const_reference operator [](size_t index) const { return const_reference(castThis(), getLayout()[index]); }
|
|
|
|
|
|
|
|
/// Returns a setting by its name. Throws an exception if there is not setting with such name.
|
|
|
|
reference operator [](const String & name)
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
return reference(castThis(), layout[layout.findStrict(name)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reference operator [](const String & name) const
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
return const_reference(castThis(), layout[layout.findStrict(name)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Finds a setting by name and returns its index. Returns npos if not found.
|
|
|
|
size_t find(const String & name) const { return getLayout().find(name); }
|
|
|
|
static constexpr size_t npos = static_cast<size_t>(-1);
|
|
|
|
|
|
|
|
/// Finds a setting by name and returns its index. Throws an exception if not found.
|
|
|
|
size_t findStrict(const String & name) const { return getLayout().findStrict(name); }
|
|
|
|
|
|
|
|
/// Sets setting by name.
|
|
|
|
void set(const String & name, const Field & value) { (*this)[name].setValue(value); }
|
|
|
|
|
|
|
|
/// Sets setting by name. Read value in text form from string (for example, from configuration file or from URL parameter).
|
|
|
|
void set(const String & name, const String & value) { (*this)[name].setValue(value); }
|
|
|
|
|
|
|
|
/// Returns the value of a setting.
|
|
|
|
Field get(const String & name) const { return (*this)[name].getValue(); }
|
|
|
|
|
|
|
|
/// Returns the value of a setting converted to string.
|
|
|
|
String getAsString(const String & name) const { return (*this)[name].getValueAsString(); }
|
|
|
|
|
|
|
|
/// Returns the value of a setting.
|
|
|
|
bool tryGet(const String & name, Field & value) const
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
size_t index = layout.find(name);
|
|
|
|
if (index == npos)
|
|
|
|
return false;
|
|
|
|
value = layout[index].get_field(castThis());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the value of a setting converted to string.
|
|
|
|
bool tryGet(const String & name, String & value) const
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
size_t index = layout.find(name);
|
|
|
|
if (index == npos)
|
|
|
|
return false;
|
|
|
|
value = layout[index].get_string(castThis());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compares two collections of settings.
|
|
|
|
bool operator ==(const Type & rhs) const
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
for (size_t i = 0; i != layout.size(); ++i)
|
|
|
|
{
|
|
|
|
const auto & member = layout[i];
|
|
|
|
bool left_changed = member.isChanged(castThis());
|
|
|
|
bool right_changed = member.isChanged(rhs);
|
|
|
|
if (left_changed || right_changed)
|
|
|
|
{
|
|
|
|
if (left_changed != right_changed)
|
|
|
|
return false;
|
|
|
|
if (member.get_field(castThis()) != member.get_field(rhs))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator !=(const Type & rhs) const
|
|
|
|
{
|
|
|
|
return !(*this == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gathers all changed values (e.g. for applying them later to another collection of settings).
|
|
|
|
SettingsChanges changes() const
|
|
|
|
{
|
|
|
|
SettingsChanges found_changes;
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
for (size_t i = 0; i != layout.size(); ++i)
|
|
|
|
{
|
|
|
|
const auto & member = layout[i];
|
|
|
|
if (member.isChanged(castThis()))
|
|
|
|
found_changes.emplace_back(member.name.toString(), member.get_field(castThis()));
|
|
|
|
}
|
|
|
|
return found_changes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Applies changes to the settings.
|
|
|
|
void applyChange(const SettingChange & change)
|
|
|
|
{
|
|
|
|
set(change.name, change.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void applyChanges(const SettingsChanges & changes)
|
|
|
|
{
|
|
|
|
for (const SettingChange & change : changes)
|
|
|
|
applyChange(change);
|
|
|
|
}
|
|
|
|
|
|
|
|
void copyChangesFrom(const Type & src)
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
for (size_t i = 0; i != layout.size(); ++i)
|
|
|
|
{
|
|
|
|
const auto & member = layout[i];
|
|
|
|
if (member.isChanged(src))
|
|
|
|
member.set_field(castThis(), member.get_field(src));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void copyChangesTo(Type & dest) const
|
|
|
|
{
|
|
|
|
dest.copyChangesFrom(castThis());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Writes the settings to buffer (e.g. to be sent to remote server).
|
|
|
|
/// Only changed settings are written. They are written as list of contiguous name-value pairs,
|
|
|
|
/// finished with empty name.
|
|
|
|
void serialize(WriteBuffer & buf) const
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
for (size_t i = 0; i != layout.size(); ++i)
|
|
|
|
{
|
|
|
|
const auto & member = layout[i];
|
|
|
|
if (member.isChanged(castThis()))
|
|
|
|
{
|
|
|
|
details::SettingsCollectionUtils::serializeName(member.name, buf);
|
|
|
|
member.serialize(castThis(), buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
details::SettingsCollectionUtils::serializeName(StringRef{} /* empty string is a marker of the end of settings */, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads the settings from buffer.
|
|
|
|
void deserialize(ReadBuffer & buf)
|
|
|
|
{
|
|
|
|
const Layout & layout = getLayout();
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
String name = details::SettingsCollectionUtils::deserializeName(buf);
|
|
|
|
if (name.empty() /* empty string is a marker of the end of settings */)
|
|
|
|
break; \
|
|
|
|
const auto & member = layout[layout.findStrict(name)];
|
|
|
|
member.deserialize(castThis(), buf); \
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define DECLARE_SETTINGS_COLLECTION(APPLY_MACRO) \
|
|
|
|
APPLY_MACRO(DECLARE_SETTINGS_COLLECTION_DECLARE_VARIABLES_HELPER_)
|
|
|
|
|
|
|
|
|
|
|
|
#define IMPLEMENT_SETTINGS_COLLECTION(CLASS_NAME, APPLY_MACRO) \
|
|
|
|
template<> \
|
|
|
|
const SettingsCollection<CLASS_NAME>::Layout & SettingsCollection<CLASS_NAME>::getLayout() \
|
|
|
|
{ \
|
|
|
|
struct Functions \
|
|
|
|
{ \
|
|
|
|
APPLY_MACRO(IMPLEMENT_SETTINGS_COLLECTION_DEFINE_FUNCTIONS_HELPER_) \
|
|
|
|
}; \
|
|
|
|
static const SettingsCollection<Type>::Layout single_instance = [] \
|
|
|
|
{ \
|
|
|
|
SettingsCollection<Type>::Layout layout; \
|
|
|
|
APPLY_MACRO(IMPLEMENT_SETTINGS_COLLECTION_ADD_MEMBER_INFO_HELPER_) \
|
|
|
|
return layout; \
|
|
|
|
}(); \
|
|
|
|
return single_instance; \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define DECLARE_SETTINGS_COLLECTION_DECLARE_VARIABLES_HELPER_(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
|
|
|
TYPE NAME {DEFAULT};
|
|
|
|
|
|
|
|
|
|
|
|
#define IMPLEMENT_SETTINGS_COLLECTION_DEFINE_FUNCTIONS_HELPER_(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
|
|
|
static String NAME##_getString(const Type & collection) { return collection.NAME.toString(); } \
|
|
|
|
static Field NAME##_getField(const Type & collection) { return collection.NAME.toField(); } \
|
|
|
|
static void NAME##_setString(Type & collection, const String & value) { collection.NAME.set(value); } \
|
|
|
|
static void NAME##_setField(Type & collection, const Field & value) { collection.NAME.set(value); } \
|
|
|
|
static void NAME##_serialize(const Type & collection, WriteBuffer & buf) { collection.NAME.serialize(buf); } \
|
|
|
|
static void NAME##_deserialize(Type & collection, ReadBuffer & buf) { collection.NAME.deserialize(buf); }
|
|
|
|
|
|
|
|
|
|
|
|
#define IMPLEMENT_SETTINGS_COLLECTION_ADD_MEMBER_INFO_HELPER_(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
|
|
|
static_assert(std::is_same_v<decltype(std::declval<TYPE>().changed), bool>); \
|
|
|
|
layout.addMemberInfo({offsetof(Type, NAME.changed), \
|
|
|
|
StringRef(#NAME, strlen(#NAME)), StringRef(#DESCRIPTION, strlen(#DESCRIPTION)), \
|
|
|
|
&Functions::NAME##_getString, &Functions::NAME##_getField, \
|
|
|
|
&Functions::NAME##_setString, &Functions::NAME##_setField, \
|
|
|
|
&Functions::NAME##_serialize, &Functions::NAME##_deserialize });
|
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
}
|