Improve the function range() to allow iterating through enum values.

This commit is contained in:
Vitaly Baranov 2020-05-02 19:05:01 +03:00
parent c7213ab607
commit 6f15a0d443
19 changed files with 107 additions and 59 deletions

View File

@ -1,42 +1,62 @@
#pragma once
#include <type_traits>
#include <boost/range/counting_range.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <type_traits>
namespace ext
{
/// For loop adaptor which is used to iterate through a half-closed interval [begin, end).
template <typename BeginType, typename EndType>
inline auto range(BeginType begin, EndType end)
namespace internal
{
template <typename ResultType, typename CountingType, typename BeginType, typename EndType>
auto rangeImpl(BeginType begin, EndType end)
{
using CommonType = typename std::common_type<BeginType, EndType>::type;
return boost::counting_range<CommonType>(begin, end);
}
template <typename Type>
inline auto range(Type end)
{
return range<Type, Type>(static_cast<Type>(0), end);
}
/// The same as range(), but every value is casted statically to a specified `ValueType`.
/// This is useful to iterate through all constants of a enum.
template <typename ValueType, typename BeginType, typename EndType>
inline auto range_with_static_cast(BeginType begin, EndType end)
{
using CommonType = typename std::common_type<BeginType, EndType>::type;
if constexpr (std::is_same_v<ValueType, CommonType>)
return boost::counting_range<CommonType>(begin, end);
if constexpr (std::is_same_v<ResultType, CountingType>)
return boost::counting_range<CountingType>(static_cast<CountingType>(begin), static_cast<CountingType>(end));
else
return boost::counting_range<CommonType>(begin, end)
| boost::adaptors::transformed([](CommonType x) -> ValueType { return static_cast<ValueType>(x); });
}
template <typename ValueType, typename EndType>
inline auto range_with_static_cast(EndType end)
{
return range_with_static_cast<ValueType, EndType, EndType>(static_cast<EndType>(0), end);
return boost::counting_range<CountingType>(static_cast<CountingType>(begin), static_cast<CountingType>(end))
| boost::adaptors::transformed([](CountingType x) { return static_cast<ResultType>(x); });
}
}
/// For loop adaptor which is used to iterate through a half-closed interval [begin, end).
/// The parameters `begin` and `end` can have any integral or enum types.
template <typename BeginType,
typename EndType,
typename = std::enable_if_t<
(std::is_integral_v<BeginType> || std::is_enum_v<BeginType>) &&
(std::is_integral_v<EndType> || std::is_enum_v<EndType>) &&
(!std::is_enum_v<BeginType> || !std::is_enum_v<EndType> || std::is_same_v<BeginType, EndType>), void>>
inline auto range(BeginType begin, EndType end)
{
if constexpr (std::is_integral_v<BeginType> && std::is_integral_v<EndType>)
{
using CommonType = std::common_type_t<BeginType, EndType>;
return internal::rangeImpl<CommonType, CommonType>(begin, end);
}
else if constexpr (std::is_enum_v<BeginType>)
{
return internal::rangeImpl<BeginType, std::underlying_type_t<BeginType>>(begin, end);
}
else
{
return internal::rangeImpl<EndType, std::underlying_type_t<EndType>>(begin, end);
}
}
/// For loop adaptor which is used to iterate through a half-closed interval [0, end).
/// The parameter `end` can have any integral or enum type.
/// The same as range(0, end).
template <typename Type,
typename = std::enable_if_t<std::is_integral_v<Type> || std::is_enum_v<Type>, void>>
inline auto range(Type end)
{
if constexpr (std::is_integral_v<Type>)
return internal::rangeImpl<Type, Type>(0, end);
else
return internal::rangeImpl<Type, std::underlying_type_t<Type>>(0, end);
}
}

View File

@ -128,7 +128,7 @@ struct EnabledQuota::Impl
const Intervals & intervals,
std::chrono::system_clock::time_point current_time)
{
for (auto resource_type : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto resource_type : ext::range(Quota::MAX_RESOURCE_TYPE))
checkExceeded(user_name, intervals, resource_type, current_time);
}
};

View File

@ -65,7 +65,7 @@ private:
const String & getUserName() const { return params.user_name; }
static constexpr size_t MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
static constexpr auto MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
struct Interval
{

View File

@ -58,8 +58,7 @@ private:
{
size_t operator()(const DatabaseAndTableNameRef & database_and_table_name) const;
};
static constexpr size_t MAX_CONDITION_TYPE = RowPolicy::MAX_CONDITION_TYPE;
using ParsedConditions = std::array<ASTPtr, MAX_CONDITION_TYPE>;
using ParsedConditions = std::array<ASTPtr, RowPolicy::MAX_CONDITION_TYPE>;
struct MixedConditions
{
std::unique_ptr<DatabaseAndTableName> database_and_table_name_keeper;

View File

@ -5,6 +5,12 @@
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
Quota::Limits::Limits()
{
boost::range::fill(max, 0);
@ -38,8 +44,9 @@ const char * Quota::resourceTypeToColumnName(ResourceType resource_type)
case Quota::READ_ROWS: return "read_rows";
case Quota::READ_BYTES: return "read_bytes";
case Quota::EXECUTION_TIME: return "execution_time";
case Quota::MAX_RESOURCE_TYPE: break;
}
__builtin_unreachable();
throw Exception("Unexpected resource type: " + std::to_string(static_cast<int>(resource_type)), ErrorCodes::LOGICAL_ERROR);
}
}

View File

@ -7,6 +7,12 @@
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
/** Quota for resources consumption for specific interval.
* Used to limit resource usage by user.
* Quota is applied "softly" - could be slightly exceed, because it is checked usually only on each block of processed data.
@ -26,8 +32,9 @@ struct Quota : public IAccessEntity
READ_ROWS, /// Number of rows read from tables.
READ_BYTES, /// Number of bytes read from tables.
EXECUTION_TIME, /// Total amount of query execution time in nanoseconds.
MAX_RESOURCE_TYPE
};
static constexpr size_t MAX_RESOURCE_TYPE = 7;
using ResourceAmount = UInt64;
static constexpr ResourceAmount UNLIMITED = 0; /// 0 means unlimited.
@ -58,8 +65,9 @@ struct Quota : public IAccessEntity
CLIENT_KEY, /// Client should explicitly supply a key to use.
CLIENT_KEY_OR_USER_NAME, /// Same as CLIENT_KEY, but use USER_NAME if the client doesn't supply a key.
CLIENT_KEY_OR_IP_ADDRESS, /// Same as CLIENT_KEY, but use IP_ADDRESS if the client doesn't supply a key.
MAX
};
static constexpr size_t MAX_KEY_TYPE = 6;
KeyType key_type = KeyType::NONE;
/// Which roles or users should use this quota.
@ -88,8 +96,9 @@ inline const char * Quota::getNameOfResourceType(ResourceType resource_type)
case Quota::READ_ROWS: return "read rows";
case Quota::READ_BYTES: return "read bytes";
case Quota::EXECUTION_TIME: return "execution time";
case Quota::MAX_RESOURCE_TYPE: break;
}
__builtin_unreachable();
throw Exception("Unexpected resource type: " + std::to_string(static_cast<int>(resource_type)), ErrorCodes::LOGICAL_ERROR);
}
@ -104,8 +113,9 @@ inline const char * Quota::resourceTypeToKeyword(ResourceType resource_type)
case Quota::READ_ROWS: return "READ ROWS";
case Quota::READ_BYTES: return "READ BYTES";
case Quota::EXECUTION_TIME: return "EXECUTION TIME";
case Quota::MAX_RESOURCE_TYPE: break;
}
__builtin_unreachable();
throw Exception("Unexpected resource type: " + std::to_string(static_cast<int>(resource_type)), ErrorCodes::LOGICAL_ERROR);
}
@ -119,8 +129,9 @@ inline const char * Quota::getNameOfKeyType(KeyType key_type)
case KeyType::CLIENT_KEY: return "client key";
case KeyType::CLIENT_KEY_OR_USER_NAME: return "client key or user name";
case KeyType::CLIENT_KEY_OR_IP_ADDRESS: return "client key or ip address";
case KeyType::MAX: break;
}
__builtin_unreachable();
throw Exception("Unexpected quota key type: " + std::to_string(static_cast<int>(key_type)), ErrorCodes::LOGICAL_ERROR);
}

View File

@ -17,6 +17,7 @@ namespace DB
namespace ErrorCodes
{
extern const int QUOTA_REQUIRES_CLIENT_KEY;
extern const int LOGICAL_ERROR;
}
@ -72,8 +73,9 @@ String QuotaCache::QuotaInfo::calculateKey(const EnabledQuota & enabled) const
return params.client_key;
return params.client_address.toString();
}
case KeyType::MAX: break;
}
__builtin_unreachable();
throw Exception("Unexpected quota key type: " + std::to_string(static_cast<int>(quota->key_type)), ErrorCodes::LOGICAL_ERROR);
}
@ -101,7 +103,7 @@ boost::shared_ptr<const EnabledQuota::Intervals> QuotaCache::QuotaInfo::rebuildI
new_intervals->quota_key = key;
auto & intervals = new_intervals->intervals;
intervals.reserve(quota->all_limits.size());
static constexpr size_t MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
static constexpr auto MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
for (const auto & limits : quota->all_limits)
{
intervals.emplace_back();

View File

@ -11,7 +11,7 @@ struct QuotaUsageInfo
{
using ResourceType = Quota::ResourceType;
using ResourceAmount = Quota::ResourceAmount;
static constexpr size_t MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
static constexpr auto MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
struct Interval
{

View File

@ -6,6 +6,12 @@
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
namespace
{
void generateFullNameImpl(const String & database_, const String & table_name_, const String & policy_name_, String & full_name_)
@ -90,8 +96,9 @@ const char * RowPolicy::conditionTypeToString(ConditionType index)
case UPDATE_FILTER: return "UPDATE_FILTER";
case UPDATE_CHECK: return "UPDATE_CHECK";
case DELETE_FILTER: return "DELETE_FILTER";
case MAX_CONDITION_TYPE: break;
}
__builtin_unreachable();
throw Exception("Unexpected condition type: " + std::to_string(static_cast<int>(index)), ErrorCodes::LOGICAL_ERROR);
}
@ -104,8 +111,9 @@ const char * RowPolicy::conditionTypeToColumnName(ConditionType index)
case UPDATE_FILTER: return "update_filter";
case UPDATE_CHECK: return "update_check";
case DELETE_FILTER: return "delete_filter";
case MAX_CONDITION_TYPE: break;
}
__builtin_unreachable();
throw Exception("Unexpected condition type: " + std::to_string(static_cast<int>(index)), ErrorCodes::LOGICAL_ERROR);
}
}

View File

@ -44,8 +44,9 @@ struct RowPolicy : public IAccessEntity
UPDATE_FILTER,
UPDATE_CHECK,
DELETE_FILTER,
MAX_CONDITION_TYPE
};
static constexpr size_t MAX_CONDITION_TYPE = 5;
static const char * conditionTypeToString(ConditionType index);
static const char * conditionTypeToColumnName(ConditionType index);

View File

@ -16,7 +16,7 @@ namespace DB
namespace
{
using ConditionType = RowPolicy::ConditionType;
constexpr size_t MAX_CONDITION_TYPE = RowPolicy::MAX_CONDITION_TYPE;
constexpr auto MAX_CONDITION_TYPE = RowPolicy::MAX_CONDITION_TYPE;
/// Accumulates conditions from multiple row policies and joins them using the AND logical operation.
@ -58,7 +58,7 @@ void RowPolicyCache::PolicyInfo::setPolicy(const RowPolicyPtr & policy_)
policy = policy_;
roles = &policy->to_roles;
for (auto type : ext::range_with_static_cast<ConditionType>(0, MAX_CONDITION_TYPE))
for (auto type : ext::range(0, MAX_CONDITION_TYPE))
{
parsed_conditions[type] = nullptr;
const String & condition = policy->conditions[type];

View File

@ -166,7 +166,7 @@ namespace
if (policy.isRestrictive())
query->is_restrictive = policy.isRestrictive();
for (auto index : ext::range_with_static_cast<RowPolicy::ConditionType>(RowPolicy::MAX_CONDITION_TYPE))
for (auto index : ext::range(RowPolicy::MAX_CONDITION_TYPE))
{
const auto & condition = policy.conditions[index];
if (!condition.empty())

View File

@ -30,7 +30,7 @@ String InterpreterShowQuotasQuery::getRewrittenQuery()
expr = "name || ' key=\\'' || key || '\\'' || if(isNull(end_of_interval), '', ' interval=[' || "
"toString(end_of_interval - duration) || ' .. ' || "
"toString(end_of_interval) || ']'";
for (auto resource_type : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto resource_type : ext::range(Quota::MAX_RESOURCE_TYPE))
{
String column_name = Quota::resourceTypeToColumnName(resource_type);
expr += String{" || ' "} + column_name + "=' || toString(" + column_name + ")";

View File

@ -67,7 +67,7 @@ namespace
else
{
bool limit_found = false;
for (auto resource_type : ext::range_with_static_cast<ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto resource_type : ext::range(Quota::MAX_RESOURCE_TYPE))
{
if (limits.max[resource_type])
{

View File

@ -42,7 +42,7 @@ public:
using ResourceType = Quota::ResourceType;
using ResourceAmount = Quota::ResourceAmount;
static constexpr size_t MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
static constexpr auto MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
struct Limits
{

View File

@ -48,7 +48,7 @@ namespace
return false;
const String & key_type_str = key_type_ast->as<ASTLiteral &>().value.safeGet<const String &>();
for (auto kt : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
for (auto kt : ext::range(Quota::KeyType::MAX))
if (boost::iequals(Quota::getNameOfKeyType(kt), key_type_str))
{
key_type = kt;
@ -56,7 +56,7 @@ namespace
}
String all_key_types_str;
for (auto kt : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
for (auto kt : ext::range(Quota::KeyType::MAX))
all_key_types_str += String(all_key_types_str.empty() ? "" : ", ") + "'" + Quota::getNameOfKeyType(kt) + "'";
String msg = "Quota cannot be keyed by '" + key_type_str + "'. Expected one of these literals: " + all_key_types_str;
throw Exception(msg, ErrorCodes::SYNTAX_ERROR);
@ -81,7 +81,7 @@ namespace
}
bool resource_type_set = false;
for (auto rt : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto rt : ext::range(Quota::MAX_RESOURCE_TYPE))
{
if (ParserKeyword{Quota::resourceTypeToKeyword(rt)}.ignore(pos, expected))
{

View File

@ -23,7 +23,7 @@ NamesAndTypesList StorageSystemQuotaUsage::getNamesAndTypes()
{"duration", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeUInt64>())},
{"end_of_interval", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeDateTime>())}};
for (auto resource_type : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto resource_type : ext::range(Quota::MAX_RESOURCE_TYPE))
{
DataTypePtr data_type;
if (resource_type == Quota::EXECUTION_TIME)

View File

@ -20,7 +20,7 @@ namespace
DataTypeEnum8::Values getKeyTypeEnumValues()
{
DataTypeEnum8::Values enum_values;
for (auto key_type : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
for (auto key_type : ext::range(Quota::KeyType::MAX))
enum_values.push_back({Quota::getNameOfKeyType(key_type), static_cast<UInt8>(key_type)});
return enum_values;
}
@ -38,7 +38,7 @@ NamesAndTypesList StorageSystemQuotas::getNamesAndTypes()
{"intervals.duration", std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt64>())},
{"intervals.randomize_interval", std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt8>())}};
for (auto resource_type : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto resource_type : ext::range(Quota::MAX_RESOURCE_TYPE))
{
DataTypePtr data_type;
if (resource_type == Quota::EXECUTION_TIME)

View File

@ -25,7 +25,7 @@ NamesAndTypesList StorageSystemRowPolicies::getNamesAndTypes()
{"restrictive", std::make_shared<DataTypeUInt8>()},
};
for (auto index : ext::range_with_static_cast<RowPolicy::ConditionType>(RowPolicy::MAX_CONDITION_TYPE))
for (auto index : ext::range(RowPolicy::MAX_CONDITION_TYPE))
names_and_types.push_back({RowPolicy::conditionTypeToColumnName(index), std::make_shared<DataTypeString>()});
return names_and_types;